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:- if(exists_source(http_exception)). 68:- autoload(http_exception,[map_exception_to_http_status/4]). 69:- endif. 70:- autoload(mimepack,[mime_pack/3]). 71:- autoload(mimetype,[file_mime_type/2]). 72:- autoload(library(apply),[maplist/2]). 73:- autoload(library(base64),[base64/2]). 74:- use_module(library(debug),[debug/3,debugging/1]). 75:- autoload(library(error),[syntax_error/1,domain_error/2]). 76:- autoload(library(lists),[append/3,member/2,select/3,delete/3]). 77:- autoload(library(memfile), 78 [ new_memory_file/1, open_memory_file/3, 79 free_memory_file/1, open_memory_file/4, 80 size_memory_file/3 81 ]). 82:- autoload(library(option),[option/3,option/2]). 83:- autoload(library(pairs),[pairs_values/2]). 84:- autoload(library(readutil), 85 [read_line_to_codes/2,read_line_to_codes/3]). 86:- autoload(library(sgml_write),[xml_write/3]). 87:- autoload(library(socket),[gethostname/1]). 88:- autoload(library(uri), 89 [ uri_components/2, uri_data/3, uri_encoded/3, uri_query_components/2 90 ]). 91:- autoload(library(url),[parse_url_search/2]). 92:- autoload(library(dcg/basics), 93 [ integer/3, atom/3, whites/2, blanks_to_nl/2, string/3, 94 number/3, blanks/2, float/3, nonblanks/3, eos/2 95 ]). 96:- use_module(library(settings),[setting/4,setting/2]). 97 98:- multifile 99 http:status_page/3, % +Status, +Context, -HTML 100 http:status_reply/3, % +Status, -Reply, +Options 101 http:serialize_reply/2, % +Reply, -Body 102 http:post_data_hook/3, % +Data, +Out, +HdrExtra 103 http:mime_type_encoding/2. % +MimeType, -Encoding 104 105% see http_update_transfer/4. 106 107:- setting(http:chunked_transfer, oneof([never,on_request,if_possible]), 108 on_request, 'When to use Transfer-Encoding: Chunked').
118:- discontiguous 119 term_expansion/2. 120 121 122 /******************************* 123 * READ REQUEST * 124 *******************************/
end_of_file
if FdIn is at the end of input.
132http_read_request(In, Request) :-
133 catch(read_line_to_codes(In, Codes), E, true),
134 ( var(E)
135 -> ( Codes == end_of_file
136 -> debug(http(header), 'end-of-file', []),
137 Request = end_of_file
138 ; debug(http(header), 'First line: ~s', [Codes]),
139 Request = [input(In)|Request1],
140 phrase(request(In, Request1), Codes),
141 ( Request1 = [unknown(Text)|_]
142 -> string_codes(S, Text),
143 syntax_error(http_request(S))
144 ; true
145 )
146 )
147 ; ( debugging(http(request))
148 -> message_to_string(E, Msg),
149 debug(http(request), "Exception reading 1st line: ~s", [Msg])
150 ; true
151 ),
152 Request = end_of_file
153 ).
161http_read_reply_header(In, [input(In)|Reply]) :- 162 read_line_to_codes(In, Codes), 163 ( Codes == end_of_file 164 -> debug(http(header), 'end-of-file', []), 165 throw(error(syntax(http_reply_header, end_of_file), _)) 166 ; debug(http(header), 'First line: ~s~n', [Codes]), 167 ( phrase(reply(In, Reply), Codes) 168 -> true 169 ; atom_codes(Header, Codes), 170 syntax_error(http_reply_header(Header)) 171 ) 172 ). 173 174 175 /******************************* 176 * FORMULATE REPLY * 177 *******************************/
html_write.pl
file
, but do not include modification time226http_reply(What, Out) :- 227 http_reply(What, Out, [connection(close)], _). 228 229http_reply(Data, Out, HdrExtra) :- 230 http_reply(Data, Out, HdrExtra, _Code). 231 232http_reply(Data, Out, HdrExtra, Code) :- 233 http_reply(Data, Out, HdrExtra, [], Code). 234 235http_reply(Data, Out, HdrExtra, Context, Code) :- 236 http_reply(Data, Out, HdrExtra, Context, [method(get)], Code). 237 238http_reply(Data, Out, HdrExtra, _Context, Request, Code) :- 239 byte_count(Out, C0), 240 memberchk(method(Method), Request), 241 catch(http_reply_data(Data, Out, HdrExtra, Method, Code), E, true), 242 !, 243 ( var(E) 244 -> true 245 ; ( E = error(io_error(write,_), _) 246 ; E = error(socket_error(_,_), _) 247 ) 248 -> byte_count(Out, C1), 249 Sent is C1 - C0, 250 throw(error(http_write_short(Data, Sent), _)) 251 ; E = error(timeout_error(write, _), _) 252 -> throw(E) 253 ; map_exception_to_http_status(E, Status, NewHdr, NewContext) 254 -> http_status_reply(Status, Out, NewHdr, NewContext, Request, Code) 255 ; throw(E) 256 ). 257http_reply(Status, Out, HdrExtra, Context, Request, Code) :- 258 http_status_reply(Status, Out, HdrExtra, Context, Request, Code). 259 260:- if(\+current_predicate(map_exception_to_http_status/4)). 261map_exception_to_http_status(_E, _Status, _NewHdr, _NewContext) :- 262 fail. 263:- endif. 264 265:- meta_predicate 266 if_no_head( , ).
275http_reply_data(Data, Out, HdrExtra, Method, Code) :- 276 http_reply_data_(Data, Out, HdrExtra, Method, Code), 277 flush_output(Out). 278 279http_reply_data_(html(HTML), Out, HdrExtra, Method, Code) :- 280 !, 281 phrase(reply_header(html(HTML), HdrExtra, Code), Header), 282 send_reply_header(Out, Header), 283 if_no_head(print_html(Out, HTML), Method). 284http_reply_data_(file(Type, File), Out, HdrExtra, Method, Code) :- 285 !, 286 phrase(reply_header(file(Type, File), HdrExtra, Code), Header), 287 reply_file(Out, File, Header, Method). 288http_reply_data_(gzip_file(Type, File), Out, HdrExtra, Method, Code) :- 289 !, 290 phrase(reply_header(gzip_file(Type, File), HdrExtra, Code), Header), 291 reply_file(Out, File, Header, Method). 292http_reply_data_(file(Type, File, Range), Out, HdrExtra, Method, Code) :- 293 !, 294 phrase(reply_header(file(Type, File, Range), HdrExtra, Code), Header), 295 reply_file_range(Out, File, Header, Range, Method). 296http_reply_data_(tmp_file(Type, File), Out, HdrExtra, Method, Code) :- 297 !, 298 phrase(reply_header(tmp_file(Type, File), HdrExtra, Code), Header), 299 reply_file(Out, File, Header, Method). 300http_reply_data_(bytes(Type, Bytes), Out, HdrExtra, Method, Code) :- 301 !, 302 phrase(reply_header(bytes(Type, Bytes), HdrExtra, Code), Header), 303 send_reply_header(Out, Header), 304 if_no_head(format(Out, '~s', [Bytes]), Method). 305http_reply_data_(stream(In, Len), Out, HdrExtra, Method, Code) :- 306 !, 307 phrase(reply_header(cgi_data(Len), HdrExtra, Code), Header), 308 copy_stream(Out, In, Header, Method, 0, end). 309http_reply_data_(cgi_stream(In, Len), Out, HdrExtra, Method, Code) :- 310 !, 311 http_read_header(In, CgiHeader), 312 seek(In, 0, current, Pos), 313 Size is Len - Pos, 314 http_join_headers(HdrExtra, CgiHeader, Hdr2), 315 phrase(reply_header(cgi_data(Size), Hdr2, Code), Header), 316 copy_stream(Out, In, Header, Method, 0, end). 317 318if_no_head(_, head) :- 319 !. 320if_no_head(Goal, _) :- 321 call(Goal). 322 323reply_file(Out, _File, Header, head) :- 324 !, 325 send_reply_header(Out, Header). 326reply_file(Out, File, Header, _) :- 327 setup_call_cleanup( 328 open(File, read, In, [type(binary)]), 329 copy_stream(Out, In, Header, 0, end), 330 close(In)). 331 332reply_file_range(Out, _File, Header, _Range, head) :- 333 !, 334 send_reply_header(Out, Header). 335reply_file_range(Out, File, Header, bytes(From, To), _) :- 336 setup_call_cleanup( 337 open(File, read, In, [type(binary)]), 338 copy_stream(Out, In, Header, From, To), 339 close(In)). 340 341copy_stream(Out, _, Header, head, _, _) :- 342 !, 343 send_reply_header(Out, Header). 344copy_stream(Out, In, Header, _, From, To) :- 345 copy_stream(Out, In, Header, From, To). 346 347copy_stream(Out, In, Header, From, To) :- 348 ( From == 0 349 -> true 350 ; seek(In, From, bof, _) 351 ), 352 peek_byte(In, _), 353 send_reply_header(Out, Header), 354 ( To == end 355 -> copy_stream_data(In, Out) 356 ; Len is To - From, 357 copy_stream_data(In, Out, Len) 358 ).
Status can be one of the following:
basic(Realm)
digest(Digest)
authorise(basic(Realm))
. Deprecated.392http_status_reply(Status, Out, Options) :- 393 _{header:HdrExtra, context:Context, code:Code, method:Method} :< Options, 394 http_status_reply(Status, Out, HdrExtra, Context, [method(Method)], Code). 395 396http_status_reply(Status, Out, HdrExtra, Code) :- 397 http_status_reply(Status, Out, HdrExtra, [], Code). 398 399http_status_reply(Status, Out, HdrExtra, Context, Code) :- 400 http_status_reply(Status, Out, HdrExtra, Context, [method(get)], Code). 401 402http_status_reply(Status, Out, HdrExtra, Context, Request, Code) :- 403 option(method(Method), Request, get), 404 parsed_accept(Request, Accept), 405 status_reply_flush(Status, Out, 406 _{ context: Context, 407 method: Method, 408 code: Code, 409 accept: Accept, 410 header: HdrExtra 411 }). 412 413parsed_accept(Request, Accept) :- 414 memberchk(accept(Accept0), Request), 415 http_parse_header_value(accept, Accept0, Accept1), 416 !, 417 Accept = Accept1. 418parsed_accept(_, [ media(text/html, [], 0.1, []), 419 media(_, [], 0.01, []) 420 ]). 421 422status_reply_flush(Status, Out, Options) :- 423 status_reply(Status, Out, Options), 424 !, 425 flush_output(Out).
438% Replies without content 439status_reply(no_content, Out, Options) :- 440 !, 441 phrase(reply_header(status(no_content), Options), Header), 442 send_reply_header(Out, Header). 443status_reply(switching_protocols(_Goal,SwitchOptions), Out, Options) :- 444 !, 445 ( option(headers(Extra1), SwitchOptions) 446 -> true 447 ; option(header(Extra1), SwitchOptions, []) 448 ), 449 http_join_headers(Options.header, Extra1, HdrExtra), 450 phrase(reply_header(status(switching_protocols), 451 Options.put(header,HdrExtra)), Header), 452 send_reply_header(Out, Header). 453status_reply(authorise(basic, ''), Out, Options) :- 454 !, 455 status_reply(authorise(basic), Out, Options). 456status_reply(authorise(basic, Realm), Out, Options) :- 457 !, 458 status_reply(authorise(basic(Realm)), Out, Options). 459status_reply(not_modified, Out, Options) :- 460 !, 461 phrase(reply_header(status(not_modified), Options), Header), 462 send_reply_header(Out, Header). 463% aliases (compatibility) 464status_reply(busy, Out, Options) :- 465 status_reply(service_unavailable(busy), Out, Options). 466status_reply(unavailable(Why), Out, Options) :- 467 status_reply(service_unavailable(Why), Out, Options). 468status_reply(resource_error(Why), Out, Options) :- 469 status_reply(service_unavailable(Why), Out, Options). 470% replies with content 471status_reply(Status, Out, Options) :- 472 status_has_content(Status), 473 status_page_hook(Status, Reply, Options), 474 serialize_body(Reply, Body), 475 Status =.. List, 476 append(List, [Body], ExList), 477 ExStatus =.. ExList, 478 phrase(reply_header(ExStatus, Options), Header), 479 send_reply_header(Out, Header), 480 reply_status_body(Out, Body, Options).
487status_has_content(created(_Location)). 488status_has_content(moved(_To)). 489status_has_content(moved_temporary(_To)). 490status_has_content(gone(_URL)). 491status_has_content(see_other(_To)). 492status_has_content(bad_request(_ErrorTerm)). 493status_has_content(authorise(_Method)). 494status_has_content(forbidden(_URL)). 495status_has_content(not_found(_URL)). 496status_has_content(method_not_allowed(_Method, _URL)). 497status_has_content(not_acceptable(_Why)). 498status_has_content(server_error(_ErrorTerm)). 499status_has_content(service_unavailable(_Why)).
510serialize_body(Reply, Body) :- 511 http:serialize_reply(Reply, Body), 512 !. 513serialize_body(html_tokens(Tokens), body(text/html, utf8, Content)) :- 514 !, 515 with_output_to(string(Content), print_html(Tokens)). 516serialize_body(Reply, Reply) :- 517 Reply = body(_,_,_), 518 !. 519serialize_body(Reply, _) :- 520 domain_error(http_reply_body, Reply). 521 522reply_status_body(_, _, Options) :- 523 Options.method == head, 524 !. 525reply_status_body(Out, body(_Type, Encoding, Content), _Options) :- 526 ( Encoding == octet 527 -> format(Out, '~s', [Content]) 528 ; setup_call_cleanup( 529 set_stream(Out, encoding(Encoding)), 530 format(Out, '~s', [Content]), 531 set_stream(Out, encoding(octet))) 532 ).
559status_page_hook(Term, Reply, Options) :- 560 Context = Options.context, 561 functor(Term, Name, _), 562 status_number_fact(Name, Code), 563 ( Options.code = Code, 564 http:status_reply(Term, Reply, Options) 565 ; http:status_page(Term, Context, HTML), 566 Reply = html_tokens(HTML) 567 ; http:status_page(Code, Context, HTML), % deprecated 568 Reply = html_tokens(HTML) 569 ), 570 !. 571status_page_hook(created(Location), html_tokens(HTML), _Options) :- 572 phrase(page([ title('201 Created') 573 ], 574 [ h1('Created'), 575 p(['The document was created ', 576 a(href(Location), ' Here') 577 ]), 578 \address 579 ]), 580 HTML). 581status_page_hook(moved(To), html_tokens(HTML), _Options) :- 582 phrase(page([ title('301 Moved Permanently') 583 ], 584 [ h1('Moved Permanently'), 585 p(['The document has moved ', 586 a(href(To), ' Here') 587 ]), 588 \address 589 ]), 590 HTML). 591status_page_hook(moved_temporary(To), html_tokens(HTML), _Options) :- 592 phrase(page([ title('302 Moved Temporary') 593 ], 594 [ h1('Moved Temporary'), 595 p(['The document is currently ', 596 a(href(To), ' Here') 597 ]), 598 \address 599 ]), 600 HTML). 601status_page_hook(gone(URL), html_tokens(HTML), _Options) :- 602 phrase(page([ title('410 Resource Gone') 603 ], 604 [ h1('Resource Gone'), 605 p(['The document has been removed ', 606 a(href(URL), ' from here') 607 ]), 608 \address 609 ]), 610 HTML). 611status_page_hook(see_other(To), html_tokens(HTML), _Options) :- 612 phrase(page([ title('303 See Other') 613 ], 614 [ h1('See Other'), 615 p(['See other document ', 616 a(href(To), ' Here') 617 ]), 618 \address 619 ]), 620 HTML). 621status_page_hook(bad_request(ErrorTerm), html_tokens(HTML), _Options) :- 622 '$messages':translate_message(ErrorTerm, Lines, []), 623 phrase(page([ title('400 Bad Request') 624 ], 625 [ h1('Bad Request'), 626 p(\html_message_lines(Lines)), 627 \address 628 ]), 629 HTML). 630status_page_hook(authorise(_Method), html_tokens(HTML), _Options):- 631 phrase(page([ title('401 Authorization Required') 632 ], 633 [ h1('Authorization Required'), 634 p(['This server could not verify that you ', 635 'are authorized to access the document ', 636 'requested. Either you supplied the wrong ', 637 'credentials (e.g., bad password), or your ', 638 'browser doesn\'t understand how to supply ', 639 'the credentials required.' 640 ]), 641 \address 642 ]), 643 HTML). 644status_page_hook(forbidden(URL), html_tokens(HTML), _Options) :- 645 phrase(page([ title('403 Forbidden') 646 ], 647 [ h1('Forbidden'), 648 p(['You don\'t have permission to access ', URL, 649 ' on this server' 650 ]), 651 \address 652 ]), 653 HTML). 654status_page_hook(not_found(URL), html_tokens(HTML), _Options) :- 655 phrase(page([ title('404 Not Found') 656 ], 657 [ h1('Not Found'), 658 p(['The requested URL ', tt(URL), 659 ' was not found on this server' 660 ]), 661 \address 662 ]), 663 HTML). 664status_page_hook(method_not_allowed(Method,URL), html_tokens(HTML), _Options) :- 665 upcase_atom(Method, UMethod), 666 phrase(page([ title('405 Method not allowed') 667 ], 668 [ h1('Method not allowed'), 669 p(['The requested URL ', tt(URL), 670 ' does not support method ', tt(UMethod), '.' 671 ]), 672 \address 673 ]), 674 HTML). 675status_page_hook(not_acceptable(WhyHTML), html_tokens(HTML), _Options) :- 676 phrase(page([ title('406 Not Acceptable') 677 ], 678 [ h1('Not Acceptable'), 679 WhyHTML, 680 \address 681 ]), 682 HTML). 683status_page_hook(server_error(ErrorTerm), html_tokens(HTML), _Options) :- 684 '$messages':translate_message(ErrorTerm, Lines, []), 685 phrase(page([ title('500 Internal server error') 686 ], 687 [ h1('Internal server error'), 688 p(\html_message_lines(Lines)), 689 \address 690 ]), 691 HTML). 692status_page_hook(service_unavailable(Why), html_tokens(HTML), _Options) :- 693 phrase(page([ title('503 Service Unavailable') 694 ], 695 [ h1('Service Unavailable'), 696 \unavailable(Why), 697 \address 698 ]), 699 HTML). 700 (busy)--> 702 html(p(['The server is temporarily out of resources, ', 703 'please try again later'])). 704unavailable(error(Formal,Context)) --> 705 { '$messages':translate_message(error(Formal,Context), Lines, []) }, 706 html_message_lines(Lines). 707unavailable(HTML) --> 708 html(HTML). 709 710html_message_lines([]) --> 711 []. 712html_message_lines([nl|T]) --> 713 !, 714 html([br([])]), 715 html_message_lines(T). 716html_message_lines([flush]) --> 717 []. 718html_message_lines([ansi(_Style,Fmt,Args)|T]) --> 719 !, 720 { format(string(S), Fmt, Args) 721 }, 722 html([S]), 723 html_message_lines(T). 724html_message_lines([url(Pos)|T]) --> 725 !, 726 msg_url(Pos), 727 html_message_lines(T). 728html_message_lines([url(URL, Label)|T]) --> 729 !, 730 html(a(href(URL), Label)), 731 html_message_lines(T). 732html_message_lines([Fmt-Args|T]) --> 733 !, 734 { format(string(S), Fmt, Args) 735 }, 736 html([S]), 737 html_message_lines(T). 738html_message_lines([Fmt|T]) --> 739 !, 740 { format(string(S), Fmt, []) 741 }, 742 html([S]), 743 html_message_lines(T). 744 745msg_url(File:Line:Pos) --> 746 !, 747 html([File, :, Line, :, Pos]). 748msg_url(File:Line) --> 749 !, 750 html([File, :, Line]). 751msg_url(File) --> 752 html([File]).
759http_join_headers([], H, H). 760http_join_headers([H|T], Hdr0, Hdr) :- 761 functor(H, N, A), 762 functor(H2, N, A), 763 member(H2, Hdr0), 764 !, 765 http_join_headers(T, Hdr0, Hdr). 766http_join_headers([H|T], Hdr0, [H|Hdr]) :- 767 http_join_headers(T, Hdr0, Hdr).
779http_update_encoding(Header0, utf8, [content_type(Type)|Header]) :- 780 select(content_type(Type0), Header0, Header), 781 sub_atom(Type0, 0, _, _, 'text/'), 782 !, 783 ( sub_atom(Type0, S, _, _, ';') 784 -> sub_atom(Type0, 0, S, _, B) 785 ; B = Type0 786 ), 787 atom_concat(B, '; charset=UTF-8', Type). 788http_update_encoding(Header, Encoding, Header) :- 789 memberchk(content_type(Type), Header), 790 ( sub_atom_icasechk(Type, _, 'utf-8') 791 -> Encoding = utf8 792 ; http:mime_type_encoding(Type, Encoding) 793 -> true 794 ; mime_type_encoding(Type, Encoding) 795 ). 796http_update_encoding(Header, octet, Header).
803mime_type_encoding('application/json', utf8). 804mime_type_encoding('application/jsonrequest', utf8). 805mime_type_encoding('application/x-prolog', utf8). 806mime_type_encoding('application/n-quads', utf8). 807mime_type_encoding('application/n-triples', utf8). 808mime_type_encoding('application/sparql-query', utf8). 809mime_type_encoding('application/trig', utf8). 810mime_type_encoding('application/sparql-results+json', utf8). 811mime_type_encoding('application/sparql-results+xml', 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.827http_update_connection(CgiHeader, Request, Connect, 828 [connection(Connect)|Rest]) :- 829 select(connection(CgiConn), CgiHeader, Rest), 830 !, 831 connection(Request, ReqConnection), 832 join_connection(ReqConnection, CgiConn, Connect). 833http_update_connection(CgiHeader, Request, Connect, 834 [connection(Connect)|CgiHeader]) :- 835 connection(Request, Connect). 836 837join_connection(Keep1, Keep2, Connection) :- 838 ( downcase_atom(Keep1, 'keep-alive'), 839 downcase_atom(Keep2, 'keep-alive') 840 -> Connection = 'Keep-Alive' 841 ; Connection = close 842 ).
849connection(Header, Close) :-
850 ( memberchk(connection(Connection), Header)
851 -> Close = Connection
852 ; memberchk(http_version(1-X), Header),
853 X >= 1
854 -> Close = 'Keep-Alive'
855 ; Close = close
856 ).
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.
875http_update_transfer(Request, CgiHeader, Transfer, Header) :- 876 setting(http:chunked_transfer, When), 877 http_update_transfer(When, Request, CgiHeader, Transfer, Header). 878 879http_update_transfer(never, _, CgiHeader, none, Header) :- 880 !, 881 delete(CgiHeader, transfer_encoding(_), Header). 882http_update_transfer(_, _, CgiHeader, none, Header) :- 883 memberchk(location(_), CgiHeader), 884 !, 885 delete(CgiHeader, transfer_encoding(_), Header). 886http_update_transfer(_, Request, CgiHeader, Transfer, Header) :- 887 select(transfer_encoding(CgiTransfer), CgiHeader, Rest), 888 !, 889 transfer(Request, ReqConnection), 890 join_transfer(ReqConnection, CgiTransfer, Transfer), 891 ( Transfer == none 892 -> Header = Rest 893 ; Header = [transfer_encoding(Transfer)|Rest] 894 ). 895http_update_transfer(if_possible, Request, CgiHeader, Transfer, Header) :- 896 transfer(Request, Transfer), 897 Transfer \== none, 898 !, 899 Header = [transfer_encoding(Transfer)|CgiHeader]. 900http_update_transfer(_, _, CgiHeader, none, CgiHeader). 901 902join_transfer(chunked, chunked, chunked) :- !. 903join_transfer(_, _, none).
910transfer(Header, Transfer) :-
911 ( memberchk(transfer_encoding(Transfer0), Header)
912 -> Transfer = Transfer0
913 ; memberchk(http_version(1-X), Header),
914 X >= 1
915 -> Transfer = chunked
916 ; Transfer = none
917 ).
926content_length_in_encoding(Enc, Stream, Bytes) :- 927 stream_property(Stream, position(Here)), 928 setup_call_cleanup( 929 open_null_stream(Out), 930 ( set_stream(Out, encoding(Enc)), 931 catch(copy_stream_data(Stream, Out), _, fail), 932 flush_output(Out), 933 byte_count(Out, Bytes) 934 ), 935 ( close(Out, [force(true)]), 936 set_stream_position(Stream, Here) 937 )). 938 939 940 /******************************* 941 * POST SUPPORT * 942 *******************************/
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
json(+Term)
Posting a JSON query and processing the JSON reply (or any other
reply understood by http_read_data/3) is simple as
http_post(URL, json(Term), Reply, [])
, where Term is a JSON
term as described in json.pl
and reply is of the same format if
the server replies with JSON, when using module :-
use_module(library(http/http_json))
. Note that the module is
used in both http server and http client, see
library(http/http_json).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, []), ...,
1045http_post_data(Data, Out, HdrExtra) :- 1046 http:post_data_hook(Data, Out, HdrExtra), 1047 !. 1048http_post_data(html(HTML), Out, HdrExtra) :- 1049 !, 1050 phrase(post_header(html(HTML), HdrExtra), Header), 1051 send_request_header(Out, Header), 1052 print_html(Out, HTML). 1053http_post_data(xml(XML), Out, HdrExtra) :- 1054 !, 1055 http_post_data(xml(text/xml, XML, []), Out, HdrExtra). 1056http_post_data(xml(Type, XML), Out, HdrExtra) :- 1057 !, 1058 http_post_data(xml(Type, XML, []), Out, HdrExtra). 1059http_post_data(xml(Type, XML, Options), Out, HdrExtra) :- 1060 !, 1061 setup_call_cleanup( 1062 new_memory_file(MemFile), 1063 ( setup_call_cleanup( 1064 open_memory_file(MemFile, write, MemOut), 1065 xml_write(MemOut, XML, Options), 1066 close(MemOut)), 1067 http_post_data(memory_file(Type, MemFile), Out, HdrExtra) 1068 ), 1069 free_memory_file(MemFile)). 1070http_post_data(file(File), Out, HdrExtra) :- 1071 !, 1072 ( file_mime_type(File, Type) 1073 -> true 1074 ; Type = text/plain 1075 ), 1076 http_post_data(file(Type, File), Out, HdrExtra). 1077http_post_data(file(Type, File), Out, HdrExtra) :- 1078 !, 1079 phrase(post_header(file(Type, File), HdrExtra), Header), 1080 send_request_header(Out, Header), 1081 setup_call_cleanup( 1082 open(File, read, In, [type(binary)]), 1083 copy_stream_data(In, Out), 1084 close(In)). 1085http_post_data(memory_file(Type, Handle), Out, HdrExtra) :- 1086 !, 1087 phrase(post_header(memory_file(Type, Handle), HdrExtra), Header), 1088 send_request_header(Out, Header), 1089 setup_call_cleanup( 1090 open_memory_file(Handle, read, In, [encoding(octet)]), 1091 copy_stream_data(In, Out), 1092 close(In)). 1093http_post_data(codes(Codes), Out, HdrExtra) :- 1094 !, 1095 http_post_data(codes(text/plain, Codes), Out, HdrExtra). 1096http_post_data(codes(Type, Codes), Out, HdrExtra) :- 1097 !, 1098 phrase(post_header(codes(Type, Codes), HdrExtra), Header), 1099 send_request_header(Out, Header), 1100 setup_call_cleanup( 1101 set_stream(Out, encoding(utf8)), 1102 format(Out, '~s', [Codes]), 1103 set_stream(Out, encoding(octet))). 1104http_post_data(bytes(Type, Bytes), Out, HdrExtra) :- 1105 !, 1106 phrase(post_header(bytes(Type, Bytes), HdrExtra), Header), 1107 send_request_header(Out, Header), 1108 format(Out, '~s', [Bytes]). 1109http_post_data(atom(Atom), Out, HdrExtra) :- 1110 !, 1111 http_post_data(atom(text/plain, Atom), Out, HdrExtra). 1112http_post_data(atom(Type, Atom), Out, HdrExtra) :- 1113 !, 1114 phrase(post_header(atom(Type, Atom), HdrExtra), Header), 1115 send_request_header(Out, Header), 1116 setup_call_cleanup( 1117 set_stream(Out, encoding(utf8)), 1118 write(Out, Atom), 1119 set_stream(Out, encoding(octet))). 1120http_post_data(string(String), Out, HdrExtra) :- 1121 !, 1122 http_post_data(atom(text/plain, String), Out, HdrExtra). 1123http_post_data(string(Type, String), Out, HdrExtra) :- 1124 !, 1125 phrase(post_header(string(Type, String), HdrExtra), Header), 1126 send_request_header(Out, Header), 1127 setup_call_cleanup( 1128 set_stream(Out, encoding(utf8)), 1129 write(Out, String), 1130 set_stream(Out, encoding(octet))). 1131http_post_data(cgi_stream(In, _Len), Out, HdrExtra) :- 1132 !, 1133 debug(obsolete, 'Obsolete 2nd argument in cgi_stream(In,Len)', []), 1134 http_post_data(cgi_stream(In), Out, HdrExtra). 1135http_post_data(cgi_stream(In), Out, HdrExtra) :- 1136 !, 1137 http_read_header(In, Header0), 1138 http_update_encoding(Header0, Encoding, Header), 1139 content_length_in_encoding(Encoding, In, Size), 1140 http_join_headers(HdrExtra, Header, Hdr2), 1141 phrase(post_header(cgi_data(Size), Hdr2), HeaderText), 1142 send_request_header(Out, HeaderText), 1143 setup_call_cleanup( 1144 set_stream(Out, encoding(Encoding)), 1145 copy_stream_data(In, Out), 1146 set_stream(Out, encoding(octet))). 1147http_post_data(form(Fields), Out, HdrExtra) :- 1148 !, 1149 parse_url_search(Codes, Fields), 1150 length(Codes, Size), 1151 http_join_headers(HdrExtra, 1152 [ content_type('application/x-www-form-urlencoded') 1153 ], Header), 1154 phrase(post_header(cgi_data(Size), Header), HeaderChars), 1155 send_request_header(Out, HeaderChars), 1156 format(Out, '~s', [Codes]). 1157http_post_data(form_data(Data), Out, HdrExtra) :- 1158 !, 1159 setup_call_cleanup( 1160 new_memory_file(MemFile), 1161 ( setup_call_cleanup( 1162 open_memory_file(MemFile, write, MimeOut), 1163 mime_pack(Data, MimeOut, Boundary), 1164 close(MimeOut)), 1165 size_memory_file(MemFile, Size, octet), 1166 format(string(ContentType), 1167 'multipart/form-data; boundary=~w', [Boundary]), 1168 http_join_headers(HdrExtra, 1169 [ mime_version('1.0'), 1170 content_type(ContentType) 1171 ], Header), 1172 phrase(post_header(cgi_data(Size), Header), HeaderChars), 1173 send_request_header(Out, HeaderChars), 1174 setup_call_cleanup( 1175 open_memory_file(MemFile, read, In, [encoding(octet)]), 1176 copy_stream_data(In, Out), 1177 close(In)) 1178 ), 1179 free_memory_file(MemFile)). 1180http_post_data(List, Out, HdrExtra) :- % multipart-mixed 1181 is_list(List), 1182 !, 1183 setup_call_cleanup( 1184 new_memory_file(MemFile), 1185 ( setup_call_cleanup( 1186 open_memory_file(MemFile, write, MimeOut), 1187 mime_pack(List, MimeOut, Boundary), 1188 close(MimeOut)), 1189 size_memory_file(MemFile, Size, octet), 1190 format(string(ContentType), 1191 'multipart/mixed; boundary=~w', [Boundary]), 1192 http_join_headers(HdrExtra, 1193 [ mime_version('1.0'), 1194 content_type(ContentType) 1195 ], Header), 1196 phrase(post_header(cgi_data(Size), Header), HeaderChars), 1197 send_request_header(Out, HeaderChars), 1198 setup_call_cleanup( 1199 open_memory_file(MemFile, read, In, [encoding(octet)]), 1200 copy_stream_data(In, Out), 1201 close(In)) 1202 ), 1203 free_memory_file(MemFile)).
1210post_header(html(Tokens), HdrExtra) --> 1211 header_fields(HdrExtra, Len), 1212 content_length(html(Tokens), Len), 1213 content_type(text/html), 1214 "\r\n". 1215post_header(file(Type, File), HdrExtra) --> 1216 header_fields(HdrExtra, Len), 1217 content_length(file(File), Len), 1218 content_type(Type), 1219 "\r\n". 1220post_header(memory_file(Type, File), HdrExtra) --> 1221 header_fields(HdrExtra, Len), 1222 content_length(memory_file(File), Len), 1223 content_type(Type), 1224 "\r\n". 1225post_header(cgi_data(Size), HdrExtra) --> 1226 header_fields(HdrExtra, Len), 1227 content_length(Size, Len), 1228 "\r\n". 1229post_header(codes(Type, Codes), HdrExtra) --> 1230 header_fields(HdrExtra, Len), 1231 content_length(codes(Codes, utf8), Len), 1232 content_type(Type, utf8), 1233 "\r\n". 1234post_header(bytes(Type, Bytes), HdrExtra) --> 1235 header_fields(HdrExtra, Len), 1236 content_length(bytes(Bytes), Len), 1237 content_type(Type), 1238 "\r\n". 1239post_header(atom(Type, Atom), HdrExtra) --> 1240 header_fields(HdrExtra, Len), 1241 content_length(atom(Atom, utf8), Len), 1242 content_type(Type, utf8), 1243 "\r\n". 1244post_header(string(Type, String), HdrExtra) --> 1245 header_fields(HdrExtra, Len), 1246 content_length(string(String, utf8), Len), 1247 content_type(Type, utf8), 1248 "\r\n". 1249 1250 1251 /******************************* 1252 * OUTPUT HEADER DCG * 1253 *******************************/
1260http_reply_header(Out, What, HdrExtra) :-
1261 phrase(reply_header(What, HdrExtra, _Code), String),
1262 !,
1263 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)
1287reply_header(Data, Dict) --> 1288 { _{header:HdrExtra, code:Code} :< Dict }, 1289 reply_header(Data, HdrExtra, Code). 1290 1291reply_header(string(String), HdrExtra, Code) --> 1292 reply_header(string(text/plain, String), HdrExtra, Code). 1293reply_header(string(Type, String), HdrExtra, Code) --> 1294 vstatus(ok, Code, HdrExtra), 1295 date(now), 1296 header_fields(HdrExtra, CLen), 1297 content_length(codes(String, utf8), CLen), 1298 content_type(Type, utf8), 1299 "\r\n". 1300reply_header(bytes(Type, Bytes), HdrExtra, Code) --> 1301 vstatus(ok, Code, HdrExtra), 1302 date(now), 1303 header_fields(HdrExtra, CLen), 1304 content_length(bytes(Bytes), CLen), 1305 content_type(Type), 1306 "\r\n". 1307reply_header(html(Tokens), HdrExtra, Code) --> 1308 vstatus(ok, Code, HdrExtra), 1309 date(now), 1310 header_fields(HdrExtra, CLen), 1311 content_length(html(Tokens), CLen), 1312 content_type(text/html), 1313 "\r\n". 1314reply_header(file(Type, File), HdrExtra, Code) --> 1315 vstatus(ok, Code, HdrExtra), 1316 date(now), 1317 modified(file(File)), 1318 header_fields(HdrExtra, CLen), 1319 content_length(file(File), CLen), 1320 content_type(Type), 1321 "\r\n". 1322reply_header(gzip_file(Type, File), HdrExtra, Code) --> 1323 vstatus(ok, Code, HdrExtra), 1324 date(now), 1325 modified(file(File)), 1326 header_fields(HdrExtra, CLen), 1327 content_length(file(File), CLen), 1328 content_type(Type), 1329 content_encoding(gzip), 1330 "\r\n". 1331reply_header(file(Type, File, Range), HdrExtra, Code) --> 1332 vstatus(partial_content, Code, HdrExtra), 1333 date(now), 1334 modified(file(File)), 1335 header_fields(HdrExtra, CLen), 1336 content_length(file(File, Range), CLen), 1337 content_type(Type), 1338 "\r\n". 1339reply_header(tmp_file(Type, File), HdrExtra, Code) --> 1340 vstatus(ok, Code, HdrExtra), 1341 date(now), 1342 header_fields(HdrExtra, CLen), 1343 content_length(file(File), CLen), 1344 content_type(Type), 1345 "\r\n". 1346reply_header(cgi_data(Size), HdrExtra, Code) --> 1347 vstatus(ok, Code, HdrExtra), 1348 date(now), 1349 header_fields(HdrExtra, CLen), 1350 content_length(Size, CLen), 1351 "\r\n". 1352reply_header(chunked_data, HdrExtra, Code) --> 1353 vstatus(ok, Code, HdrExtra), 1354 date(now), 1355 header_fields(HdrExtra, _), 1356 ( {memberchk(transfer_encoding(_), HdrExtra)} 1357 -> "" 1358 ; transfer_encoding(chunked) 1359 ), 1360 "\r\n". 1361% non-200 replies without a body (e.g., 1xx, 204, 304) 1362reply_header(status(Status), HdrExtra, Code) --> 1363 vstatus(Status, Code), 1364 header_fields(HdrExtra, Clen), 1365 { Clen = 0 }, 1366 "\r\n". 1367% non-200 replies with a body 1368reply_header(Data, HdrExtra, Code) --> 1369 { status_reply_headers(Data, 1370 body(Type, Encoding, Content), 1371 ReplyHeaders), 1372 http_join_headers(ReplyHeaders, HdrExtra, Headers), 1373 functor(Data, CodeName, _) 1374 }, 1375 vstatus(CodeName, Code, Headers), 1376 date(now), 1377 header_fields(Headers, CLen), 1378 content_length(codes(Content, Encoding), CLen), 1379 content_type(Type, Encoding), 1380 "\r\n". 1381 1382status_reply_headers(created(Location, Body), Body, 1383 [ location(Location) ]). 1384status_reply_headers(moved(To, Body), Body, 1385 [ location(To) ]). 1386status_reply_headers(moved_temporary(To, Body), Body, 1387 [ location(To) ]). 1388status_reply_headers(gone(_URL, Body), Body, []). 1389status_reply_headers(see_other(To, Body), Body, 1390 [ location(To) ]). 1391status_reply_headers(authorise(Method, Body), Body, 1392 [ www_authenticate(Method) ]). 1393status_reply_headers(not_found(_URL, Body), Body, []). 1394status_reply_headers(forbidden(_URL, Body), Body, []). 1395status_reply_headers(method_not_allowed(_Method, _URL, Body), Body, []). 1396status_reply_headers(server_error(_Error, Body), Body, []). 1397status_reply_headers(service_unavailable(_Why, Body), Body, []). 1398status_reply_headers(not_acceptable(_Why, Body), Body, []). 1399status_reply_headers(bad_request(_Error, Body), Body, []).
1407vstatus(_Status, Code, HdrExtra) --> 1408 {memberchk(status(Code), HdrExtra)}, 1409 !, 1410 vstatus(_NewStatus, Code). 1411vstatus(Status, Code, _) --> 1412 vstatus(Status, Code). 1413 1414vstatus(Status, Code) --> 1415 "HTTP/1.1 ", 1416 status_number(Status, Code), 1417 " ", 1418 status_comment(Status), 1419 "\r\n".
1428status_number(Status, Code) --> 1429 { var(Status) }, 1430 !, 1431 integer(Code), 1432 { status_number(Status, Code) }, 1433 !. 1434status_number(Status, Code) --> 1435 { status_number(Status, Code) }, 1436 integer(Code).
1450% Unrecognized status codes that are within a defined code class. 1451% RFC 7231 states: 1452% "[...] a client MUST understand the class of any status code, 1453% as indicated by the first digit, and treat an unrecognized status code 1454% as being equivalent to the `x00` status code of that class [...] 1455% " 1456% @see http://tools.ietf.org/html/rfc7231#section-6 1457 1458status_number(Status, Code) :- 1459 nonvar(Status), 1460 !, 1461 status_number_fact(Status, Code). 1462status_number(Status, Code) :- 1463 nonvar(Code), 1464 !, 1465 ( between(100, 599, Code) 1466 -> ( status_number_fact(Status, Code) 1467 -> true 1468 ; ClassCode is Code // 100 * 100, 1469 status_number_fact(Status, ClassCode) 1470 ) 1471 ; domain_error(http_code, Code) 1472 ). 1473 1474status_number_fact(continue, 100). 1475status_number_fact(switching_protocols, 101). 1476status_number_fact(ok, 200). 1477status_number_fact(created, 201). 1478status_number_fact(accepted, 202). 1479status_number_fact(non_authoritative_info, 203). 1480status_number_fact(no_content, 204). 1481status_number_fact(reset_content, 205). 1482status_number_fact(partial_content, 206). 1483status_number_fact(multiple_choices, 300). 1484status_number_fact(moved, 301). 1485status_number_fact(moved_temporary, 302). 1486status_number_fact(see_other, 303). 1487status_number_fact(not_modified, 304). 1488status_number_fact(use_proxy, 305). 1489status_number_fact(unused, 306). 1490status_number_fact(temporary_redirect, 307). 1491status_number_fact(bad_request, 400). 1492status_number_fact(authorise, 401). 1493status_number_fact(payment_required, 402). 1494status_number_fact(forbidden, 403). 1495status_number_fact(not_found, 404). 1496status_number_fact(method_not_allowed, 405). 1497status_number_fact(not_acceptable, 406). 1498status_number_fact(request_timeout, 408). 1499status_number_fact(conflict, 409). 1500status_number_fact(gone, 410). 1501status_number_fact(length_required, 411). 1502status_number_fact(payload_too_large, 413). 1503status_number_fact(uri_too_long, 414). 1504status_number_fact(unsupported_media_type, 415). 1505status_number_fact(expectation_failed, 417). 1506status_number_fact(upgrade_required, 426). 1507status_number_fact(server_error, 500). 1508status_number_fact(not_implemented, 501). 1509status_number_fact(bad_gateway, 502). 1510status_number_fact(service_unavailable, 503). 1511status_number_fact(gateway_timeout, 504). 1512status_number_fact(http_version_not_supported, 505).
1519status_comment(continue) --> 1520 "Continue". 1521status_comment(switching_protocols) --> 1522 "Switching Protocols". 1523status_comment(ok) --> 1524 "OK". 1525status_comment(created) --> 1526 "Created". 1527status_comment(accepted) --> 1528 "Accepted". 1529status_comment(non_authoritative_info) --> 1530 "Non-Authoritative Information". 1531status_comment(no_content) --> 1532 "No Content". 1533status_comment(reset_content) --> 1534 "Reset Content". 1535status_comment(created) --> 1536 "Created". 1537status_comment(partial_content) --> 1538 "Partial content". 1539status_comment(multiple_choices) --> 1540 "Multiple Choices". 1541status_comment(moved) --> 1542 "Moved Permanently". 1543status_comment(moved_temporary) --> 1544 "Moved Temporary". 1545status_comment(see_other) --> 1546 "See Other". 1547status_comment(not_modified) --> 1548 "Not Modified". 1549status_comment(use_proxy) --> 1550 "Use Proxy". 1551status_comment(unused) --> 1552 "Unused". 1553status_comment(temporary_redirect) --> 1554 "Temporary Redirect". 1555status_comment(bad_request) --> 1556 "Bad Request". 1557status_comment(authorise) --> 1558 "Authorization Required". 1559status_comment(payment_required) --> 1560 "Payment Required". 1561status_comment(forbidden) --> 1562 "Forbidden". 1563status_comment(not_found) --> 1564 "Not Found". 1565status_comment(method_not_allowed) --> 1566 "Method Not Allowed". 1567status_comment(not_acceptable) --> 1568 "Not Acceptable". 1569status_comment(request_timeout) --> 1570 "Request Timeout". 1571status_comment(conflict) --> 1572 "Conflict". 1573status_comment(gone) --> 1574 "Gone". 1575status_comment(length_required) --> 1576 "Length Required". 1577status_comment(payload_too_large) --> 1578 "Payload Too Large". 1579status_comment(uri_too_long) --> 1580 "URI Too Long". 1581status_comment(unsupported_media_type) --> 1582 "Unsupported Media Type". 1583status_comment(expectation_failed) --> 1584 "Expectation Failed". 1585status_comment(upgrade_required) --> 1586 "Upgrade Required". 1587status_comment(server_error) --> 1588 "Internal Server Error". 1589status_comment(not_implemented) --> 1590 "Not Implemented". 1591status_comment(bad_gateway) --> 1592 "Bad Gateway". 1593status_comment(service_unavailable) --> 1594 "Service Unavailable". 1595status_comment(gateway_timeout) --> 1596 "Gateway Timeout". 1597status_comment(http_version_not_supported) --> 1598 "HTTP Version Not Supported". 1599 1600date(Time) --> 1601 "Date: ", 1602 ( { Time == now } 1603 -> now 1604 ; rfc_date(Time) 1605 ), 1606 "\r\n". 1607 1608modified(file(File)) --> 1609 !, 1610 { time_file(File, Time) 1611 }, 1612 modified(Time). 1613modified(Time) --> 1614 "Last-modified: ", 1615 ( { Time == now } 1616 -> now 1617 ; rfc_date(Time) 1618 ), 1619 "\r\n".
1629content_length(file(File, bytes(From, To)), Len) --> 1630 !, 1631 { size_file(File, Size), 1632 ( To == end 1633 -> Len is Size - From, 1634 RangeEnd is Size - 1 1635 ; Len is To+1 - From, % To is index of last byte 1636 RangeEnd = To 1637 ) 1638 }, 1639 content_range(bytes, From, RangeEnd, Size), 1640 content_length(Len, Len). 1641content_length(Reply, Len) --> 1642 { length_of(Reply, Len) 1643 }, 1644 "Content-Length: ", integer(Len), 1645 "\r\n". 1646 1647 1648length_of(_, Len) :- 1649 nonvar(Len), 1650 !. 1651length_of(string(String, Encoding), Len) :- 1652 length_of(codes(String, Encoding), Len). 1653length_of(codes(String, Encoding), Len) :- 1654 !, 1655 setup_call_cleanup( 1656 open_null_stream(Out), 1657 ( set_stream(Out, encoding(Encoding)), 1658 format(Out, '~s', [String]), 1659 byte_count(Out, Len) 1660 ), 1661 close(Out)). 1662length_of(atom(Atom, Encoding), Len) :- 1663 !, 1664 setup_call_cleanup( 1665 open_null_stream(Out), 1666 ( set_stream(Out, encoding(Encoding)), 1667 format(Out, '~a', [Atom]), 1668 byte_count(Out, Len) 1669 ), 1670 close(Out)). 1671length_of(file(File), Len) :- 1672 !, 1673 size_file(File, Len). 1674length_of(memory_file(Handle), Len) :- 1675 !, 1676 size_memory_file(Handle, Len, octet). 1677length_of(html_tokens(Tokens), Len) :- 1678 !, 1679 html_print_length(Tokens, Len). 1680length_of(html(Tokens), Len) :- % deprecated 1681 !, 1682 html_print_length(Tokens, Len). 1683length_of(bytes(Bytes), Len) :- 1684 !, 1685 ( string(Bytes) 1686 -> string_length(Bytes, Len) 1687 ; length(Bytes, Len) % assuming a list of 0..255 1688 ). 1689length_of(Len, Len).
Content-Range
header for partial content (206)
replies.1697content_range(Unit, From, RangeEnd, Size) --> 1698 "Content-Range: ", atom(Unit), " ", 1699 integer(From), "-", integer(RangeEnd), "/", integer(Size), 1700 "\r\n". 1701 1702content_encoding(Encoding) --> 1703 "Content-Encoding: ", atom(Encoding), "\r\n". 1704 1705transfer_encoding(Encoding) --> 1706 "Transfer-Encoding: ", atom(Encoding), "\r\n". 1707 1708content_type(Type) --> 1709 content_type(Type, _). 1710 1711content_type(Type, Charset) --> 1712 ctype(Type), 1713 charset(Charset), 1714 "\r\n". 1715 1716ctype(Main/Sub) --> 1717 !, 1718 "Content-Type: ", 1719 atom(Main), 1720 "/", 1721 atom(Sub). 1722ctype(Type) --> 1723 !, 1724 "Content-Type: ", 1725 atom(Type). 1726 1727charset(Var) --> 1728 { var(Var) }, 1729 !. 1730charset(utf8) --> 1731 !, 1732 "; charset=UTF-8". 1733charset(CharSet) --> 1734 "; charset=", 1735 atom(CharSet).
1743header_field(Name, Value) --> 1744 { var(Name) }, % parsing 1745 !, 1746 field_name(Name), 1747 ":", 1748 whites, 1749 read_field_value(ValueChars), 1750 blanks_to_nl, 1751 !, 1752 { field_to_prolog(Name, ValueChars, Value) 1753 -> true 1754 ; atom_codes(Value, ValueChars), 1755 domain_error(Name, Value) 1756 }. 1757header_field(Name, Value) --> 1758 field_name(Name), 1759 ": ", 1760 field_value(Name, Value), 1761 "\r\n".
1767read_field_value([H|T]) --> 1768 [H], 1769 { \+ code_type(H, space) }, 1770 !, 1771 read_field_value(T). 1772read_field_value([]) --> 1773 "". 1774read_field_value([H|T]) --> 1775 [H], 1776 read_field_value(T).
1783send_reply_header(Out, String) :- 1784 debug(http(send_reply), "< ~s", [String]), 1785 format(Out, '~s', [String]). 1786 1787send_request_header(Out, String) :- 1788 debug(http(send_request), "> ~s", [String]), 1789 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.
1829http_parse_header_value(Field, Value, Prolog) :- 1830 known_field(Field, _, Type), 1831 ( already_parsed(Type, Value) 1832 -> Prolog = Value 1833 ; to_codes(Value, Codes), 1834 parse_header_value(Field, Codes, Prolog) 1835 ). 1836 1837already_parsed(integer, V) :- !, integer(V). 1838already_parsed(list(Type), L) :- !, is_list(L), maplist(already_parsed(Type), L). 1839already_parsed(Term, V) :- subsumes_term(Term, V).
1847known_field(content_length, true, integer). 1848known_field(status, true, integer). 1849known_field(cookie, true, list(_=_)). 1850known_field(set_cookie, true, list(set_cookie(_Name,_Value,_Options))). 1851known_field(host, true, _Host:_Port). 1852known_field(range, maybe, bytes(_,_)). 1853known_field(accept, maybe, list(media(_Type, _Parms, _Q, _Exts))). 1854known_field(content_disposition, maybe, disposition(_Name, _Attributes)). 1855known_field(content_type, false, media(_Type/_Sub, _Attributes)). 1856 1857to_codes(In, Codes) :- 1858 ( is_list(In) 1859 -> Codes = In 1860 ; atom_codes(In, Codes) 1861 ).
known_fields(_,true)
, this must succeed. For maybe
, we just
return the atom if the translation fails.1869field_to_prolog(Field, Codes, Prolog) :- 1870 known_field(Field, true, _Type), 1871 !, 1872 ( parse_header_value(Field, Codes, Prolog0) 1873 -> Prolog = Prolog0 1874 ). 1875field_to_prolog(Field, Codes, Prolog) :- 1876 known_field(Field, maybe, _Type), 1877 parse_header_value(Field, Codes, Prolog0), 1878 !, 1879 Prolog = Prolog0. 1880field_to_prolog(_, Codes, Atom) :- 1881 atom_codes(Atom, Codes).
1888parse_header_value(content_length, ValueChars, ContentLength) :- 1889 number_codes(ContentLength, ValueChars). 1890parse_header_value(status, ValueChars, Code) :- 1891 ( phrase(" ", L, _), 1892 append(Pre, L, ValueChars) 1893 -> number_codes(Code, Pre) 1894 ; number_codes(Code, ValueChars) 1895 ). 1896parse_header_value(cookie, ValueChars, Cookies) :- 1897 debug(cookie, 'Cookie: ~s', [ValueChars]), 1898 phrase(cookies(Cookies), ValueChars). 1899parse_header_value(set_cookie, ValueChars, SetCookie) :- 1900 debug(cookie, 'SetCookie: ~s', [ValueChars]), 1901 phrase(set_cookie(SetCookie), ValueChars). 1902parse_header_value(host, ValueChars, Host) :- 1903 ( append(HostChars, [0':|PortChars], ValueChars), 1904 catch(number_codes(Port, PortChars), _, fail) 1905 -> atom_codes(HostName, HostChars), 1906 Host = HostName:Port 1907 ; atom_codes(Host, ValueChars) 1908 ). 1909parse_header_value(range, ValueChars, Range) :- 1910 phrase(range(Range), ValueChars). 1911parse_header_value(accept, ValueChars, Media) :- 1912 parse_accept(ValueChars, Media). 1913parse_header_value(content_disposition, ValueChars, Disposition) :- 1914 phrase(content_disposition(Disposition), ValueChars). 1915parse_header_value(content_type, ValueChars, Type) :- 1916 phrase(parse_content_type(Type), ValueChars).
1920field_value(_, set_cookie(Name, Value, Options)) --> 1921 !, 1922 atom(Name), "=", atom(Value), 1923 value_options(Options, cookie). 1924field_value(_, disposition(Disposition, Options)) --> 1925 !, 1926 atom(Disposition), value_options(Options, disposition). 1927field_value(www_authenticate, Auth) --> 1928 auth_field_value(Auth). 1929field_value(_, Atomic) --> 1930 atom(Atomic).
1936auth_field_value(negotiate(Data)) --> 1937 "Negotiate ", 1938 { base64(Data, DataBase64), 1939 atom_codes(DataBase64, Codes) 1940 }, 1941 string(Codes). 1942auth_field_value(negotiate) --> 1943 "Negotiate". 1944auth_field_value(basic) --> 1945 !, 1946 "Basic". 1947auth_field_value(basic(Realm)) --> 1948 "Basic Realm=\"", atom(Realm), "\"". 1949auth_field_value(digest) --> 1950 !, 1951 "Digest". 1952auth_field_value(digest(Details)) --> 1953 "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.1962value_options([], _) --> []. 1963value_options([H|T], Field) --> 1964 "; ", value_option(H, Field), 1965 value_options(T, Field). 1966 1967value_option(secure=true, cookie) --> 1968 !, 1969 "secure". 1970value_option(Name=Value, Type) --> 1971 { string_option(Name, Type) }, 1972 !, 1973 atom(Name), "=", 1974 qstring(Value). 1975value_option(Name=Value, Type) --> 1976 { token_option(Name, Type) }, 1977 !, 1978 atom(Name), "=", atom(Value). 1979value_option(Name=Value, _Type) --> 1980 atom(Name), "=", 1981 option_value(Value). 1982 1983string_option(filename, disposition). 1984 1985token_option(path, cookie). 1986 1987option_value(Value) --> 1988 { number(Value) }, 1989 !, 1990 number(Value). 1991option_value(Value) --> 1992 { ( atom(Value) 1993 -> true 1994 ; string(Value) 1995 ), 1996 forall(string_code(_, Value, C), 1997 token_char(C)) 1998 }, 1999 !, 2000 atom(Value). 2001option_value(Atomic) --> 2002 qstring(Atomic). 2003 2004qstring(Atomic) --> 2005 { string_codes(Atomic, Codes) }, 2006 "\"", 2007 qstring_codes(Codes), 2008 "\"". 2009 2010qstring_codes([]) --> []. 2011qstring_codes([H|T]) --> qstring_code(H), qstring_codes(T). 2012 2013qstring_code(C) --> {qstring_esc(C)}, !, "\\", [C]. 2014qstring_code(C) --> [C]. 2015 2016qstring_esc(0'"). 2017qstring_esc(C) :- ctl(C). 2018 2019 2020 /******************************* 2021 * ACCEPT HEADERS * 2022 *******************************/ 2023 2024:- dynamic accept_cache/2. 2025:- volatile accept_cache/2. 2026 2027parse_accept(Codes, Media) :- 2028 atom_codes(Atom, Codes), 2029 ( accept_cache(Atom, Media0) 2030 -> Media = Media0 2031 ; phrase(accept(Media0), Codes), 2032 keysort(Media0, Media1), 2033 pairs_values(Media1, Media2), 2034 assertz(accept_cache(Atom, Media2)), 2035 Media = Media2 2036 ).
2042accept([H|T]) --> 2043 blanks, 2044 media_range(H), 2045 blanks, 2046 ( "," 2047 -> accept(T) 2048 ; {T=[]} 2049 ). 2050 2051media_range(s(SortQuality,Spec)-media(Type, TypeParams, Quality, AcceptExts)) --> 2052 media_type(Type), 2053 blanks, 2054 ( ";" 2055 -> blanks, 2056 parameters_and_quality(TypeParams, Quality, AcceptExts) 2057 ; { TypeParams = [], 2058 Quality = 1.0, 2059 AcceptExts = [] 2060 } 2061 ), 2062 { SortQuality is float(-Quality), 2063 rank_specialised(Type, TypeParams, Spec) 2064 }.
2071content_disposition(disposition(Disposition, Options)) -->
2072 token(Disposition), blanks,
2073 value_parameters(Options).
media(Type/SubType,
Parameters)
.
2080parse_content_type(media(Type, Parameters)) -->
2081 media_type(Type), blanks,
2082 value_parameters(Parameters).
2093rank_specialised(Type/SubType, TypeParams, v(VT, VS, SortVP)) :- 2094 var_or_given(Type, VT), 2095 var_or_given(SubType, VS), 2096 length(TypeParams, VP), 2097 SortVP is -VP. 2098 2099var_or_given(V, Val) :- 2100 ( var(V) 2101 -> Val = 0 2102 ; Val = -1 2103 ). 2104 2105media_type(Type/SubType) --> 2106 type(Type), "/", type(SubType). 2107 2108type(_) --> 2109 "*", 2110 !. 2111type(Type) --> 2112 token(Type). 2113 2114parameters_and_quality(Params, Quality, AcceptExts) --> 2115 token(Name), 2116 blanks, "=", blanks, 2117 ( { Name == q } 2118 -> float(Quality), blanks, 2119 value_parameters(AcceptExts), 2120 { Params = [] } 2121 ; { Params = [Name=Value|T] }, 2122 parameter_value(Value), 2123 blanks, 2124 ( ";" 2125 -> blanks, 2126 parameters_and_quality(T, Quality, AcceptExts) 2127 ; { T = [], 2128 Quality = 1.0, 2129 AcceptExts = [] 2130 } 2131 ) 2132 ).
2139value_parameters([H|T]) --> 2140 ";", 2141 !, 2142 blanks, token(Name), blanks, 2143 ( "=" 2144 -> blanks, 2145 ( token(Value) 2146 -> [] 2147 ; quoted_string(Value) 2148 ), 2149 { H = (Name=Value) } 2150 ; { H = Name } 2151 ), 2152 blanks, 2153 value_parameters(T). 2154value_parameters([]) --> 2155 []. 2156 2157parameter_value(Value) --> token(Value), !. 2158parameter_value(Value) --> quoted_string(Value).
2165token(Name) --> 2166 token_char(C1), 2167 token_chars(Cs), 2168 { atom_codes(Name, [C1|Cs]) }. 2169 2170token_chars([H|T]) --> 2171 token_char(H), 2172 !, 2173 token_chars(T). 2174token_chars([]) --> []. 2175 2176token_char(C) :- 2177 \+ ctl(C), 2178 \+ separator_code(C). 2179 2180ctl(C) :- between(0,31,C), !. 2181ctl(127). 2182 2183separator_code(0'(). 2184separator_code(0')). 2185separator_code(0'<). 2186separator_code(0'>). 2187separator_code(0'@). 2188separator_code(0',). 2189separator_code(0';). 2190separator_code(0':). 2191separator_code(0'\\). 2192separator_code(0'"). 2193separator_code(0'/). 2194separator_code(0'[). 2195separator_code(0']). 2196separator_code(0'?). 2197separator_code(0'=). 2198separator_code(0'{). 2199separator_code(0'}). 2200separator_code(0'\s). 2201separator_code(0'\t). 2202 2203term_expansion(token_char(x) --> [x], Clauses) :- 2204 findall((token_char(C)-->[C]), 2205 ( between(0, 255, C), 2206 token_char(C) 2207 ), 2208 Clauses). 2209 2210token_char(x) --> [x].
2216quoted_string(Text) --> 2217 "\"", 2218 quoted_text(Codes), 2219 { atom_codes(Text, Codes) }. 2220 2221quoted_text([]) --> 2222 "\"", 2223 !. 2224quoted_text([H|T]) --> 2225 "\\", !, [H], 2226 quoted_text(T). 2227quoted_text([H|T]) --> 2228 [H], 2229 !, 2230 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.2241header_fields([], _) --> []. 2242header_fields([content_length(CLen)|T], CLen) --> 2243 !, 2244 ( { var(CLen) } 2245 -> "" 2246 ; header_field(content_length, CLen) 2247 ), 2248 header_fields(T, CLen). % Continue or return first only? 2249header_fields([status(_)|T], CLen) --> % handled by vstatus//3. 2250 !, 2251 header_fields(T, CLen). 2252header_fields([H|T], CLen) --> 2253 { H =.. [Name, Value] }, 2254 header_field(Name, Value), 2255 header_fields(T, CLen).
token = 1*<any CHAR except CTLs or separators> separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
2272:- public 2273 field_name//1. 2274 2275field_name(Name) --> 2276 { var(Name) }, 2277 !, 2278 rd_field_chars(Chars), 2279 { atom_codes(Name, Chars) }. 2280field_name(mime_version) --> 2281 !, 2282 "MIME-Version". 2283field_name(www_authenticate) --> 2284 !, 2285 "WWW-Authenticate". 2286field_name(Name) --> 2287 { atom_codes(Name, Chars) }, 2288 wr_field_chars(Chars). 2289 2290rd_field_chars_no_fold([C|T]) --> 2291 [C], 2292 { rd_field_char(C, _) }, 2293 !, 2294 rd_field_chars_no_fold(T). 2295rd_field_chars_no_fold([]) --> 2296 []. 2297 2298rd_field_chars([C0|T]) --> 2299 [C], 2300 { rd_field_char(C, C0) }, 2301 !, 2302 rd_field_chars(T). 2303rd_field_chars([]) --> 2304 [].
2310separators("()<>@,;:\\\"/[]?={} \t"). 2311 2312term_expansion(rd_field_char('expand me',_), Clauses) :- 2313 2314 Clauses = [ rd_field_char(0'-, 0'_) 2315 | Cls 2316 ], 2317 separators(SepString), 2318 string_codes(SepString, Seps), 2319 findall(rd_field_char(In, Out), 2320 ( between(32, 127, In), 2321 \+ memberchk(In, Seps), 2322 In \== 0'-, % 0' 2323 code_type(Out, to_lower(In))), 2324 Cls). 2325 2326rd_field_char('expand me', _). % avoid recursion 2327 2328wr_field_chars([C|T]) --> 2329 !, 2330 { code_type(C, to_lower(U)) }, 2331 [U], 2332 wr_field_chars2(T). 2333wr_field_chars([]) --> 2334 []. 2335 2336wr_field_chars2([]) --> []. 2337wr_field_chars2([C|T]) --> % 0' 2338 ( { C == 0'_ } 2339 -> "-", 2340 wr_field_chars(T) 2341 ; [C], 2342 wr_field_chars2(T) 2343 ).
2349now -->
2350 { get_time(Time)
2351 },
2352 rfc_date(Time).
2359rfc_date(Time, String, Tail) :-
2360 stamp_date_time(Time, Date, 'UTC'),
2361 format_time(codes(String, Tail),
2362 '%a, %d %b %Y %T GMT',
2363 Date, posix).
2369http_timestamp(Time, Atom) :- 2370 stamp_date_time(Time, Date, 'UTC'), 2371 format_time(atom(Atom), 2372 '%a, %d %b %Y %T GMT', 2373 Date, posix). 2374 2375 2376 /******************************* 2377 * REQUEST DCG * 2378 *******************************/ 2379 2380request(Fd, [method(Method),request_uri(ReqURI)|Header]) --> 2381 method(Method), 2382 blanks, 2383 nonblanks(Query), 2384 { atom_codes(ReqURI, Query), 2385 request_uri_parts(ReqURI, Header, Rest) 2386 }, 2387 request_header(Fd, Rest), 2388 !. 2389request(Fd, [unknown(What)|Header]) --> 2390 string(What), 2391 eos, 2392 !, 2393 { http_read_header(Fd, Header) 2394 -> true 2395 ; Header = [] 2396 }. 2397 2398method(get) --> "GET", !. 2399method(put) --> "PUT", !. 2400method(head) --> "HEAD", !. 2401method(post) --> "POST", !. 2402method(delete) --> "DELETE", !. 2403method(patch) --> "PATCH", !. 2404method(options) --> "OPTIONS", !. 2405method(trace) --> "TRACE", !.
2419request_uri_parts(ReqURI, [path(Path)|Parts], Rest) :- 2420 uri_components(ReqURI, Components), 2421 uri_data(path, Components, PathText), 2422 uri_encoded(path, Path, PathText), 2423 phrase(uri_parts(Components), Parts, Rest). 2424 2425uri_parts(Components) --> 2426 uri_search(Components), 2427 uri_fragment(Components). 2428 2429uri_search(Components) --> 2430 { uri_data(search, Components, Search), 2431 nonvar(Search), 2432 catch(uri_query_components(Search, Query), 2433 error(syntax_error(_),_), 2434 fail) 2435 }, 2436 !, 2437 [ search(Query) ]. 2438uri_search(_) --> []. 2439 2440uri_fragment(Components) --> 2441 { uri_data(fragment, Components, String), 2442 nonvar(String), 2443 !, 2444 uri_encoded(fragment, Fragment, String) 2445 }, 2446 [ fragment(Fragment) ]. 2447uri_fragment(_) --> [].
2454request_header(_, []) --> % Old-style non-version header 2455 blanks, 2456 eos, 2457 !. 2458request_header(Fd, [http_version(Version)|Header]) --> 2459 http_version(Version), 2460 blanks, 2461 eos, 2462 !, 2463 { Version = 1-_ 2464 -> http_read_header(Fd, Header) 2465 ; Header = [] 2466 }. 2467 2468http_version(Version) --> 2469 blanks, 2470 "HTTP/", 2471 http_version_number(Version). 2472 2473http_version_number(Major-Minor) --> 2474 integer(Major), 2475 ".", 2476 integer(Minor). 2477 2478 2479 /******************************* 2480 * COOKIES * 2481 *******************************/
2487cookies([Name=Value|T]) --> 2488 blanks, 2489 cookie(Name, Value), 2490 !, 2491 blanks, 2492 ( ";" 2493 -> cookies(T) 2494 ; { T = [] } 2495 ). 2496cookies(List) --> 2497 string(Skipped), 2498 ";", 2499 !, 2500 { print_message(warning, http(skipped_cookie(Skipped))) }, 2501 cookies(List). 2502cookies([]) --> 2503 blanks. 2504 Name, Value) (--> 2506 cookie_name(Name), 2507 blanks, "=", blanks, 2508 cookie_value(Value). 2509 Name) (--> 2511 { var(Name) }, 2512 !, 2513 rd_field_chars_no_fold(Chars), 2514 { atom_codes(Name, Chars) }. 2515 Value) (--> 2517 quoted_string(Value), 2518 !. 2519cookie_value(Value) --> 2520 chars_to_semicolon_or_blank(Chars), 2521 { atom_codes(Value, Chars) 2522 }. 2523 2524chars_to_semicolon_or_blank([]), ";" --> 2525 ";", 2526 !. 2527chars_to_semicolon_or_blank([]) --> 2528 " ", 2529 blanks, 2530 eos, 2531 !. 2532chars_to_semicolon_or_blank([H|T]) --> 2533 [H], 2534 !, 2535 chars_to_semicolon_or_blank(T). 2536chars_to_semicolon_or_blank([]) --> 2537 []. 2538 set_cookie(Name, Value, Options)) (--> 2540 ws, 2541 cookie(Name, Value), 2542 cookie_options(Options). 2543 [H|T]) (--> 2545 ws, 2546 ";", 2547 ws, 2548 cookie_option(H), 2549 !, 2550 cookie_options(T). 2551cookie_options([]) --> 2552 ws. 2553 2554ws --> " ", !, ws. 2555ws --> [].
Secure
and HttpOnly
.
2567cookie_option(Name=Value) --> 2568 rd_field_chars(NameChars), ws, 2569 { atom_codes(Name, NameChars) }, 2570 ( "=" 2571 -> ws, 2572 chars_to_semicolon(ValueChars), 2573 { atom_codes(Value, ValueChars) 2574 } 2575 ; { Value = true } 2576 ). 2577 2578chars_to_semicolon([H|T]) --> 2579 [H], 2580 { H \== 32, H \== 0'; }, 2581 !, 2582 chars_to_semicolon(T). 2583chars_to_semicolon([]), ";" --> 2584 ws, ";", 2585 !. 2586chars_to_semicolon([H|T]) --> 2587 [H], 2588 chars_to_semicolon(T). 2589chars_to_semicolon([]) --> 2590 [].
end
.2600range(bytes(From, To)) --> 2601 "bytes", whites, "=", whites, integer(From), "-", 2602 ( integer(To) 2603 -> "" 2604 ; { To = end } 2605 ). 2606 2607 2608 /******************************* 2609 * REPLY DCG * 2610 *******************************/
2627reply(Fd, [http_version(HttpVersion), status(Code, Status, Comment)|Header]) --> 2628 http_version(HttpVersion), 2629 blanks, 2630 ( status_number(Status, Code) 2631 -> [] 2632 ; integer(Status) 2633 ), 2634 blanks, 2635 string(CommentCodes), 2636 blanks_to_nl, 2637 !, 2638 blanks, 2639 { atom_codes(Comment, CommentCodes), 2640 http_read_header(Fd, Header) 2641 }. 2642 2643 2644 /******************************* 2645 * READ HEADER * 2646 *******************************/
content_type(text/html)
2654http_read_header(Fd, Header) :- 2655 read_header_data(Fd, Text), 2656 http_parse_header(Text, Header). 2657 2658read_header_data(Fd, Header) :- 2659 read_line_to_codes(Fd, Header, Tail), 2660 read_header_data(Header, Fd, Tail), 2661 debug(http(header), 'Header = ~n~s~n', [Header]). 2662 2663read_header_data([0'\r,0'\n], _, _) :- !. 2664read_header_data([0'\n], _, _) :- !. 2665read_header_data([], _, _) :- !. 2666read_header_data(_, Fd, Tail) :- 2667 read_line_to_codes(Fd, Tail, NewTail), 2668 read_header_data(Tail, Fd, NewTail).
2677http_parse_header(Text, Header) :- 2678 phrase(header(Header), Text), 2679 debug(http(header), 'Field: ~p', [Header]). 2680 2681header(List) --> 2682 header_field(Name, Value), 2683 !, 2684 { mkfield(Name, Value, List, Tail) 2685 }, 2686 blanks, 2687 header(Tail). 2688header([]) --> 2689 blanks, 2690 eos, 2691 !. 2692header(_) --> 2693 string(S), blanks_to_nl, 2694 !, 2695 { string_codes(Line, S), 2696 syntax_error(http_parameter(Line)) 2697 }.
SWI-Prolog httpd at <hostname>
The address can be modified by providing a definition for the multifile predicate http:http_address//0.
2711:- multifile 2712 http:http_address//0. 2713 2714address --> 2715 http:http_address, 2716 !. 2717address --> 2718 { gethostname(Host) }, 2719 html(address([ a(href('http://www.swi-prolog.org'), 'SWI-Prolog'), 2720 ' httpd at ', Host 2721 ])). 2722 2723mkfield(host, Host:Port, [host(Host),port(Port)|Tail], Tail) :- !. 2724mkfield(Name, Value, [Att|Tail], Tail) :- 2725 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.
2764 /******************************* 2765 * MESSAGES * 2766 *******************************/ 2767 2768:- multifile 2769 prolog:message//1, 2770 prolog:error_message//1. 2771 2772prologerror_message(http_write_short(Data, Sent)) --> 2773 data(Data), 2774 [ ': remote hangup after ~D bytes'-[Sent] ]. 2775prologerror_message(syntax_error(http_request(Request))) --> 2776 [ 'Illegal HTTP request: ~s'-[Request] ]. 2777prologerror_message(syntax_error(http_parameter(Line))) --> 2778 [ 'Illegal HTTP parameter: ~s'-[Line] ]. 2779 2780prologmessage(http(skipped_cookie(S))) --> 2781 [ 'Skipped illegal cookie: ~s'-[S] ]. 2782 2783data(bytes(MimeType, _Bytes)) --> 2784 !, 2785 [ 'bytes(~p, ...)'-[MimeType] ]. 2786data(Data) --> 2787 [ '~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. */