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
43 #define MIN(a,b) ((a) < (b) ? (a) : (b))
45 static http_parser *parser;
48 const char *name; // for debugging purposes
50 enum http_parser_type type;
51 enum http_method method;
53 char response_status[MAX_ELEMENT_SIZE];
54 char request_path[MAX_ELEMENT_SIZE];
55 char request_url[MAX_ELEMENT_SIZE];
56 char fragment[MAX_ELEMENT_SIZE];
57 char query_string[MAX_ELEMENT_SIZE];
58 char body[MAX_ELEMENT_SIZE];
64 enum { NONE=0, FIELD, VALUE } last_header_element;
65 char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
66 int should_keep_alive;
68 const char *upgrade; // upgraded body
70 unsigned short http_major;
71 unsigned short http_minor;
73 int message_begin_cb_called;
74 int headers_complete_cb_called;
75 int message_complete_cb_called;
76 int message_complete_on_eof;
80 static int currently_parsing_eof;
82 static struct message messages[5];
83 static int num_messages;
84 static http_parser_settings *current_pause_parser;
86 /* * R E Q U E S T S * */
87 const struct message requests[] =
91 ,.raw= "GET /test HTTP/1.1\r\n"
92 "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"
93 "Host: 0.0.0.0=5000\r\n"
96 ,.should_keep_alive= TRUE
97 ,.message_complete_on_eof= FALSE
103 ,.request_path= "/test"
104 ,.request_url= "/test"
107 { { "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" }
108 , { "Host", "0.0.0.0=5000" }
109 , { "Accept", "*/*" }
114 #define FIREFOX_GET 1
115 , {.name= "firefox get"
117 ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
118 "Host: 0.0.0.0=5000\r\n"
119 "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
120 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
121 "Accept-Language: en-us,en;q=0.5\r\n"
122 "Accept-Encoding: gzip,deflate\r\n"
123 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
124 "Keep-Alive: 300\r\n"
125 "Connection: keep-alive\r\n"
127 ,.should_keep_alive= TRUE
128 ,.message_complete_on_eof= FALSE
134 ,.request_path= "/favicon.ico"
135 ,.request_url= "/favicon.ico"
138 { { "Host", "0.0.0.0=5000" }
139 , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
140 , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
141 , { "Accept-Language", "en-us,en;q=0.5" }
142 , { "Accept-Encoding", "gzip,deflate" }
143 , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
144 , { "Keep-Alive", "300" }
145 , { "Connection", "keep-alive" }
153 ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
154 "aaaaaaaaaaaaa:++++++++++\r\n"
156 ,.should_keep_alive= TRUE
157 ,.message_complete_on_eof= FALSE
163 ,.request_path= "/dumbfuck"
164 ,.request_url= "/dumbfuck"
167 { { "aaaaaaaaaaaaa", "++++++++++" }
172 #define FRAGMENT_IN_URI 3
173 , {.name= "fragment in url"
175 ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
177 ,.should_keep_alive= TRUE
178 ,.message_complete_on_eof= FALSE
182 ,.query_string= "page=1"
183 ,.fragment= "posts-17408"
184 ,.request_path= "/forums/1/topics/2375"
185 /* XXX request url does include fragment? */
186 ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
191 #define GET_NO_HEADERS_NO_BODY 4
192 , {.name= "get no headers no body"
194 ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
196 ,.should_keep_alive= TRUE
197 ,.message_complete_on_eof= FALSE /* would need Connection: close */
203 ,.request_path= "/get_no_headers_no_body/world"
204 ,.request_url= "/get_no_headers_no_body/world"
209 #define GET_ONE_HEADER_NO_BODY 5
210 , {.name= "get one header no body"
212 ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
215 ,.should_keep_alive= TRUE
216 ,.message_complete_on_eof= FALSE /* would need Connection: close */
222 ,.request_path= "/get_one_header_no_body"
223 ,.request_url= "/get_one_header_no_body"
226 { { "Accept" , "*/*" }
231 #define GET_FUNKY_CONTENT_LENGTH 6
232 , {.name= "get funky content length body hello"
234 ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
235 "conTENT-Length: 5\r\n"
238 ,.should_keep_alive= FALSE
239 ,.message_complete_on_eof= FALSE
245 ,.request_path= "/get_funky_content_length_body_hello"
246 ,.request_url= "/get_funky_content_length_body_hello"
249 { { "conTENT-Length" , "5" }
254 #define POST_IDENTITY_BODY_WORLD 7
255 , {.name= "post identity body world"
257 ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
259 "Transfer-Encoding: identity\r\n"
260 "Content-Length: 5\r\n"
263 ,.should_keep_alive= TRUE
264 ,.message_complete_on_eof= FALSE
268 ,.query_string= "q=search"
270 ,.request_path= "/post_identity_body_world"
271 ,.request_url= "/post_identity_body_world?q=search#hey"
274 { { "Accept", "*/*" }
275 , { "Transfer-Encoding", "identity" }
276 , { "Content-Length", "5" }
281 #define POST_CHUNKED_ALL_YOUR_BASE 8
282 , {.name= "post - chunked body: all your base are belong to us"
284 ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
285 "Transfer-Encoding: chunked\r\n"
287 "1e\r\nall your base are belong to us\r\n"
290 ,.should_keep_alive= TRUE
291 ,.message_complete_on_eof= FALSE
297 ,.request_path= "/post_chunked_all_your_base"
298 ,.request_url= "/post_chunked_all_your_base"
301 { { "Transfer-Encoding" , "chunked" }
303 ,.body= "all your base are belong to us"
306 #define TWO_CHUNKS_MULT_ZERO_END 9
307 , {.name= "two chunks ; triple zero ending"
309 ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
310 "Transfer-Encoding: chunked\r\n"
316 ,.should_keep_alive= TRUE
317 ,.message_complete_on_eof= FALSE
323 ,.request_path= "/two_chunks_mult_zero_end"
324 ,.request_url= "/two_chunks_mult_zero_end"
327 { { "Transfer-Encoding", "chunked" }
329 ,.body= "hello world"
332 #define CHUNKED_W_TRAILING_HEADERS 10
333 , {.name= "chunked with trailing headers. blech."
335 ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
336 "Transfer-Encoding: chunked\r\n"
342 "Content-Type: text/plain\r\n"
344 ,.should_keep_alive= TRUE
345 ,.message_complete_on_eof= FALSE
351 ,.request_path= "/chunked_w_trailing_headers"
352 ,.request_url= "/chunked_w_trailing_headers"
355 { { "Transfer-Encoding", "chunked" }
357 , { "Content-Type", "text/plain" }
359 ,.body= "hello world"
362 #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
363 , {.name= "with bullshit after the length"
365 ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
366 "Transfer-Encoding: chunked\r\n"
368 "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
369 "6; blahblah; blah\r\n world\r\n"
372 ,.should_keep_alive= TRUE
373 ,.message_complete_on_eof= FALSE
379 ,.request_path= "/chunked_w_bullshit_after_length"
380 ,.request_url= "/chunked_w_bullshit_after_length"
383 { { "Transfer-Encoding", "chunked" }
385 ,.body= "hello world"
388 #define WITH_QUOTES 12
389 , {.name= "with quotes"
391 ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
392 ,.should_keep_alive= TRUE
393 ,.message_complete_on_eof= FALSE
397 ,.query_string= "foo=\"bar\""
399 ,.request_path= "/with_\"stupid\"_quotes"
400 ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
406 #define APACHEBENCH_GET 13
407 /* The server receiving this request SHOULD NOT wait for EOF
408 * to know that content-length == 0.
409 * How to represent this in a unit test? message_complete_on_eof
410 * Compare with NO_CONTENT_LENGTH_RESPONSE.
412 , {.name = "apachebench get"
414 ,.raw= "GET /test HTTP/1.0\r\n"
415 "Host: 0.0.0.0:5000\r\n"
416 "User-Agent: ApacheBench/2.3\r\n"
417 "Accept: */*\r\n\r\n"
418 ,.should_keep_alive= FALSE
419 ,.message_complete_on_eof= FALSE
425 ,.request_path= "/test"
426 ,.request_url= "/test"
428 ,.headers= { { "Host", "0.0.0.0:5000" }
429 , { "User-Agent", "ApacheBench/2.3" }
430 , { "Accept", "*/*" }
435 #define QUERY_URL_WITH_QUESTION_MARK_GET 14
436 /* Some clients include '?' characters in query strings.
438 , {.name = "query url with question mark"
440 ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
441 ,.should_keep_alive= TRUE
442 ,.message_complete_on_eof= FALSE
446 ,.query_string= "foo=bar?baz"
448 ,.request_path= "/test.cgi"
449 ,.request_url= "/test.cgi?foo=bar?baz"
455 #define PREFIX_NEWLINE_GET 15
456 /* Some clients, especially after a POST in a keep-alive connection,
457 * will send an extra CRLF before the next request
459 , {.name = "newline prefix get"
461 ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
462 ,.should_keep_alive= TRUE
463 ,.message_complete_on_eof= FALSE
469 ,.request_path= "/test"
470 ,.request_url= "/test"
476 #define UPGRADE_REQUEST 16
477 , {.name = "upgrade request"
479 ,.raw= "GET /demo HTTP/1.1\r\n"
480 "Host: example.com\r\n"
481 "Connection: Upgrade\r\n"
482 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
483 "Sec-WebSocket-Protocol: sample\r\n"
484 "Upgrade: WebSocket\r\n"
485 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
486 "Origin: http://example.com\r\n"
489 ,.should_keep_alive= TRUE
490 ,.message_complete_on_eof= FALSE
496 ,.request_path= "/demo"
497 ,.request_url= "/demo"
499 ,.upgrade="Hot diggity dogg"
500 ,.headers= { { "Host", "example.com" }
501 , { "Connection", "Upgrade" }
502 , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
503 , { "Sec-WebSocket-Protocol", "sample" }
504 , { "Upgrade", "WebSocket" }
505 , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
506 , { "Origin", "http://example.com" }
511 #define CONNECT_REQUEST 17
512 , {.name = "connect request"
514 ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
515 "User-agent: Mozilla/1.1N\r\n"
516 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
519 "and yet even more data"
520 ,.should_keep_alive= FALSE
521 ,.message_complete_on_eof= FALSE
524 ,.method= HTTP_CONNECT
528 ,.request_url= "0-home0.netscape.com:443"
530 ,.upgrade="some data\r\nand yet even more data"
531 ,.headers= { { "User-agent", "Mozilla/1.1N" }
532 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
537 #define REPORT_REQ 18
538 , {.name= "report request"
540 ,.raw= "REPORT /test HTTP/1.1\r\n"
542 ,.should_keep_alive= TRUE
543 ,.message_complete_on_eof= FALSE
546 ,.method= HTTP_REPORT
549 ,.request_path= "/test"
550 ,.request_url= "/test"
556 #define NO_HTTP_VERSION 19
557 , {.name= "request with no http version"
561 ,.should_keep_alive= FALSE
562 ,.message_complete_on_eof= FALSE
575 #define MSEARCH_REQ 20
576 , {.name= "m-search request"
578 ,.raw= "M-SEARCH * HTTP/1.1\r\n"
579 "HOST: 239.255.255.250:1900\r\n"
580 "MAN: \"ssdp:discover\"\r\n"
581 "ST: \"ssdp:all\"\r\n"
583 ,.should_keep_alive= TRUE
584 ,.message_complete_on_eof= FALSE
587 ,.method= HTTP_MSEARCH
593 ,.headers= { { "HOST", "239.255.255.250:1900" }
594 , { "MAN", "\"ssdp:discover\"" }
595 , { "ST", "\"ssdp:all\"" }
600 #define LINE_FOLDING_IN_HEADER 21
601 , {.name= "line folding in header value"
603 ,.raw= "GET / HTTP/1.1\r\n"
610 "Line2: \t line2\t\r\n"
618 ,.should_keep_alive= FALSE
619 ,.message_complete_on_eof= FALSE
628 ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
629 , { "Line2", "line2\t" }
630 , { "Line3", "line3" }
632 , { "Connection", "close" },
638 #define QUERY_TERMINATED_HOST 22
639 , {.name= "host terminated by a query string"
641 ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
643 ,.should_keep_alive= TRUE
644 ,.message_complete_on_eof= FALSE
648 ,.query_string= "hail=all"
651 ,.request_url= "http://hypnotoad.org?hail=all"
652 ,.host= "hypnotoad.org"
658 #define QUERY_TERMINATED_HOSTPORT 23
659 , {.name= "host:port terminated by a query string"
661 ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
663 ,.should_keep_alive= TRUE
664 ,.message_complete_on_eof= FALSE
668 ,.query_string= "hail=all"
671 ,.request_url= "http://hypnotoad.org:1234?hail=all"
672 ,.host= "hypnotoad.org"
679 #define SPACE_TERMINATED_HOSTPORT 24
680 , {.name= "host:port terminated by a space"
682 ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
684 ,.should_keep_alive= TRUE
685 ,.message_complete_on_eof= FALSE
692 ,.request_url= "http://hypnotoad.org:1234"
693 ,.host= "hypnotoad.org"
701 , {.name = "PATCH request"
703 ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
704 "Host: www.example.com\r\n"
705 "Content-Type: application/example\r\n"
706 "If-Match: \"e0023aa4e\"\r\n"
707 "Content-Length: 10\r\n"
710 ,.should_keep_alive= TRUE
711 ,.message_complete_on_eof= FALSE
717 ,.request_path= "/file.txt"
718 ,.request_url= "/file.txt"
720 ,.headers= { { "Host", "www.example.com" }
721 , { "Content-Type", "application/example" }
722 , { "If-Match", "\"e0023aa4e\"" }
723 , { "Content-Length", "10" }
728 #define CONNECT_CAPS_REQUEST 26
729 , {.name = "connect caps request"
731 ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
732 "User-agent: Mozilla/1.1N\r\n"
733 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
735 ,.should_keep_alive= FALSE
736 ,.message_complete_on_eof= FALSE
739 ,.method= HTTP_CONNECT
743 ,.request_url= "HOME0.NETSCAPE.COM:443"
746 ,.headers= { { "User-agent", "Mozilla/1.1N" }
747 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
752 #if !HTTP_PARSER_STRICT
753 #define UTF8_PATH_REQ 27
754 , {.name= "utf-8 path request"
756 ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
757 "Host: github.com\r\n"
759 ,.should_keep_alive= TRUE
760 ,.message_complete_on_eof= FALSE
764 ,.query_string= "q=1"
766 ,.request_path= "/δ¶/δt/pope"
767 ,.request_url= "/δ¶/δt/pope?q=1#narf"
769 ,.headers= { {"Host", "github.com" }
774 #define HOSTNAME_UNDERSCORE 28
775 , {.name = "hostname underscore"
777 ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
778 "User-agent: Mozilla/1.1N\r\n"
779 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
781 ,.should_keep_alive= FALSE
782 ,.message_complete_on_eof= FALSE
785 ,.method= HTTP_CONNECT
789 ,.request_url= "home_0.netscape.com:443"
792 ,.headers= { { "User-agent", "Mozilla/1.1N" }
793 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
797 #endif /* !HTTP_PARSER_STRICT */
799 /* see https://github.com/ry/http-parser/issues/47 */
800 #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
801 , {.name = "eat CRLF between requests, no \"Connection: close\" header"
802 ,.raw= "POST / HTTP/1.1\r\n"
803 "Host: www.example.com\r\n"
804 "Content-Type: application/x-www-form-urlencoded\r\n"
805 "Content-Length: 4\r\n"
807 "q=42\r\n" /* note the trailing CRLF */
808 ,.should_keep_alive= TRUE
809 ,.message_complete_on_eof= FALSE
819 ,.headers= { { "Host", "www.example.com" }
820 , { "Content-Type", "application/x-www-form-urlencoded" }
821 , { "Content-Length", "4" }
826 /* see https://github.com/ry/http-parser/issues/47 */
827 #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
828 , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
829 ,.raw= "POST / HTTP/1.1\r\n"
830 "Host: www.example.com\r\n"
831 "Content-Type: application/x-www-form-urlencoded\r\n"
832 "Content-Length: 4\r\n"
833 "Connection: close\r\n"
835 "q=42\r\n" /* note the trailing CRLF */
836 ,.should_keep_alive= FALSE
837 ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
847 ,.headers= { { "Host", "www.example.com" }
848 , { "Content-Type", "application/x-www-form-urlencoded" }
849 , { "Content-Length", "4" }
850 , { "Connection", "close" }
856 , {.name = "PURGE request"
858 ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
859 "Host: www.example.com\r\n"
861 ,.should_keep_alive= TRUE
862 ,.message_complete_on_eof= FALSE
868 ,.request_path= "/file.txt"
869 ,.request_url= "/file.txt"
871 ,.headers= { { "Host", "www.example.com" } }
875 #define SEARCH_REQ 32
876 , {.name = "SEARCH request"
878 ,.raw= "SEARCH / HTTP/1.1\r\n"
879 "Host: www.example.com\r\n"
881 ,.should_keep_alive= TRUE
882 ,.message_complete_on_eof= FALSE
885 ,.method= HTTP_SEARCH
891 ,.headers= { { "Host", "www.example.com" } }
895 #define PROXY_WITH_BASIC_AUTH 33
896 , {.name= "host:port and basic_auth"
898 ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
900 ,.should_keep_alive= TRUE
901 ,.message_complete_on_eof= FALSE
906 ,.request_path= "/toto"
907 ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
908 ,.host= "hypnotoad.org"
909 ,.userinfo= "a%12:b!&*$"
916 #define LINE_FOLDING_IN_HEADER_WITH_LF 34
917 , {.name= "line folding in header value"
919 ,.raw= "GET / HTTP/1.1\n"
926 "Line2: \t line2\t\n"
934 ,.should_keep_alive= FALSE
935 ,.message_complete_on_eof= FALSE
944 ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
945 , { "Line2", "line2\t" }
946 , { "Line3", "line3" }
948 , { "Connection", "close" },
953 #define CONNECTION_MULTI 35
954 , {.name = "multiple connection header values with folding"
956 ,.raw= "GET /demo HTTP/1.1\r\n"
957 "Host: example.com\r\n"
958 "Connection: Something,\r\n"
959 " Upgrade, ,Keep-Alive\r\n"
960 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
961 "Sec-WebSocket-Protocol: sample\r\n"
962 "Upgrade: WebSocket\r\n"
963 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
964 "Origin: http://example.com\r\n"
967 ,.should_keep_alive= TRUE
968 ,.message_complete_on_eof= FALSE
974 ,.request_path= "/demo"
975 ,.request_url= "/demo"
977 ,.upgrade="Hot diggity dogg"
978 ,.headers= { { "Host", "example.com" }
979 , { "Connection", "Something, Upgrade, ,Keep-Alive" }
980 , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
981 , { "Sec-WebSocket-Protocol", "sample" }
982 , { "Upgrade", "WebSocket" }
983 , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
984 , { "Origin", "http://example.com" }
990 , {.name= NULL } /* sentinel */
993 /* * R E S P O N S E S * */
994 const struct message responses[] =
996 { {.name= "google 301"
997 ,.type= HTTP_RESPONSE
998 ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
999 "Location: http://www.google.com/\r\n"
1000 "Content-Type: text/html; charset=UTF-8\r\n"
1001 "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
1002 "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
1003 "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
1004 "Cache-Control: public, max-age=2592000\r\n"
1006 "Content-Length: 219 \r\n"
1008 "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1009 "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1010 "<H1>301 Moved</H1>\n"
1011 "The document has moved\n"
1012 "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1013 "</BODY></HTML>\r\n"
1014 ,.should_keep_alive= TRUE
1015 ,.message_complete_on_eof= FALSE
1019 ,.response_status= "Moved Permanently"
1022 { { "Location", "http://www.google.com/" }
1023 , { "Content-Type", "text/html; charset=UTF-8" }
1024 , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
1025 , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
1026 , { "X-$PrototypeBI-Version", "1.6.0.3" }
1027 , { "Cache-Control", "public, max-age=2592000" }
1028 , { "Server", "gws" }
1029 , { "Content-Length", "219 " }
1031 ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1032 "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1033 "<H1>301 Moved</H1>\n"
1034 "The document has moved\n"
1035 "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1036 "</BODY></HTML>\r\n"
1039 #define NO_CONTENT_LENGTH_RESPONSE 1
1040 /* The client should wait for the server's EOF. That is, when content-length
1041 * is not specified, and "Connection: close", the end of body is specified
1043 * Compare with APACHEBENCH_GET
1045 , {.name= "no content-length response"
1046 ,.type= HTTP_RESPONSE
1047 ,.raw= "HTTP/1.1 200 OK\r\n"
1048 "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
1049 "Server: Apache\r\n"
1050 "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
1051 "Content-Type: text/xml; charset=utf-8\r\n"
1052 "Connection: close\r\n"
1054 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1055 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1056 " <SOAP-ENV:Body>\n"
1057 " <SOAP-ENV:Fault>\n"
1058 " <faultcode>SOAP-ENV:Client</faultcode>\n"
1059 " <faultstring>Client Error</faultstring>\n"
1060 " </SOAP-ENV:Fault>\n"
1061 " </SOAP-ENV:Body>\n"
1062 "</SOAP-ENV:Envelope>"
1063 ,.should_keep_alive= FALSE
1064 ,.message_complete_on_eof= TRUE
1068 ,.response_status= "OK"
1071 { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
1072 , { "Server", "Apache" }
1073 , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
1074 , { "Content-Type", "text/xml; charset=utf-8" }
1075 , { "Connection", "close" }
1077 ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1078 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1079 " <SOAP-ENV:Body>\n"
1080 " <SOAP-ENV:Fault>\n"
1081 " <faultcode>SOAP-ENV:Client</faultcode>\n"
1082 " <faultstring>Client Error</faultstring>\n"
1083 " </SOAP-ENV:Fault>\n"
1084 " </SOAP-ENV:Body>\n"
1085 "</SOAP-ENV:Envelope>"
1088 #define NO_HEADERS_NO_BODY_404 2
1089 , {.name= "404 no headers no body"
1090 ,.type= HTTP_RESPONSE
1091 ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
1092 ,.should_keep_alive= FALSE
1093 ,.message_complete_on_eof= TRUE
1097 ,.response_status= "Not Found"
1104 #define NO_REASON_PHRASE 3
1105 , {.name= "301 no response phrase"
1106 ,.type= HTTP_RESPONSE
1107 ,.raw= "HTTP/1.1 301\r\n\r\n"
1108 ,.should_keep_alive = FALSE
1109 ,.message_complete_on_eof= TRUE
1113 ,.response_status= ""
1119 #define TRAILING_SPACE_ON_CHUNKED_BODY 4
1120 , {.name="200 trailing space on chunked body"
1121 ,.type= HTTP_RESPONSE
1122 ,.raw= "HTTP/1.1 200 OK\r\n"
1123 "Content-Type: text/plain\r\n"
1124 "Transfer-Encoding: chunked\r\n"
1127 "This is the data in the first chunk\r\n"
1130 "and this is the second one\r\n"
1134 ,.should_keep_alive= TRUE
1135 ,.message_complete_on_eof= FALSE
1139 ,.response_status= "OK"
1142 { {"Content-Type", "text/plain" }
1143 , {"Transfer-Encoding", "chunked" }
1147 "This is the data in the first chunk\r\n"
1148 "and this is the second one\r\n"
1152 #define NO_CARRIAGE_RET 5
1153 , {.name="no carriage ret"
1154 ,.type= HTTP_RESPONSE
1155 ,.raw= "HTTP/1.1 200 OK\n"
1156 "Content-Type: text/html; charset=utf-8\n"
1157 "Connection: close\n"
1159 "these headers are from http://news.ycombinator.com/"
1160 ,.should_keep_alive= FALSE
1161 ,.message_complete_on_eof= TRUE
1165 ,.response_status= "OK"
1168 { {"Content-Type", "text/html; charset=utf-8" }
1169 , {"Connection", "close" }
1171 ,.body= "these headers are from http://news.ycombinator.com/"
1174 #define PROXY_CONNECTION 6
1175 , {.name="proxy connection"
1176 ,.type= HTTP_RESPONSE
1177 ,.raw= "HTTP/1.1 200 OK\r\n"
1178 "Content-Type: text/html; charset=UTF-8\r\n"
1179 "Content-Length: 11\r\n"
1180 "Proxy-Connection: close\r\n"
1181 "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
1184 ,.should_keep_alive= FALSE
1185 ,.message_complete_on_eof= FALSE
1189 ,.response_status= "OK"
1192 { {"Content-Type", "text/html; charset=UTF-8" }
1193 , {"Content-Length", "11" }
1194 , {"Proxy-Connection", "close" }
1195 , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
1197 ,.body= "hello world"
1200 #define UNDERSTORE_HEADER_KEY 7
1202 // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
1203 , {.name="underscore header key"
1204 ,.type= HTTP_RESPONSE
1205 ,.raw= "HTTP/1.1 200 OK\r\n"
1206 "Server: DCLK-AdSvr\r\n"
1207 "Content-Type: text/xml\r\n"
1208 "Content-Length: 0\r\n"
1209 "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
1210 ,.should_keep_alive= TRUE
1211 ,.message_complete_on_eof= FALSE
1215 ,.response_status= "OK"
1218 { {"Server", "DCLK-AdSvr" }
1219 , {"Content-Type", "text/xml" }
1220 , {"Content-Length", "0" }
1221 , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1226 #define BONJOUR_MADAME_FR 8
1227 /* The client should not merge two headers fields when the first one doesn't
1230 , {.name= "bonjourmadame.fr"
1231 ,.type= HTTP_RESPONSE
1232 ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
1233 "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
1234 "Server: Apache/2.2.3 (Red Hat)\r\n"
1235 "Cache-Control: public\r\n"
1237 "Location: http://www.bonjourmadame.fr/\r\n"
1238 "Vary: Accept-Encoding\r\n"
1239 "Content-Length: 0\r\n"
1240 "Content-Type: text/html; charset=UTF-8\r\n"
1241 "Connection: keep-alive\r\n"
1243 ,.should_keep_alive= TRUE
1244 ,.message_complete_on_eof= FALSE
1248 ,.response_status= "Moved Permanently"
1251 { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1252 , { "Server", "Apache/2.2.3 (Red Hat)" }
1253 , { "Cache-Control", "public" }
1255 , { "Location", "http://www.bonjourmadame.fr/" }
1256 , { "Vary", "Accept-Encoding" }
1257 , { "Content-Length", "0" }
1258 , { "Content-Type", "text/html; charset=UTF-8" }
1259 , { "Connection", "keep-alive" }
1264 #define RES_FIELD_UNDERSCORE 9
1265 /* Should handle spaces in header fields */
1266 , {.name= "field underscore"
1267 ,.type= HTTP_RESPONSE
1268 ,.raw= "HTTP/1.1 200 OK\r\n"
1269 "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1270 "Server: Apache\r\n"
1271 "Cache-Control: no-cache, must-revalidate\r\n"
1272 "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1273 ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1274 "Vary: Accept-Encoding\r\n"
1275 "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1276 "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1277 "Transfer-Encoding: chunked\r\n"
1278 "Content-Type: text/html\r\n"
1279 "Connection: close\r\n"
1282 ,.should_keep_alive= FALSE
1283 ,.message_complete_on_eof= FALSE
1287 ,.response_status= "OK"
1290 { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1291 , { "Server", "Apache" }
1292 , { "Cache-Control", "no-cache, must-revalidate" }
1293 , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1294 , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1295 , { "Vary", "Accept-Encoding" }
1296 , { "_eep-Alive", "timeout=45" }
1297 , { "_onnection", "Keep-Alive" }
1298 , { "Transfer-Encoding", "chunked" }
1299 , { "Content-Type", "text/html" }
1300 , { "Connection", "close" }
1305 #define NON_ASCII_IN_STATUS_LINE 10
1306 /* Should handle non-ASCII in status line */
1307 , {.name= "non-ASCII in status line"
1308 ,.type= HTTP_RESPONSE
1309 ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1310 "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1311 "Content-Length: 0\r\n"
1312 "Connection: close\r\n"
1314 ,.should_keep_alive= FALSE
1315 ,.message_complete_on_eof= FALSE
1319 ,.response_status= "Oriëntatieprobleem"
1322 { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1323 , { "Content-Length", "0" }
1324 , { "Connection", "close" }
1329 #define HTTP_VERSION_0_9 11
1330 /* Should handle HTTP/0.9 */
1331 , {.name= "http version 0.9"
1332 ,.type= HTTP_RESPONSE
1333 ,.raw= "HTTP/0.9 200 OK\r\n"
1335 ,.should_keep_alive= FALSE
1336 ,.message_complete_on_eof= TRUE
1340 ,.response_status= "OK"
1347 #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1348 /* The client should wait for the server's EOF. That is, when neither
1349 * content-length nor transfer-encoding is specified, the end of body
1350 * is specified by the EOF.
1352 , {.name= "neither content-length nor transfer-encoding response"
1353 ,.type= HTTP_RESPONSE
1354 ,.raw= "HTTP/1.1 200 OK\r\n"
1355 "Content-Type: text/plain\r\n"
1358 ,.should_keep_alive= FALSE
1359 ,.message_complete_on_eof= TRUE
1363 ,.response_status= "OK"
1366 { { "Content-Type", "text/plain" }
1368 ,.body= "hello world"
1371 #define NO_BODY_HTTP10_KA_200 13
1372 , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1373 ,.type= HTTP_RESPONSE
1374 ,.raw= "HTTP/1.0 200 OK\r\n"
1375 "Connection: keep-alive\r\n"
1377 ,.should_keep_alive= FALSE
1378 ,.message_complete_on_eof= TRUE
1382 ,.response_status= "OK"
1385 { { "Connection", "keep-alive" }
1391 #define NO_BODY_HTTP10_KA_204 14
1392 , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1393 ,.type= HTTP_RESPONSE
1394 ,.raw= "HTTP/1.0 204 No content\r\n"
1395 "Connection: keep-alive\r\n"
1397 ,.should_keep_alive= TRUE
1398 ,.message_complete_on_eof= FALSE
1402 ,.response_status= "No content"
1405 { { "Connection", "keep-alive" }
1411 #define NO_BODY_HTTP11_KA_200 15
1412 , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1413 ,.type= HTTP_RESPONSE
1414 ,.raw= "HTTP/1.1 200 OK\r\n"
1416 ,.should_keep_alive= FALSE
1417 ,.message_complete_on_eof= TRUE
1421 ,.response_status= "OK"
1428 #define NO_BODY_HTTP11_KA_204 16
1429 , {.name= "HTTP/1.1 with a 204 status"
1430 ,.type= HTTP_RESPONSE
1431 ,.raw= "HTTP/1.1 204 No content\r\n"
1433 ,.should_keep_alive= TRUE
1434 ,.message_complete_on_eof= FALSE
1438 ,.response_status= "No content"
1445 #define NO_BODY_HTTP11_NOKA_204 17
1446 , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1447 ,.type= HTTP_RESPONSE
1448 ,.raw= "HTTP/1.1 204 No content\r\n"
1449 "Connection: close\r\n"
1451 ,.should_keep_alive= FALSE
1452 ,.message_complete_on_eof= FALSE
1456 ,.response_status= "No content"
1459 { { "Connection", "close" }
1465 #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1466 , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1467 ,.type= HTTP_RESPONSE
1468 ,.raw= "HTTP/1.1 200 OK\r\n"
1469 "Transfer-Encoding: chunked\r\n"
1473 ,.should_keep_alive= TRUE
1474 ,.message_complete_on_eof= FALSE
1478 ,.response_status= "OK"
1481 { { "Transfer-Encoding", "chunked" }
1487 #if !HTTP_PARSER_STRICT
1488 #define SPACE_IN_FIELD_RES 19
1489 /* Should handle spaces in header fields */
1490 , {.name= "field space"
1491 ,.type= HTTP_RESPONSE
1492 ,.raw= "HTTP/1.1 200 OK\r\n"
1493 "Server: Microsoft-IIS/6.0\r\n"
1494 "X-Powered-By: ASP.NET\r\n"
1495 "en-US Content-Type: text/xml\r\n" /* this is the problem */
1496 "Content-Type: text/xml\r\n"
1497 "Content-Length: 16\r\n"
1498 "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
1499 "Connection: keep-alive\r\n"
1501 "<xml>hello</xml>" /* fake body */
1502 ,.should_keep_alive= TRUE
1503 ,.message_complete_on_eof= FALSE
1507 ,.response_status= "OK"
1510 { { "Server", "Microsoft-IIS/6.0" }
1511 , { "X-Powered-By", "ASP.NET" }
1512 , { "en-US Content-Type", "text/xml" }
1513 , { "Content-Type", "text/xml" }
1514 , { "Content-Length", "16" }
1515 , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1516 , { "Connection", "keep-alive" }
1518 ,.body= "<xml>hello</xml>"
1520 #endif /* !HTTP_PARSER_STRICT */
1522 #define AMAZON_COM 20
1523 , {.name= "amazon.com"
1524 ,.type= HTTP_RESPONSE
1525 ,.raw= "HTTP/1.1 301 MovedPermanently\r\n"
1526 "Date: Wed, 15 May 2013 17:06:33 GMT\r\n"
1527 "Server: Server\r\n"
1528 "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n"
1529 "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"
1530 "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n"
1531 "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"
1532 "Vary: Accept-Encoding,User-Agent\r\n"
1533 "Content-Type: text/html; charset=ISO-8859-1\r\n"
1534 "Transfer-Encoding: chunked\r\n"
1540 ,.should_keep_alive= TRUE
1541 ,.message_complete_on_eof= FALSE
1545 ,.response_status= "MovedPermanently"
1547 ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" }
1548 , { "Server", "Server" }
1549 , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" }
1550 , { "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 \"" }
1551 , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" }
1552 , { "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" }
1553 , { "Vary", "Accept-Encoding,User-Agent" }
1554 , { "Content-Type", "text/html; charset=ISO-8859-1" }
1555 , { "Transfer-Encoding", "chunked" }
1560 #define EMPTY_REASON_PHRASE_AFTER_SPACE 20
1561 , {.name= "empty reason phrase after space"
1562 ,.type= HTTP_RESPONSE
1563 ,.raw= "HTTP/1.1 200 \r\n"
1565 ,.should_keep_alive= FALSE
1566 ,.message_complete_on_eof= TRUE
1570 ,.response_status= ""
1576 , {.name= NULL } /* sentinel */
1579 /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1580 * define it ourselves.
1583 strnlen(const char *s, size_t maxlen)
1587 p = memchr(s, '\0', maxlen);
1595 strlncat(char *dst, size_t len, const char *src, size_t n)
1602 slen = strnlen(src, n);
1603 dlen = strnlen(dst, len);
1607 ncpy = slen < rlen ? slen : (rlen - 1);
1608 memcpy(dst + dlen, src, ncpy);
1609 dst[dlen + ncpy] = '\0';
1612 assert(len > slen + dlen);
1617 strlcat(char *dst, const char *src, size_t len)
1619 return strlncat(dst, len, src, (size_t) -1);
1623 strlncpy(char *dst, size_t len, const char *src, size_t n)
1628 slen = strnlen(src, n);
1631 ncpy = slen < len ? slen : (len - 1);
1632 memcpy(dst, src, ncpy);
1641 strlcpy(char *dst, const char *src, size_t len)
1643 return strlncpy(dst, len, src, (size_t) -1);
1647 request_url_cb (http_parser *p, const char *buf, size_t len)
1649 assert(p == parser);
1650 strlncat(messages[num_messages].request_url,
1651 sizeof(messages[num_messages].request_url),
1658 header_field_cb (http_parser *p, const char *buf, size_t len)
1660 assert(p == parser);
1661 struct message *m = &messages[num_messages];
1663 if (m->last_header_element != FIELD)
1666 strlncat(m->headers[m->num_headers-1][0],
1667 sizeof(m->headers[m->num_headers-1][0]),
1671 m->last_header_element = FIELD;
1677 header_value_cb (http_parser *p, const char *buf, size_t len)
1679 assert(p == parser);
1680 struct message *m = &messages[num_messages];
1682 strlncat(m->headers[m->num_headers-1][1],
1683 sizeof(m->headers[m->num_headers-1][1]),
1687 m->last_header_element = VALUE;
1693 check_body_is_final (const http_parser *p)
1695 if (messages[num_messages].body_is_final) {
1696 fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1697 "on last on_body callback call "
1698 "but it doesn't! ***\n\n");
1702 messages[num_messages].body_is_final = http_body_is_final(p);
1706 body_cb (http_parser *p, const char *buf, size_t len)
1708 assert(p == parser);
1709 strlncat(messages[num_messages].body,
1710 sizeof(messages[num_messages].body),
1713 messages[num_messages].body_size += len;
1714 check_body_is_final(p);
1715 // printf("body_cb: '%s'\n", requests[num_messages].body);
1720 count_body_cb (http_parser *p, const char *buf, size_t len)
1722 assert(p == parser);
1724 messages[num_messages].body_size += len;
1725 check_body_is_final(p);
1730 message_begin_cb (http_parser *p)
1732 assert(p == parser);
1733 messages[num_messages].message_begin_cb_called = TRUE;
1738 headers_complete_cb (http_parser *p)
1740 assert(p == parser);
1741 messages[num_messages].method = parser->method;
1742 messages[num_messages].status_code = parser->status_code;
1743 messages[num_messages].http_major = parser->http_major;
1744 messages[num_messages].http_minor = parser->http_minor;
1745 messages[num_messages].headers_complete_cb_called = TRUE;
1746 messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
1751 message_complete_cb (http_parser *p)
1753 assert(p == parser);
1754 if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
1756 fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
1757 "value in both on_message_complete and on_headers_complete "
1758 "but it doesn't! ***\n\n");
1763 if (messages[num_messages].body_size &&
1764 http_body_is_final(p) &&
1765 !messages[num_messages].body_is_final)
1767 fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1768 "on last on_body callback call "
1769 "but it doesn't! ***\n\n");
1774 messages[num_messages].message_complete_cb_called = TRUE;
1776 messages[num_messages].message_complete_on_eof = currently_parsing_eof;
1783 response_status_cb (http_parser *p, const char *buf, size_t len)
1785 assert(p == parser);
1786 strlncat(messages[num_messages].response_status,
1787 sizeof(messages[num_messages].response_status),
1793 /* These dontcall_* callbacks exist so that we can verify that when we're
1794 * paused, no additional callbacks are invoked */
1796 dontcall_message_begin_cb (http_parser *p)
1799 fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1804 dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1806 if (p || buf || len) { } // gcc
1807 fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1812 dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1814 if (p || buf || len) { } // gcc
1815 fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1820 dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1822 if (p || buf || len) { } // gcc
1823 fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1828 dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1830 if (p || buf || len) { } // gcc
1831 fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1836 dontcall_headers_complete_cb (http_parser *p)
1839 fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1845 dontcall_message_complete_cb (http_parser *p)
1848 fprintf(stderr, "\n\n*** on_message_complete() called on paused "
1854 dontcall_response_status_cb (http_parser *p, const char *buf, size_t len)
1856 if (p || buf || len) { } // gcc
1857 fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n");
1861 static http_parser_settings settings_dontcall =
1862 {.on_message_begin = dontcall_message_begin_cb
1863 ,.on_header_field = dontcall_header_field_cb
1864 ,.on_header_value = dontcall_header_value_cb
1865 ,.on_url = dontcall_request_url_cb
1866 ,.on_status = dontcall_response_status_cb
1867 ,.on_body = dontcall_body_cb
1868 ,.on_headers_complete = dontcall_headers_complete_cb
1869 ,.on_message_complete = dontcall_message_complete_cb
1872 /* These pause_* callbacks always pause the parser and just invoke the regular
1873 * callback that tracks content. Before returning, we overwrite the parser
1874 * settings to point to the _dontcall variety so that we can verify that
1875 * the pause actually did, you know, pause. */
1877 pause_message_begin_cb (http_parser *p)
1879 http_parser_pause(p, 1);
1880 *current_pause_parser = settings_dontcall;
1881 return message_begin_cb(p);
1885 pause_header_field_cb (http_parser *p, const char *buf, size_t len)
1887 http_parser_pause(p, 1);
1888 *current_pause_parser = settings_dontcall;
1889 return header_field_cb(p, buf, len);
1893 pause_header_value_cb (http_parser *p, const char *buf, size_t len)
1895 http_parser_pause(p, 1);
1896 *current_pause_parser = settings_dontcall;
1897 return header_value_cb(p, buf, len);
1901 pause_request_url_cb (http_parser *p, const char *buf, size_t len)
1903 http_parser_pause(p, 1);
1904 *current_pause_parser = settings_dontcall;
1905 return request_url_cb(p, buf, len);
1909 pause_body_cb (http_parser *p, const char *buf, size_t len)
1911 http_parser_pause(p, 1);
1912 *current_pause_parser = settings_dontcall;
1913 return body_cb(p, buf, len);
1917 pause_headers_complete_cb (http_parser *p)
1919 http_parser_pause(p, 1);
1920 *current_pause_parser = settings_dontcall;
1921 return headers_complete_cb(p);
1925 pause_message_complete_cb (http_parser *p)
1927 http_parser_pause(p, 1);
1928 *current_pause_parser = settings_dontcall;
1929 return message_complete_cb(p);
1933 pause_response_status_cb (http_parser *p, const char *buf, size_t len)
1935 http_parser_pause(p, 1);
1936 *current_pause_parser = settings_dontcall;
1937 return response_status_cb(p, buf, len);
1940 static http_parser_settings settings_pause =
1941 {.on_message_begin = pause_message_begin_cb
1942 ,.on_header_field = pause_header_field_cb
1943 ,.on_header_value = pause_header_value_cb
1944 ,.on_url = pause_request_url_cb
1945 ,.on_status = pause_response_status_cb
1946 ,.on_body = pause_body_cb
1947 ,.on_headers_complete = pause_headers_complete_cb
1948 ,.on_message_complete = pause_message_complete_cb
1951 static http_parser_settings settings =
1952 {.on_message_begin = message_begin_cb
1953 ,.on_header_field = header_field_cb
1954 ,.on_header_value = header_value_cb
1955 ,.on_url = request_url_cb
1956 ,.on_status = response_status_cb
1958 ,.on_headers_complete = headers_complete_cb
1959 ,.on_message_complete = message_complete_cb
1962 static http_parser_settings settings_count_body =
1963 {.on_message_begin = message_begin_cb
1964 ,.on_header_field = header_field_cb
1965 ,.on_header_value = header_value_cb
1966 ,.on_url = request_url_cb
1967 ,.on_status = response_status_cb
1968 ,.on_body = count_body_cb
1969 ,.on_headers_complete = headers_complete_cb
1970 ,.on_message_complete = message_complete_cb
1973 static http_parser_settings settings_null =
1974 {.on_message_begin = 0
1975 ,.on_header_field = 0
1976 ,.on_header_value = 0
1980 ,.on_headers_complete = 0
1981 ,.on_message_complete = 0
1985 parser_init (enum http_parser_type type)
1989 assert(parser == NULL);
1991 parser = malloc(sizeof(http_parser));
1993 http_parser_init(parser, type);
1995 memset(&messages, 0, sizeof messages);
2007 size_t parse (const char *buf, size_t len)
2010 currently_parsing_eof = (len == 0);
2011 nparsed = http_parser_execute(parser, &settings, buf, len);
2015 size_t parse_count_body (const char *buf, size_t len)
2018 currently_parsing_eof = (len == 0);
2019 nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
2023 size_t parse_pause (const char *buf, size_t len)
2026 http_parser_settings s = settings_pause;
2028 currently_parsing_eof = (len == 0);
2029 current_pause_parser = &s;
2030 nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
2035 check_str_eq (const struct message *m,
2037 const char *expected,
2038 const char *found) {
2039 if ((expected == NULL) != (found == NULL)) {
2040 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2041 printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
2042 printf(" found %s\n", (found == NULL) ? "NULL" : found);
2045 if (expected != NULL && 0 != strcmp(expected, found)) {
2046 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2047 printf("expected '%s'\n", expected);
2048 printf(" found '%s'\n", found);
2055 check_num_eq (const struct message *m,
2059 if (expected != found) {
2060 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2061 printf("expected %d\n", expected);
2062 printf(" found %d\n", found);
2068 #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
2069 if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
2071 #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
2072 if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
2074 #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
2078 if ((u)->field_set & (1 << (fn))) { \
2079 memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
2080 (u)->field_data[(fn)].len); \
2081 ubuf[(u)->field_data[(fn)].len] = '\0'; \
2086 check_str_eq(expected, #prop, expected->prop, ubuf); \
2090 message_eq (int index, const struct message *expected)
2093 struct message *m = &messages[index];
2095 MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
2096 MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
2098 if (expected->type == HTTP_REQUEST) {
2099 MESSAGE_CHECK_NUM_EQ(expected, m, method);
2101 MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
2102 MESSAGE_CHECK_STR_EQ(expected, m, response_status);
2105 MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
2106 MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
2108 assert(m->message_begin_cb_called);
2109 assert(m->headers_complete_cb_called);
2110 assert(m->message_complete_cb_called);
2113 MESSAGE_CHECK_STR_EQ(expected, m, request_url);
2115 /* Check URL components; we can't do this w/ CONNECT since it doesn't
2116 * send us a well-formed URL.
2118 if (*m->request_url && m->method != HTTP_CONNECT) {
2119 struct http_parser_url u;
2121 if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
2122 fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
2127 if (expected->host) {
2128 MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
2131 if (expected->userinfo) {
2132 MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
2135 m->port = (u.field_set & (1 << UF_PORT)) ?
2138 MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
2139 MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
2140 MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
2141 MESSAGE_CHECK_NUM_EQ(expected, m, port);
2144 if (expected->body_size) {
2145 MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
2147 MESSAGE_CHECK_STR_EQ(expected, m, body);
2150 MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
2153 for (i = 0; i < m->num_headers; i++) {
2154 r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
2156 r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
2160 MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
2165 /* Given a sequence of varargs messages, return the number of them that the
2166 * parser should successfully parse, taking into account that upgraded
2167 * messages prevent all subsequent messages from being parsed.
2170 count_parsed_messages(const size_t nmsgs, ...) {
2174 va_start(ap, nmsgs);
2176 for (i = 0; i < nmsgs; i++) {
2177 struct message *m = va_arg(ap, struct message *);
2189 /* Given a sequence of bytes and the number of these that we were able to
2190 * parse, verify that upgrade bodies are correct.
2193 upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2198 va_start(ap, nmsgs);
2200 for (i = 0; i < nmsgs; i++) {
2201 struct message *m = va_arg(ap, struct message *);
2203 off += strlen(m->raw);
2206 off -= strlen(m->upgrade);
2208 /* Check the portion of the response after its specified upgrade */
2209 if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2213 /* Fix up the response so that message_eq() will verify the beginning
2215 *(body + nread + strlen(m->upgrade)) = '\0';
2216 messages[num_messages -1 ].upgrade = body + nread;
2224 printf("\n\n*** Error: expected a message with upgrade ***\n");
2230 print_error (const char *raw, size_t error_location)
2232 fprintf(stderr, "\n*** %s ***\n\n",
2233 http_errno_description(HTTP_PARSER_ERRNO(parser)));
2235 int this_line = 0, char_len = 0;
2236 size_t i, j, len = strlen(raw), error_location_line = 0;
2237 for (i = 0; i < len; i++) {
2238 if (i == error_location) this_line = 1;
2242 fprintf(stderr, "\\r");
2246 fprintf(stderr, "\\n\n");
2248 if (this_line) goto print;
2250 error_location_line = 0;
2255 fputc(raw[i], stderr);
2258 if (!this_line) error_location_line += char_len;
2261 fprintf(stderr, "[eof]\n");
2264 for (j = 0; j < error_location_line; j++) {
2267 fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2271 test_preserve_data (void)
2273 char my_data[] = "application-specific data";
2275 parser.data = my_data;
2276 http_parser_init(&parser, HTTP_REQUEST);
2277 if (parser.data != my_data) {
2278 printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2287 struct http_parser_url u;
2291 const struct url_test url_tests[] =
2292 { {.name="proxy request"
2293 ,.url="http://hostname/"
2296 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2299 {{ 0, 4 } /* UF_SCHEMA */
2300 ,{ 7, 8 } /* UF_HOST */
2301 ,{ 0, 0 } /* UF_PORT */
2302 ,{ 15, 1 } /* UF_PATH */
2303 ,{ 0, 0 } /* UF_QUERY */
2304 ,{ 0, 0 } /* UF_FRAGMENT */
2305 ,{ 0, 0 } /* UF_USERINFO */
2311 , {.name="proxy request with port"
2312 ,.url="http://hostname:444/"
2315 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2318 {{ 0, 4 } /* UF_SCHEMA */
2319 ,{ 7, 8 } /* UF_HOST */
2320 ,{ 16, 3 } /* UF_PORT */
2321 ,{ 19, 1 } /* UF_PATH */
2322 ,{ 0, 0 } /* UF_QUERY */
2323 ,{ 0, 0 } /* UF_FRAGMENT */
2324 ,{ 0, 0 } /* UF_USERINFO */
2330 , {.name="CONNECT request"
2331 ,.url="hostname:443"
2334 {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2337 {{ 0, 0 } /* UF_SCHEMA */
2338 ,{ 0, 8 } /* UF_HOST */
2339 ,{ 9, 3 } /* UF_PORT */
2340 ,{ 0, 0 } /* UF_PATH */
2341 ,{ 0, 0 } /* UF_QUERY */
2342 ,{ 0, 0 } /* UF_FRAGMENT */
2343 ,{ 0, 0 } /* UF_USERINFO */
2349 , {.name="CONNECT request but not connect"
2350 ,.url="hostname:443"
2355 , {.name="proxy ipv6 request"
2356 ,.url="http://[1:2::3:4]/"
2359 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2362 {{ 0, 4 } /* UF_SCHEMA */
2363 ,{ 8, 8 } /* UF_HOST */
2364 ,{ 0, 0 } /* UF_PORT */
2365 ,{ 17, 1 } /* UF_PATH */
2366 ,{ 0, 0 } /* UF_QUERY */
2367 ,{ 0, 0 } /* UF_FRAGMENT */
2368 ,{ 0, 0 } /* UF_USERINFO */
2374 , {.name="proxy ipv6 request with port"
2375 ,.url="http://[1:2::3:4]:67/"
2378 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2381 {{ 0, 4 } /* UF_SCHEMA */
2382 ,{ 8, 8 } /* UF_HOST */
2383 ,{ 18, 2 } /* UF_PORT */
2384 ,{ 20, 1 } /* UF_PATH */
2385 ,{ 0, 0 } /* UF_QUERY */
2386 ,{ 0, 0 } /* UF_FRAGMENT */
2387 ,{ 0, 0 } /* UF_USERINFO */
2393 , {.name="CONNECT ipv6 address"
2394 ,.url="[1:2::3:4]:443"
2397 {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2400 {{ 0, 0 } /* UF_SCHEMA */
2401 ,{ 1, 8 } /* UF_HOST */
2402 ,{ 11, 3 } /* UF_PORT */
2403 ,{ 0, 0 } /* UF_PATH */
2404 ,{ 0, 0 } /* UF_QUERY */
2405 ,{ 0, 0 } /* UF_FRAGMENT */
2406 ,{ 0, 0 } /* UF_USERINFO */
2412 , {.name="ipv4 in ipv6 address"
2413 ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2416 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2419 {{ 0, 4 } /* UF_SCHEMA */
2420 ,{ 8, 37 } /* UF_HOST */
2421 ,{ 0, 0 } /* UF_PORT */
2422 ,{ 46, 1 } /* UF_PATH */
2423 ,{ 0, 0 } /* UF_QUERY */
2424 ,{ 0, 0 } /* UF_FRAGMENT */
2425 ,{ 0, 0 } /* UF_USERINFO */
2431 , {.name="extra ? in query string"
2432 ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
2433 "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
2434 "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2437 {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2440 {{ 0, 4 } /* UF_SCHEMA */
2441 ,{ 7, 10 } /* UF_HOST */
2442 ,{ 0, 0 } /* UF_PORT */
2443 ,{ 17, 12 } /* UF_PATH */
2444 ,{ 30,187 } /* UF_QUERY */
2445 ,{ 0, 0 } /* UF_FRAGMENT */
2446 ,{ 0, 0 } /* UF_USERINFO */
2452 , {.name="space URL encoded"
2453 ,.url="/toto.html?toto=a%20b"
2456 {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2459 {{ 0, 0 } /* UF_SCHEMA */
2460 ,{ 0, 0 } /* UF_HOST */
2461 ,{ 0, 0 } /* UF_PORT */
2462 ,{ 0, 10 } /* UF_PATH */
2463 ,{ 11, 10 } /* UF_QUERY */
2464 ,{ 0, 0 } /* UF_FRAGMENT */
2465 ,{ 0, 0 } /* UF_USERINFO */
2472 , {.name="URL fragment"
2473 ,.url="/toto.html#titi"
2476 {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2479 {{ 0, 0 } /* UF_SCHEMA */
2480 ,{ 0, 0 } /* UF_HOST */
2481 ,{ 0, 0 } /* UF_PORT */
2482 ,{ 0, 10 } /* UF_PATH */
2483 ,{ 0, 0 } /* UF_QUERY */
2484 ,{ 11, 4 } /* UF_FRAGMENT */
2485 ,{ 0, 0 } /* UF_USERINFO */
2491 , {.name="complex URL fragment"
2492 ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
2493 "http://www.example.com/index.html?foo=bar&hello=world#midpage"
2496 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2500 {{ 0, 4 } /* UF_SCHEMA */
2501 ,{ 7, 22 } /* UF_HOST */
2502 ,{ 0, 0 } /* UF_PORT */
2503 ,{ 29, 6 } /* UF_PATH */
2504 ,{ 36, 69 } /* UF_QUERY */
2505 ,{106, 7 } /* UF_FRAGMENT */
2506 ,{ 0, 0 } /* UF_USERINFO */
2512 , {.name="complex URL from node js url parser doc"
2513 ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
2516 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2517 (1<<UF_QUERY) | (1<<UF_FRAGMENT)
2520 {{ 0, 4 } /* UF_SCHEMA */
2521 ,{ 7, 8 } /* UF_HOST */
2522 ,{ 16, 4 } /* UF_PORT */
2523 ,{ 20, 8 } /* UF_PATH */
2524 ,{ 29, 12 } /* UF_QUERY */
2525 ,{ 42, 4 } /* UF_FRAGMENT */
2526 ,{ 0, 0 } /* UF_USERINFO */
2532 , {.name="complex URL with basic auth from node js url parser doc"
2533 ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
2536 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2537 (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
2540 {{ 0, 4 } /* UF_SCHEMA */
2541 ,{ 11, 8 } /* UF_HOST */
2542 ,{ 20, 4 } /* UF_PORT */
2543 ,{ 24, 8 } /* UF_PATH */
2544 ,{ 33, 12 } /* UF_QUERY */
2545 ,{ 46, 4 } /* UF_FRAGMENT */
2546 ,{ 7, 3 } /* UF_USERINFO */
2553 ,.url="http://a:b@@hostname:443/"
2558 , {.name="proxy empty host"
2559 ,.url="http://:443/"
2564 , {.name="proxy empty port"
2565 ,.url="http://hostname:/"
2570 , {.name="CONNECT with basic auth"
2571 ,.url="a:b@hostname:443"
2576 , {.name="CONNECT empty host"
2582 , {.name="CONNECT empty port"
2588 , {.name="CONNECT with extra bits"
2589 ,.url="hostname:443/"
2594 , {.name="space in URL"
2599 , {.name="proxy basic auth with space url encoded"
2600 ,.url="http://a%20:b@host.com/"
2603 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2606 {{ 0, 4 } /* UF_SCHEMA */
2607 ,{ 14, 8 } /* UF_HOST */
2608 ,{ 0, 0 } /* UF_PORT */
2609 ,{ 22, 1 } /* UF_PATH */
2610 ,{ 0, 0 } /* UF_QUERY */
2611 ,{ 0, 0 } /* UF_FRAGMENT */
2612 ,{ 7, 6 } /* UF_USERINFO */
2618 , {.name="carriage return in URL"
2623 , {.name="proxy double : in URL"
2624 ,.url="http://hostname::443/"
2628 , {.name="proxy basic auth with double :"
2629 ,.url="http://a::b@host.com/"
2632 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2635 {{ 0, 4 } /* UF_SCHEMA */
2636 ,{ 12, 8 } /* UF_HOST */
2637 ,{ 0, 0 } /* UF_PORT */
2638 ,{ 20, 1 } /* UF_PATH */
2639 ,{ 0, 0 } /* UF_QUERY */
2640 ,{ 0, 0 } /* UF_FRAGMENT */
2641 ,{ 7, 4 } /* UF_USERINFO */
2647 , {.name="line feed in URL"
2652 , {.name="proxy empty basic auth"
2653 ,.url="http://@hostname/fo"
2655 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
2658 {{ 0, 4 } /* UF_SCHEMA */
2659 ,{ 8, 8 } /* UF_HOST */
2660 ,{ 0, 0 } /* UF_PORT */
2661 ,{ 16, 3 } /* UF_PATH */
2662 ,{ 0, 0 } /* UF_QUERY */
2663 ,{ 0, 0 } /* UF_FRAGMENT */
2664 ,{ 0, 0 } /* UF_USERINFO */
2669 , {.name="proxy line feed in hostname"
2670 ,.url="http://host\name/fo"
2674 , {.name="proxy % in hostname"
2675 ,.url="http://host%name/fo"
2679 , {.name="proxy ; in hostname"
2680 ,.url="http://host;ame/fo"
2684 , {.name="proxy basic auth with unreservedchars"
2685 ,.url="http://a!;-_!=+$@host.com/"
2688 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2691 {{ 0, 4 } /* UF_SCHEMA */
2692 ,{ 17, 8 } /* UF_HOST */
2693 ,{ 0, 0 } /* UF_PORT */
2694 ,{ 25, 1 } /* UF_PATH */
2695 ,{ 0, 0 } /* UF_QUERY */
2696 ,{ 0, 0 } /* UF_FRAGMENT */
2697 ,{ 7, 9 } /* UF_USERINFO */
2703 , {.name="proxy only empty basic auth"
2708 , {.name="proxy only basic auth"
2709 ,.url="http://toto@/fo"
2713 , {.name="proxy emtpy hostname"
2718 , {.name="proxy = in URL"
2719 ,.url="http://host=ame/fo"
2723 #if HTTP_PARSER_STRICT
2725 , {.name="tab in URL"
2730 , {.name="form feed in URL"
2735 #else /* !HTTP_PARSER_STRICT */
2737 , {.name="tab in URL"
2740 {.field_set=(1 << UF_PATH)
2742 {{ 0, 0 } /* UF_SCHEMA */
2743 ,{ 0, 0 } /* UF_HOST */
2744 ,{ 0, 0 } /* UF_PORT */
2745 ,{ 0, 9 } /* UF_PATH */
2746 ,{ 0, 0 } /* UF_QUERY */
2747 ,{ 0, 0 } /* UF_FRAGMENT */
2748 ,{ 0, 0 } /* UF_USERINFO */
2754 , {.name="form feed in URL"
2757 {.field_set=(1 << UF_PATH)
2759 {{ 0, 0 } /* UF_SCHEMA */
2760 ,{ 0, 0 } /* UF_HOST */
2761 ,{ 0, 0 } /* UF_PORT */
2762 ,{ 0, 9 } /* UF_PATH */
2763 ,{ 0, 0 } /* UF_QUERY */
2764 ,{ 0, 0 } /* UF_FRAGMENT */
2765 ,{ 0, 0 } /* UF_USERINFO */
2774 dump_url (const char *url, const struct http_parser_url *u)
2778 printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
2779 for (i = 0; i < UF_MAX; i++) {
2780 if ((u->field_set & (1 << i)) == 0) {
2781 printf("\tfield_data[%u]: unset\n", i);
2785 printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
2787 u->field_data[i].off,
2788 u->field_data[i].len,
2789 u->field_data[i].len,
2790 url + u->field_data[i].off);
2795 test_parse_url (void)
2797 struct http_parser_url u;
2798 const struct url_test *test;
2802 for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
2803 test = &url_tests[i];
2804 memset(&u, 0, sizeof(u));
2806 rv = http_parser_parse_url(test->url,
2811 if (test->rv == 0) {
2813 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2814 "unexpected rv %d ***\n\n", test->url, test->name, rv);
2818 if (memcmp(&u, &test->u, sizeof(u)) != 0) {
2819 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
2820 test->url, test->name);
2822 printf("target http_parser_url:\n");
2823 dump_url(test->url, &test->u);
2824 printf("result http_parser_url:\n");
2825 dump_url(test->url, &u);
2832 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2833 "unexpected rv %d ***\n\n", test->url, test->name, rv);
2841 test_method_str (void)
2843 assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
2844 assert(0 == strcmp("<unknown>", http_method_str(1337)));
2848 test_message (const struct message *message)
2850 size_t raw_len = strlen(message->raw);
2852 for (msg1len = 0; msg1len < raw_len; msg1len++) {
2853 parser_init(message->type);
2856 const char *msg1 = message->raw;
2857 const char *msg2 = msg1 + msg1len;
2858 size_t msg2len = raw_len - msg1len;
2861 read = parse(msg1, msg1len);
2863 if (message->upgrade && parser->upgrade) {
2864 messages[num_messages - 1].upgrade = msg1 + read;
2868 if (read != msg1len) {
2869 print_error(msg1, read);
2875 read = parse(msg2, msg2len);
2877 if (message->upgrade && parser->upgrade) {
2878 messages[num_messages - 1].upgrade = msg2 + read;
2882 if (read != msg2len) {
2883 print_error(msg2, read);
2887 read = parse(NULL, 0);
2890 print_error(message->raw, read);
2896 if (num_messages != 1) {
2897 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2901 if(!message_eq(0, message)) abort();
2908 test_message_count_body (const struct message *message)
2910 parser_init(message->type);
2913 size_t l = strlen(message->raw);
2915 size_t chunk = 4024;
2917 for (i = 0; i < l; i+= chunk) {
2918 toread = MIN(l-i, chunk);
2919 read = parse_count_body(message->raw + i, toread);
2920 if (read != toread) {
2921 print_error(message->raw, read);
2927 read = parse_count_body(NULL, 0);
2929 print_error(message->raw, read);
2933 if (num_messages != 1) {
2934 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2938 if(!message_eq(0, message)) abort();
2944 test_simple (const char *buf, enum http_errno err_expected)
2946 parser_init(HTTP_REQUEST);
2948 enum http_errno err;
2950 parse(buf, strlen(buf));
2951 err = HTTP_PARSER_ERRNO(parser);
2956 /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
2957 * long as the caller isn't expecting success.
2959 #if HTTP_PARSER_STRICT
2960 if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
2962 if (err_expected != err) {
2964 fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
2965 http_errno_name(err_expected), http_errno_name(err), buf);
2971 test_header_overflow_error (int req)
2974 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2977 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
2978 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
2979 assert(parsed == strlen(buf));
2981 buf = "header-key: header-value\r\n";
2982 size_t buflen = strlen(buf);
2985 for (i = 0; i < 10000; i++) {
2986 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
2987 if (parsed != buflen) {
2988 //fprintf(stderr, "error found on iter %d\n", i);
2989 assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
2994 fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
2999 test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
3002 http_parser_init(&parser, HTTP_RESPONSE);
3003 http_parser_execute(&parser, &settings_null, buf, buflen);
3006 assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
3008 assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
3012 test_header_content_length_overflow_error (void)
3015 "HTTP/1.1 200 OK\r\n" \
3016 "Content-Length: " #size "\r\n" \
3018 const char a[] = X(1844674407370955160); /* 2^64 / 10 - 1 */
3019 const char b[] = X(18446744073709551615); /* 2^64-1 */
3020 const char c[] = X(18446744073709551616); /* 2^64 */
3022 test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
3023 test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
3024 test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
3028 test_chunk_content_length_overflow_error (void)
3031 "HTTP/1.1 200 OK\r\n" \
3032 "Transfer-Encoding: chunked\r\n" \
3036 const char a[] = X(FFFFFFFFFFFFFFE); /* 2^64 / 16 - 1 */
3037 const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
3038 const char c[] = X(10000000000000000); /* 2^64 */
3040 test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
3041 test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
3042 test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
3046 test_no_overflow_long_body (int req, size_t length)
3049 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3053 size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
3054 req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
3055 parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
3056 if (parsed != buf1len)
3059 for (i = 0; i < length; i++) {
3061 parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
3066 parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
3067 if (parsed != buf1len) goto err;
3072 "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
3073 req ? "REQUEST" : "RESPONSE",
3074 (unsigned long)length);
3079 test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
3081 int message_count = count_parsed_messages(3, r1, r2, r3);
3083 char total[ strlen(r1->raw)
3090 strcat(total, r1->raw);
3091 strcat(total, r2->raw);
3092 strcat(total, r3->raw);
3094 parser_init(r1->type);
3098 read = parse(total, strlen(total));
3100 if (parser->upgrade) {
3101 upgrade_message_fix(total, read, 3, r1, r2, r3);
3105 if (read != strlen(total)) {
3106 print_error(total, read);
3110 read = parse(NULL, 0);
3113 print_error(total, read);
3119 if (message_count != num_messages) {
3120 fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
3124 if (!message_eq(0, r1)) abort();
3125 if (message_count > 1 && !message_eq(1, r2)) abort();
3126 if (message_count > 2 && !message_eq(2, r3)) abort();
3131 /* SCAN through every possible breaking to make sure the
3132 * parser can handle getting the content in any chunks that
3133 * might come from the socket
3136 test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
3138 char total[80*1024] = "\0";
3139 char buf1[80*1024] = "\0";
3140 char buf2[80*1024] = "\0";
3141 char buf3[80*1024] = "\0";
3143 strcat(total, r1->raw);
3144 strcat(total, r2->raw);
3145 strcat(total, r3->raw);
3149 int total_len = strlen(total);
3151 int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
3154 size_t buf1_len, buf2_len, buf3_len;
3155 int message_count = count_parsed_messages(3, r1, r2, r3);
3158 for (type_both = 0; type_both < 2; type_both ++ ) {
3159 for (j = 2; j < total_len; j ++ ) {
3160 for (i = 1; i < j; i ++ ) {
3162 if (ops % 1000 == 0) {
3163 printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
3168 parser_init(type_both ? HTTP_BOTH : r1->type);
3171 strlncpy(buf1, sizeof(buf1), total, buf1_len);
3175 strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
3178 buf3_len = total_len - j;
3179 strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
3182 read = parse(buf1, buf1_len);
3184 if (parser->upgrade) goto test;
3186 if (read != buf1_len) {
3187 print_error(buf1, read);
3191 read += parse(buf2, buf2_len);
3193 if (parser->upgrade) goto test;
3195 if (read != buf1_len + buf2_len) {
3196 print_error(buf2, read);
3200 read += parse(buf3, buf3_len);
3202 if (parser->upgrade) goto test;
3204 if (read != buf1_len + buf2_len + buf3_len) {
3205 print_error(buf3, read);
3212 if (parser->upgrade) {
3213 upgrade_message_fix(total, read, 3, r1, r2, r3);
3216 if (message_count != num_messages) {
3217 fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3218 message_count, num_messages);
3222 if (!message_eq(0, r1)) {
3223 fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
3227 if (message_count > 1 && !message_eq(1, r2)) {
3228 fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
3232 if (message_count > 2 && !message_eq(2, r3)) {
3233 fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
3241 puts("\b\b\b\b100%");
3245 fprintf(stderr, "i=%d j=%d\n", i, j);
3246 fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
3247 fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
3248 fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
3252 // user required to free the result
3253 // string terminated by \0
3255 create_large_chunked_message (int body_size_in_kb, const char* headers)
3259 size_t headers_len = strlen(headers);
3260 size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
3261 char * buf = malloc(bufsize);
3263 memcpy(buf, headers, headers_len);
3264 wrote += headers_len;
3266 for (i = 0; i < body_size_in_kb; i++) {
3267 // write 1kb chunk into the body.
3268 memcpy(buf + wrote, "400\r\n", 5);
3270 memset(buf + wrote, 'C', 1024);
3272 strcpy(buf + wrote, "\r\n");
3276 memcpy(buf + wrote, "0\r\n\r\n", 6);
3278 assert(wrote == bufsize);
3283 /* Verify that we can pause parsing at any of the bytes in the
3284 * message and still get the result that we're expecting. */
3286 test_message_pause (const struct message *msg)
3288 char *buf = (char*) msg->raw;
3289 size_t buflen = strlen(msg->raw);
3292 parser_init(msg->type);
3295 nread = parse_pause(buf, buflen);
3297 // We can only set the upgrade buffer once we've gotten our message
3298 // completion callback.
3299 if (messages[0].message_complete_cb_called &&
3302 messages[0].upgrade = buf + nread;
3306 if (nread < buflen) {
3308 // Not much do to if we failed a strict-mode check
3309 if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
3314 assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
3319 http_parser_pause(parser, 0);
3320 } while (buflen > 0);
3322 nread = parse_pause(NULL, 0);
3323 assert (nread == 0);
3326 if (num_messages != 1) {
3327 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
3331 if(!message_eq(0, msg)) abort();
3343 unsigned long version;
3348 version = http_parser_version();
3349 major = (version >> 16) & 255;
3350 minor = (version >> 8) & 255;
3351 patch = version & 255;
3352 printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version);
3354 printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
3356 for (request_count = 0; requests[request_count].name; request_count++);
3357 for (response_count = 0; responses[response_count].name; response_count++);
3360 test_preserve_data();
3364 //// OVERFLOW CONDITIONS
3366 test_header_overflow_error(HTTP_REQUEST);
3367 test_no_overflow_long_body(HTTP_REQUEST, 1000);
3368 test_no_overflow_long_body(HTTP_REQUEST, 100000);
3370 test_header_overflow_error(HTTP_RESPONSE);
3371 test_no_overflow_long_body(HTTP_RESPONSE, 1000);
3372 test_no_overflow_long_body(HTTP_RESPONSE, 100000);
3374 test_header_content_length_overflow_error();
3375 test_chunk_content_length_overflow_error();
3379 for (i = 0; i < response_count; i++) {
3380 test_message(&responses[i]);
3383 for (i = 0; i < response_count; i++) {
3384 test_message_pause(&responses[i]);
3387 for (i = 0; i < response_count; i++) {
3388 if (!responses[i].should_keep_alive) continue;
3389 for (j = 0; j < response_count; j++) {
3390 if (!responses[j].should_keep_alive) continue;
3391 for (k = 0; k < response_count; k++) {
3392 test_multiple3(&responses[i], &responses[j], &responses[k]);
3397 test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
3398 test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
3400 // test very large chunked response
3402 char * msg = create_large_chunked_message(31337,
3403 "HTTP/1.0 200 OK\r\n"
3404 "Transfer-Encoding: chunked\r\n"
3405 "Content-Type: text/plain\r\n"
3407 struct message large_chunked =
3408 {.name= "large chunked"
3409 ,.type= HTTP_RESPONSE
3411 ,.should_keep_alive= FALSE
3412 ,.message_complete_on_eof= FALSE
3416 ,.response_status= "OK"
3419 { { "Transfer-Encoding", "chunked" }
3420 , { "Content-Type", "text/plain" }
3422 ,.body_size= 31337*1024
3424 test_message_count_body(&large_chunked);
3430 printf("response scan 1/2 ");
3431 test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
3432 , &responses[NO_BODY_HTTP10_KA_204]
3433 , &responses[NO_REASON_PHRASE]
3436 printf("response scan 2/2 ");
3437 test_scan( &responses[BONJOUR_MADAME_FR]
3438 , &responses[UNDERSTORE_HEADER_KEY]
3439 , &responses[NO_CARRIAGE_RET]
3442 puts("responses okay");
3447 test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
3449 // Well-formed but incomplete
3450 test_simple("GET / HTTP/1.1\r\n"
3451 "Content-Type: text/plain\r\n"
3452 "Content-Length: 6\r\n"
3457 static const char *all_methods[] = {
3463 //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
3483 const char **this_method;
3484 for (this_method = all_methods; *this_method; this_method++) {
3486 sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3487 test_simple(buf, HPE_OK);
3490 static const char *bad_methods[] = {
3504 for (this_method = bad_methods; *this_method; this_method++) {
3506 sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3507 test_simple(buf, HPE_INVALID_METHOD);
3510 // illegal header field name line folding
3511 test_simple("GET / HTTP/1.1\r\n"
3515 HPE_INVALID_HEADER_TOKEN);
3517 const char *dumbfuck2 =
3518 "GET / HTTP/1.1\r\n"
3519 "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
3520 "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
3521 "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
3522 "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
3523 "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
3524 "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
3525 "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
3526 "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
3527 "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
3528 "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
3529 "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
3530 "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
3531 "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
3532 "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
3533 "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
3534 "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
3535 "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
3536 "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
3537 "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
3538 "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
3539 "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
3540 "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
3541 "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
3542 "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
3543 "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
3544 "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
3545 "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
3546 "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
3547 "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
3548 "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
3550 "\t-----END CERTIFICATE-----\r\n"
3552 test_simple(dumbfuck2, HPE_OK);
3555 // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
3558 // no content-length
3559 // error if there is a body without content length
3560 const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
3564 test_simple(bad_get_no_headers_no_body, 0);
3566 /* TODO sending junk and large headers gets rejected */
3569 /* check to make sure our predefined requests are okay */
3570 for (i = 0; requests[i].name; i++) {
3571 test_message(&requests[i]);
3574 for (i = 0; i < request_count; i++) {
3575 test_message_pause(&requests[i]);
3578 for (i = 0; i < request_count; i++) {
3579 if (!requests[i].should_keep_alive) continue;
3580 for (j = 0; j < request_count; j++) {
3581 if (!requests[j].should_keep_alive) continue;
3582 for (k = 0; k < request_count; k++) {
3583 test_multiple3(&requests[i], &requests[j], &requests[k]);
3588 printf("request scan 1/4 ");
3589 test_scan( &requests[GET_NO_HEADERS_NO_BODY]
3590 , &requests[GET_ONE_HEADER_NO_BODY]
3591 , &requests[GET_NO_HEADERS_NO_BODY]
3594 printf("request scan 2/4 ");
3595 test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
3596 , &requests[POST_IDENTITY_BODY_WORLD]
3597 , &requests[GET_FUNKY_CONTENT_LENGTH]
3600 printf("request scan 3/4 ");
3601 test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
3602 , &requests[CHUNKED_W_TRAILING_HEADERS]
3603 , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
3606 printf("request scan 4/4 ");
3607 test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
3608 , &requests[PREFIX_NEWLINE_GET ]
3609 , &requests[CONNECT_REQUEST]
3612 puts("requests okay");