tizen 2.4 release
[external/nghttp2.git] / third-party / http-parser / test.c
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
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:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
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
19  * IN THE SOFTWARE.
20  */
21 #include "http_parser.h"
22 #include <stdlib.h>
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h> /* rand */
26 #include <string.h>
27 #include <stdarg.h>
28
29 #if defined(__APPLE__)
30 # undef strlcat
31 # undef strlncpy
32 # undef strlcpy
33 #endif  /* defined(__APPLE__) */
34
35 #undef TRUE
36 #define TRUE 1
37 #undef FALSE
38 #define FALSE 0
39
40 #define MAX_HEADERS 13
41 #define MAX_ELEMENT_SIZE 2048
42
43 #define MIN(a,b) ((a) < (b) ? (a) : (b))
44
45 static http_parser *parser;
46
47 struct message {
48   const char *name; // for debugging purposes
49   const char *raw;
50   enum http_parser_type type;
51   enum http_method method;
52   int status_code;
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];
59   size_t body_size;
60   const char *host;
61   const char *userinfo;
62   uint16_t port;
63   int num_headers;
64   enum { NONE=0, FIELD, VALUE } last_header_element;
65   char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
66   int should_keep_alive;
67
68   const char *upgrade; // upgraded body
69
70   unsigned short http_major;
71   unsigned short http_minor;
72
73   int message_begin_cb_called;
74   int headers_complete_cb_called;
75   int message_complete_cb_called;
76   int message_complete_on_eof;
77   int body_is_final;
78 };
79
80 static int currently_parsing_eof;
81
82 static struct message messages[5];
83 static int num_messages;
84 static http_parser_settings *current_pause_parser;
85
86 /* * R E Q U E S T S * */
87 const struct message requests[] =
88 #define CURL_GET 0
89 { {.name= "curl get"
90   ,.type= HTTP_REQUEST
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"
94          "Accept: */*\r\n"
95          "\r\n"
96   ,.should_keep_alive= TRUE
97   ,.message_complete_on_eof= FALSE
98   ,.http_major= 1
99   ,.http_minor= 1
100   ,.method= HTTP_GET
101   ,.query_string= ""
102   ,.fragment= ""
103   ,.request_path= "/test"
104   ,.request_url= "/test"
105   ,.num_headers= 3
106   ,.headers=
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", "*/*" }
110     }
111   ,.body= ""
112   }
113
114 #define FIREFOX_GET 1
115 , {.name= "firefox get"
116   ,.type= HTTP_REQUEST
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"
126          "\r\n"
127   ,.should_keep_alive= TRUE
128   ,.message_complete_on_eof= FALSE
129   ,.http_major= 1
130   ,.http_minor= 1
131   ,.method= HTTP_GET
132   ,.query_string= ""
133   ,.fragment= ""
134   ,.request_path= "/favicon.ico"
135   ,.request_url= "/favicon.ico"
136   ,.num_headers= 8
137   ,.headers=
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" }
146     }
147   ,.body= ""
148   }
149
150 #define DUMBFUCK 2
151 , {.name= "dumbfuck"
152   ,.type= HTTP_REQUEST
153   ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
154          "aaaaaaaaaaaaa:++++++++++\r\n"
155          "\r\n"
156   ,.should_keep_alive= TRUE
157   ,.message_complete_on_eof= FALSE
158   ,.http_major= 1
159   ,.http_minor= 1
160   ,.method= HTTP_GET
161   ,.query_string= ""
162   ,.fragment= ""
163   ,.request_path= "/dumbfuck"
164   ,.request_url= "/dumbfuck"
165   ,.num_headers= 1
166   ,.headers=
167     { { "aaaaaaaaaaaaa",  "++++++++++" }
168     }
169   ,.body= ""
170   }
171
172 #define FRAGMENT_IN_URI 3
173 , {.name= "fragment in url"
174   ,.type= HTTP_REQUEST
175   ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
176          "\r\n"
177   ,.should_keep_alive= TRUE
178   ,.message_complete_on_eof= FALSE
179   ,.http_major= 1
180   ,.http_minor= 1
181   ,.method= HTTP_GET
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"
187   ,.num_headers= 0
188   ,.body= ""
189   }
190
191 #define GET_NO_HEADERS_NO_BODY 4
192 , {.name= "get no headers no body"
193   ,.type= HTTP_REQUEST
194   ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
195          "\r\n"
196   ,.should_keep_alive= TRUE
197   ,.message_complete_on_eof= FALSE /* would need Connection: close */
198   ,.http_major= 1
199   ,.http_minor= 1
200   ,.method= HTTP_GET
201   ,.query_string= ""
202   ,.fragment= ""
203   ,.request_path= "/get_no_headers_no_body/world"
204   ,.request_url= "/get_no_headers_no_body/world"
205   ,.num_headers= 0
206   ,.body= ""
207   }
208
209 #define GET_ONE_HEADER_NO_BODY 5
210 , {.name= "get one header no body"
211   ,.type= HTTP_REQUEST
212   ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
213          "Accept: */*\r\n"
214          "\r\n"
215   ,.should_keep_alive= TRUE
216   ,.message_complete_on_eof= FALSE /* would need Connection: close */
217   ,.http_major= 1
218   ,.http_minor= 1
219   ,.method= HTTP_GET
220   ,.query_string= ""
221   ,.fragment= ""
222   ,.request_path= "/get_one_header_no_body"
223   ,.request_url= "/get_one_header_no_body"
224   ,.num_headers= 1
225   ,.headers=
226     { { "Accept" , "*/*" }
227     }
228   ,.body= ""
229   }
230
231 #define GET_FUNKY_CONTENT_LENGTH 6
232 , {.name= "get funky content length body hello"
233   ,.type= HTTP_REQUEST
234   ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
235          "conTENT-Length: 5\r\n"
236          "\r\n"
237          "HELLO"
238   ,.should_keep_alive= FALSE
239   ,.message_complete_on_eof= FALSE
240   ,.http_major= 1
241   ,.http_minor= 0
242   ,.method= HTTP_GET
243   ,.query_string= ""
244   ,.fragment= ""
245   ,.request_path= "/get_funky_content_length_body_hello"
246   ,.request_url= "/get_funky_content_length_body_hello"
247   ,.num_headers= 1
248   ,.headers=
249     { { "conTENT-Length" , "5" }
250     }
251   ,.body= "HELLO"
252   }
253
254 #define POST_IDENTITY_BODY_WORLD 7
255 , {.name= "post identity body world"
256   ,.type= HTTP_REQUEST
257   ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
258          "Accept: */*\r\n"
259          "Transfer-Encoding: identity\r\n"
260          "Content-Length: 5\r\n"
261          "\r\n"
262          "World"
263   ,.should_keep_alive= TRUE
264   ,.message_complete_on_eof= FALSE
265   ,.http_major= 1
266   ,.http_minor= 1
267   ,.method= HTTP_POST
268   ,.query_string= "q=search"
269   ,.fragment= "hey"
270   ,.request_path= "/post_identity_body_world"
271   ,.request_url= "/post_identity_body_world?q=search#hey"
272   ,.num_headers= 3
273   ,.headers=
274     { { "Accept", "*/*" }
275     , { "Transfer-Encoding", "identity" }
276     , { "Content-Length", "5" }
277     }
278   ,.body= "World"
279   }
280
281 #define POST_CHUNKED_ALL_YOUR_BASE 8
282 , {.name= "post - chunked body: all your base are belong to us"
283   ,.type= HTTP_REQUEST
284   ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
285          "Transfer-Encoding: chunked\r\n"
286          "\r\n"
287          "1e\r\nall your base are belong to us\r\n"
288          "0\r\n"
289          "\r\n"
290   ,.should_keep_alive= TRUE
291   ,.message_complete_on_eof= FALSE
292   ,.http_major= 1
293   ,.http_minor= 1
294   ,.method= HTTP_POST
295   ,.query_string= ""
296   ,.fragment= ""
297   ,.request_path= "/post_chunked_all_your_base"
298   ,.request_url= "/post_chunked_all_your_base"
299   ,.num_headers= 1
300   ,.headers=
301     { { "Transfer-Encoding" , "chunked" }
302     }
303   ,.body= "all your base are belong to us"
304   }
305
306 #define TWO_CHUNKS_MULT_ZERO_END 9
307 , {.name= "two chunks ; triple zero ending"
308   ,.type= HTTP_REQUEST
309   ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
310          "Transfer-Encoding: chunked\r\n"
311          "\r\n"
312          "5\r\nhello\r\n"
313          "6\r\n world\r\n"
314          "000\r\n"
315          "\r\n"
316   ,.should_keep_alive= TRUE
317   ,.message_complete_on_eof= FALSE
318   ,.http_major= 1
319   ,.http_minor= 1
320   ,.method= HTTP_POST
321   ,.query_string= ""
322   ,.fragment= ""
323   ,.request_path= "/two_chunks_mult_zero_end"
324   ,.request_url= "/two_chunks_mult_zero_end"
325   ,.num_headers= 1
326   ,.headers=
327     { { "Transfer-Encoding", "chunked" }
328     }
329   ,.body= "hello world"
330   }
331
332 #define CHUNKED_W_TRAILING_HEADERS 10
333 , {.name= "chunked with trailing headers. blech."
334   ,.type= HTTP_REQUEST
335   ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
336          "Transfer-Encoding: chunked\r\n"
337          "\r\n"
338          "5\r\nhello\r\n"
339          "6\r\n world\r\n"
340          "0\r\n"
341          "Vary: *\r\n"
342          "Content-Type: text/plain\r\n"
343          "\r\n"
344   ,.should_keep_alive= TRUE
345   ,.message_complete_on_eof= FALSE
346   ,.http_major= 1
347   ,.http_minor= 1
348   ,.method= HTTP_POST
349   ,.query_string= ""
350   ,.fragment= ""
351   ,.request_path= "/chunked_w_trailing_headers"
352   ,.request_url= "/chunked_w_trailing_headers"
353   ,.num_headers= 3
354   ,.headers=
355     { { "Transfer-Encoding",  "chunked" }
356     , { "Vary", "*" }
357     , { "Content-Type", "text/plain" }
358     }
359   ,.body= "hello world"
360   }
361
362 #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
363 , {.name= "with bullshit after the length"
364   ,.type= HTTP_REQUEST
365   ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
366          "Transfer-Encoding: chunked\r\n"
367          "\r\n"
368          "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
369          "6; blahblah; blah\r\n world\r\n"
370          "0\r\n"
371          "\r\n"
372   ,.should_keep_alive= TRUE
373   ,.message_complete_on_eof= FALSE
374   ,.http_major= 1
375   ,.http_minor= 1
376   ,.method= HTTP_POST
377   ,.query_string= ""
378   ,.fragment= ""
379   ,.request_path= "/chunked_w_bullshit_after_length"
380   ,.request_url= "/chunked_w_bullshit_after_length"
381   ,.num_headers= 1
382   ,.headers=
383     { { "Transfer-Encoding", "chunked" }
384     }
385   ,.body= "hello world"
386   }
387
388 #define WITH_QUOTES 12
389 , {.name= "with quotes"
390   ,.type= HTTP_REQUEST
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
394   ,.http_major= 1
395   ,.http_minor= 1
396   ,.method= HTTP_GET
397   ,.query_string= "foo=\"bar\""
398   ,.fragment= ""
399   ,.request_path= "/with_\"stupid\"_quotes"
400   ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
401   ,.num_headers= 0
402   ,.headers= { }
403   ,.body= ""
404   }
405
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.
411  */
412 , {.name = "apachebench get"
413   ,.type= HTTP_REQUEST
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
420   ,.http_major= 1
421   ,.http_minor= 0
422   ,.method= HTTP_GET
423   ,.query_string= ""
424   ,.fragment= ""
425   ,.request_path= "/test"
426   ,.request_url= "/test"
427   ,.num_headers= 3
428   ,.headers= { { "Host", "0.0.0.0:5000" }
429              , { "User-Agent", "ApacheBench/2.3" }
430              , { "Accept", "*/*" }
431              }
432   ,.body= ""
433   }
434
435 #define QUERY_URL_WITH_QUESTION_MARK_GET 14
436 /* Some clients include '?' characters in query strings.
437  */
438 , {.name = "query url with question mark"
439   ,.type= HTTP_REQUEST
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
443   ,.http_major= 1
444   ,.http_minor= 1
445   ,.method= HTTP_GET
446   ,.query_string= "foo=bar?baz"
447   ,.fragment= ""
448   ,.request_path= "/test.cgi"
449   ,.request_url= "/test.cgi?foo=bar?baz"
450   ,.num_headers= 0
451   ,.headers= {}
452   ,.body= ""
453   }
454
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
458  */
459 , {.name = "newline prefix get"
460   ,.type= HTTP_REQUEST
461   ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
462   ,.should_keep_alive= TRUE
463   ,.message_complete_on_eof= FALSE
464   ,.http_major= 1
465   ,.http_minor= 1
466   ,.method= HTTP_GET
467   ,.query_string= ""
468   ,.fragment= ""
469   ,.request_path= "/test"
470   ,.request_url= "/test"
471   ,.num_headers= 0
472   ,.headers= { }
473   ,.body= ""
474   }
475
476 #define UPGRADE_REQUEST 16
477 , {.name = "upgrade request"
478   ,.type= HTTP_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"
487          "\r\n"
488          "Hot diggity dogg"
489   ,.should_keep_alive= TRUE
490   ,.message_complete_on_eof= FALSE
491   ,.http_major= 1
492   ,.http_minor= 1
493   ,.method= HTTP_GET
494   ,.query_string= ""
495   ,.fragment= ""
496   ,.request_path= "/demo"
497   ,.request_url= "/demo"
498   ,.num_headers= 7
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" }
507              }
508   ,.body= ""
509   }
510
511 #define CONNECT_REQUEST 17
512 , {.name = "connect request"
513   ,.type= HTTP_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"
517          "\r\n"
518          "some data\r\n"
519          "and yet even more data"
520   ,.should_keep_alive= FALSE
521   ,.message_complete_on_eof= FALSE
522   ,.http_major= 1
523   ,.http_minor= 0
524   ,.method= HTTP_CONNECT
525   ,.query_string= ""
526   ,.fragment= ""
527   ,.request_path= ""
528   ,.request_url= "0-home0.netscape.com:443"
529   ,.num_headers= 2
530   ,.upgrade="some data\r\nand yet even more data"
531   ,.headers= { { "User-agent", "Mozilla/1.1N" }
532              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
533              }
534   ,.body= ""
535   }
536
537 #define REPORT_REQ 18
538 , {.name= "report request"
539   ,.type= HTTP_REQUEST
540   ,.raw= "REPORT /test HTTP/1.1\r\n"
541          "\r\n"
542   ,.should_keep_alive= TRUE
543   ,.message_complete_on_eof= FALSE
544   ,.http_major= 1
545   ,.http_minor= 1
546   ,.method= HTTP_REPORT
547   ,.query_string= ""
548   ,.fragment= ""
549   ,.request_path= "/test"
550   ,.request_url= "/test"
551   ,.num_headers= 0
552   ,.headers= {}
553   ,.body= ""
554   }
555
556 #define NO_HTTP_VERSION 19
557 , {.name= "request with no http version"
558   ,.type= HTTP_REQUEST
559   ,.raw= "GET /\r\n"
560          "\r\n"
561   ,.should_keep_alive= FALSE
562   ,.message_complete_on_eof= FALSE
563   ,.http_major= 0
564   ,.http_minor= 9
565   ,.method= HTTP_GET
566   ,.query_string= ""
567   ,.fragment= ""
568   ,.request_path= "/"
569   ,.request_url= "/"
570   ,.num_headers= 0
571   ,.headers= {}
572   ,.body= ""
573   }
574
575 #define MSEARCH_REQ 20
576 , {.name= "m-search request"
577   ,.type= HTTP_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"
582          "\r\n"
583   ,.should_keep_alive= TRUE
584   ,.message_complete_on_eof= FALSE
585   ,.http_major= 1
586   ,.http_minor= 1
587   ,.method= HTTP_MSEARCH
588   ,.query_string= ""
589   ,.fragment= ""
590   ,.request_path= "*"
591   ,.request_url= "*"
592   ,.num_headers= 3
593   ,.headers= { { "HOST", "239.255.255.250:1900" }
594              , { "MAN", "\"ssdp:discover\"" }
595              , { "ST", "\"ssdp:all\"" }
596              }
597   ,.body= ""
598   }
599
600 #define LINE_FOLDING_IN_HEADER 21
601 , {.name= "line folding in header value"
602   ,.type= HTTP_REQUEST
603   ,.raw= "GET / HTTP/1.1\r\n"
604          "Line1:   abc\r\n"
605          "\tdef\r\n"
606          " ghi\r\n"
607          "\t\tjkl\r\n"
608          "  mno \r\n"
609          "\t \tqrs\r\n"
610          "Line2: \t line2\t\r\n"
611          "Line3:\r\n"
612          " line3\r\n"
613          "Line4: \r\n"
614          " \r\n"
615          "Connection:\r\n"
616          " close\r\n"
617          "\r\n"
618   ,.should_keep_alive= FALSE
619   ,.message_complete_on_eof= FALSE
620   ,.http_major= 1
621   ,.http_minor= 1
622   ,.method= HTTP_GET
623   ,.query_string= ""
624   ,.fragment= ""
625   ,.request_path= "/"
626   ,.request_url= "/"
627   ,.num_headers= 5
628   ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl  mno \t \tqrs" }
629              , { "Line2", "line2\t" }
630              , { "Line3", "line3" }
631              , { "Line4", "" }
632              , { "Connection", "close" },
633              }
634   ,.body= ""
635   }
636
637
638 #define QUERY_TERMINATED_HOST 22
639 , {.name= "host terminated by a query string"
640   ,.type= HTTP_REQUEST
641   ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
642          "\r\n"
643   ,.should_keep_alive= TRUE
644   ,.message_complete_on_eof= FALSE
645   ,.http_major= 1
646   ,.http_minor= 1
647   ,.method= HTTP_GET
648   ,.query_string= "hail=all"
649   ,.fragment= ""
650   ,.request_path= ""
651   ,.request_url= "http://hypnotoad.org?hail=all"
652   ,.host= "hypnotoad.org"
653   ,.num_headers= 0
654   ,.headers= { }
655   ,.body= ""
656   }
657
658 #define QUERY_TERMINATED_HOSTPORT 23
659 , {.name= "host:port terminated by a query string"
660   ,.type= HTTP_REQUEST
661   ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
662          "\r\n"
663   ,.should_keep_alive= TRUE
664   ,.message_complete_on_eof= FALSE
665   ,.http_major= 1
666   ,.http_minor= 1
667   ,.method= HTTP_GET
668   ,.query_string= "hail=all"
669   ,.fragment= ""
670   ,.request_path= ""
671   ,.request_url= "http://hypnotoad.org:1234?hail=all"
672   ,.host= "hypnotoad.org"
673   ,.port= 1234
674   ,.num_headers= 0
675   ,.headers= { }
676   ,.body= ""
677   }
678
679 #define SPACE_TERMINATED_HOSTPORT 24
680 , {.name= "host:port terminated by a space"
681   ,.type= HTTP_REQUEST
682   ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
683          "\r\n"
684   ,.should_keep_alive= TRUE
685   ,.message_complete_on_eof= FALSE
686   ,.http_major= 1
687   ,.http_minor= 1
688   ,.method= HTTP_GET
689   ,.query_string= ""
690   ,.fragment= ""
691   ,.request_path= ""
692   ,.request_url= "http://hypnotoad.org:1234"
693   ,.host= "hypnotoad.org"
694   ,.port= 1234
695   ,.num_headers= 0
696   ,.headers= { }
697   ,.body= ""
698   }
699
700 #define PATCH_REQ 25
701 , {.name = "PATCH request"
702   ,.type= HTTP_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"
708          "\r\n"
709          "cccccccccc"
710   ,.should_keep_alive= TRUE
711   ,.message_complete_on_eof= FALSE
712   ,.http_major= 1
713   ,.http_minor= 1
714   ,.method= HTTP_PATCH
715   ,.query_string= ""
716   ,.fragment= ""
717   ,.request_path= "/file.txt"
718   ,.request_url= "/file.txt"
719   ,.num_headers= 4
720   ,.headers= { { "Host", "www.example.com" }
721              , { "Content-Type", "application/example" }
722              , { "If-Match", "\"e0023aa4e\"" }
723              , { "Content-Length", "10" }
724              }
725   ,.body= "cccccccccc"
726   }
727
728 #define CONNECT_CAPS_REQUEST 26
729 , {.name = "connect caps request"
730   ,.type= HTTP_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"
734          "\r\n"
735   ,.should_keep_alive= FALSE
736   ,.message_complete_on_eof= FALSE
737   ,.http_major= 1
738   ,.http_minor= 0
739   ,.method= HTTP_CONNECT
740   ,.query_string= ""
741   ,.fragment= ""
742   ,.request_path= ""
743   ,.request_url= "HOME0.NETSCAPE.COM:443"
744   ,.num_headers= 2
745   ,.upgrade=""
746   ,.headers= { { "User-agent", "Mozilla/1.1N" }
747              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
748              }
749   ,.body= ""
750   }
751
752 #if !HTTP_PARSER_STRICT
753 #define UTF8_PATH_REQ 27
754 , {.name= "utf-8 path request"
755   ,.type= HTTP_REQUEST
756   ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
757          "Host: github.com\r\n"
758          "\r\n"
759   ,.should_keep_alive= TRUE
760   ,.message_complete_on_eof= FALSE
761   ,.http_major= 1
762   ,.http_minor= 1
763   ,.method= HTTP_GET
764   ,.query_string= "q=1"
765   ,.fragment= "narf"
766   ,.request_path= "/δ¶/δt/pope"
767   ,.request_url= "/δ¶/δt/pope?q=1#narf"
768   ,.num_headers= 1
769   ,.headers= { {"Host", "github.com" }
770              }
771   ,.body= ""
772   }
773
774 #define HOSTNAME_UNDERSCORE 28
775 , {.name = "hostname underscore"
776   ,.type= HTTP_REQUEST
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"
780          "\r\n"
781   ,.should_keep_alive= FALSE
782   ,.message_complete_on_eof= FALSE
783   ,.http_major= 1
784   ,.http_minor= 0
785   ,.method= HTTP_CONNECT
786   ,.query_string= ""
787   ,.fragment= ""
788   ,.request_path= ""
789   ,.request_url= "home_0.netscape.com:443"
790   ,.num_headers= 2
791   ,.upgrade=""
792   ,.headers= { { "User-agent", "Mozilla/1.1N" }
793              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
794              }
795   ,.body= ""
796   }
797 #endif  /* !HTTP_PARSER_STRICT */
798
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"
806          "\r\n"
807          "q=42\r\n" /* note the trailing CRLF */
808   ,.should_keep_alive= TRUE
809   ,.message_complete_on_eof= FALSE
810   ,.http_major= 1
811   ,.http_minor= 1
812   ,.method= HTTP_POST
813   ,.query_string= ""
814   ,.fragment= ""
815   ,.request_path= "/"
816   ,.request_url= "/"
817   ,.num_headers= 3
818   ,.upgrade= 0
819   ,.headers= { { "Host", "www.example.com" }
820              , { "Content-Type", "application/x-www-form-urlencoded" }
821              , { "Content-Length", "4" }
822              }
823   ,.body= "q=42"
824   }
825
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"
834          "\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 */
838   ,.http_major= 1
839   ,.http_minor= 1
840   ,.method= HTTP_POST
841   ,.query_string= ""
842   ,.fragment= ""
843   ,.request_path= "/"
844   ,.request_url= "/"
845   ,.num_headers= 4
846   ,.upgrade= 0
847   ,.headers= { { "Host", "www.example.com" }
848              , { "Content-Type", "application/x-www-form-urlencoded" }
849              , { "Content-Length", "4" }
850              , { "Connection", "close" }
851              }
852   ,.body= "q=42"
853   }
854
855 #define PURGE_REQ 31
856 , {.name = "PURGE request"
857   ,.type= HTTP_REQUEST
858   ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
859          "Host: www.example.com\r\n"
860          "\r\n"
861   ,.should_keep_alive= TRUE
862   ,.message_complete_on_eof= FALSE
863   ,.http_major= 1
864   ,.http_minor= 1
865   ,.method= HTTP_PURGE
866   ,.query_string= ""
867   ,.fragment= ""
868   ,.request_path= "/file.txt"
869   ,.request_url= "/file.txt"
870   ,.num_headers= 1
871   ,.headers= { { "Host", "www.example.com" } }
872   ,.body= ""
873   }
874
875 #define SEARCH_REQ 32
876 , {.name = "SEARCH request"
877   ,.type= HTTP_REQUEST
878   ,.raw= "SEARCH / HTTP/1.1\r\n"
879          "Host: www.example.com\r\n"
880          "\r\n"
881   ,.should_keep_alive= TRUE
882   ,.message_complete_on_eof= FALSE
883   ,.http_major= 1
884   ,.http_minor= 1
885   ,.method= HTTP_SEARCH
886   ,.query_string= ""
887   ,.fragment= ""
888   ,.request_path= "/"
889   ,.request_url= "/"
890   ,.num_headers= 1
891   ,.headers= { { "Host", "www.example.com" } }
892   ,.body= ""
893   }
894
895 #define PROXY_WITH_BASIC_AUTH 33
896 , {.name= "host:port and basic_auth"
897   ,.type= HTTP_REQUEST
898   ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
899          "\r\n"
900   ,.should_keep_alive= TRUE
901   ,.message_complete_on_eof= FALSE
902   ,.http_major= 1
903   ,.http_minor= 1
904   ,.method= HTTP_GET
905   ,.fragment= ""
906   ,.request_path= "/toto"
907   ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
908   ,.host= "hypnotoad.org"
909   ,.userinfo= "a%12:b!&*$"
910   ,.port= 1234
911   ,.num_headers= 0
912   ,.headers= { }
913   ,.body= ""
914   }
915
916 #define LINE_FOLDING_IN_HEADER_WITH_LF 34
917 , {.name= "line folding in header value"
918   ,.type= HTTP_REQUEST
919   ,.raw= "GET / HTTP/1.1\n"
920          "Line1:   abc\n"
921          "\tdef\n"
922          " ghi\n"
923          "\t\tjkl\n"
924          "  mno \n"
925          "\t \tqrs\n"
926          "Line2: \t line2\t\n"
927          "Line3:\n"
928          " line3\n"
929          "Line4: \n"
930          " \n"
931          "Connection:\n"
932          " close\n"
933          "\n"
934   ,.should_keep_alive= FALSE
935   ,.message_complete_on_eof= FALSE
936   ,.http_major= 1
937   ,.http_minor= 1
938   ,.method= HTTP_GET
939   ,.query_string= ""
940   ,.fragment= ""
941   ,.request_path= "/"
942   ,.request_url= "/"
943   ,.num_headers= 5
944   ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl  mno \t \tqrs" }
945              , { "Line2", "line2\t" }
946              , { "Line3", "line3" }
947              , { "Line4", "" }
948              , { "Connection", "close" },
949              }
950   ,.body= ""
951   }
952
953 #define CONNECTION_MULTI 35
954 , {.name = "multiple connection header values with folding"
955   ,.type= HTTP_REQUEST
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"
965          "\r\n"
966          "Hot diggity dogg"
967   ,.should_keep_alive= TRUE
968   ,.message_complete_on_eof= FALSE
969   ,.http_major= 1
970   ,.http_minor= 1
971   ,.method= HTTP_GET
972   ,.query_string= ""
973   ,.fragment= ""
974   ,.request_path= "/demo"
975   ,.request_url= "/demo"
976   ,.num_headers= 7
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" }
985              }
986   ,.body= ""
987   }
988
989
990 , {.name= NULL } /* sentinel */
991 };
992
993 /* * R E S P O N S E S * */
994 const struct message responses[] =
995 #define GOOGLE_301 0
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"
1005          "Server: gws\r\n"
1006          "Content-Length:  219  \r\n"
1007          "\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
1016   ,.http_major= 1
1017   ,.http_minor= 1
1018   ,.status_code= 301
1019   ,.response_status= "Moved Permanently"
1020   ,.num_headers= 8
1021   ,.headers=
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  " }
1030     }
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"
1037   }
1038
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
1042  * by the EOF.
1043  * Compare with APACHEBENCH_GET
1044  */
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"
1053          "\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
1065   ,.http_major= 1
1066   ,.http_minor= 1
1067   ,.status_code= 200
1068   ,.response_status= "OK"
1069   ,.num_headers= 5
1070   ,.headers=
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" }
1076     }
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>"
1086   }
1087
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
1094   ,.http_major= 1
1095   ,.http_minor= 1
1096   ,.status_code= 404
1097   ,.response_status= "Not Found"
1098   ,.num_headers= 0
1099   ,.headers= {}
1100   ,.body_size= 0
1101   ,.body= ""
1102   }
1103
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
1110   ,.http_major= 1
1111   ,.http_minor= 1
1112   ,.status_code= 301
1113   ,.response_status= ""
1114   ,.num_headers= 0
1115   ,.headers= {}
1116   ,.body= ""
1117   }
1118
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"
1125          "\r\n"
1126          "25  \r\n"
1127          "This is the data in the first chunk\r\n"
1128          "\r\n"
1129          "1C\r\n"
1130          "and this is the second one\r\n"
1131          "\r\n"
1132          "0  \r\n"
1133          "\r\n"
1134   ,.should_keep_alive= TRUE
1135   ,.message_complete_on_eof= FALSE
1136   ,.http_major= 1
1137   ,.http_minor= 1
1138   ,.status_code= 200
1139   ,.response_status= "OK"
1140   ,.num_headers= 2
1141   ,.headers=
1142     { {"Content-Type", "text/plain" }
1143     , {"Transfer-Encoding", "chunked" }
1144     }
1145   ,.body_size = 37+28
1146   ,.body =
1147          "This is the data in the first chunk\r\n"
1148          "and this is the second one\r\n"
1149
1150   }
1151
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"
1158          "\n"
1159          "these headers are from http://news.ycombinator.com/"
1160   ,.should_keep_alive= FALSE
1161   ,.message_complete_on_eof= TRUE
1162   ,.http_major= 1
1163   ,.http_minor= 1
1164   ,.status_code= 200
1165   ,.response_status= "OK"
1166   ,.num_headers= 2
1167   ,.headers=
1168     { {"Content-Type", "text/html; charset=utf-8" }
1169     , {"Connection", "close" }
1170     }
1171   ,.body= "these headers are from http://news.ycombinator.com/"
1172   }
1173
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"
1182          "\r\n"
1183          "hello world"
1184   ,.should_keep_alive= FALSE
1185   ,.message_complete_on_eof= FALSE
1186   ,.http_major= 1
1187   ,.http_minor= 1
1188   ,.status_code= 200
1189   ,.response_status= "OK"
1190   ,.num_headers= 4
1191   ,.headers=
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"}
1196     }
1197   ,.body= "hello world"
1198   }
1199
1200 #define UNDERSTORE_HEADER_KEY 7
1201   // shown by
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
1212   ,.http_major= 1
1213   ,.http_minor= 1
1214   ,.status_code= 200
1215   ,.response_status= "OK"
1216   ,.num_headers= 4
1217   ,.headers=
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" }
1222     }
1223   ,.body= ""
1224   }
1225
1226 #define BONJOUR_MADAME_FR 8
1227 /* The client should not merge two headers fields when the first one doesn't
1228  * have a value.
1229  */
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"
1236          "Pragma: \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"
1242          "\r\n"
1243   ,.should_keep_alive= TRUE
1244   ,.message_complete_on_eof= FALSE
1245   ,.http_major= 1
1246   ,.http_minor= 0
1247   ,.status_code= 301
1248   ,.response_status= "Moved Permanently"
1249   ,.num_headers= 9
1250   ,.headers=
1251     { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1252     , { "Server", "Apache/2.2.3 (Red Hat)" }
1253     , { "Cache-Control", "public" }
1254     , { "Pragma", "" }
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" }
1260     }
1261   ,.body= ""
1262   }
1263
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"
1280          "\r\n"
1281          "0\r\n\r\n"
1282   ,.should_keep_alive= FALSE
1283   ,.message_complete_on_eof= FALSE
1284   ,.http_major= 1
1285   ,.http_minor= 1
1286   ,.status_code= 200
1287   ,.response_status= "OK"
1288   ,.num_headers= 11
1289   ,.headers=
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" }
1301     }
1302   ,.body= ""
1303   }
1304
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"
1313          "\r\n"
1314   ,.should_keep_alive= FALSE
1315   ,.message_complete_on_eof= FALSE
1316   ,.http_major= 1
1317   ,.http_minor= 1
1318   ,.status_code= 500
1319   ,.response_status= "Oriëntatieprobleem"
1320   ,.num_headers= 3
1321   ,.headers=
1322     { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1323     , { "Content-Length", "0" }
1324     , { "Connection", "close" }
1325     }
1326   ,.body= ""
1327   }
1328
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"
1334          "\r\n"
1335   ,.should_keep_alive= FALSE
1336   ,.message_complete_on_eof= TRUE
1337   ,.http_major= 0
1338   ,.http_minor= 9
1339   ,.status_code= 200
1340   ,.response_status= "OK"
1341   ,.num_headers= 0
1342   ,.headers=
1343     {}
1344   ,.body= ""
1345   }
1346
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.
1351  */
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"
1356          "\r\n"
1357          "hello world"
1358   ,.should_keep_alive= FALSE
1359   ,.message_complete_on_eof= TRUE
1360   ,.http_major= 1
1361   ,.http_minor= 1
1362   ,.status_code= 200
1363   ,.response_status= "OK"
1364   ,.num_headers= 1
1365   ,.headers=
1366     { { "Content-Type", "text/plain" }
1367     }
1368   ,.body= "hello world"
1369   }
1370
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"
1376          "\r\n"
1377   ,.should_keep_alive= FALSE
1378   ,.message_complete_on_eof= TRUE
1379   ,.http_major= 1
1380   ,.http_minor= 0
1381   ,.status_code= 200
1382   ,.response_status= "OK"
1383   ,.num_headers= 1
1384   ,.headers=
1385     { { "Connection", "keep-alive" }
1386     }
1387   ,.body_size= 0
1388   ,.body= ""
1389   }
1390
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"
1396          "\r\n"
1397   ,.should_keep_alive= TRUE
1398   ,.message_complete_on_eof= FALSE
1399   ,.http_major= 1
1400   ,.http_minor= 0
1401   ,.status_code= 204
1402   ,.response_status= "No content"
1403   ,.num_headers= 1
1404   ,.headers=
1405     { { "Connection", "keep-alive" }
1406     }
1407   ,.body_size= 0
1408   ,.body= ""
1409   }
1410
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"
1415          "\r\n"
1416   ,.should_keep_alive= FALSE
1417   ,.message_complete_on_eof= TRUE
1418   ,.http_major= 1
1419   ,.http_minor= 1
1420   ,.status_code= 200
1421   ,.response_status= "OK"
1422   ,.num_headers= 0
1423   ,.headers={}
1424   ,.body_size= 0
1425   ,.body= ""
1426   }
1427
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"
1432          "\r\n"
1433   ,.should_keep_alive= TRUE
1434   ,.message_complete_on_eof= FALSE
1435   ,.http_major= 1
1436   ,.http_minor= 1
1437   ,.status_code= 204
1438   ,.response_status= "No content"
1439   ,.num_headers= 0
1440   ,.headers={}
1441   ,.body_size= 0
1442   ,.body= ""
1443   }
1444
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"
1450          "\r\n"
1451   ,.should_keep_alive= FALSE
1452   ,.message_complete_on_eof= FALSE
1453   ,.http_major= 1
1454   ,.http_minor= 1
1455   ,.status_code= 204
1456   ,.response_status= "No content"
1457   ,.num_headers= 1
1458   ,.headers=
1459     { { "Connection", "close" }
1460     }
1461   ,.body_size= 0
1462   ,.body= ""
1463   }
1464
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"
1470          "\r\n"
1471          "0\r\n"
1472          "\r\n"
1473   ,.should_keep_alive= TRUE
1474   ,.message_complete_on_eof= FALSE
1475   ,.http_major= 1
1476   ,.http_minor= 1
1477   ,.status_code= 200
1478   ,.response_status= "OK"
1479   ,.num_headers= 1
1480   ,.headers=
1481     { { "Transfer-Encoding", "chunked" }
1482     }
1483   ,.body_size= 0
1484   ,.body= ""
1485   }
1486
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"
1500          "\r\n"
1501          "<xml>hello</xml>" /* fake body */
1502   ,.should_keep_alive= TRUE
1503   ,.message_complete_on_eof= FALSE
1504   ,.http_major= 1
1505   ,.http_minor= 1
1506   ,.status_code= 200
1507   ,.response_status= "OK"
1508   ,.num_headers= 7
1509   ,.headers=
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" }
1517     }
1518   ,.body= "<xml>hello</xml>"
1519   }
1520 #endif /* !HTTP_PARSER_STRICT */
1521
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"
1535          "\r\n"
1536          "1\r\n"
1537          "\n\r\n"
1538          "0\r\n"
1539          "\r\n"
1540   ,.should_keep_alive= TRUE
1541   ,.message_complete_on_eof= FALSE
1542   ,.http_major= 1
1543   ,.http_minor= 1
1544   ,.status_code= 301
1545   ,.response_status= "MovedPermanently"
1546   ,.num_headers= 9
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" }
1556              }
1557   ,.body= "\n"
1558   }
1559
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"
1564          "\r\n"
1565   ,.should_keep_alive= FALSE
1566   ,.message_complete_on_eof= TRUE
1567   ,.http_major= 1
1568   ,.http_minor= 1
1569   ,.status_code= 200
1570   ,.response_status= ""
1571   ,.num_headers= 0
1572   ,.headers= {}
1573   ,.body= ""
1574   }
1575
1576 , {.name= NULL } /* sentinel */
1577 };
1578
1579 /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1580  * define it ourselves.
1581  */
1582 size_t
1583 strnlen(const char *s, size_t maxlen)
1584 {
1585   const char *p;
1586
1587   p = memchr(s, '\0', maxlen);
1588   if (p == NULL)
1589     return maxlen;
1590
1591   return p - s;
1592 }
1593
1594 size_t
1595 strlncat(char *dst, size_t len, const char *src, size_t n)
1596 {
1597   size_t slen;
1598   size_t dlen;
1599   size_t rlen;
1600   size_t ncpy;
1601
1602   slen = strnlen(src, n);
1603   dlen = strnlen(dst, len);
1604
1605   if (dlen < len) {
1606     rlen = len - dlen;
1607     ncpy = slen < rlen ? slen : (rlen - 1);
1608     memcpy(dst + dlen, src, ncpy);
1609     dst[dlen + ncpy] = '\0';
1610   }
1611
1612   assert(len > slen + dlen);
1613   return slen + dlen;
1614 }
1615
1616 size_t
1617 strlcat(char *dst, const char *src, size_t len)
1618 {
1619   return strlncat(dst, len, src, (size_t) -1);
1620 }
1621
1622 size_t
1623 strlncpy(char *dst, size_t len, const char *src, size_t n)
1624 {
1625   size_t slen;
1626   size_t ncpy;
1627
1628   slen = strnlen(src, n);
1629
1630   if (len > 0) {
1631     ncpy = slen < len ? slen : (len - 1);
1632     memcpy(dst, src, ncpy);
1633     dst[ncpy] = '\0';
1634   }
1635
1636   assert(len > slen);
1637   return slen;
1638 }
1639
1640 size_t
1641 strlcpy(char *dst, const char *src, size_t len)
1642 {
1643   return strlncpy(dst, len, src, (size_t) -1);
1644 }
1645
1646 int
1647 request_url_cb (http_parser *p, const char *buf, size_t len)
1648 {
1649   assert(p == parser);
1650   strlncat(messages[num_messages].request_url,
1651            sizeof(messages[num_messages].request_url),
1652            buf,
1653            len);
1654   return 0;
1655 }
1656
1657 int
1658 header_field_cb (http_parser *p, const char *buf, size_t len)
1659 {
1660   assert(p == parser);
1661   struct message *m = &messages[num_messages];
1662
1663   if (m->last_header_element != FIELD)
1664     m->num_headers++;
1665
1666   strlncat(m->headers[m->num_headers-1][0],
1667            sizeof(m->headers[m->num_headers-1][0]),
1668            buf,
1669            len);
1670
1671   m->last_header_element = FIELD;
1672
1673   return 0;
1674 }
1675
1676 int
1677 header_value_cb (http_parser *p, const char *buf, size_t len)
1678 {
1679   assert(p == parser);
1680   struct message *m = &messages[num_messages];
1681
1682   strlncat(m->headers[m->num_headers-1][1],
1683            sizeof(m->headers[m->num_headers-1][1]),
1684            buf,
1685            len);
1686
1687   m->last_header_element = VALUE;
1688
1689   return 0;
1690 }
1691
1692 void
1693 check_body_is_final (const http_parser *p)
1694 {
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");
1699     assert(0);
1700     abort();
1701   }
1702   messages[num_messages].body_is_final = http_body_is_final(p);
1703 }
1704
1705 int
1706 body_cb (http_parser *p, const char *buf, size_t len)
1707 {
1708   assert(p == parser);
1709   strlncat(messages[num_messages].body,
1710            sizeof(messages[num_messages].body),
1711            buf,
1712            len);
1713   messages[num_messages].body_size += len;
1714   check_body_is_final(p);
1715  // printf("body_cb: '%s'\n", requests[num_messages].body);
1716   return 0;
1717 }
1718
1719 int
1720 count_body_cb (http_parser *p, const char *buf, size_t len)
1721 {
1722   assert(p == parser);
1723   assert(buf);
1724   messages[num_messages].body_size += len;
1725   check_body_is_final(p);
1726   return 0;
1727 }
1728
1729 int
1730 message_begin_cb (http_parser *p)
1731 {
1732   assert(p == parser);
1733   messages[num_messages].message_begin_cb_called = TRUE;
1734   return 0;
1735 }
1736
1737 int
1738 headers_complete_cb (http_parser *p)
1739 {
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);
1747   return 0;
1748 }
1749
1750 int
1751 message_complete_cb (http_parser *p)
1752 {
1753   assert(p == parser);
1754   if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
1755   {
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");
1759     assert(0);
1760     abort();
1761   }
1762
1763   if (messages[num_messages].body_size &&
1764       http_body_is_final(p) &&
1765       !messages[num_messages].body_is_final)
1766   {
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");
1770     assert(0);
1771     abort();
1772   }
1773
1774   messages[num_messages].message_complete_cb_called = TRUE;
1775
1776   messages[num_messages].message_complete_on_eof = currently_parsing_eof;
1777
1778   num_messages++;
1779   return 0;
1780 }
1781
1782 int
1783 response_status_cb (http_parser *p, const char *buf, size_t len)
1784 {
1785   assert(p == parser);
1786   strlncat(messages[num_messages].response_status,
1787            sizeof(messages[num_messages].response_status),
1788            buf,
1789            len);
1790   return 0;
1791 }
1792
1793 /* These dontcall_* callbacks exist so that we can verify that when we're
1794  * paused, no additional callbacks are invoked */
1795 int
1796 dontcall_message_begin_cb (http_parser *p)
1797 {
1798   if (p) { } // gcc
1799   fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1800   abort();
1801 }
1802
1803 int
1804 dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1805 {
1806   if (p || buf || len) { } // gcc
1807   fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1808   abort();
1809 }
1810
1811 int
1812 dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1813 {
1814   if (p || buf || len) { } // gcc
1815   fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1816   abort();
1817 }
1818
1819 int
1820 dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1821 {
1822   if (p || buf || len) { } // gcc
1823   fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1824   abort();
1825 }
1826
1827 int
1828 dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1829 {
1830   if (p || buf || len) { } // gcc
1831   fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1832   abort();
1833 }
1834
1835 int
1836 dontcall_headers_complete_cb (http_parser *p)
1837 {
1838   if (p) { } // gcc
1839   fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1840                   "parser ***\n\n");
1841   abort();
1842 }
1843
1844 int
1845 dontcall_message_complete_cb (http_parser *p)
1846 {
1847   if (p) { } // gcc
1848   fprintf(stderr, "\n\n*** on_message_complete() called on paused "
1849                   "parser ***\n\n");
1850   abort();
1851 }
1852
1853 int
1854 dontcall_response_status_cb (http_parser *p, const char *buf, size_t len)
1855 {
1856   if (p || buf || len) { } // gcc
1857   fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n");
1858   abort();
1859 }
1860
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
1870   };
1871
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. */
1876 int
1877 pause_message_begin_cb (http_parser *p)
1878 {
1879   http_parser_pause(p, 1);
1880   *current_pause_parser = settings_dontcall;
1881   return message_begin_cb(p);
1882 }
1883
1884 int
1885 pause_header_field_cb (http_parser *p, const char *buf, size_t len)
1886 {
1887   http_parser_pause(p, 1);
1888   *current_pause_parser = settings_dontcall;
1889   return header_field_cb(p, buf, len);
1890 }
1891
1892 int
1893 pause_header_value_cb (http_parser *p, const char *buf, size_t len)
1894 {
1895   http_parser_pause(p, 1);
1896   *current_pause_parser = settings_dontcall;
1897   return header_value_cb(p, buf, len);
1898 }
1899
1900 int
1901 pause_request_url_cb (http_parser *p, const char *buf, size_t len)
1902 {
1903   http_parser_pause(p, 1);
1904   *current_pause_parser = settings_dontcall;
1905   return request_url_cb(p, buf, len);
1906 }
1907
1908 int
1909 pause_body_cb (http_parser *p, const char *buf, size_t len)
1910 {
1911   http_parser_pause(p, 1);
1912   *current_pause_parser = settings_dontcall;
1913   return body_cb(p, buf, len);
1914 }
1915
1916 int
1917 pause_headers_complete_cb (http_parser *p)
1918 {
1919   http_parser_pause(p, 1);
1920   *current_pause_parser = settings_dontcall;
1921   return headers_complete_cb(p);
1922 }
1923
1924 int
1925 pause_message_complete_cb (http_parser *p)
1926 {
1927   http_parser_pause(p, 1);
1928   *current_pause_parser = settings_dontcall;
1929   return message_complete_cb(p);
1930 }
1931
1932 int
1933 pause_response_status_cb (http_parser *p, const char *buf, size_t len)
1934 {
1935   http_parser_pause(p, 1);
1936   *current_pause_parser = settings_dontcall;
1937   return response_status_cb(p, buf, len);
1938 }
1939
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
1949   };
1950
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
1957   ,.on_body = body_cb
1958   ,.on_headers_complete = headers_complete_cb
1959   ,.on_message_complete = message_complete_cb
1960   };
1961
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
1971   };
1972
1973 static http_parser_settings settings_null =
1974   {.on_message_begin = 0
1975   ,.on_header_field = 0
1976   ,.on_header_value = 0
1977   ,.on_url = 0
1978   ,.on_status = 0
1979   ,.on_body = 0
1980   ,.on_headers_complete = 0
1981   ,.on_message_complete = 0
1982   };
1983
1984 void
1985 parser_init (enum http_parser_type type)
1986 {
1987   num_messages = 0;
1988
1989   assert(parser == NULL);
1990
1991   parser = malloc(sizeof(http_parser));
1992
1993   http_parser_init(parser, type);
1994
1995   memset(&messages, 0, sizeof messages);
1996
1997 }
1998
1999 void
2000 parser_free ()
2001 {
2002   assert(parser);
2003   free(parser);
2004   parser = NULL;
2005 }
2006
2007 size_t parse (const char *buf, size_t len)
2008 {
2009   size_t nparsed;
2010   currently_parsing_eof = (len == 0);
2011   nparsed = http_parser_execute(parser, &settings, buf, len);
2012   return nparsed;
2013 }
2014
2015 size_t parse_count_body (const char *buf, size_t len)
2016 {
2017   size_t nparsed;
2018   currently_parsing_eof = (len == 0);
2019   nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
2020   return nparsed;
2021 }
2022
2023 size_t parse_pause (const char *buf, size_t len)
2024 {
2025   size_t nparsed;
2026   http_parser_settings s = settings_pause;
2027
2028   currently_parsing_eof = (len == 0);
2029   current_pause_parser = &s;
2030   nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
2031   return nparsed;
2032 }
2033
2034 static inline int
2035 check_str_eq (const struct message *m,
2036               const char *prop,
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);
2043     return 0;
2044   }
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);
2049     return 0;
2050   }
2051   return 1;
2052 }
2053
2054 static inline int
2055 check_num_eq (const struct message *m,
2056               const char *prop,
2057               int expected,
2058               int found) {
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);
2063     return 0;
2064   }
2065   return 1;
2066 }
2067
2068 #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
2069   if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
2070
2071 #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
2072   if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
2073
2074 #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn)           \
2075 do {                                                                 \
2076   char ubuf[256];                                                    \
2077                                                                      \
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';                          \
2082   } else {                                                           \
2083     ubuf[0] = '\0';                                                  \
2084   }                                                                  \
2085                                                                      \
2086   check_str_eq(expected, #prop, expected->prop, ubuf);               \
2087 } while(0)
2088
2089 int
2090 message_eq (int index, const struct message *expected)
2091 {
2092   int i;
2093   struct message *m = &messages[index];
2094
2095   MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
2096   MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
2097
2098   if (expected->type == HTTP_REQUEST) {
2099     MESSAGE_CHECK_NUM_EQ(expected, m, method);
2100   } else {
2101     MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
2102     MESSAGE_CHECK_STR_EQ(expected, m, response_status);
2103   }
2104
2105   MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
2106   MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
2107
2108   assert(m->message_begin_cb_called);
2109   assert(m->headers_complete_cb_called);
2110   assert(m->message_complete_cb_called);
2111
2112
2113   MESSAGE_CHECK_STR_EQ(expected, m, request_url);
2114
2115   /* Check URL components; we can't do this w/ CONNECT since it doesn't
2116    * send us a well-formed URL.
2117    */
2118   if (*m->request_url && m->method != HTTP_CONNECT) {
2119     struct http_parser_url u;
2120
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",
2123         m->request_url);
2124       abort();
2125     }
2126
2127     if (expected->host) {
2128       MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
2129     }
2130
2131     if (expected->userinfo) {
2132       MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
2133     }
2134
2135     m->port = (u.field_set & (1 << UF_PORT)) ?
2136       u.port : 0;
2137
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);
2142   }
2143
2144   if (expected->body_size) {
2145     MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
2146   } else {
2147     MESSAGE_CHECK_STR_EQ(expected, m, body);
2148   }
2149
2150   MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
2151
2152   int r;
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]);
2155     if (!r) return 0;
2156     r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
2157     if (!r) return 0;
2158   }
2159
2160   MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
2161
2162   return 1;
2163 }
2164
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.
2168  */
2169 size_t
2170 count_parsed_messages(const size_t nmsgs, ...) {
2171   size_t i;
2172   va_list ap;
2173
2174   va_start(ap, nmsgs);
2175
2176   for (i = 0; i < nmsgs; i++) {
2177     struct message *m = va_arg(ap, struct message *);
2178
2179     if (m->upgrade) {
2180       va_end(ap);
2181       return i + 1;
2182     }
2183   }
2184
2185   va_end(ap);
2186   return nmsgs;
2187 }
2188
2189 /* Given a sequence of bytes and the number of these that we were able to
2190  * parse, verify that upgrade bodies are correct.
2191  */
2192 void
2193 upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2194   va_list ap;
2195   size_t i;
2196   size_t off = 0;
2197  
2198   va_start(ap, nmsgs);
2199
2200   for (i = 0; i < nmsgs; i++) {
2201     struct message *m = va_arg(ap, struct message *);
2202
2203     off += strlen(m->raw);
2204
2205     if (m->upgrade) {
2206       off -= strlen(m->upgrade);
2207
2208       /* Check the portion of the response after its specified upgrade */
2209       if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2210         abort();
2211       }
2212
2213       /* Fix up the response so that message_eq() will verify the beginning
2214        * of the upgrade */
2215       *(body + nread + strlen(m->upgrade)) = '\0';
2216       messages[num_messages -1 ].upgrade = body + nread;
2217
2218       va_end(ap);
2219       return;
2220     }
2221   }
2222
2223   va_end(ap);
2224   printf("\n\n*** Error: expected a message with upgrade ***\n");
2225
2226   abort();
2227 }
2228
2229 static void
2230 print_error (const char *raw, size_t error_location)
2231 {
2232   fprintf(stderr, "\n*** %s ***\n\n",
2233           http_errno_description(HTTP_PARSER_ERRNO(parser)));
2234
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;
2239     switch (raw[i]) {
2240       case '\r':
2241         char_len = 2;
2242         fprintf(stderr, "\\r");
2243         break;
2244
2245       case '\n':
2246         fprintf(stderr, "\\n\n");
2247
2248         if (this_line) goto print;
2249
2250         error_location_line = 0;
2251         continue;
2252
2253       default:
2254         char_len = 1;
2255         fputc(raw[i], stderr);
2256         break;
2257     }
2258     if (!this_line) error_location_line += char_len;
2259   }
2260
2261   fprintf(stderr, "[eof]\n");
2262
2263  print:
2264   for (j = 0; j < error_location_line; j++) {
2265     fputc(' ', stderr);
2266   }
2267   fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2268 }
2269
2270 void
2271 test_preserve_data (void)
2272 {
2273   char my_data[] = "application-specific data";
2274   http_parser parser;
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");
2279     abort();
2280   }
2281 }
2282
2283 struct url_test {
2284   const char *name;
2285   const char *url;
2286   int is_connect;
2287   struct http_parser_url u;
2288   int rv;
2289 };
2290
2291 const struct url_test url_tests[] =
2292 { {.name="proxy request"
2293   ,.url="http://hostname/"
2294   ,.is_connect=0
2295   ,.u=
2296     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2297     ,.port=0
2298     ,.field_data=
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 */
2306       }
2307     }
2308   ,.rv=0
2309   }
2310
2311 , {.name="proxy request with port"
2312   ,.url="http://hostname:444/"
2313   ,.is_connect=0
2314   ,.u=
2315     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2316     ,.port=444
2317     ,.field_data=
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 */
2325       }
2326     }
2327   ,.rv=0
2328   }
2329
2330 , {.name="CONNECT request"
2331   ,.url="hostname:443"
2332   ,.is_connect=1
2333   ,.u=
2334     {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2335     ,.port=443
2336     ,.field_data=
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 */
2344       }
2345     }
2346   ,.rv=0
2347   }
2348
2349 , {.name="CONNECT request but not connect"
2350   ,.url="hostname:443"
2351   ,.is_connect=0
2352   ,.rv=1
2353   }
2354
2355 , {.name="proxy ipv6 request"
2356   ,.url="http://[1:2::3:4]/"
2357   ,.is_connect=0
2358   ,.u=
2359     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2360     ,.port=0
2361     ,.field_data=
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 */
2369       }
2370     }
2371   ,.rv=0
2372   }
2373
2374 , {.name="proxy ipv6 request with port"
2375   ,.url="http://[1:2::3:4]:67/"
2376   ,.is_connect=0
2377   ,.u=
2378     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2379     ,.port=67
2380     ,.field_data=
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 */
2388       }
2389     }
2390   ,.rv=0
2391   }
2392
2393 , {.name="CONNECT ipv6 address"
2394   ,.url="[1:2::3:4]:443"
2395   ,.is_connect=1
2396   ,.u=
2397     {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2398     ,.port=443
2399     ,.field_data=
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 */
2407       }
2408     }
2409   ,.rv=0
2410   }
2411
2412 , {.name="ipv4 in ipv6 address"
2413   ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2414   ,.is_connect=0
2415   ,.u=
2416     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2417     ,.port=0
2418     ,.field_data=
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 */
2426       }
2427     }
2428   ,.rv=0
2429   }
2430
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"
2435   ,.is_connect=0
2436   ,.u=
2437     {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2438     ,.port=0
2439     ,.field_data=
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 */
2447       }
2448     }
2449   ,.rv=0
2450   }
2451
2452 , {.name="space URL encoded"
2453   ,.url="/toto.html?toto=a%20b"
2454   ,.is_connect=0
2455   ,.u=
2456     {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2457     ,.port=0
2458     ,.field_data=
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 */
2466       }
2467     }
2468   ,.rv=0
2469   }
2470
2471
2472 , {.name="URL fragment"
2473   ,.url="/toto.html#titi"
2474   ,.is_connect=0
2475   ,.u=
2476     {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2477     ,.port=0
2478     ,.field_data=
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 */
2486       }
2487     }
2488   ,.rv=0
2489   }
2490
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"
2494   ,.is_connect=0
2495   ,.u=
2496     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2497       (1<<UF_FRAGMENT)
2498     ,.port=0
2499     ,.field_data=
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 */
2507       }
2508     }
2509   ,.rv=0
2510   }
2511
2512 , {.name="complex URL from node js url parser doc"
2513   ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
2514   ,.is_connect=0
2515   ,.u=
2516     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2517       (1<<UF_QUERY) | (1<<UF_FRAGMENT)
2518     ,.port=8080
2519     ,.field_data=
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 */
2527       }
2528     }
2529   ,.rv=0
2530   }
2531
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"
2534   ,.is_connect=0
2535   ,.u=
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)
2538     ,.port=8080
2539     ,.field_data=
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 */
2547       }
2548     }
2549   ,.rv=0
2550   }
2551
2552 , {.name="double @"
2553   ,.url="http://a:b@@hostname:443/"
2554   ,.is_connect=0
2555   ,.rv=1
2556   }
2557
2558 , {.name="proxy empty host"
2559   ,.url="http://:443/"
2560   ,.is_connect=0
2561   ,.rv=1
2562   }
2563
2564 , {.name="proxy empty port"
2565   ,.url="http://hostname:/"
2566   ,.is_connect=0
2567   ,.rv=1
2568   }
2569
2570 , {.name="CONNECT with basic auth"
2571   ,.url="a:b@hostname:443"
2572   ,.is_connect=1
2573   ,.rv=1
2574   }
2575
2576 , {.name="CONNECT empty host"
2577   ,.url=":443"
2578   ,.is_connect=1
2579   ,.rv=1
2580   }
2581
2582 , {.name="CONNECT empty port"
2583   ,.url="hostname:"
2584   ,.is_connect=1
2585   ,.rv=1
2586   }
2587
2588 , {.name="CONNECT with extra bits"
2589   ,.url="hostname:443/"
2590   ,.is_connect=1
2591   ,.rv=1
2592   }
2593
2594 , {.name="space in URL"
2595   ,.url="/foo bar/"
2596   ,.rv=1 /* s_dead */
2597   }
2598
2599 , {.name="proxy basic auth with space url encoded"
2600   ,.url="http://a%20:b@host.com/"
2601   ,.is_connect=0
2602   ,.u=
2603     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2604     ,.port=0
2605     ,.field_data=
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 */
2613       }
2614     }
2615   ,.rv=0
2616   }
2617
2618 , {.name="carriage return in URL"
2619   ,.url="/foo\rbar/"
2620   ,.rv=1 /* s_dead */
2621   }
2622
2623 , {.name="proxy double : in URL"
2624   ,.url="http://hostname::443/"
2625   ,.rv=1 /* s_dead */
2626   }
2627
2628 , {.name="proxy basic auth with double :"
2629   ,.url="http://a::b@host.com/"
2630   ,.is_connect=0
2631   ,.u=
2632     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2633     ,.port=0
2634     ,.field_data=
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 */
2642       }
2643     }
2644   ,.rv=0
2645   }
2646
2647 , {.name="line feed in URL"
2648   ,.url="/foo\nbar/"
2649   ,.rv=1 /* s_dead */
2650   }
2651
2652 , {.name="proxy empty basic auth"
2653   ,.url="http://@hostname/fo"
2654   ,.u=
2655     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
2656     ,.port=0
2657     ,.field_data=
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 */
2665       }
2666     }
2667   ,.rv=0
2668   }
2669 , {.name="proxy line feed in hostname"
2670   ,.url="http://host\name/fo"
2671   ,.rv=1 /* s_dead */
2672   }
2673
2674 , {.name="proxy % in hostname"
2675   ,.url="http://host%name/fo"
2676   ,.rv=1 /* s_dead */
2677   }
2678
2679 , {.name="proxy ; in hostname"
2680   ,.url="http://host;ame/fo"
2681   ,.rv=1 /* s_dead */
2682   }
2683
2684 , {.name="proxy basic auth with unreservedchars"
2685   ,.url="http://a!;-_!=+$@host.com/"
2686   ,.is_connect=0
2687   ,.u=
2688     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2689     ,.port=0
2690     ,.field_data=
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 */
2698       }
2699     }
2700   ,.rv=0
2701   }
2702
2703 , {.name="proxy only empty basic auth"
2704   ,.url="http://@/fo"
2705   ,.rv=1 /* s_dead */
2706   }
2707
2708 , {.name="proxy only basic auth"
2709   ,.url="http://toto@/fo"
2710   ,.rv=1 /* s_dead */
2711   }
2712
2713 , {.name="proxy emtpy hostname"
2714   ,.url="http:///fo"
2715   ,.rv=1 /* s_dead */
2716   }
2717
2718 , {.name="proxy = in URL"
2719   ,.url="http://host=ame/fo"
2720   ,.rv=1 /* s_dead */
2721   }
2722
2723 #if HTTP_PARSER_STRICT
2724
2725 , {.name="tab in URL"
2726   ,.url="/foo\tbar/"
2727   ,.rv=1 /* s_dead */
2728   }
2729
2730 , {.name="form feed in URL"
2731   ,.url="/foo\fbar/"
2732   ,.rv=1 /* s_dead */
2733   }
2734
2735 #else /* !HTTP_PARSER_STRICT */
2736
2737 , {.name="tab in URL"
2738   ,.url="/foo\tbar/"
2739   ,.u=
2740     {.field_set=(1 << UF_PATH)
2741     ,.field_data=
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 */
2749       }
2750     }
2751   ,.rv=0
2752   }
2753
2754 , {.name="form feed in URL"
2755   ,.url="/foo\fbar/"
2756   ,.u=
2757     {.field_set=(1 << UF_PATH)
2758     ,.field_data=
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 */
2766       }
2767     }
2768   ,.rv=0
2769   }
2770 #endif
2771 };
2772
2773 void
2774 dump_url (const char *url, const struct http_parser_url *u)
2775 {
2776   unsigned int i;
2777
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);
2782       continue;
2783     }
2784
2785     printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
2786            i,
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);
2791   }
2792 }
2793
2794 void
2795 test_parse_url (void)
2796 {
2797   struct http_parser_url u;
2798   const struct url_test *test;
2799   unsigned int i;
2800   int rv;
2801
2802   for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
2803     test = &url_tests[i];
2804     memset(&u, 0, sizeof(u));
2805
2806     rv = http_parser_parse_url(test->url,
2807                                strlen(test->url),
2808                                test->is_connect,
2809                                &u);
2810
2811     if (test->rv == 0) {
2812       if (rv != 0) {
2813         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2814                "unexpected rv %d ***\n\n", test->url, test->name, rv);
2815         abort();
2816       }
2817
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);
2821
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);
2826
2827         abort();
2828       }
2829     } else {
2830       /* test->rv != 0 */
2831       if (rv == 0) {
2832         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2833                "unexpected rv %d ***\n\n", test->url, test->name, rv);
2834         abort();
2835       }
2836     }
2837   }
2838 }
2839
2840 void
2841 test_method_str (void)
2842 {
2843   assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
2844   assert(0 == strcmp("<unknown>", http_method_str(1337)));
2845 }
2846
2847 void
2848 test_message (const struct message *message)
2849 {
2850   size_t raw_len = strlen(message->raw);
2851   size_t msg1len;
2852   for (msg1len = 0; msg1len < raw_len; msg1len++) {
2853     parser_init(message->type);
2854
2855     size_t read;
2856     const char *msg1 = message->raw;
2857     const char *msg2 = msg1 + msg1len;
2858     size_t msg2len = raw_len - msg1len;
2859
2860     if (msg1len) {
2861       read = parse(msg1, msg1len);
2862
2863       if (message->upgrade && parser->upgrade) {
2864         messages[num_messages - 1].upgrade = msg1 + read;
2865         goto test;
2866       }
2867
2868       if (read != msg1len) {
2869         print_error(msg1, read);
2870         abort();
2871       }
2872     }
2873
2874
2875     read = parse(msg2, msg2len);
2876
2877     if (message->upgrade && parser->upgrade) {
2878       messages[num_messages - 1].upgrade = msg2 + read;
2879       goto test;
2880     }
2881
2882     if (read != msg2len) {
2883       print_error(msg2, read);
2884       abort();
2885     }
2886
2887     read = parse(NULL, 0);
2888
2889     if (read != 0) {
2890       print_error(message->raw, read);
2891       abort();
2892     }
2893
2894   test:
2895
2896     if (num_messages != 1) {
2897       printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2898       abort();
2899     }
2900
2901     if(!message_eq(0, message)) abort();
2902
2903     parser_free();
2904   }
2905 }
2906
2907 void
2908 test_message_count_body (const struct message *message)
2909 {
2910   parser_init(message->type);
2911
2912   size_t read;
2913   size_t l = strlen(message->raw);
2914   size_t i, toread;
2915   size_t chunk = 4024;
2916
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);
2922       abort();
2923     }
2924   }
2925
2926
2927   read = parse_count_body(NULL, 0);
2928   if (read != 0) {
2929     print_error(message->raw, read);
2930     abort();
2931   }
2932
2933   if (num_messages != 1) {
2934     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2935     abort();
2936   }
2937
2938   if(!message_eq(0, message)) abort();
2939
2940   parser_free();
2941 }
2942
2943 void
2944 test_simple (const char *buf, enum http_errno err_expected)
2945 {
2946   parser_init(HTTP_REQUEST);
2947
2948   enum http_errno err;
2949
2950   parse(buf, strlen(buf));
2951   err = HTTP_PARSER_ERRNO(parser);
2952   parse(NULL, 0);
2953
2954   parser_free();
2955
2956   /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
2957    * long as the caller isn't expecting success.
2958    */
2959 #if HTTP_PARSER_STRICT
2960   if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
2961 #else
2962   if (err_expected != err) {
2963 #endif
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);
2966     abort();
2967   }
2968 }
2969
2970 void
2971 test_header_overflow_error (int req)
2972 {
2973   http_parser parser;
2974   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2975   size_t parsed;
2976   const char *buf;
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));
2980
2981   buf = "header-key: header-value\r\n";
2982   size_t buflen = strlen(buf);
2983
2984   int i;
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);
2990       return;
2991     }
2992   }
2993
2994   fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
2995   abort();
2996 }
2997
2998 static void
2999 test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
3000 {
3001   http_parser parser;
3002   http_parser_init(&parser, HTTP_RESPONSE);
3003   http_parser_execute(&parser, &settings_null, buf, buflen);
3004
3005   if (expect_ok)
3006     assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
3007   else
3008     assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
3009 }
3010
3011 void
3012 test_header_content_length_overflow_error (void)
3013 {
3014 #define X(size)                                                               \
3015   "HTTP/1.1 200 OK\r\n"                                                       \
3016   "Content-Length: " #size "\r\n"                                             \
3017   "\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   */
3021 #undef X
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 */
3025 }
3026
3027 void
3028 test_chunk_content_length_overflow_error (void)
3029 {
3030 #define X(size)                                                               \
3031     "HTTP/1.1 200 OK\r\n"                                                     \
3032     "Transfer-Encoding: chunked\r\n"                                          \
3033     "\r\n"                                                                    \
3034     #size "\r\n"                                                              \
3035     "..."
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   */
3039 #undef X
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 */
3043 }
3044
3045 void
3046 test_no_overflow_long_body (int req, size_t length)
3047 {
3048   http_parser parser;
3049   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3050   size_t parsed;
3051   size_t i;
3052   char buf1[3000];
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)
3057     goto err;
3058
3059   for (i = 0; i < length; i++) {
3060     char foo = 'a';
3061     parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
3062     if (parsed != 1)
3063       goto err;
3064   }
3065
3066   parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
3067   if (parsed != buf1len) goto err;
3068   return;
3069
3070  err:
3071   fprintf(stderr,
3072           "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
3073           req ? "REQUEST" : "RESPONSE",
3074           (unsigned long)length);
3075   abort();
3076 }
3077
3078 void
3079 test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
3080 {
3081   int message_count = count_parsed_messages(3, r1, r2, r3);
3082
3083   char total[ strlen(r1->raw)
3084             + strlen(r2->raw)
3085             + strlen(r3->raw)
3086             + 1
3087             ];
3088   total[0] = '\0';
3089
3090   strcat(total, r1->raw);
3091   strcat(total, r2->raw);
3092   strcat(total, r3->raw);
3093
3094   parser_init(r1->type);
3095
3096   size_t read;
3097
3098   read = parse(total, strlen(total));
3099
3100   if (parser->upgrade) {
3101     upgrade_message_fix(total, read, 3, r1, r2, r3);
3102     goto test;
3103   }
3104
3105   if (read != strlen(total)) {
3106     print_error(total, read);
3107     abort();
3108   }
3109
3110   read = parse(NULL, 0);
3111
3112   if (read != 0) {
3113     print_error(total, read);
3114     abort();
3115   }
3116
3117 test:
3118
3119   if (message_count != num_messages) {
3120     fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
3121     abort();
3122   }
3123
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();
3127
3128   parser_free();
3129 }
3130
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
3134  */
3135 void
3136 test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
3137 {
3138   char total[80*1024] = "\0";
3139   char buf1[80*1024] = "\0";
3140   char buf2[80*1024] = "\0";
3141   char buf3[80*1024] = "\0";
3142
3143   strcat(total, r1->raw);
3144   strcat(total, r2->raw);
3145   strcat(total, r3->raw);
3146
3147   size_t read;
3148
3149   int total_len = strlen(total);
3150
3151   int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
3152   int ops = 0 ;
3153
3154   size_t buf1_len, buf2_len, buf3_len;
3155   int message_count = count_parsed_messages(3, r1, r2, r3);
3156
3157   int i,j,type_both;
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 ++ ) {
3161
3162         if (ops % 1000 == 0)  {
3163           printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
3164           fflush(stdout);
3165         }
3166         ops += 1;
3167
3168         parser_init(type_both ? HTTP_BOTH : r1->type);
3169
3170         buf1_len = i;
3171         strlncpy(buf1, sizeof(buf1), total, buf1_len);
3172         buf1[buf1_len] = 0;
3173
3174         buf2_len = j - i;
3175         strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
3176         buf2[buf2_len] = 0;
3177
3178         buf3_len = total_len - j;
3179         strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
3180         buf3[buf3_len] = 0;
3181
3182         read = parse(buf1, buf1_len);
3183
3184         if (parser->upgrade) goto test;
3185
3186         if (read != buf1_len) {
3187           print_error(buf1, read);
3188           goto error;
3189         }
3190
3191         read += parse(buf2, buf2_len);
3192
3193         if (parser->upgrade) goto test;
3194
3195         if (read != buf1_len + buf2_len) {
3196           print_error(buf2, read);
3197           goto error;
3198         }
3199
3200         read += parse(buf3, buf3_len);
3201
3202         if (parser->upgrade) goto test;
3203
3204         if (read != buf1_len + buf2_len + buf3_len) {
3205           print_error(buf3, read);
3206           goto error;
3207         }
3208
3209         parse(NULL, 0);
3210
3211 test:
3212         if (parser->upgrade) {
3213           upgrade_message_fix(total, read, 3, r1, r2, r3);
3214         }
3215
3216         if (message_count != num_messages) {
3217           fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3218             message_count, num_messages);
3219           goto error;
3220         }
3221
3222         if (!message_eq(0, r1)) {
3223           fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
3224           goto error;
3225         }
3226
3227         if (message_count > 1 && !message_eq(1, r2)) {
3228           fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
3229           goto error;
3230         }
3231
3232         if (message_count > 2 && !message_eq(2, r3)) {
3233           fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
3234           goto error;
3235         }
3236
3237         parser_free();
3238       }
3239     }
3240   }
3241   puts("\b\b\b\b100%");
3242   return;
3243
3244  error:
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);
3249   abort();
3250 }
3251
3252 // user required to free the result
3253 // string terminated by \0
3254 char *
3255 create_large_chunked_message (int body_size_in_kb, const char* headers)
3256 {
3257   int i;
3258   size_t wrote = 0;
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);
3262
3263   memcpy(buf, headers, headers_len);
3264   wrote += headers_len;
3265
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);
3269     wrote += 5;
3270     memset(buf + wrote, 'C', 1024);
3271     wrote += 1024;
3272     strcpy(buf + wrote, "\r\n");
3273     wrote += 2;
3274   }
3275
3276   memcpy(buf + wrote, "0\r\n\r\n", 6);
3277   wrote += 6;
3278   assert(wrote == bufsize);
3279
3280   return buf;
3281 }
3282
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. */
3285 void
3286 test_message_pause (const struct message *msg)
3287 {
3288   char *buf = (char*) msg->raw;
3289   size_t buflen = strlen(msg->raw);
3290   size_t nread;
3291
3292   parser_init(msg->type);
3293
3294   do {
3295     nread = parse_pause(buf, buflen);
3296
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 &&
3300         msg->upgrade &&
3301         parser->upgrade) {
3302       messages[0].upgrade = buf + nread;
3303       goto test;
3304     }
3305
3306     if (nread < buflen) {
3307
3308       // Not much do to if we failed a strict-mode check
3309       if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
3310         parser_free();
3311         return;
3312       }
3313
3314       assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
3315     }
3316
3317     buf += nread;
3318     buflen -= nread;
3319     http_parser_pause(parser, 0);
3320   } while (buflen > 0);
3321
3322   nread = parse_pause(NULL, 0);
3323   assert (nread == 0);
3324
3325 test:
3326   if (num_messages != 1) {
3327     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
3328     abort();
3329   }
3330
3331   if(!message_eq(0, msg)) abort();
3332
3333   parser_free();
3334 }
3335
3336 int
3337 main (void)
3338 {
3339   parser = NULL;
3340   int i, j, k;
3341   int request_count;
3342   int response_count;
3343   unsigned long version;
3344   unsigned major;
3345   unsigned minor;
3346   unsigned patch;
3347
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);
3353
3354   printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
3355
3356   for (request_count = 0; requests[request_count].name; request_count++);
3357   for (response_count = 0; responses[response_count].name; response_count++);
3358
3359   //// API
3360   test_preserve_data();
3361   test_parse_url();
3362   test_method_str();
3363
3364   //// OVERFLOW CONDITIONS
3365
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);
3369
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);
3373
3374   test_header_content_length_overflow_error();
3375   test_chunk_content_length_overflow_error();
3376
3377   //// RESPONSES
3378
3379   for (i = 0; i < response_count; i++) {
3380     test_message(&responses[i]);
3381   }
3382
3383   for (i = 0; i < response_count; i++) {
3384     test_message_pause(&responses[i]);
3385   }
3386
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]);
3393       }
3394     }
3395   }
3396
3397   test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
3398   test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
3399
3400   // test very large chunked response
3401   {
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"
3406       "\r\n");
3407     struct message large_chunked =
3408       {.name= "large chunked"
3409       ,.type= HTTP_RESPONSE
3410       ,.raw= msg
3411       ,.should_keep_alive= FALSE
3412       ,.message_complete_on_eof= FALSE
3413       ,.http_major= 1
3414       ,.http_minor= 0
3415       ,.status_code= 200
3416       ,.response_status= "OK"
3417       ,.num_headers= 2
3418       ,.headers=
3419         { { "Transfer-Encoding", "chunked" }
3420         , { "Content-Type", "text/plain" }
3421         }
3422       ,.body_size= 31337*1024
3423       };
3424     test_message_count_body(&large_chunked);
3425     free(msg);
3426   }
3427
3428
3429
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]
3434            );
3435
3436   printf("response scan 2/2      ");
3437   test_scan( &responses[BONJOUR_MADAME_FR]
3438            , &responses[UNDERSTORE_HEADER_KEY]
3439            , &responses[NO_CARRIAGE_RET]
3440            );
3441
3442   puts("responses okay");
3443
3444
3445   /// REQUESTS
3446
3447   test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
3448
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"
3453               "\r\n"
3454               "fooba",
3455               HPE_OK);
3456
3457   static const char *all_methods[] = {
3458     "DELETE",
3459     "GET",
3460     "HEAD",
3461     "POST",
3462     "PUT",
3463     //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
3464     "OPTIONS",
3465     "TRACE",
3466     "COPY",
3467     "LOCK",
3468     "MKCOL",
3469     "MOVE",
3470     "PROPFIND",
3471     "PROPPATCH",
3472     "UNLOCK",
3473     "REPORT",
3474     "MKACTIVITY",
3475     "CHECKOUT",
3476     "MERGE",
3477     "M-SEARCH",
3478     "NOTIFY",
3479     "SUBSCRIBE",
3480     "UNSUBSCRIBE",
3481     "PATCH",
3482     0 };
3483   const char **this_method;
3484   for (this_method = all_methods; *this_method; this_method++) {
3485     char buf[200];
3486     sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3487     test_simple(buf, HPE_OK);
3488   }
3489
3490   static const char *bad_methods[] = {
3491       "ASDF",
3492       "C******",
3493       "COLA",
3494       "GEM",
3495       "GETA",
3496       "M****",
3497       "MKCOLA",
3498       "PROPPATCHA",
3499       "PUN",
3500       "PX",
3501       "SA",
3502       "hello world",
3503       0 };
3504   for (this_method = bad_methods; *this_method; this_method++) {
3505     char buf[200];
3506     sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3507     test_simple(buf, HPE_INVALID_METHOD);
3508   }
3509
3510   // illegal header field name line folding
3511   test_simple("GET / HTTP/1.1\r\n"
3512               "name\r\n"
3513               " : value\r\n"
3514               "\r\n",
3515               HPE_INVALID_HEADER_TOKEN);
3516
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"
3549     "\tRA==\r\n"
3550     "\t-----END CERTIFICATE-----\r\n"
3551     "\r\n";
3552   test_simple(dumbfuck2, HPE_OK);
3553
3554 #if 0
3555   // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
3556   // until EOF.
3557   //
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"
3561                                            "Accept: */*\r\n"
3562                                            "\r\n"
3563                                            "HELLO";
3564   test_simple(bad_get_no_headers_no_body, 0);
3565 #endif
3566   /* TODO sending junk and large headers gets rejected */
3567
3568
3569   /* check to make sure our predefined requests are okay */
3570   for (i = 0; requests[i].name; i++) {
3571     test_message(&requests[i]);
3572   }
3573
3574   for (i = 0; i < request_count; i++) {
3575     test_message_pause(&requests[i]);
3576   }
3577
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]);
3584       }
3585     }
3586   }
3587
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]
3592            );
3593
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]
3598            );
3599
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]
3604            );
3605
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]
3610            );
3611
3612   puts("requests okay");
3613
3614   return 0;
3615 }