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) 2007-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_dispatch, 37 [ http_dispatch/1, % +Request 38 http_handler/3, % +Path, +Predicate, +Options 39 http_delete_handler/1, % +Path 40 http_reply_file/3, % +File, +Options, +Request 41 http_redirect/3, % +How, +Path, +Request 42 http_404/2, % +Options, +Request 43 http_switch_protocol/2, % :Goal, +Options 44 http_current_handler/2, % ?Path, ?Pred 45 http_current_handler/3, % ?Path, ?Pred, -Options 46 http_location_by_id/2, % +ID, -Location 47 http_link_to_id/3, % +ID, +Parameters, -HREF 48 http_reload_with_parameters/3, % +Request, +Parameters, -HREF 49 http_safe_file/2 % +Spec, +Options 50 ]). 51:- use_module(library(option)). 52:- use_module(library(lists)). 53:- use_module(library(time)). 54:- use_module(library(error)). 55:- use_module(library(settings)). 56:- use_module(library(uri)). 57:- use_module(library(apply)). 58:- use_module(library(http/mimetype)). 59:- use_module(library(http/http_path)). 60:- use_module(library(http/http_header)). 61:- use_module(library(http/thread_httpd)). 62 63:- predicate_options(http_404/2, 1, [index(any)]). 64:- predicate_options(http_reply_file/3, 2, 65 [ cache(boolean), 66 mime_type(any), 67 static_gzip(boolean), 68 pass_to(http_safe_file/2, 2), 69 headers(list) 70 ]). 71:- predicate_options(http_safe_file/2, 2, [unsafe(boolean)]). 72:- predicate_options(http_switch_protocol/2, 2, []).
95:- setting(http:time_limit, nonneg, 300,
96 'Time limit handling a single query (0=infinite)').
http_path.pl
. If an HTTP
request arrives at the server that matches Path, Closure
is called with one extra argument: the parsed HTTP request.
Options is a list containing the following options:
http_authenticate.pl
provides
a plugin for user/password based Basic
HTTP
authentication.Transfer-encoding: chunked
if the client
allows for it.true
on a prefix-handler (see prefix), possible
children are masked. This can be used to (temporary)
overrule part of the tree.methods([Method])
. Using method(*)
allows for all methods.:- http_handler(/, http_404([index('index.html')]), [spawn(my_pool),prefix]).
infinite
, default
or a positive number
(seconds). If default
, the value from the setting
http:time_limit
is taken. The default of this
setting is 300 (5 minutes). See setting/2.Note that http_handler/3 is normally invoked as a directive and processed using term-expansion. Using term-expansion ensures proper update through make/0 when the specification is modified. We do not expand when the cross-referencer is running to ensure proper handling of the meta-call.
188:- dynamic handler/4. % Path, Action, IsPrefix, Options 189:- multifile handler/4. 190:- dynamic generation/1. 191 192:- meta_predicate 193 http_handler( , , ), 194 http_current_handler( , ), 195 http_current_handler( , , ), 196 http_switch_protocol( , ). 197 198http_handler(Path, Pred, Options) :- 199 compile_handler(Path, Pred, Options, Clause), 200 next_generation, 201 assert(Clause). 202 203:- multifile 204 system:term_expansion/2. 205 206systemterm_expansion((:- http_handler(Path, Pred, Options)), Clause) :- 207 \+ current_prolog_flag(xref, true), 208 prolog_load_context(module, M), 209 compile_handler(Path, M:Pred, Options, Clause), 210 next_generation.
225http_delete_handler(id(Id)) :- 226 !, 227 clause(handler(_Path, _:Pred, _, Options), true, Ref), 228 functor(Pred, DefID, _), 229 option(id(Id0), Options, DefID), 230 Id == Id0, 231 erase(Ref), 232 next_generation. 233http_delete_handler(path(Path)) :- 234 !, 235 retractall(handler(Path, _Pred, _, _Options)), 236 next_generation. 237http_delete_handler(Path) :- 238 http_delete_handler(path(Path)).
246next_generation :- 247 retractall(id_location_cache(_,_)), 248 with_mutex(http_dispatch, next_generation_unlocked). 249 250next_generation_unlocked :- 251 retract(generation(G0)), 252 !, 253 G is G0 + 1, 254 assert(generation(G)). 255next_generation_unlocked :- 256 assert(generation(1)). 257 258current_generation(G) :- 259 with_mutex(http_dispatch, generation(G)), 260 !. 261current_generation(0).
271compile_handler(Path, Pred, Options0, 272 http_dispatch:handler(Path1, Pred, IsPrefix, Options)) :- 273 check_path(Path, Path1), 274 ( select(prefix, Options0, Options1) 275 -> IsPrefix = true 276 ; IsPrefix = false, 277 Options1 = Options0 278 ), 279 Pred = M:_, 280 maplist(qualify_option(M), Options1, Options2), 281 combine_methods(Options2, Options). 282 283qualify_option(M, condition(Pred), condition(M:Pred)) :- 284 Pred \= _:_, !. 285qualify_option(_, Option, Option).
method(M)
and methods(MList)
options into a single
methods(MList)
option.292combine_methods(Options0, Options) :- 293 collect_methods(Options0, Options1, Methods), 294 ( Methods == [] 295 -> Options = Options0 296 ; append(Methods, Flat), 297 sort(Flat, Unique), 298 ( memberchk('*', Unique) 299 -> Final = '*' 300 ; Final = Unique 301 ), 302 Options = [methods(Final)|Options1] 303 ). 304 305collect_methods([], [], []). 306collect_methods([method(M)|T0], T, [[M]|TM]) :- 307 !, 308 ( M == '*' 309 -> true 310 ; must_be_method(M) 311 ), 312 collect_methods(T0, T, TM). 313collect_methods([methods(M)|T0], T, [M|TM]) :- 314 !, 315 must_be(list, M), 316 maplist(must_be_method, M), 317 collect_methods(T0, T, TM). 318collect_methods([H|T0], [H|T], TM) :- 319 !, 320 collect_methods(T0, T, TM). 321 322must_be_method(M) :- 323 must_be(atom, M), 324 ( method(M) 325 -> true 326 ; domain_error(http_method, M) 327 ). 328 329method(get). 330method(put). 331method(head). 332method(post). 333method(delete). 334method(patch). 335method(options). 336method(trace).
Similar to absolute_file_name/3, Relative can be a term Component/Component/...
352check_path(Path, Path) :- 353 atom(Path), 354 !, 355 ( sub_atom(Path, 0, _, _, /) 356 -> true 357 ; domain_error(absolute_http_location, Path) 358 ). 359check_path(Alias, AliasOut) :- 360 compound(Alias), 361 Alias =.. [Name, Relative], 362 !, 363 to_atom(Relative, Local), 364 ( sub_atom(Local, 0, _, _, /) 365 -> domain_error(relative_location, Relative) 366 ; AliasOut =.. [Name, Local] 367 ). 368check_path(PathSpec, _) :- 369 type_error(path_or_alias, PathSpec). 370 371to_atom(Atom, Atom) :- 372 atom(Atom), 373 !. 374to_atom(Path, Atom) :- 375 phrase(path_to_list(Path), Components), 376 !, 377 atomic_list_concat(Components, '/', Atom). 378to_atom(Path, _) :- 379 ground(Path), 380 !, 381 type_error(relative_location, Path). 382to_atom(Path, _) :- 383 instantiation_error(Path). 384 385path_to_list(Var) --> 386 { var(Var), 387 !, 388 fail 389 }. 390path_to_list(A/B) --> 391 path_to_list(A), 392 path_to_list(B). 393path_to_list(Atom) --> 394 { atom(Atom) }, 395 [Atom].
403http_dispatch(Request) :-
404 memberchk(path(Path), Request),
405 find_handler(Path, Pred, Options),
406 supports_method(Request, Options),
407 authentication(Options, Request, Fields),
408 append(Fields, Request, AuthRequest),
409 action(Pred, AuthRequest, Options).
417http_current_handler(Path, Closure) :- 418 atom(Path), 419 !, 420 path_tree(Tree), 421 find_handler(Tree, Path, Closure, _). 422http_current_handler(Path, M:C) :- 423 handler(Spec, M:C, _, _), 424 http_absolute_location(Spec, Path, []).
431http_current_handler(Path, Closure, Options) :- 432 atom(Path), 433 !, 434 path_tree(Tree), 435 find_handler(Tree, Path, Closure, Options). 436http_current_handler(Path, M:C, Options) :- 437 handler(Spec, M:C, _, _), 438 http_absolute_location(Spec, Path, []), 439 path_tree(Tree), 440 find_handler(Tree, Path, _, Options).
463:- dynamic 464 id_location_cache/2. 465 466http_location_by_id(ID, Location) :- 467 must_be(ground, ID), 468 id_location_cache(ID, L0), 469 !, 470 Location = L0. 471http_location_by_id(ID, Location) :- 472 findall(P-L, location_by_id(ID, L, P), List), 473 keysort(List, RevSorted), 474 reverse(RevSorted, Sorted), 475 ( Sorted = [_-One] 476 -> assert(id_location_cache(ID, One)), 477 Location = One 478 ; List == [] 479 -> existence_error(http_handler_id, ID) 480 ; List = [P0-Best,P1-_|_] 481 -> ( P0 == P1 482 -> print_message(warning, 483 http_dispatch(ambiguous_id(ID, Sorted, Best))) 484 ; true 485 ), 486 assert(id_location_cache(ID, Best)), 487 Location = Best 488 ). 489 490location_by_id(ID, Location, Priority) :- 491 location_by_id_raw(ID, L0, Priority), 492 to_path(L0, Location). 493 494to_path(prefix(Path0), Path) :- % old style prefix notation 495 !, 496 add_prefix(Path0, Path). 497to_path(Path0, Path) :- 498 atomic(Path0), % old style notation 499 !, 500 add_prefix(Path0, Path). 501to_path(Spec, Path) :- % new style notation 502 http_absolute_location(Spec, Path, []). 503 504add_prefix(P0, P) :- 505 ( catch(setting(http:prefix, Prefix), _, fail), 506 Prefix \== '' 507 -> atom_concat(Prefix, P0, P) 508 ; P = P0 509 ). 510 511location_by_id_raw(ID, Location, Priority) :- 512 handler(Location, _, _, Options), 513 option(id(ID), Options), 514 option(priority(P0), Options, 0), 515 Priority is P0+1000. % id(ID) takes preference over predicate 516location_by_id_raw(ID, Location, Priority) :- 517 handler(Location, M:C, _, Options), 518 option(priority(Priority), Options, 0), 519 functor(C, PN, _), 520 ( ID = M:PN 521 ; ID = PN 522 ), 523 !.
root(user_details)
) is irrelevant in this equation and
HTTP locations can thus be moved freely without breaking this
code fragment.
:- http_handler(root(user_details), user_details, []). user_details(Request) :- http_parameters(Request, [ user_id(ID) ]), ... user_link(ID) --> { user_name(ID, Name), http_link_to_id(user_details, [id(ID)], HREF) }, html(a([class(user), href(HREF)], Name)).
566http_link_to_id(HandleID, path_postfix(File), HREF) :- 567 !, 568 http_location_by_id(HandleID, HandlerLocation), 569 uri_encoded(path, File, EncFile), 570 directory_file_path(HandlerLocation, EncFile, Location), 571 uri_data(path, Components, Location), 572 uri_components(HREF, Components). 573http_link_to_id(HandleID, Parameters, HREF) :- 574 must_be(list, Parameters), 575 http_location_by_id(HandleID, Location), 576 uri_data(path, Components, Location), 577 uri_query_components(String, Parameters), 578 uri_data(search, Components, String), 579 uri_components(HREF, Components).
586http_reload_with_parameters(Request, NewParams, HREF) :- 587 memberchk(path(Path), Request), 588 ( memberchk(search(Params), Request) 589 -> true 590 ; Params = [] 591 ), 592 merge_options(NewParams, Params, AllParams), 593 uri_query_components(Search, AllParams), 594 uri_data(path, Data, Path), 595 uri_data(search, Data, Search), 596 uri_components(HREF, Data). 597 598 599% hook into html_write:attribute_value//1. 600 601:- multifile 602 html_write:expand_attribute_value//1. 603 604html_writeexpand_attribute_value(location_by_id(ID)) --> 605 { http_location_by_id(ID, Location) }, 606 html_write:html_quoted_attribute(Location).
http_authenticate.pl
provides an implementation thereof.
618:- multifile 619 http:authenticate/3. 620 621authentication([], _, []). 622authentication([authentication(Type)|Options], Request, Fields) :- 623 !, 624 ( http:authenticate(Type, Request, XFields) 625 -> append(XFields, More, Fields), 626 authentication(Options, Request, More) 627 ; memberchk(path(Path), Request), 628 permission_error(access, http_location, Path) 629 ). 630authentication([_|Options], Request, Fields) :- 631 authentication(Options, Request, Fields).
prefix(Path)
handlers, use the
longest.
If there is a handler for /dir/
and the requested path is
/dir
, find_handler/3 throws a http_reply exception, causing
the wrapper to generate a 301 (Moved Permanently) reply.
650find_handler(Path, Action, Options) :- 651 path_tree(Tree), 652 ( find_handler(Tree, Path, Action, Options), 653 eval_condition(Options) 654 -> true 655 ; \+ sub_atom(Path, _, _, 0, /), 656 atom_concat(Path, /, Dir), 657 find_handler(Tree, Dir, Action, Options) 658 -> throw(http_reply(moved(Dir))) 659 ; throw(error(existence_error(http_location, Path), _)) 660 ). 661 662 663find_handler([node(prefix(Prefix), PAction, POptions, Children)|_], 664 Path, Action, Options) :- 665 sub_atom(Path, 0, _, After, Prefix), 666 !, 667 ( option(hide_children(false), POptions, false), 668 find_handler(Children, Path, Action, Options) 669 -> true 670 ; Action = PAction, 671 path_info(After, Path, POptions, Options) 672 ). 673find_handler([node(Path, Action, Options, _)|_], Path, Action, Options) :- !. 674find_handler([_|Tree], Path, Action, Options) :- 675 find_handler(Tree, Path, Action, Options). 676 677path_info(0, _, Options, 678 [prefix(true)|Options]) :- !. 679path_info(After, Path, Options, 680 [path_info(PathInfo),prefix(true)|Options]) :- 681 sub_atom(Path, _, After, 0, PathInfo). 682 683eval_condition(Options) :- 684 ( memberchk(condition(Cond), Options) 685 -> catch(, E, (print_message(warning, E), fail)) 686 ; true 687 ).
698supports_method(Request, Options) :- 699 ( option(methods(Methods), Options) 700 -> ( Methods == '*' 701 -> true 702 ; memberchk(method(Method), Request), 703 memberchk(Method, Methods) 704 ) 705 ; true 706 ), 707 !. 708supports_method(Request, _Options) :- 709 memberchk(path(Location), Request), 710 memberchk(method(Method), Request), 711 permission_error(http_method, Method, Location).
time_limit
, chunked
and spawn
.
721action(Action, Request, Options) :- 722 memberchk(chunked, Options), 723 !, 724 format('Transfer-encoding: chunked~n'), 725 spawn_action(Action, Request, Options). 726action(Action, Request, Options) :- 727 spawn_action(Action, Request, Options). 728 729spawn_action(Action, Request, Options) :- 730 option(spawn(Spawn), Options), 731 !, 732 spawn_options(Spawn, SpawnOption), 733 http_spawn(time_limit_action(Action, Request, Options), SpawnOption). 734spawn_action(Action, Request, Options) :- 735 time_limit_action(Action, Request, Options). 736 737spawn_options([], []) :- !. 738spawn_options(Pool, Options) :- 739 atom(Pool), 740 !, 741 Options = [pool(Pool)]. 742spawn_options(List, List). 743 744time_limit_action(Action, Request, Options) :- 745 ( option(time_limit(TimeLimit), Options), 746 TimeLimit \== default 747 -> true 748 ; setting(http:time_limit, TimeLimit) 749 ), 750 number(TimeLimit), 751 TimeLimit > 0, 752 !, 753 call_with_time_limit(TimeLimit, call_action(Action, Request, Options)). 754time_limit_action(Action, Request, Options) :- 755 call_action(Action, Request, Options).
762call_action(reply_file(File, FileOptions), Request, _Options) :- 763 !, 764 http_reply_file(File, FileOptions, Request). 765call_action(Pred, Request, Options) :- 766 memberchk(path_info(PathInfo), Options), 767 !, 768 call_action(Pred, [path_info(PathInfo)|Request]). 769call_action(Pred, Request, _Options) :- 770 call_action(Pred, Request). 771 772call_action(Pred, Request) :- 773 ( call(Pred, Request) 774 -> true 775 ; extend(Pred, [Request], Goal), 776 throw(error(goal_failed(Goal), _)) 777 ). 778 779extend(Var, _, Var) :- 780 var(Var), 781 !. 782extend(M:G0, Extra, M:G) :- 783 extend(G0, Extra, G). 784extend(G0, Extra, G) :- 785 G0 =.. List, 786 append(List, Extra, List2), 787 G =.. List2.
true
(default), handle If-modified-since and send
modification time.false
) and, in addition to the plain
file, there is a .gz
file that is not older than the
plain file and the client acceps gzip
encoding, send
the compressed file with Transfer-encoding: gzip
.false
(default), validate that FileSpec does not
contain references to parent directories. E.g.,
specifications such as www('../../etc/passwd')
are
not allowed.
If caching is not disabled, it processes the request headers
If-modified-since
and Range
.
823http_reply_file(File, Options, Request) :- 824 http_safe_file(File, Options), 825 absolute_file_name(File, Path, 826 [ access(read) 827 ]), 828 ( option(cache(true), Options, true) 829 -> ( memberchk(if_modified_since(Since), Request), 830 time_file(Path, Time), 831 catch(http_timestamp(Time, Since), _, fail) 832 -> throw(http_reply(not_modified)) 833 ; true 834 ), 835 ( memberchk(range(Range), Request) 836 -> Reply = file(Type, Path, Range) 837 ; option(static_gzip(true), Options), 838 accepts_encoding(Request, gzip), 839 file_name_extension(Path, gz, PathGZ), 840 access_file(PathGZ, read), 841 time_file(PathGZ, TimeGZ), 842 time_file(Path, Time), 843 TimeGZ >= Time 844 -> Reply = gzip_file(Type, PathGZ) 845 ; Reply = file(Type, Path) 846 ) 847 ; Reply = tmp_file(Type, Path) 848 ), 849 ( option(mime_type(Type), Options) 850 -> true 851 ; file_mime_type(Path, Type) 852 -> true 853 ; Type = text/plain % fallback type 854 ), 855 option(headers(Headers), Options, []), 856 throw(http_reply(Reply, Headers)). 857 858accepts_encoding(Request, Enc) :- 859 memberchk(accept_encoding(Accept), Request), 860 split_string(Accept, ",", " ", Parts), 861 member(Part, Parts), 862 split_string(Part, ";", " ", [EncS|_]), 863 atom_string(Enc, EncS).
alias(Sub)
, than Sub cannot
have references to parent directories.
876http_safe_file(File, _) :- 877 var(File), 878 !, 879 instantiation_error(File). 880http_safe_file(_, Options) :- 881 option(unsafe(true), Options, false), 882 !. 883http_safe_file(File, _) :- 884 http_safe_file(File). 885 886http_safe_file(File) :- 887 compound(File), 888 functor(File, _, 1), 889 !, 890 arg(1, File, Name), 891 safe_name(Name, File). 892http_safe_file(Name) :- 893 ( is_absolute_file_name(Name) 894 -> permission_error(read, file, Name) 895 ; true 896 ), 897 safe_name(Name, Name). 898 899safe_name(Name, _) :- 900 must_be(atom, Name), 901 prolog_to_os_filename(FileName, Name), 902 \+ unsafe_name(FileName), 903 !. 904safe_name(_, Spec) :- 905 permission_error(read, file, Spec). 906 907unsafe_name(Name) :- Name == '..'. 908unsafe_name(Name) :- sub_atom(Name, 0, _, _, '../'). 909unsafe_name(Name) :- sub_atom(Name, _, _, _, '/../'). 910unsafe_name(Name) :- sub_atom(Name, _, _, 0, '/..').
:- http_handler(root(.), http_redirect(moved, myapp('index.html')), []).
930http_redirect(How, To, Request) :-
931 ( To = location_by_id(Id)
932 -> http_location_by_id(Id, URL)
933 ; memberchk(path(Base), Request),
934 http_absolute_location(To, URL, [relative_to(Base)])
935 ),
936 must_be(oneof([moved, moved_temporary, see_other]), How),
937 Term =.. [How,URL],
938 throw(http_reply(Term)).
953http_404(Options, Request) :- 954 option(index(Index), Options), 955 \+ ( option(path_info(PathInfo), Request), 956 PathInfo \== '' 957 ), 958 !, 959 http_redirect(moved, Index, Request). 960http_404(_Options, Request) :- 961 option(path(Path), Request), 962 !, 963 throw(http_reply(not_found(Path))). 964http_404(_Options, Request) :- 965 domain_error(http_request, Request).
"HTTP 101 Switching Protocols"
reply. After sending
the reply, the HTTP library calls call(Goal, InStream,
OutStream)
, where InStream and OutStream are the raw streams to
the HTTP client. This allows the communication to continue using
an an alternative protocol.
If Goal fails or throws an exception, the streams are closed by
the server. Otherwise Goal is responsible for closing the
streams. Note that Goal runs in the HTTP handler thread.
Typically, the handler should be registered using the spawn
option if http_handler/3 or Goal must call thread_create/3 to
allow the HTTP worker to return to the worker pool.
The streams use binary (octet) encoding and have their I/O timeout set to the server timeout (default 60 seconds). The predicate set_stream/2 can be used to change the encoding, change or cancel the timeout.
This predicate interacts with the server library by throwing an exception.
The following options are supported:
headers(+Headers)
.999% @throws http_reply(switch_protocol(Goal, Options)) 1000 1001http_switch_protocol(Goal, Options) :- 1002 throw(http_reply(switching_protocols(Goal, Options))). 1003 1004 1005 /******************************* 1006 * PATH COMPILATION * 1007 *******************************/
node(PathOrPrefix, Action, Options, Children)
The tree is a potentially complicated structure. It is cached in a global variable. Note that this cache is per-thread, so each worker thread holds a copy of the tree. If handler facts are changed the generation is incremented using next_generation/0 and each worker thread will re-compute the tree on the next ocasion.
1023path_tree(Tree) :- 1024 current_generation(G), 1025 nb_current(http_dispatch_tree, G-Tree), 1026 !. % Avoid existence error 1027path_tree(Tree) :- 1028 path_tree_nocache(Tree), 1029 current_generation(G), 1030 nb_setval(http_dispatch_tree, G-Tree). 1031 1032path_tree_nocache(Tree) :- 1033 findall(Prefix, prefix_handler(Prefix, _, _), Prefixes0), 1034 sort(Prefixes0, Prefixes), 1035 prefix_tree(Prefixes, [], PTree), 1036 prefix_options(PTree, [], OPTree), 1037 add_paths_tree(OPTree, Tree). 1038 1039prefix_handler(Prefix, Action, Options) :- 1040 handler(Spec, Action, true, Options), 1041 Error = error(existence_error(http_alias,_),_), 1042 catch(http_absolute_location(Spec, Prefix, []), Error, 1043 ( print_message(warning, Error), 1044 fail 1045 )).
1051prefix_tree([], Tree, Tree). 1052prefix_tree([H|T], Tree0, Tree) :- 1053 insert_prefix(H, Tree0, Tree1), 1054 prefix_tree(T, Tree1, Tree). 1055 1056insert_prefix(Prefix, Tree0, Tree) :- 1057 select(P-T, Tree0, Tree1), 1058 sub_atom(Prefix, 0, _, _, P), 1059 !, 1060 insert_prefix(Prefix, T, T1), 1061 Tree = [P-T1|Tree1]. 1062insert_prefix(Prefix, Tree, [Prefix-[]|Tree]).
1071prefix_options([], _, []). 1072prefix_options([P-C|T0], DefOptions, 1073 [node(prefix(P), Action, Options, Children)|T]) :- 1074 once(prefix_handler(P, Action, Options0)), 1075 merge_options(Options0, DefOptions, Options), 1076 delete(Options, id(_), InheritOpts), 1077 prefix_options(C, InheritOpts, Children), 1078 prefix_options(T0, DefOptions, T).
1085add_paths_tree(OPTree, Tree) :- 1086 findall(path(Path, Action, Options), 1087 plain_path(Path, Action, Options), 1088 Triples), 1089 add_paths_tree(Triples, OPTree, Tree). 1090 1091add_paths_tree([], Tree, Tree). 1092add_paths_tree([path(Path, Action, Options)|T], Tree0, Tree) :- 1093 add_path_tree(Path, Action, Options, [], Tree0, Tree1), 1094 add_paths_tree(T, Tree1, Tree).
1102plain_path(Path, Action, Options) :-
1103 handler(Spec, Action, false, Options),
1104 catch(http_absolute_location(Spec, Path, []), E,
1105 (print_message(error, E), fail)).
1114add_path_tree(Path, Action, Options0, DefOptions, [], 1115 [node(Path, Action, Options, [])]) :- 1116 !, 1117 merge_options(Options0, DefOptions, Options). 1118add_path_tree(Path, Action, Options, _, 1119 [node(prefix(Prefix), PA, DefOptions, Children0)|RestTree], 1120 [node(prefix(Prefix), PA, DefOptions, Children)|RestTree]) :- 1121 sub_atom(Path, 0, _, _, Prefix), 1122 !, 1123 delete(DefOptions, id(_), InheritOpts), 1124 add_path_tree(Path, Action, Options, InheritOpts, Children0, Children). 1125add_path_tree(Path, Action, Options1, DefOptions, [H0|T], [H|T]) :- 1126 H0 = node(Path, _, Options2, _), 1127 option(priority(P1), Options1, 0), 1128 option(priority(P2), Options2, 0), 1129 P1 >= P2, 1130 !, 1131 merge_options(Options1, DefOptions, Options), 1132 H = node(Path, Action, Options, []). 1133add_path_tree(Path, Action, Options, DefOptions, [H|T0], [H|T]) :- 1134 add_path_tree(Path, Action, Options, DefOptions, T0, T). 1135 1136 1137 /******************************* 1138 * MESSAGES * 1139 *******************************/ 1140 1141:- multifile 1142 prolog:message/3. 1143 1144prologmessage(http_dispatch(ambiguous_id(ID, _List, Selected))) --> 1145 [ 'HTTP dispatch: ambiguous handler ID ~q (selected ~q)'-[ID, Selected] 1146 ]. 1147 1148 1149 /******************************* 1150 * XREF * 1151 *******************************/ 1152 1153:- multifile 1154 prolog:meta_goal/2. 1155:- dynamic 1156 prolog:meta_goal/2. 1157 1158prologmeta_goal(http_handler(_, G, _), [G+1]). 1159prologmeta_goal(http_current_handler(_, G), [G+1]). 1160 1161 1162 /******************************* 1163 * EDIT * 1164 *******************************/ 1165 1166% Allow edit(Location) to edit the implementation for an HTTP location. 1167 1168:- multifile 1169 prolog_edit:locate/3. 1170 1171prolog_edit:locate(Path, Spec, Location) :- 1172 atom(Path), 1173 sub_atom(Path, 0, _, _, /), 1174 Pred = _M:_H, 1175 catch(http_current_handler(Path, Pred), _, fail), 1176 closure_name_arity(Pred, 1, PI), 1177 prolog_edit:locate(PI, Spec, Location). 1178 1179closure_name_arity(M:Term, Extra, M:Name/Arity) :- 1180 !, 1181 callable(Term), 1182 functor(Term, Name, Arity0), 1183 Arity is Arity0 + Extra. 1184closure_name_arity(Term, Extra, Name/Arity) :- 1185 callable(Term), 1186 functor(Term, Name, Arity0), 1187 Arity is Arity0 + Extra. 1188 1189 1190 /******************************* 1191 * CACHE CLEANUP * 1192 *******************************/ 1193 1194:- listen(settings(changed(http:prefix, _, _)), 1195 next_generation). 1196 1197:- multifile 1198 user:message_hook/3. 1199:- dynamic 1200 user:message_hook/3. 1201 1202user:message_hook(make(done(Reload)), _Level, _Lines) :- 1203 Reload \== [], 1204 next_generation, 1205 fail
Dispatch requests in the HTTP server
This module can be placed between
http_wrapper.pl
and the application code to associate HTTP locations to predicates that serve the pages. In addition, it associates parameters with locations that deal with timeout handling and user authentication. The typical setup is:*/