Proper Error handling http_open

I’m using: SWI-Prolog version 9.2.4

Hello,

I’ve been trying to have a webpage call another api and display the formatted results on a page. It works but when the other server is down, or throws an error my page returns the full exception string, what I’d prefer to do is error handle it and return just a formatted empty list when the second server is down. What would be the proper way to error handle?

My code looks like this:

% the http reply endpoint
engine(Request) :-
    member(method(post), Request),!,
    http_read_data(Request, [query=Q|_], []),
    process_query(Q,R0),
    length(R0,Total),
    format_results(R0,R),
    reply_html_page(
	[title('search engine')],
	[
	    \html_requires(static('styles.css')),
	    \html_requires(static('themes.css')),
	    \nav_bar,
	    div(id(main),
		[
		    \description,
		    \query_form,
		    div(['Total Results: ',Total]),
		    div(R)
		]),
	    \footer   
	]).
% The http_open call
process_query(Query, Message):-
    string_concat('/query/',Query,Path),
    parse_url(Url,
	      [ protocol(http),
		host('localhost'),
		port(8000),
		path(Path)
	      ]),
    write(Url),
    setup_call_cleanup(
	http_open(Url, In,
		  [ method(get),                
		    request_header('Content-Type'='application/json'),
		    request_header(accept='*/*'),		
		    cert_verify_hook(cert_accept_any),
		    status_code(200)
		  ]),
	(json_read(In, json(JSON)),
	 Message = JSON.message),
	close(In)).
process_query(_, []).

This is whats returned to the page when the other server is down

Internal server error
Socket error: Connection refused
In:
[30] throw(error(socket_error(econnrefused,'Connection refused'),_1340))
[28] socket:tcp_connect('<garbage_collected>','<garbage_collected>',[visited(...),...|...]) at /usr/lib/swipl/library/ext/clib/socket.pl:471
[27] http_open:open_socket('<garbage_collected>',_1428,[visited(...),...|...]) at /usr/lib/swipl/library/ext/http/http/http_open.pl:951
[25] http_open:try_http_proxy(direct,[uri('http://localhost:8000/query/hello'),...|...],_1480,'<garbage_collected>') at /usr/lib/swipl/library/ext/http/http/http_open.pl:494
[23] <meta call>
[22] sig_atomic(search_engine:http_open('http://localhost:8000/query/hello',_1558,...)) <foreign>
[21] setup_call_cleanup('<garbage_collected>',search_engine:(...,...),search_engine:close(_1610)) at /usr/lib/swipl/boot/init.pl:681
[20] search_engine:process_query(hello,_1642) at /home/alek/Code/Prolog/swi-site/src/showcases/search_engine.pl:93
[19] search_engine:engine('<garbage_collected>') at /home/alek/Code/Prolog/swi-site/src/showcases/search_engine.pl:19
[18] http_dispatch:call_action(user:engine,[protocol(http),...|...]) at /usr/lib/swipl/library/ext/http/http/http_dispatch.pl:1043
[16] time:run_alarm_goal('<garbage_collected>','<garbage_collected>') at /usr/lib/swipl/library/ext/clib/time.pl:163
[15] setup_call_cleanup(time:alarm(300,...,...,...),time:run_alarm_goal(...,...),time:remove_alarm_notrace(...)) at /usr/lib/swipl/boot/init.pl:682
[14] time:call_with_time_limit(300,'<garbage_collected>','$no_ctx') at /usr/lib/swipl/library/ext/clib/time.pl:154
[12] http_dispatch:time_limit_action('<garbage_collected>','<garbage_collected>','<garbage_collected>') at /usr/lib/swipl/library/ext/http/http/http_dispatch.pl:1022
[8] httpd_wrapper:call_handler('<garbage_collected>',1,'<garbage_collected>') at /usr/lib/swipl/library/ext/http/http/http_wrapper.pl:329
[7] catch(httpd_wrapper:call_handler(...,1,...),_1952,httpd_wrapper:true) at /usr/lib/swipl/boot/init.pl:565
[6] httpd_wrapper:handler_with_output_to(user:http_dispatch,1,'<garbage_collected>',current_output,_2012) at /usr/lib/swipl/library/ext/http/http/http_wrapper.pl:308
[5] httpd_wrapper:handler_with_output_to('<garbage_collected>',1,'<garbage_collected>',<stream>(0x746d540232e0),'<garbage_collected>') at /usr/lib/swipl/library/ext/http/http/http_wrapper.pl:318

Note: some frames are missing due to last-call optimization.
Re-run your program in debug mode (:- debug.) to get more detail.

SWI-Prolog httpd at alek

To catch the thrown error, use catch/3 - here’s a potentially-convenient wrapper:

catch_ok_exception(Goal, IfOk, IfException) :-
    catch(Goal, Err, true),
    (   var(Err)
    ->  IfOk
    ;   IfException
    ).

You are using status_code(200) - which will fail if the status code is not 200. Instead, use e.g. status_code(StatusCode) to have the code continue, and then check the StatusCode later, because it could be e.g. 504 HTTP error code.

Thank you! I think I had some misunderstanding of how to use catch/3, next step is I will try adding different error code messages/handling. The working solution is this

process_query(Query, Message):-string_concat(‘/query/’,Query,Path),parse_url(Url,[ protocol(http),host(‘localhost’),port(8000),path(Path)]),write(Url),catch((http_open(Url, In,[ method(get),request_header(‘Content-Type’=‘application/json’),request_header(accept=‘/’),		cert_verify_hook(cert_accept_any)]),json_read(In, json(JSON)),Message = JSON.message),_Err,Message = [‘Server down’]).

Why are you not using what I suggested:

status_code(StatusCode)

The HTTP status code is vitally important, so this StatusCode variable needs to be checked by your code. It’s intended to be 200, but might not be.

Using the wrapper I’m getting issues about the arguments being sufficiently instantiated. But I’m trying to add logic to handle various error codes right now, if I turn off the second api and the error gets thrown, the catch doesn’t seem to pass along any binding for the status code, though I’ll be running some tests and handling for status codes from my other api next.

The wrapper can be modified/customised, it’s just an example.