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          ]).   41:- use_module(library(settings), [current_setting/1, setting/2]).

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 */
   60:- multifile
   61    http:bad_request_error/2,                 % Formal, Context
   62    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).
   69map_exception_to_http_status(Exception, Reply, HdrExtra, Context) :-
   70    http:map_exception_to_http_status_hook(Exception, Reply, HdrExtra, Context),
   71    !.
   72map_exception_to_http_status(http(not_modified),
   73              not_modified,
   74              [connection('Keep-Alive')],
   75              []) :- !.
   76map_exception_to_http_status(http_reply(Reply),
   77              Reply,
   78              [connection(Close)],
   79              []) :-
   80    !,
   81    keep_alive(Reply, Close).
   82map_exception_to_http_status(http_reply(Reply, HdrExtra0),
   83              Reply,
   84              HdrExtra,
   85              Context) :-
   86    !,
   87    map_exception_to_http_status(http_reply(Reply, HdrExtra0, []),
   88                                 Reply,
   89                                 HdrExtra,
   90                                 Context).
   91
   92map_exception_to_http_status(http_reply(Reply, HdrExtra0, Context),
   93              Reply,
   94              HdrExtra,
   95              Context):-
   96    !,
   97    (   memberchk(connection(_), HdrExtra0)
   98    ->  HdrExtra = HdrExtra0
   99    ;   HdrExtra = [connection(Close)|HdrExtra0],
  100        keep_alive(Reply, Close)
  101    ).
  102map_exception_to_http_status(error(existence_error(http_location, Location), _),
  103              not_found(Location),
  104              [connection(close)],
  105              []) :- !.
  106map_exception_to_http_status(error(permission_error(http_method, Method, Location), _),
  107              method_not_allowed(Method, Location),
  108              [connection(close)],
  109              []) :- !.
  110map_exception_to_http_status(error(permission_error(_, http_location, Location), _),
  111              forbidden(Location),
  112              [connection(close)],
  113              []) :- !.
  114map_exception_to_http_status(error(threads_in_pool(_Pool), _),
  115              busy,
  116              [connection(close)],
  117              []) :- !.
  118map_exception_to_http_status(E,
  119              resource_error(E),
  120              [connection(close)],
  121              []) :-
  122    is_resource_error(E),
  123    !.
  124map_exception_to_http_status(E,
  125              bad_request(E2),
  126              [connection(close)],
  127              []) :-
  128    bad_request_exception(E),
  129    !,
  130    discard_stack_trace(E, E2).
  131map_exception_to_http_status(E,
  132              server_error(E),
  133              [connection(close)],
  134              []).
  135
  136is_resource_error(error(resource_error(_), _)).
  137
  138bad_request_exception(error(Error, Context)) :-
  139    nonvar(Error),
  140    bad_request_error(Error, ContextGeneral),
  141    (   var(ContextGeneral)
  142    ->  true
  143    ;   Context = context(_Stack, ContextInstance)
  144    ->  subsumes_term(ContextGeneral, ContextInstance)
  145    ),
  146    !.
  147
  148bad_request_error(Error, Context) :-
  149    http:bad_request_error(Error, Context).
  150bad_request_error(Error, Context) :-
  151    default_bad_request_error(Error, Context).
  152
  153default_bad_request_error(domain_error(http_request, _), _).
  154default_bad_request_error(existence_error(http_parameter, _), _).
  155default_bad_request_error(type_error(_, _), http_parameter(_)).
  156default_bad_request_error(syntax_error(http_request_line(_)), _).
  157default_bad_request_error(syntax_error(http_request(_)), _).
  158default_bad_request_error(syntax_error(_), in_http_request).
  159
  160discard_stack_trace(error(Formal, context(_,Msg)),
  161                    error(Formal, context(_,Msg))).
 in_or_exclude_backtrace(+ErrorIn, -ErrorOut)
Remove the stacktrace from the exception, unless setting http:client_backtrace is true.
  168in_or_exclude_backtrace(Error, Error) :-
  169    current_setting(http:client_backtrace),
  170    setting(http:client_backtrace, true),
  171    !.
  172in_or_exclude_backtrace(Error0, Error) :-
  173    discard_stack_trace(Error0, Error),
  174    !.
  175in_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.
  200keep_alive(Reply, Connection) :-
  201    (   keep_alive(Reply)
  202    ->  Connection = 'Keep-Alive'
  203    ;   Connection = close
  204    ).
  205
  206keep_alive(not_modified).
  207keep_alive(bytes(_Type, _Bytes)).
  208keep_alive(file(_Type, _File)).
  209keep_alive(tmp_file(_Type, _File)).
  210keep_alive(stream(_In, _Len)).
  211keep_alive(cgi_stream(_In, _Len)).
  212keep_alive(switching_protocols(_Goal, _)).
  213
  214
  215                 /*******************************
  216                 *          IDE SUPPORT         *
  217                 *******************************/
  218
  219% See library('trace/exceptions')
  220
  221:- multifile
  222    prolog:general_exception/2.  223
  224prolog:general_exception(http_reply(_), http_reply(_)).
  225prolog:general_exception(http_reply(_,_), http_reply(_,_))