• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 strlncpy
31 #endif  /* defined(__APPLE__) */
32 
33 #undef TRUE
34 #define TRUE 1
35 #undef FALSE
36 #define FALSE 0
37 
38 #define MAX_HEADERS 13
39 #define MAX_ELEMENT_SIZE 2048
40 #define MAX_CHUNKS 16
41 
42 #define MIN(a,b) ((a) < (b) ? (a) : (b))
43 
44 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
45 
46 static http_parser parser;
47 
48 struct message {
49   const char *name; // for debugging purposes
50   const char *raw;
51   enum http_parser_type type;
52   enum http_method method;
53   int status_code;
54   char response_status[MAX_ELEMENT_SIZE];
55   char request_path[MAX_ELEMENT_SIZE];
56   char request_url[MAX_ELEMENT_SIZE];
57   char fragment[MAX_ELEMENT_SIZE];
58   char query_string[MAX_ELEMENT_SIZE];
59   char body[MAX_ELEMENT_SIZE];
60   size_t body_size;
61   const char *host;
62   const char *userinfo;
63   uint16_t port;
64   int num_headers;
65   enum { NONE=0, FIELD, VALUE } last_header_element;
66   char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
67   int should_keep_alive;
68 
69   int num_chunks;
70   int num_chunks_complete;
71   int chunk_lengths[MAX_CHUNKS];
72 
73   const char *upgrade; // upgraded body
74 
75   unsigned short http_major;
76   unsigned short http_minor;
77   uint64_t content_length;
78 
79   int message_begin_cb_called;
80   int headers_complete_cb_called;
81   int message_complete_cb_called;
82   int status_cb_called;
83   int message_complete_on_eof;
84   int body_is_final;
85   int allow_chunked_length;
86 };
87 
88 static int currently_parsing_eof;
89 
90 static struct message messages[5];
91 static int num_messages;
92 static http_parser_settings *current_pause_parser;
93 
94 /* * R E Q U E S T S * */
95 const struct message requests[] =
96 #define CURL_GET 0
97 { {.name= "curl get"
98   ,.type= HTTP_REQUEST
99   ,.raw= "GET /test HTTP/1.1\r\n"
100          "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"
101          "Host: 0.0.0.0=5000\r\n"
102          "Accept: */*\r\n"
103          "\r\n"
104   ,.should_keep_alive= TRUE
105   ,.message_complete_on_eof= FALSE
106   ,.http_major= 1
107   ,.http_minor= 1
108   ,.method= HTTP_GET
109   ,.query_string= ""
110   ,.fragment= ""
111   ,.request_path= "/test"
112   ,.request_url= "/test"
113   ,.content_length= -1
114   ,.num_headers= 3
115   ,.headers=
116     { { "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" }
117     , { "Host", "0.0.0.0=5000" }
118     , { "Accept", "*/*" }
119     }
120   ,.body= ""
121   }
122 
123 #define FIREFOX_GET 1
124 , {.name= "firefox get"
125   ,.type= HTTP_REQUEST
126   ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
127          "Host: 0.0.0.0=5000\r\n"
128          "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
129          "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
130          "Accept-Language: en-us,en;q=0.5\r\n"
131          "Accept-Encoding: gzip,deflate\r\n"
132          "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
133          "Keep-Alive: 300\r\n"
134          "Connection: keep-alive\r\n"
135          "\r\n"
136   ,.should_keep_alive= TRUE
137   ,.message_complete_on_eof= FALSE
138   ,.http_major= 1
139   ,.http_minor= 1
140   ,.method= HTTP_GET
141   ,.query_string= ""
142   ,.fragment= ""
143   ,.request_path= "/favicon.ico"
144   ,.request_url= "/favicon.ico"
145   ,.content_length= -1
146   ,.num_headers= 8
147   ,.headers=
148     { { "Host", "0.0.0.0=5000" }
149     , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
150     , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
151     , { "Accept-Language", "en-us,en;q=0.5" }
152     , { "Accept-Encoding", "gzip,deflate" }
153     , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
154     , { "Keep-Alive", "300" }
155     , { "Connection", "keep-alive" }
156     }
157   ,.body= ""
158   }
159 
160 #define DUMBLUCK 2
161 , {.name= "dumbluck"
162   ,.type= HTTP_REQUEST
163   ,.raw= "GET /dumbluck HTTP/1.1\r\n"
164          "aaaaaaaaaaaaa:++++++++++\r\n"
165          "\r\n"
166   ,.should_keep_alive= TRUE
167   ,.message_complete_on_eof= FALSE
168   ,.http_major= 1
169   ,.http_minor= 1
170   ,.method= HTTP_GET
171   ,.query_string= ""
172   ,.fragment= ""
173   ,.request_path= "/dumbluck"
174   ,.request_url= "/dumbluck"
175   ,.content_length= -1
176   ,.num_headers= 1
177   ,.headers=
178     { { "aaaaaaaaaaaaa",  "++++++++++" }
179     }
180   ,.body= ""
181   }
182 
183 #define FRAGMENT_IN_URI 3
184 , {.name= "fragment in url"
185   ,.type= HTTP_REQUEST
186   ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
187          "\r\n"
188   ,.should_keep_alive= TRUE
189   ,.message_complete_on_eof= FALSE
190   ,.http_major= 1
191   ,.http_minor= 1
192   ,.method= HTTP_GET
193   ,.query_string= "page=1"
194   ,.fragment= "posts-17408"
195   ,.request_path= "/forums/1/topics/2375"
196   /* XXX request url does include fragment? */
197   ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
198   ,.content_length= -1
199   ,.num_headers= 0
200   ,.body= ""
201   }
202 
203 #define GET_NO_HEADERS_NO_BODY 4
204 , {.name= "get no headers no body"
205   ,.type= HTTP_REQUEST
206   ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
207          "\r\n"
208   ,.should_keep_alive= TRUE
209   ,.message_complete_on_eof= FALSE /* would need Connection: close */
210   ,.http_major= 1
211   ,.http_minor= 1
212   ,.method= HTTP_GET
213   ,.query_string= ""
214   ,.fragment= ""
215   ,.request_path= "/get_no_headers_no_body/world"
216   ,.request_url= "/get_no_headers_no_body/world"
217   ,.content_length= -1
218   ,.num_headers= 0
219   ,.body= ""
220   }
221 
222 #define GET_ONE_HEADER_NO_BODY 5
223 , {.name= "get one header no body"
224   ,.type= HTTP_REQUEST
225   ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
226          "Accept: */*\r\n"
227          "\r\n"
228   ,.should_keep_alive= TRUE
229   ,.message_complete_on_eof= FALSE /* would need Connection: close */
230   ,.http_major= 1
231   ,.http_minor= 1
232   ,.method= HTTP_GET
233   ,.query_string= ""
234   ,.fragment= ""
235   ,.request_path= "/get_one_header_no_body"
236   ,.request_url= "/get_one_header_no_body"
237   ,.content_length= -1
238   ,.num_headers= 1
239   ,.headers=
240     { { "Accept" , "*/*" }
241     }
242   ,.body= ""
243   }
244 
245 #define GET_FUNKY_CONTENT_LENGTH 6
246 , {.name= "get funky content length body hello"
247   ,.type= HTTP_REQUEST
248   ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
249          "conTENT-Length: 5\r\n"
250          "\r\n"
251          "HELLO"
252   ,.should_keep_alive= FALSE
253   ,.message_complete_on_eof= FALSE
254   ,.http_major= 1
255   ,.http_minor= 0
256   ,.method= HTTP_GET
257   ,.query_string= ""
258   ,.fragment= ""
259   ,.request_path= "/get_funky_content_length_body_hello"
260   ,.request_url= "/get_funky_content_length_body_hello"
261   ,.content_length= 5
262   ,.num_headers= 1
263   ,.headers=
264     { { "conTENT-Length" , "5" }
265     }
266   ,.body= "HELLO"
267   }
268 
269 #define POST_IDENTITY_BODY_WORLD 7
270 , {.name= "post identity body world"
271   ,.type= HTTP_REQUEST
272   ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
273          "Accept: */*\r\n"
274          "Content-Length: 5\r\n"
275          "\r\n"
276          "World"
277   ,.should_keep_alive= TRUE
278   ,.message_complete_on_eof= FALSE
279   ,.http_major= 1
280   ,.http_minor= 1
281   ,.method= HTTP_POST
282   ,.query_string= "q=search"
283   ,.fragment= "hey"
284   ,.request_path= "/post_identity_body_world"
285   ,.request_url= "/post_identity_body_world?q=search#hey"
286   ,.content_length= 5
287   ,.num_headers= 2
288   ,.headers=
289     { { "Accept", "*/*" }
290     , { "Content-Length", "5" }
291     }
292   ,.body= "World"
293   }
294 
295 #define POST_CHUNKED_ALL_YOUR_BASE 8
296 , {.name= "post - chunked body: all your base are belong to us"
297   ,.type= HTTP_REQUEST
298   ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
299          "Transfer-Encoding: chunked\r\n"
300          "\r\n"
301          "1e\r\nall your base are belong to us\r\n"
302          "0\r\n"
303          "\r\n"
304   ,.should_keep_alive= TRUE
305   ,.message_complete_on_eof= FALSE
306   ,.http_major= 1
307   ,.http_minor= 1
308   ,.method= HTTP_POST
309   ,.query_string= ""
310   ,.fragment= ""
311   ,.request_path= "/post_chunked_all_your_base"
312   ,.request_url= "/post_chunked_all_your_base"
313   ,.content_length= -1
314   ,.num_headers= 1
315   ,.headers=
316     { { "Transfer-Encoding" , "chunked" }
317     }
318   ,.body= "all your base are belong to us"
319   ,.num_chunks_complete= 2
320   ,.chunk_lengths= { 0x1e }
321   }
322 
323 #define TWO_CHUNKS_MULT_ZERO_END 9
324 , {.name= "two chunks ; triple zero ending"
325   ,.type= HTTP_REQUEST
326   ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
327          "Transfer-Encoding: chunked\r\n"
328          "\r\n"
329          "5\r\nhello\r\n"
330          "6\r\n world\r\n"
331          "000\r\n"
332          "\r\n"
333   ,.should_keep_alive= TRUE
334   ,.message_complete_on_eof= FALSE
335   ,.http_major= 1
336   ,.http_minor= 1
337   ,.method= HTTP_POST
338   ,.query_string= ""
339   ,.fragment= ""
340   ,.request_path= "/two_chunks_mult_zero_end"
341   ,.request_url= "/two_chunks_mult_zero_end"
342   ,.content_length= -1
343   ,.num_headers= 1
344   ,.headers=
345     { { "Transfer-Encoding", "chunked" }
346     }
347   ,.body= "hello world"
348   ,.num_chunks_complete= 3
349   ,.chunk_lengths= { 5, 6 }
350   }
351 
352 #define CHUNKED_W_TRAILING_HEADERS 10
353 , {.name= "chunked with trailing headers. blech."
354   ,.type= HTTP_REQUEST
355   ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
356          "Transfer-Encoding: chunked\r\n"
357          "\r\n"
358          "5\r\nhello\r\n"
359          "6\r\n world\r\n"
360          "0\r\n"
361          "Vary: *\r\n"
362          "Content-Type: text/plain\r\n"
363          "\r\n"
364   ,.should_keep_alive= TRUE
365   ,.message_complete_on_eof= FALSE
366   ,.http_major= 1
367   ,.http_minor= 1
368   ,.method= HTTP_POST
369   ,.query_string= ""
370   ,.fragment= ""
371   ,.request_path= "/chunked_w_trailing_headers"
372   ,.request_url= "/chunked_w_trailing_headers"
373   ,.content_length= -1
374   ,.num_headers= 3
375   ,.headers=
376     { { "Transfer-Encoding",  "chunked" }
377     , { "Vary", "*" }
378     , { "Content-Type", "text/plain" }
379     }
380   ,.body= "hello world"
381   ,.num_chunks_complete= 3
382   ,.chunk_lengths= { 5, 6 }
383   }
384 
385 #define CHUNKED_W_NONSENSE_AFTER_LENGTH 11
386 , {.name= "with nonsense after the length"
387   ,.type= HTTP_REQUEST
388   ,.raw= "POST /chunked_w_nonsense_after_length HTTP/1.1\r\n"
389          "Transfer-Encoding: chunked\r\n"
390          "\r\n"
391          "5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
392          "6; blahblah; blah\r\n world\r\n"
393          "0\r\n"
394          "\r\n"
395   ,.should_keep_alive= TRUE
396   ,.message_complete_on_eof= FALSE
397   ,.http_major= 1
398   ,.http_minor= 1
399   ,.method= HTTP_POST
400   ,.query_string= ""
401   ,.fragment= ""
402   ,.request_path= "/chunked_w_nonsense_after_length"
403   ,.request_url= "/chunked_w_nonsense_after_length"
404   ,.content_length= -1
405   ,.num_headers= 1
406   ,.headers=
407     { { "Transfer-Encoding", "chunked" }
408     }
409   ,.body= "hello world"
410   ,.num_chunks_complete= 3
411   ,.chunk_lengths= { 5, 6 }
412   }
413 
414 #define WITH_QUOTES 12
415 , {.name= "with quotes"
416   ,.type= HTTP_REQUEST
417   ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
418   ,.should_keep_alive= TRUE
419   ,.message_complete_on_eof= FALSE
420   ,.http_major= 1
421   ,.http_minor= 1
422   ,.method= HTTP_GET
423   ,.query_string= "foo=\"bar\""
424   ,.fragment= ""
425   ,.request_path= "/with_\"stupid\"_quotes"
426   ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
427   ,.content_length= -1
428   ,.num_headers= 0
429   ,.headers= { }
430   ,.body= ""
431   }
432 
433 #define APACHEBENCH_GET 13
434 /* The server receiving this request SHOULD NOT wait for EOF
435  * to know that content-length == 0.
436  * How to represent this in a unit test? message_complete_on_eof
437  * Compare with NO_CONTENT_LENGTH_RESPONSE.
438  */
439 , {.name = "apachebench get"
440   ,.type= HTTP_REQUEST
441   ,.raw= "GET /test HTTP/1.0\r\n"
442          "Host: 0.0.0.0:5000\r\n"
443          "User-Agent: ApacheBench/2.3\r\n"
444          "Accept: */*\r\n\r\n"
445   ,.should_keep_alive= FALSE
446   ,.message_complete_on_eof= FALSE
447   ,.http_major= 1
448   ,.http_minor= 0
449   ,.method= HTTP_GET
450   ,.query_string= ""
451   ,.fragment= ""
452   ,.request_path= "/test"
453   ,.request_url= "/test"
454   ,.content_length= -1
455   ,.num_headers= 3
456   ,.headers= { { "Host", "0.0.0.0:5000" }
457              , { "User-Agent", "ApacheBench/2.3" }
458              , { "Accept", "*/*" }
459              }
460   ,.body= ""
461   }
462 
463 #define QUERY_URL_WITH_QUESTION_MARK_GET 14
464 /* Some clients include '?' characters in query strings.
465  */
466 , {.name = "query url with question mark"
467   ,.type= HTTP_REQUEST
468   ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
469   ,.should_keep_alive= TRUE
470   ,.message_complete_on_eof= FALSE
471   ,.http_major= 1
472   ,.http_minor= 1
473   ,.method= HTTP_GET
474   ,.query_string= "foo=bar?baz"
475   ,.fragment= ""
476   ,.request_path= "/test.cgi"
477   ,.request_url= "/test.cgi?foo=bar?baz"
478   ,.content_length= -1
479   ,.num_headers= 0
480   ,.headers= {}
481   ,.body= ""
482   }
483 
484 #define PREFIX_NEWLINE_GET 15
485 /* Some clients, especially after a POST in a keep-alive connection,
486  * will send an extra CRLF before the next request
487  */
488 , {.name = "newline prefix get"
489   ,.type= HTTP_REQUEST
490   ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
491   ,.should_keep_alive= TRUE
492   ,.message_complete_on_eof= FALSE
493   ,.http_major= 1
494   ,.http_minor= 1
495   ,.method= HTTP_GET
496   ,.query_string= ""
497   ,.fragment= ""
498   ,.request_path= "/test"
499   ,.request_url= "/test"
500   ,.content_length= -1
501   ,.num_headers= 0
502   ,.headers= { }
503   ,.body= ""
504   }
505 
506 #define UPGRADE_REQUEST 16
507 , {.name = "upgrade request"
508   ,.type= HTTP_REQUEST
509   ,.raw= "GET /demo HTTP/1.1\r\n"
510          "Host: example.com\r\n"
511          "Connection: Upgrade\r\n"
512          "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
513          "Sec-WebSocket-Protocol: sample\r\n"
514          "Upgrade: WebSocket\r\n"
515          "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
516          "Origin: http://example.com\r\n"
517          "\r\n"
518          "Hot diggity dogg"
519   ,.should_keep_alive= TRUE
520   ,.message_complete_on_eof= FALSE
521   ,.http_major= 1
522   ,.http_minor= 1
523   ,.method= HTTP_GET
524   ,.query_string= ""
525   ,.fragment= ""
526   ,.request_path= "/demo"
527   ,.request_url= "/demo"
528   ,.content_length= -1
529   ,.num_headers= 7
530   ,.upgrade="Hot diggity dogg"
531   ,.headers= { { "Host", "example.com" }
532              , { "Connection", "Upgrade" }
533              , { "Sec-WebSocket-Key2", "12998 5 Y3 1  .P00" }
534              , { "Sec-WebSocket-Protocol", "sample" }
535              , { "Upgrade", "WebSocket" }
536              , { "Sec-WebSocket-Key1", "4 @1  46546xW%0l 1 5" }
537              , { "Origin", "http://example.com" }
538              }
539   ,.body= ""
540   }
541 
542 #define CONNECT_REQUEST 17
543 , {.name = "connect request"
544   ,.type= HTTP_REQUEST
545   ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
546          "User-agent: Mozilla/1.1N\r\n"
547          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
548          "\r\n"
549          "some data\r\n"
550          "and yet even more data"
551   ,.should_keep_alive= FALSE
552   ,.message_complete_on_eof= FALSE
553   ,.http_major= 1
554   ,.http_minor= 0
555   ,.method= HTTP_CONNECT
556   ,.query_string= ""
557   ,.fragment= ""
558   ,.request_path= ""
559   ,.request_url= "0-home0.netscape.com:443"
560   ,.content_length= -1
561   ,.num_headers= 2
562   ,.upgrade="some data\r\nand yet even more data"
563   ,.headers= { { "User-agent", "Mozilla/1.1N" }
564              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
565              }
566   ,.body= ""
567   }
568 
569 #define REPORT_REQ 18
570 , {.name= "report request"
571   ,.type= HTTP_REQUEST
572   ,.raw= "REPORT /test HTTP/1.1\r\n"
573          "\r\n"
574   ,.should_keep_alive= TRUE
575   ,.message_complete_on_eof= FALSE
576   ,.http_major= 1
577   ,.http_minor= 1
578   ,.method= HTTP_REPORT
579   ,.query_string= ""
580   ,.fragment= ""
581   ,.request_path= "/test"
582   ,.request_url= "/test"
583   ,.content_length= -1
584   ,.num_headers= 0
585   ,.headers= {}
586   ,.body= ""
587   }
588 
589 #define NO_HTTP_VERSION 19
590 , {.name= "request with no http version"
591   ,.type= HTTP_REQUEST
592   ,.raw= "GET /\r\n"
593          "\r\n"
594   ,.should_keep_alive= FALSE
595   ,.message_complete_on_eof= FALSE
596   ,.http_major= 0
597   ,.http_minor= 9
598   ,.method= HTTP_GET
599   ,.query_string= ""
600   ,.fragment= ""
601   ,.request_path= "/"
602   ,.request_url= "/"
603   ,.content_length= -1
604   ,.num_headers= 0
605   ,.headers= {}
606   ,.body= ""
607   }
608 
609 #define MSEARCH_REQ 20
610 , {.name= "m-search request"
611   ,.type= HTTP_REQUEST
612   ,.raw= "M-SEARCH * HTTP/1.1\r\n"
613          "HOST: 239.255.255.250:1900\r\n"
614          "MAN: \"ssdp:discover\"\r\n"
615          "ST: \"ssdp:all\"\r\n"
616          "\r\n"
617   ,.should_keep_alive= TRUE
618   ,.message_complete_on_eof= FALSE
619   ,.http_major= 1
620   ,.http_minor= 1
621   ,.method= HTTP_MSEARCH
622   ,.query_string= ""
623   ,.fragment= ""
624   ,.request_path= "*"
625   ,.request_url= "*"
626   ,.content_length= -1
627   ,.num_headers= 3
628   ,.headers= { { "HOST", "239.255.255.250:1900" }
629              , { "MAN", "\"ssdp:discover\"" }
630              , { "ST", "\"ssdp:all\"" }
631              }
632   ,.body= ""
633   }
634 
635 #define LINE_FOLDING_IN_HEADER 21
636 , {.name= "line folding in header value"
637   ,.type= HTTP_REQUEST
638   ,.raw= "GET / HTTP/1.1\r\n"
639          "Line1:   abc\r\n"
640          "\tdef\r\n"
641          " ghi\r\n"
642          "\t\tjkl\r\n"
643          "  mno \r\n"
644          "\t \tqrs\r\n"
645          "Line2: \t line2\t\r\n"
646          "Line3:\r\n"
647          " line3\r\n"
648          "Line4: \r\n"
649          " \r\n"
650          "Connection:\r\n"
651          " close\r\n"
652          "\r\n"
653   ,.should_keep_alive= FALSE
654   ,.message_complete_on_eof= FALSE
655   ,.http_major= 1
656   ,.http_minor= 1
657   ,.method= HTTP_GET
658   ,.query_string= ""
659   ,.fragment= ""
660   ,.request_path= "/"
661   ,.request_url= "/"
662   ,.content_length= -1
663   ,.num_headers= 5
664   ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl  mno \t \tqrs" }
665              , { "Line2", "line2\t" }
666              , { "Line3", "line3" }
667              , { "Line4", "" }
668              , { "Connection", "close" },
669              }
670   ,.body= ""
671   }
672 
673 
674 #define QUERY_TERMINATED_HOST 22
675 , {.name= "host terminated by a query string"
676   ,.type= HTTP_REQUEST
677   ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
678          "\r\n"
679   ,.should_keep_alive= TRUE
680   ,.message_complete_on_eof= FALSE
681   ,.http_major= 1
682   ,.http_minor= 1
683   ,.method= HTTP_GET
684   ,.query_string= "hail=all"
685   ,.fragment= ""
686   ,.request_path= ""
687   ,.request_url= "http://hypnotoad.org?hail=all"
688   ,.host= "hypnotoad.org"
689   ,.content_length= -1
690   ,.num_headers= 0
691   ,.headers= { }
692   ,.body= ""
693   }
694 
695 #define QUERY_TERMINATED_HOSTPORT 23
696 , {.name= "host:port terminated by a query string"
697   ,.type= HTTP_REQUEST
698   ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
699          "\r\n"
700   ,.should_keep_alive= TRUE
701   ,.message_complete_on_eof= FALSE
702   ,.http_major= 1
703   ,.http_minor= 1
704   ,.method= HTTP_GET
705   ,.query_string= "hail=all"
706   ,.fragment= ""
707   ,.request_path= ""
708   ,.request_url= "http://hypnotoad.org:1234?hail=all"
709   ,.host= "hypnotoad.org"
710   ,.port= 1234
711   ,.content_length= -1
712   ,.num_headers= 0
713   ,.headers= { }
714   ,.body= ""
715   }
716 
717 #define SPACE_TERMINATED_HOSTPORT 24
718 , {.name= "host:port terminated by a space"
719   ,.type= HTTP_REQUEST
720   ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
721          "\r\n"
722   ,.should_keep_alive= TRUE
723   ,.message_complete_on_eof= FALSE
724   ,.http_major= 1
725   ,.http_minor= 1
726   ,.method= HTTP_GET
727   ,.query_string= ""
728   ,.fragment= ""
729   ,.request_path= ""
730   ,.request_url= "http://hypnotoad.org:1234"
731   ,.host= "hypnotoad.org"
732   ,.port= 1234
733   ,.content_length= -1
734   ,.num_headers= 0
735   ,.headers= { }
736   ,.body= ""
737   }
738 
739 #define PATCH_REQ 25
740 , {.name = "PATCH request"
741   ,.type= HTTP_REQUEST
742   ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
743          "Host: www.example.com\r\n"
744          "Content-Type: application/example\r\n"
745          "If-Match: \"e0023aa4e\"\r\n"
746          "Content-Length: 10\r\n"
747          "\r\n"
748          "cccccccccc"
749   ,.should_keep_alive= TRUE
750   ,.message_complete_on_eof= FALSE
751   ,.http_major= 1
752   ,.http_minor= 1
753   ,.method= HTTP_PATCH
754   ,.query_string= ""
755   ,.fragment= ""
756   ,.request_path= "/file.txt"
757   ,.request_url= "/file.txt"
758   ,.content_length= 10
759   ,.num_headers= 4
760   ,.headers= { { "Host", "www.example.com" }
761              , { "Content-Type", "application/example" }
762              , { "If-Match", "\"e0023aa4e\"" }
763              , { "Content-Length", "10" }
764              }
765   ,.body= "cccccccccc"
766   }
767 
768 #define CONNECT_CAPS_REQUEST 26
769 , {.name = "connect caps request"
770   ,.type= HTTP_REQUEST
771   ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
772          "User-agent: Mozilla/1.1N\r\n"
773          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
774          "\r\n"
775   ,.should_keep_alive= FALSE
776   ,.message_complete_on_eof= FALSE
777   ,.http_major= 1
778   ,.http_minor= 0
779   ,.method= HTTP_CONNECT
780   ,.query_string= ""
781   ,.fragment= ""
782   ,.request_path= ""
783   ,.request_url= "HOME0.NETSCAPE.COM:443"
784   ,.content_length= -1
785   ,.num_headers= 2
786   ,.upgrade=""
787   ,.headers= { { "User-agent", "Mozilla/1.1N" }
788              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
789              }
790   ,.body= ""
791   }
792 
793 #if !HTTP_PARSER_STRICT
794 #define UTF8_PATH_REQ 27
795 , {.name= "utf-8 path request"
796   ,.type= HTTP_REQUEST
797   ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
798          "Host: github.com\r\n"
799          "\r\n"
800   ,.should_keep_alive= TRUE
801   ,.message_complete_on_eof= FALSE
802   ,.http_major= 1
803   ,.http_minor= 1
804   ,.method= HTTP_GET
805   ,.query_string= "q=1"
806   ,.fragment= "narf"
807   ,.request_path= "/δ¶/δt/pope"
808   ,.request_url= "/δ¶/δt/pope?q=1#narf"
809   ,.content_length= -1
810   ,.num_headers= 1
811   ,.headers= { {"Host", "github.com" }
812              }
813   ,.body= ""
814   }
815 
816 #define HOSTNAME_UNDERSCORE 28
817 , {.name = "hostname underscore"
818   ,.type= HTTP_REQUEST
819   ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
820          "User-agent: Mozilla/1.1N\r\n"
821          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
822          "\r\n"
823   ,.should_keep_alive= FALSE
824   ,.message_complete_on_eof= FALSE
825   ,.http_major= 1
826   ,.http_minor= 0
827   ,.method= HTTP_CONNECT
828   ,.query_string= ""
829   ,.fragment= ""
830   ,.request_path= ""
831   ,.request_url= "home_0.netscape.com:443"
832   ,.content_length= -1
833   ,.num_headers= 2
834   ,.upgrade=""
835   ,.headers= { { "User-agent", "Mozilla/1.1N" }
836              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
837              }
838   ,.body= ""
839   }
840 #endif  /* !HTTP_PARSER_STRICT */
841 
842 /* see https://github.com/ry/http-parser/issues/47 */
843 #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
844 , {.name = "eat CRLF between requests, no \"Connection: close\" header"
845   ,.raw= "POST / HTTP/1.1\r\n"
846          "Host: www.example.com\r\n"
847          "Content-Type: application/x-www-form-urlencoded\r\n"
848          "Content-Length: 4\r\n"
849          "\r\n"
850          "q=42\r\n" /* note the trailing CRLF */
851   ,.should_keep_alive= TRUE
852   ,.message_complete_on_eof= FALSE
853   ,.http_major= 1
854   ,.http_minor= 1
855   ,.method= HTTP_POST
856   ,.query_string= ""
857   ,.fragment= ""
858   ,.request_path= "/"
859   ,.request_url= "/"
860   ,.content_length= 4
861   ,.num_headers= 3
862   ,.upgrade= 0
863   ,.headers= { { "Host", "www.example.com" }
864              , { "Content-Type", "application/x-www-form-urlencoded" }
865              , { "Content-Length", "4" }
866              }
867   ,.body= "q=42"
868   }
869 
870 /* see https://github.com/ry/http-parser/issues/47 */
871 #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
872 , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
873   ,.raw= "POST / HTTP/1.1\r\n"
874          "Host: www.example.com\r\n"
875          "Content-Type: application/x-www-form-urlencoded\r\n"
876          "Content-Length: 4\r\n"
877          "Connection: close\r\n"
878          "\r\n"
879          "q=42\r\n" /* note the trailing CRLF */
880   ,.should_keep_alive= FALSE
881   ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
882   ,.http_major= 1
883   ,.http_minor= 1
884   ,.method= HTTP_POST
885   ,.query_string= ""
886   ,.fragment= ""
887   ,.request_path= "/"
888   ,.request_url= "/"
889   ,.content_length= 4
890   ,.num_headers= 4
891   ,.upgrade= 0
892   ,.headers= { { "Host", "www.example.com" }
893              , { "Content-Type", "application/x-www-form-urlencoded" }
894              , { "Content-Length", "4" }
895              , { "Connection", "close" }
896              }
897   ,.body= "q=42"
898   }
899 
900 #define PURGE_REQ 31
901 , {.name = "PURGE request"
902   ,.type= HTTP_REQUEST
903   ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
904          "Host: www.example.com\r\n"
905          "\r\n"
906   ,.should_keep_alive= TRUE
907   ,.message_complete_on_eof= FALSE
908   ,.http_major= 1
909   ,.http_minor= 1
910   ,.method= HTTP_PURGE
911   ,.query_string= ""
912   ,.fragment= ""
913   ,.request_path= "/file.txt"
914   ,.request_url= "/file.txt"
915   ,.content_length= -1
916   ,.num_headers= 1
917   ,.headers= { { "Host", "www.example.com" } }
918   ,.body= ""
919   }
920 
921 #define SEARCH_REQ 32
922 , {.name = "SEARCH request"
923   ,.type= HTTP_REQUEST
924   ,.raw= "SEARCH / HTTP/1.1\r\n"
925          "Host: www.example.com\r\n"
926          "\r\n"
927   ,.should_keep_alive= TRUE
928   ,.message_complete_on_eof= FALSE
929   ,.http_major= 1
930   ,.http_minor= 1
931   ,.method= HTTP_SEARCH
932   ,.query_string= ""
933   ,.fragment= ""
934   ,.request_path= "/"
935   ,.request_url= "/"
936   ,.content_length= -1
937   ,.num_headers= 1
938   ,.headers= { { "Host", "www.example.com" } }
939   ,.body= ""
940   }
941 
942 #define PROXY_WITH_BASIC_AUTH 33
943 , {.name= "host:port and basic_auth"
944   ,.type= HTTP_REQUEST
945   ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
946          "\r\n"
947   ,.should_keep_alive= TRUE
948   ,.message_complete_on_eof= FALSE
949   ,.http_major= 1
950   ,.http_minor= 1
951   ,.method= HTTP_GET
952   ,.fragment= ""
953   ,.request_path= "/toto"
954   ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
955   ,.host= "hypnotoad.org"
956   ,.userinfo= "a%12:b!&*$"
957   ,.port= 1234
958   ,.content_length= -1
959   ,.num_headers= 0
960   ,.headers= { }
961   ,.body= ""
962   }
963 
964 #define LINE_FOLDING_IN_HEADER_WITH_LF 34
965 , {.name= "line folding in header value"
966   ,.type= HTTP_REQUEST
967   ,.raw= "GET / HTTP/1.1\n"
968          "Line1:   abc\n"
969          "\tdef\n"
970          " ghi\n"
971          "\t\tjkl\n"
972          "  mno \n"
973          "\t \tqrs\n"
974          "Line2: \t line2\t\n"
975          "Line3:\n"
976          " line3\n"
977          "Line4: \n"
978          " \n"
979          "Connection:\n"
980          " close\n"
981          "\n"
982   ,.should_keep_alive= FALSE
983   ,.message_complete_on_eof= FALSE
984   ,.http_major= 1
985   ,.http_minor= 1
986   ,.method= HTTP_GET
987   ,.query_string= ""
988   ,.fragment= ""
989   ,.request_path= "/"
990   ,.request_url= "/"
991   ,.content_length= -1
992   ,.num_headers= 5
993   ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl  mno \t \tqrs" }
994              , { "Line2", "line2\t" }
995              , { "Line3", "line3" }
996              , { "Line4", "" }
997              , { "Connection", "close" },
998              }
999   ,.body= ""
1000   }
1001 
1002 #define CONNECTION_MULTI 35
1003 , {.name = "multiple connection header values with folding"
1004   ,.type= HTTP_REQUEST
1005   ,.raw= "GET /demo HTTP/1.1\r\n"
1006          "Host: example.com\r\n"
1007          "Connection: Something,\r\n"
1008          " Upgrade, ,Keep-Alive\r\n"
1009          "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
1010          "Sec-WebSocket-Protocol: sample\r\n"
1011          "Upgrade: WebSocket\r\n"
1012          "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
1013          "Origin: http://example.com\r\n"
1014          "\r\n"
1015          "Hot diggity dogg"
1016   ,.should_keep_alive= TRUE
1017   ,.message_complete_on_eof= FALSE
1018   ,.http_major= 1
1019   ,.http_minor= 1
1020   ,.method= HTTP_GET
1021   ,.query_string= ""
1022   ,.fragment= ""
1023   ,.request_path= "/demo"
1024   ,.request_url= "/demo"
1025   ,.content_length= -1
1026   ,.num_headers= 7
1027   ,.upgrade="Hot diggity dogg"
1028   ,.headers= { { "Host", "example.com" }
1029              , { "Connection", "Something, Upgrade, ,Keep-Alive" }
1030              , { "Sec-WebSocket-Key2", "12998 5 Y3 1  .P00" }
1031              , { "Sec-WebSocket-Protocol", "sample" }
1032              , { "Upgrade", "WebSocket" }
1033              , { "Sec-WebSocket-Key1", "4 @1  46546xW%0l 1 5" }
1034              , { "Origin", "http://example.com" }
1035              }
1036   ,.body= ""
1037   }
1038 
1039 #define CONNECTION_MULTI_LWS 36
1040 , {.name = "multiple connection header values with folding and lws"
1041   ,.type= HTTP_REQUEST
1042   ,.raw= "GET /demo HTTP/1.1\r\n"
1043          "Connection: keep-alive, upgrade\r\n"
1044          "Upgrade: WebSocket\r\n"
1045          "\r\n"
1046          "Hot diggity dogg"
1047   ,.should_keep_alive= TRUE
1048   ,.message_complete_on_eof= FALSE
1049   ,.http_major= 1
1050   ,.http_minor= 1
1051   ,.method= HTTP_GET
1052   ,.query_string= ""
1053   ,.fragment= ""
1054   ,.request_path= "/demo"
1055   ,.request_url= "/demo"
1056   ,.content_length= -1
1057   ,.num_headers= 2
1058   ,.upgrade="Hot diggity dogg"
1059   ,.headers= { { "Connection", "keep-alive, upgrade" }
1060              , { "Upgrade", "WebSocket" }
1061              }
1062   ,.body= ""
1063   }
1064 
1065 #define CONNECTION_MULTI_LWS_CRLF 37
1066 , {.name = "multiple connection header values with folding and lws"
1067   ,.type= HTTP_REQUEST
1068   ,.raw= "GET /demo HTTP/1.1\r\n"
1069          "Connection: keep-alive, \r\n upgrade\r\n"
1070          "Upgrade: WebSocket\r\n"
1071          "\r\n"
1072          "Hot diggity dogg"
1073   ,.should_keep_alive= TRUE
1074   ,.message_complete_on_eof= FALSE
1075   ,.http_major= 1
1076   ,.http_minor= 1
1077   ,.method= HTTP_GET
1078   ,.query_string= ""
1079   ,.fragment= ""
1080   ,.request_path= "/demo"
1081   ,.request_url= "/demo"
1082   ,.content_length= -1
1083   ,.num_headers= 2
1084   ,.upgrade="Hot diggity dogg"
1085   ,.headers= { { "Connection", "keep-alive,  upgrade" }
1086              , { "Upgrade", "WebSocket" }
1087              }
1088   ,.body= ""
1089   }
1090 
1091 #define UPGRADE_POST_REQUEST 38
1092 , {.name = "upgrade post request"
1093   ,.type= HTTP_REQUEST
1094   ,.raw= "POST /demo HTTP/1.1\r\n"
1095          "Host: example.com\r\n"
1096          "Connection: Upgrade\r\n"
1097          "Upgrade: HTTP/2.0\r\n"
1098          "Content-Length: 15\r\n"
1099          "\r\n"
1100          "sweet post body"
1101          "Hot diggity dogg"
1102   ,.should_keep_alive= TRUE
1103   ,.message_complete_on_eof= FALSE
1104   ,.http_major= 1
1105   ,.http_minor= 1
1106   ,.method= HTTP_POST
1107   ,.request_path= "/demo"
1108   ,.request_url= "/demo"
1109   ,.content_length= 15
1110   ,.num_headers= 4
1111   ,.upgrade="Hot diggity dogg"
1112   ,.headers= { { "Host", "example.com" }
1113              , { "Connection", "Upgrade" }
1114              , { "Upgrade", "HTTP/2.0" }
1115              , { "Content-Length", "15" }
1116              }
1117   ,.body= "sweet post body"
1118   }
1119 
1120 #define CONNECT_WITH_BODY_REQUEST 39
1121 , {.name = "connect with body request"
1122   ,.type= HTTP_REQUEST
1123   ,.raw= "CONNECT foo.bar.com:443 HTTP/1.0\r\n"
1124          "User-agent: Mozilla/1.1N\r\n"
1125          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
1126          "Content-Length: 10\r\n"
1127          "\r\n"
1128          "blarfcicle"
1129   ,.should_keep_alive= FALSE
1130   ,.message_complete_on_eof= FALSE
1131   ,.http_major= 1
1132   ,.http_minor= 0
1133   ,.method= HTTP_CONNECT
1134   ,.request_url= "foo.bar.com:443"
1135   ,.content_length= 10
1136   ,.num_headers= 3
1137   ,.upgrade="blarfcicle"
1138   ,.headers= { { "User-agent", "Mozilla/1.1N" }
1139              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
1140              , { "Content-Length", "10" }
1141              }
1142   ,.body= ""
1143   }
1144 
1145 /* Examples from the Internet draft for LINK/UNLINK methods:
1146  * https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5
1147  */
1148 
1149 #define LINK_REQUEST 40
1150 , {.name = "link request"
1151   ,.type= HTTP_REQUEST
1152   ,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n"
1153          "Host: example.com\r\n"
1154          "Link: <http://example.com/profiles/joe>; rel=\"tag\"\r\n"
1155          "Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
1156          "\r\n"
1157   ,.should_keep_alive= TRUE
1158   ,.message_complete_on_eof= FALSE
1159   ,.http_major= 1
1160   ,.http_minor= 1
1161   ,.method= HTTP_LINK
1162   ,.request_path= "/images/my_dog.jpg"
1163   ,.request_url= "/images/my_dog.jpg"
1164   ,.query_string= ""
1165   ,.fragment= ""
1166   ,.content_length= -1
1167   ,.num_headers= 3
1168   ,.headers= { { "Host", "example.com" }
1169              , { "Link", "<http://example.com/profiles/joe>; rel=\"tag\"" }
1170 	     , { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
1171              }
1172   ,.body= ""
1173   }
1174 
1175 #define UNLINK_REQUEST 41
1176 , {.name = "unlink request"
1177   ,.type= HTTP_REQUEST
1178   ,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
1179          "Host: example.com\r\n"
1180          "Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
1181          "\r\n"
1182   ,.should_keep_alive= TRUE
1183   ,.message_complete_on_eof= FALSE
1184   ,.http_major= 1
1185   ,.http_minor= 1
1186   ,.method= HTTP_UNLINK
1187   ,.request_path= "/images/my_dog.jpg"
1188   ,.request_url= "/images/my_dog.jpg"
1189   ,.query_string= ""
1190   ,.fragment= ""
1191   ,.content_length= -1
1192   ,.num_headers= 2
1193   ,.headers= { { "Host", "example.com" }
1194 	     , { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
1195              }
1196   ,.body= ""
1197   }
1198 
1199 #define SOURCE_REQUEST 42
1200 , {.name = "source request"
1201   ,.type= HTTP_REQUEST
1202   ,.raw= "SOURCE /music/sweet/music HTTP/1.1\r\n"
1203          "Host: example.com\r\n"
1204          "\r\n"
1205   ,.should_keep_alive= TRUE
1206   ,.message_complete_on_eof= FALSE
1207   ,.http_major= 1
1208   ,.http_minor= 1
1209   ,.method= HTTP_SOURCE
1210   ,.request_path= "/music/sweet/music"
1211   ,.request_url= "/music/sweet/music"
1212   ,.query_string= ""
1213   ,.fragment= ""
1214   ,.content_length= -1
1215   ,.num_headers= 1
1216   ,.headers= { { "Host", "example.com" } }
1217   ,.body= ""
1218   }
1219 
1220 #define SOURCE_ICE_REQUEST 43
1221 , {.name = "source request"
1222   ,.type= HTTP_REQUEST
1223   ,.raw= "SOURCE /music/sweet/music ICE/1.0\r\n"
1224          "Host: example.com\r\n"
1225          "\r\n"
1226   ,.should_keep_alive= FALSE
1227   ,.message_complete_on_eof= FALSE
1228   ,.http_major= 1
1229   ,.http_minor= 0
1230   ,.method= HTTP_SOURCE
1231   ,.request_path= "/music/sweet/music"
1232   ,.request_url= "/music/sweet/music"
1233   ,.query_string= ""
1234   ,.fragment= ""
1235   ,.content_length= -1
1236   ,.num_headers= 1
1237   ,.headers= { { "Host", "example.com" } }
1238   ,.body= ""
1239   }
1240 
1241 #define POST_MULTI_TE_LAST_CHUNKED 44
1242 , {.name= "post - multi coding transfer-encoding chunked body"
1243   ,.type= HTTP_REQUEST
1244   ,.raw= "POST / HTTP/1.1\r\n"
1245          "Transfer-Encoding: deflate, chunked\r\n"
1246          "\r\n"
1247          "1e\r\nall your base are belong to us\r\n"
1248          "0\r\n"
1249          "\r\n"
1250   ,.should_keep_alive= TRUE
1251   ,.message_complete_on_eof= FALSE
1252   ,.http_major= 1
1253   ,.http_minor= 1
1254   ,.method= HTTP_POST
1255   ,.query_string= ""
1256   ,.fragment= ""
1257   ,.request_path= "/"
1258   ,.request_url= "/"
1259   ,.content_length= -1
1260   ,.num_headers= 1
1261   ,.headers=
1262     { { "Transfer-Encoding" , "deflate, chunked" }
1263     }
1264   ,.body= "all your base are belong to us"
1265   ,.num_chunks_complete= 2
1266   ,.chunk_lengths= { 0x1e }
1267   }
1268 
1269 #define POST_MULTI_LINE_TE_LAST_CHUNKED 45
1270 , {.name= "post - multi line coding transfer-encoding chunked body"
1271   ,.type= HTTP_REQUEST
1272   ,.raw= "POST / HTTP/1.1\r\n"
1273          "Transfer-Encoding: deflate,\r\n"
1274          " chunked\r\n"
1275          "\r\n"
1276          "1e\r\nall your base are belong to us\r\n"
1277          "0\r\n"
1278          "\r\n"
1279   ,.should_keep_alive= TRUE
1280   ,.message_complete_on_eof= FALSE
1281   ,.http_major= 1
1282   ,.http_minor= 1
1283   ,.method= HTTP_POST
1284   ,.query_string= ""
1285   ,.fragment= ""
1286   ,.request_path= "/"
1287   ,.request_url= "/"
1288   ,.content_length= -1
1289   ,.num_headers= 1
1290   ,.headers=
1291     { { "Transfer-Encoding" , "deflate, chunked" }
1292     }
1293   ,.body= "all your base are belong to us"
1294   ,.num_chunks_complete= 2
1295   ,.chunk_lengths= { 0x1e }
1296   }
1297 
1298 #define CHUNKED_CONTENT_LENGTH 46
1299 , {.name= "chunked with content-length set, allow_chunked_length flag is set"
1300   ,.type= HTTP_REQUEST
1301   ,.raw= "POST /chunked_w_content_length HTTP/1.1\r\n"
1302          "Content-Length: 10\r\n"
1303          "Transfer-Encoding: chunked\r\n"
1304          "\r\n"
1305          "5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
1306          "6; blahblah; blah\r\n world\r\n"
1307          "0\r\n"
1308          "\r\n"
1309   ,.allow_chunked_length = 1
1310   ,.should_keep_alive= TRUE
1311   ,.message_complete_on_eof= FALSE
1312   ,.http_major= 1
1313   ,.http_minor= 1
1314   ,.method= HTTP_POST
1315   ,.query_string= ""
1316   ,.fragment= ""
1317   ,.request_path= "/chunked_w_content_length"
1318   ,.request_url= "/chunked_w_content_length"
1319   ,.content_length= 10
1320   ,.num_headers= 2
1321   ,.headers={ { "Content-Length", "10"}
1322             , { "Transfer-Encoding", "chunked" }
1323   }
1324   ,.body= "hello world"
1325   ,.num_chunks_complete= 3
1326   ,.chunk_lengths= { 5, 6 }
1327   }
1328 };
1329 
1330 /* * R E S P O N S E S * */
1331 const struct message responses[] =
1332 #define GOOGLE_301 0
1333 { {.name= "google 301"
1334   ,.type= HTTP_RESPONSE
1335   ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
1336          "Location: http://www.google.com/\r\n"
1337          "Content-Type: text/html; charset=UTF-8\r\n"
1338          "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
1339          "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
1340          "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
1341          "Cache-Control: public, max-age=2592000\r\n"
1342          "Server: gws\r\n"
1343          "Content-Length:  219  \r\n"
1344          "\r\n"
1345          "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1346          "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1347          "<H1>301 Moved</H1>\n"
1348          "The document has moved\n"
1349          "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1350          "</BODY></HTML>\r\n"
1351   ,.should_keep_alive= TRUE
1352   ,.message_complete_on_eof= FALSE
1353   ,.http_major= 1
1354   ,.http_minor= 1
1355   ,.status_code= 301
1356   ,.response_status= "Moved Permanently"
1357   ,.content_length= 219
1358   ,.num_headers= 8
1359   ,.headers=
1360     { { "Location", "http://www.google.com/" }
1361     , { "Content-Type", "text/html; charset=UTF-8" }
1362     , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
1363     , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
1364     , { "X-$PrototypeBI-Version", "1.6.0.3" }
1365     , { "Cache-Control", "public, max-age=2592000" }
1366     , { "Server", "gws" }
1367     , { "Content-Length", "219  " }
1368     }
1369   ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1370           "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1371           "<H1>301 Moved</H1>\n"
1372           "The document has moved\n"
1373           "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1374           "</BODY></HTML>\r\n"
1375   }
1376 
1377 #define NO_CONTENT_LENGTH_RESPONSE 1
1378 /* The client should wait for the server's EOF. That is, when content-length
1379  * is not specified, and "Connection: close", the end of body is specified
1380  * by the EOF.
1381  * Compare with APACHEBENCH_GET
1382  */
1383 , {.name= "no content-length response"
1384   ,.type= HTTP_RESPONSE
1385   ,.raw= "HTTP/1.1 200 OK\r\n"
1386          "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
1387          "Server: Apache\r\n"
1388          "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
1389          "Content-Type: text/xml; charset=utf-8\r\n"
1390          "Connection: close\r\n"
1391          "\r\n"
1392          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1393          "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1394          "  <SOAP-ENV:Body>\n"
1395          "    <SOAP-ENV:Fault>\n"
1396          "       <faultcode>SOAP-ENV:Client</faultcode>\n"
1397          "       <faultstring>Client Error</faultstring>\n"
1398          "    </SOAP-ENV:Fault>\n"
1399          "  </SOAP-ENV:Body>\n"
1400          "</SOAP-ENV:Envelope>"
1401   ,.should_keep_alive= FALSE
1402   ,.message_complete_on_eof= TRUE
1403   ,.http_major= 1
1404   ,.http_minor= 1
1405   ,.status_code= 200
1406   ,.response_status= "OK"
1407   ,.content_length= -1
1408   ,.num_headers= 5
1409   ,.headers=
1410     { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
1411     , { "Server", "Apache" }
1412     , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
1413     , { "Content-Type", "text/xml; charset=utf-8" }
1414     , { "Connection", "close" }
1415     }
1416   ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1417           "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1418           "  <SOAP-ENV:Body>\n"
1419           "    <SOAP-ENV:Fault>\n"
1420           "       <faultcode>SOAP-ENV:Client</faultcode>\n"
1421           "       <faultstring>Client Error</faultstring>\n"
1422           "    </SOAP-ENV:Fault>\n"
1423           "  </SOAP-ENV:Body>\n"
1424           "</SOAP-ENV:Envelope>"
1425   }
1426 
1427 #define NO_HEADERS_NO_BODY_404 2
1428 , {.name= "404 no headers no body"
1429   ,.type= HTTP_RESPONSE
1430   ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
1431   ,.should_keep_alive= FALSE
1432   ,.message_complete_on_eof= TRUE
1433   ,.http_major= 1
1434   ,.http_minor= 1
1435   ,.status_code= 404
1436   ,.response_status= "Not Found"
1437   ,.content_length= -1
1438   ,.num_headers= 0
1439   ,.headers= {}
1440   ,.body_size= 0
1441   ,.body= ""
1442   }
1443 
1444 #define NO_REASON_PHRASE 3
1445 , {.name= "301 no response phrase"
1446   ,.type= HTTP_RESPONSE
1447   ,.raw= "HTTP/1.1 301\r\n\r\n"
1448   ,.should_keep_alive = FALSE
1449   ,.message_complete_on_eof= TRUE
1450   ,.http_major= 1
1451   ,.http_minor= 1
1452   ,.status_code= 301
1453   ,.content_length= -1
1454   ,.response_status= ""
1455   ,.num_headers= 0
1456   ,.headers= {}
1457   ,.body= ""
1458   }
1459 
1460 #define TRAILING_SPACE_ON_CHUNKED_BODY 4
1461 , {.name="200 trailing space on chunked body"
1462   ,.type= HTTP_RESPONSE
1463   ,.raw= "HTTP/1.1 200 OK\r\n"
1464          "Content-Type: text/plain\r\n"
1465          "Transfer-Encoding: chunked\r\n"
1466          "\r\n"
1467          "25  \r\n"
1468          "This is the data in the first chunk\r\n"
1469          "\r\n"
1470          "1C\r\n"
1471          "and this is the second one\r\n"
1472          "\r\n"
1473          "0  \r\n"
1474          "\r\n"
1475   ,.should_keep_alive= TRUE
1476   ,.message_complete_on_eof= FALSE
1477   ,.http_major= 1
1478   ,.http_minor= 1
1479   ,.status_code= 200
1480   ,.response_status= "OK"
1481   ,.content_length= -1
1482   ,.num_headers= 2
1483   ,.headers=
1484     { {"Content-Type", "text/plain" }
1485     , {"Transfer-Encoding", "chunked" }
1486     }
1487   ,.body_size = 37+28
1488   ,.body =
1489          "This is the data in the first chunk\r\n"
1490          "and this is the second one\r\n"
1491   ,.num_chunks_complete= 3
1492   ,.chunk_lengths= { 0x25, 0x1c }
1493   }
1494 
1495 #define NO_CARRIAGE_RET 5
1496 , {.name="no carriage ret"
1497   ,.type= HTTP_RESPONSE
1498   ,.raw= "HTTP/1.1 200 OK\n"
1499          "Content-Type: text/html; charset=utf-8\n"
1500          "Connection: close\n"
1501          "\n"
1502          "these headers are from http://news.ycombinator.com/"
1503   ,.should_keep_alive= FALSE
1504   ,.message_complete_on_eof= TRUE
1505   ,.http_major= 1
1506   ,.http_minor= 1
1507   ,.status_code= 200
1508   ,.response_status= "OK"
1509   ,.content_length= -1
1510   ,.num_headers= 2
1511   ,.headers=
1512     { {"Content-Type", "text/html; charset=utf-8" }
1513     , {"Connection", "close" }
1514     }
1515   ,.body= "these headers are from http://news.ycombinator.com/"
1516   }
1517 
1518 #define PROXY_CONNECTION 6
1519 , {.name="proxy connection"
1520   ,.type= HTTP_RESPONSE
1521   ,.raw= "HTTP/1.1 200 OK\r\n"
1522          "Content-Type: text/html; charset=UTF-8\r\n"
1523          "Content-Length: 11\r\n"
1524          "Proxy-Connection: close\r\n"
1525          "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
1526          "\r\n"
1527          "hello world"
1528   ,.should_keep_alive= FALSE
1529   ,.message_complete_on_eof= FALSE
1530   ,.http_major= 1
1531   ,.http_minor= 1
1532   ,.status_code= 200
1533   ,.response_status= "OK"
1534   ,.content_length= 11
1535   ,.num_headers= 4
1536   ,.headers=
1537     { {"Content-Type", "text/html; charset=UTF-8" }
1538     , {"Content-Length", "11" }
1539     , {"Proxy-Connection", "close" }
1540     , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
1541     }
1542   ,.body= "hello world"
1543   }
1544 
1545 #define UNDERSTORE_HEADER_KEY 7
1546   // shown by
1547   // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
1548 , {.name="underscore header key"
1549   ,.type= HTTP_RESPONSE
1550   ,.raw= "HTTP/1.1 200 OK\r\n"
1551          "Server: DCLK-AdSvr\r\n"
1552          "Content-Type: text/xml\r\n"
1553          "Content-Length: 0\r\n"
1554          "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
1555   ,.should_keep_alive= TRUE
1556   ,.message_complete_on_eof= FALSE
1557   ,.http_major= 1
1558   ,.http_minor= 1
1559   ,.status_code= 200
1560   ,.response_status= "OK"
1561   ,.content_length= 0
1562   ,.num_headers= 4
1563   ,.headers=
1564     { {"Server", "DCLK-AdSvr" }
1565     , {"Content-Type", "text/xml" }
1566     , {"Content-Length", "0" }
1567     , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1568     }
1569   ,.body= ""
1570   }
1571 
1572 #define BONJOUR_MADAME_FR 8
1573 /* The client should not merge two headers fields when the first one doesn't
1574  * have a value.
1575  */
1576 , {.name= "bonjourmadame.fr"
1577   ,.type= HTTP_RESPONSE
1578   ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
1579          "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
1580          "Server: Apache/2.2.3 (Red Hat)\r\n"
1581          "Cache-Control: public\r\n"
1582          "Pragma: \r\n"
1583          "Location: http://www.bonjourmadame.fr/\r\n"
1584          "Vary: Accept-Encoding\r\n"
1585          "Content-Length: 0\r\n"
1586          "Content-Type: text/html; charset=UTF-8\r\n"
1587          "Connection: keep-alive\r\n"
1588          "\r\n"
1589   ,.should_keep_alive= TRUE
1590   ,.message_complete_on_eof= FALSE
1591   ,.http_major= 1
1592   ,.http_minor= 0
1593   ,.status_code= 301
1594   ,.response_status= "Moved Permanently"
1595   ,.content_length= 0
1596   ,.num_headers= 9
1597   ,.headers=
1598     { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1599     , { "Server", "Apache/2.2.3 (Red Hat)" }
1600     , { "Cache-Control", "public" }
1601     , { "Pragma", "" }
1602     , { "Location", "http://www.bonjourmadame.fr/" }
1603     , { "Vary",  "Accept-Encoding" }
1604     , { "Content-Length", "0" }
1605     , { "Content-Type", "text/html; charset=UTF-8" }
1606     , { "Connection", "keep-alive" }
1607     }
1608   ,.body= ""
1609   }
1610 
1611 #define RES_FIELD_UNDERSCORE 9
1612 /* Should handle spaces in header fields */
1613 , {.name= "field underscore"
1614   ,.type= HTTP_RESPONSE
1615   ,.raw= "HTTP/1.1 200 OK\r\n"
1616          "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1617          "Server: Apache\r\n"
1618          "Cache-Control: no-cache, must-revalidate\r\n"
1619          "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1620          ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1621          "Vary: Accept-Encoding\r\n"
1622          "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1623          "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1624          "Transfer-Encoding: chunked\r\n"
1625          "Content-Type: text/html\r\n"
1626          "Connection: close\r\n"
1627          "\r\n"
1628          "0\r\n\r\n"
1629   ,.should_keep_alive= FALSE
1630   ,.message_complete_on_eof= FALSE
1631   ,.http_major= 1
1632   ,.http_minor= 1
1633   ,.status_code= 200
1634   ,.response_status= "OK"
1635   ,.content_length= -1
1636   ,.num_headers= 11
1637   ,.headers=
1638     { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1639     , { "Server", "Apache" }
1640     , { "Cache-Control", "no-cache, must-revalidate" }
1641     , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1642     , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1643     , { "Vary", "Accept-Encoding" }
1644     , { "_eep-Alive", "timeout=45" }
1645     , { "_onnection", "Keep-Alive" }
1646     , { "Transfer-Encoding", "chunked" }
1647     , { "Content-Type", "text/html" }
1648     , { "Connection", "close" }
1649     }
1650   ,.body= ""
1651   ,.num_chunks_complete= 1
1652   ,.chunk_lengths= {}
1653   }
1654 
1655 #define NON_ASCII_IN_STATUS_LINE 10
1656 /* Should handle non-ASCII in status line */
1657 , {.name= "non-ASCII in status line"
1658   ,.type= HTTP_RESPONSE
1659   ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1660          "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1661          "Content-Length: 0\r\n"
1662          "Connection: close\r\n"
1663          "\r\n"
1664   ,.should_keep_alive= FALSE
1665   ,.message_complete_on_eof= FALSE
1666   ,.http_major= 1
1667   ,.http_minor= 1
1668   ,.status_code= 500
1669   ,.response_status= "Oriëntatieprobleem"
1670   ,.content_length= 0
1671   ,.num_headers= 3
1672   ,.headers=
1673     { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1674     , { "Content-Length", "0" }
1675     , { "Connection", "close" }
1676     }
1677   ,.body= ""
1678   }
1679 
1680 #define HTTP_VERSION_0_9 11
1681 /* Should handle HTTP/0.9 */
1682 , {.name= "http version 0.9"
1683   ,.type= HTTP_RESPONSE
1684   ,.raw= "HTTP/0.9 200 OK\r\n"
1685          "\r\n"
1686   ,.should_keep_alive= FALSE
1687   ,.message_complete_on_eof= TRUE
1688   ,.http_major= 0
1689   ,.http_minor= 9
1690   ,.status_code= 200
1691   ,.response_status= "OK"
1692   ,.content_length= -1
1693   ,.num_headers= 0
1694   ,.headers=
1695     {}
1696   ,.body= ""
1697   }
1698 
1699 #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1700 /* The client should wait for the server's EOF. That is, when neither
1701  * content-length nor transfer-encoding is specified, the end of body
1702  * is specified by the EOF.
1703  */
1704 , {.name= "neither content-length nor transfer-encoding response"
1705   ,.type= HTTP_RESPONSE
1706   ,.raw= "HTTP/1.1 200 OK\r\n"
1707          "Content-Type: text/plain\r\n"
1708          "\r\n"
1709          "hello world"
1710   ,.should_keep_alive= FALSE
1711   ,.message_complete_on_eof= TRUE
1712   ,.http_major= 1
1713   ,.http_minor= 1
1714   ,.status_code= 200
1715   ,.response_status= "OK"
1716   ,.content_length= -1
1717   ,.num_headers= 1
1718   ,.headers=
1719     { { "Content-Type", "text/plain" }
1720     }
1721   ,.body= "hello world"
1722   }
1723 
1724 #define NO_BODY_HTTP10_KA_200 13
1725 , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1726   ,.type= HTTP_RESPONSE
1727   ,.raw= "HTTP/1.0 200 OK\r\n"
1728          "Connection: keep-alive\r\n"
1729          "\r\n"
1730   ,.should_keep_alive= FALSE
1731   ,.message_complete_on_eof= TRUE
1732   ,.http_major= 1
1733   ,.http_minor= 0
1734   ,.status_code= 200
1735   ,.response_status= "OK"
1736   ,.content_length= -1
1737   ,.num_headers= 1
1738   ,.headers=
1739     { { "Connection", "keep-alive" }
1740     }
1741   ,.body_size= 0
1742   ,.body= ""
1743   }
1744 
1745 #define NO_BODY_HTTP10_KA_204 14
1746 , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1747   ,.type= HTTP_RESPONSE
1748   ,.raw= "HTTP/1.0 204 No content\r\n"
1749          "Connection: keep-alive\r\n"
1750          "\r\n"
1751   ,.should_keep_alive= TRUE
1752   ,.message_complete_on_eof= FALSE
1753   ,.http_major= 1
1754   ,.http_minor= 0
1755   ,.status_code= 204
1756   ,.response_status= "No content"
1757   ,.content_length= -1
1758   ,.num_headers= 1
1759   ,.headers=
1760     { { "Connection", "keep-alive" }
1761     }
1762   ,.body_size= 0
1763   ,.body= ""
1764   }
1765 
1766 #define NO_BODY_HTTP11_KA_200 15
1767 , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1768   ,.type= HTTP_RESPONSE
1769   ,.raw= "HTTP/1.1 200 OK\r\n"
1770          "\r\n"
1771   ,.should_keep_alive= FALSE
1772   ,.message_complete_on_eof= TRUE
1773   ,.http_major= 1
1774   ,.http_minor= 1
1775   ,.status_code= 200
1776   ,.response_status= "OK"
1777   ,.content_length= -1
1778   ,.num_headers= 0
1779   ,.headers={}
1780   ,.body_size= 0
1781   ,.body= ""
1782   }
1783 
1784 #define NO_BODY_HTTP11_KA_204 16
1785 , {.name= "HTTP/1.1 with a 204 status"
1786   ,.type= HTTP_RESPONSE
1787   ,.raw= "HTTP/1.1 204 No content\r\n"
1788          "\r\n"
1789   ,.should_keep_alive= TRUE
1790   ,.message_complete_on_eof= FALSE
1791   ,.http_major= 1
1792   ,.http_minor= 1
1793   ,.status_code= 204
1794   ,.response_status= "No content"
1795   ,.content_length= -1
1796   ,.num_headers= 0
1797   ,.headers={}
1798   ,.body_size= 0
1799   ,.body= ""
1800   }
1801 
1802 #define NO_BODY_HTTP11_NOKA_204 17
1803 , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1804   ,.type= HTTP_RESPONSE
1805   ,.raw= "HTTP/1.1 204 No content\r\n"
1806          "Connection: close\r\n"
1807          "\r\n"
1808   ,.should_keep_alive= FALSE
1809   ,.message_complete_on_eof= FALSE
1810   ,.http_major= 1
1811   ,.http_minor= 1
1812   ,.status_code= 204
1813   ,.response_status= "No content"
1814   ,.content_length= -1
1815   ,.num_headers= 1
1816   ,.headers=
1817     { { "Connection", "close" }
1818     }
1819   ,.body_size= 0
1820   ,.body= ""
1821   }
1822 
1823 #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1824 , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1825   ,.type= HTTP_RESPONSE
1826   ,.raw= "HTTP/1.1 200 OK\r\n"
1827          "Transfer-Encoding: chunked\r\n"
1828          "\r\n"
1829          "0\r\n"
1830          "\r\n"
1831   ,.should_keep_alive= TRUE
1832   ,.message_complete_on_eof= FALSE
1833   ,.http_major= 1
1834   ,.http_minor= 1
1835   ,.status_code= 200
1836   ,.response_status= "OK"
1837   ,.content_length= -1
1838   ,.num_headers= 1
1839   ,.headers=
1840     { { "Transfer-Encoding", "chunked" }
1841     }
1842   ,.body_size= 0
1843   ,.body= ""
1844   ,.num_chunks_complete= 1
1845   }
1846 
1847 #if !HTTP_PARSER_STRICT
1848 #define SPACE_IN_FIELD_RES 19
1849 /* Should handle spaces in header fields */
1850 , {.name= "field space"
1851   ,.type= HTTP_RESPONSE
1852   ,.raw= "HTTP/1.1 200 OK\r\n"
1853          "Server: Microsoft-IIS/6.0\r\n"
1854          "X-Powered-By: ASP.NET\r\n"
1855          "en-US Content-Type: text/xml\r\n" /* this is the problem */
1856          "Content-Type: text/xml\r\n"
1857          "Content-Length: 16\r\n"
1858          "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
1859          "Connection: keep-alive\r\n"
1860          "\r\n"
1861          "<xml>hello</xml>" /* fake body */
1862   ,.should_keep_alive= TRUE
1863   ,.message_complete_on_eof= FALSE
1864   ,.http_major= 1
1865   ,.http_minor= 1
1866   ,.status_code= 200
1867   ,.response_status= "OK"
1868   ,.content_length= 16
1869   ,.num_headers= 7
1870   ,.headers=
1871     { { "Server",  "Microsoft-IIS/6.0" }
1872     , { "X-Powered-By", "ASP.NET" }
1873     , { "en-US Content-Type", "text/xml" }
1874     , { "Content-Type", "text/xml" }
1875     , { "Content-Length", "16" }
1876     , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1877     , { "Connection", "keep-alive" }
1878     }
1879   ,.body= "<xml>hello</xml>"
1880   }
1881 #endif /* !HTTP_PARSER_STRICT */
1882 
1883 #define AMAZON_COM 20
1884 , {.name= "amazon.com"
1885   ,.type= HTTP_RESPONSE
1886   ,.raw= "HTTP/1.1 301 MovedPermanently\r\n"
1887          "Date: Wed, 15 May 2013 17:06:33 GMT\r\n"
1888          "Server: Server\r\n"
1889          "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n"
1890          "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"
1891          "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n"
1892          "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"
1893          "Vary: Accept-Encoding,User-Agent\r\n"
1894          "Content-Type: text/html; charset=ISO-8859-1\r\n"
1895          "Transfer-Encoding: chunked\r\n"
1896          "\r\n"
1897          "1\r\n"
1898          "\n\r\n"
1899          "0\r\n"
1900          "\r\n"
1901   ,.should_keep_alive= TRUE
1902   ,.message_complete_on_eof= FALSE
1903   ,.http_major= 1
1904   ,.http_minor= 1
1905   ,.status_code= 301
1906   ,.response_status= "MovedPermanently"
1907   ,.content_length= -1
1908   ,.num_headers= 9
1909   ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" }
1910              , { "Server", "Server" }
1911              , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" }
1912              , { "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 \"" }
1913              , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" }
1914              , { "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" }
1915              , { "Vary", "Accept-Encoding,User-Agent" }
1916              , { "Content-Type", "text/html; charset=ISO-8859-1" }
1917              , { "Transfer-Encoding", "chunked" }
1918              }
1919   ,.body= "\n"
1920   ,.num_chunks_complete= 2
1921   ,.chunk_lengths= { 1 }
1922   }
1923 
1924 #define EMPTY_REASON_PHRASE_AFTER_SPACE 21
1925 , {.name= "empty reason phrase after space"
1926   ,.type= HTTP_RESPONSE
1927   ,.raw= "HTTP/1.1 200 \r\n"
1928          "\r\n"
1929   ,.should_keep_alive= FALSE
1930   ,.message_complete_on_eof= TRUE
1931   ,.http_major= 1
1932   ,.http_minor= 1
1933   ,.status_code= 200
1934   ,.response_status= ""
1935   ,.content_length= -1
1936   ,.num_headers= 0
1937   ,.headers= {}
1938   ,.body= ""
1939   }
1940 
1941 #define CONTENT_LENGTH_X 22
1942 , {.name= "Content-Length-X"
1943   ,.type= HTTP_RESPONSE
1944   ,.raw= "HTTP/1.1 200 OK\r\n"
1945          "Content-Length-X: 0\r\n"
1946          "Transfer-Encoding: chunked\r\n"
1947          "\r\n"
1948          "2\r\n"
1949          "OK\r\n"
1950          "0\r\n"
1951          "\r\n"
1952   ,.should_keep_alive= TRUE
1953   ,.message_complete_on_eof= FALSE
1954   ,.http_major= 1
1955   ,.http_minor= 1
1956   ,.status_code= 200
1957   ,.response_status= "OK"
1958   ,.content_length= -1
1959   ,.num_headers= 2
1960   ,.headers= { { "Content-Length-X", "0" }
1961              , { "Transfer-Encoding", "chunked" }
1962              }
1963   ,.body= "OK"
1964   ,.num_chunks_complete= 2
1965   ,.chunk_lengths= { 2 }
1966   }
1967 
1968 #define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER 23
1969 , {.name= "HTTP 101 response with Upgrade header"
1970   ,.type= HTTP_RESPONSE
1971   ,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
1972          "Connection: upgrade\r\n"
1973          "Upgrade: h2c\r\n"
1974          "\r\n"
1975          "proto"
1976   ,.should_keep_alive= TRUE
1977   ,.message_complete_on_eof= FALSE
1978   ,.http_major= 1
1979   ,.http_minor= 1
1980   ,.status_code= 101
1981   ,.response_status= "Switching Protocols"
1982   ,.upgrade= "proto"
1983   ,.content_length= -1
1984   ,.num_headers= 2
1985   ,.headers=
1986     { { "Connection", "upgrade" }
1987     , { "Upgrade", "h2c" }
1988     }
1989   }
1990 
1991 #define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 24
1992 , {.name= "HTTP 101 response with Upgrade and Content-Length header"
1993   ,.type= HTTP_RESPONSE
1994   ,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
1995          "Connection: upgrade\r\n"
1996          "Upgrade: h2c\r\n"
1997          "Content-Length: 4\r\n"
1998          "\r\n"
1999          "body"
2000          "proto"
2001   ,.should_keep_alive= TRUE
2002   ,.message_complete_on_eof= FALSE
2003   ,.http_major= 1
2004   ,.http_minor= 1
2005   ,.status_code= 101
2006   ,.response_status= "Switching Protocols"
2007   ,.body= "body"
2008   ,.upgrade= "proto"
2009   ,.content_length= 4
2010   ,.num_headers= 3
2011   ,.headers=
2012     { { "Connection", "upgrade" }
2013     , { "Upgrade", "h2c" }
2014     , { "Content-Length", "4" }
2015     }
2016   }
2017 
2018 #define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 25
2019 , {.name= "HTTP 101 response with Upgrade and Transfer-Encoding header"
2020   ,.type= HTTP_RESPONSE
2021   ,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
2022          "Connection: upgrade\r\n"
2023          "Upgrade: h2c\r\n"
2024          "Transfer-Encoding: chunked\r\n"
2025          "\r\n"
2026          "2\r\n"
2027          "bo\r\n"
2028          "2\r\n"
2029          "dy\r\n"
2030          "0\r\n"
2031          "\r\n"
2032          "proto"
2033   ,.should_keep_alive= TRUE
2034   ,.message_complete_on_eof= FALSE
2035   ,.http_major= 1
2036   ,.http_minor= 1
2037   ,.status_code= 101
2038   ,.response_status= "Switching Protocols"
2039   ,.body= "body"
2040   ,.upgrade= "proto"
2041   ,.content_length= -1
2042   ,.num_headers= 3
2043   ,.headers=
2044     { { "Connection", "upgrade" }
2045     , { "Upgrade", "h2c" }
2046     , { "Transfer-Encoding", "chunked" }
2047     }
2048   ,.num_chunks_complete= 3
2049   ,.chunk_lengths= { 2, 2 }
2050   }
2051 
2052 #define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER 26
2053 , {.name= "HTTP 200 response with Upgrade header"
2054   ,.type= HTTP_RESPONSE
2055   ,.raw= "HTTP/1.1 200 OK\r\n"
2056          "Connection: upgrade\r\n"
2057          "Upgrade: h2c\r\n"
2058          "\r\n"
2059          "body"
2060   ,.should_keep_alive= FALSE
2061   ,.message_complete_on_eof= TRUE
2062   ,.http_major= 1
2063   ,.http_minor= 1
2064   ,.status_code= 200
2065   ,.response_status= "OK"
2066   ,.body= "body"
2067   ,.upgrade= NULL
2068   ,.content_length= -1
2069   ,.num_headers= 2
2070   ,.headers=
2071     { { "Connection", "upgrade" }
2072     , { "Upgrade", "h2c" }
2073     }
2074   }
2075 
2076 #define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 27
2077 , {.name= "HTTP 200 response with Upgrade and Content-Length header"
2078   ,.type= HTTP_RESPONSE
2079   ,.raw= "HTTP/1.1 200 OK\r\n"
2080          "Connection: upgrade\r\n"
2081          "Upgrade: h2c\r\n"
2082          "Content-Length: 4\r\n"
2083          "\r\n"
2084          "body"
2085   ,.should_keep_alive= TRUE
2086   ,.message_complete_on_eof= FALSE
2087   ,.http_major= 1
2088   ,.http_minor= 1
2089   ,.status_code= 200
2090   ,.response_status= "OK"
2091   ,.content_length= 4
2092   ,.num_headers= 3
2093   ,.body= "body"
2094   ,.upgrade= NULL
2095   ,.headers=
2096     { { "Connection", "upgrade" }
2097     , { "Upgrade", "h2c" }
2098     , { "Content-Length", "4" }
2099     }
2100   }
2101 
2102 #define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 28
2103 , {.name= "HTTP 200 response with Upgrade and Transfer-Encoding header"
2104   ,.type= HTTP_RESPONSE
2105   ,.raw= "HTTP/1.1 200 OK\r\n"
2106          "Connection: upgrade\r\n"
2107          "Upgrade: h2c\r\n"
2108          "Transfer-Encoding: chunked\r\n"
2109          "\r\n"
2110          "2\r\n"
2111          "bo\r\n"
2112          "2\r\n"
2113          "dy\r\n"
2114          "0\r\n"
2115          "\r\n"
2116   ,.should_keep_alive= TRUE
2117   ,.message_complete_on_eof= FALSE
2118   ,.http_major= 1
2119   ,.http_minor= 1
2120   ,.status_code= 200
2121   ,.response_status= "OK"
2122   ,.content_length= -1
2123   ,.num_headers= 3
2124   ,.body= "body"
2125   ,.upgrade= NULL
2126   ,.headers=
2127     { { "Connection", "upgrade" }
2128     , { "Upgrade", "h2c" }
2129     , { "Transfer-Encoding", "chunked" }
2130     }
2131   ,.num_chunks_complete= 3
2132   ,.chunk_lengths= { 2, 2 }
2133   }
2134 #define HTTP_200_MULTI_TE_NOT_LAST_CHUNKED 29
2135 , {.name= "HTTP 200 response with `chunked` being *not last* Transfer-Encoding"
2136   ,.type= HTTP_RESPONSE
2137   ,.raw= "HTTP/1.1 200 OK\r\n"
2138          "Transfer-Encoding: chunked, identity\r\n"
2139          "\r\n"
2140          "2\r\n"
2141          "OK\r\n"
2142          "0\r\n"
2143          "\r\n"
2144   ,.should_keep_alive= FALSE
2145   ,.message_complete_on_eof= TRUE
2146   ,.http_major= 1
2147   ,.http_minor= 1
2148   ,.status_code= 200
2149   ,.response_status= "OK"
2150   ,.content_length= -1
2151   ,.num_headers= 1
2152   ,.headers= { { "Transfer-Encoding", "chunked, identity" }
2153              }
2154   ,.body= "2\r\nOK\r\n0\r\n\r\n"
2155   ,.num_chunks_complete= 0
2156   }
2157 #define HTTP_200_DUPLICATE_TE_NOT_LAST_CHUNKED 30
2158 , {.name= "HTTP 200 response with `chunked` and duplicate Transfer-Encoding"
2159   ,.type= HTTP_RESPONSE
2160   ,.raw= "HTTP/1.1 200 OK\r\n"
2161          "Transfer-Encoding: chunked\r\n"
2162          "Transfer-Encoding: identity\r\n"
2163          "\r\n"
2164          "2\r\n"
2165          "OK\r\n"
2166          "0\r\n"
2167          "\r\n"
2168   ,.should_keep_alive= FALSE
2169   ,.message_complete_on_eof= TRUE
2170   ,.http_major= 1
2171   ,.http_minor= 1
2172   ,.status_code= 200
2173   ,.response_status= "OK"
2174   ,.content_length= -1
2175   ,.num_headers= 2
2176   ,.headers=
2177     { { "Transfer-Encoding", "chunked" }
2178     , { "Transfer-Encoding", "identity" }
2179     }
2180   ,.body= "2\r\nOK\r\n0\r\n\r\n"
2181   ,.num_chunks_complete= 0
2182   }
2183 };
2184 
2185 /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
2186  * define it ourselves.
2187  */
2188 size_t
strnlen(const char * s,size_t maxlen)2189 strnlen(const char *s, size_t maxlen)
2190 {
2191   const char *p;
2192 
2193   p = memchr(s, '\0', maxlen);
2194   if (p == NULL)
2195     return maxlen;
2196 
2197   return p - s;
2198 }
2199 
2200 size_t
strlncat(char * dst,size_t len,const char * src,size_t n)2201 strlncat(char *dst, size_t len, const char *src, size_t n)
2202 {
2203   size_t slen;
2204   size_t dlen;
2205   size_t rlen;
2206   size_t ncpy;
2207 
2208   slen = strnlen(src, n);
2209   dlen = strnlen(dst, len);
2210 
2211   if (dlen < len) {
2212     rlen = len - dlen;
2213     ncpy = slen < rlen ? slen : (rlen - 1);
2214     memcpy(dst + dlen, src, ncpy);
2215     dst[dlen + ncpy] = '\0';
2216   }
2217 
2218   assert(len > slen + dlen);
2219   return slen + dlen;
2220 }
2221 
2222 size_t
strlncpy(char * dst,size_t len,const char * src,size_t n)2223 strlncpy(char *dst, size_t len, const char *src, size_t n)
2224 {
2225   size_t slen;
2226   size_t ncpy;
2227 
2228   slen = strnlen(src, n);
2229 
2230   if (len > 0) {
2231     ncpy = slen < len ? slen : (len - 1);
2232     memcpy(dst, src, ncpy);
2233     dst[ncpy] = '\0';
2234   }
2235 
2236   assert(len > slen);
2237   return slen;
2238 }
2239 
2240 int
request_url_cb(http_parser * p,const char * buf,size_t len)2241 request_url_cb (http_parser *p, const char *buf, size_t len)
2242 {
2243   assert(p == &parser);
2244   strlncat(messages[num_messages].request_url,
2245            sizeof(messages[num_messages].request_url),
2246            buf,
2247            len);
2248   return 0;
2249 }
2250 
2251 int
header_field_cb(http_parser * p,const char * buf,size_t len)2252 header_field_cb (http_parser *p, const char *buf, size_t len)
2253 {
2254   assert(p == &parser);
2255   struct message *m = &messages[num_messages];
2256 
2257   if (m->last_header_element != FIELD)
2258     m->num_headers++;
2259 
2260   strlncat(m->headers[m->num_headers-1][0],
2261            sizeof(m->headers[m->num_headers-1][0]),
2262            buf,
2263            len);
2264 
2265   m->last_header_element = FIELD;
2266 
2267   return 0;
2268 }
2269 
2270 int
header_value_cb(http_parser * p,const char * buf,size_t len)2271 header_value_cb (http_parser *p, const char *buf, size_t len)
2272 {
2273   assert(p == &parser);
2274   struct message *m = &messages[num_messages];
2275 
2276   strlncat(m->headers[m->num_headers-1][1],
2277            sizeof(m->headers[m->num_headers-1][1]),
2278            buf,
2279            len);
2280 
2281   m->last_header_element = VALUE;
2282 
2283   return 0;
2284 }
2285 
2286 void
check_body_is_final(const http_parser * p)2287 check_body_is_final (const http_parser *p)
2288 {
2289   if (messages[num_messages].body_is_final) {
2290     fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
2291                     "on last on_body callback call "
2292                     "but it doesn't! ***\n\n");
2293     assert(0);
2294     abort();
2295   }
2296   messages[num_messages].body_is_final = http_body_is_final(p);
2297 }
2298 
2299 int
body_cb(http_parser * p,const char * buf,size_t len)2300 body_cb (http_parser *p, const char *buf, size_t len)
2301 {
2302   assert(p == &parser);
2303   strlncat(messages[num_messages].body,
2304            sizeof(messages[num_messages].body),
2305            buf,
2306            len);
2307   messages[num_messages].body_size += len;
2308   check_body_is_final(p);
2309  // printf("body_cb: '%s'\n", requests[num_messages].body);
2310   return 0;
2311 }
2312 
2313 int
count_body_cb(http_parser * p,const char * buf,size_t len)2314 count_body_cb (http_parser *p, const char *buf, size_t len)
2315 {
2316   assert(p == &parser);
2317   assert(buf);
2318   messages[num_messages].body_size += len;
2319   check_body_is_final(p);
2320   return 0;
2321 }
2322 
2323 int
message_begin_cb(http_parser * p)2324 message_begin_cb (http_parser *p)
2325 {
2326   assert(p == &parser);
2327   assert(!messages[num_messages].message_begin_cb_called);
2328   messages[num_messages].message_begin_cb_called = TRUE;
2329   return 0;
2330 }
2331 
2332 int
headers_complete_cb(http_parser * p)2333 headers_complete_cb (http_parser *p)
2334 {
2335   assert(p == &parser);
2336   messages[num_messages].method = parser.method;
2337   messages[num_messages].status_code = parser.status_code;
2338   messages[num_messages].http_major = parser.http_major;
2339   messages[num_messages].http_minor = parser.http_minor;
2340   messages[num_messages].content_length = parser.content_length;
2341   messages[num_messages].headers_complete_cb_called = TRUE;
2342   messages[num_messages].should_keep_alive = http_should_keep_alive(&parser);
2343   return 0;
2344 }
2345 
2346 int
message_complete_cb(http_parser * p)2347 message_complete_cb (http_parser *p)
2348 {
2349   assert(p == &parser);
2350   if (messages[num_messages].should_keep_alive !=
2351       http_should_keep_alive(&parser))
2352   {
2353     fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
2354                     "value in both on_message_complete and on_headers_complete "
2355                     "but it doesn't! ***\n\n");
2356     assert(0);
2357     abort();
2358   }
2359 
2360   if (messages[num_messages].body_size &&
2361       http_body_is_final(p) &&
2362       !messages[num_messages].body_is_final)
2363   {
2364     fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
2365                     "on last on_body callback call "
2366                     "but it doesn't! ***\n\n");
2367     assert(0);
2368     abort();
2369   }
2370 
2371   messages[num_messages].message_complete_cb_called = TRUE;
2372 
2373   messages[num_messages].message_complete_on_eof = currently_parsing_eof;
2374 
2375   num_messages++;
2376   return 0;
2377 }
2378 
2379 int
response_status_cb(http_parser * p,const char * buf,size_t len)2380 response_status_cb (http_parser *p, const char *buf, size_t len)
2381 {
2382   assert(p == &parser);
2383 
2384   messages[num_messages].status_cb_called = TRUE;
2385 
2386   strlncat(messages[num_messages].response_status,
2387            sizeof(messages[num_messages].response_status),
2388            buf,
2389            len);
2390   return 0;
2391 }
2392 
2393 int
chunk_header_cb(http_parser * p)2394 chunk_header_cb (http_parser *p)
2395 {
2396   assert(p == &parser);
2397   int chunk_idx = messages[num_messages].num_chunks;
2398   messages[num_messages].num_chunks++;
2399   if (chunk_idx < MAX_CHUNKS) {
2400     messages[num_messages].chunk_lengths[chunk_idx] = p->content_length;
2401   }
2402 
2403   return 0;
2404 }
2405 
2406 int
chunk_complete_cb(http_parser * p)2407 chunk_complete_cb (http_parser *p)
2408 {
2409   assert(p == &parser);
2410 
2411   /* Here we want to verify that each chunk_header_cb is matched by a
2412    * chunk_complete_cb, so not only should the total number of calls to
2413    * both callbacks be the same, but they also should be interleaved
2414    * properly */
2415   assert(messages[num_messages].num_chunks ==
2416          messages[num_messages].num_chunks_complete + 1);
2417 
2418   messages[num_messages].num_chunks_complete++;
2419   return 0;
2420 }
2421 
2422 /* These dontcall_* callbacks exist so that we can verify that when we're
2423  * paused, no additional callbacks are invoked */
2424 int
dontcall_message_begin_cb(http_parser * p)2425 dontcall_message_begin_cb (http_parser *p)
2426 {
2427   if (p) { } // gcc
2428   fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
2429   abort();
2430 }
2431 
2432 int
dontcall_header_field_cb(http_parser * p,const char * buf,size_t len)2433 dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
2434 {
2435   if (p || buf || len) { } // gcc
2436   fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
2437   abort();
2438 }
2439 
2440 int
dontcall_header_value_cb(http_parser * p,const char * buf,size_t len)2441 dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
2442 {
2443   if (p || buf || len) { } // gcc
2444   fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
2445   abort();
2446 }
2447 
2448 int
dontcall_request_url_cb(http_parser * p,const char * buf,size_t len)2449 dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
2450 {
2451   if (p || buf || len) { } // gcc
2452   fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
2453   abort();
2454 }
2455 
2456 int
dontcall_body_cb(http_parser * p,const char * buf,size_t len)2457 dontcall_body_cb (http_parser *p, const char *buf, size_t len)
2458 {
2459   if (p || buf || len) { } // gcc
2460   fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
2461   abort();
2462 }
2463 
2464 int
dontcall_headers_complete_cb(http_parser * p)2465 dontcall_headers_complete_cb (http_parser *p)
2466 {
2467   if (p) { } // gcc
2468   fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
2469                   "parser ***\n\n");
2470   abort();
2471 }
2472 
2473 int
dontcall_message_complete_cb(http_parser * p)2474 dontcall_message_complete_cb (http_parser *p)
2475 {
2476   if (p) { } // gcc
2477   fprintf(stderr, "\n\n*** on_message_complete() called on paused "
2478                   "parser ***\n\n");
2479   abort();
2480 }
2481 
2482 int
dontcall_response_status_cb(http_parser * p,const char * buf,size_t len)2483 dontcall_response_status_cb (http_parser *p, const char *buf, size_t len)
2484 {
2485   if (p || buf || len) { } // gcc
2486   fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n");
2487   abort();
2488 }
2489 
2490 int
dontcall_chunk_header_cb(http_parser * p)2491 dontcall_chunk_header_cb (http_parser *p)
2492 {
2493   if (p) { } // gcc
2494   fprintf(stderr, "\n\n*** on_chunk_header() called on paused parser ***\n\n");
2495   exit(1);
2496 }
2497 
2498 int
dontcall_chunk_complete_cb(http_parser * p)2499 dontcall_chunk_complete_cb (http_parser *p)
2500 {
2501   if (p) { } // gcc
2502   fprintf(stderr, "\n\n*** on_chunk_complete() "
2503           "called on paused parser ***\n\n");
2504   exit(1);
2505 }
2506 
2507 static http_parser_settings settings_dontcall =
2508   {.on_message_begin = dontcall_message_begin_cb
2509   ,.on_header_field = dontcall_header_field_cb
2510   ,.on_header_value = dontcall_header_value_cb
2511   ,.on_url = dontcall_request_url_cb
2512   ,.on_status = dontcall_response_status_cb
2513   ,.on_body = dontcall_body_cb
2514   ,.on_headers_complete = dontcall_headers_complete_cb
2515   ,.on_message_complete = dontcall_message_complete_cb
2516   ,.on_chunk_header = dontcall_chunk_header_cb
2517   ,.on_chunk_complete = dontcall_chunk_complete_cb
2518   };
2519 
2520 /* These pause_* callbacks always pause the parser and just invoke the regular
2521  * callback that tracks content. Before returning, we overwrite the parser
2522  * settings to point to the _dontcall variety so that we can verify that
2523  * the pause actually did, you know, pause. */
2524 int
pause_message_begin_cb(http_parser * p)2525 pause_message_begin_cb (http_parser *p)
2526 {
2527   http_parser_pause(p, 1);
2528   *current_pause_parser = settings_dontcall;
2529   return message_begin_cb(p);
2530 }
2531 
2532 int
pause_header_field_cb(http_parser * p,const char * buf,size_t len)2533 pause_header_field_cb (http_parser *p, const char *buf, size_t len)
2534 {
2535   http_parser_pause(p, 1);
2536   *current_pause_parser = settings_dontcall;
2537   return header_field_cb(p, buf, len);
2538 }
2539 
2540 int
pause_header_value_cb(http_parser * p,const char * buf,size_t len)2541 pause_header_value_cb (http_parser *p, const char *buf, size_t len)
2542 {
2543   http_parser_pause(p, 1);
2544   *current_pause_parser = settings_dontcall;
2545   return header_value_cb(p, buf, len);
2546 }
2547 
2548 int
pause_request_url_cb(http_parser * p,const char * buf,size_t len)2549 pause_request_url_cb (http_parser *p, const char *buf, size_t len)
2550 {
2551   http_parser_pause(p, 1);
2552   *current_pause_parser = settings_dontcall;
2553   return request_url_cb(p, buf, len);
2554 }
2555 
2556 int
pause_body_cb(http_parser * p,const char * buf,size_t len)2557 pause_body_cb (http_parser *p, const char *buf, size_t len)
2558 {
2559   http_parser_pause(p, 1);
2560   *current_pause_parser = settings_dontcall;
2561   return body_cb(p, buf, len);
2562 }
2563 
2564 int
pause_headers_complete_cb(http_parser * p)2565 pause_headers_complete_cb (http_parser *p)
2566 {
2567   http_parser_pause(p, 1);
2568   *current_pause_parser = settings_dontcall;
2569   return headers_complete_cb(p);
2570 }
2571 
2572 int
pause_message_complete_cb(http_parser * p)2573 pause_message_complete_cb (http_parser *p)
2574 {
2575   http_parser_pause(p, 1);
2576   *current_pause_parser = settings_dontcall;
2577   return message_complete_cb(p);
2578 }
2579 
2580 int
pause_response_status_cb(http_parser * p,const char * buf,size_t len)2581 pause_response_status_cb (http_parser *p, const char *buf, size_t len)
2582 {
2583   http_parser_pause(p, 1);
2584   *current_pause_parser = settings_dontcall;
2585   return response_status_cb(p, buf, len);
2586 }
2587 
2588 int
pause_chunk_header_cb(http_parser * p)2589 pause_chunk_header_cb (http_parser *p)
2590 {
2591   http_parser_pause(p, 1);
2592   *current_pause_parser = settings_dontcall;
2593   return chunk_header_cb(p);
2594 }
2595 
2596 int
pause_chunk_complete_cb(http_parser * p)2597 pause_chunk_complete_cb (http_parser *p)
2598 {
2599   http_parser_pause(p, 1);
2600   *current_pause_parser = settings_dontcall;
2601   return chunk_complete_cb(p);
2602 }
2603 
2604 int
connect_headers_complete_cb(http_parser * p)2605 connect_headers_complete_cb (http_parser *p)
2606 {
2607   headers_complete_cb(p);
2608   return 1;
2609 }
2610 
2611 int
connect_message_complete_cb(http_parser * p)2612 connect_message_complete_cb (http_parser *p)
2613 {
2614   messages[num_messages].should_keep_alive = http_should_keep_alive(&parser);
2615   return message_complete_cb(p);
2616 }
2617 
2618 static http_parser_settings settings_pause =
2619   {.on_message_begin = pause_message_begin_cb
2620   ,.on_header_field = pause_header_field_cb
2621   ,.on_header_value = pause_header_value_cb
2622   ,.on_url = pause_request_url_cb
2623   ,.on_status = pause_response_status_cb
2624   ,.on_body = pause_body_cb
2625   ,.on_headers_complete = pause_headers_complete_cb
2626   ,.on_message_complete = pause_message_complete_cb
2627   ,.on_chunk_header = pause_chunk_header_cb
2628   ,.on_chunk_complete = pause_chunk_complete_cb
2629   };
2630 
2631 static http_parser_settings settings =
2632   {.on_message_begin = message_begin_cb
2633   ,.on_header_field = header_field_cb
2634   ,.on_header_value = header_value_cb
2635   ,.on_url = request_url_cb
2636   ,.on_status = response_status_cb
2637   ,.on_body = body_cb
2638   ,.on_headers_complete = headers_complete_cb
2639   ,.on_message_complete = message_complete_cb
2640   ,.on_chunk_header = chunk_header_cb
2641   ,.on_chunk_complete = chunk_complete_cb
2642   };
2643 
2644 static http_parser_settings settings_count_body =
2645   {.on_message_begin = message_begin_cb
2646   ,.on_header_field = header_field_cb
2647   ,.on_header_value = header_value_cb
2648   ,.on_url = request_url_cb
2649   ,.on_status = response_status_cb
2650   ,.on_body = count_body_cb
2651   ,.on_headers_complete = headers_complete_cb
2652   ,.on_message_complete = message_complete_cb
2653   ,.on_chunk_header = chunk_header_cb
2654   ,.on_chunk_complete = chunk_complete_cb
2655   };
2656 
2657 static http_parser_settings settings_connect =
2658   {.on_message_begin = message_begin_cb
2659   ,.on_header_field = header_field_cb
2660   ,.on_header_value = header_value_cb
2661   ,.on_url = request_url_cb
2662   ,.on_status = response_status_cb
2663   ,.on_body = dontcall_body_cb
2664   ,.on_headers_complete = connect_headers_complete_cb
2665   ,.on_message_complete = connect_message_complete_cb
2666   ,.on_chunk_header = chunk_header_cb
2667   ,.on_chunk_complete = chunk_complete_cb
2668   };
2669 
2670 static http_parser_settings settings_null =
2671   {.on_message_begin = 0
2672   ,.on_header_field = 0
2673   ,.on_header_value = 0
2674   ,.on_url = 0
2675   ,.on_status = 0
2676   ,.on_body = 0
2677   ,.on_headers_complete = 0
2678   ,.on_message_complete = 0
2679   ,.on_chunk_header = 0
2680   ,.on_chunk_complete = 0
2681   };
2682 
2683 void
parser_init(enum http_parser_type type)2684 parser_init (enum http_parser_type type)
2685 {
2686   num_messages = 0;
2687   http_parser_init(&parser, type);
2688   memset(&messages, 0, sizeof messages);
2689 }
2690 
parse(const char * buf,size_t len)2691 size_t parse (const char *buf, size_t len)
2692 {
2693   size_t nparsed;
2694   currently_parsing_eof = (len == 0);
2695   nparsed = http_parser_execute(&parser, &settings, buf, len);
2696   return nparsed;
2697 }
2698 
parse_count_body(const char * buf,size_t len)2699 size_t parse_count_body (const char *buf, size_t len)
2700 {
2701   size_t nparsed;
2702   currently_parsing_eof = (len == 0);
2703   nparsed = http_parser_execute(&parser, &settings_count_body, buf, len);
2704   return nparsed;
2705 }
2706 
parse_pause(const char * buf,size_t len)2707 size_t parse_pause (const char *buf, size_t len)
2708 {
2709   size_t nparsed;
2710   http_parser_settings s = settings_pause;
2711 
2712   currently_parsing_eof = (len == 0);
2713   current_pause_parser = &s;
2714   nparsed = http_parser_execute(&parser, current_pause_parser, buf, len);
2715   return nparsed;
2716 }
2717 
parse_connect(const char * buf,size_t len)2718 size_t parse_connect (const char *buf, size_t len)
2719 {
2720   size_t nparsed;
2721   currently_parsing_eof = (len == 0);
2722   nparsed = http_parser_execute(&parser, &settings_connect, buf, len);
2723   return nparsed;
2724 }
2725 
2726 static inline int
check_str_eq(const struct message * m,const char * prop,const char * expected,const char * found)2727 check_str_eq (const struct message *m,
2728               const char *prop,
2729               const char *expected,
2730               const char *found) {
2731   if ((expected == NULL) != (found == NULL)) {
2732     printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2733     printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
2734     printf("   found %s\n", (found == NULL) ? "NULL" : found);
2735     return 0;
2736   }
2737   if (expected != NULL && 0 != strcmp(expected, found)) {
2738     printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2739     printf("expected '%s'\n", expected);
2740     printf("   found '%s'\n", found);
2741     return 0;
2742   }
2743   return 1;
2744 }
2745 
2746 static inline int
check_num_eq(const struct message * m,const char * prop,int expected,int found)2747 check_num_eq (const struct message *m,
2748               const char *prop,
2749               int expected,
2750               int found) {
2751   if (expected != found) {
2752     printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2753     printf("expected %d\n", expected);
2754     printf("   found %d\n", found);
2755     return 0;
2756   }
2757   return 1;
2758 }
2759 
2760 #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
2761   if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
2762 
2763 #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
2764   if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
2765 
2766 #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn)           \
2767 do {                                                                 \
2768   char ubuf[256];                                                    \
2769                                                                      \
2770   if ((u)->field_set & (1 << (fn))) {                                \
2771     memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off,   \
2772       (u)->field_data[(fn)].len);                                    \
2773     ubuf[(u)->field_data[(fn)].len] = '\0';                          \
2774   } else {                                                           \
2775     ubuf[0] = '\0';                                                  \
2776   }                                                                  \
2777                                                                      \
2778   check_str_eq(expected, #prop, expected->prop, ubuf);               \
2779 } while(0)
2780 
2781 int
message_eq(int index,int connect,const struct message * expected)2782 message_eq (int index, int connect, const struct message *expected)
2783 {
2784   int i;
2785   struct message *m = &messages[index];
2786 
2787   MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
2788   MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
2789   MESSAGE_CHECK_NUM_EQ(expected, m, content_length);
2790 
2791   if (expected->type == HTTP_REQUEST) {
2792     MESSAGE_CHECK_NUM_EQ(expected, m, method);
2793   } else {
2794     MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
2795     MESSAGE_CHECK_STR_EQ(expected, m, response_status);
2796     assert(m->status_cb_called);
2797   }
2798 
2799   if (!connect) {
2800     MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
2801     MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
2802   }
2803 
2804   assert(m->message_begin_cb_called);
2805   assert(m->headers_complete_cb_called);
2806   assert(m->message_complete_cb_called);
2807 
2808 
2809   MESSAGE_CHECK_STR_EQ(expected, m, request_url);
2810 
2811   /* Check URL components; we can't do this w/ CONNECT since it doesn't
2812    * send us a well-formed URL.
2813    */
2814   if (*m->request_url && m->method != HTTP_CONNECT) {
2815     struct http_parser_url u;
2816 
2817     if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
2818       fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
2819         m->request_url);
2820       abort();
2821     }
2822 
2823     if (expected->host) {
2824       MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
2825     }
2826 
2827     if (expected->userinfo) {
2828       MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
2829     }
2830 
2831     m->port = (u.field_set & (1 << UF_PORT)) ?
2832       u.port : 0;
2833 
2834     MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
2835     MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
2836     MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
2837     MESSAGE_CHECK_NUM_EQ(expected, m, port);
2838   }
2839 
2840   if (connect) {
2841     check_num_eq(m, "body_size", 0, m->body_size);
2842   } else if (expected->body_size) {
2843     MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
2844   } else {
2845     MESSAGE_CHECK_STR_EQ(expected, m, body);
2846   }
2847 
2848   if (connect) {
2849     check_num_eq(m, "num_chunks_complete", 0, m->num_chunks_complete);
2850   } else {
2851     assert(m->num_chunks == m->num_chunks_complete);
2852     MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete);
2853     for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) {
2854       MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]);
2855     }
2856   }
2857 
2858   MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
2859 
2860   int r;
2861   for (i = 0; i < m->num_headers; i++) {
2862     r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
2863     if (!r) return 0;
2864     r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
2865     if (!r) return 0;
2866   }
2867 
2868   if (!connect) {
2869     MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
2870   }
2871 
2872   return 1;
2873 }
2874 
2875 /* Given a sequence of varargs messages, return the number of them that the
2876  * parser should successfully parse, taking into account that upgraded
2877  * messages prevent all subsequent messages from being parsed.
2878  */
2879 size_t
count_parsed_messages(const size_t nmsgs,...)2880 count_parsed_messages(const size_t nmsgs, ...) {
2881   size_t i;
2882   va_list ap;
2883 
2884   va_start(ap, nmsgs);
2885 
2886   for (i = 0; i < nmsgs; i++) {
2887     struct message *m = va_arg(ap, struct message *);
2888 
2889     if (m->upgrade) {
2890       va_end(ap);
2891       return i + 1;
2892     }
2893   }
2894 
2895   va_end(ap);
2896   return nmsgs;
2897 }
2898 
2899 /* Given a sequence of bytes and the number of these that we were able to
2900  * parse, verify that upgrade bodies are correct.
2901  */
2902 void
upgrade_message_fix(char * body,const size_t nread,const size_t nmsgs,...)2903 upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2904   va_list ap;
2905   size_t i;
2906   size_t off = 0;
2907 
2908   va_start(ap, nmsgs);
2909 
2910   for (i = 0; i < nmsgs; i++) {
2911     struct message *m = va_arg(ap, struct message *);
2912 
2913     off += strlen(m->raw);
2914 
2915     if (m->upgrade) {
2916       off -= strlen(m->upgrade);
2917 
2918       /* Check the portion of the response after its specified upgrade */
2919       if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2920         abort();
2921       }
2922 
2923       /* Fix up the response so that message_eq() will verify the beginning
2924        * of the upgrade */
2925       *(body + nread + strlen(m->upgrade)) = '\0';
2926       messages[num_messages -1 ].upgrade = body + nread;
2927 
2928       va_end(ap);
2929       return;
2930     }
2931   }
2932 
2933   va_end(ap);
2934   printf("\n\n*** Error: expected a message with upgrade ***\n");
2935 
2936   abort();
2937 }
2938 
2939 static void
print_error(const char * raw,size_t error_location)2940 print_error (const char *raw, size_t error_location)
2941 {
2942   fprintf(stderr, "\n*** %s ***\n\n",
2943           http_errno_description(HTTP_PARSER_ERRNO(&parser)));
2944 
2945   int this_line = 0, char_len = 0;
2946   size_t i, j, len = strlen(raw), error_location_line = 0;
2947   for (i = 0; i < len; i++) {
2948     if (i == error_location) this_line = 1;
2949     switch (raw[i]) {
2950       case '\r':
2951         char_len = 2;
2952         fprintf(stderr, "\\r");
2953         break;
2954 
2955       case '\n':
2956         fprintf(stderr, "\\n\n");
2957 
2958         if (this_line) goto print;
2959 
2960         error_location_line = 0;
2961         continue;
2962 
2963       default:
2964         char_len = 1;
2965         fputc(raw[i], stderr);
2966         break;
2967     }
2968     if (!this_line) error_location_line += char_len;
2969   }
2970 
2971   fprintf(stderr, "[eof]\n");
2972 
2973  print:
2974   for (j = 0; j < error_location_line; j++) {
2975     fputc(' ', stderr);
2976   }
2977   fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2978 }
2979 
2980 void
test_preserve_data(void)2981 test_preserve_data (void)
2982 {
2983   char my_data[] = "application-specific data";
2984   http_parser parser;
2985   parser.data = my_data;
2986   http_parser_init(&parser, HTTP_REQUEST);
2987   if (parser.data != my_data) {
2988     printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2989     abort();
2990   }
2991 }
2992 
2993 struct url_test {
2994   const char *name;
2995   const char *url;
2996   int is_connect;
2997   struct http_parser_url u;
2998   int rv;
2999 };
3000 
3001 const struct url_test url_tests[] =
3002 { {.name="proxy request"
3003   ,.url="http://hostname/"
3004   ,.is_connect=0
3005   ,.u=
3006     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
3007     ,.port=0
3008     ,.field_data=
3009       {{  0,  4 } /* UF_SCHEMA */
3010       ,{  7,  8 } /* UF_HOST */
3011       ,{  0,  0 } /* UF_PORT */
3012       ,{ 15,  1 } /* UF_PATH */
3013       ,{  0,  0 } /* UF_QUERY */
3014       ,{  0,  0 } /* UF_FRAGMENT */
3015       ,{  0,  0 } /* UF_USERINFO */
3016       }
3017     }
3018   ,.rv=0
3019   }
3020 
3021 , {.name="proxy request with port"
3022   ,.url="http://hostname:444/"
3023   ,.is_connect=0
3024   ,.u=
3025     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
3026     ,.port=444
3027     ,.field_data=
3028       {{  0,  4 } /* UF_SCHEMA */
3029       ,{  7,  8 } /* UF_HOST */
3030       ,{ 16,  3 } /* UF_PORT */
3031       ,{ 19,  1 } /* UF_PATH */
3032       ,{  0,  0 } /* UF_QUERY */
3033       ,{  0,  0 } /* UF_FRAGMENT */
3034       ,{  0,  0 } /* UF_USERINFO */
3035       }
3036     }
3037   ,.rv=0
3038   }
3039 
3040 , {.name="CONNECT request"
3041   ,.url="hostname:443"
3042   ,.is_connect=1
3043   ,.u=
3044     {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
3045     ,.port=443
3046     ,.field_data=
3047       {{  0,  0 } /* UF_SCHEMA */
3048       ,{  0,  8 } /* UF_HOST */
3049       ,{  9,  3 } /* UF_PORT */
3050       ,{  0,  0 } /* UF_PATH */
3051       ,{  0,  0 } /* UF_QUERY */
3052       ,{  0,  0 } /* UF_FRAGMENT */
3053       ,{  0,  0 } /* UF_USERINFO */
3054       }
3055     }
3056   ,.rv=0
3057   }
3058 
3059 , {.name="CONNECT request but not connect"
3060   ,.url="hostname:443"
3061   ,.is_connect=0
3062   ,.rv=1
3063   }
3064 
3065 , {.name="proxy ipv6 request"
3066   ,.url="http://[1:2::3:4]/"
3067   ,.is_connect=0
3068   ,.u=
3069     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
3070     ,.port=0
3071     ,.field_data=
3072       {{  0,  4 } /* UF_SCHEMA */
3073       ,{  8,  8 } /* UF_HOST */
3074       ,{  0,  0 } /* UF_PORT */
3075       ,{ 17,  1 } /* UF_PATH */
3076       ,{  0,  0 } /* UF_QUERY */
3077       ,{  0,  0 } /* UF_FRAGMENT */
3078       ,{  0,  0 } /* UF_USERINFO */
3079       }
3080     }
3081   ,.rv=0
3082   }
3083 
3084 , {.name="proxy ipv6 request with port"
3085   ,.url="http://[1:2::3:4]:67/"
3086   ,.is_connect=0
3087   ,.u=
3088     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
3089     ,.port=67
3090     ,.field_data=
3091       {{  0,  4 } /* UF_SCHEMA */
3092       ,{  8,  8 } /* UF_HOST */
3093       ,{ 18,  2 } /* UF_PORT */
3094       ,{ 20,  1 } /* UF_PATH */
3095       ,{  0,  0 } /* UF_QUERY */
3096       ,{  0,  0 } /* UF_FRAGMENT */
3097       ,{  0,  0 } /* UF_USERINFO */
3098       }
3099     }
3100   ,.rv=0
3101   }
3102 
3103 , {.name="CONNECT ipv6 address"
3104   ,.url="[1:2::3:4]:443"
3105   ,.is_connect=1
3106   ,.u=
3107     {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
3108     ,.port=443
3109     ,.field_data=
3110       {{  0,  0 } /* UF_SCHEMA */
3111       ,{  1,  8 } /* UF_HOST */
3112       ,{ 11,  3 } /* UF_PORT */
3113       ,{  0,  0 } /* UF_PATH */
3114       ,{  0,  0 } /* UF_QUERY */
3115       ,{  0,  0 } /* UF_FRAGMENT */
3116       ,{  0,  0 } /* UF_USERINFO */
3117       }
3118     }
3119   ,.rv=0
3120   }
3121 
3122 , {.name="ipv4 in ipv6 address"
3123   ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
3124   ,.is_connect=0
3125   ,.u=
3126     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
3127     ,.port=0
3128     ,.field_data=
3129       {{  0,  4 } /* UF_SCHEMA */
3130       ,{  8, 37 } /* UF_HOST */
3131       ,{  0,  0 } /* UF_PORT */
3132       ,{ 46,  1 } /* UF_PATH */
3133       ,{  0,  0 } /* UF_QUERY */
3134       ,{  0,  0 } /* UF_FRAGMENT */
3135       ,{  0,  0 } /* UF_USERINFO */
3136       }
3137     }
3138   ,.rv=0
3139   }
3140 
3141 , {.name="extra ? in query string"
3142   ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
3143   "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
3144   "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
3145   ,.is_connect=0
3146   ,.u=
3147     {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
3148     ,.port=0
3149     ,.field_data=
3150       {{  0,  4 } /* UF_SCHEMA */
3151       ,{  7, 10 } /* UF_HOST */
3152       ,{  0,  0 } /* UF_PORT */
3153       ,{ 17, 12 } /* UF_PATH */
3154       ,{ 30,187 } /* UF_QUERY */
3155       ,{  0,  0 } /* UF_FRAGMENT */
3156       ,{  0,  0 } /* UF_USERINFO */
3157       }
3158     }
3159   ,.rv=0
3160   }
3161 
3162 , {.name="space URL encoded"
3163   ,.url="/toto.html?toto=a%20b"
3164   ,.is_connect=0
3165   ,.u=
3166     {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
3167     ,.port=0
3168     ,.field_data=
3169       {{  0,  0 } /* UF_SCHEMA */
3170       ,{  0,  0 } /* UF_HOST */
3171       ,{  0,  0 } /* UF_PORT */
3172       ,{  0, 10 } /* UF_PATH */
3173       ,{ 11, 10 } /* UF_QUERY */
3174       ,{  0,  0 } /* UF_FRAGMENT */
3175       ,{  0,  0 } /* UF_USERINFO */
3176       }
3177     }
3178   ,.rv=0
3179   }
3180 
3181 
3182 , {.name="URL fragment"
3183   ,.url="/toto.html#titi"
3184   ,.is_connect=0
3185   ,.u=
3186     {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
3187     ,.port=0
3188     ,.field_data=
3189       {{  0,  0 } /* UF_SCHEMA */
3190       ,{  0,  0 } /* UF_HOST */
3191       ,{  0,  0 } /* UF_PORT */
3192       ,{  0, 10 } /* UF_PATH */
3193       ,{  0,  0 } /* UF_QUERY */
3194       ,{ 11,  4 } /* UF_FRAGMENT */
3195       ,{  0,  0 } /* UF_USERINFO */
3196       }
3197     }
3198   ,.rv=0
3199   }
3200 
3201 , {.name="complex URL fragment"
3202   ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
3203     "http://www.example.com/index.html?foo=bar&hello=world#midpage"
3204   ,.is_connect=0
3205   ,.u=
3206     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
3207       (1<<UF_FRAGMENT)
3208     ,.port=0
3209     ,.field_data=
3210       {{  0,  4 } /* UF_SCHEMA */
3211       ,{  7, 22 } /* UF_HOST */
3212       ,{  0,  0 } /* UF_PORT */
3213       ,{ 29,  6 } /* UF_PATH */
3214       ,{ 36, 69 } /* UF_QUERY */
3215       ,{106,  7 } /* UF_FRAGMENT */
3216       ,{  0,  0 } /* UF_USERINFO */
3217       }
3218     }
3219   ,.rv=0
3220   }
3221 
3222 , {.name="complex URL from node js url parser doc"
3223   ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
3224   ,.is_connect=0
3225   ,.u=
3226     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
3227       (1<<UF_QUERY) | (1<<UF_FRAGMENT)
3228     ,.port=8080
3229     ,.field_data=
3230       {{  0,  4 } /* UF_SCHEMA */
3231       ,{  7,  8 } /* UF_HOST */
3232       ,{ 16,  4 } /* UF_PORT */
3233       ,{ 20,  8 } /* UF_PATH */
3234       ,{ 29, 12 } /* UF_QUERY */
3235       ,{ 42,  4 } /* UF_FRAGMENT */
3236       ,{  0,  0 } /* UF_USERINFO */
3237       }
3238     }
3239   ,.rv=0
3240   }
3241 
3242 , {.name="complex URL with basic auth from node js url parser doc"
3243   ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
3244   ,.is_connect=0
3245   ,.u=
3246     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
3247       (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
3248     ,.port=8080
3249     ,.field_data=
3250       {{  0,  4 } /* UF_SCHEMA */
3251       ,{ 11,  8 } /* UF_HOST */
3252       ,{ 20,  4 } /* UF_PORT */
3253       ,{ 24,  8 } /* UF_PATH */
3254       ,{ 33, 12 } /* UF_QUERY */
3255       ,{ 46,  4 } /* UF_FRAGMENT */
3256       ,{  7,  3 } /* UF_USERINFO */
3257       }
3258     }
3259   ,.rv=0
3260   }
3261 
3262 , {.name="double @"
3263   ,.url="http://a:b@@hostname:443/"
3264   ,.is_connect=0
3265   ,.rv=1
3266   }
3267 
3268 , {.name="proxy empty host"
3269   ,.url="http://:443/"
3270   ,.is_connect=0
3271   ,.rv=1
3272   }
3273 
3274 , {.name="proxy empty port"
3275   ,.url="http://hostname:/"
3276   ,.is_connect=0
3277   ,.rv=1
3278   }
3279 
3280 , {.name="CONNECT with basic auth"
3281   ,.url="a:b@hostname:443"
3282   ,.is_connect=1
3283   ,.rv=1
3284   }
3285 
3286 , {.name="CONNECT empty host"
3287   ,.url=":443"
3288   ,.is_connect=1
3289   ,.rv=1
3290   }
3291 
3292 , {.name="CONNECT empty port"
3293   ,.url="hostname:"
3294   ,.is_connect=1
3295   ,.rv=1
3296   }
3297 
3298 , {.name="CONNECT with extra bits"
3299   ,.url="hostname:443/"
3300   ,.is_connect=1
3301   ,.rv=1
3302   }
3303 
3304 , {.name="space in URL"
3305   ,.url="/foo bar/"
3306   ,.rv=1 /* s_dead */
3307   }
3308 
3309 , {.name="proxy basic auth with space url encoded"
3310   ,.url="http://a%20:b@host.com/"
3311   ,.is_connect=0
3312   ,.u=
3313     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
3314     ,.port=0
3315     ,.field_data=
3316       {{  0,  4 } /* UF_SCHEMA */
3317       ,{ 14,  8 } /* UF_HOST */
3318       ,{  0,  0 } /* UF_PORT */
3319       ,{ 22,  1 } /* UF_PATH */
3320       ,{  0,  0 } /* UF_QUERY */
3321       ,{  0,  0 } /* UF_FRAGMENT */
3322       ,{  7,  6 } /* UF_USERINFO */
3323       }
3324     }
3325   ,.rv=0
3326   }
3327 
3328 , {.name="carriage return in URL"
3329   ,.url="/foo\rbar/"
3330   ,.rv=1 /* s_dead */
3331   }
3332 
3333 , {.name="proxy double : in URL"
3334   ,.url="http://hostname::443/"
3335   ,.rv=1 /* s_dead */
3336   }
3337 
3338 , {.name="proxy basic auth with double :"
3339   ,.url="http://a::b@host.com/"
3340   ,.is_connect=0
3341   ,.u=
3342     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
3343     ,.port=0
3344     ,.field_data=
3345       {{  0,  4 } /* UF_SCHEMA */
3346       ,{ 12,  8 } /* UF_HOST */
3347       ,{  0,  0 } /* UF_PORT */
3348       ,{ 20,  1 } /* UF_PATH */
3349       ,{  0,  0 } /* UF_QUERY */
3350       ,{  0,  0 } /* UF_FRAGMENT */
3351       ,{  7,  4 } /* UF_USERINFO */
3352       }
3353     }
3354   ,.rv=0
3355   }
3356 
3357 , {.name="line feed in URL"
3358   ,.url="/foo\nbar/"
3359   ,.rv=1 /* s_dead */
3360   }
3361 
3362 , {.name="proxy empty basic auth"
3363   ,.url="http://@hostname/fo"
3364   ,.u=
3365     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
3366     ,.port=0
3367     ,.field_data=
3368       {{  0,  4 } /* UF_SCHEMA */
3369       ,{  8,  8 } /* UF_HOST */
3370       ,{  0,  0 } /* UF_PORT */
3371       ,{ 16,  3 } /* UF_PATH */
3372       ,{  0,  0 } /* UF_QUERY */
3373       ,{  0,  0 } /* UF_FRAGMENT */
3374       ,{  0,  0 } /* UF_USERINFO */
3375       }
3376     }
3377   ,.rv=0
3378   }
3379 , {.name="proxy line feed in hostname"
3380   ,.url="http://host\name/fo"
3381   ,.rv=1 /* s_dead */
3382   }
3383 
3384 , {.name="proxy % in hostname"
3385   ,.url="http://host%name/fo"
3386   ,.rv=1 /* s_dead */
3387   }
3388 
3389 , {.name="proxy ; in hostname"
3390   ,.url="http://host;ame/fo"
3391   ,.rv=1 /* s_dead */
3392   }
3393 
3394 , {.name="proxy basic auth with unreservedchars"
3395   ,.url="http://a!;-_!=+$@host.com/"
3396   ,.is_connect=0
3397   ,.u=
3398     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
3399     ,.port=0
3400     ,.field_data=
3401       {{  0,  4 } /* UF_SCHEMA */
3402       ,{ 17,  8 } /* UF_HOST */
3403       ,{  0,  0 } /* UF_PORT */
3404       ,{ 25,  1 } /* UF_PATH */
3405       ,{  0,  0 } /* UF_QUERY */
3406       ,{  0,  0 } /* UF_FRAGMENT */
3407       ,{  7,  9 } /* UF_USERINFO */
3408       }
3409     }
3410   ,.rv=0
3411   }
3412 
3413 , {.name="proxy only empty basic auth"
3414   ,.url="http://@/fo"
3415   ,.rv=1 /* s_dead */
3416   }
3417 
3418 , {.name="proxy only basic auth"
3419   ,.url="http://toto@/fo"
3420   ,.rv=1 /* s_dead */
3421   }
3422 
3423 , {.name="proxy emtpy hostname"
3424   ,.url="http:///fo"
3425   ,.rv=1 /* s_dead */
3426   }
3427 
3428 , {.name="proxy = in URL"
3429   ,.url="http://host=ame/fo"
3430   ,.rv=1 /* s_dead */
3431   }
3432 
3433 , {.name="ipv6 address with Zone ID"
3434   ,.url="http://[fe80::a%25eth0]/"
3435   ,.is_connect=0
3436   ,.u=
3437     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
3438     ,.port=0
3439     ,.field_data=
3440       {{  0,  4 } /* UF_SCHEMA */
3441       ,{  8, 14 } /* UF_HOST */
3442       ,{  0,  0 } /* UF_PORT */
3443       ,{ 23,  1 } /* UF_PATH */
3444       ,{  0,  0 } /* UF_QUERY */
3445       ,{  0,  0 } /* UF_FRAGMENT */
3446       ,{  0,  0 } /* UF_USERINFO */
3447       }
3448     }
3449   ,.rv=0
3450   }
3451 
3452 , {.name="ipv6 address with Zone ID, but '%' is not percent-encoded"
3453   ,.url="http://[fe80::a%eth0]/"
3454   ,.is_connect=0
3455   ,.u=
3456     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
3457     ,.port=0
3458     ,.field_data=
3459       {{  0,  4 } /* UF_SCHEMA */
3460       ,{  8, 12 } /* UF_HOST */
3461       ,{  0,  0 } /* UF_PORT */
3462       ,{ 21,  1 } /* UF_PATH */
3463       ,{  0,  0 } /* UF_QUERY */
3464       ,{  0,  0 } /* UF_FRAGMENT */
3465       ,{  0,  0 } /* UF_USERINFO */
3466       }
3467     }
3468   ,.rv=0
3469   }
3470 
3471 , {.name="ipv6 address ending with '%'"
3472   ,.url="http://[fe80::a%]/"
3473   ,.rv=1 /* s_dead */
3474   }
3475 
3476 , {.name="ipv6 address with Zone ID including bad character"
3477   ,.url="http://[fe80::a%$HOME]/"
3478   ,.rv=1 /* s_dead */
3479   }
3480 
3481 , {.name="just ipv6 Zone ID"
3482   ,.url="http://[%eth0]/"
3483   ,.rv=1 /* s_dead */
3484   }
3485 
3486 , {.name="empty url"
3487   ,.url=""
3488   ,.is_connect=0
3489   ,.rv=1
3490   }
3491 
3492 , {.name="NULL url"
3493   ,.url=NULL
3494   ,.is_connect=0
3495   ,.rv=1
3496   }
3497 
3498 , {.name="full of spaces url"
3499   ,.url="  "
3500   ,.is_connect=0
3501   ,.rv=1
3502   }
3503 
3504 #if HTTP_PARSER_STRICT
3505 
3506 , {.name="tab in URL"
3507   ,.url="/foo\tbar/"
3508   ,.rv=1 /* s_dead */
3509   }
3510 
3511 , {.name="form feed in URL"
3512   ,.url="/foo\fbar/"
3513   ,.rv=1 /* s_dead */
3514   }
3515 
3516 #else /* !HTTP_PARSER_STRICT */
3517 
3518 , {.name="tab in URL"
3519   ,.url="/foo\tbar/"
3520   ,.u=
3521     {.field_set=(1 << UF_PATH)
3522     ,.field_data=
3523       {{  0,  0 } /* UF_SCHEMA */
3524       ,{  0,  0 } /* UF_HOST */
3525       ,{  0,  0 } /* UF_PORT */
3526       ,{  0,  9 } /* UF_PATH */
3527       ,{  0,  0 } /* UF_QUERY */
3528       ,{  0,  0 } /* UF_FRAGMENT */
3529       ,{  0,  0 } /* UF_USERINFO */
3530       }
3531     }
3532   ,.rv=0
3533   }
3534 
3535 , {.name="form feed in URL"
3536   ,.url="/foo\fbar/"
3537   ,.u=
3538     {.field_set=(1 << UF_PATH)
3539     ,.field_data=
3540       {{  0,  0 } /* UF_SCHEMA */
3541       ,{  0,  0 } /* UF_HOST */
3542       ,{  0,  0 } /* UF_PORT */
3543       ,{  0,  9 } /* UF_PATH */
3544       ,{  0,  0 } /* UF_QUERY */
3545       ,{  0,  0 } /* UF_FRAGMENT */
3546       ,{  0,  0 } /* UF_USERINFO */
3547       }
3548     }
3549   ,.rv=0
3550   }
3551 #endif
3552 };
3553 
3554 void
dump_url(const char * url,const struct http_parser_url * u)3555 dump_url (const char *url, const struct http_parser_url *u)
3556 {
3557   unsigned int i;
3558 
3559   printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
3560   for (i = 0; i < UF_MAX; i++) {
3561     if ((u->field_set & (1 << i)) == 0) {
3562       printf("\tfield_data[%u]: unset\n", i);
3563       continue;
3564     }
3565 
3566     printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
3567            i,
3568            u->field_data[i].off,
3569            u->field_data[i].len,
3570            u->field_data[i].len,
3571            url + u->field_data[i].off);
3572   }
3573 }
3574 
3575 void
test_parse_url(void)3576 test_parse_url (void)
3577 {
3578   struct http_parser_url u;
3579   const struct url_test *test;
3580   unsigned int i;
3581   int rv;
3582 
3583   for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
3584     test = &url_tests[i];
3585     memset(&u, 0, sizeof(u));
3586 
3587     rv = http_parser_parse_url(test->url,
3588                                test->url ? strlen(test->url) : 0,
3589                                test->is_connect,
3590                                &u);
3591 
3592     if (test->rv == 0) {
3593       if (rv != 0) {
3594         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
3595                "unexpected rv %d ***\n\n", test->url, test->name, rv);
3596         abort();
3597       }
3598 
3599       if (memcmp(&u, &test->u, sizeof(u)) != 0) {
3600         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
3601                test->url, test->name);
3602 
3603         printf("target http_parser_url:\n");
3604         dump_url(test->url, &test->u);
3605         printf("result http_parser_url:\n");
3606         dump_url(test->url, &u);
3607 
3608         abort();
3609       }
3610     } else {
3611       /* test->rv != 0 */
3612       if (rv == 0) {
3613         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
3614                "unexpected rv %d ***\n\n", test->url, test->name, rv);
3615         abort();
3616       }
3617     }
3618   }
3619 }
3620 
3621 void
test_method_str(void)3622 test_method_str (void)
3623 {
3624   assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
3625   assert(0 == strcmp("<unknown>", http_method_str(1337)));
3626 }
3627 
3628 void
test_status_str(void)3629 test_status_str (void)
3630 {
3631   assert(0 == strcmp("OK", http_status_str(HTTP_STATUS_OK)));
3632   assert(0 == strcmp("Not Found", http_status_str(HTTP_STATUS_NOT_FOUND)));
3633   assert(0 == strcmp("<unknown>", http_status_str(1337)));
3634 }
3635 
3636 void
test_message(const struct message * message)3637 test_message (const struct message *message)
3638 {
3639   size_t raw_len = strlen(message->raw);
3640   size_t msg1len;
3641   for (msg1len = 0; msg1len < raw_len; msg1len++) {
3642     parser_init(message->type);
3643     if (message->allow_chunked_length) {
3644       parser.allow_chunked_length = 1;
3645     }
3646 
3647     size_t read;
3648     const char *msg1 = message->raw;
3649     const char *msg2 = msg1 + msg1len;
3650     size_t msg2len = raw_len - msg1len;
3651 
3652     if (msg1len) {
3653       assert(num_messages == 0);
3654       messages[0].headers_complete_cb_called = FALSE;
3655 
3656       read = parse(msg1, msg1len);
3657 
3658       if (!messages[0].headers_complete_cb_called && parser.nread != read) {
3659         assert(parser.nread == read);
3660         print_error(msg1, read);
3661         abort();
3662       }
3663 
3664       if (message->upgrade && parser.upgrade && num_messages > 0) {
3665         messages[num_messages - 1].upgrade = msg1 + read;
3666         goto test;
3667       }
3668 
3669       if (read != msg1len) {
3670         print_error(msg1, read);
3671         abort();
3672       }
3673     }
3674 
3675 
3676     read = parse(msg2, msg2len);
3677 
3678     if (message->upgrade && parser.upgrade) {
3679       messages[num_messages - 1].upgrade = msg2 + read;
3680       goto test;
3681     }
3682 
3683     if (read != msg2len) {
3684       print_error(msg2, read);
3685       abort();
3686     }
3687 
3688     read = parse(NULL, 0);
3689 
3690     if (read != 0) {
3691       print_error(message->raw, read);
3692       abort();
3693     }
3694 
3695   test:
3696 
3697     if (num_messages != 1) {
3698       printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
3699       abort();
3700     }
3701 
3702     if(!message_eq(0, 0, message)) abort();
3703   }
3704 }
3705 
3706 void
test_message_count_body(const struct message * message)3707 test_message_count_body (const struct message *message)
3708 {
3709   parser_init(message->type);
3710 
3711   size_t read;
3712   size_t l = strlen(message->raw);
3713   size_t i, toread;
3714   size_t chunk = 4024;
3715 
3716   for (i = 0; i < l; i+= chunk) {
3717     toread = MIN(l-i, chunk);
3718     read = parse_count_body(message->raw + i, toread);
3719     if (read != toread) {
3720       print_error(message->raw, read);
3721       abort();
3722     }
3723   }
3724 
3725 
3726   read = parse_count_body(NULL, 0);
3727   if (read != 0) {
3728     print_error(message->raw, read);
3729     abort();
3730   }
3731 
3732   if (num_messages != 1) {
3733     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
3734     abort();
3735   }
3736 
3737   if(!message_eq(0, 0, message)) abort();
3738 }
3739 
3740 void
test_simple_type(const char * buf,enum http_errno err_expected,enum http_parser_type type)3741 test_simple_type (const char *buf,
3742                   enum http_errno err_expected,
3743                   enum http_parser_type type)
3744 {
3745   parser_init(type);
3746 
3747   enum http_errno err;
3748 
3749   parse(buf, strlen(buf));
3750   err = HTTP_PARSER_ERRNO(&parser);
3751   parse(NULL, 0);
3752 
3753   /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
3754    * long as the caller isn't expecting success.
3755    */
3756 #if HTTP_PARSER_STRICT
3757   if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
3758 #else
3759   if (err_expected != err) {
3760 #endif
3761     fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
3762         http_errno_name(err_expected), http_errno_name(err), buf);
3763     abort();
3764   }
3765 }
3766 
3767 void
3768 test_simple (const char *buf, enum http_errno err_expected)
3769 {
3770   test_simple_type(buf, err_expected, HTTP_REQUEST);
3771 }
3772 
3773 void
3774 test_invalid_header_content (int req, const char* str)
3775 {
3776   http_parser parser;
3777   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3778   size_t parsed;
3779   const char *buf;
3780   buf = req ?
3781     "GET / HTTP/1.1\r\n" :
3782     "HTTP/1.1 200 OK\r\n";
3783   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3784   assert(parsed == strlen(buf));
3785 
3786   buf = str;
3787   size_t buflen = strlen(buf);
3788 
3789   parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3790   if (parsed != buflen) {
3791     assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3792     return;
3793   }
3794 
3795   fprintf(stderr,
3796           "\n*** Error expected but none in invalid header content test ***\n");
3797   abort();
3798 }
3799 
3800 void
3801 test_invalid_header_field_content_error (int req)
3802 {
3803   test_invalid_header_content(req, "Foo: F\01ailure");
3804   test_invalid_header_content(req, "Foo: B\02ar");
3805 }
3806 
3807 void
3808 test_invalid_header_field (int req, const char* str)
3809 {
3810   http_parser parser;
3811   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3812   size_t parsed;
3813   const char *buf;
3814   buf = req ?
3815     "GET / HTTP/1.1\r\n" :
3816     "HTTP/1.1 200 OK\r\n";
3817   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3818   assert(parsed == strlen(buf));
3819 
3820   buf = str;
3821   size_t buflen = strlen(buf);
3822 
3823   parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3824   if (parsed != buflen) {
3825     assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3826     return;
3827   }
3828 
3829   fprintf(stderr,
3830           "\n*** Error expected but none in invalid header token test ***\n");
3831   abort();
3832 }
3833 
3834 void
3835 test_invalid_header_field_token_error (int req)
3836 {
3837   test_invalid_header_field(req, "Fo@: Failure");
3838   test_invalid_header_field(req, "Foo\01\test: Bar");
3839 }
3840 
3841 void
3842 test_double_content_length_error (int req)
3843 {
3844   http_parser parser;
3845   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3846   size_t parsed;
3847   const char *buf;
3848   buf = req ?
3849     "GET / HTTP/1.1\r\n" :
3850     "HTTP/1.1 200 OK\r\n";
3851   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3852   assert(parsed == strlen(buf));
3853 
3854   buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
3855   size_t buflen = strlen(buf);
3856 
3857   parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3858   if (parsed != buflen) {
3859     assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
3860     return;
3861   }
3862 
3863   fprintf(stderr,
3864           "\n*** Error expected but none in double content-length test ***\n");
3865   abort();
3866 }
3867 
3868 void
3869 test_chunked_content_length_error (int req)
3870 {
3871   http_parser parser;
3872   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3873   size_t parsed;
3874   const char *buf;
3875   buf = req ?
3876     "GET / HTTP/1.1\r\n" :
3877     "HTTP/1.1 200 OK\r\n";
3878   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3879   assert(parsed == strlen(buf));
3880 
3881   buf = "Transfer-Encoding: anything\r\nContent-Length: 1\r\n\r\n";
3882   size_t buflen = strlen(buf);
3883 
3884   parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3885   if (parsed != buflen) {
3886     assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
3887     return;
3888   }
3889 
3890   fprintf(stderr,
3891           "\n*** Error expected but none in chunked content-length test ***\n");
3892   abort();
3893 }
3894 
3895 void
3896 test_header_cr_no_lf_error (int req)
3897 {
3898   http_parser parser;
3899   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3900   size_t parsed;
3901   const char *buf;
3902   buf = req ?
3903     "GET / HTTP/1.1\r\n" :
3904     "HTTP/1.1 200 OK\r\n";
3905   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3906   assert(parsed == strlen(buf));
3907 
3908   buf = "Foo: 1\rBar: 1\r\n\r\n";
3909   size_t buflen = strlen(buf);
3910 
3911   parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3912   if (parsed != buflen) {
3913     assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED);
3914     return;
3915   }
3916 
3917   fprintf(stderr,
3918           "\n*** Error expected but none in header whitespace test ***\n");
3919   abort();
3920 }
3921 
3922 void
3923 test_no_overflow_parse_url (void)
3924 {
3925   int rv;
3926   struct http_parser_url u;
3927 
3928   http_parser_url_init(&u);
3929   rv = http_parser_parse_url("http://example.com:8001", 22, 0, &u);
3930 
3931   if (rv != 0) {
3932     fprintf(stderr,
3933             "\n*** test_no_overflow_parse_url invalid return value=%d\n",
3934             rv);
3935     abort();
3936   }
3937 
3938   if (u.port != 800) {
3939     fprintf(stderr,
3940             "\n*** test_no_overflow_parse_url invalid port number=%d\n",
3941             u.port);
3942     abort();
3943   }
3944 }
3945 
3946 void
3947 test_header_overflow_error (int req)
3948 {
3949   http_parser parser;
3950   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3951   size_t parsed;
3952   const char *buf;
3953   buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
3954   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3955   assert(parsed == strlen(buf));
3956 
3957   buf = "header-key: header-value\r\n";
3958   size_t buflen = strlen(buf);
3959 
3960   int i;
3961   for (i = 0; i < 10000; i++) {
3962     parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3963     if (parsed != buflen) {
3964       //fprintf(stderr, "error found on iter %d\n", i);
3965       assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
3966       return;
3967     }
3968   }
3969 
3970   fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
3971   abort();
3972 }
3973 
3974 
3975 void
3976 test_header_nread_value ()
3977 {
3978   http_parser parser;
3979   http_parser_init(&parser, HTTP_REQUEST);
3980   size_t parsed;
3981   const char *buf;
3982   buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n";
3983   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3984   assert(parsed == strlen(buf));
3985 
3986   assert(parser.nread == strlen(buf));
3987 }
3988 
3989 
3990 static void
3991 test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
3992 {
3993   http_parser parser;
3994   http_parser_init(&parser, HTTP_RESPONSE);
3995   http_parser_execute(&parser, &settings_null, buf, buflen);
3996 
3997   if (expect_ok)
3998     assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
3999   else
4000     assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
4001 }
4002 
4003 void
4004 test_header_content_length_overflow_error (void)
4005 {
4006 #define X(size)                                                               \
4007   "HTTP/1.1 200 OK\r\n"                                                       \
4008   "Content-Length: " #size "\r\n"                                             \
4009   "\r\n"
4010   const char a[] = X(1844674407370955160);  /* 2^64 / 10 - 1 */
4011   const char b[] = X(18446744073709551615); /* 2^64-1 */
4012   const char c[] = X(18446744073709551616); /* 2^64   */
4013 #undef X
4014   test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
4015   test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
4016   test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
4017 }
4018 
4019 void
4020 test_chunk_content_length_overflow_error (void)
4021 {
4022 #define X(size)                                                               \
4023     "HTTP/1.1 200 OK\r\n"                                                     \
4024     "Transfer-Encoding: chunked\r\n"                                          \
4025     "\r\n"                                                                    \
4026     #size "\r\n"                                                              \
4027     "..."
4028   const char a[] = X(FFFFFFFFFFFFFFE);   /* 2^64 / 16 - 1 */
4029   const char b[] = X(FFFFFFFFFFFFFFFF);  /* 2^64-1 */
4030   const char c[] = X(10000000000000000); /* 2^64   */
4031 #undef X
4032   test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
4033   test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
4034   test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
4035 }
4036 
4037 void
4038 test_no_overflow_long_body (int req, size_t length)
4039 {
4040   http_parser parser;
4041   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
4042   size_t parsed;
4043   size_t i;
4044   char buf1[3000];
4045   size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
4046       req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
4047   parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
4048   if (parsed != buf1len)
4049     goto err;
4050 
4051   for (i = 0; i < length; i++) {
4052     char foo = 'a';
4053     parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
4054     if (parsed != 1)
4055       goto err;
4056   }
4057 
4058   parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
4059   if (parsed != buf1len) goto err;
4060   return;
4061 
4062  err:
4063   fprintf(stderr,
4064           "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
4065           req ? "REQUEST" : "RESPONSE",
4066           (unsigned long)length);
4067   abort();
4068 }
4069 
4070 void
4071 test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
4072 {
4073   int message_count = count_parsed_messages(3, r1, r2, r3);
4074 
4075   char total[ strlen(r1->raw)
4076             + strlen(r2->raw)
4077             + strlen(r3->raw)
4078             + 1
4079             ];
4080   total[0] = '\0';
4081 
4082   strcat(total, r1->raw);
4083   strcat(total, r2->raw);
4084   strcat(total, r3->raw);
4085 
4086   parser_init(r1->type);
4087   if (r1->allow_chunked_length ||
4088       r2->allow_chunked_length ||
4089       r3->allow_chunked_length) {
4090     parser.allow_chunked_length = 1;
4091   }
4092 
4093   size_t read;
4094 
4095   read = parse(total, strlen(total));
4096 
4097   if (parser.upgrade) {
4098     upgrade_message_fix(total, read, 3, r1, r2, r3);
4099     goto test;
4100   }
4101 
4102   if (read != strlen(total)) {
4103     print_error(total, read);
4104     abort();
4105   }
4106 
4107   read = parse(NULL, 0);
4108 
4109   if (read != 0) {
4110     print_error(total, read);
4111     abort();
4112   }
4113 
4114 test:
4115 
4116   if (message_count != num_messages) {
4117     fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
4118     abort();
4119   }
4120 
4121   if (!message_eq(0, 0, r1)) abort();
4122   if (message_count > 1 && !message_eq(1, 0, r2)) abort();
4123   if (message_count > 2 && !message_eq(2, 0, r3)) abort();
4124 }
4125 
4126 /* SCAN through every possible breaking to make sure the
4127  * parser can handle getting the content in any chunks that
4128  * might come from the socket
4129  */
4130 void
4131 test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
4132 {
4133   char total[80*1024] = "\0";
4134   char buf1[80*1024] = "\0";
4135   char buf2[80*1024] = "\0";
4136   char buf3[80*1024] = "\0";
4137 
4138   strcat(total, r1->raw);
4139   strcat(total, r2->raw);
4140   strcat(total, r3->raw);
4141 
4142   size_t read;
4143 
4144   int total_len = strlen(total);
4145 
4146   int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
4147   int ops = 0 ;
4148 
4149   size_t buf1_len, buf2_len, buf3_len;
4150   int message_count = count_parsed_messages(3, r1, r2, r3);
4151 
4152   int i,j,type_both;
4153   for (type_both = 0; type_both < 2; type_both ++ ) {
4154     for (j = 2; j < total_len; j ++ ) {
4155       for (i = 1; i < j; i ++ ) {
4156 
4157         if (ops % 1000 == 0)  {
4158           printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
4159           fflush(stdout);
4160         }
4161         ops += 1;
4162 
4163         parser_init(type_both ? HTTP_BOTH : r1->type);
4164 
4165         buf1_len = i;
4166         strlncpy(buf1, sizeof(buf1), total, buf1_len);
4167         buf1[buf1_len] = 0;
4168 
4169         buf2_len = j - i;
4170         strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
4171         buf2[buf2_len] = 0;
4172 
4173         buf3_len = total_len - j;
4174         strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
4175         buf3[buf3_len] = 0;
4176 
4177         assert(num_messages == 0);
4178         messages[0].headers_complete_cb_called = FALSE;
4179 
4180         read = parse(buf1, buf1_len);
4181 
4182         if (!messages[0].headers_complete_cb_called && parser.nread != read) {
4183           print_error(buf1, read);
4184           goto error;
4185         }
4186 
4187         if (parser.upgrade) goto test;
4188 
4189         if (read != buf1_len) {
4190           print_error(buf1, read);
4191           goto error;
4192         }
4193 
4194         read += parse(buf2, buf2_len);
4195 
4196         if (parser.upgrade) goto test;
4197 
4198         if (read != buf1_len + buf2_len) {
4199           print_error(buf2, read);
4200           goto error;
4201         }
4202 
4203         read += parse(buf3, buf3_len);
4204 
4205         if (parser.upgrade) goto test;
4206 
4207         if (read != buf1_len + buf2_len + buf3_len) {
4208           print_error(buf3, read);
4209           goto error;
4210         }
4211 
4212         parse(NULL, 0);
4213 
4214 test:
4215         if (parser.upgrade) {
4216           upgrade_message_fix(total, read, 3, r1, r2, r3);
4217         }
4218 
4219         if (message_count != num_messages) {
4220           fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
4221             message_count, num_messages);
4222           goto error;
4223         }
4224 
4225         if (!message_eq(0, 0, r1)) {
4226           fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
4227           goto error;
4228         }
4229 
4230         if (message_count > 1 && !message_eq(1, 0, r2)) {
4231           fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
4232           goto error;
4233         }
4234 
4235         if (message_count > 2 && !message_eq(2, 0, r3)) {
4236           fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
4237           goto error;
4238         }
4239       }
4240     }
4241   }
4242   puts("\b\b\b\b100%");
4243   return;
4244 
4245  error:
4246   fprintf(stderr, "i=%d  j=%d\n", i, j);
4247   fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
4248   fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
4249   fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
4250   abort();
4251 }
4252 
4253 // user required to free the result
4254 // string terminated by \0
4255 char *
4256 create_large_chunked_message (int body_size_in_kb, const char* headers)
4257 {
4258   int i;
4259   size_t wrote = 0;
4260   size_t headers_len = strlen(headers);
4261   size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
4262   char * buf = malloc(bufsize);
4263 
4264   memcpy(buf, headers, headers_len);
4265   wrote += headers_len;
4266 
4267   for (i = 0; i < body_size_in_kb; i++) {
4268     // write 1kb chunk into the body.
4269     memcpy(buf + wrote, "400\r\n", 5);
4270     wrote += 5;
4271     memset(buf + wrote, 'C', 1024);
4272     wrote += 1024;
4273     strcpy(buf + wrote, "\r\n");
4274     wrote += 2;
4275   }
4276 
4277   memcpy(buf + wrote, "0\r\n\r\n", 6);
4278   wrote += 6;
4279   assert(wrote == bufsize);
4280 
4281   return buf;
4282 }
4283 
4284 /* Verify that we can pause parsing at any of the bytes in the
4285  * message and still get the result that we're expecting. */
4286 void
4287 test_message_pause (const struct message *msg)
4288 {
4289   char *buf = (char*) msg->raw;
4290   size_t buflen = strlen(msg->raw);
4291   size_t nread;
4292 
4293   parser_init(msg->type);
4294   if (msg->allow_chunked_length) {
4295     parser.allow_chunked_length = 1;
4296   }
4297 
4298   do {
4299     nread = parse_pause(buf, buflen);
4300 
4301     // We can only set the upgrade buffer once we've gotten our message
4302     // completion callback.
4303     if (messages[0].message_complete_cb_called &&
4304         msg->upgrade &&
4305         parser.upgrade) {
4306       messages[0].upgrade = buf + nread;
4307       goto test;
4308     }
4309 
4310     if (nread < buflen) {
4311 
4312       // Not much do to if we failed a strict-mode check
4313       if (HTTP_PARSER_ERRNO(&parser) == HPE_STRICT) {
4314         return;
4315       }
4316 
4317       assert (HTTP_PARSER_ERRNO(&parser) == HPE_PAUSED);
4318     }
4319 
4320     buf += nread;
4321     buflen -= nread;
4322     http_parser_pause(&parser, 0);
4323   } while (buflen > 0);
4324 
4325   nread = parse_pause(NULL, 0);
4326   assert (nread == 0);
4327 
4328 test:
4329   if (num_messages != 1) {
4330     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
4331     abort();
4332   }
4333 
4334   if(!message_eq(0, 0, msg)) abort();
4335 }
4336 
4337 /* Verify that body and next message won't be parsed in responses to CONNECT */
4338 void
4339 test_message_connect (const struct message *msg)
4340 {
4341   char *buf = (char*) msg->raw;
4342   size_t buflen = strlen(msg->raw);
4343 
4344   parser_init(msg->type);
4345 
4346   parse_connect(buf, buflen);
4347 
4348   if (num_messages != 1) {
4349     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
4350     abort();
4351   }
4352 
4353   if(!message_eq(0, 1, msg)) abort();
4354 }
4355 
4356 int
4357 main (void)
4358 {
4359   unsigned i, j, k;
4360   unsigned long version;
4361   unsigned major;
4362   unsigned minor;
4363   unsigned patch;
4364 
4365   version = http_parser_version();
4366   major = (version >> 16) & 255;
4367   minor = (version >> 8) & 255;
4368   patch = version & 255;
4369   printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version);
4370 
4371   printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
4372 
4373 #if defined(__i386__) || defined(__x86_64__)
4374   /* Should be 32 on both 32 bits and 64 bits x86 because of struct padding,
4375    * see https://github.com/nodejs/http-parser/issues/507.
4376    */
4377   assert(sizeof(http_parser) == 32);
4378 #endif
4379 
4380   //// API
4381   test_preserve_data();
4382   test_parse_url();
4383   test_method_str();
4384   test_status_str();
4385 
4386   //// NREAD
4387   test_header_nread_value();
4388 
4389   //// OVERFLOW CONDITIONS
4390   test_no_overflow_parse_url();
4391 
4392   test_header_overflow_error(HTTP_REQUEST);
4393   test_no_overflow_long_body(HTTP_REQUEST, 1000);
4394   test_no_overflow_long_body(HTTP_REQUEST, 100000);
4395 
4396   test_header_overflow_error(HTTP_RESPONSE);
4397   test_no_overflow_long_body(HTTP_RESPONSE, 1000);
4398   test_no_overflow_long_body(HTTP_RESPONSE, 100000);
4399 
4400   test_header_content_length_overflow_error();
4401   test_chunk_content_length_overflow_error();
4402 
4403   //// HEADER FIELD CONDITIONS
4404   test_double_content_length_error(HTTP_REQUEST);
4405   test_chunked_content_length_error(HTTP_REQUEST);
4406   test_header_cr_no_lf_error(HTTP_REQUEST);
4407   test_invalid_header_field_token_error(HTTP_REQUEST);
4408   test_invalid_header_field_content_error(HTTP_REQUEST);
4409   test_double_content_length_error(HTTP_RESPONSE);
4410   test_chunked_content_length_error(HTTP_RESPONSE);
4411   test_header_cr_no_lf_error(HTTP_RESPONSE);
4412   test_invalid_header_field_token_error(HTTP_RESPONSE);
4413   test_invalid_header_field_content_error(HTTP_RESPONSE);
4414 
4415   test_simple_type(
4416       "POST / HTTP/1.1\r\n"
4417       "Content-Length:\r\n"  // empty
4418       "\r\n",
4419       HPE_INVALID_CONTENT_LENGTH,
4420       HTTP_REQUEST);
4421 
4422   test_simple_type(
4423       "POST / HTTP/1.1\r\n"
4424       "Content-Length:  42 \r\n"  // Note the surrounding whitespace.
4425       "\r\n",
4426       HPE_OK,
4427       HTTP_REQUEST);
4428 
4429   test_simple_type(
4430       "POST / HTTP/1.1\r\n"
4431       "Content-Length: 4 2\r\n"
4432       "\r\n",
4433       HPE_INVALID_CONTENT_LENGTH,
4434       HTTP_REQUEST);
4435 
4436   test_simple_type(
4437       "POST / HTTP/1.1\r\n"
4438       "Content-Length: 13 37\r\n"
4439       "\r\n",
4440       HPE_INVALID_CONTENT_LENGTH,
4441       HTTP_REQUEST);
4442 
4443   test_simple_type(
4444       "POST / HTTP/1.1\r\n"
4445       "Content-Length:  42\r\n"
4446       " Hello world!\r\n",
4447       HPE_INVALID_CONTENT_LENGTH,
4448       HTTP_REQUEST);
4449 
4450   test_simple_type(
4451       "POST / HTTP/1.1\r\n"
4452       "Content-Length:  42\r\n"
4453       " \r\n",
4454       HPE_OK,
4455       HTTP_REQUEST);
4456 
4457   //// RESPONSES
4458 
4459   test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4460   test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4461   test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4462   test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4463   test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4464   test_simple_type("\rHTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4465 
4466   for (i = 0; i < ARRAY_SIZE(responses); i++) {
4467     test_message(&responses[i]);
4468   }
4469 
4470   for (i = 0; i < ARRAY_SIZE(responses); i++) {
4471     test_message_pause(&responses[i]);
4472   }
4473 
4474   for (i = 0; i < ARRAY_SIZE(responses); i++) {
4475     test_message_connect(&responses[i]);
4476   }
4477 
4478   for (i = 0; i < ARRAY_SIZE(responses); i++) {
4479     if (!responses[i].should_keep_alive) continue;
4480     for (j = 0; j < ARRAY_SIZE(responses); j++) {
4481       if (!responses[j].should_keep_alive) continue;
4482       for (k = 0; k < ARRAY_SIZE(responses); k++) {
4483         test_multiple3(&responses[i], &responses[j], &responses[k]);
4484       }
4485     }
4486   }
4487 
4488   test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
4489   test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
4490 
4491   // test very large chunked response
4492   {
4493     char * msg = create_large_chunked_message(31337,
4494       "HTTP/1.0 200 OK\r\n"
4495       "Transfer-Encoding: chunked\r\n"
4496       "Content-Type: text/plain\r\n"
4497       "\r\n");
4498     struct message large_chunked =
4499       {.name= "large chunked"
4500       ,.type= HTTP_RESPONSE
4501       ,.raw= msg
4502       ,.should_keep_alive= FALSE
4503       ,.message_complete_on_eof= FALSE
4504       ,.http_major= 1
4505       ,.http_minor= 0
4506       ,.status_code= 200
4507       ,.response_status= "OK"
4508       ,.content_length= -1
4509       ,.num_headers= 2
4510       ,.headers=
4511         { { "Transfer-Encoding", "chunked" }
4512         , { "Content-Type", "text/plain" }
4513         }
4514       ,.body_size= 31337*1024
4515       ,.num_chunks_complete= 31338
4516       };
4517     for (i = 0; i < MAX_CHUNKS; i++) {
4518       large_chunked.chunk_lengths[i] = 1024;
4519     }
4520     test_message_count_body(&large_chunked);
4521     free(msg);
4522   }
4523 
4524 
4525 
4526   printf("response scan 1/2      ");
4527   test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
4528            , &responses[NO_BODY_HTTP10_KA_204]
4529            , &responses[NO_REASON_PHRASE]
4530            );
4531 
4532   printf("response scan 2/2      ");
4533   test_scan( &responses[BONJOUR_MADAME_FR]
4534            , &responses[UNDERSTORE_HEADER_KEY]
4535            , &responses[NO_CARRIAGE_RET]
4536            );
4537 
4538   puts("responses okay");
4539 
4540 
4541   /// REQUESTS
4542 
4543   test_simple("GET / IHTTP/1.0\r\n\r\n", HPE_INVALID_CONSTANT);
4544   test_simple("GET / ICE/1.0\r\n\r\n", HPE_INVALID_CONSTANT);
4545   test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
4546   test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION);
4547   test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION);
4548   test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION);
4549 
4550   test_simple("GET / HTTP/1.0\r\nHello: w\1rld\r\n\r\n", HPE_INVALID_HEADER_TOKEN);
4551   test_simple("GET / HTTP/1.0\r\nHello: woooo\2rld\r\n\r\n", HPE_INVALID_HEADER_TOKEN);
4552 
4553   // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
4554   test_simple("GET / HTTP/1.1\r\n"
4555               "Test: Düsseldorf\r\n",
4556               HPE_OK);
4557 
4558   // Well-formed but incomplete
4559   test_simple("GET / HTTP/1.1\r\n"
4560               "Content-Type: text/plain\r\n"
4561               "Content-Length: 6\r\n"
4562               "\r\n"
4563               "fooba",
4564               HPE_OK);
4565 
4566   // Unknown Transfer-Encoding in request
4567   test_simple("GET / HTTP/1.1\r\n"
4568               "Transfer-Encoding: unknown\r\n"
4569               "\r\n",
4570               HPE_INVALID_TRANSFER_ENCODING);
4571 
4572   static const char *all_methods[] = {
4573     "DELETE",
4574     "GET",
4575     "HEAD",
4576     "POST",
4577     "PUT",
4578     //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
4579     "OPTIONS",
4580     "TRACE",
4581     "COPY",
4582     "LOCK",
4583     "MKCOL",
4584     "MOVE",
4585     "PROPFIND",
4586     "PROPPATCH",
4587     "SEARCH",
4588     "UNLOCK",
4589     "BIND",
4590     "REBIND",
4591     "UNBIND",
4592     "ACL",
4593     "REPORT",
4594     "MKACTIVITY",
4595     "CHECKOUT",
4596     "MERGE",
4597     "M-SEARCH",
4598     "NOTIFY",
4599     "SUBSCRIBE",
4600     "UNSUBSCRIBE",
4601     "PATCH",
4602     "PURGE",
4603     "MKCALENDAR",
4604     "LINK",
4605     "UNLINK",
4606     0 };
4607   const char **this_method;
4608   for (this_method = all_methods; *this_method; this_method++) {
4609     char buf[200];
4610     sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
4611     test_simple(buf, HPE_OK);
4612   }
4613 
4614   static const char *bad_methods[] = {
4615       "ASDF",
4616       "C******",
4617       "COLA",
4618       "GEM",
4619       "GETA",
4620       "M****",
4621       "MKCOLA",
4622       "PROPPATCHA",
4623       "PUN",
4624       "PX",
4625       "SA",
4626       "hello world",
4627       0 };
4628   for (this_method = bad_methods; *this_method; this_method++) {
4629     char buf[200];
4630     sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
4631     test_simple(buf, HPE_INVALID_METHOD);
4632   }
4633 
4634   // illegal header field name line folding
4635   test_simple("GET / HTTP/1.1\r\n"
4636               "name\r\n"
4637               " : value\r\n"
4638               "\r\n",
4639               HPE_INVALID_HEADER_TOKEN);
4640 
4641   const char *dumbluck2 =
4642     "GET / HTTP/1.1\r\n"
4643     "X-SSL-Nonsense:   -----BEGIN CERTIFICATE-----\r\n"
4644     "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
4645     "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
4646     "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
4647     "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
4648     "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
4649     "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
4650     "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
4651     "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
4652     "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
4653     "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
4654     "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
4655     "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
4656     "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
4657     "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
4658     "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
4659     "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
4660     "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
4661     "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
4662     "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
4663     "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
4664     "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
4665     "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
4666     "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
4667     "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
4668     "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
4669     "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
4670     "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
4671     "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
4672     "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
4673     "\tRA==\r\n"
4674     "\t-----END CERTIFICATE-----\r\n"
4675     "\r\n";
4676   test_simple(dumbluck2, HPE_OK);
4677 
4678   const char *corrupted_connection =
4679     "GET / HTTP/1.1\r\n"
4680     "Host: www.example.com\r\n"
4681     "Connection\r\033\065\325eep-Alive\r\n"
4682     "Accept-Encoding: gzip\r\n"
4683     "\r\n";
4684   test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN);
4685 
4686   const char *corrupted_header_name =
4687     "GET / HTTP/1.1\r\n"
4688     "Host: www.example.com\r\n"
4689     "X-Some-Header\r\033\065\325eep-Alive\r\n"
4690     "Accept-Encoding: gzip\r\n"
4691     "\r\n";
4692   test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN);
4693 
4694 #if 0
4695   // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
4696   // until EOF.
4697   //
4698   // no content-length
4699   // error if there is a body without content length
4700   const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
4701                                            "Accept: */*\r\n"
4702                                            "\r\n"
4703                                            "HELLO";
4704   test_simple(bad_get_no_headers_no_body, 0);
4705 #endif
4706   /* TODO sending junk and large headers gets rejected */
4707 
4708 
4709   /* check to make sure our predefined requests are okay */
4710   for (i = 0; i < ARRAY_SIZE(requests); i++) {
4711     test_message(&requests[i]);
4712   }
4713 
4714   for (i = 0; i < ARRAY_SIZE(requests); i++) {
4715     test_message_pause(&requests[i]);
4716   }
4717 
4718   for (i = 0; i < ARRAY_SIZE(requests); i++) {
4719     if (!requests[i].should_keep_alive) continue;
4720     for (j = 0; j < ARRAY_SIZE(requests); j++) {
4721       if (!requests[j].should_keep_alive) continue;
4722       for (k = 0; k < ARRAY_SIZE(requests); k++) {
4723         test_multiple3(&requests[i], &requests[j], &requests[k]);
4724       }
4725     }
4726   }
4727 
4728   printf("request scan 1/4      ");
4729   test_scan( &requests[GET_NO_HEADERS_NO_BODY]
4730            , &requests[GET_ONE_HEADER_NO_BODY]
4731            , &requests[GET_NO_HEADERS_NO_BODY]
4732            );
4733 
4734   printf("request scan 2/4      ");
4735   test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
4736            , &requests[POST_IDENTITY_BODY_WORLD]
4737            , &requests[GET_FUNKY_CONTENT_LENGTH]
4738            );
4739 
4740   printf("request scan 3/4      ");
4741   test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
4742            , &requests[CHUNKED_W_TRAILING_HEADERS]
4743            , &requests[CHUNKED_W_NONSENSE_AFTER_LENGTH]
4744            );
4745 
4746   printf("request scan 4/4      ");
4747   test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
4748            , &requests[PREFIX_NEWLINE_GET ]
4749            , &requests[CONNECT_REQUEST]
4750            );
4751 
4752   puts("requests okay");
4753 
4754   return 0;
4755 }
4756