1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 #include "http_parser.h"
25 #include <stdlib.h> /* rand */
29 #if defined(__APPLE__)
33 #endif /* defined(__APPLE__) */
40 #define MAX_HEADERS 13
41 #define MAX_ELEMENT_SIZE 2048
44 #define MIN(a,b) ((a) < (b) ? (a) : (b))
46 static http_parser *parser;
49 const char *name; // for debugging purposes
51 enum http_parser_type type;
52 enum http_method method;
54 char response_status[MAX_ELEMENT_SIZE];
55 char request_path[MAX_ELEMENT_SIZE];
56 char request_url[MAX_ELEMENT_SIZE];
57 char fragment[MAX_ELEMENT_SIZE];
58 char query_string[MAX_ELEMENT_SIZE];
59 char body[MAX_ELEMENT_SIZE];
65 enum { NONE=0, FIELD, VALUE } last_header_element;
66 char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
67 int should_keep_alive;
70 int num_chunks_complete;
71 int chunk_lengths[MAX_CHUNKS];
73 const char *upgrade; // upgraded body
75 unsigned short http_major;
76 unsigned short http_minor;
78 int message_begin_cb_called;
79 int headers_complete_cb_called;
80 int message_complete_cb_called;
81 int message_complete_on_eof;
85 static int currently_parsing_eof;
87 static struct message messages[5];
88 static int num_messages;
89 static http_parser_settings *current_pause_parser;
91 /* * R E Q U E S T S * */
92 const struct message requests[] =
96 ,.raw= "GET /test HTTP/1.1\r\n"
97 "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
98 "Host: 0.0.0.0=5000\r\n"
101 ,.should_keep_alive= TRUE
102 ,.message_complete_on_eof= FALSE
108 ,.request_path= "/test"
109 ,.request_url= "/test"
112 { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" }
113 , { "Host", "0.0.0.0=5000" }
114 , { "Accept", "*/*" }
119 #define FIREFOX_GET 1
120 , {.name= "firefox get"
122 ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
123 "Host: 0.0.0.0=5000\r\n"
124 "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
125 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
126 "Accept-Language: en-us,en;q=0.5\r\n"
127 "Accept-Encoding: gzip,deflate\r\n"
128 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
129 "Keep-Alive: 300\r\n"
130 "Connection: keep-alive\r\n"
132 ,.should_keep_alive= TRUE
133 ,.message_complete_on_eof= FALSE
139 ,.request_path= "/favicon.ico"
140 ,.request_url= "/favicon.ico"
143 { { "Host", "0.0.0.0=5000" }
144 , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
145 , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
146 , { "Accept-Language", "en-us,en;q=0.5" }
147 , { "Accept-Encoding", "gzip,deflate" }
148 , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
149 , { "Keep-Alive", "300" }
150 , { "Connection", "keep-alive" }
158 ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
159 "aaaaaaaaaaaaa:++++++++++\r\n"
161 ,.should_keep_alive= TRUE
162 ,.message_complete_on_eof= FALSE
168 ,.request_path= "/dumbfuck"
169 ,.request_url= "/dumbfuck"
172 { { "aaaaaaaaaaaaa", "++++++++++" }
177 #define FRAGMENT_IN_URI 3
178 , {.name= "fragment in url"
180 ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
182 ,.should_keep_alive= TRUE
183 ,.message_complete_on_eof= FALSE
187 ,.query_string= "page=1"
188 ,.fragment= "posts-17408"
189 ,.request_path= "/forums/1/topics/2375"
190 /* XXX request url does include fragment? */
191 ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
196 #define GET_NO_HEADERS_NO_BODY 4
197 , {.name= "get no headers no body"
199 ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
201 ,.should_keep_alive= TRUE
202 ,.message_complete_on_eof= FALSE /* would need Connection: close */
208 ,.request_path= "/get_no_headers_no_body/world"
209 ,.request_url= "/get_no_headers_no_body/world"
214 #define GET_ONE_HEADER_NO_BODY 5
215 , {.name= "get one header no body"
217 ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
220 ,.should_keep_alive= TRUE
221 ,.message_complete_on_eof= FALSE /* would need Connection: close */
227 ,.request_path= "/get_one_header_no_body"
228 ,.request_url= "/get_one_header_no_body"
231 { { "Accept" , "*/*" }
236 #define GET_FUNKY_CONTENT_LENGTH 6
237 , {.name= "get funky content length body hello"
239 ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
240 "conTENT-Length: 5\r\n"
243 ,.should_keep_alive= FALSE
244 ,.message_complete_on_eof= FALSE
250 ,.request_path= "/get_funky_content_length_body_hello"
251 ,.request_url= "/get_funky_content_length_body_hello"
254 { { "conTENT-Length" , "5" }
259 #define POST_IDENTITY_BODY_WORLD 7
260 , {.name= "post identity body world"
262 ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
264 "Transfer-Encoding: identity\r\n"
265 "Content-Length: 5\r\n"
268 ,.should_keep_alive= TRUE
269 ,.message_complete_on_eof= FALSE
273 ,.query_string= "q=search"
275 ,.request_path= "/post_identity_body_world"
276 ,.request_url= "/post_identity_body_world?q=search#hey"
279 { { "Accept", "*/*" }
280 , { "Transfer-Encoding", "identity" }
281 , { "Content-Length", "5" }
286 #define POST_CHUNKED_ALL_YOUR_BASE 8
287 , {.name= "post - chunked body: all your base are belong to us"
289 ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
290 "Transfer-Encoding: chunked\r\n"
292 "1e\r\nall your base are belong to us\r\n"
295 ,.should_keep_alive= TRUE
296 ,.message_complete_on_eof= FALSE
302 ,.request_path= "/post_chunked_all_your_base"
303 ,.request_url= "/post_chunked_all_your_base"
306 { { "Transfer-Encoding" , "chunked" }
308 ,.body= "all your base are belong to us"
309 ,.num_chunks_complete= 2
310 ,.chunk_lengths= { 0x1e }
313 #define TWO_CHUNKS_MULT_ZERO_END 9
314 , {.name= "two chunks ; triple zero ending"
316 ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
317 "Transfer-Encoding: chunked\r\n"
323 ,.should_keep_alive= TRUE
324 ,.message_complete_on_eof= FALSE
330 ,.request_path= "/two_chunks_mult_zero_end"
331 ,.request_url= "/two_chunks_mult_zero_end"
334 { { "Transfer-Encoding", "chunked" }
336 ,.body= "hello world"
337 ,.num_chunks_complete= 3
338 ,.chunk_lengths= { 5, 6 }
341 #define CHUNKED_W_TRAILING_HEADERS 10
342 , {.name= "chunked with trailing headers. blech."
344 ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
345 "Transfer-Encoding: chunked\r\n"
351 "Content-Type: text/plain\r\n"
353 ,.should_keep_alive= TRUE
354 ,.message_complete_on_eof= FALSE
360 ,.request_path= "/chunked_w_trailing_headers"
361 ,.request_url= "/chunked_w_trailing_headers"
364 { { "Transfer-Encoding", "chunked" }
366 , { "Content-Type", "text/plain" }
368 ,.body= "hello world"
369 ,.num_chunks_complete= 3
370 ,.chunk_lengths= { 5, 6 }
373 #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
374 , {.name= "with bullshit after the length"
376 ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
377 "Transfer-Encoding: chunked\r\n"
379 "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
380 "6; blahblah; blah\r\n world\r\n"
383 ,.should_keep_alive= TRUE
384 ,.message_complete_on_eof= FALSE
390 ,.request_path= "/chunked_w_bullshit_after_length"
391 ,.request_url= "/chunked_w_bullshit_after_length"
394 { { "Transfer-Encoding", "chunked" }
396 ,.body= "hello world"
397 ,.num_chunks_complete= 3
398 ,.chunk_lengths= { 5, 6 }
401 #define WITH_QUOTES 12
402 , {.name= "with quotes"
404 ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
405 ,.should_keep_alive= TRUE
406 ,.message_complete_on_eof= FALSE
410 ,.query_string= "foo=\"bar\""
412 ,.request_path= "/with_\"stupid\"_quotes"
413 ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
419 #define APACHEBENCH_GET 13
420 /* The server receiving this request SHOULD NOT wait for EOF
421 * to know that content-length == 0.
422 * How to represent this in a unit test? message_complete_on_eof
423 * Compare with NO_CONTENT_LENGTH_RESPONSE.
425 , {.name = "apachebench get"
427 ,.raw= "GET /test HTTP/1.0\r\n"
428 "Host: 0.0.0.0:5000\r\n"
429 "User-Agent: ApacheBench/2.3\r\n"
430 "Accept: */*\r\n\r\n"
431 ,.should_keep_alive= FALSE
432 ,.message_complete_on_eof= FALSE
438 ,.request_path= "/test"
439 ,.request_url= "/test"
441 ,.headers= { { "Host", "0.0.0.0:5000" }
442 , { "User-Agent", "ApacheBench/2.3" }
443 , { "Accept", "*/*" }
448 #define QUERY_URL_WITH_QUESTION_MARK_GET 14
449 /* Some clients include '?' characters in query strings.
451 , {.name = "query url with question mark"
453 ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
454 ,.should_keep_alive= TRUE
455 ,.message_complete_on_eof= FALSE
459 ,.query_string= "foo=bar?baz"
461 ,.request_path= "/test.cgi"
462 ,.request_url= "/test.cgi?foo=bar?baz"
468 #define PREFIX_NEWLINE_GET 15
469 /* Some clients, especially after a POST in a keep-alive connection,
470 * will send an extra CRLF before the next request
472 , {.name = "newline prefix get"
474 ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
475 ,.should_keep_alive= TRUE
476 ,.message_complete_on_eof= FALSE
482 ,.request_path= "/test"
483 ,.request_url= "/test"
489 #define UPGRADE_REQUEST 16
490 , {.name = "upgrade request"
492 ,.raw= "GET /demo HTTP/1.1\r\n"
493 "Host: example.com\r\n"
494 "Connection: Upgrade\r\n"
495 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
496 "Sec-WebSocket-Protocol: sample\r\n"
497 "Upgrade: WebSocket\r\n"
498 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
499 "Origin: http://example.com\r\n"
502 ,.should_keep_alive= TRUE
503 ,.message_complete_on_eof= FALSE
509 ,.request_path= "/demo"
510 ,.request_url= "/demo"
512 ,.upgrade="Hot diggity dogg"
513 ,.headers= { { "Host", "example.com" }
514 , { "Connection", "Upgrade" }
515 , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
516 , { "Sec-WebSocket-Protocol", "sample" }
517 , { "Upgrade", "WebSocket" }
518 , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
519 , { "Origin", "http://example.com" }
524 #define CONNECT_REQUEST 17
525 , {.name = "connect request"
527 ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
528 "User-agent: Mozilla/1.1N\r\n"
529 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
532 "and yet even more data"
533 ,.should_keep_alive= FALSE
534 ,.message_complete_on_eof= FALSE
537 ,.method= HTTP_CONNECT
541 ,.request_url= "0-home0.netscape.com:443"
543 ,.upgrade="some data\r\nand yet even more data"
544 ,.headers= { { "User-agent", "Mozilla/1.1N" }
545 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
550 #define REPORT_REQ 18
551 , {.name= "report request"
553 ,.raw= "REPORT /test HTTP/1.1\r\n"
555 ,.should_keep_alive= TRUE
556 ,.message_complete_on_eof= FALSE
559 ,.method= HTTP_REPORT
562 ,.request_path= "/test"
563 ,.request_url= "/test"
569 #define NO_HTTP_VERSION 19
570 , {.name= "request with no http version"
574 ,.should_keep_alive= FALSE
575 ,.message_complete_on_eof= FALSE
588 #define MSEARCH_REQ 20
589 , {.name= "m-search request"
591 ,.raw= "M-SEARCH * HTTP/1.1\r\n"
592 "HOST: 239.255.255.250:1900\r\n"
593 "MAN: \"ssdp:discover\"\r\n"
594 "ST: \"ssdp:all\"\r\n"
596 ,.should_keep_alive= TRUE
597 ,.message_complete_on_eof= FALSE
600 ,.method= HTTP_MSEARCH
606 ,.headers= { { "HOST", "239.255.255.250:1900" }
607 , { "MAN", "\"ssdp:discover\"" }
608 , { "ST", "\"ssdp:all\"" }
613 #define LINE_FOLDING_IN_HEADER 21
614 , {.name= "line folding in header value"
616 ,.raw= "GET / HTTP/1.1\r\n"
623 "Line2: \t line2\t\r\n"
631 ,.should_keep_alive= FALSE
632 ,.message_complete_on_eof= FALSE
641 ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
642 , { "Line2", "line2\t" }
643 , { "Line3", "line3" }
645 , { "Connection", "close" },
651 #define QUERY_TERMINATED_HOST 22
652 , {.name= "host terminated by a query string"
654 ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
656 ,.should_keep_alive= TRUE
657 ,.message_complete_on_eof= FALSE
661 ,.query_string= "hail=all"
664 ,.request_url= "http://hypnotoad.org?hail=all"
665 ,.host= "hypnotoad.org"
671 #define QUERY_TERMINATED_HOSTPORT 23
672 , {.name= "host:port terminated by a query string"
674 ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
676 ,.should_keep_alive= TRUE
677 ,.message_complete_on_eof= FALSE
681 ,.query_string= "hail=all"
684 ,.request_url= "http://hypnotoad.org:1234?hail=all"
685 ,.host= "hypnotoad.org"
692 #define SPACE_TERMINATED_HOSTPORT 24
693 , {.name= "host:port terminated by a space"
695 ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
697 ,.should_keep_alive= TRUE
698 ,.message_complete_on_eof= FALSE
705 ,.request_url= "http://hypnotoad.org:1234"
706 ,.host= "hypnotoad.org"
714 , {.name = "PATCH request"
716 ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
717 "Host: www.example.com\r\n"
718 "Content-Type: application/example\r\n"
719 "If-Match: \"e0023aa4e\"\r\n"
720 "Content-Length: 10\r\n"
723 ,.should_keep_alive= TRUE
724 ,.message_complete_on_eof= FALSE
730 ,.request_path= "/file.txt"
731 ,.request_url= "/file.txt"
733 ,.headers= { { "Host", "www.example.com" }
734 , { "Content-Type", "application/example" }
735 , { "If-Match", "\"e0023aa4e\"" }
736 , { "Content-Length", "10" }
741 #define CONNECT_CAPS_REQUEST 26
742 , {.name = "connect caps request"
744 ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
745 "User-agent: Mozilla/1.1N\r\n"
746 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
748 ,.should_keep_alive= FALSE
749 ,.message_complete_on_eof= FALSE
752 ,.method= HTTP_CONNECT
756 ,.request_url= "HOME0.NETSCAPE.COM:443"
759 ,.headers= { { "User-agent", "Mozilla/1.1N" }
760 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
765 #if !HTTP_PARSER_STRICT
766 #define UTF8_PATH_REQ 27
767 , {.name= "utf-8 path request"
769 ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
770 "Host: github.com\r\n"
772 ,.should_keep_alive= TRUE
773 ,.message_complete_on_eof= FALSE
777 ,.query_string= "q=1"
779 ,.request_path= "/δ¶/δt/pope"
780 ,.request_url= "/δ¶/δt/pope?q=1#narf"
782 ,.headers= { {"Host", "github.com" }
787 #define HOSTNAME_UNDERSCORE 28
788 , {.name = "hostname underscore"
790 ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
791 "User-agent: Mozilla/1.1N\r\n"
792 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
794 ,.should_keep_alive= FALSE
795 ,.message_complete_on_eof= FALSE
798 ,.method= HTTP_CONNECT
802 ,.request_url= "home_0.netscape.com:443"
805 ,.headers= { { "User-agent", "Mozilla/1.1N" }
806 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
810 #endif /* !HTTP_PARSER_STRICT */
812 /* see https://github.com/ry/http-parser/issues/47 */
813 #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
814 , {.name = "eat CRLF between requests, no \"Connection: close\" header"
815 ,.raw= "POST / HTTP/1.1\r\n"
816 "Host: www.example.com\r\n"
817 "Content-Type: application/x-www-form-urlencoded\r\n"
818 "Content-Length: 4\r\n"
820 "q=42\r\n" /* note the trailing CRLF */
821 ,.should_keep_alive= TRUE
822 ,.message_complete_on_eof= FALSE
832 ,.headers= { { "Host", "www.example.com" }
833 , { "Content-Type", "application/x-www-form-urlencoded" }
834 , { "Content-Length", "4" }
839 /* see https://github.com/ry/http-parser/issues/47 */
840 #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
841 , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
842 ,.raw= "POST / HTTP/1.1\r\n"
843 "Host: www.example.com\r\n"
844 "Content-Type: application/x-www-form-urlencoded\r\n"
845 "Content-Length: 4\r\n"
846 "Connection: close\r\n"
848 "q=42\r\n" /* note the trailing CRLF */
849 ,.should_keep_alive= FALSE
850 ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
860 ,.headers= { { "Host", "www.example.com" }
861 , { "Content-Type", "application/x-www-form-urlencoded" }
862 , { "Content-Length", "4" }
863 , { "Connection", "close" }
869 , {.name = "PURGE request"
871 ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
872 "Host: www.example.com\r\n"
874 ,.should_keep_alive= TRUE
875 ,.message_complete_on_eof= FALSE
881 ,.request_path= "/file.txt"
882 ,.request_url= "/file.txt"
884 ,.headers= { { "Host", "www.example.com" } }
888 #define SEARCH_REQ 32
889 , {.name = "SEARCH request"
891 ,.raw= "SEARCH / HTTP/1.1\r\n"
892 "Host: www.example.com\r\n"
894 ,.should_keep_alive= TRUE
895 ,.message_complete_on_eof= FALSE
898 ,.method= HTTP_SEARCH
904 ,.headers= { { "Host", "www.example.com" } }
908 #define PROXY_WITH_BASIC_AUTH 33
909 , {.name= "host:port and basic_auth"
911 ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
913 ,.should_keep_alive= TRUE
914 ,.message_complete_on_eof= FALSE
919 ,.request_path= "/toto"
920 ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
921 ,.host= "hypnotoad.org"
922 ,.userinfo= "a%12:b!&*$"
929 #define LINE_FOLDING_IN_HEADER_WITH_LF 34
930 , {.name= "line folding in header value"
932 ,.raw= "GET / HTTP/1.1\n"
939 "Line2: \t line2\t\n"
947 ,.should_keep_alive= FALSE
948 ,.message_complete_on_eof= FALSE
957 ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
958 , { "Line2", "line2\t" }
959 , { "Line3", "line3" }
961 , { "Connection", "close" },
966 #define CONNECTION_MULTI 35
967 , {.name = "multiple connection header values with folding"
969 ,.raw= "GET /demo HTTP/1.1\r\n"
970 "Host: example.com\r\n"
971 "Connection: Something,\r\n"
972 " Upgrade, ,Keep-Alive\r\n"
973 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
974 "Sec-WebSocket-Protocol: sample\r\n"
975 "Upgrade: WebSocket\r\n"
976 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
977 "Origin: http://example.com\r\n"
980 ,.should_keep_alive= TRUE
981 ,.message_complete_on_eof= FALSE
987 ,.request_path= "/demo"
988 ,.request_url= "/demo"
990 ,.upgrade="Hot diggity dogg"
991 ,.headers= { { "Host", "example.com" }
992 , { "Connection", "Something, Upgrade, ,Keep-Alive" }
993 , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
994 , { "Sec-WebSocket-Protocol", "sample" }
995 , { "Upgrade", "WebSocket" }
996 , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
997 , { "Origin", "http://example.com" }
1002 #define CONNECTION_MULTI_LWS 36
1003 , {.name = "multiple connection header values with folding and lws"
1004 ,.type= HTTP_REQUEST
1005 ,.raw= "GET /demo HTTP/1.1\r\n"
1006 "Connection: keep-alive, upgrade\r\n"
1007 "Upgrade: WebSocket\r\n"
1010 ,.should_keep_alive= TRUE
1011 ,.message_complete_on_eof= FALSE
1017 ,.request_path= "/demo"
1018 ,.request_url= "/demo"
1020 ,.upgrade="Hot diggity dogg"
1021 ,.headers= { { "Connection", "keep-alive, upgrade" }
1022 , { "Upgrade", "WebSocket" }
1027 #define CONNECTION_MULTI_LWS_CRLF 37
1028 , {.name = "multiple connection header values with folding and lws"
1029 ,.type= HTTP_REQUEST
1030 ,.raw= "GET /demo HTTP/1.1\r\n"
1031 "Connection: keep-alive, \r\n upgrade\r\n"
1032 "Upgrade: WebSocket\r\n"
1035 ,.should_keep_alive= TRUE
1036 ,.message_complete_on_eof= FALSE
1042 ,.request_path= "/demo"
1043 ,.request_url= "/demo"
1045 ,.upgrade="Hot diggity dogg"
1046 ,.headers= { { "Connection", "keep-alive, upgrade" }
1047 , { "Upgrade", "WebSocket" }
1052 #define UPGRADE_POST_REQUEST 38
1053 , {.name = "upgrade post request"
1054 ,.type= HTTP_REQUEST
1055 ,.raw= "POST /demo HTTP/1.1\r\n"
1056 "Host: example.com\r\n"
1057 "Connection: Upgrade\r\n"
1058 "Upgrade: HTTP/2.0\r\n"
1059 "Content-Length: 15\r\n"
1063 ,.should_keep_alive= TRUE
1064 ,.message_complete_on_eof= FALSE
1068 ,.request_path= "/demo"
1069 ,.request_url= "/demo"
1071 ,.upgrade="Hot diggity dogg"
1072 ,.headers= { { "Host", "example.com" }
1073 , { "Connection", "Upgrade" }
1074 , { "Upgrade", "HTTP/2.0" }
1075 , { "Content-Length", "15" }
1077 ,.body= "sweet post body"
1080 #define CONNECT_WITH_BODY_REQUEST 39
1081 , {.name = "connect with body request"
1082 ,.type= HTTP_REQUEST
1083 ,.raw= "CONNECT foo.bar.com:443 HTTP/1.0\r\n"
1084 "User-agent: Mozilla/1.1N\r\n"
1085 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
1086 "Content-Length: 10\r\n"
1089 ,.should_keep_alive= FALSE
1090 ,.message_complete_on_eof= FALSE
1093 ,.method= HTTP_CONNECT
1094 ,.request_url= "foo.bar.com:443"
1096 ,.upgrade="blarfcicle"
1097 ,.headers= { { "User-agent", "Mozilla/1.1N" }
1098 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
1099 , { "Content-Length", "10" }
1104 , {.name= NULL } /* sentinel */
1107 /* * R E S P O N S E S * */
1108 const struct message responses[] =
1109 #define GOOGLE_301 0
1110 { {.name= "google 301"
1111 ,.type= HTTP_RESPONSE
1112 ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
1113 "Location: http://www.google.com/\r\n"
1114 "Content-Type: text/html; charset=UTF-8\r\n"
1115 "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
1116 "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
1117 "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
1118 "Cache-Control: public, max-age=2592000\r\n"
1120 "Content-Length: 219 \r\n"
1122 "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1123 "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1124 "<H1>301 Moved</H1>\n"
1125 "The document has moved\n"
1126 "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1127 "</BODY></HTML>\r\n"
1128 ,.should_keep_alive= TRUE
1129 ,.message_complete_on_eof= FALSE
1133 ,.response_status= "Moved Permanently"
1136 { { "Location", "http://www.google.com/" }
1137 , { "Content-Type", "text/html; charset=UTF-8" }
1138 , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
1139 , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
1140 , { "X-$PrototypeBI-Version", "1.6.0.3" }
1141 , { "Cache-Control", "public, max-age=2592000" }
1142 , { "Server", "gws" }
1143 , { "Content-Length", "219 " }
1145 ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1146 "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1147 "<H1>301 Moved</H1>\n"
1148 "The document has moved\n"
1149 "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1150 "</BODY></HTML>\r\n"
1153 #define NO_CONTENT_LENGTH_RESPONSE 1
1154 /* The client should wait for the server's EOF. That is, when content-length
1155 * is not specified, and "Connection: close", the end of body is specified
1157 * Compare with APACHEBENCH_GET
1159 , {.name= "no content-length response"
1160 ,.type= HTTP_RESPONSE
1161 ,.raw= "HTTP/1.1 200 OK\r\n"
1162 "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
1163 "Server: Apache\r\n"
1164 "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
1165 "Content-Type: text/xml; charset=utf-8\r\n"
1166 "Connection: close\r\n"
1168 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1169 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1170 " <SOAP-ENV:Body>\n"
1171 " <SOAP-ENV:Fault>\n"
1172 " <faultcode>SOAP-ENV:Client</faultcode>\n"
1173 " <faultstring>Client Error</faultstring>\n"
1174 " </SOAP-ENV:Fault>\n"
1175 " </SOAP-ENV:Body>\n"
1176 "</SOAP-ENV:Envelope>"
1177 ,.should_keep_alive= FALSE
1178 ,.message_complete_on_eof= TRUE
1182 ,.response_status= "OK"
1185 { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
1186 , { "Server", "Apache" }
1187 , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
1188 , { "Content-Type", "text/xml; charset=utf-8" }
1189 , { "Connection", "close" }
1191 ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1192 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1193 " <SOAP-ENV:Body>\n"
1194 " <SOAP-ENV:Fault>\n"
1195 " <faultcode>SOAP-ENV:Client</faultcode>\n"
1196 " <faultstring>Client Error</faultstring>\n"
1197 " </SOAP-ENV:Fault>\n"
1198 " </SOAP-ENV:Body>\n"
1199 "</SOAP-ENV:Envelope>"
1202 #define NO_HEADERS_NO_BODY_404 2
1203 , {.name= "404 no headers no body"
1204 ,.type= HTTP_RESPONSE
1205 ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
1206 ,.should_keep_alive= FALSE
1207 ,.message_complete_on_eof= TRUE
1211 ,.response_status= "Not Found"
1218 #define NO_REASON_PHRASE 3
1219 , {.name= "301 no response phrase"
1220 ,.type= HTTP_RESPONSE
1221 ,.raw= "HTTP/1.1 301\r\n\r\n"
1222 ,.should_keep_alive = FALSE
1223 ,.message_complete_on_eof= TRUE
1227 ,.response_status= ""
1233 #define TRAILING_SPACE_ON_CHUNKED_BODY 4
1234 , {.name="200 trailing space on chunked body"
1235 ,.type= HTTP_RESPONSE
1236 ,.raw= "HTTP/1.1 200 OK\r\n"
1237 "Content-Type: text/plain\r\n"
1238 "Transfer-Encoding: chunked\r\n"
1241 "This is the data in the first chunk\r\n"
1244 "and this is the second one\r\n"
1248 ,.should_keep_alive= TRUE
1249 ,.message_complete_on_eof= FALSE
1253 ,.response_status= "OK"
1256 { {"Content-Type", "text/plain" }
1257 , {"Transfer-Encoding", "chunked" }
1261 "This is the data in the first chunk\r\n"
1262 "and this is the second one\r\n"
1263 ,.num_chunks_complete= 3
1264 ,.chunk_lengths= { 0x25, 0x1c }
1267 #define NO_CARRIAGE_RET 5
1268 , {.name="no carriage ret"
1269 ,.type= HTTP_RESPONSE
1270 ,.raw= "HTTP/1.1 200 OK\n"
1271 "Content-Type: text/html; charset=utf-8\n"
1272 "Connection: close\n"
1274 "these headers are from http://news.ycombinator.com/"
1275 ,.should_keep_alive= FALSE
1276 ,.message_complete_on_eof= TRUE
1280 ,.response_status= "OK"
1283 { {"Content-Type", "text/html; charset=utf-8" }
1284 , {"Connection", "close" }
1286 ,.body= "these headers are from http://news.ycombinator.com/"
1289 #define PROXY_CONNECTION 6
1290 , {.name="proxy connection"
1291 ,.type= HTTP_RESPONSE
1292 ,.raw= "HTTP/1.1 200 OK\r\n"
1293 "Content-Type: text/html; charset=UTF-8\r\n"
1294 "Content-Length: 11\r\n"
1295 "Proxy-Connection: close\r\n"
1296 "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
1299 ,.should_keep_alive= FALSE
1300 ,.message_complete_on_eof= FALSE
1304 ,.response_status= "OK"
1307 { {"Content-Type", "text/html; charset=UTF-8" }
1308 , {"Content-Length", "11" }
1309 , {"Proxy-Connection", "close" }
1310 , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
1312 ,.body= "hello world"
1315 #define UNDERSTORE_HEADER_KEY 7
1317 // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
1318 , {.name="underscore header key"
1319 ,.type= HTTP_RESPONSE
1320 ,.raw= "HTTP/1.1 200 OK\r\n"
1321 "Server: DCLK-AdSvr\r\n"
1322 "Content-Type: text/xml\r\n"
1323 "Content-Length: 0\r\n"
1324 "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
1325 ,.should_keep_alive= TRUE
1326 ,.message_complete_on_eof= FALSE
1330 ,.response_status= "OK"
1333 { {"Server", "DCLK-AdSvr" }
1334 , {"Content-Type", "text/xml" }
1335 , {"Content-Length", "0" }
1336 , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1341 #define BONJOUR_MADAME_FR 8
1342 /* The client should not merge two headers fields when the first one doesn't
1345 , {.name= "bonjourmadame.fr"
1346 ,.type= HTTP_RESPONSE
1347 ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
1348 "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
1349 "Server: Apache/2.2.3 (Red Hat)\r\n"
1350 "Cache-Control: public\r\n"
1352 "Location: http://www.bonjourmadame.fr/\r\n"
1353 "Vary: Accept-Encoding\r\n"
1354 "Content-Length: 0\r\n"
1355 "Content-Type: text/html; charset=UTF-8\r\n"
1356 "Connection: keep-alive\r\n"
1358 ,.should_keep_alive= TRUE
1359 ,.message_complete_on_eof= FALSE
1363 ,.response_status= "Moved Permanently"
1366 { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1367 , { "Server", "Apache/2.2.3 (Red Hat)" }
1368 , { "Cache-Control", "public" }
1370 , { "Location", "http://www.bonjourmadame.fr/" }
1371 , { "Vary", "Accept-Encoding" }
1372 , { "Content-Length", "0" }
1373 , { "Content-Type", "text/html; charset=UTF-8" }
1374 , { "Connection", "keep-alive" }
1379 #define RES_FIELD_UNDERSCORE 9
1380 /* Should handle spaces in header fields */
1381 , {.name= "field underscore"
1382 ,.type= HTTP_RESPONSE
1383 ,.raw= "HTTP/1.1 200 OK\r\n"
1384 "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1385 "Server: Apache\r\n"
1386 "Cache-Control: no-cache, must-revalidate\r\n"
1387 "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1388 ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1389 "Vary: Accept-Encoding\r\n"
1390 "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1391 "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1392 "Transfer-Encoding: chunked\r\n"
1393 "Content-Type: text/html\r\n"
1394 "Connection: close\r\n"
1397 ,.should_keep_alive= FALSE
1398 ,.message_complete_on_eof= FALSE
1402 ,.response_status= "OK"
1405 { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1406 , { "Server", "Apache" }
1407 , { "Cache-Control", "no-cache, must-revalidate" }
1408 , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1409 , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1410 , { "Vary", "Accept-Encoding" }
1411 , { "_eep-Alive", "timeout=45" }
1412 , { "_onnection", "Keep-Alive" }
1413 , { "Transfer-Encoding", "chunked" }
1414 , { "Content-Type", "text/html" }
1415 , { "Connection", "close" }
1418 ,.num_chunks_complete= 1
1422 #define NON_ASCII_IN_STATUS_LINE 10
1423 /* Should handle non-ASCII in status line */
1424 , {.name= "non-ASCII in status line"
1425 ,.type= HTTP_RESPONSE
1426 ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1427 "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1428 "Content-Length: 0\r\n"
1429 "Connection: close\r\n"
1431 ,.should_keep_alive= FALSE
1432 ,.message_complete_on_eof= FALSE
1436 ,.response_status= "Oriëntatieprobleem"
1439 { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1440 , { "Content-Length", "0" }
1441 , { "Connection", "close" }
1446 #define HTTP_VERSION_0_9 11
1447 /* Should handle HTTP/0.9 */
1448 , {.name= "http version 0.9"
1449 ,.type= HTTP_RESPONSE
1450 ,.raw= "HTTP/0.9 200 OK\r\n"
1452 ,.should_keep_alive= FALSE
1453 ,.message_complete_on_eof= TRUE
1457 ,.response_status= "OK"
1464 #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1465 /* The client should wait for the server's EOF. That is, when neither
1466 * content-length nor transfer-encoding is specified, the end of body
1467 * is specified by the EOF.
1469 , {.name= "neither content-length nor transfer-encoding response"
1470 ,.type= HTTP_RESPONSE
1471 ,.raw= "HTTP/1.1 200 OK\r\n"
1472 "Content-Type: text/plain\r\n"
1475 ,.should_keep_alive= FALSE
1476 ,.message_complete_on_eof= TRUE
1480 ,.response_status= "OK"
1483 { { "Content-Type", "text/plain" }
1485 ,.body= "hello world"
1488 #define NO_BODY_HTTP10_KA_200 13
1489 , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1490 ,.type= HTTP_RESPONSE
1491 ,.raw= "HTTP/1.0 200 OK\r\n"
1492 "Connection: keep-alive\r\n"
1494 ,.should_keep_alive= FALSE
1495 ,.message_complete_on_eof= TRUE
1499 ,.response_status= "OK"
1502 { { "Connection", "keep-alive" }
1508 #define NO_BODY_HTTP10_KA_204 14
1509 , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1510 ,.type= HTTP_RESPONSE
1511 ,.raw= "HTTP/1.0 204 No content\r\n"
1512 "Connection: keep-alive\r\n"
1514 ,.should_keep_alive= TRUE
1515 ,.message_complete_on_eof= FALSE
1519 ,.response_status= "No content"
1522 { { "Connection", "keep-alive" }
1528 #define NO_BODY_HTTP11_KA_200 15
1529 , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1530 ,.type= HTTP_RESPONSE
1531 ,.raw= "HTTP/1.1 200 OK\r\n"
1533 ,.should_keep_alive= FALSE
1534 ,.message_complete_on_eof= TRUE
1538 ,.response_status= "OK"
1545 #define NO_BODY_HTTP11_KA_204 16
1546 , {.name= "HTTP/1.1 with a 204 status"
1547 ,.type= HTTP_RESPONSE
1548 ,.raw= "HTTP/1.1 204 No content\r\n"
1550 ,.should_keep_alive= TRUE
1551 ,.message_complete_on_eof= FALSE
1555 ,.response_status= "No content"
1562 #define NO_BODY_HTTP11_NOKA_204 17
1563 , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1564 ,.type= HTTP_RESPONSE
1565 ,.raw= "HTTP/1.1 204 No content\r\n"
1566 "Connection: close\r\n"
1568 ,.should_keep_alive= FALSE
1569 ,.message_complete_on_eof= FALSE
1573 ,.response_status= "No content"
1576 { { "Connection", "close" }
1582 #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1583 , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1584 ,.type= HTTP_RESPONSE
1585 ,.raw= "HTTP/1.1 200 OK\r\n"
1586 "Transfer-Encoding: chunked\r\n"
1590 ,.should_keep_alive= TRUE
1591 ,.message_complete_on_eof= FALSE
1595 ,.response_status= "OK"
1598 { { "Transfer-Encoding", "chunked" }
1602 ,.num_chunks_complete= 1
1605 #if !HTTP_PARSER_STRICT
1606 #define SPACE_IN_FIELD_RES 19
1607 /* Should handle spaces in header fields */
1608 , {.name= "field space"
1609 ,.type= HTTP_RESPONSE
1610 ,.raw= "HTTP/1.1 200 OK\r\n"
1611 "Server: Microsoft-IIS/6.0\r\n"
1612 "X-Powered-By: ASP.NET\r\n"
1613 "en-US Content-Type: text/xml\r\n" /* this is the problem */
1614 "Content-Type: text/xml\r\n"
1615 "Content-Length: 16\r\n"
1616 "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
1617 "Connection: keep-alive\r\n"
1619 "<xml>hello</xml>" /* fake body */
1620 ,.should_keep_alive= TRUE
1621 ,.message_complete_on_eof= FALSE
1625 ,.response_status= "OK"
1628 { { "Server", "Microsoft-IIS/6.0" }
1629 , { "X-Powered-By", "ASP.NET" }
1630 , { "en-US Content-Type", "text/xml" }
1631 , { "Content-Type", "text/xml" }
1632 , { "Content-Length", "16" }
1633 , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1634 , { "Connection", "keep-alive" }
1636 ,.body= "<xml>hello</xml>"
1638 #endif /* !HTTP_PARSER_STRICT */
1640 #define AMAZON_COM 20
1641 , {.name= "amazon.com"
1642 ,.type= HTTP_RESPONSE
1643 ,.raw= "HTTP/1.1 301 MovedPermanently\r\n"
1644 "Date: Wed, 15 May 2013 17:06:33 GMT\r\n"
1645 "Server: Server\r\n"
1646 "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n"
1647 "p3p: policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"\r\n"
1648 "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n"
1649 "Location: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\r\n"
1650 "Vary: Accept-Encoding,User-Agent\r\n"
1651 "Content-Type: text/html; charset=ISO-8859-1\r\n"
1652 "Transfer-Encoding: chunked\r\n"
1658 ,.should_keep_alive= TRUE
1659 ,.message_complete_on_eof= FALSE
1663 ,.response_status= "MovedPermanently"
1665 ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" }
1666 , { "Server", "Server" }
1667 , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" }
1668 , { "p3p", "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"" }
1669 , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" }
1670 , { "Location", "http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846" }
1671 , { "Vary", "Accept-Encoding,User-Agent" }
1672 , { "Content-Type", "text/html; charset=ISO-8859-1" }
1673 , { "Transfer-Encoding", "chunked" }
1676 ,.num_chunks_complete= 2
1677 ,.chunk_lengths= { 1 }
1680 #define EMPTY_REASON_PHRASE_AFTER_SPACE 20
1681 , {.name= "empty reason phrase after space"
1682 ,.type= HTTP_RESPONSE
1683 ,.raw= "HTTP/1.1 200 \r\n"
1685 ,.should_keep_alive= FALSE
1686 ,.message_complete_on_eof= TRUE
1690 ,.response_status= ""
1696 , {.name= NULL } /* sentinel */
1699 /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1700 * define it ourselves.
1703 strnlen(const char *s, size_t maxlen)
1707 p = memchr(s, '\0', maxlen);
1715 strlncat(char *dst, size_t len, const char *src, size_t n)
1722 slen = strnlen(src, n);
1723 dlen = strnlen(dst, len);
1727 ncpy = slen < rlen ? slen : (rlen - 1);
1728 memcpy(dst + dlen, src, ncpy);
1729 dst[dlen + ncpy] = '\0';
1732 assert(len > slen + dlen);
1737 strlcat(char *dst, const char *src, size_t len)
1739 return strlncat(dst, len, src, (size_t) -1);
1743 strlncpy(char *dst, size_t len, const char *src, size_t n)
1748 slen = strnlen(src, n);
1751 ncpy = slen < len ? slen : (len - 1);
1752 memcpy(dst, src, ncpy);
1761 strlcpy(char *dst, const char *src, size_t len)
1763 return strlncpy(dst, len, src, (size_t) -1);
1767 request_url_cb (http_parser *p, const char *buf, size_t len)
1769 assert(p == parser);
1770 strlncat(messages[num_messages].request_url,
1771 sizeof(messages[num_messages].request_url),
1778 header_field_cb (http_parser *p, const char *buf, size_t len)
1780 assert(p == parser);
1781 struct message *m = &messages[num_messages];
1783 if (m->last_header_element != FIELD)
1786 strlncat(m->headers[m->num_headers-1][0],
1787 sizeof(m->headers[m->num_headers-1][0]),
1791 m->last_header_element = FIELD;
1797 header_value_cb (http_parser *p, const char *buf, size_t len)
1799 assert(p == parser);
1800 struct message *m = &messages[num_messages];
1802 strlncat(m->headers[m->num_headers-1][1],
1803 sizeof(m->headers[m->num_headers-1][1]),
1807 m->last_header_element = VALUE;
1813 check_body_is_final (const http_parser *p)
1815 if (messages[num_messages].body_is_final) {
1816 fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1817 "on last on_body callback call "
1818 "but it doesn't! ***\n\n");
1822 messages[num_messages].body_is_final = http_body_is_final(p);
1826 body_cb (http_parser *p, const char *buf, size_t len)
1828 assert(p == parser);
1829 strlncat(messages[num_messages].body,
1830 sizeof(messages[num_messages].body),
1833 messages[num_messages].body_size += len;
1834 check_body_is_final(p);
1835 // printf("body_cb: '%s'\n", requests[num_messages].body);
1840 count_body_cb (http_parser *p, const char *buf, size_t len)
1842 assert(p == parser);
1844 messages[num_messages].body_size += len;
1845 check_body_is_final(p);
1850 message_begin_cb (http_parser *p)
1852 assert(p == parser);
1853 messages[num_messages].message_begin_cb_called = TRUE;
1858 headers_complete_cb (http_parser *p)
1860 assert(p == parser);
1861 messages[num_messages].method = parser->method;
1862 messages[num_messages].status_code = parser->status_code;
1863 messages[num_messages].http_major = parser->http_major;
1864 messages[num_messages].http_minor = parser->http_minor;
1865 messages[num_messages].headers_complete_cb_called = TRUE;
1866 messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
1871 message_complete_cb (http_parser *p)
1873 assert(p == parser);
1874 if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
1876 fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
1877 "value in both on_message_complete and on_headers_complete "
1878 "but it doesn't! ***\n\n");
1883 if (messages[num_messages].body_size &&
1884 http_body_is_final(p) &&
1885 !messages[num_messages].body_is_final)
1887 fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1888 "on last on_body callback call "
1889 "but it doesn't! ***\n\n");
1894 messages[num_messages].message_complete_cb_called = TRUE;
1896 messages[num_messages].message_complete_on_eof = currently_parsing_eof;
1903 response_status_cb (http_parser *p, const char *buf, size_t len)
1905 assert(p == parser);
1906 strlncat(messages[num_messages].response_status,
1907 sizeof(messages[num_messages].response_status),
1914 chunk_header_cb (http_parser *p)
1916 assert(p == parser);
1917 int chunk_idx = messages[num_messages].num_chunks;
1918 messages[num_messages].num_chunks++;
1919 if (chunk_idx < MAX_CHUNKS) {
1920 messages[num_messages].chunk_lengths[chunk_idx] = p->content_length;
1927 chunk_complete_cb (http_parser *p)
1929 assert(p == parser);
1931 /* Here we want to verify that each chunk_header_cb is matched by a
1932 * chunk_complete_cb, so not only should the total number of calls to
1933 * both callbacks be the same, but they also should be interleaved
1935 assert(messages[num_messages].num_chunks ==
1936 messages[num_messages].num_chunks_complete + 1);
1938 messages[num_messages].num_chunks_complete++;
1942 /* These dontcall_* callbacks exist so that we can verify that when we're
1943 * paused, no additional callbacks are invoked */
1945 dontcall_message_begin_cb (http_parser *p)
1948 fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1953 dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1955 if (p || buf || len) { } // gcc
1956 fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1961 dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1963 if (p || buf || len) { } // gcc
1964 fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1969 dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1971 if (p || buf || len) { } // gcc
1972 fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1977 dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1979 if (p || buf || len) { } // gcc
1980 fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1985 dontcall_headers_complete_cb (http_parser *p)
1988 fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1994 dontcall_message_complete_cb (http_parser *p)
1997 fprintf(stderr, "\n\n*** on_message_complete() called on paused "
2003 dontcall_response_status_cb (http_parser *p, const char *buf, size_t len)
2005 if (p || buf || len) { } // gcc
2006 fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n");
2011 dontcall_chunk_header_cb (http_parser *p)
2014 fprintf(stderr, "\n\n*** on_chunk_header() called on paused parser ***\n\n");
2019 dontcall_chunk_complete_cb (http_parser *p)
2022 fprintf(stderr, "\n\n*** on_chunk_complete() "
2023 "called on paused parser ***\n\n");
2027 static http_parser_settings settings_dontcall =
2028 {.on_message_begin = dontcall_message_begin_cb
2029 ,.on_header_field = dontcall_header_field_cb
2030 ,.on_header_value = dontcall_header_value_cb
2031 ,.on_url = dontcall_request_url_cb
2032 ,.on_status = dontcall_response_status_cb
2033 ,.on_body = dontcall_body_cb
2034 ,.on_headers_complete = dontcall_headers_complete_cb
2035 ,.on_message_complete = dontcall_message_complete_cb
2036 ,.on_chunk_header = dontcall_chunk_header_cb
2037 ,.on_chunk_complete = dontcall_chunk_complete_cb
2040 /* These pause_* callbacks always pause the parser and just invoke the regular
2041 * callback that tracks content. Before returning, we overwrite the parser
2042 * settings to point to the _dontcall variety so that we can verify that
2043 * the pause actually did, you know, pause. */
2045 pause_message_begin_cb (http_parser *p)
2047 http_parser_pause(p, 1);
2048 *current_pause_parser = settings_dontcall;
2049 return message_begin_cb(p);
2053 pause_header_field_cb (http_parser *p, const char *buf, size_t len)
2055 http_parser_pause(p, 1);
2056 *current_pause_parser = settings_dontcall;
2057 return header_field_cb(p, buf, len);
2061 pause_header_value_cb (http_parser *p, const char *buf, size_t len)
2063 http_parser_pause(p, 1);
2064 *current_pause_parser = settings_dontcall;
2065 return header_value_cb(p, buf, len);
2069 pause_request_url_cb (http_parser *p, const char *buf, size_t len)
2071 http_parser_pause(p, 1);
2072 *current_pause_parser = settings_dontcall;
2073 return request_url_cb(p, buf, len);
2077 pause_body_cb (http_parser *p, const char *buf, size_t len)
2079 http_parser_pause(p, 1);
2080 *current_pause_parser = settings_dontcall;
2081 return body_cb(p, buf, len);
2085 pause_headers_complete_cb (http_parser *p)
2087 http_parser_pause(p, 1);
2088 *current_pause_parser = settings_dontcall;
2089 return headers_complete_cb(p);
2093 pause_message_complete_cb (http_parser *p)
2095 http_parser_pause(p, 1);
2096 *current_pause_parser = settings_dontcall;
2097 return message_complete_cb(p);
2101 pause_response_status_cb (http_parser *p, const char *buf, size_t len)
2103 http_parser_pause(p, 1);
2104 *current_pause_parser = settings_dontcall;
2105 return response_status_cb(p, buf, len);
2109 pause_chunk_header_cb (http_parser *p)
2111 http_parser_pause(p, 1);
2112 *current_pause_parser = settings_dontcall;
2113 return chunk_header_cb(p);
2117 pause_chunk_complete_cb (http_parser *p)
2119 http_parser_pause(p, 1);
2120 *current_pause_parser = settings_dontcall;
2121 return chunk_complete_cb(p);
2124 static http_parser_settings settings_pause =
2125 {.on_message_begin = pause_message_begin_cb
2126 ,.on_header_field = pause_header_field_cb
2127 ,.on_header_value = pause_header_value_cb
2128 ,.on_url = pause_request_url_cb
2129 ,.on_status = pause_response_status_cb
2130 ,.on_body = pause_body_cb
2131 ,.on_headers_complete = pause_headers_complete_cb
2132 ,.on_message_complete = pause_message_complete_cb
2133 ,.on_chunk_header = pause_chunk_header_cb
2134 ,.on_chunk_complete = pause_chunk_complete_cb
2137 static http_parser_settings settings =
2138 {.on_message_begin = message_begin_cb
2139 ,.on_header_field = header_field_cb
2140 ,.on_header_value = header_value_cb
2141 ,.on_url = request_url_cb
2142 ,.on_status = response_status_cb
2144 ,.on_headers_complete = headers_complete_cb
2145 ,.on_message_complete = message_complete_cb
2146 ,.on_chunk_header = chunk_header_cb
2147 ,.on_chunk_complete = chunk_complete_cb
2150 static http_parser_settings settings_count_body =
2151 {.on_message_begin = message_begin_cb
2152 ,.on_header_field = header_field_cb
2153 ,.on_header_value = header_value_cb
2154 ,.on_url = request_url_cb
2155 ,.on_status = response_status_cb
2156 ,.on_body = count_body_cb
2157 ,.on_headers_complete = headers_complete_cb
2158 ,.on_message_complete = message_complete_cb
2159 ,.on_chunk_header = chunk_header_cb
2160 ,.on_chunk_complete = chunk_complete_cb
2163 static http_parser_settings settings_null =
2164 {.on_message_begin = 0
2165 ,.on_header_field = 0
2166 ,.on_header_value = 0
2170 ,.on_headers_complete = 0
2171 ,.on_message_complete = 0
2172 ,.on_chunk_header = 0
2173 ,.on_chunk_complete = 0
2177 parser_init (enum http_parser_type type)
2181 assert(parser == NULL);
2183 parser = malloc(sizeof(http_parser));
2185 http_parser_init(parser, type);
2187 memset(&messages, 0, sizeof messages);
2199 size_t parse (const char *buf, size_t len)
2202 currently_parsing_eof = (len == 0);
2203 nparsed = http_parser_execute(parser, &settings, buf, len);
2207 size_t parse_count_body (const char *buf, size_t len)
2210 currently_parsing_eof = (len == 0);
2211 nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
2215 size_t parse_pause (const char *buf, size_t len)
2218 http_parser_settings s = settings_pause;
2220 currently_parsing_eof = (len == 0);
2221 current_pause_parser = &s;
2222 nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
2227 check_str_eq (const struct message *m,
2229 const char *expected,
2230 const char *found) {
2231 if ((expected == NULL) != (found == NULL)) {
2232 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2233 printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
2234 printf(" found %s\n", (found == NULL) ? "NULL" : found);
2237 if (expected != NULL && 0 != strcmp(expected, found)) {
2238 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2239 printf("expected '%s'\n", expected);
2240 printf(" found '%s'\n", found);
2247 check_num_eq (const struct message *m,
2251 if (expected != found) {
2252 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2253 printf("expected %d\n", expected);
2254 printf(" found %d\n", found);
2260 #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
2261 if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
2263 #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
2264 if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
2266 #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
2270 if ((u)->field_set & (1 << (fn))) { \
2271 memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
2272 (u)->field_data[(fn)].len); \
2273 ubuf[(u)->field_data[(fn)].len] = '\0'; \
2278 check_str_eq(expected, #prop, expected->prop, ubuf); \
2282 message_eq (int index, const struct message *expected)
2285 struct message *m = &messages[index];
2287 MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
2288 MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
2290 if (expected->type == HTTP_REQUEST) {
2291 MESSAGE_CHECK_NUM_EQ(expected, m, method);
2293 MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
2294 MESSAGE_CHECK_STR_EQ(expected, m, response_status);
2297 MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
2298 MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
2300 assert(m->message_begin_cb_called);
2301 assert(m->headers_complete_cb_called);
2302 assert(m->message_complete_cb_called);
2305 MESSAGE_CHECK_STR_EQ(expected, m, request_url);
2307 /* Check URL components; we can't do this w/ CONNECT since it doesn't
2308 * send us a well-formed URL.
2310 if (*m->request_url && m->method != HTTP_CONNECT) {
2311 struct http_parser_url u;
2313 if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
2314 fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
2319 if (expected->host) {
2320 MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
2323 if (expected->userinfo) {
2324 MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
2327 m->port = (u.field_set & (1 << UF_PORT)) ?
2330 MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
2331 MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
2332 MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
2333 MESSAGE_CHECK_NUM_EQ(expected, m, port);
2336 if (expected->body_size) {
2337 MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
2339 MESSAGE_CHECK_STR_EQ(expected, m, body);
2342 assert(m->num_chunks == m->num_chunks_complete);
2343 MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete);
2344 for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) {
2345 MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]);
2348 MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
2351 for (i = 0; i < m->num_headers; i++) {
2352 r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
2354 r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
2358 MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
2363 /* Given a sequence of varargs messages, return the number of them that the
2364 * parser should successfully parse, taking into account that upgraded
2365 * messages prevent all subsequent messages from being parsed.
2368 count_parsed_messages(const size_t nmsgs, ...) {
2372 va_start(ap, nmsgs);
2374 for (i = 0; i < nmsgs; i++) {
2375 struct message *m = va_arg(ap, struct message *);
2387 /* Given a sequence of bytes and the number of these that we were able to
2388 * parse, verify that upgrade bodies are correct.
2391 upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2396 va_start(ap, nmsgs);
2398 for (i = 0; i < nmsgs; i++) {
2399 struct message *m = va_arg(ap, struct message *);
2401 off += strlen(m->raw);
2404 off -= strlen(m->upgrade);
2406 /* Check the portion of the response after its specified upgrade */
2407 if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2411 /* Fix up the response so that message_eq() will verify the beginning
2413 *(body + nread + strlen(m->upgrade)) = '\0';
2414 messages[num_messages -1 ].upgrade = body + nread;
2422 printf("\n\n*** Error: expected a message with upgrade ***\n");
2428 print_error (const char *raw, size_t error_location)
2430 fprintf(stderr, "\n*** %s ***\n\n",
2431 http_errno_description(HTTP_PARSER_ERRNO(parser)));
2433 int this_line = 0, char_len = 0;
2434 size_t i, j, len = strlen(raw), error_location_line = 0;
2435 for (i = 0; i < len; i++) {
2436 if (i == error_location) this_line = 1;
2440 fprintf(stderr, "\\r");
2444 fprintf(stderr, "\\n\n");
2446 if (this_line) goto print;
2448 error_location_line = 0;
2453 fputc(raw[i], stderr);
2456 if (!this_line) error_location_line += char_len;
2459 fprintf(stderr, "[eof]\n");
2462 for (j = 0; j < error_location_line; j++) {
2465 fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2469 test_preserve_data (void)
2471 char my_data[] = "application-specific data";
2473 parser.data = my_data;
2474 http_parser_init(&parser, HTTP_REQUEST);
2475 if (parser.data != my_data) {
2476 printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2485 struct http_parser_url u;
2489 const struct url_test url_tests[] =
2490 { {.name="proxy request"
2491 ,.url="http://hostname/"
2494 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2497 {{ 0, 4 } /* UF_SCHEMA */
2498 ,{ 7, 8 } /* UF_HOST */
2499 ,{ 0, 0 } /* UF_PORT */
2500 ,{ 15, 1 } /* UF_PATH */
2501 ,{ 0, 0 } /* UF_QUERY */
2502 ,{ 0, 0 } /* UF_FRAGMENT */
2503 ,{ 0, 0 } /* UF_USERINFO */
2509 , {.name="proxy request with port"
2510 ,.url="http://hostname:444/"
2513 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2516 {{ 0, 4 } /* UF_SCHEMA */
2517 ,{ 7, 8 } /* UF_HOST */
2518 ,{ 16, 3 } /* UF_PORT */
2519 ,{ 19, 1 } /* UF_PATH */
2520 ,{ 0, 0 } /* UF_QUERY */
2521 ,{ 0, 0 } /* UF_FRAGMENT */
2522 ,{ 0, 0 } /* UF_USERINFO */
2528 , {.name="CONNECT request"
2529 ,.url="hostname:443"
2532 {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2535 {{ 0, 0 } /* UF_SCHEMA */
2536 ,{ 0, 8 } /* UF_HOST */
2537 ,{ 9, 3 } /* UF_PORT */
2538 ,{ 0, 0 } /* UF_PATH */
2539 ,{ 0, 0 } /* UF_QUERY */
2540 ,{ 0, 0 } /* UF_FRAGMENT */
2541 ,{ 0, 0 } /* UF_USERINFO */
2547 , {.name="CONNECT request but not connect"
2548 ,.url="hostname:443"
2553 , {.name="proxy ipv6 request"
2554 ,.url="http://[1:2::3:4]/"
2557 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2560 {{ 0, 4 } /* UF_SCHEMA */
2561 ,{ 8, 8 } /* UF_HOST */
2562 ,{ 0, 0 } /* UF_PORT */
2563 ,{ 17, 1 } /* UF_PATH */
2564 ,{ 0, 0 } /* UF_QUERY */
2565 ,{ 0, 0 } /* UF_FRAGMENT */
2566 ,{ 0, 0 } /* UF_USERINFO */
2572 , {.name="proxy ipv6 request with port"
2573 ,.url="http://[1:2::3:4]:67/"
2576 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2579 {{ 0, 4 } /* UF_SCHEMA */
2580 ,{ 8, 8 } /* UF_HOST */
2581 ,{ 18, 2 } /* UF_PORT */
2582 ,{ 20, 1 } /* UF_PATH */
2583 ,{ 0, 0 } /* UF_QUERY */
2584 ,{ 0, 0 } /* UF_FRAGMENT */
2585 ,{ 0, 0 } /* UF_USERINFO */
2591 , {.name="CONNECT ipv6 address"
2592 ,.url="[1:2::3:4]:443"
2595 {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2598 {{ 0, 0 } /* UF_SCHEMA */
2599 ,{ 1, 8 } /* UF_HOST */
2600 ,{ 11, 3 } /* UF_PORT */
2601 ,{ 0, 0 } /* UF_PATH */
2602 ,{ 0, 0 } /* UF_QUERY */
2603 ,{ 0, 0 } /* UF_FRAGMENT */
2604 ,{ 0, 0 } /* UF_USERINFO */
2610 , {.name="ipv4 in ipv6 address"
2611 ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2614 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2617 {{ 0, 4 } /* UF_SCHEMA */
2618 ,{ 8, 37 } /* UF_HOST */
2619 ,{ 0, 0 } /* UF_PORT */
2620 ,{ 46, 1 } /* UF_PATH */
2621 ,{ 0, 0 } /* UF_QUERY */
2622 ,{ 0, 0 } /* UF_FRAGMENT */
2623 ,{ 0, 0 } /* UF_USERINFO */
2629 , {.name="extra ? in query string"
2630 ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
2631 "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
2632 "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2635 {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2638 {{ 0, 4 } /* UF_SCHEMA */
2639 ,{ 7, 10 } /* UF_HOST */
2640 ,{ 0, 0 } /* UF_PORT */
2641 ,{ 17, 12 } /* UF_PATH */
2642 ,{ 30,187 } /* UF_QUERY */
2643 ,{ 0, 0 } /* UF_FRAGMENT */
2644 ,{ 0, 0 } /* UF_USERINFO */
2650 , {.name="space URL encoded"
2651 ,.url="/toto.html?toto=a%20b"
2654 {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2657 {{ 0, 0 } /* UF_SCHEMA */
2658 ,{ 0, 0 } /* UF_HOST */
2659 ,{ 0, 0 } /* UF_PORT */
2660 ,{ 0, 10 } /* UF_PATH */
2661 ,{ 11, 10 } /* UF_QUERY */
2662 ,{ 0, 0 } /* UF_FRAGMENT */
2663 ,{ 0, 0 } /* UF_USERINFO */
2670 , {.name="URL fragment"
2671 ,.url="/toto.html#titi"
2674 {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2677 {{ 0, 0 } /* UF_SCHEMA */
2678 ,{ 0, 0 } /* UF_HOST */
2679 ,{ 0, 0 } /* UF_PORT */
2680 ,{ 0, 10 } /* UF_PATH */
2681 ,{ 0, 0 } /* UF_QUERY */
2682 ,{ 11, 4 } /* UF_FRAGMENT */
2683 ,{ 0, 0 } /* UF_USERINFO */
2689 , {.name="complex URL fragment"
2690 ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
2691 "http://www.example.com/index.html?foo=bar&hello=world#midpage"
2694 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2698 {{ 0, 4 } /* UF_SCHEMA */
2699 ,{ 7, 22 } /* UF_HOST */
2700 ,{ 0, 0 } /* UF_PORT */
2701 ,{ 29, 6 } /* UF_PATH */
2702 ,{ 36, 69 } /* UF_QUERY */
2703 ,{106, 7 } /* UF_FRAGMENT */
2704 ,{ 0, 0 } /* UF_USERINFO */
2710 , {.name="complex URL from node js url parser doc"
2711 ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
2714 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2715 (1<<UF_QUERY) | (1<<UF_FRAGMENT)
2718 {{ 0, 4 } /* UF_SCHEMA */
2719 ,{ 7, 8 } /* UF_HOST */
2720 ,{ 16, 4 } /* UF_PORT */
2721 ,{ 20, 8 } /* UF_PATH */
2722 ,{ 29, 12 } /* UF_QUERY */
2723 ,{ 42, 4 } /* UF_FRAGMENT */
2724 ,{ 0, 0 } /* UF_USERINFO */
2730 , {.name="complex URL with basic auth from node js url parser doc"
2731 ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
2734 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2735 (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
2738 {{ 0, 4 } /* UF_SCHEMA */
2739 ,{ 11, 8 } /* UF_HOST */
2740 ,{ 20, 4 } /* UF_PORT */
2741 ,{ 24, 8 } /* UF_PATH */
2742 ,{ 33, 12 } /* UF_QUERY */
2743 ,{ 46, 4 } /* UF_FRAGMENT */
2744 ,{ 7, 3 } /* UF_USERINFO */
2751 ,.url="http://a:b@@hostname:443/"
2756 , {.name="proxy empty host"
2757 ,.url="http://:443/"
2762 , {.name="proxy empty port"
2763 ,.url="http://hostname:/"
2768 , {.name="CONNECT with basic auth"
2769 ,.url="a:b@hostname:443"
2774 , {.name="CONNECT empty host"
2780 , {.name="CONNECT empty port"
2786 , {.name="CONNECT with extra bits"
2787 ,.url="hostname:443/"
2792 , {.name="space in URL"
2797 , {.name="proxy basic auth with space url encoded"
2798 ,.url="http://a%20:b@host.com/"
2801 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2804 {{ 0, 4 } /* UF_SCHEMA */
2805 ,{ 14, 8 } /* UF_HOST */
2806 ,{ 0, 0 } /* UF_PORT */
2807 ,{ 22, 1 } /* UF_PATH */
2808 ,{ 0, 0 } /* UF_QUERY */
2809 ,{ 0, 0 } /* UF_FRAGMENT */
2810 ,{ 7, 6 } /* UF_USERINFO */
2816 , {.name="carriage return in URL"
2821 , {.name="proxy double : in URL"
2822 ,.url="http://hostname::443/"
2826 , {.name="proxy basic auth with double :"
2827 ,.url="http://a::b@host.com/"
2830 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2833 {{ 0, 4 } /* UF_SCHEMA */
2834 ,{ 12, 8 } /* UF_HOST */
2835 ,{ 0, 0 } /* UF_PORT */
2836 ,{ 20, 1 } /* UF_PATH */
2837 ,{ 0, 0 } /* UF_QUERY */
2838 ,{ 0, 0 } /* UF_FRAGMENT */
2839 ,{ 7, 4 } /* UF_USERINFO */
2845 , {.name="line feed in URL"
2850 , {.name="proxy empty basic auth"
2851 ,.url="http://@hostname/fo"
2853 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
2856 {{ 0, 4 } /* UF_SCHEMA */
2857 ,{ 8, 8 } /* UF_HOST */
2858 ,{ 0, 0 } /* UF_PORT */
2859 ,{ 16, 3 } /* UF_PATH */
2860 ,{ 0, 0 } /* UF_QUERY */
2861 ,{ 0, 0 } /* UF_FRAGMENT */
2862 ,{ 0, 0 } /* UF_USERINFO */
2867 , {.name="proxy line feed in hostname"
2868 ,.url="http://host\name/fo"
2872 , {.name="proxy % in hostname"
2873 ,.url="http://host%name/fo"
2877 , {.name="proxy ; in hostname"
2878 ,.url="http://host;ame/fo"
2882 , {.name="proxy basic auth with unreservedchars"
2883 ,.url="http://a!;-_!=+$@host.com/"
2886 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2889 {{ 0, 4 } /* UF_SCHEMA */
2890 ,{ 17, 8 } /* UF_HOST */
2891 ,{ 0, 0 } /* UF_PORT */
2892 ,{ 25, 1 } /* UF_PATH */
2893 ,{ 0, 0 } /* UF_QUERY */
2894 ,{ 0, 0 } /* UF_FRAGMENT */
2895 ,{ 7, 9 } /* UF_USERINFO */
2901 , {.name="proxy only empty basic auth"
2906 , {.name="proxy only basic auth"
2907 ,.url="http://toto@/fo"
2911 , {.name="proxy emtpy hostname"
2916 , {.name="proxy = in URL"
2917 ,.url="http://host=ame/fo"
2921 #if HTTP_PARSER_STRICT
2923 , {.name="tab in URL"
2928 , {.name="form feed in URL"
2933 #else /* !HTTP_PARSER_STRICT */
2935 , {.name="tab in URL"
2938 {.field_set=(1 << UF_PATH)
2940 {{ 0, 0 } /* UF_SCHEMA */
2941 ,{ 0, 0 } /* UF_HOST */
2942 ,{ 0, 0 } /* UF_PORT */
2943 ,{ 0, 9 } /* UF_PATH */
2944 ,{ 0, 0 } /* UF_QUERY */
2945 ,{ 0, 0 } /* UF_FRAGMENT */
2946 ,{ 0, 0 } /* UF_USERINFO */
2952 , {.name="form feed in URL"
2955 {.field_set=(1 << UF_PATH)
2957 {{ 0, 0 } /* UF_SCHEMA */
2958 ,{ 0, 0 } /* UF_HOST */
2959 ,{ 0, 0 } /* UF_PORT */
2960 ,{ 0, 9 } /* UF_PATH */
2961 ,{ 0, 0 } /* UF_QUERY */
2962 ,{ 0, 0 } /* UF_FRAGMENT */
2963 ,{ 0, 0 } /* UF_USERINFO */
2972 dump_url (const char *url, const struct http_parser_url *u)
2976 printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
2977 for (i = 0; i < UF_MAX; i++) {
2978 if ((u->field_set & (1 << i)) == 0) {
2979 printf("\tfield_data[%u]: unset\n", i);
2983 printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
2985 u->field_data[i].off,
2986 u->field_data[i].len,
2987 u->field_data[i].len,
2988 url + u->field_data[i].off);
2993 test_parse_url (void)
2995 struct http_parser_url u;
2996 const struct url_test *test;
3000 for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
3001 test = &url_tests[i];
3002 memset(&u, 0, sizeof(u));
3004 rv = http_parser_parse_url(test->url,
3009 if (test->rv == 0) {
3011 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
3012 "unexpected rv %d ***\n\n", test->url, test->name, rv);
3016 if (memcmp(&u, &test->u, sizeof(u)) != 0) {
3017 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
3018 test->url, test->name);
3020 printf("target http_parser_url:\n");
3021 dump_url(test->url, &test->u);
3022 printf("result http_parser_url:\n");
3023 dump_url(test->url, &u);
3030 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
3031 "unexpected rv %d ***\n\n", test->url, test->name, rv);
3039 test_method_str (void)
3041 assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
3042 assert(0 == strcmp("<unknown>", http_method_str(1337)));
3046 test_message (const struct message *message)
3048 size_t raw_len = strlen(message->raw);
3050 for (msg1len = 0; msg1len < raw_len; msg1len++) {
3051 parser_init(message->type);
3054 const char *msg1 = message->raw;
3055 const char *msg2 = msg1 + msg1len;
3056 size_t msg2len = raw_len - msg1len;
3059 read = parse(msg1, msg1len);
3061 if (message->upgrade && parser->upgrade && num_messages > 0) {
3062 messages[num_messages - 1].upgrade = msg1 + read;
3066 if (read != msg1len) {
3067 print_error(msg1, read);
3073 read = parse(msg2, msg2len);
3075 if (message->upgrade && parser->upgrade) {
3076 messages[num_messages - 1].upgrade = msg2 + read;
3080 if (read != msg2len) {
3081 print_error(msg2, read);
3085 read = parse(NULL, 0);
3088 print_error(message->raw, read);
3094 if (num_messages != 1) {
3095 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
3099 if(!message_eq(0, message)) abort();
3106 test_message_count_body (const struct message *message)
3108 parser_init(message->type);
3111 size_t l = strlen(message->raw);
3113 size_t chunk = 4024;
3115 for (i = 0; i < l; i+= chunk) {
3116 toread = MIN(l-i, chunk);
3117 read = parse_count_body(message->raw + i, toread);
3118 if (read != toread) {
3119 print_error(message->raw, read);
3125 read = parse_count_body(NULL, 0);
3127 print_error(message->raw, read);
3131 if (num_messages != 1) {
3132 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
3136 if(!message_eq(0, message)) abort();
3142 test_simple (const char *buf, enum http_errno err_expected)
3144 parser_init(HTTP_REQUEST);
3146 enum http_errno err;
3148 parse(buf, strlen(buf));
3149 err = HTTP_PARSER_ERRNO(parser);
3154 /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
3155 * long as the caller isn't expecting success.
3157 #if HTTP_PARSER_STRICT
3158 if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
3160 if (err_expected != err) {
3162 fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
3163 http_errno_name(err_expected), http_errno_name(err), buf);
3169 test_invalid_header_content (int req, const char* str)
3172 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3176 "GET / HTTP/1.1\r\n" :
3177 "HTTP/1.1 200 OK\r\n";
3178 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3179 assert(parsed == strlen(buf));
3182 size_t buflen = strlen(buf);
3184 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3185 if (parsed != buflen) {
3186 assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3191 "\n*** Error expected but none in invalid header content test ***\n");
3196 test_invalid_header_field_content_error (int req)
3198 test_invalid_header_content(req, "Foo: F\01ailure");
3199 test_invalid_header_content(req, "Foo: B\02ar");
3203 test_invalid_header_field (int req, const char* str)
3206 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3210 "GET / HTTP/1.1\r\n" :
3211 "HTTP/1.1 200 OK\r\n";
3212 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3213 assert(parsed == strlen(buf));
3216 size_t buflen = strlen(buf);
3218 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3219 if (parsed != buflen) {
3220 assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3225 "\n*** Error expected but none in invalid header token test ***\n");
3230 test_invalid_header_field_token_error (int req)
3232 test_invalid_header_field(req, "Fo@: Failure");
3233 test_invalid_header_field(req, "Foo\01\test: Bar");
3237 test_double_content_length_error (int req)
3240 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3244 "GET / HTTP/1.1\r\n" :
3245 "HTTP/1.1 200 OK\r\n";
3246 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3247 assert(parsed == strlen(buf));
3249 buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
3250 size_t buflen = strlen(buf);
3252 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3253 if (parsed != buflen) {
3254 assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
3259 "\n*** Error expected but none in double content-length test ***\n");
3264 test_chunked_content_length_error (int req)
3267 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3271 "GET / HTTP/1.1\r\n" :
3272 "HTTP/1.1 200 OK\r\n";
3273 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3274 assert(parsed == strlen(buf));
3276 buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
3277 size_t buflen = strlen(buf);
3279 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3280 if (parsed != buflen) {
3281 assert(HTTP_PARSER_ERRNO(&parser) == HPE_CHUNKED_WITH_CONTENT_LENGTH);
3286 "\n*** Error expected but none in chunked content-length test ***\n");
3291 test_header_cr_no_lf_error (int req)
3294 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3298 "GET / HTTP/1.1\r\n" :
3299 "HTTP/1.1 200 OK\r\n";
3300 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3301 assert(parsed == strlen(buf));
3303 buf = "Foo: 1\rBar: 1\r\n\r\n";
3304 size_t buflen = strlen(buf);
3306 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3307 if (parsed != buflen) {
3308 assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED);
3313 "\n*** Error expected but none in header whitespace test ***\n");
3318 test_header_overflow_error (int req)
3321 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3324 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
3325 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3326 assert(parsed == strlen(buf));
3328 buf = "header-key: header-value\r\n";
3329 size_t buflen = strlen(buf);
3332 for (i = 0; i < 10000; i++) {
3333 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3334 if (parsed != buflen) {
3335 //fprintf(stderr, "error found on iter %d\n", i);
3336 assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
3341 fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
3347 test_header_nread_value ()
3350 http_parser_init(&parser, HTTP_REQUEST);
3353 buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n";
3354 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3355 assert(parsed == strlen(buf));
3357 assert(parser.nread == strlen(buf));
3362 test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
3365 http_parser_init(&parser, HTTP_RESPONSE);
3366 http_parser_execute(&parser, &settings_null, buf, buflen);
3369 assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
3371 assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
3375 test_header_content_length_overflow_error (void)
3378 "HTTP/1.1 200 OK\r\n" \
3379 "Content-Length: " #size "\r\n" \
3381 const char a[] = X(1844674407370955160); /* 2^64 / 10 - 1 */
3382 const char b[] = X(18446744073709551615); /* 2^64-1 */
3383 const char c[] = X(18446744073709551616); /* 2^64 */
3385 test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
3386 test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
3387 test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
3391 test_chunk_content_length_overflow_error (void)
3394 "HTTP/1.1 200 OK\r\n" \
3395 "Transfer-Encoding: chunked\r\n" \
3399 const char a[] = X(FFFFFFFFFFFFFFE); /* 2^64 / 16 - 1 */
3400 const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
3401 const char c[] = X(10000000000000000); /* 2^64 */
3403 test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
3404 test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
3405 test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
3409 test_no_overflow_long_body (int req, size_t length)
3412 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3416 size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
3417 req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
3418 parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
3419 if (parsed != buf1len)
3422 for (i = 0; i < length; i++) {
3424 parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
3429 parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
3430 if (parsed != buf1len) goto err;
3435 "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
3436 req ? "REQUEST" : "RESPONSE",
3437 (unsigned long)length);
3442 test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
3444 int message_count = count_parsed_messages(3, r1, r2, r3);
3446 char total[ strlen(r1->raw)
3453 strcat(total, r1->raw);
3454 strcat(total, r2->raw);
3455 strcat(total, r3->raw);
3457 parser_init(r1->type);
3461 read = parse(total, strlen(total));
3463 if (parser->upgrade) {
3464 upgrade_message_fix(total, read, 3, r1, r2, r3);
3468 if (read != strlen(total)) {
3469 print_error(total, read);
3473 read = parse(NULL, 0);
3476 print_error(total, read);
3482 if (message_count != num_messages) {
3483 fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
3487 if (!message_eq(0, r1)) abort();
3488 if (message_count > 1 && !message_eq(1, r2)) abort();
3489 if (message_count > 2 && !message_eq(2, r3)) abort();
3494 /* SCAN through every possible breaking to make sure the
3495 * parser can handle getting the content in any chunks that
3496 * might come from the socket
3499 test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
3501 char total[80*1024] = "\0";
3502 char buf1[80*1024] = "\0";
3503 char buf2[80*1024] = "\0";
3504 char buf3[80*1024] = "\0";
3506 strcat(total, r1->raw);
3507 strcat(total, r2->raw);
3508 strcat(total, r3->raw);
3512 int total_len = strlen(total);
3514 int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
3517 size_t buf1_len, buf2_len, buf3_len;
3518 int message_count = count_parsed_messages(3, r1, r2, r3);
3521 for (type_both = 0; type_both < 2; type_both ++ ) {
3522 for (j = 2; j < total_len; j ++ ) {
3523 for (i = 1; i < j; i ++ ) {
3525 if (ops % 1000 == 0) {
3526 printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
3531 parser_init(type_both ? HTTP_BOTH : r1->type);
3534 strlncpy(buf1, sizeof(buf1), total, buf1_len);
3538 strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
3541 buf3_len = total_len - j;
3542 strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
3545 read = parse(buf1, buf1_len);
3547 if (parser->upgrade) goto test;
3549 if (read != buf1_len) {
3550 print_error(buf1, read);
3554 read += parse(buf2, buf2_len);
3556 if (parser->upgrade) goto test;
3558 if (read != buf1_len + buf2_len) {
3559 print_error(buf2, read);
3563 read += parse(buf3, buf3_len);
3565 if (parser->upgrade) goto test;
3567 if (read != buf1_len + buf2_len + buf3_len) {
3568 print_error(buf3, read);
3575 if (parser->upgrade) {
3576 upgrade_message_fix(total, read, 3, r1, r2, r3);
3579 if (message_count != num_messages) {
3580 fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3581 message_count, num_messages);
3585 if (!message_eq(0, r1)) {
3586 fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
3590 if (message_count > 1 && !message_eq(1, r2)) {
3591 fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
3595 if (message_count > 2 && !message_eq(2, r3)) {
3596 fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
3604 puts("\b\b\b\b100%");
3608 fprintf(stderr, "i=%d j=%d\n", i, j);
3609 fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
3610 fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
3611 fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
3615 // user required to free the result
3616 // string terminated by \0
3618 create_large_chunked_message (int body_size_in_kb, const char* headers)
3622 size_t headers_len = strlen(headers);
3623 size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
3624 char * buf = malloc(bufsize);
3626 memcpy(buf, headers, headers_len);
3627 wrote += headers_len;
3629 for (i = 0; i < body_size_in_kb; i++) {
3630 // write 1kb chunk into the body.
3631 memcpy(buf + wrote, "400\r\n", 5);
3633 memset(buf + wrote, 'C', 1024);
3635 strcpy(buf + wrote, "\r\n");
3639 memcpy(buf + wrote, "0\r\n\r\n", 6);
3641 assert(wrote == bufsize);
3646 /* Verify that we can pause parsing at any of the bytes in the
3647 * message and still get the result that we're expecting. */
3649 test_message_pause (const struct message *msg)
3651 char *buf = (char*) msg->raw;
3652 size_t buflen = strlen(msg->raw);
3655 parser_init(msg->type);
3658 nread = parse_pause(buf, buflen);
3660 // We can only set the upgrade buffer once we've gotten our message
3661 // completion callback.
3662 if (messages[0].message_complete_cb_called &&
3665 messages[0].upgrade = buf + nread;
3669 if (nread < buflen) {
3671 // Not much do to if we failed a strict-mode check
3672 if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
3677 assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
3682 http_parser_pause(parser, 0);
3683 } while (buflen > 0);
3685 nread = parse_pause(NULL, 0);
3686 assert (nread == 0);
3689 if (num_messages != 1) {
3690 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
3694 if(!message_eq(0, msg)) abort();
3706 unsigned long version;
3711 version = http_parser_version();
3712 major = (version >> 16) & 255;
3713 minor = (version >> 8) & 255;
3714 patch = version & 255;
3715 printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version);
3717 printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
3719 for (request_count = 0; requests[request_count].name; request_count++);
3720 for (response_count = 0; responses[response_count].name; response_count++);
3723 test_preserve_data();
3728 test_header_nread_value();
3730 //// OVERFLOW CONDITIONS
3732 test_header_overflow_error(HTTP_REQUEST);
3733 test_no_overflow_long_body(HTTP_REQUEST, 1000);
3734 test_no_overflow_long_body(HTTP_REQUEST, 100000);
3736 test_header_overflow_error(HTTP_RESPONSE);
3737 test_no_overflow_long_body(HTTP_RESPONSE, 1000);
3738 test_no_overflow_long_body(HTTP_RESPONSE, 100000);
3740 test_header_content_length_overflow_error();
3741 test_chunk_content_length_overflow_error();
3743 //// HEADER FIELD CONDITIONS
3744 test_double_content_length_error(HTTP_REQUEST);
3745 test_chunked_content_length_error(HTTP_REQUEST);
3746 test_header_cr_no_lf_error(HTTP_REQUEST);
3747 test_invalid_header_field_token_error(HTTP_REQUEST);
3748 test_invalid_header_field_content_error(HTTP_REQUEST);
3749 test_double_content_length_error(HTTP_RESPONSE);
3750 test_chunked_content_length_error(HTTP_RESPONSE);
3751 test_header_cr_no_lf_error(HTTP_RESPONSE);
3752 test_invalid_header_field_token_error(HTTP_RESPONSE);
3753 test_invalid_header_field_content_error(HTTP_RESPONSE);
3757 for (i = 0; i < response_count; i++) {
3758 test_message(&responses[i]);
3761 for (i = 0; i < response_count; i++) {
3762 test_message_pause(&responses[i]);
3765 for (i = 0; i < response_count; i++) {
3766 if (!responses[i].should_keep_alive) continue;
3767 for (j = 0; j < response_count; j++) {
3768 if (!responses[j].should_keep_alive) continue;
3769 for (k = 0; k < response_count; k++) {
3770 test_multiple3(&responses[i], &responses[j], &responses[k]);
3775 test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
3776 test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
3778 // test very large chunked response
3780 char * msg = create_large_chunked_message(31337,
3781 "HTTP/1.0 200 OK\r\n"
3782 "Transfer-Encoding: chunked\r\n"
3783 "Content-Type: text/plain\r\n"
3785 struct message large_chunked =
3786 {.name= "large chunked"
3787 ,.type= HTTP_RESPONSE
3789 ,.should_keep_alive= FALSE
3790 ,.message_complete_on_eof= FALSE
3794 ,.response_status= "OK"
3797 { { "Transfer-Encoding", "chunked" }
3798 , { "Content-Type", "text/plain" }
3800 ,.body_size= 31337*1024
3801 ,.num_chunks_complete= 31338
3803 for (i = 0; i < MAX_CHUNKS; i++) {
3804 large_chunked.chunk_lengths[i] = 1024;
3806 test_message_count_body(&large_chunked);
3812 printf("response scan 1/2 ");
3813 test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
3814 , &responses[NO_BODY_HTTP10_KA_204]
3815 , &responses[NO_REASON_PHRASE]
3818 printf("response scan 2/2 ");
3819 test_scan( &responses[BONJOUR_MADAME_FR]
3820 , &responses[UNDERSTORE_HEADER_KEY]
3821 , &responses[NO_CARRIAGE_RET]
3824 puts("responses okay");
3829 test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
3831 // Well-formed but incomplete
3832 test_simple("GET / HTTP/1.1\r\n"
3833 "Content-Type: text/plain\r\n"
3834 "Content-Length: 6\r\n"
3839 static const char *all_methods[] = {
3845 //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
3865 const char **this_method;
3866 for (this_method = all_methods; *this_method; this_method++) {
3868 sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3869 test_simple(buf, HPE_OK);
3872 static const char *bad_methods[] = {
3886 for (this_method = bad_methods; *this_method; this_method++) {
3888 sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3889 test_simple(buf, HPE_INVALID_METHOD);
3892 // illegal header field name line folding
3893 test_simple("GET / HTTP/1.1\r\n"
3897 HPE_INVALID_HEADER_TOKEN);
3899 const char *dumbfuck2 =
3900 "GET / HTTP/1.1\r\n"
3901 "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
3902 "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
3903 "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
3904 "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
3905 "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
3906 "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
3907 "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
3908 "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
3909 "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
3910 "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
3911 "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
3912 "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
3913 "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
3914 "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
3915 "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
3916 "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
3917 "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
3918 "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
3919 "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
3920 "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
3921 "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
3922 "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
3923 "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
3924 "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
3925 "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
3926 "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
3927 "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
3928 "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
3929 "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
3930 "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
3932 "\t-----END CERTIFICATE-----\r\n"
3934 test_simple(dumbfuck2, HPE_OK);
3936 const char *corrupted_connection =
3937 "GET / HTTP/1.1\r\n"
3938 "Host: www.example.com\r\n"
3939 "Connection\r\033\065\325eep-Alive\r\n"
3940 "Accept-Encoding: gzip\r\n"
3942 test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN);
3944 const char *corrupted_header_name =
3945 "GET / HTTP/1.1\r\n"
3946 "Host: www.example.com\r\n"
3947 "X-Some-Header\r\033\065\325eep-Alive\r\n"
3948 "Accept-Encoding: gzip\r\n"
3950 test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN);
3953 // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
3956 // no content-length
3957 // error if there is a body without content length
3958 const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
3962 test_simple(bad_get_no_headers_no_body, 0);
3964 /* TODO sending junk and large headers gets rejected */
3967 /* check to make sure our predefined requests are okay */
3968 for (i = 0; requests[i].name; i++) {
3969 test_message(&requests[i]);
3972 for (i = 0; i < request_count; i++) {
3973 test_message_pause(&requests[i]);
3976 for (i = 0; i < request_count; i++) {
3977 if (!requests[i].should_keep_alive) continue;
3978 for (j = 0; j < request_count; j++) {
3979 if (!requests[j].should_keep_alive) continue;
3980 for (k = 0; k < request_count; k++) {
3981 test_multiple3(&requests[i], &requests[j], &requests[k]);
3986 printf("request scan 1/4 ");
3987 test_scan( &requests[GET_NO_HEADERS_NO_BODY]
3988 , &requests[GET_ONE_HEADER_NO_BODY]
3989 , &requests[GET_NO_HEADERS_NO_BODY]
3992 printf("request scan 2/4 ");
3993 test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
3994 , &requests[POST_IDENTITY_BODY_WORLD]
3995 , &requests[GET_FUNKY_CONTENT_LENGTH]
3998 printf("request scan 3/4 ");
3999 test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
4000 , &requests[CHUNKED_W_TRAILING_HEADERS]
4001 , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
4004 printf("request scan 4/4 ");
4005 test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
4006 , &requests[PREFIX_NEWLINE_GET ]
4007 , &requests[CONNECT_REQUEST]
4010 puts("requests okay");