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) 2006-2015, 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_parameters, 37 [ http_parameters/2, % +Request, -Params 38 http_parameters/3, % +Request, -Params, +TypeG 39 40 http_convert_parameter/4, % +Options, +FieldName, +ValIn, -ValOut 41 http_convert_parameters/2, % +Data, +Params 42 http_convert_parameters/3 % +Data, +Params, :DeclGoal 43 ]). 44:- use_module(http_client). 45:- use_module(http_multipart_plugin). 46:- use_module(http_hook). 47:- use_module(library(debug)). 48:- use_module(library(option)). 49:- use_module(library(error)). 50:- use_module(library(broadcast)). 51 52:- multifile 53 http:convert_parameter/3. 54 55:- predicate_options(http_parameters/3, 3, 56 [ form_data(-list), 57 attribute_declarations(callable) 58 ]).
82:- meta_predicate
83 http_parameters( , , ),
84 http_convert_parameters( , , ).
call(Goal, A, Declarations)
.The attribute_declarations hook allows sharing the declaration of attribute-properties between many http_parameters/3 calls. In this form, the requested attribute takes only one argument and the options are acquired by calling the hook. For example:
..., http_parameters(Request, [ sex(Sex) ], [ attribute_declarations(http_param) ]), ... http_param(sex, [ oneof(male, female), description('Sex of the person') ]).
131http_parameters(Request, Params) :- 132 http_parameters(Request, Params, []). 133 134http_parameters(Request, Params, Options) :- 135 must_be(list, Params), 136 meta_options(is_meta, Options, QOptions), 137 option(attribute_declarations(DeclGoal), QOptions, no_decl_goal), 138 http_parms(Request, Params, DeclGoal, Form), 139 ( memberchk(form_data(RForm), QOptions) 140 -> RForm = Form 141 ; true 142 ). 143 144is_meta(attribute_declarations). 145 146 147http_parms(Request, Params, DeclGoal, Search) :- 148 memberchk(search(Search), Request), 149 !, 150 fill_parameters(Params, Search, DeclGoal). 151http_parms(Request, Params, DeclGoal, Data) :- 152 memberchk(method(Method), Request), 153 Method == post, 154 memberchk(content_type(Content), Request), 155 form_data_content_type(Content), 156 !, 157 debug(post_request, 'POST Request: ~p', [Request]), 158 posted_form(Request, Data), 159 fill_parameters(Params, Data, DeclGoal). 160http_parms(_Request, Params, DeclGoal, []) :- 161 fill_parameters(Params, [], DeclGoal). 162 163:- multifile 164 form_data_content_type/1. 165 166form_data_content_type('application/x-www-form-urlencoded') :- !. 167form_data_content_type(ContentType) :- 168 sub_atom(ContentType, 0, _, _, 'application/x-www-form-urlencoded;').
175posted_form(Request, _Data) :- 176 nb_current(http_post_data, read), 177 !, 178 option(request_uri(URI), Request), 179 throw(error(permission_error('re-read', 'POST data', URI), 180 context(_, 'Attempt to re-read POST data'))). 181posted_form(Request, Data) :- 182 http_read_data(Request, Data, []), 183 nb_setval(http_post_data, read), 184 debug(post, 'POST Data: ~p', [Data]). 185 186wipe_posted_data :- 187 debug(post, 'Wiping posted data', []), 188 nb_delete(http_post_data). 189 190:- listen(http(request_finished(_Id, _Code, _Status, _CPU, _Bytes)), 191 wipe_posted_data).
198:- meta_predicate fill_parameters( , , ). 199 200fill_parameters([], _, _). 201fill_parameters([H|T], FormData, DeclGoal) :- 202 fill_parameter(H, FormData, DeclGoal), 203 fill_parameters(T, FormData, DeclGoal). 204 205fill_parameter(H, _, _) :- 206 var(H), 207 !, 208 instantiation_error(H). 209fill_parameter(group(Members, _Options), FormData, DeclGoal) :- 210 is_list(Members), 211 !, 212 fill_parameters(Members, FormData, DeclGoal). 213fill_parameter(H, FormData, _) :- 214 H =.. [Name,Value,Options], 215 !, 216 fill_param(Name, Value, Options, FormData). 217fill_parameter(H, FormData, DeclGoal) :- 218 H =.. [Name,Value], 219 ( DeclGoal \== (-), 220 call(DeclGoal, Name, Options) 221 -> true 222 ; throw(error(existence_error(attribute_declaration, Name), _)) 223 ), 224 fill_param(Name, Value, Options, FormData). 225 226fill_param(Name, Values, Options, FormData) :- 227 memberchk(zero_or_more, Options), 228 !, 229 fill_param_list(FormData, Name, Values, Options). 230fill_param(Name, Values, Options, FormData) :- 231 memberchk(list(Type), Options), 232 !, 233 fill_param_list(FormData, Name, Values, [Type|Options]). 234fill_param(Name, Value, Options, FormData) :- 235 ( memberchk(Name=Value0, FormData), 236 Value0 \== '' % Not sure 237 -> http_convert_parameter(Options, Name, Value0, Value) 238 ; memberchk(default(Value), Options) 239 -> true 240 ; memberchk(optional(true), Options) 241 -> true 242 ; throw(error(existence_error(http_parameter, Name), _)) 243 ). 244 245 246fill_param_list([], _, [], _). 247fill_param_list([Name=Value0|Form], Name, [Value|VT], Options) :- 248 !, 249 http_convert_parameter(Options, Name, Value0, Value), 250 fill_param_list(Form, Name, VT, Options). 251fill_param_list([_|Form], Name, VT, Options) :- 252 fill_param_list(Form, Name, VT, Options).
http_parameters(Request, Params) :- http_read_data(Request, Data, []), http_convert_parameters(Data, Params).
268http_convert_parameters(Data, ParamDecls) :- 269 fill_parameters(ParamDecls, Data, no_decl_goal). 270http_convert_parameters(Data, ParamDecls, DeclGoal) :- 271 fill_parameters(ParamDecls, Data, DeclGoal). 272 273no_decl_goal(_,_) :- fail.
286http_convert_parameter([], _, Value, Value). 287http_convert_parameter([H|T], Field, Value0, Value) :- 288 ( check_type_no_error(H, Value0, Value1) 289 -> catch(http_convert_parameter(T, Field, Value1, Value), 290 error(Formal, _), 291 throw(error(Formal, context(_, http_parameter(Field))))) 292 ; throw(error(type_error(H, Value0), 293 context(_, http_parameter(Field)))) 294 ). 295 296check_type_no_error(Type, In, Out) :- 297 http:convert_parameter(Type, In, Out), 298 !. 299check_type_no_error(Type, In, Out) :- 300 check_type3(Type, In, Out).
306check_type3((T1;T2), In, Out) :- 307 !, 308 ( check_type_no_error(T1, In, Out) 309 -> true 310 ; check_type_no_error(T2, In, Out) 311 ). 312check_type3(string, Atom, String) :- 313 !, 314 to_string(Atom, String). 315check_type3(number, Atom, Number) :- 316 !, 317 to_number(Atom, Number). 318check_type3(integer, Atom, Integer) :- 319 !, 320 to_number(Atom, Integer), 321 integer(Integer). 322check_type3(nonneg, Atom, Integer) :- 323 !, 324 to_number(Atom, Integer), 325 integer(Integer), 326 Integer >= 0. 327check_type3(float, Atom, Float) :- 328 !, 329 to_number(Atom, Number), 330 Float is float(Number). 331check_type3(between(Low, High), Atom, Value) :- 332 !, 333 to_number(Atom, Number), 334 ( (float(Low) ; float(High)) 335 -> Value is float(Number) 336 ; Value = Number 337 ), 338 is_of_type(between(Low, High), Value). 339check_type3(boolean, Atom, Bool) :- 340 !, 341 truth(Atom, Bool). 342check_type3(Type, Atom, Atom) :- 343 check_type2(Type, Atom). 344 345to_number(In, Number) :- 346 number(In), !, Number = In. 347to_number(In, Number) :- 348 atom(In), 349 atom_number(In, Number). 350 351to_string(In, String) :- string(In), !, String = In. 352to_string(In, String) :- atom(In), !, atom_string(In, String). 353to_string(In, String) :- number(In), !, number_string(In, String).
359check_type2(oneof(Set), Value) :- 360 !, 361 memberchk(Value, Set). 362check_type2(length > N, Value) :- 363 !, 364 atom_length(Value, Len), 365 Len > N. 366check_type2(length >= N, Value) :- 367 !, 368 atom_length(Value, Len), 369 Len >= N. 370check_type2(length < N, Value) :- 371 !, 372 atom_length(Value, Len), 373 Len < N. 374check_type2(length =< N, Value) :- 375 !, 376 atom_length(Value, Len), 377 Len =< N. 378check_type2(_, _).
385truth(true, true). 386truth('TRUE', true). 387truth(yes, true). 388truth('YES', true). 389truth(on, true). 390truth('ON', true). % IE7 391truth('1', true). 392 393truth(false, false). 394truth('FALSE', false). 395truth(no, false). 396truth('NO', false). 397truth(off, false). 398truth('OFF', false). 399truth('0', false). 400 401 402 /******************************* 403 * XREF SUPPORT * 404 *******************************/ 405 406:- multifile 407 prolog:called_by/2, 408 emacs_prolog_colours:goal_colours/2. 409 410prologcalled_by(http_parameters(_,_,Options), [G+2]) :- 411 option(attribute_declarations(G), Options, _), 412 callable(G), 413 !. 414 415emacs_prolog_colours:goal_colours(http_parameters(_,_,Options), 416 built_in-[classify, classify, Colours]) :- 417 option_list_colours(Options, Colours). 418 419option_list_colours(Var, error) :- 420 var(Var), 421 !. 422option_list_colours([], classify) :- !. 423option_list_colours(Term, list-Elements) :- 424 Term = [_|_], 425 !, 426 option_list_colours_2(Term, Elements). 427option_list_colours(_, error). 428 429option_list_colours_2(Var, classify) :- 430 var(Var). 431option_list_colours_2([], []). 432option_list_colours_2([H0|T0], [H|T]) :- 433 option_colours(H0, H), 434 option_list_colours_2(T0, T). 435 436option_colours(Var, classify) :- 437 var(Var), 438 !. 439option_colours(_=_, built_in-[classify,classify]) :- !. 440option_colours(attribute_declarations(_), % DCG = is a hack! 441 option(attribute_declarations)-[dcg]) :- !. 442option_colours(Term, option(Name)-[classify]) :- 443 compound(Term), 444 Term =.. [Name,_Value], 445 !. 446option_colours(_, error). 447 448 /******************************* 449 * MESSAGES * 450 *******************************/ 451 452:- multifile prolog:error_message//1. 453:- multifile prolog:message//1. 454 455prologerror_message(existence_error(http_parameter, Name)) --> 456 [ 'Missing value for parameter "~w".'-[Name] ]. 457prologmessage(error(type_error(Type, Term), context(_, http_parameter(Param)))) --> 458 { atom(Param) }, 459 [ 'Parameter "~w" must be '-[Param] ], 460 param_type(Type), 461 ['. Found "~w".'-[Term] ]. 462 463param_type(length>N) --> 464 !, 465 ['longer than ~D characters'-[N]]. 466param_type(length>=N) --> 467 !, 468 ['at least ~D characters'-[N]]. 469param_type(length<N) --> 470 !, 471 ['shorter than ~D characters'-[N]]. 472param_type(length=<N) --> 473 !, 474 ['at most ~D characters'-[N]]. 475param_type(between(Low,High)) --> 476 !, 477 ( {float(Low);float(High)} 478 -> ['a number between ~w and ~w'-[Low,High]] 479 ; ['an integer between ~w and ~w'-[Low,High]] 480 ). 481param_type(oneof([Only])) --> 482 !, 483 ['"~w"'-[Only]]. 484param_type(oneof(List)) --> 485 !, 486 ['one of '-[]], oneof(List). 487param_type(T) --> 488 ['of type ~p'-[T]]. 489 490 491oneof([]) --> []. 492oneof([H|T]) --> 493 ['"~w"'-[H]], 494 ( {T == []} 495 -> [] 496 ; {T = [Last]} 497 -> [' or "~w"'-[Last] ] 498 ; [', '-[]], 499 oneof(T) 500 )
Extract parameters (GET and POST) from HTTP requests
This module is used to extract the value of GET or POST parameters from an HTTP request. The typical usage is e.g.,
http_dispatch.pl
dispatches requests to predicates. */