View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2009-2017, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(http_exception,
   37          [ map_exception_to_http_status/4,     % +Exception, -Reply,
   38                                                % -HdrExtra, -Context
   39            in_or_exclude_backtrace/2           % +Error, -CleanedError
   40          ]).

Map Prolog exceptions to HTTP errors

This module maps exceptions from various parts of the HTTP libraries as well as exceptions from user handler predicates into meaningful HTTP error codes such as 4XX and 5XX codes. For example, existence errors on http locations are mapped to 404 while out-of-stack is mapped to 503.

This library provides two hooks:

See also
- http_header.pl, http_wrapper.pl */
   59:- multifile
   60    http:bad_request_error/2,                 % Formal, Context
   61    http:map_exception_to_http_status_hook/4. % Exception, Reply, HdrExtra, Ctx
 map_exception_to_http_status(+Exception, -Reply, -HdrExtra, -Context)
Map certain exceptions to HTTP status codes. The http(not_modified) provides backward compatibility to http_reply(not_modified).
   68map_exception_to_http_status(Exception, Reply, HdrExtra, Context) :-
   69    http:map_exception_to_http_status_hook(Exception, Reply, HdrExtra, Context),
   70    !.
   71map_exception_to_http_status(http(not_modified),
   72              not_modified,
   73              [connection('Keep-Alive')],
   74              []) :- !.
   75map_exception_to_http_status(http_reply(Reply),
   76              Reply,
   77              [connection(Close)],
   78              []) :-
   79    !,
   80    keep_alive(Reply, Close).
   81map_exception_to_http_status(http_reply(Reply, HdrExtra0),
   82              Reply,
   83              HdrExtra,
   84              Context) :-
   85    !,
   86    map_exception_to_http_status(http_reply(Reply, HdrExtra0, []),
   87                                 Reply,
   88                                 HdrExtra,
   89                                 Context).
   90
   91map_exception_to_http_status(http_reply(Reply, HdrExtra0, Context),
   92              Reply,
   93              HdrExtra,
   94              Context):-
   95    !,
   96    (   memberchk(connection(_), HdrExtra0)
   97    ->  HdrExtra = HdrExtra0
   98    ;   HdrExtra = [connection(Close)|HdrExtra0],
   99        keep_alive(Reply, Close)
  100    ).
  101map_exception_to_http_status(error(existence_error(http_location, Location), _),
  102              not_found(Location),
  103              [connection(close)],
  104              []) :- !.
  105map_exception_to_http_status(error(permission_error(http_method, Method, Location), _),
  106              method_not_allowed(Method, Location),
  107              [connection(close)],
  108              []) :- !.
  109map_exception_to_http_status(error(permission_error(_, http_location, Location), _),
  110              forbidden(Location),
  111              [connection(close)],
  112              []) :- !.
  113map_exception_to_http_status(error(threads_in_pool(_Pool), _),
  114              busy,
  115              [connection(close)],
  116              []) :- !.
  117map_exception_to_http_status(E,
  118              resource_error(E),
  119              [connection(close)],
  120              []) :-
  121    is_resource_error(E),
  122    !.
  123map_exception_to_http_status(E,
  124              bad_request(E2),
  125              [connection(close)],
  126              []) :-
  127    bad_request_exception(E),
  128    !,
  129    discard_stack_trace(E, E2).
  130map_exception_to_http_status(E,
  131              server_error(E),
  132              [connection(close)],
  133              []).
  134
  135is_resource_error(error(resource_error(_), _)).
  136
  137bad_request_exception(error(Error, Context)) :-
  138    nonvar(Error),
  139    bad_request_error(Error, ContextGeneral),
  140    (   var(ContextGeneral)
  141    ->  true
  142    ;   Context = context(_Stack, ContextInstance)
  143    ->  subsumes_term(ContextGeneral, ContextInstance)
  144    ),
  145    !.
  146
  147bad_request_error(Error, Context) :-
  148    http:bad_request_error(Error, Context).
  149bad_request_error(Error, Context) :-
  150    default_bad_request_error(Error, Context).
  151
  152default_bad_request_error(domain_error(http_request, _), _).
  153default_bad_request_error(existence_error(http_parameter, _), _).
  154default_bad_request_error(type_error(_, _), http_parameter(_)).
  155default_bad_request_error(syntax_error(http_request_line(_)), _).
  156default_bad_request_error(syntax_error(http_request(_)), _).
  157default_bad_request_error(syntax_error(_), in_http_request).
  158
  159discard_stack_trace(error(Formal, context(_,Msg)),
  160                    error(Formal, context(_,Msg))).
 in_or_exclude_backtrace(+ErrorIn, -ErrorOut)
Remove the stacktrace from the exception, unless setting http:client_backtrace is true.
  167in_or_exclude_backtrace(Error, Error) :-
  168    current_setting(http:client_backtrace),
  169    setting(http:client_backtrace, true),
  170    !.
  171in_or_exclude_backtrace(Error0, Error) :-
  172    discard_stack_trace(Error0, Error),
  173    !.
  174in_or_exclude_backtrace(Exception, Exception).
 http:bad_request_error(+Formal, -ContextTemplate) is semidet
If an exception of the term error(Formal, context(Stack, Context)) is caught and subsumes_term(ContextTemplate, Context) is true, translate the exception into an HTTP 400 exception. If the exception contains a stack-trace, this is stripped from the response.

The idea behind this hook is that applications can raise 400 responses by

 keep_alive(+Reply) is semidet
 keep_alive(+Reply, -Connection) is det
If true for Reply, the default is to keep the connection open.
  199keep_alive(Reply, Connection) :-
  200    (   keep_alive(Reply)
  201    ->  Connection = 'Keep-Alive'
  202    ;   Connection = close
  203    ).
  204
  205keep_alive(not_modified).
  206keep_alive(bytes(_Type, _Bytes)).
  207keep_alive(file(_Type, _File)).
  208keep_alive(tmp_file(_Type, _File)).
  209keep_alive(stream(_In, _Len)).
  210keep_alive(cgi_stream(_In, _Len)).
  211keep_alive(switching_protocols(_Goal, _)).
  212
  213
  214                 /*******************************
  215                 *          IDE SUPPORT         *
  216                 *******************************/
  217
  218% See library('trace/exceptions')
  219
  220:- multifile
  221    prolog:general_exception/2.  222
  223prolog:general_exception(http_reply(_), http_reply(_)).
  224prolog:general_exception(http_reply(_,_), http_reply(_,_))