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) 2002-2020, 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_header, 37 [ http_read_request/2, % +Stream, -Request 38 http_read_reply_header/2, % +Stream, -Reply 39 http_reply/2, % +What, +Stream 40 http_reply/3, % +What, +Stream, +HdrExtra 41 http_reply/4, % +What, +Stream, +HdrExtra, -Code 42 http_reply/5, % +What, +Stream, +HdrExtra, +Context, 43 % -Code 44 http_reply/6, % +What, +Stream, +HdrExtra, +Context, 45 % +Request, -Code 46 http_reply_header/3, % +Stream, +What, +HdrExtra 47 http_status_reply/4, % +Status, +Out, +HdrExtra, -Code 48 http_status_reply/5, % +Status, +Out, +HdrExtra, 49 % +Context, -Code 50 51 http_timestamp/2, % +Time, -HTTP string 52 53 http_post_data/3, % +Stream, +Data, +HdrExtra 54 55 http_read_header/2, % +Fd, -Header 56 http_parse_header/2, % +Codes, -Header 57 http_parse_header_value/3, % +Header, +HeaderValue, -MediaTypes 58 http_join_headers/3, % +Default, +InHdr, -OutHdr 59 http_update_encoding/3, % +HeaderIn, -Encoding, -HeaderOut 60 http_update_connection/4, % +HeaderIn, +Request, -Connection, -HeaderOut 61 http_update_transfer/4 % +HeaderIn, +Request, -Transfer, -HeaderOut 62 ]). 63:- autoload(html_write, 64 [ print_html/2, print_html/1, page/4, html/3, 65 html_print_length/2 66 ]). 67:- autoload(http_exception,[map_exception_to_http_status/4]). 68:- autoload(mimepack,[mime_pack/3]). 69:- autoload(mimetype,[file_mime_type/2]). 70:- autoload(library(apply),[maplist/2]). 71:- autoload(library(base64),[base64/2]). 72:- autoload(library(debug),[debug/3,debugging/1]). 73:- autoload(library(error),[syntax_error/1,domain_error/2]). 74:- autoload(library(lists),[append/3,member/2,select/3,delete/3]). 75:- autoload(library(memfile), 76 [ new_memory_file/1, open_memory_file/3, 77 free_memory_file/1, open_memory_file/4, 78 size_memory_file/3 79 ]). 80:- autoload(library(option),[option/3,option/2]). 81:- autoload(library(pairs),[pairs_values/2]). 82:- autoload(library(readutil), 83 [read_line_to_codes/2,read_line_to_codes/3]). 84:- autoload(library(sgml_write),[xml_write/3]). 85:- autoload(library(socket),[gethostname/1]). 86:- autoload(library(uri), 87 [ uri_components/2, uri_data/3, uri_encoded/3, uri_query_components/2 88 ]). 89:- autoload(library(url),[parse_url_search/2]). 90:- autoload(library(dcg/basics), 91 [ integer/3, atom/3, whites/2, blanks_to_nl/2, string/3, 92 number/3, blanks/2, float/3, nonblanks/3, eos/2 93 ]). 94:- use_module(library(settings),[setting/4,setting/2]). 95 96:- multifile 97 http:status_page/3, % +Status, +Context, -HTML 98 http:status_reply/3, % +Status, -Reply, +Options 99 http:serialize_reply/2, % +Reply, -Body 100 http:post_data_hook/3, % +Data, +Out, +HdrExtra 101 http:mime_type_encoding/2. % +MimeType, -Encoding 102 103% see http_update_transfer/4. 104 105:- setting(http:chunked_transfer, oneof([never,on_request,if_possible]), 106 on_request, 'When to use Transfer-Encoding: Chunked').
116:- discontiguous 117 term_expansion/2. 118 119 120 /******************************* 121 * READ REQUEST * 122 *******************************/
end_of_file
if FdIn is at the end of input.
130http_read_request(In, Request) :-
131 catch(read_line_to_codes(In, Codes), E, true),
132 ( var(E)
133 -> ( Codes == end_of_file
134 -> debug(http(header), 'end-of-file', []),
135 Request = end_of_file
136 ; debug(http(header), 'First line: ~s', [Codes]),
137 Request = [input(In)|Request1],
138 phrase(request(In, Request1), Codes),
139 ( Request1 = [unknown(Text)|_]
140 -> string_codes(S, Text),
141 syntax_error(http_request(S))
142 ; true
143 )
144 )
145 ; ( debugging(http(request))
146 -> message_to_string(E, Msg),
147 debug(http(request), "Exception reading 1st line: ~s", [Msg])
148 ; true
149 ),
150 Request = end_of_file
151 ).
159http_read_reply_header(In, [input(In)|Reply]) :- 160 read_line_to_codes(In, Codes), 161 ( Codes == end_of_file 162 -> debug(http(header), 'end-of-file', []), 163 throw(error(syntax(http_reply_header, end_of_file), _)) 164 ; debug(http(header), 'First line: ~s~n', [Codes]), 165 ( phrase(reply(In, Reply), Codes) 166 -> true 167 ; atom_codes(Header, Codes), 168 syntax_error(http_reply_header(Header)) 169 ) 170 ). 171 172 173 /******************************* 174 * FORMULATE REPLY * 175 *******************************/
html_write.pl
file
, but do not include modification time224http_reply(What, Out) :- 225 http_reply(What, Out, [connection(close)], _). 226 227http_reply(Data, Out, HdrExtra) :- 228 http_reply(Data, Out, HdrExtra, _Code). 229 230http_reply(Data, Out, HdrExtra, Code) :- 231 http_reply(Data, Out, HdrExtra, [], Code). 232 233http_reply(Data, Out, HdrExtra, Context, Code) :- 234 http_reply(Data, Out, HdrExtra, Context, [method(get)], Code). 235 236http_reply(Data, Out, HdrExtra, _Context, Request, Code) :- 237 byte_count(Out, C0), 238 memberchk(method(Method), Request), 239 catch(http_reply_data(Data, Out, HdrExtra, Method, Code), E, true), 240 !, 241 ( var(E) 242 -> true 243 ; ( E = error(io_error(write,_), _) 244 ; E = error(socket_error(_,_), _) 245 ) 246 -> byte_count(Out, C1), 247 Sent is C1 - C0, 248 throw(error(http_write_short(Data, Sent), _)) 249 ; E = error(timeout_error(write, _), _) 250 -> throw(E) 251 ; map_exception_to_http_status(E, Status, NewHdr, NewContext), 252 http_status_reply(Status, Out, NewHdr, NewContext, Request, Code) 253 ). 254http_reply(Status, Out, HdrExtra, Context, Request, Code) :- 255 http_status_reply(Status, Out, HdrExtra, Context, Request, Code). 256 257:- meta_predicate 258 if_no_head( , ).
267http_reply_data(Data, Out, HdrExtra, Method, Code) :- 268 http_reply_data_(Data, Out, HdrExtra, Method, Code), 269 flush_output(Out). 270 271http_reply_data_(html(HTML), Out, HdrExtra, Method, Code) :- 272 !, 273 phrase(reply_header(html(HTML), HdrExtra, Code), Header), 274 send_reply_header(Out, Header), 275 if_no_head(print_html(Out, HTML), Method). 276http_reply_data_(file(Type, File), Out, HdrExtra, Method, Code) :- 277 !, 278 phrase(reply_header(file(Type, File), HdrExtra, Code), Header), 279 reply_file(Out, File, Header, Method). 280http_reply_data_(gzip_file(Type, File), Out, HdrExtra, Method, Code) :- 281 !, 282 phrase(reply_header(gzip_file(Type, File), HdrExtra, Code), Header), 283 reply_file(Out, File, Header, Method). 284http_reply_data_(file(Type, File, Range), Out, HdrExtra, Method, Code) :- 285 !, 286 phrase(reply_header(file(Type, File, Range), HdrExtra, Code), Header), 287 reply_file_range(Out, File, Header, Range, Method). 288http_reply_data_(tmp_file(Type, File), Out, HdrExtra, Method, Code) :- 289 !, 290 phrase(reply_header(tmp_file(Type, File), HdrExtra, Code), Header), 291 reply_file(Out, File, Header, Method). 292http_reply_data_(bytes(Type, Bytes), Out, HdrExtra, Method, Code) :- 293 !, 294 phrase(reply_header(bytes(Type, Bytes), HdrExtra, Code), Header), 295 send_reply_header(Out, Header), 296 if_no_head(format(Out, '~s', [Bytes]), Method). 297http_reply_data_(stream(In, Len), Out, HdrExtra, Method, Code) :- 298 !, 299 phrase(reply_header(cgi_data(Len), HdrExtra, Code), Header), 300 copy_stream(Out, In, Header, Method, 0, end). 301http_reply_data_(cgi_stream(In, Len), Out, HdrExtra, Method, Code) :- 302 !, 303 http_read_header(In, CgiHeader), 304 seek(In, 0, current, Pos), 305 Size is Len - Pos, 306 http_join_headers(HdrExtra, CgiHeader, Hdr2), 307 phrase(reply_header(cgi_data(Size), Hdr2, Code), Header), 308 copy_stream(Out, In, Header, Method, 0, end). 309 310if_no_head(_, head) :- 311 !. 312if_no_head(Goal, _) :- 313 call(Goal). 314 315reply_file(Out, _File, Header, head) :- 316 !, 317 send_reply_header(Out, Header). 318reply_file(Out, File, Header, _) :- 319 setup_call_cleanup( 320 open(File, read, In, [type(binary)]), 321 copy_stream(Out, In, Header, 0, end), 322 close(In)). 323 324reply_file_range(Out, _File, Header, _Range, head) :- 325 !, 326 send_reply_header(Out, Header). 327reply_file_range(Out, File, Header, bytes(From, To), _) :- 328 setup_call_cleanup( 329 open(File, read, In, [type(binary)]), 330 copy_stream(Out, In, Header, From, To), 331 close(In)). 332 333copy_stream(Out, _, Header, head, _, _) :- 334 !, 335 send_reply_header(Out, Header). 336copy_stream(Out, In, Header, _, From, To) :- 337 copy_stream(Out, In, Header, From, To). 338 339copy_stream(Out, In, Header, From, To) :- 340 ( From == 0 341 -> true 342 ; seek(In, From, bof, _) 343 ), 344 peek_byte(In, _), 345 send_reply_header(Out, Header), 346 ( To == end 347 -> copy_stream_data(In, Out) 348 ; Len is To - From, 349 copy_stream_data(In, Out, Len) 350 ).
Status can be one of the following:
basic(Realm)
digest(Digest)
authorise(basic(Realm))
. Deprecated.384http_status_reply(Status, Out, Options) :- 385 _{header:HdrExtra, context:Context, code:Code, method:Method} :< Options, 386 http_status_reply(Status, Out, HdrExtra, Context, [method(Method)], Code). 387 388http_status_reply(Status, Out, HdrExtra, Code) :- 389 http_status_reply(Status, Out, HdrExtra, [], Code). 390 391http_status_reply(Status, Out, HdrExtra, Context, Code) :- 392 http_status_reply(Status, Out, HdrExtra, Context, [method(get)], Code). 393 394http_status_reply(Status, Out, HdrExtra, Context, Request, Code) :- 395 option(method(Method), Request, get), 396 parsed_accept(Request, Accept), 397 status_reply_flush(Status, Out, 398 _{ context: Context, 399 method: Method, 400 code: Code, 401 accept: Accept, 402 header: HdrExtra 403 }). 404 405parsed_accept(Request, Accept) :- 406 memberchk(accept(Accept0), Request), 407 http_parse_header_value(accept, Accept0, Accept1), 408 !, 409 Accept = Accept1. 410parsed_accept(_, [ media(text/html, [], 0.1, []), 411 media(_, [], 0.01, []) 412 ]). 413 414status_reply_flush(Status, Out, Options) :- 415 status_reply(Status, Out, Options), 416 !, 417 flush_output(Out).
430% Replies without content 431status_reply(no_content, Out, Options) :- 432 !, 433 phrase(reply_header(status(no_content), Options), Header), 434 send_reply_header(Out, Header). 435status_reply(switching_protocols(_Goal,SwitchOptions), Out, Options) :- 436 !, 437 ( option(headers(Extra1), SwitchOptions) 438 -> true 439 ; option(header(Extra1), SwitchOptions, []) 440 ), 441 http_join_headers(Options.header, Extra1, HdrExtra), 442 phrase(reply_header(status(switching_protocols), 443 Options.put(header,HdrExtra)), Header), 444 send_reply_header(Out, Header). 445status_reply(authorise(basic, ''), Out, Options) :- 446 !, 447 status_reply(authorise(basic), Out, Options). 448status_reply(authorise(basic, Realm), Out, Options) :- 449 !, 450 status_reply(authorise(basic(Realm)), Out, Options). 451status_reply(not_modified, Out, Options) :- 452 !, 453 phrase(reply_header(status(not_modified), Options), Header), 454 send_reply_header(Out, Header). 455% aliases (compatibility) 456status_reply(busy, Out, Options) :- 457 status_reply(service_unavailable(busy), Out, Options). 458status_reply(unavailable(Why), Out, Options) :- 459 status_reply(service_unavailable(Why), Out, Options). 460status_reply(resource_error(Why), Out, Options) :- 461 status_reply(service_unavailable(Why), Out, Options). 462% replies with content 463status_reply(Status, Out, Options) :- 464 status_has_content(Status), 465 status_page_hook(Status, Reply, Options), 466 serialize_body(Reply, Body), 467 Status =.. List, 468 append(List, [Body], ExList), 469 ExStatus =.. ExList, 470 phrase(reply_header(ExStatus, Options), Header), 471 send_reply_header(Out, Header), 472 reply_status_body(Out, Body, Options).
479status_has_content(created(_Location)). 480status_has_content(moved(_To)). 481status_has_content(moved_temporary(_To)). 482status_has_content(gone(_URL)). 483status_has_content(see_other(_To)). 484status_has_content(bad_request(_ErrorTerm)). 485status_has_content(authorise(_Method)). 486status_has_content(forbidden(_URL)). 487status_has_content(not_found(_URL)). 488status_has_content(method_not_allowed(_Method, _URL)). 489status_has_content(not_acceptable(_Why)). 490status_has_content(server_error(_ErrorTerm)). 491status_has_content(service_unavailable(_Why)).
502serialize_body(Reply, Body) :- 503 http:serialize_reply(Reply, Body), 504 !. 505serialize_body(html_tokens(Tokens), body(text/html, utf8, Content)) :- 506 !, 507 with_output_to(string(Content), print_html(Tokens)). 508serialize_body(Reply, Reply) :- 509 Reply = body(_,_,_), 510 !. 511serialize_body(Reply, _) :- 512 domain_error(http_reply_body, Reply). 513 514reply_status_body(_, _, Options) :- 515 Options.method == head, 516 !. 517reply_status_body(Out, body(_Type, Encoding, Content), _Options) :- 518 ( Encoding == octet 519 -> format(Out, '~s', [Content]) 520 ; setup_call_cleanup( 521 set_stream(Out, encoding(Encoding)), 522 format(Out, '~s', [Content]), 523 set_stream(Out, encoding(octet))) 524 ).
551status_page_hook(Term, Reply, Options) :- 552 Context = Options.context, 553 functor(Term, Name, _), 554 status_number_fact(Name, Code), 555 ( Options.code = Code, 556 http:status_reply(Term, Reply, Options) 557 ; http:status_page(Term, Context, HTML), 558 Reply = html_tokens(HTML) 559 ; http:status_page(Code, Context, HTML), % deprecated 560 Reply = html_tokens(HTML) 561 ), 562 !. 563status_page_hook(created(Location), html_tokens(HTML), _Options) :- 564 phrase(page([ title('201 Created') 565 ], 566 [ h1('Created'), 567 p(['The document was created ', 568 a(href(Location), ' Here') 569 ]), 570 \address 571 ]), 572 HTML). 573status_page_hook(moved(To), html_tokens(HTML), _Options) :- 574 phrase(page([ title('301 Moved Permanently') 575 ], 576 [ h1('Moved Permanently'), 577 p(['The document has moved ', 578 a(href(To), ' Here') 579 ]), 580 \address 581 ]), 582 HTML). 583status_page_hook(moved_temporary(To), html_tokens(HTML), _Options) :- 584 phrase(page([ title('302 Moved Temporary') 585 ], 586 [ h1('Moved Temporary'), 587 p(['The document is currently ', 588 a(href(To), ' Here') 589 ]), 590 \address 591 ]), 592 HTML). 593status_page_hook(gone(URL), html_tokens(HTML), _Options) :- 594 phrase(page([ title('410 Resource Gone') 595 ], 596 [ h1('Resource Gone'), 597 p(['The document has been removed ', 598 a(href(URL), ' from here') 599 ]), 600 \address 601 ]), 602 HTML). 603status_page_hook(see_other(To), html_tokens(HTML), _Options) :- 604 phrase(page([ title('303 See Other') 605 ], 606 [ h1('See Other'), 607 p(['See other document ', 608 a(href(To), ' Here') 609 ]), 610 \address 611 ]), 612 HTML). 613status_page_hook(bad_request(ErrorTerm), html_tokens(HTML), _Options) :- 614 '$messages':translate_message(ErrorTerm, Lines, []), 615 phrase(page([ title('400 Bad Request') 616 ], 617 [ h1('Bad Request'), 618 p(\html_message_lines(Lines)), 619 \address 620 ]), 621 HTML). 622status_page_hook(authorise(_Method), html_tokens(HTML), _Options):- 623 phrase(page([ title('401 Authorization Required') 624 ], 625 [ h1('Authorization Required'), 626 p(['This server could not verify that you ', 627 'are authorized to access the document ', 628 'requested. Either you supplied the wrong ', 629 'credentials (e.g., bad password), or your ', 630 'browser doesn\'t understand how to supply ', 631 'the credentials required.' 632 ]), 633 \address 634 ]), 635 HTML). 636status_page_hook(forbidden(URL), html_tokens(HTML), _Options) :- 637 phrase(page([ title('403 Forbidden') 638 ], 639 [ h1('Forbidden'), 640 p(['You don\'t have permission to access ', URL, 641 ' on this server' 642 ]), 643 \address 644 ]), 645 HTML). 646status_page_hook(not_found(URL), html_tokens(HTML), _Options) :- 647 phrase(page([ title('404 Not Found') 648 ], 649 [ h1('Not Found'), 650 p(['The requested URL ', tt(URL), 651 ' was not found on this server' 652 ]), 653 \address 654 ]), 655 HTML). 656status_page_hook(method_not_allowed(Method,URL), html_tokens(HTML), _Options) :- 657 upcase_atom(Method, UMethod), 658 phrase(page([ title('405 Method not allowed') 659 ], 660 [ h1('Method not allowed'), 661 p(['The requested URL ', tt(URL), 662 ' does not support method ', tt(UMethod), '.' 663 ]), 664 \address 665 ]), 666 HTML). 667status_page_hook(not_acceptable(WhyHTML), html_tokens(HTML), _Options) :- 668 phrase(page([ title('406 Not Acceptable') 669 ], 670 [ h1('Not Acceptable'), 671 WhyHTML, 672 \address 673 ]), 674 HTML). 675status_page_hook(server_error(ErrorTerm), html_tokens(HTML), _Options) :- 676 '$messages':translate_message(ErrorTerm, Lines, []), 677 phrase(page([ title('500 Internal server error') 678 ], 679 [ h1('Internal server error'), 680 p(\html_message_lines(Lines)), 681 \address 682 ]), 683 HTML). 684status_page_hook(service_unavailable(Why), html_tokens(HTML), _Options) :- 685 phrase(page([ title('503 Service Unavailable') 686 ], 687 [ h1('Service Unavailable'), 688 \unavailable(Why), 689 \address 690 ]), 691 HTML). 692 (busy)--> 694 html(p(['The server is temporarily out of resources, ', 695 'please try again later'])). 696unavailable(error(Formal,Context)) --> 697 { '$messages':translate_message(error(Formal,Context), Lines, []) }, 698 html_message_lines(Lines). 699unavailable(HTML) --> 700 html(HTML). 701 702html_message_lines([]) --> 703 []. 704html_message_lines([nl|T]) --> 705 !, 706 html([br([])]), 707 html_message_lines(T). 708html_message_lines([flush]) --> 709 []. 710html_message_lines([ansi(_Style,Fmt,Args)|T]) --> 711 !, 712 { format(string(S), Fmt, Args) 713 }, 714 html([S]), 715 html_message_lines(T). 716html_message_lines([url(Pos)|T]) --> 717 !, 718 msg_url(Pos), 719 html_message_lines(T). 720html_message_lines([url(URL, Label)|T]) --> 721 !, 722 html(a(href(URL), Label)), 723 html_message_lines(T). 724html_message_lines([Fmt-Args|T]) --> 725 !, 726 { format(string(S), Fmt, Args) 727 }, 728 html([S]), 729 html_message_lines(T). 730html_message_lines([Fmt|T]) --> 731 !, 732 { format(string(S), Fmt, []) 733 }, 734 html([S]), 735 html_message_lines(T). 736 737msg_url(File:Line:Pos) --> 738 !, 739 html([File, :, Line, :, Pos]). 740msg_url(File:Line) --> 741 !, 742 html([File, :, Line]). 743msg_url(File) --> 744 html([File]).
751http_join_headers([], H, H). 752http_join_headers([H|T], Hdr0, Hdr) :- 753 functor(H, N, A), 754 functor(H2, N, A), 755 member(H2, Hdr0), 756 !, 757 http_join_headers(T, Hdr0, Hdr). 758http_join_headers([H|T], Hdr0, [H|Hdr]) :- 759 http_join_headers(T, Hdr0, Hdr).
771http_update_encoding(Header0, utf8, [content_type(Type)|Header]) :- 772 select(content_type(Type0), Header0, Header), 773 sub_atom(Type0, 0, _, _, 'text/'), 774 !, 775 ( sub_atom(Type0, S, _, _, ';') 776 -> sub_atom(Type0, 0, S, _, B) 777 ; B = Type0 778 ), 779 atom_concat(B, '; charset=UTF-8', Type). 780http_update_encoding(Header, Encoding, Header) :- 781 memberchk(content_type(Type), Header), 782 ( sub_atom_icasechk(Type, _, 'utf-8') 783 -> Encoding = utf8 784 ; http:mime_type_encoding(Type, Encoding) 785 -> true 786 ; mime_type_encoding(Type, Encoding) 787 ). 788http_update_encoding(Header, octet, Header).
795mime_type_encoding('application/json', utf8). 796mime_type_encoding('application/jsonrequest', utf8). 797mime_type_encoding('application/x-prolog', utf8). 798mime_type_encoding('application/n-quads', utf8). 799mime_type_encoding('application/n-triples', utf8). 800mime_type_encoding('application/sparql-query', utf8). 801mime_type_encoding('application/trig', utf8).
format('Content-type: <MIME type>~n')
. This hook is called before
mime_type_encoding/2. This default defines utf8
for JSON and
Turtle derived application/
MIME types.817http_update_connection(CgiHeader, Request, Connect, 818 [connection(Connect)|Rest]) :- 819 select(connection(CgiConn), CgiHeader, Rest), 820 !, 821 connection(Request, ReqConnection), 822 join_connection(ReqConnection, CgiConn, Connect). 823http_update_connection(CgiHeader, Request, Connect, 824 [connection(Connect)|CgiHeader]) :- 825 connection(Request, Connect). 826 827join_connection(Keep1, Keep2, Connection) :- 828 ( downcase_atom(Keep1, 'keep-alive'), 829 downcase_atom(Keep2, 'keep-alive') 830 -> Connection = 'Keep-Alive' 831 ; Connection = close 832 ).
839connection(Header, Close) :-
840 ( memberchk(connection(Connection), Header)
841 -> Close = Connection
842 ; memberchk(http_version(1-X), Header),
843 X >= 1
844 -> Close = 'Keep-Alive'
845 ; Close = close
846 ).
never
, even explitic requests are
ignored. If on_request
, chunked encoding is used if requested
through the CGI header and allowed by the client. If
if_possible
, chunked encoding is used whenever the client
allows for it, which is interpreted as the client supporting
HTTP 1.1 or higher.
Chunked encoding is more space efficient and allows the client to start processing partial results. The drawback is that errors lead to incomplete pages instead of a nicely formatted complete page.
865http_update_transfer(Request, CgiHeader, Transfer, Header) :- 866 setting(http:chunked_transfer, When), 867 http_update_transfer(When, Request, CgiHeader, Transfer, Header). 868 869http_update_transfer(never, _, CgiHeader, none, Header) :- 870 !, 871 delete(CgiHeader, transfer_encoding(_), Header). 872http_update_transfer(_, _, CgiHeader, none, Header) :- 873 memberchk(location(_), CgiHeader), 874 !, 875 delete(CgiHeader, transfer_encoding(_), Header). 876http_update_transfer(_, Request, CgiHeader, Transfer, Header) :- 877 select(transfer_encoding(CgiTransfer), CgiHeader, Rest), 878 !, 879 transfer(Request, ReqConnection), 880 join_transfer(ReqConnection, CgiTransfer, Transfer), 881 ( Transfer == none 882 -> Header = Rest 883 ; Header = [transfer_encoding(Transfer)|Rest] 884 ). 885http_update_transfer(if_possible, Request, CgiHeader, Transfer, Header) :- 886 transfer(Request, Transfer), 887 Transfer \== none, 888 !, 889 Header = [transfer_encoding(Transfer)|CgiHeader]. 890http_update_transfer(_, _, CgiHeader, none, CgiHeader). 891 892join_transfer(chunked, chunked, chunked) :- !. 893join_transfer(_, _, none).
900transfer(Header, Transfer) :-
901 ( memberchk(transfer_encoding(Transfer0), Header)
902 -> Transfer = Transfer0
903 ; memberchk(http_version(1-X), Header),
904 X >= 1
905 -> Transfer = chunked
906 ; Transfer = none
907 ).
916content_length_in_encoding(Enc, Stream, Bytes) :- 917 stream_property(Stream, position(Here)), 918 setup_call_cleanup( 919 open_null_stream(Out), 920 ( set_stream(Out, encoding(Enc)), 921 catch(copy_stream_data(Stream, Out), _, fail), 922 flush_output(Out), 923 byte_count(Out, Bytes) 924 ), 925 ( close(Out, [force(true)]), 926 set_stream_position(Stream, Here) 927 )). 928 929 930 /******************************* 931 * POST SUPPORT * 932 *******************************/
http_client.pl
to send the
POST data to the server. Data is one of:
html(+Tokens)
Result of html//1 from html_write.pl
xml(+Term)
Post the result of xml_write/3 using the Mime-type
text/xml
xml(+Type, +Term)
Post the result of xml_write/3 using the given Mime-type
and an empty option list to xml_write/3.xml(+Type, +Term, +Options)
Post the result of xml_write/3 using the given Mime-type
and option list for xml_write/3.file(+File)
Send contents of a file. Mime-type is determined by
file_mime_type/2.file(+Type, +File)
Send file with content of indicated mime-type.memory_file(+Type, +Handle)
Similar to file(+Type, +File)
, but using a memory file
instead of a real file. See new_memory_file/1.codes(+Codes)
As codes(text/plain, Codes)
.codes(+Type, +Codes)
Send Codes using the indicated MIME-type.bytes(+Type, +Bytes)
Send Bytes using the indicated MIME-type. Bytes is either a
string of character codes 0..255 or list of integers in the
range 0..255. Out-of-bound codes result in a representation
error exception.atom(+Atom)
As atom(text/plain, Atom)
.atom(+Type, +Atom)
Send Atom using the indicated MIME-type.cgi_stream(+Stream, +Len)
Read the input from Stream which,
like CGI data starts with a partial HTTP header. The fields of
this header are merged with the provided HdrExtra fields. The
first Len characters of Stream are used.form(+ListOfParameter)
Send data of the MIME type application/x-www-form-urlencoded as
produced by browsers issuing a POST request from an HTML form.
ListOfParameter is a list of Name=Value or Name(Value).form_data(+ListOfData)
Send data of the MIME type multipart/form-data
as produced
by browsers issuing a POST request from an HTML form using
enctype multipart/form-data
. ListOfData is the same as for
the List alternative described below. Below is an example.
Repository, etc. are atoms providing the value, while the last
argument provides a value from a file.
..., http_post([ protocol(http), host(Host), port(Port), path(ActionPath) ], form_data([ repository = Repository, dataFormat = DataFormat, baseURI = BaseURI, verifyData = Verify, data = file(File) ]), _Reply, []), ...,
1025http_post_data(Data, Out, HdrExtra) :- 1026 http:post_data_hook(Data, Out, HdrExtra), 1027 !. 1028http_post_data(html(HTML), Out, HdrExtra) :- 1029 !, 1030 phrase(post_header(html(HTML), HdrExtra), Header), 1031 send_request_header(Out, Header), 1032 print_html(Out, HTML). 1033http_post_data(xml(XML), Out, HdrExtra) :- 1034 !, 1035 http_post_data(xml(text/xml, XML, []), Out, HdrExtra). 1036http_post_data(xml(Type, XML), Out, HdrExtra) :- 1037 !, 1038 http_post_data(xml(Type, XML, []), Out, HdrExtra). 1039http_post_data(xml(Type, XML, Options), Out, HdrExtra) :- 1040 !, 1041 setup_call_cleanup( 1042 new_memory_file(MemFile), 1043 ( setup_call_cleanup( 1044 open_memory_file(MemFile, write, MemOut), 1045 xml_write(MemOut, XML, Options), 1046 close(MemOut)), 1047 http_post_data(memory_file(Type, MemFile), Out, HdrExtra) 1048 ), 1049 free_memory_file(MemFile)). 1050http_post_data(file(File), Out, HdrExtra) :- 1051 !, 1052 ( file_mime_type(File, Type) 1053 -> true 1054 ; Type = text/plain 1055 ), 1056 http_post_data(file(Type, File), Out, HdrExtra). 1057http_post_data(file(Type, File), Out, HdrExtra) :- 1058 !, 1059 phrase(post_header(file(Type, File), HdrExtra), Header), 1060 send_request_header(Out, Header), 1061 setup_call_cleanup( 1062 open(File, read, In, [type(binary)]), 1063 copy_stream_data(In, Out), 1064 close(In)). 1065http_post_data(memory_file(Type, Handle), Out, HdrExtra) :- 1066 !, 1067 phrase(post_header(memory_file(Type, Handle), HdrExtra), Header), 1068 send_request_header(Out, Header), 1069 setup_call_cleanup( 1070 open_memory_file(Handle, read, In, [encoding(octet)]), 1071 copy_stream_data(In, Out), 1072 close(In)). 1073http_post_data(codes(Codes), Out, HdrExtra) :- 1074 !, 1075 http_post_data(codes(text/plain, Codes), Out, HdrExtra). 1076http_post_data(codes(Type, Codes), Out, HdrExtra) :- 1077 !, 1078 phrase(post_header(codes(Type, Codes), HdrExtra), Header), 1079 send_request_header(Out, Header), 1080 setup_call_cleanup( 1081 set_stream(Out, encoding(utf8)), 1082 format(Out, '~s', [Codes]), 1083 set_stream(Out, encoding(octet))). 1084http_post_data(bytes(Type, Bytes), Out, HdrExtra) :- 1085 !, 1086 phrase(post_header(bytes(Type, Bytes), HdrExtra), Header), 1087 send_request_header(Out, Header), 1088 format(Out, '~s', [Bytes]). 1089http_post_data(atom(Atom), Out, HdrExtra) :- 1090 !, 1091 http_post_data(atom(text/plain, Atom), Out, HdrExtra). 1092http_post_data(atom(Type, Atom), Out, HdrExtra) :- 1093 !, 1094 phrase(post_header(atom(Type, Atom), HdrExtra), Header), 1095 send_request_header(Out, Header), 1096 setup_call_cleanup( 1097 set_stream(Out, encoding(utf8)), 1098 write(Out, Atom), 1099 set_stream(Out, encoding(octet))). 1100http_post_data(string(String), Out, HdrExtra) :- 1101 !, 1102 http_post_data(atom(text/plain, String), Out, HdrExtra). 1103http_post_data(string(Type, String), Out, HdrExtra) :- 1104 !, 1105 phrase(post_header(string(Type, String), HdrExtra), Header), 1106 send_request_header(Out, Header), 1107 setup_call_cleanup( 1108 set_stream(Out, encoding(utf8)), 1109 write(Out, String), 1110 set_stream(Out, encoding(octet))). 1111http_post_data(cgi_stream(In, _Len), Out, HdrExtra) :- 1112 !, 1113 debug(obsolete, 'Obsolete 2nd argument in cgi_stream(In,Len)', []), 1114 http_post_data(cgi_stream(In), Out, HdrExtra). 1115http_post_data(cgi_stream(In), Out, HdrExtra) :- 1116 !, 1117 http_read_header(In, Header0), 1118 http_update_encoding(Header0, Encoding, Header), 1119 content_length_in_encoding(Encoding, In, Size), 1120 http_join_headers(HdrExtra, Header, Hdr2), 1121 phrase(post_header(cgi_data(Size), Hdr2), HeaderText), 1122 send_request_header(Out, HeaderText), 1123 setup_call_cleanup( 1124 set_stream(Out, encoding(Encoding)), 1125 copy_stream_data(In, Out), 1126 set_stream(Out, encoding(octet))). 1127http_post_data(form(Fields), Out, HdrExtra) :- 1128 !, 1129 parse_url_search(Codes, Fields), 1130 length(Codes, Size), 1131 http_join_headers(HdrExtra, 1132 [ content_type('application/x-www-form-urlencoded') 1133 ], Header), 1134 phrase(post_header(cgi_data(Size), Header), HeaderChars), 1135 send_request_header(Out, HeaderChars), 1136 format(Out, '~s', [Codes]). 1137http_post_data(form_data(Data), Out, HdrExtra) :- 1138 !, 1139 setup_call_cleanup( 1140 new_memory_file(MemFile), 1141 ( setup_call_cleanup( 1142 open_memory_file(MemFile, write, MimeOut), 1143 mime_pack(Data, MimeOut, Boundary), 1144 close(MimeOut)), 1145 size_memory_file(MemFile, Size, octet), 1146 format(string(ContentType), 1147 'multipart/form-data; boundary=~w', [Boundary]), 1148 http_join_headers(HdrExtra, 1149 [ mime_version('1.0'), 1150 content_type(ContentType) 1151 ], Header), 1152 phrase(post_header(cgi_data(Size), Header), HeaderChars), 1153 send_request_header(Out, HeaderChars), 1154 setup_call_cleanup( 1155 open_memory_file(MemFile, read, In, [encoding(octet)]), 1156 copy_stream_data(In, Out), 1157 close(In)) 1158 ), 1159 free_memory_file(MemFile)). 1160http_post_data(List, Out, HdrExtra) :- % multipart-mixed 1161 is_list(List), 1162 !, 1163 setup_call_cleanup( 1164 new_memory_file(MemFile), 1165 ( setup_call_cleanup( 1166 open_memory_file(MemFile, write, MimeOut), 1167 mime_pack(List, MimeOut, Boundary), 1168 close(MimeOut)), 1169 size_memory_file(MemFile, Size, octet), 1170 format(string(ContentType), 1171 'multipart/mixed; boundary=~w', [Boundary]), 1172 http_join_headers(HdrExtra, 1173 [ mime_version('1.0'), 1174 content_type(ContentType) 1175 ], Header), 1176 phrase(post_header(cgi_data(Size), Header), HeaderChars), 1177 send_request_header(Out, HeaderChars), 1178 setup_call_cleanup( 1179 open_memory_file(MemFile, read, In, [encoding(octet)]), 1180 copy_stream_data(In, Out), 1181 close(In)) 1182 ), 1183 free_memory_file(MemFile)).
1190post_header(html(Tokens), HdrExtra) --> 1191 header_fields(HdrExtra, Len), 1192 content_length(html(Tokens), Len), 1193 content_type(text/html), 1194 "\r\n". 1195post_header(file(Type, File), HdrExtra) --> 1196 header_fields(HdrExtra, Len), 1197 content_length(file(File), Len), 1198 content_type(Type), 1199 "\r\n". 1200post_header(memory_file(Type, File), HdrExtra) --> 1201 header_fields(HdrExtra, Len), 1202 content_length(memory_file(File), Len), 1203 content_type(Type), 1204 "\r\n". 1205post_header(cgi_data(Size), HdrExtra) --> 1206 header_fields(HdrExtra, Len), 1207 content_length(Size, Len), 1208 "\r\n". 1209post_header(codes(Type, Codes), HdrExtra) --> 1210 header_fields(HdrExtra, Len), 1211 content_length(codes(Codes, utf8), Len), 1212 content_type(Type, utf8), 1213 "\r\n". 1214post_header(bytes(Type, Bytes), HdrExtra) --> 1215 header_fields(HdrExtra, Len), 1216 content_length(bytes(Bytes), Len), 1217 content_type(Type), 1218 "\r\n". 1219post_header(atom(Type, Atom), HdrExtra) --> 1220 header_fields(HdrExtra, Len), 1221 content_length(atom(Atom, utf8), Len), 1222 content_type(Type, utf8), 1223 "\r\n". 1224post_header(string(Type, String), HdrExtra) --> 1225 header_fields(HdrExtra, Len), 1226 content_length(string(String, utf8), Len), 1227 content_type(Type, utf8), 1228 "\r\n". 1229 1230 1231 /******************************* 1232 * OUTPUT HEADER DCG * 1233 *******************************/
1240http_reply_header(Out, What, HdrExtra) :-
1241 phrase(reply_header(What, HdrExtra, _Code), String),
1242 !,
1243 send_reply_header(Out, String).
created(+URL, +HTMLTokens)
moved(+URL, +HTMLTokens)
moved_temporary(+URL, +HTMLTokens)
see_other(+URL, +HTMLTokens)
status(+Status)
status(+Status, +HTMLTokens)
authorise(+Method, +Realm, +Tokens)
authorise(+Method, +Tokens)
not_found(+URL, +HTMLTokens)
server_error(+Error, +Tokens)
resource_error(+Error, +Tokens)
service_unavailable(+Why, +Tokens)
1267reply_header(Data, Dict) --> 1268 { _{header:HdrExtra, code:Code} :< Dict }, 1269 reply_header(Data, HdrExtra, Code). 1270 1271reply_header(string(String), HdrExtra, Code) --> 1272 reply_header(string(text/plain, String), HdrExtra, Code). 1273reply_header(string(Type, String), HdrExtra, Code) --> 1274 vstatus(ok, Code, HdrExtra), 1275 date(now), 1276 header_fields(HdrExtra, CLen), 1277 content_length(codes(String, utf8), CLen), 1278 content_type(Type, utf8), 1279 "\r\n". 1280reply_header(bytes(Type, Bytes), HdrExtra, Code) --> 1281 vstatus(ok, Code, HdrExtra), 1282 date(now), 1283 header_fields(HdrExtra, CLen), 1284 content_length(bytes(Bytes), CLen), 1285 content_type(Type), 1286 "\r\n". 1287reply_header(html(Tokens), HdrExtra, Code) --> 1288 vstatus(ok, Code, HdrExtra), 1289 date(now), 1290 header_fields(HdrExtra, CLen), 1291 content_length(html(Tokens), CLen), 1292 content_type(text/html), 1293 "\r\n". 1294reply_header(file(Type, File), HdrExtra, Code) --> 1295 vstatus(ok, Code, HdrExtra), 1296 date(now), 1297 modified(file(File)), 1298 header_fields(HdrExtra, CLen), 1299 content_length(file(File), CLen), 1300 content_type(Type), 1301 "\r\n". 1302reply_header(gzip_file(Type, File), HdrExtra, Code) --> 1303 vstatus(ok, Code, HdrExtra), 1304 date(now), 1305 modified(file(File)), 1306 header_fields(HdrExtra, CLen), 1307 content_length(file(File), CLen), 1308 content_type(Type), 1309 content_encoding(gzip), 1310 "\r\n". 1311reply_header(file(Type, File, Range), HdrExtra, Code) --> 1312 vstatus(partial_content, Code, HdrExtra), 1313 date(now), 1314 modified(file(File)), 1315 header_fields(HdrExtra, CLen), 1316 content_length(file(File, Range), CLen), 1317 content_type(Type), 1318 "\r\n". 1319reply_header(tmp_file(Type, File), HdrExtra, Code) --> 1320 vstatus(ok, Code, HdrExtra), 1321 date(now), 1322 header_fields(HdrExtra, CLen), 1323 content_length(file(File), CLen), 1324 content_type(Type), 1325 "\r\n". 1326reply_header(cgi_data(Size), HdrExtra, Code) --> 1327 vstatus(ok, Code, HdrExtra), 1328 date(now), 1329 header_fields(HdrExtra, CLen), 1330 content_length(Size, CLen), 1331 "\r\n". 1332reply_header(chunked_data, HdrExtra, Code) --> 1333 vstatus(ok, Code, HdrExtra), 1334 date(now), 1335 header_fields(HdrExtra, _), 1336 ( {memberchk(transfer_encoding(_), HdrExtra)} 1337 -> "" 1338 ; transfer_encoding(chunked) 1339 ), 1340 "\r\n". 1341% non-200 replies without a body (e.g., 1xx, 204, 304) 1342reply_header(status(Status), HdrExtra, Code) --> 1343 vstatus(Status, Code), 1344 header_fields(HdrExtra, Clen), 1345 { Clen = 0 }, 1346 "\r\n". 1347% non-200 replies with a body 1348reply_header(Data, HdrExtra, Code) --> 1349 { status_reply_headers(Data, 1350 body(Type, Encoding, Content), 1351 ReplyHeaders), 1352 http_join_headers(ReplyHeaders, HdrExtra, Headers), 1353 functor(Data, CodeName, _) 1354 }, 1355 vstatus(CodeName, Code, Headers), 1356 date(now), 1357 header_fields(Headers, CLen), 1358 content_length(codes(Content, Encoding), CLen), 1359 content_type(Type, Encoding), 1360 "\r\n". 1361 1362status_reply_headers(created(Location, Body), Body, 1363 [ location(Location) ]). 1364status_reply_headers(moved(To, Body), Body, 1365 [ location(To) ]). 1366status_reply_headers(moved_temporary(To, Body), Body, 1367 [ location(To) ]). 1368status_reply_headers(gone(_URL, Body), Body, []). 1369status_reply_headers(see_other(To, Body), Body, 1370 [ location(To) ]). 1371status_reply_headers(authorise(Method, Body), Body, 1372 [ www_authenticate(Method) ]). 1373status_reply_headers(not_found(_URL, Body), Body, []). 1374status_reply_headers(forbidden(_URL, Body), Body, []). 1375status_reply_headers(method_not_allowed(_Method, _URL, Body), Body, []). 1376status_reply_headers(server_error(_Error, Body), Body, []). 1377status_reply_headers(service_unavailable(_Why, Body), Body, []). 1378status_reply_headers(not_acceptable(_Why, Body), Body, []). 1379status_reply_headers(bad_request(_Error, Body), Body, []).
1387vstatus(_Status, Code, HdrExtra) --> 1388 {memberchk(status(Code), HdrExtra)}, 1389 !, 1390 vstatus(_NewStatus, Code). 1391vstatus(Status, Code, _) --> 1392 vstatus(Status, Code). 1393 1394vstatus(Status, Code) --> 1395 "HTTP/1.1 ", 1396 status_number(Status, Code), 1397 " ", 1398 status_comment(Status), 1399 "\r\n".
1408status_number(Status, Code) --> 1409 { var(Status) }, 1410 !, 1411 integer(Code), 1412 { status_number(Status, Code) }, 1413 !. 1414status_number(Status, Code) --> 1415 { status_number(Status, Code) }, 1416 integer(Code).
1430% Unrecognized status codes that are within a defined code class. 1431% RFC 7231 states: 1432% "[...] a client MUST understand the class of any status code, 1433% as indicated by the first digit, and treat an unrecognized status code 1434% as being equivalent to the `x00` status code of that class [...] 1435% " 1436% @see http://tools.ietf.org/html/rfc7231#section-6 1437 1438status_number(Status, Code) :- 1439 nonvar(Status), 1440 !, 1441 status_number_fact(Status, Code). 1442status_number(Status, Code) :- 1443 nonvar(Code), 1444 !, 1445 ( between(100, 599, Code) 1446 -> ( status_number_fact(Status, Code) 1447 -> true 1448 ; ClassCode is Code // 100 * 100, 1449 status_number_fact(Status, ClassCode) 1450 ) 1451 ; domain_error(http_code, Code) 1452 ). 1453 1454status_number_fact(continue, 100). 1455status_number_fact(switching_protocols, 101). 1456status_number_fact(ok, 200). 1457status_number_fact(created, 201). 1458status_number_fact(accepted, 202). 1459status_number_fact(non_authoritative_info, 203). 1460status_number_fact(no_content, 204). 1461status_number_fact(reset_content, 205). 1462status_number_fact(partial_content, 206). 1463status_number_fact(multiple_choices, 300). 1464status_number_fact(moved, 301). 1465status_number_fact(moved_temporary, 302). 1466status_number_fact(see_other, 303). 1467status_number_fact(not_modified, 304). 1468status_number_fact(use_proxy, 305). 1469status_number_fact(unused, 306). 1470status_number_fact(temporary_redirect, 307). 1471status_number_fact(bad_request, 400). 1472status_number_fact(authorise, 401). 1473status_number_fact(payment_required, 402). 1474status_number_fact(forbidden, 403). 1475status_number_fact(not_found, 404). 1476status_number_fact(method_not_allowed, 405). 1477status_number_fact(not_acceptable, 406). 1478status_number_fact(request_timeout, 408). 1479status_number_fact(conflict, 409). 1480status_number_fact(gone, 410). 1481status_number_fact(length_required, 411). 1482status_number_fact(payload_too_large, 413). 1483status_number_fact(uri_too_long, 414). 1484status_number_fact(unsupported_media_type, 415). 1485status_number_fact(expectation_failed, 417). 1486status_number_fact(upgrade_required, 426). 1487status_number_fact(server_error, 500). 1488status_number_fact(not_implemented, 501). 1489status_number_fact(bad_gateway, 502). 1490status_number_fact(service_unavailable, 503). 1491status_number_fact(gateway_timeout, 504). 1492status_number_fact(http_version_not_supported, 505).
1499status_comment(continue) --> 1500 "Continue". 1501status_comment(switching_protocols) --> 1502 "Switching Protocols". 1503status_comment(ok) --> 1504 "OK". 1505status_comment(created) --> 1506 "Created". 1507status_comment(accepted) --> 1508 "Accepted". 1509status_comment(non_authoritative_info) --> 1510 "Non-Authoritative Information". 1511status_comment(no_content) --> 1512 "No Content". 1513status_comment(reset_content) --> 1514 "Reset Content". 1515status_comment(created) --> 1516 "Created". 1517status_comment(partial_content) --> 1518 "Partial content". 1519status_comment(multiple_choices) --> 1520 "Multiple Choices". 1521status_comment(moved) --> 1522 "Moved Permanently". 1523status_comment(moved_temporary) --> 1524 "Moved Temporary". 1525status_comment(see_other) --> 1526 "See Other". 1527status_comment(not_modified) --> 1528 "Not Modified". 1529status_comment(use_proxy) --> 1530 "Use Proxy". 1531status_comment(unused) --> 1532 "Unused". 1533status_comment(temporary_redirect) --> 1534 "Temporary Redirect". 1535status_comment(bad_request) --> 1536 "Bad Request". 1537status_comment(authorise) --> 1538 "Authorization Required". 1539status_comment(payment_required) --> 1540 "Payment Required". 1541status_comment(forbidden) --> 1542 "Forbidden". 1543status_comment(not_found) --> 1544 "Not Found". 1545status_comment(method_not_allowed) --> 1546 "Method Not Allowed". 1547status_comment(not_acceptable) --> 1548 "Not Acceptable". 1549status_comment(request_timeout) --> 1550 "Request Timeout". 1551status_comment(conflict) --> 1552 "Conflict". 1553status_comment(gone) --> 1554 "Gone". 1555status_comment(length_required) --> 1556 "Length Required". 1557status_comment(payload_too_large) --> 1558 "Payload Too Large". 1559status_comment(uri_too_long) --> 1560 "URI Too Long". 1561status_comment(unsupported_media_type) --> 1562 "Unsupported Media Type". 1563status_comment(expectation_failed) --> 1564 "Expectation Failed". 1565status_comment(upgrade_required) --> 1566 "Upgrade Required". 1567status_comment(server_error) --> 1568 "Internal Server Error". 1569status_comment(not_implemented) --> 1570 "Not Implemented". 1571status_comment(bad_gateway) --> 1572 "Bad Gateway". 1573status_comment(service_unavailable) --> 1574 "Service Unavailable". 1575status_comment(gateway_timeout) --> 1576 "Gateway Timeout". 1577status_comment(http_version_not_supported) --> 1578 "HTTP Version Not Supported". 1579 1580date(Time) --> 1581 "Date: ", 1582 ( { Time == now } 1583 -> now 1584 ; rfc_date(Time) 1585 ), 1586 "\r\n". 1587 1588modified(file(File)) --> 1589 !, 1590 { time_file(File, Time) 1591 }, 1592 modified(Time). 1593modified(Time) --> 1594 "Last-modified: ", 1595 ( { Time == now } 1596 -> now 1597 ; rfc_date(Time) 1598 ), 1599 "\r\n".
1609content_length(file(File, bytes(From, To)), Len) --> 1610 !, 1611 { size_file(File, Size), 1612 ( To == end 1613 -> Len is Size - From, 1614 RangeEnd is Size - 1 1615 ; Len is To+1 - From, % To is index of last byte 1616 RangeEnd = To 1617 ) 1618 }, 1619 content_range(bytes, From, RangeEnd, Size), 1620 content_length(Len, Len). 1621content_length(Reply, Len) --> 1622 { length_of(Reply, Len) 1623 }, 1624 "Content-Length: ", integer(Len), 1625 "\r\n". 1626 1627 1628length_of(_, Len) :- 1629 nonvar(Len), 1630 !. 1631length_of(string(String, Encoding), Len) :- 1632 length_of(codes(String, Encoding), Len). 1633length_of(codes(String, Encoding), Len) :- 1634 !, 1635 setup_call_cleanup( 1636 open_null_stream(Out), 1637 ( set_stream(Out, encoding(Encoding)), 1638 format(Out, '~s', [String]), 1639 byte_count(Out, Len) 1640 ), 1641 close(Out)). 1642length_of(atom(Atom, Encoding), Len) :- 1643 !, 1644 setup_call_cleanup( 1645 open_null_stream(Out), 1646 ( set_stream(Out, encoding(Encoding)), 1647 format(Out, '~a', [Atom]), 1648 byte_count(Out, Len) 1649 ), 1650 close(Out)). 1651length_of(file(File), Len) :- 1652 !, 1653 size_file(File, Len). 1654length_of(memory_file(Handle), Len) :- 1655 !, 1656 size_memory_file(Handle, Len, octet). 1657length_of(html_tokens(Tokens), Len) :- 1658 !, 1659 html_print_length(Tokens, Len). 1660length_of(html(Tokens), Len) :- % deprecated 1661 !, 1662 html_print_length(Tokens, Len). 1663length_of(bytes(Bytes), Len) :- 1664 !, 1665 ( string(Bytes) 1666 -> string_length(Bytes, Len) 1667 ; length(Bytes, Len) % assuming a list of 0..255 1668 ). 1669length_of(Len, Len).
Content-Range
header for partial content (206)
replies.1677content_range(Unit, From, RangeEnd, Size) --> 1678 "Content-Range: ", atom(Unit), " ", 1679 integer(From), "-", integer(RangeEnd), "/", integer(Size), 1680 "\r\n". 1681 1682content_encoding(Encoding) --> 1683 "Content-Encoding: ", atom(Encoding), "\r\n". 1684 1685transfer_encoding(Encoding) --> 1686 "Transfer-Encoding: ", atom(Encoding), "\r\n". 1687 1688content_type(Type) --> 1689 content_type(Type, _). 1690 1691content_type(Type, Charset) --> 1692 ctype(Type), 1693 charset(Charset), 1694 "\r\n". 1695 1696ctype(Main/Sub) --> 1697 !, 1698 "Content-Type: ", 1699 atom(Main), 1700 "/", 1701 atom(Sub). 1702ctype(Type) --> 1703 !, 1704 "Content-Type: ", 1705 atom(Type). 1706 1707charset(Var) --> 1708 { var(Var) }, 1709 !. 1710charset(utf8) --> 1711 !, 1712 "; charset=UTF-8". 1713charset(CharSet) --> 1714 "; charset=", 1715 atom(CharSet).
1723header_field(Name, Value) --> 1724 { var(Name) }, % parsing 1725 !, 1726 field_name(Name), 1727 ":", 1728 whites, 1729 read_field_value(ValueChars), 1730 blanks_to_nl, 1731 !, 1732 { field_to_prolog(Name, ValueChars, Value) 1733 -> true 1734 ; atom_codes(Value, ValueChars), 1735 domain_error(Name, Value) 1736 }. 1737header_field(Name, Value) --> 1738 field_name(Name), 1739 ": ", 1740 field_value(Name, Value), 1741 "\r\n".
1747read_field_value([H|T]) --> 1748 [H], 1749 { \+ code_type(H, space) }, 1750 !, 1751 read_field_value(T). 1752read_field_value([]) --> 1753 "". 1754read_field_value([H|T]) --> 1755 [H], 1756 read_field_value(T).
1763send_reply_header(Out, String) :- 1764 debug(http(send_reply), "< ~s", [String]), 1765 format(Out, '~s', [String]). 1766 1767send_request_header(Out, String) :- 1768 debug(http(send_request), "> ~s", [String]), 1769 format(Out, '~s', [String]).
set_cookie(Name, Value, Options)
.
Options is a list consisting of Name=Value or a single
atom (e.g., secure
)bytes(From, To)
, where From is an integer
and To is either an integer or the atom end
.media(Type, TypeParams, Quality, AcceptExts)
. The list is
sorted according to preference.disposition(Name, Attributes)
, where Attributes is
a list of Name=Value pairs.media(Type/SubType, Attributes)
, where Attributes
is a list of Name=Value pairs.As some fields are already parsed in the Request, this predicate is a no-op when called on an already parsed field.
1809http_parse_header_value(Field, Value, Prolog) :- 1810 known_field(Field, _, Type), 1811 ( already_parsed(Type, Value) 1812 -> Prolog = Value 1813 ; to_codes(Value, Codes), 1814 parse_header_value(Field, Codes, Prolog) 1815 ). 1816 1817already_parsed(integer, V) :- !, integer(V). 1818already_parsed(list(Type), L) :- !, is_list(L), maplist(already_parsed(Type), L). 1819already_parsed(Term, V) :- subsumes_term(Term, V).
1827known_field(content_length, true, integer). 1828known_field(status, true, integer). 1829known_field(cookie, true, list(_=_)). 1830known_field(set_cookie, true, list(set_cookie(_Name,_Value,_Options))). 1831known_field(host, true, _Host:_Port). 1832known_field(range, maybe, bytes(_,_)). 1833known_field(accept, maybe, list(media(_Type, _Parms, _Q, _Exts))). 1834known_field(content_disposition, maybe, disposition(_Name, _Attributes)). 1835known_field(content_type, false, media(_Type/_Sub, _Attributes)). 1836 1837to_codes(In, Codes) :- 1838 ( is_list(In) 1839 -> Codes = In 1840 ; atom_codes(In, Codes) 1841 ).
known_fields(_,true)
, this must succeed. For maybe
, we just
return the atom if the translation fails.1849field_to_prolog(Field, Codes, Prolog) :- 1850 known_field(Field, true, _Type), 1851 !, 1852 ( parse_header_value(Field, Codes, Prolog0) 1853 -> Prolog = Prolog0 1854 ). 1855field_to_prolog(Field, Codes, Prolog) :- 1856 known_field(Field, maybe, _Type), 1857 parse_header_value(Field, Codes, Prolog0), 1858 !, 1859 Prolog = Prolog0. 1860field_to_prolog(_, Codes, Atom) :- 1861 atom_codes(Atom, Codes).
1868parse_header_value(content_length, ValueChars, ContentLength) :- 1869 number_codes(ContentLength, ValueChars). 1870parse_header_value(status, ValueChars, Code) :- 1871 ( phrase(" ", L, _), 1872 append(Pre, L, ValueChars) 1873 -> number_codes(Code, Pre) 1874 ; number_codes(Code, ValueChars) 1875 ). 1876parse_header_value(cookie, ValueChars, Cookies) :- 1877 debug(cookie, 'Cookie: ~s', [ValueChars]), 1878 phrase(cookies(Cookies), ValueChars). 1879parse_header_value(set_cookie, ValueChars, SetCookie) :- 1880 debug(cookie, 'SetCookie: ~s', [ValueChars]), 1881 phrase(set_cookie(SetCookie), ValueChars). 1882parse_header_value(host, ValueChars, Host) :- 1883 ( append(HostChars, [0':|PortChars], ValueChars), 1884 catch(number_codes(Port, PortChars), _, fail) 1885 -> atom_codes(HostName, HostChars), 1886 Host = HostName:Port 1887 ; atom_codes(Host, ValueChars) 1888 ). 1889parse_header_value(range, ValueChars, Range) :- 1890 phrase(range(Range), ValueChars). 1891parse_header_value(accept, ValueChars, Media) :- 1892 parse_accept(ValueChars, Media). 1893parse_header_value(content_disposition, ValueChars, Disposition) :- 1894 phrase(content_disposition(Disposition), ValueChars). 1895parse_header_value(content_type, ValueChars, Type) :- 1896 phrase(parse_content_type(Type), ValueChars).
1900field_value(_, set_cookie(Name, Value, Options)) --> 1901 !, 1902 atom(Name), "=", atom(Value), 1903 value_options(Options, cookie). 1904field_value(_, disposition(Disposition, Options)) --> 1905 !, 1906 atom(Disposition), value_options(Options, disposition). 1907field_value(www_authenticate, Auth) --> 1908 auth_field_value(Auth). 1909field_value(_, Atomic) --> 1910 atom(Atomic).
1916auth_field_value(negotiate(Data)) --> 1917 "Negotiate ", 1918 { base64(Data, DataBase64), 1919 atom_codes(DataBase64, Codes) 1920 }, 1921 string(Codes). 1922auth_field_value(negotiate) --> 1923 "Negotiate". 1924auth_field_value(basic) --> 1925 !, 1926 "Basic". 1927auth_field_value(basic(Realm)) --> 1928 "Basic Realm=\"", atom(Realm), "\"". 1929auth_field_value(digest) --> 1930 !, 1931 "Digest". 1932auth_field_value(digest(Details)) --> 1933 "Digest ", atom(Details).
; charset=UTF-8
. There
are three versions: a plain key (secure
), token values
and quoted string values. Seems we cannot deduce that from
the actual value.1942value_options([], _) --> []. 1943value_options([H|T], Field) --> 1944 "; ", value_option(H, Field), 1945 value_options(T, Field). 1946 1947value_option(secure=true, cookie) --> 1948 !, 1949 "secure". 1950value_option(Name=Value, Type) --> 1951 { string_option(Name, Type) }, 1952 !, 1953 atom(Name), "=", 1954 qstring(Value). 1955value_option(Name=Value, Type) --> 1956 { token_option(Name, Type) }, 1957 !, 1958 atom(Name), "=", atom(Value). 1959value_option(Name=Value, _Type) --> 1960 atom(Name), "=", 1961 option_value(Value). 1962 1963string_option(filename, disposition). 1964 1965token_option(path, cookie). 1966 1967option_value(Value) --> 1968 { number(Value) }, 1969 !, 1970 number(Value). 1971option_value(Value) --> 1972 { ( atom(Value) 1973 -> true 1974 ; string(Value) 1975 ), 1976 forall(string_code(_, Value, C), 1977 token_char(C)) 1978 }, 1979 !, 1980 atom(Value). 1981option_value(Atomic) --> 1982 qstring(Atomic). 1983 1984qstring(Atomic) --> 1985 { string_codes(Atomic, Codes) }, 1986 "\"", 1987 qstring_codes(Codes), 1988 "\"". 1989 1990qstring_codes([]) --> []. 1991qstring_codes([H|T]) --> qstring_code(H), qstring_codes(T). 1992 1993qstring_code(C) --> {qstring_esc(C)}, !, "\\", [C]. 1994qstring_code(C) --> [C]. 1995 1996qstring_esc(0'"). 1997qstring_esc(C) :- ctl(C). 1998 1999 2000 /******************************* 2001 * ACCEPT HEADERS * 2002 *******************************/ 2003 2004:- dynamic accept_cache/2. 2005:- volatile accept_cache/2. 2006 2007parse_accept(Codes, Media) :- 2008 atom_codes(Atom, Codes), 2009 ( accept_cache(Atom, Media0) 2010 -> Media = Media0 2011 ; phrase(accept(Media0), Codes), 2012 keysort(Media0, Media1), 2013 pairs_values(Media1, Media2), 2014 assertz(accept_cache(Atom, Media2)), 2015 Media = Media2 2016 ).
2022accept([H|T]) --> 2023 blanks, 2024 media_range(H), 2025 blanks, 2026 ( "," 2027 -> accept(T) 2028 ; {T=[]} 2029 ). 2030 2031media_range(s(SortQuality,Spec)-media(Type, TypeParams, Quality, AcceptExts)) --> 2032 media_type(Type), 2033 blanks, 2034 ( ";" 2035 -> blanks, 2036 parameters_and_quality(TypeParams, Quality, AcceptExts) 2037 ; { TypeParams = [], 2038 Quality = 1.0, 2039 AcceptExts = [] 2040 } 2041 ), 2042 { SortQuality is float(-Quality), 2043 rank_specialised(Type, TypeParams, Spec) 2044 }.
2051content_disposition(disposition(Disposition, Options)) -->
2052 token(Disposition), blanks,
2053 value_parameters(Options).
media(Type/SubType,
Parameters)
.
2060parse_content_type(media(Type, Parameters)) -->
2061 media_type(Type), blanks,
2062 value_parameters(Parameters).
2073rank_specialised(Type/SubType, TypeParams, v(VT, VS, SortVP)) :- 2074 var_or_given(Type, VT), 2075 var_or_given(SubType, VS), 2076 length(TypeParams, VP), 2077 SortVP is -VP. 2078 2079var_or_given(V, Val) :- 2080 ( var(V) 2081 -> Val = 0 2082 ; Val = -1 2083 ). 2084 2085media_type(Type/SubType) --> 2086 type(Type), "/", type(SubType). 2087 2088type(_) --> 2089 "*", 2090 !. 2091type(Type) --> 2092 token(Type). 2093 2094parameters_and_quality(Params, Quality, AcceptExts) --> 2095 token(Name), 2096 blanks, "=", blanks, 2097 ( { Name == q } 2098 -> float(Quality), blanks, 2099 value_parameters(AcceptExts), 2100 { Params = [] } 2101 ; { Params = [Name=Value|T] }, 2102 parameter_value(Value), 2103 blanks, 2104 ( ";" 2105 -> blanks, 2106 parameters_and_quality(T, Quality, AcceptExts) 2107 ; { T = [], 2108 Quality = 1.0, 2109 AcceptExts = [] 2110 } 2111 ) 2112 ).
2119value_parameters([H|T]) --> 2120 ";", 2121 !, 2122 blanks, token(Name), blanks, 2123 ( "=" 2124 -> blanks, 2125 ( token(Value) 2126 -> [] 2127 ; quoted_string(Value) 2128 ), 2129 { H = (Name=Value) } 2130 ; { H = Name } 2131 ), 2132 blanks, 2133 value_parameters(T). 2134value_parameters([]) --> 2135 []. 2136 2137parameter_value(Value) --> token(Value), !. 2138parameter_value(Value) --> quoted_string(Value).
2145token(Name) --> 2146 token_char(C1), 2147 token_chars(Cs), 2148 { atom_codes(Name, [C1|Cs]) }. 2149 2150token_chars([H|T]) --> 2151 token_char(H), 2152 !, 2153 token_chars(T). 2154token_chars([]) --> []. 2155 2156token_char(C) :- 2157 \+ ctl(C), 2158 \+ separator_code(C). 2159 2160ctl(C) :- between(0,31,C), !. 2161ctl(127). 2162 2163separator_code(0'(). 2164separator_code(0')). 2165separator_code(0'<). 2166separator_code(0'>). 2167separator_code(0'@). 2168separator_code(0',). 2169separator_code(0';). 2170separator_code(0':). 2171separator_code(0'\\). 2172separator_code(0'"). 2173separator_code(0'/). 2174separator_code(0'[). 2175separator_code(0']). 2176separator_code(0'?). 2177separator_code(0'=). 2178separator_code(0'{). 2179separator_code(0'}). 2180separator_code(0'\s). 2181separator_code(0'\t). 2182 2183term_expansion(token_char(x) --> [x], Clauses) :- 2184 findall((token_char(C)-->[C]), 2185 ( between(0, 255, C), 2186 token_char(C) 2187 ), 2188 Clauses). 2189 2190token_char(x) --> [x].
2196quoted_string(Text) --> 2197 "\"", 2198 quoted_text(Codes), 2199 { atom_codes(Text, Codes) }. 2200 2201quoted_text([]) --> 2202 "\"", 2203 !. 2204quoted_text([H|T]) --> 2205 "\\", !, [H], 2206 quoted_text(T). 2207quoted_text([H|T]) --> 2208 [H], 2209 !, 2210 quoted_text(T).
content_length(Len)
is special. If instantiated
it emits the header. If not it just unifies ContentLength with
the argument of the content_length(Len)
term. This allows for
both sending and retrieving the content-length.2221header_fields([], _) --> []. 2222header_fields([content_length(CLen)|T], CLen) --> 2223 !, 2224 ( { var(CLen) } 2225 -> "" 2226 ; header_field(content_length, CLen) 2227 ), 2228 header_fields(T, CLen). % Continue or return first only? 2229header_fields([status(_)|T], CLen) --> % handled by vstatus//3. 2230 !, 2231 header_fields(T, CLen). 2232header_fields([H|T], CLen) --> 2233 { H =.. [Name, Value] }, 2234 header_field(Name, Value), 2235 header_fields(T, CLen).
token = 1*<any CHAR except CTLs or separators> separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
2252:- public 2253 field_name//1. 2254 2255field_name(Name) --> 2256 { var(Name) }, 2257 !, 2258 rd_field_chars(Chars), 2259 { atom_codes(Name, Chars) }. 2260field_name(mime_version) --> 2261 !, 2262 "MIME-Version". 2263field_name(www_authenticate) --> 2264 !, 2265 "WWW-Authenticate". 2266field_name(Name) --> 2267 { atom_codes(Name, Chars) }, 2268 wr_field_chars(Chars). 2269 2270rd_field_chars_no_fold([C|T]) --> 2271 [C], 2272 { rd_field_char(C, _) }, 2273 !, 2274 rd_field_chars_no_fold(T). 2275rd_field_chars_no_fold([]) --> 2276 []. 2277 2278rd_field_chars([C0|T]) --> 2279 [C], 2280 { rd_field_char(C, C0) }, 2281 !, 2282 rd_field_chars(T). 2283rd_field_chars([]) --> 2284 [].
2290separators("()<>@,;:\\\"/[]?={} \t"). 2291 2292term_expansion(rd_field_char('expand me',_), Clauses) :- 2293 2294 Clauses = [ rd_field_char(0'-, 0'_) 2295 | Cls 2296 ], 2297 separators(SepString), 2298 string_codes(SepString, Seps), 2299 findall(rd_field_char(In, Out), 2300 ( between(32, 127, In), 2301 \+ memberchk(In, Seps), 2302 In \== 0'-, % 0' 2303 code_type(Out, to_lower(In))), 2304 Cls). 2305 2306rd_field_char('expand me', _). % avoid recursion 2307 2308wr_field_chars([C|T]) --> 2309 !, 2310 { code_type(C, to_lower(U)) }, 2311 [U], 2312 wr_field_chars2(T). 2313wr_field_chars([]) --> 2314 []. 2315 2316wr_field_chars2([]) --> []. 2317wr_field_chars2([C|T]) --> % 0' 2318 ( { C == 0'_ } 2319 -> "-", 2320 wr_field_chars(T) 2321 ; [C], 2322 wr_field_chars2(T) 2323 ).
2329now -->
2330 { get_time(Time)
2331 },
2332 rfc_date(Time).
2339rfc_date(Time, String, Tail) :-
2340 stamp_date_time(Time, Date, 'UTC'),
2341 format_time(codes(String, Tail),
2342 '%a, %d %b %Y %T GMT',
2343 Date, posix).
2349http_timestamp(Time, Atom) :- 2350 stamp_date_time(Time, Date, 'UTC'), 2351 format_time(atom(Atom), 2352 '%a, %d %b %Y %T GMT', 2353 Date, posix). 2354 2355 2356 /******************************* 2357 * REQUEST DCG * 2358 *******************************/ 2359 2360request(Fd, [method(Method),request_uri(ReqURI)|Header]) --> 2361 method(Method), 2362 blanks, 2363 nonblanks(Query), 2364 { atom_codes(ReqURI, Query), 2365 request_uri_parts(ReqURI, Header, Rest) 2366 }, 2367 request_header(Fd, Rest), 2368 !. 2369request(Fd, [unknown(What)|Header]) --> 2370 string(What), 2371 eos, 2372 !, 2373 { http_read_header(Fd, Header) 2374 -> true 2375 ; Header = [] 2376 }. 2377 2378method(get) --> "GET", !. 2379method(put) --> "PUT", !. 2380method(head) --> "HEAD", !. 2381method(post) --> "POST", !. 2382method(delete) --> "DELETE", !. 2383method(patch) --> "PATCH", !. 2384method(options) --> "OPTIONS", !. 2385method(trace) --> "TRACE", !.
2399request_uri_parts(ReqURI, [path(Path)|Parts], Rest) :- 2400 uri_components(ReqURI, Components), 2401 uri_data(path, Components, PathText), 2402 uri_encoded(path, Path, PathText), 2403 phrase(uri_parts(Components), Parts, Rest). 2404 2405uri_parts(Components) --> 2406 uri_search(Components), 2407 uri_fragment(Components). 2408 2409uri_search(Components) --> 2410 { uri_data(search, Components, Search), 2411 nonvar(Search), 2412 catch(uri_query_components(Search, Query), 2413 error(syntax_error(_),_), 2414 fail) 2415 }, 2416 !, 2417 [ search(Query) ]. 2418uri_search(_) --> []. 2419 2420uri_fragment(Components) --> 2421 { uri_data(fragment, Components, String), 2422 nonvar(String), 2423 !, 2424 uri_encoded(fragment, Fragment, String) 2425 }, 2426 [ fragment(Fragment) ]. 2427uri_fragment(_) --> [].
2434request_header(_, []) --> % Old-style non-version header 2435 blanks, 2436 eos, 2437 !. 2438request_header(Fd, [http_version(Version)|Header]) --> 2439 http_version(Version), 2440 blanks, 2441 eos, 2442 !, 2443 { Version = 1-_ 2444 -> http_read_header(Fd, Header) 2445 ; Header = [] 2446 }. 2447 2448http_version(Version) --> 2449 blanks, 2450 "HTTP/", 2451 http_version_number(Version). 2452 2453http_version_number(Major-Minor) --> 2454 integer(Major), 2455 ".", 2456 integer(Minor). 2457 2458 2459 /******************************* 2460 * COOKIES * 2461 *******************************/
2467cookies([Name=Value|T]) --> 2468 blanks, 2469 cookie(Name, Value), 2470 !, 2471 blanks, 2472 ( ";" 2473 -> cookies(T) 2474 ; { T = [] } 2475 ). 2476cookies(List) --> 2477 string(Skipped), 2478 ";", 2479 !, 2480 { print_message(warning, http(skipped_cookie(Skipped))) }, 2481 cookies(List). 2482cookies([]) --> 2483 blanks. 2484 Name, Value) (--> 2486 cookie_name(Name), 2487 blanks, "=", blanks, 2488 cookie_value(Value). 2489 Name) (--> 2491 { var(Name) }, 2492 !, 2493 rd_field_chars_no_fold(Chars), 2494 { atom_codes(Name, Chars) }. 2495 Value) (--> 2497 quoted_string(Value), 2498 !. 2499cookie_value(Value) --> 2500 chars_to_semicolon_or_blank(Chars), 2501 { atom_codes(Value, Chars) 2502 }. 2503 2504chars_to_semicolon_or_blank([]), ";" --> 2505 ";", 2506 !. 2507chars_to_semicolon_or_blank([]) --> 2508 " ", 2509 blanks, 2510 eos, 2511 !. 2512chars_to_semicolon_or_blank([H|T]) --> 2513 [H], 2514 !, 2515 chars_to_semicolon_or_blank(T). 2516chars_to_semicolon_or_blank([]) --> 2517 []. 2518 set_cookie(Name, Value, Options)) (--> 2520 ws, 2521 cookie(Name, Value), 2522 cookie_options(Options). 2523 [H|T]) (--> 2525 ws, 2526 ";", 2527 ws, 2528 cookie_option(H), 2529 !, 2530 cookie_options(T). 2531cookie_options([]) --> 2532 ws. 2533 2534ws --> " ", !, ws. 2535ws --> [].
Secure
and HttpOnly
.
2547cookie_option(Name=Value) --> 2548 rd_field_chars(NameChars), ws, 2549 { atom_codes(Name, NameChars) }, 2550 ( "=" 2551 -> ws, 2552 chars_to_semicolon(ValueChars), 2553 { atom_codes(Value, ValueChars) 2554 } 2555 ; { Value = true } 2556 ). 2557 2558chars_to_semicolon([H|T]) --> 2559 [H], 2560 { H \== 32, H \== 0'; }, 2561 !, 2562 chars_to_semicolon(T). 2563chars_to_semicolon([]), ";" --> 2564 ws, ";", 2565 !. 2566chars_to_semicolon([H|T]) --> 2567 [H], 2568 chars_to_semicolon(T). 2569chars_to_semicolon([]) --> 2570 [].
end
.2580range(bytes(From, To)) --> 2581 "bytes", whites, "=", whites, integer(From), "-", 2582 ( integer(To) 2583 -> "" 2584 ; { To = end } 2585 ). 2586 2587 2588 /******************************* 2589 * REPLY DCG * 2590 *******************************/
2607reply(Fd, [http_version(HttpVersion), status(Code, Status, Comment)|Header]) --> 2608 http_version(HttpVersion), 2609 blanks, 2610 ( status_number(Status, Code) 2611 -> [] 2612 ; integer(Status) 2613 ), 2614 blanks, 2615 string(CommentCodes), 2616 blanks_to_nl, 2617 !, 2618 blanks, 2619 { atom_codes(Comment, CommentCodes), 2620 http_read_header(Fd, Header) 2621 }. 2622 2623 2624 /******************************* 2625 * READ HEADER * 2626 *******************************/
content_type(text/html)
2634http_read_header(Fd, Header) :- 2635 read_header_data(Fd, Text), 2636 http_parse_header(Text, Header). 2637 2638read_header_data(Fd, Header) :- 2639 read_line_to_codes(Fd, Header, Tail), 2640 read_header_data(Header, Fd, Tail), 2641 debug(http(header), 'Header = ~n~s~n', [Header]). 2642 2643read_header_data([0'\r,0'\n], _, _) :- !. 2644read_header_data([0'\n], _, _) :- !. 2645read_header_data([], _, _) :- !. 2646read_header_data(_, Fd, Tail) :- 2647 read_line_to_codes(Fd, Tail, NewTail), 2648 read_header_data(Tail, Fd, NewTail).
2657http_parse_header(Text, Header) :- 2658 phrase(header(Header), Text), 2659 debug(http(header), 'Field: ~p', [Header]). 2660 2661header(List) --> 2662 header_field(Name, Value), 2663 !, 2664 { mkfield(Name, Value, List, Tail) 2665 }, 2666 blanks, 2667 header(Tail). 2668header([]) --> 2669 blanks, 2670 eos, 2671 !. 2672header(_) --> 2673 string(S), blanks_to_nl, 2674 !, 2675 { string_codes(Line, S), 2676 syntax_error(http_parameter(Line)) 2677 }.
SWI-Prolog httpd at <hostname>
The address can be modified by providing a definition for the multifile predicate http_address//0.
2691:- multifile 2692 http:http_address//0. 2693 2694address --> 2695 http:http_address, 2696 !. 2697address --> 2698 { gethostname(Host) }, 2699 html(address([ a(href('http://www.swi-prolog.org'), 'SWI-Prolog'), 2700 ' httpd at ', Host 2701 ])). 2702 2703mkfield(host, Host:Port, [host(Host),port(Port)|Tail], Tail) :- !. 2704mkfield(Name, Value, [Att|Tail], Tail) :- 2705 Att =.. [Name, Value].
created(Location)
moved(To)
moved_temporary(To)
see_other(To)
bad_request(ErrorTerm)
authorise(AuthMethod)
forbidden(URL)
not_found(URL)
method_not_allowed(Method,URL)
not_acceptable(Why)
server_error(ErrorTerm)
unavailable(Why)
The hook is tried twice, first using the status term, e.g.,
not_found(URL)
and than with the code, e.g. 404
. The second
call is deprecated and only exists for compatibility.
2744 /******************************* 2745 * MESSAGES * 2746 *******************************/ 2747 2748:- multifile 2749 prolog:message//1, 2750 prolog:error_message//1. 2751 2752prologerror_message(http_write_short(Data, Sent)) --> 2753 data(Data), 2754 [ ': remote hangup after ~D bytes'-[Sent] ]. 2755prologerror_message(syntax_error(http_request(Request))) --> 2756 [ 'Illegal HTTP request: ~s'-[Request] ]. 2757prologerror_message(syntax_error(http_parameter(Line))) --> 2758 [ 'Illegal HTTP parameter: ~s'-[Line] ]. 2759 2760prologmessage(http(skipped_cookie(S))) --> 2761 [ 'Skipped illegal cookie: ~s'-[S] ]. 2762 2763data(bytes(MimeType, _Bytes)) --> 2764 !, 2765 [ 'bytes(~p, ...)'-[MimeType] ]. 2766data(Data) --> 2767 [ '~p'-[Data] ]
Handling HTTP headers
The library library(http/http_header) provides primitives for parsing and composing HTTP headers. Its functionality is normally hidden by the other parts of the HTTP server and client libraries. */