36
37:- module(http_dispatch,
38 [ http_dispatch/1, 39 http_handler/3, 40 http_delete_handler/1, 41 http_request_expansion/2, 42 http_reply_file/3, 43 http_redirect/3, 44 http_404/2, 45 http_switch_protocol/2, 46 http_current_handler/2, 47 http_current_handler/3, 48 http_location_by_id/2, 49 http_link_to_id/3, 50 http_reload_with_parameters/3, 51 http_safe_file/2 52 ]). 53:- use_module(library(lists),
54 [ select/3, append/3, append/2, same_length/2, member/2,
55 last/2, delete/3
56 ]). 57:- autoload(library(apply),
58 [partition/4,maplist/3,maplist/2,include/3,exclude/3]). 59:- autoload(library(broadcast),[listen/2]). 60:- autoload(library(error),
61 [ must_be/2,
62 domain_error/2,
63 type_error/2,
64 instantiation_error/1,
65 existence_error/2,
66 permission_error/3
67 ]). 68:- autoload(library(filesex),[directory_file_path/3]). 69:- autoload(library(option),[option/3,option/2,merge_options/3]). 70:- autoload(library(pairs),[pairs_values/2]). 71:- autoload(library(time),[call_with_time_limit/2]). 72:- autoload(library(uri),
73 [ uri_encoded/3,
74 uri_data/3,
75 uri_components/2,
76 uri_query_components/2
77 ]). 78:- autoload(library(http/http_header),[http_timestamp/2]). 79:- autoload(library(http/http_path),[http_absolute_location/3]). 80:- autoload(library(http/mimetype),
81 [file_content_type/2,file_content_type/3]). 82:- autoload(library(http/thread_httpd),[http_spawn/2]). 83:- use_module(library(settings),[setting/4,setting/2]). 84
85:- predicate_options(http_404/2, 1, [index(any)]). 86:- predicate_options(http_reply_file/3, 2,
87 [ cache(boolean),
88 mime_type(any),
89 static_gzip(boolean),
90 pass_to(http_safe_file/2, 2),
91 headers(list)
92 ]). 93:- predicate_options(http_safe_file/2, 2, [unsafe(boolean)]). 94:- predicate_options(http_switch_protocol/2, 2, []). 95
120
121:- setting(http:time_limit, nonneg, 300,
122 'Time limit handling a single query (0=infinite)'). 123
228
229:- dynamic handler/4. 230:- multifile handler/4. 231:- dynamic generation/1. 232
233:- meta_predicate
234 http_handler(+, :, +),
235 http_current_handler(?, :),
236 http_current_handler(?, :, ?),
237 http_request_expansion(3, +),
238 http_switch_protocol(2, +). 239
240http_handler(Path, Pred, Options) :-
241 compile_handler(Path, Pred, Options, Clause),
242 next_generation,
243 assert(Clause).
244
245:- multifile
246 system:term_expansion/2. 247
248system:term_expansion((:- http_handler(Path, Pred, Options)), Clause) :-
249 \+ current_prolog_flag(xref, true),
250 prolog_load_context(module, M),
251 compile_handler(Path, M:Pred, Options, Clause),
252 next_generation.
253
254
266
267http_delete_handler(id(Id)) :-
268 !,
269 clause(handler(_Path, _:Pred, _, Options), true, Ref),
270 functor(Pred, DefID, _),
271 option(id(Id0), Options, DefID),
272 Id == Id0,
273 erase(Ref),
274 next_generation.
275http_delete_handler(path(Path)) :-
276 !,
277 retractall(handler(Path, _Pred, _, _Options)),
278 next_generation.
279http_delete_handler(Path) :-
280 http_delete_handler(path(Path)).
281
282
287
288next_generation :-
289 retractall(id_location_cache(_,_,_,_)),
290 with_mutex(http_dispatch, next_generation_unlocked).
291
292next_generation_unlocked :-
293 retract(generation(G0)),
294 !,
295 G is G0 + 1,
296 assert(generation(G)).
297next_generation_unlocked :-
298 assert(generation(1)).
299
300current_generation(G) :-
301 with_mutex(http_dispatch, generation(G)),
302 !.
303current_generation(0).
304
305
309
310compile_handler(Path, Pred, Options0,
311 http_dispatch:handler(Path1, Pred, IsPrefix, Options)) :-
312 check_path(Path, Path1, PathOptions),
313 check_id(Options0),
314 ( memberchk(segment_pattern(_), PathOptions)
315 -> IsPrefix = true,
316 Options1 = Options0
317 ; select(prefix, Options0, Options1)
318 -> IsPrefix = true
319 ; IsPrefix = false,
320 Options1 = Options0
321 ),
322 partition(ground, Options1, Options2, QueryOptions),
323 Pred = M:_,
324 maplist(qualify_option(M), Options2, Options3),
325 combine_methods(Options3, Options4),
326 ( QueryOptions == []
327 -> append(PathOptions, Options4, Options)
328 ; append(PathOptions, ['$extract'(QueryOptions)|Options4], Options)
329 ).
330
331qualify_option(M, condition(Pred), condition(M:Pred)) :-
332 Pred \= _:_, !.
333qualify_option(_, Option, Option).
334
339
340combine_methods(Options0, Options) :-
341 collect_methods(Options0, Options1, Methods),
342 ( Methods == []
343 -> Options = Options0
344 ; append(Methods, Flat),
345 sort(Flat, Unique),
346 ( memberchk('*', Unique)
347 -> Final = '*'
348 ; Final = Unique
349 ),
350 Options = [methods(Final)|Options1]
351 ).
352
353collect_methods([], [], []).
354collect_methods([method(M)|T0], T, [[M]|TM]) :-
355 !,
356 ( M == '*'
357 -> true
358 ; must_be_method(M)
359 ),
360 collect_methods(T0, T, TM).
361collect_methods([methods(M)|T0], T, [M|TM]) :-
362 !,
363 must_be(list, M),
364 maplist(must_be_method, M),
365 collect_methods(T0, T, TM).
366collect_methods([H|T0], [H|T], TM) :-
367 !,
368 collect_methods(T0, T, TM).
369
370must_be_method(M) :-
371 must_be(atom, M),
372 ( method(M)
373 -> true
374 ; domain_error(http_method, M)
375 ).
376
377method(get).
378method(put).
379method(head).
380method(post).
381method(delete).
382method(patch).
383method(options).
384method(trace).
385
386
403
404check_path(Path, Path, []) :-
405 atom(Path),
406 !,
407 ( sub_atom(Path, 0, _, _, /)
408 -> true
409 ; domain_error(absolute_http_location, Path)
410 ).
411check_path(Alias, AliasOut, Options) :-
412 compound(Alias),
413 Alias =.. [Name, Relative],
414 !,
415 local_path(Relative, Local, Options),
416 ( sub_atom(Local, 0, _, _, /)
417 -> domain_error(relative_location, Relative)
418 ; AliasOut =.. [Name, Local]
419 ).
420check_path(PathSpec, _, _) :-
421 type_error(path_or_alias, PathSpec).
422
423local_path(Atom, Atom, []) :-
424 atom(Atom),
425 !.
426local_path(Path, Atom, Options) :-
427 phrase(path_to_list(Path), Components),
428 !,
429 ( maplist(atom, Components)
430 -> atomic_list_concat(Components, '/', Atom),
431 Options = []
432 ; append(Pre, [Var|Rest], Components),
433 var(Var)
434 -> append(Pre, [''], PreSep),
435 atomic_list_concat(PreSep, '/', Atom),
436 Options = [segment_pattern([Var|Rest])]
437 ).
438local_path(Path, _, _) :-
439 ground(Path),
440 !,
441 type_error(relative_location, Path).
442local_path(Path, _, _) :-
443 instantiation_error(Path).
444
445path_to_list(Var) -->
446 { var(Var) },
447 !,
448 [Var].
449path_to_list(A/B) -->
450 !,
451 path_to_list(A),
452 path_to_list(B).
453path_to_list(Atom) -->
454 { atom(Atom) },
455 !,
456 [Atom].
457path_to_list(Value) -->
458 { must_be(atom, Value) }.
459
460check_id(Options) :-
461 memberchk(id(Id), Options),
462 !,
463 must_be(atom, Id).
464check_id(_).
465
466
489
490http_dispatch(Request) :-
491 memberchk(path(Path), Request),
492 find_handler(Path, Closure, Options),
493 supports_method(Request, Options),
494 expand_request(Request, Request1, Options),
495 extract_from_request(Request1, Options),
496 action(Closure, Request1, Options).
497
(Request, Options) :-
499 memberchk('$extract'(Fields), Options),
500 !,
501 extract_fields(Fields, Request).
502extract_from_request(_, _).
503
([], _).
505extract_fields([H|T], Request) :-
506 memberchk(H, Request),
507 extract_fields(T, Request).
508
509
528
529http_request_expansion(Goal, Rank) :-
530 throw(error(context_error(nodirective, http_request_expansion(Goal, Rank)), _)).
531
532:- multifile
533 request_expansion/2. 534
535system:term_expansion((:- http_request_expansion(Goal, Rank)),
536 http_dispatch:request_expansion(M:Callable, Rank)) :-
537 must_be(number, Rank),
538 prolog_load_context(module, M0),
539 strip_module(M0:Goal, M, Callable),
540 must_be(callable, Callable).
541
542request_expanders(Closures) :-
543 findall(Rank-Closure, request_expansion(Closure, Rank), Pairs),
544 keysort(Pairs, Sorted),
545 pairs_values(Sorted, Closures).
546
551
552expand_request(Request0, Request, Options) :-
553 request_expanders(Closures),
554 expand_request(Closures, Request0, Request, Options).
555
556expand_request([], Request, Request, _).
557expand_request([H|T], Request0, Request, Options) :-
558 expand_request1(H, Request0, Request1, Options),
559 expand_request(T, Request1, Request, Options).
560
561expand_request1(Closure, Request0, Request, Options) :-
562 call(Closure, Request0, Request, Options),
563 !.
564expand_request1(_, Request, Request, _).
565
566
571
572http_current_handler(Path, Closure) :-
573 atom(Path),
574 !,
575 path_tree(Tree),
576 find_handler(Tree, Path, Closure, _).
577http_current_handler(Path, M:C) :-
578 handler(Spec, M:C, _, _),
579 http_absolute_location(Spec, Path, []).
580
585
586http_current_handler(Path, Closure, Options) :-
587 atom(Path),
588 !,
589 path_tree(Tree),
590 find_handler(Tree, Path, Closure, Options).
591http_current_handler(Path, M:C, Options) :-
592 handler(Spec, M:C, _, _),
593 http_absolute_location(Spec, Path, []),
594 path_tree(Tree),
595 find_handler(Tree, Path, _, Options).
596
597
627
628:- dynamic
629 id_location_cache/4. 630
631http_location_by_id(ID, _) :-
632 \+ ground(ID),
633 !,
634 instantiation_error(ID).
635http_location_by_id(M:ID, Location) :-
636 compound(ID),
637 !,
638 compound_name_arguments(ID, Name, Argv),
639 http_location_by_id(M:Name, Argv, Location).
640http_location_by_id(M:ID, Location) :-
641 atom(ID),
642 must_be(atom, M),
643 !,
644 http_location_by_id(M:ID, -, Location).
645http_location_by_id(ID, Location) :-
646 compound(ID),
647 !,
648 compound_name_arguments(ID, Name, Argv),
649 http_location_by_id(Name, Argv, Location).
650http_location_by_id(ID, Location) :-
651 atom(ID),
652 !,
653 http_location_by_id(ID, -, Location).
654http_location_by_id(ID, _) :-
655 type_error(location_id, ID).
656
657http_location_by_id(ID, Argv, Location) :-
658 id_location_cache(ID, Argv, Segments, Path),
659 !,
660 add_segments(Path, Segments, Location).
661http_location_by_id(ID, Argv, Location) :-
662 findall(t(Priority, ArgvP, Segments, Prefix),
663 location_by_id(ID, Argv, ArgvP, Segments, Prefix, Priority),
664 List),
665 sort(1, >=, List, Sorted),
666 ( Sorted = [t(_,ArgvP,Segments,Path)]
667 -> assert(id_location_cache(ID,ArgvP,Segments,Path)),
668 Argv = ArgvP
669 ; List == []
670 -> existence_error(http_handler_id, ID)
671 ; List = [t(P0,ArgvP,Segments,Path),t(P1,_,_,_)|_]
672 -> ( P0 =:= P1
673 -> print_message(warning,
674 http_dispatch(ambiguous_id(ID, Sorted, Path)))
675 ; true
676 ),
677 assert(id_location_cache(ID,Argv,Segments,Path)),
678 Argv = ArgvP
679 ),
680 add_segments(Path, Segments, Location).
681
682add_segments(Path0, [], Path) :-
683 !,
684 Path = Path0.
685add_segments(Path0, Segments, Path) :-
686 maplist(uri_encoded(path), Segments, Encoded),
687 atomic_list_concat(Encoded, '/', Rest),
688 atom_concat(Path0, Rest, Path).
689
690location_by_id(ID, -, _, [], Location, Priority) :-
691 !,
692 location_by_id_raw(ID, L0, _Segments, Priority),
693 to_path(L0, Location).
694location_by_id(ID, Argv, ArgvP, Segments, Location, Priority) :-
695 location_by_id_raw(ID, L0, Segments, Priority),
696 include(var, Segments, ArgvP),
697 same_length(Argv, ArgvP),
698 to_path(L0, Location).
699
700to_path(prefix(Path0), Path) :- 701 !,
702 add_prefix(Path0, Path).
703to_path(Path0, Path) :-
704 atomic(Path0), 705 !,
706 add_prefix(Path0, Path).
707to_path(Spec, Path) :- 708 http_absolute_location(Spec, Path, []).
709
710add_prefix(P0, P) :-
711 ( catch(setting(http:prefix, Prefix), _, fail),
712 Prefix \== ''
713 -> atom_concat(Prefix, P0, P)
714 ; P = P0
715 ).
716
717location_by_id_raw(ID, Location, Pattern, Priority) :-
718 handler(Location, _, _, Options),
719 option(id(ID), Options),
720 option(priority(P0), Options, 0),
721 option(segment_pattern(Pattern), Options, []),
722 Priority is P0+1000. 723location_by_id_raw(ID, Location, Pattern, Priority) :-
724 handler(Location, M:C, _, Options),
725 option(priority(Priority), Options, 0),
726 functor(C, PN, _),
727 ( ID = M:PN
728 -> true
729 ; ID = PN
730 ),
731 option(segment_pattern(Pattern), Options, []).
732
780
781http_link_to_id(HandleID, path_postfix(File), HREF) :-
782 !,
783 http_location_by_id(HandleID, HandlerLocation),
784 uri_encoded(path, File, EncFile),
785 directory_file_path(HandlerLocation, EncFile, Location),
786 uri_data(path, Components, Location),
787 uri_components(HREF, Components).
788http_link_to_id(HandleID, Parameters, HREF) :-
789 must_be(list, Parameters),
790 http_location_by_id(HandleID, Location),
791 ( Parameters == []
792 -> HREF = Location
793 ; uri_data(path, Components, Location),
794 uri_query_components(String, Parameters),
795 uri_data(search, Components, String),
796 uri_components(HREF, Components)
797 ).
798
803
804http_reload_with_parameters(Request, NewParams, HREF) :-
805 memberchk(path(Path), Request),
806 ( memberchk(search(Params), Request)
807 -> true
808 ; Params = []
809 ),
810 merge_options(NewParams, Params, AllParams),
811 uri_query_components(Search, AllParams),
812 uri_data(path, Data, Path),
813 uri_data(search, Data, Search),
814 uri_components(HREF, Data).
815
816
818
819:- multifile
820 html_write:expand_attribute_value//1. 821
822html_write:expand_attribute_value(location_by_id(ID)) -->
823 { http_location_by_id(ID, Location) },
824 html_write:html_quoted_attribute(Location).
825html_write:expand_attribute_value(#(ID)) -->
826 { http_location_by_id(ID, Location) },
827 html_write:html_quoted_attribute(Location).
828
829
841
842:- multifile
843 http:authenticate/3. 844
845authentication([], _, []).
846authentication([authentication(Type)|Options], Request, Fields) :-
847 !,
848 ( http:authenticate(Type, Request, XFields)
849 -> append(XFields, More, Fields),
850 authentication(Options, Request, More)
851 ; memberchk(path(Path), Request),
852 permission_error(access, http_location, Path)
853 ).
854authentication([_|Options], Request, Fields) :-
855 authentication(Options, Request, Fields).
856
857:- http_request_expansion(auth_expansion, 100). 858
865
866auth_expansion(Request0, Request, Options) :-
867 authentication(Options, Request0, Extra),
868 append(Extra, Request0, Request).
869
885
886find_handler(Path, Action, Options) :-
887 path_tree(Tree),
888 ( find_handler(Tree, Path, Action, Options),
889 eval_condition(Options)
890 -> true
891 ; \+ sub_atom(Path, _, _, 0, /),
892 atom_concat(Path, /, Dir),
893 find_handler(Tree, Dir, Action, Options)
894 -> throw(http_reply(moved(Dir)))
895 ; throw(error(existence_error(http_location, Path), _))
896 ).
897
898
899find_handler([node(prefix(Prefix), PAction, POptions, Children)|_],
900 Path, Action, Options) :-
901 sub_atom(Path, 0, _, After, Prefix),
902 !,
903 ( option(hide_children(false), POptions, false),
904 find_handler(Children, Path, Action, Options)
905 -> true
906 ; member(segment_pattern(Pattern, PatAction, PatOptions), POptions),
907 copy_term(t(Pattern,PatAction,PatOptions), t(Pattern2,Action,Options)),
908 match_segments(After, Path, Pattern2)
909 -> true
910 ; PAction \== nop
911 -> Action = PAction,
912 path_info(After, Path, POptions, Options)
913 ).
914find_handler([node(Path, Action, Options, _)|_], Path, Action, Options) :- !.
915find_handler([_|Tree], Path, Action, Options) :-
916 find_handler(Tree, Path, Action, Options).
917
918path_info(0, _, Options,
919 [prefix(true)|Options]) :- !.
920path_info(After, Path, Options,
921 [path_info(PathInfo),prefix(true)|Options]) :-
922 sub_atom(Path, _, After, 0, PathInfo).
923
924match_segments(After, Path, [Var]) :-
925 !,
926 sub_atom(Path, _, After, 0, Var).
927match_segments(After, Path, Pattern) :-
928 sub_atom(Path, _, After, 0, PathInfo),
929 split_string(PathInfo, "/", "", Segments),
930 match_segment_pattern(Pattern, Segments).
931
932match_segment_pattern([], []).
933match_segment_pattern([Var], Segments) :-
934 !,
935 atomic_list_concat(Segments, '/', Var).
936match_segment_pattern([H0|T0], [H|T]) :-
937 atom_string(H0, H),
938 match_segment_pattern(T0, T).
939
940
941eval_condition(Options) :-
942 ( memberchk(condition(Cond), Options)
943 -> catch(Cond, E, (print_message(warning, E), fail))
944 ; true
945 ).
946
947
955
956supports_method(Request, Options) :-
957 ( option(methods(Methods), Options)
958 -> ( Methods == '*'
959 -> true
960 ; memberchk(method(Method), Request),
961 memberchk(Method, Methods)
962 )
963 ; true
964 ),
965 !.
966supports_method(Request, _Options) :-
967 memberchk(path(Location), Request),
968 memberchk(method(Method), Request),
969 permission_error(http_method, Method, Location).
970
971
978
979action(Action, Request, Options) :-
980 memberchk(chunked, Options),
981 !,
982 format('Transfer-encoding: chunked~n'),
983 spawn_action(Action, Request, Options).
984action(Action, Request, Options) :-
985 spawn_action(Action, Request, Options).
986
987spawn_action(Action, Request, Options) :-
988 option(spawn(Spawn), Options),
989 !,
990 spawn_options(Spawn, SpawnOption),
991 http_spawn(time_limit_action(Action, Request, Options), SpawnOption).
992spawn_action(Action, Request, Options) :-
993 time_limit_action(Action, Request, Options).
994
995spawn_options([], []) :- !.
996spawn_options(Pool, Options) :-
997 atom(Pool),
998 !,
999 Options = [pool(Pool)].
1000spawn_options(List, List).
1001
1002time_limit_action(Action, Request, Options) :-
1003 ( option(time_limit(TimeLimit), Options),
1004 TimeLimit \== default
1005 -> true
1006 ; setting(http:time_limit, TimeLimit)
1007 ),
1008 number(TimeLimit),
1009 TimeLimit > 0,
1010 !,
1011 call_with_time_limit(TimeLimit, call_action(Action, Request, Options)).
1012time_limit_action(Action, Request, Options) :-
1013 call_action(Action, Request, Options).
1014
1015
1019
1020call_action(reply_file(File, FileOptions), Request, _Options) :-
1021 !,
1022 http_reply_file(File, FileOptions, Request).
1023call_action(Pred, Request, Options) :-
1024 memberchk(path_info(PathInfo), Options),
1025 !,
1026 call_action(Pred, [path_info(PathInfo)|Request]).
1027call_action(Pred, Request, _Options) :-
1028 call_action(Pred, Request).
1029
1030call_action(Pred, Request) :-
1031 ( call(Pred, Request)
1032 -> true
1033 ; extend(Pred, [Request], Goal),
1034 throw(error(goal_failed(Goal), _))
1035 ).
1036
1037extend(Var, _, Var) :-
1038 var(Var),
1039 !.
1040extend(M:G0, Extra, M:G) :-
1041 extend(G0, Extra, G).
1042extend(G0, Extra, G) :-
1043 G0 =.. List,
1044 append(List, Extra, List2),
1045 G =.. List2.
1046
1080
1081http_reply_file(File, Options, Request) :-
1082 http_safe_file(File, Options),
1083 absolute_file_name(File, Path,
1084 [ access(read)
1085 ]),
1086 ( option(cache(true), Options, true)
1087 -> ( memberchk(if_modified_since(Since), Request),
1088 time_file(Path, Time),
1089 catch(http_timestamp(Time, Since), _, fail)
1090 -> throw(http_reply(not_modified))
1091 ; true
1092 ),
1093 ( memberchk(range(Range), Request)
1094 -> Reply = file(Type, Path, Range)
1095 ; option(static_gzip(true), Options),
1096 accepts_encoding(Request, gzip),
1097 file_name_extension(Path, gz, PathGZ),
1098 access_file(PathGZ, read),
1099 time_file(PathGZ, TimeGZ),
1100 time_file(Path, Time),
1101 TimeGZ >= Time
1102 -> Reply = gzip_file(Type, PathGZ)
1103 ; Reply = file(Type, Path)
1104 )
1105 ; Reply = tmp_file(Type, Path)
1106 ),
1107 ( option(mime_type(MediaType), Options)
1108 -> file_content_type(Path, MediaType, Type)
1109 ; file_content_type(Path, Type)
1110 -> true
1111 ; Type = text/plain 1112 ),
1113 option(headers(Headers), Options, []),
1114 throw(http_reply(Reply, Headers)).
1115
1116accepts_encoding(Request, Enc) :-
1117 memberchk(accept_encoding(Accept), Request),
1118 split_string(Accept, ",", " ", Parts),
1119 member(Part, Parts),
1120 split_string(Part, ";", " ", [EncS|_]),
1121 atom_string(Enc, EncS).
1122
1123
1133
1134http_safe_file(File, _) :-
1135 var(File),
1136 !,
1137 instantiation_error(File).
1138http_safe_file(_, Options) :-
1139 option(unsafe(true), Options, false),
1140 !.
1141http_safe_file(File, _) :-
1142 http_safe_file(File).
1143
1144http_safe_file(File) :-
1145 compound(File),
1146 functor(File, _, 1),
1147 !,
1148 arg(1, File, Name),
1149 safe_name(Name, File).
1150http_safe_file(Name) :-
1151 ( is_absolute_file_name(Name)
1152 -> permission_error(read, file, Name)
1153 ; true
1154 ),
1155 safe_name(Name, Name).
1156
1157safe_name(Name, _) :-
1158 must_be(atom, Name),
1159 prolog_to_os_filename(FileName, Name),
1160 \+ unsafe_name(FileName),
1161 !.
1162safe_name(_, Spec) :-
1163 permission_error(read, file, Spec).
1164
1165unsafe_name(Name) :- Name == '..'.
1166unsafe_name(Name) :- sub_atom(Name, 0, _, _, '../').
1167unsafe_name(Name) :- sub_atom(Name, _, _, _, '/../').
1168unsafe_name(Name) :- sub_atom(Name, _, _, 0, '/..').
1169
1170
1188
1189http_redirect(How, To, Request) :-
1190 must_be(oneof([moved, moved_temporary, see_other]), How),
1191 must_be(ground, To),
1192 ( id_location(To, URL)
1193 -> true
1194 ; memberchk(path(Base), Request),
1195 http_absolute_location(To, URL, [relative_to(Base)])
1196 ),
1197 Term =.. [How,URL],
1198 throw(http_reply(Term)).
1199
1200id_location(location_by_id(Id), URL) :-
1201 http_location_by_id(Id, URL).
1202id_location(#(Id), URL) :-
1203 http_location_by_id(Id, URL).
1204id_location(#(Id)+Parameters, URL) :-
1205 http_link_to_id(Id, Parameters, URL).
1206
1207
1219
1220http_404(Options, Request) :-
1221 option(index(Index), Options),
1222 \+ ( option(path_info(PathInfo), Request),
1223 PathInfo \== ''
1224 ),
1225 !,
1226 http_redirect(moved, Index, Request).
1227http_404(_Options, Request) :-
1228 option(path(Path), Request),
1229 !,
1230 throw(http_reply(not_found(Path))).
1231http_404(_Options, Request) :-
1232 domain_error(http_request, Request).
1233
1234
1265
1267
1268http_switch_protocol(Goal, Options) :-
1269 throw(http_reply(switching_protocols(Goal, Options))).
1270
1271
1272 1275
1289
1290path_tree(Tree) :-
1291 current_generation(G),
1292 nb_current(http_dispatch_tree, G-Tree),
1293 !. 1294path_tree(Tree) :-
1295 path_tree_nocache(Tree),
1296 current_generation(G),
1297 nb_setval(http_dispatch_tree, G-Tree).
1298
1299path_tree_nocache(Tree) :-
1300 findall(Prefix, prefix_handler(Prefix, _, _, _), Prefixes0),
1301 sort(Prefixes0, Prefixes),
1302 prefix_tree(Prefixes, [], PTree),
1303 prefix_options(PTree, [], OPTree),
1304 add_paths_tree(OPTree, Tree).
1305
1306prefix_handler(Prefix, Action, Options, Priority-PLen) :-
1307 handler(Spec, Action, true, Options),
1308 ( memberchk(priority(Priority), Options)
1309 -> true
1310 ; Priority = 0
1311 ),
1312 ( memberchk(segment_pattern(Pattern), Options)
1313 -> length(Pattern, PLen)
1314 ; PLen = 0
1315 ),
1316 Error = error(existence_error(http_alias,_),_),
1317 catch(http_absolute_location(Spec, Prefix, []), Error,
1318 ( print_message(warning, Error),
1319 fail
1320 )).
1321
1325
1326prefix_tree([], Tree, Tree).
1327prefix_tree([H|T], Tree0, Tree) :-
1328 insert_prefix(H, Tree0, Tree1),
1329 prefix_tree(T, Tree1, Tree).
1330
1331insert_prefix(Prefix, Tree0, Tree) :-
1332 select(P-T, Tree0, Tree1),
1333 sub_atom(Prefix, 0, _, _, P),
1334 !,
1335 insert_prefix(Prefix, T, T1),
1336 Tree = [P-T1|Tree1].
1337insert_prefix(Prefix, Tree, [Prefix-[]|Tree]).
1338
1339
1345
1346prefix_options([], _, []).
1347prefix_options([Prefix-C|T0], DefOptions,
1348 [node(prefix(Prefix), Action, PrefixOptions, Children)|T]) :-
1349 findall(h(A,O,P), prefix_handler(Prefix,A,O,P), Handlers),
1350 sort(3, >=, Handlers, Handlers1),
1351 Handlers1 = [h(_,_,P0)|_],
1352 same_priority_handlers(Handlers1, P0, Same),
1353 option_patterns(Same, SegmentPatterns, Action),
1354 last(Same, h(_, Options0, _-_)),
1355 merge_options(Options0, DefOptions, Options),
1356 append(SegmentPatterns, Options, PrefixOptions),
1357 exclude(no_inherit, Options, InheritOpts),
1358 prefix_options(C, InheritOpts, Children),
1359 prefix_options(T0, DefOptions, T).
1360
1361no_inherit(id(_)).
1362no_inherit('$extract'(_)).
1363
1364same_priority_handlers([H|T0], P, [H|T]) :-
1365 H = h(_,_,P0-_),
1366 P = P0-_,
1367 !,
1368 same_priority_handlers(T0, P, T).
1369same_priority_handlers(_, _, []).
1370
1371option_patterns([], [], nop).
1372option_patterns([h(A,_,_-0)|_], [], A) :-
1373 !.
1374option_patterns([h(A,O,_)|T0], [segment_pattern(P,A,O)|T], AF) :-
1375 memberchk(segment_pattern(P), O),
1376 option_patterns(T0, T, AF).
1377
1378
1382
1383add_paths_tree(OPTree, Tree) :-
1384 findall(path(Path, Action, Options),
1385 plain_path(Path, Action, Options),
1386 Triples),
1387 add_paths_tree(Triples, OPTree, Tree).
1388
1389add_paths_tree([], Tree, Tree).
1390add_paths_tree([path(Path, Action, Options)|T], Tree0, Tree) :-
1391 add_path_tree(Path, Action, Options, [], Tree0, Tree1),
1392 add_paths_tree(T, Tree1, Tree).
1393
1394
1399
1400plain_path(Path, Action, Options) :-
1401 handler(Spec, Action, false, Options),
1402 catch(http_absolute_location(Spec, Path, []), E,
1403 (print_message(error, E), fail)).
1404
1405
1411
1412add_path_tree(Path, Action, Options0, DefOptions, [],
1413 [node(Path, Action, Options, [])]) :-
1414 !,
1415 merge_options(Options0, DefOptions, Options).
1416add_path_tree(Path, Action, Options, _,
1417 [node(prefix(Prefix), PA, DefOptions, Children0)|RestTree],
1418 [node(prefix(Prefix), PA, DefOptions, Children)|RestTree]) :-
1419 sub_atom(Path, 0, _, _, Prefix),
1420 !,
1421 delete(DefOptions, id(_), InheritOpts),
1422 add_path_tree(Path, Action, Options, InheritOpts, Children0, Children).
1423add_path_tree(Path, Action, Options1, DefOptions, [H0|T], [H|T]) :-
1424 H0 = node(Path, _, Options2, _),
1425 option(priority(P1), Options1, 0),
1426 option(priority(P2), Options2, 0),
1427 P1 >= P2,
1428 !,
1429 merge_options(Options1, DefOptions, Options),
1430 H = node(Path, Action, Options, []).
1431add_path_tree(Path, Action, Options, DefOptions, [H|T0], [H|T]) :-
1432 add_path_tree(Path, Action, Options, DefOptions, T0, T).
1433
1434
1435 1438
1439:- multifile
1440 prolog:message/3. 1441
1442prolog:message(http_dispatch(ambiguous_id(ID, _List, Selected))) -->
1443 [ 'HTTP dispatch: ambiguous handler ID ~q (selected ~q)'-[ID, Selected]
1444 ].
1445
1446
1447 1450
1451:- multifile
1452 prolog:meta_goal/2. 1453:- dynamic
1454 prolog:meta_goal/2. 1455
1456prolog:meta_goal(http_handler(_, G, _), [G+1]).
1457prolog:meta_goal(http_current_handler(_, G), [G+1]).
1458
1459
1460 1463
1465
1466:- multifile
1467 prolog_edit:locate/3. 1468
1469prolog_edit:locate(Path, Spec, Location) :-
1470 atom(Path),
1471 sub_atom(Path, 0, _, _, /),
1472 Pred = _M:_H,
1473 catch(http_current_handler(Path, Pred), _, fail),
1474 closure_name_arity(Pred, 1, PI),
1475 prolog_edit:locate(PI, Spec, Location).
1476
1477closure_name_arity(M:Term, Extra, M:Name/Arity) :-
1478 !,
1479 callable(Term),
1480 functor(Term, Name, Arity0),
1481 Arity is Arity0 + Extra.
1482closure_name_arity(Term, Extra, Name/Arity) :-
1483 callable(Term),
1484 functor(Term, Name, Arity0),
1485 Arity is Arity0 + Extra.
1486
1487
1488 1491
1492:- listen(settings(changed(http:prefix, _, _)),
1493 next_generation). 1494
1495:- multifile
1496 user:message_hook/3. 1497:- dynamic
1498 user:message_hook/3. 1499
1500user:message_hook(make(done(Reload)), _Level, _Lines) :-
1501 Reload \== [],
1502 next_generation,
1503 fail