requests.pl 4.04 KB
Newer Older
1
2
3
4
5
:- use_module(src/types).
:- ensure_loaded(error_handling).

%! read_json_body(+Request, -Payload) is det
% 
6
7
8
9
10
11
12
13
% Read JSON body from request. Only the first entity of the body is parsed. 
% Further content is discarded (up to the sent content length). No checks on 
% Payload are performed.
%
% Discarding body content after the first JSON _entity_ (meaning integer, 
% string, object, ...) is an undocumented feature of the used library predicate 
% http_read_json_dict/2. Additionally, the library predicate ignores trailing 
% commas in JSON arrays which should be an error.
14
15
16
17
18
19
%
% @throws parse_error(illegal_json_syntax(Details), Position) if request body is
%                    syntactically illformatted. Position is the term 
%                    stream_position(Line, Column).
% @throws `empty_request_body` if the request has no body
% @throws `length_required` if the request has no Content-Length header
20
21
22
% @throws invalid_header_value(content_length, Value) if a Content-Length header
%         has been sent but the given value Value is invalid (no nonnegative 
%         integer).
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
read_json_body(Request, Payload) :-
    (   memberchk(content_length(Len), Request)
    ->  (   integer(Len),
            Len >= 0
        ->  true
        ;   throw(invalid_header_value(content_length, Len))
        )
    ;   throw(length_required)
    ),
    catch(http_read_json_dict(Request, Payload, [value_string_as(atom)]),
          error(syntax_error(json(Details)), 
                stream(_S, Line, LinePos, _CharNo)),
          throw(parse_error(illegal_json_syntax(Details), 
                            stream_position(Line, LinePos)))
    )
    -> true
    ; throw(empty_request_body).
    
    
%! purge_input_stream(+Request) is det.
%
44
45
46
% Purge the content of the request body. Only possible if a 
% =Content-Length=-header has been sent. If none is sent it is assumed that no 
% payload has been sent.
47
%
48
49
50
% @throws invalid_header_value(content_length, Value) if a Content-Length header 
%         has been sent but the given value Value is invalid (no nonnegative 
%         integer).
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
purge_input_stream(Request) :-
    memberchk(input(StreamIn), Request),
    memberchk(content_length(Len), Request),
    !,
    integer(Len),
    (   Len >= 0
    ->  setup_call_cleanup(open_null_stream(StreamOut),
                            copy_stream_data(StreamIn, StreamOut, Len),
                            close(StreamOut)
    );  throw(invalid_header_value(content_length, Len))
    )
.
purge_input_stream(_).


%! parse_parameters(+In, ++Type, -Out) is det.
% 
68
69
% Parse input parameters In to the expected type (including dict's optional and 
% required keys). 
70
%
71
72
73
74
75
% @throws parse_error(Reason, Location) if some part (al location Location)
%         cannot be parsed to the desired type.
% @error api_error(_, parameter_error) if there is an error in the Type 
%        specification or some part of the input is unbound which is not allowed
%        by Type.
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

parse_parameters(In, Type, Out) :-
    catch(parse_type(In, Type, Out),
          Exception,
          ( ignore((Exception=type_def_error(_Reason),
                    api_error(parameter_error, [
                        message("Error in type definition"),
                        cause(Exception),
                        type(Type)
                    ]))),
            ignore((Exception=unknown_option(Option),
                    api_error(parameter_error, [
                        message("Unknown parse option"),
                        cause(Exception),
                        option(Option)
                    ]))),
            ignore((Exception=parse_error(instantiation(_Expected), _Location),
                    api_error(parameter_error, [
                        message("Unbound input. Cannot have been encoded in JSON"),
                        cause(Exception),
                        type(Type),
                        input(In)
                    ]))),
            throw(Exception)
          ) 
    ).
102
103
104
105
106

%! abs_path_type(-Type) is det.
%
% Return the type definition for an absolute path used to identify items.
abs_path_type(atom('^(/|.*[^/])$')).