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 #undef TRUE
30 #define TRUE 1
31 #undef FALSE
32 #define FALSE 0
33 
34 #define MAX_HEADERS 13
35 #define MAX_ELEMENT_SIZE 2048
36 
37 #define MIN(a,b) ((a) < (b) ? (a) : (b))
38 
39 static http_parser *parser;
40 
41 struct message {
42   const char *name; // for debugging purposes
43   const char *raw;
44   enum http_parser_type type;
45   enum http_method method;
46   int status_code;
47   char request_path[MAX_ELEMENT_SIZE];
48   char request_url[MAX_ELEMENT_SIZE];
49   char fragment[MAX_ELEMENT_SIZE];
50   char query_string[MAX_ELEMENT_SIZE];
51   char body[MAX_ELEMENT_SIZE];
52   size_t body_size;
53   const char *host;
54   const char *userinfo;
55   uint16_t port;
56   int num_headers;
57   enum { NONE=0, FIELD, VALUE } last_header_element;
58   char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
59   int should_keep_alive;
60 
61   const char *upgrade; // upgraded body
62 
63   unsigned short http_major;
64   unsigned short http_minor;
65 
66   int message_begin_cb_called;
67   int headers_complete_cb_called;
68   int message_complete_cb_called;
69   int message_complete_on_eof;
70   int body_is_final;
71 };
72 
73 static int currently_parsing_eof;
74 
75 static struct message messages[5];
76 static int num_messages;
77 static http_parser_settings *current_pause_parser;
78 
79 /* * R E Q U E S T S * */
80 const struct message requests[] =
81 #define CURL_GET 0
82 { {.name= "curl get"
83   ,.type= HTTP_REQUEST
84   ,.raw= "GET /test HTTP/1.1\r\n"
85          "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"
86          "Host: 0.0.0.0=5000\r\n"
87          "Accept: */*\r\n"
88          "\r\n"
89   ,.should_keep_alive= TRUE
90   ,.message_complete_on_eof= FALSE
91   ,.http_major= 1
92   ,.http_minor= 1
93   ,.method= HTTP_GET
94   ,.query_string= ""
95   ,.fragment= ""
96   ,.request_path= "/test"
97   ,.request_url= "/test"
98   ,.num_headers= 3
99   ,.headers=
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" }
101     , { "Host", "0.0.0.0=5000" }
102     , { "Accept", "*/*" }
103     }
104   ,.body= ""
105   }
106 
107 #define FIREFOX_GET 1
108 , {.name= "firefox get"
109   ,.type= HTTP_REQUEST
110   ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
111          "Host: 0.0.0.0=5000\r\n"
112          "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
113          "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
114          "Accept-Language: en-us,en;q=0.5\r\n"
115          "Accept-Encoding: gzip,deflate\r\n"
116          "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
117          "Keep-Alive: 300\r\n"
118          "Connection: keep-alive\r\n"
119          "\r\n"
120   ,.should_keep_alive= TRUE
121   ,.message_complete_on_eof= FALSE
122   ,.http_major= 1
123   ,.http_minor= 1
124   ,.method= HTTP_GET
125   ,.query_string= ""
126   ,.fragment= ""
127   ,.request_path= "/favicon.ico"
128   ,.request_url= "/favicon.ico"
129   ,.num_headers= 8
130   ,.headers=
131     { { "Host", "0.0.0.0=5000" }
132     , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
133     , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
134     , { "Accept-Language", "en-us,en;q=0.5" }
135     , { "Accept-Encoding", "gzip,deflate" }
136     , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
137     , { "Keep-Alive", "300" }
138     , { "Connection", "keep-alive" }
139     }
140   ,.body= ""
141   }
142 
143 #define DUMBFUCK 2
144 , {.name= "dumbfuck"
145   ,.type= HTTP_REQUEST
146   ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
147          "aaaaaaaaaaaaa:++++++++++\r\n"
148          "\r\n"
149   ,.should_keep_alive= TRUE
150   ,.message_complete_on_eof= FALSE
151   ,.http_major= 1
152   ,.http_minor= 1
153   ,.method= HTTP_GET
154   ,.query_string= ""
155   ,.fragment= ""
156   ,.request_path= "/dumbfuck"
157   ,.request_url= "/dumbfuck"
158   ,.num_headers= 1
159   ,.headers=
160     { { "aaaaaaaaaaaaa",  "++++++++++" }
161     }
162   ,.body= ""
163   }
164 
165 #define FRAGMENT_IN_URI 3
166 , {.name= "fragment in url"
167   ,.type= HTTP_REQUEST
168   ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
169          "\r\n"
170   ,.should_keep_alive= TRUE
171   ,.message_complete_on_eof= FALSE
172   ,.http_major= 1
173   ,.http_minor= 1
174   ,.method= HTTP_GET
175   ,.query_string= "page=1"
176   ,.fragment= "posts-17408"
177   ,.request_path= "/forums/1/topics/2375"
178   /* XXX request url does include fragment? */
179   ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
180   ,.num_headers= 0
181   ,.body= ""
182   }
183 
184 #define GET_NO_HEADERS_NO_BODY 4
185 , {.name= "get no headers no body"
186   ,.type= HTTP_REQUEST
187   ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
188          "\r\n"
189   ,.should_keep_alive= TRUE
190   ,.message_complete_on_eof= FALSE /* would need Connection: close */
191   ,.http_major= 1
192   ,.http_minor= 1
193   ,.method= HTTP_GET
194   ,.query_string= ""
195   ,.fragment= ""
196   ,.request_path= "/get_no_headers_no_body/world"
197   ,.request_url= "/get_no_headers_no_body/world"
198   ,.num_headers= 0
199   ,.body= ""
200   }
201 
202 #define GET_ONE_HEADER_NO_BODY 5
203 , {.name= "get one header no body"
204   ,.type= HTTP_REQUEST
205   ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
206          "Accept: */*\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_one_header_no_body"
216   ,.request_url= "/get_one_header_no_body"
217   ,.num_headers= 1
218   ,.headers=
219     { { "Accept" , "*/*" }
220     }
221   ,.body= ""
222   }
223 
224 #define GET_FUNKY_CONTENT_LENGTH 6
225 , {.name= "get funky content length body hello"
226   ,.type= HTTP_REQUEST
227   ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
228          "conTENT-Length: 5\r\n"
229          "\r\n"
230          "HELLO"
231   ,.should_keep_alive= FALSE
232   ,.message_complete_on_eof= FALSE
233   ,.http_major= 1
234   ,.http_minor= 0
235   ,.method= HTTP_GET
236   ,.query_string= ""
237   ,.fragment= ""
238   ,.request_path= "/get_funky_content_length_body_hello"
239   ,.request_url= "/get_funky_content_length_body_hello"
240   ,.num_headers= 1
241   ,.headers=
242     { { "conTENT-Length" , "5" }
243     }
244   ,.body= "HELLO"
245   }
246 
247 #define POST_IDENTITY_BODY_WORLD 7
248 , {.name= "post identity body world"
249   ,.type= HTTP_REQUEST
250   ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
251          "Accept: */*\r\n"
252          "Transfer-Encoding: identity\r\n"
253          "Content-Length: 5\r\n"
254          "\r\n"
255          "World"
256   ,.should_keep_alive= TRUE
257   ,.message_complete_on_eof= FALSE
258   ,.http_major= 1
259   ,.http_minor= 1
260   ,.method= HTTP_POST
261   ,.query_string= "q=search"
262   ,.fragment= "hey"
263   ,.request_path= "/post_identity_body_world"
264   ,.request_url= "/post_identity_body_world?q=search#hey"
265   ,.num_headers= 3
266   ,.headers=
267     { { "Accept", "*/*" }
268     , { "Transfer-Encoding", "identity" }
269     , { "Content-Length", "5" }
270     }
271   ,.body= "World"
272   }
273 
274 #define POST_CHUNKED_ALL_YOUR_BASE 8
275 , {.name= "post - chunked body: all your base are belong to us"
276   ,.type= HTTP_REQUEST
277   ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
278          "Transfer-Encoding: chunked\r\n"
279          "\r\n"
280          "1e\r\nall your base are belong to us\r\n"
281          "0\r\n"
282          "\r\n"
283   ,.should_keep_alive= TRUE
284   ,.message_complete_on_eof= FALSE
285   ,.http_major= 1
286   ,.http_minor= 1
287   ,.method= HTTP_POST
288   ,.query_string= ""
289   ,.fragment= ""
290   ,.request_path= "/post_chunked_all_your_base"
291   ,.request_url= "/post_chunked_all_your_base"
292   ,.num_headers= 1
293   ,.headers=
294     { { "Transfer-Encoding" , "chunked" }
295     }
296   ,.body= "all your base are belong to us"
297   }
298 
299 #define TWO_CHUNKS_MULT_ZERO_END 9
300 , {.name= "two chunks ; triple zero ending"
301   ,.type= HTTP_REQUEST
302   ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
303          "Transfer-Encoding: chunked\r\n"
304          "\r\n"
305          "5\r\nhello\r\n"
306          "6\r\n world\r\n"
307          "000\r\n"
308          "\r\n"
309   ,.should_keep_alive= TRUE
310   ,.message_complete_on_eof= FALSE
311   ,.http_major= 1
312   ,.http_minor= 1
313   ,.method= HTTP_POST
314   ,.query_string= ""
315   ,.fragment= ""
316   ,.request_path= "/two_chunks_mult_zero_end"
317   ,.request_url= "/two_chunks_mult_zero_end"
318   ,.num_headers= 1
319   ,.headers=
320     { { "Transfer-Encoding", "chunked" }
321     }
322   ,.body= "hello world"
323   }
324 
325 #define CHUNKED_W_TRAILING_HEADERS 10
326 , {.name= "chunked with trailing headers. blech."
327   ,.type= HTTP_REQUEST
328   ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
329          "Transfer-Encoding: chunked\r\n"
330          "\r\n"
331          "5\r\nhello\r\n"
332          "6\r\n world\r\n"
333          "0\r\n"
334          "Vary: *\r\n"
335          "Content-Type: text/plain\r\n"
336          "\r\n"
337   ,.should_keep_alive= TRUE
338   ,.message_complete_on_eof= FALSE
339   ,.http_major= 1
340   ,.http_minor= 1
341   ,.method= HTTP_POST
342   ,.query_string= ""
343   ,.fragment= ""
344   ,.request_path= "/chunked_w_trailing_headers"
345   ,.request_url= "/chunked_w_trailing_headers"
346   ,.num_headers= 3
347   ,.headers=
348     { { "Transfer-Encoding",  "chunked" }
349     , { "Vary", "*" }
350     , { "Content-Type", "text/plain" }
351     }
352   ,.body= "hello world"
353   }
354 
355 #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
356 , {.name= "with bullshit after the length"
357   ,.type= HTTP_REQUEST
358   ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
359          "Transfer-Encoding: chunked\r\n"
360          "\r\n"
361          "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
362          "6; blahblah; blah\r\n world\r\n"
363          "0\r\n"
364          "\r\n"
365   ,.should_keep_alive= TRUE
366   ,.message_complete_on_eof= FALSE
367   ,.http_major= 1
368   ,.http_minor= 1
369   ,.method= HTTP_POST
370   ,.query_string= ""
371   ,.fragment= ""
372   ,.request_path= "/chunked_w_bullshit_after_length"
373   ,.request_url= "/chunked_w_bullshit_after_length"
374   ,.num_headers= 1
375   ,.headers=
376     { { "Transfer-Encoding", "chunked" }
377     }
378   ,.body= "hello world"
379   }
380 
381 #define WITH_QUOTES 12
382 , {.name= "with quotes"
383   ,.type= HTTP_REQUEST
384   ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
385   ,.should_keep_alive= TRUE
386   ,.message_complete_on_eof= FALSE
387   ,.http_major= 1
388   ,.http_minor= 1
389   ,.method= HTTP_GET
390   ,.query_string= "foo=\"bar\""
391   ,.fragment= ""
392   ,.request_path= "/with_\"stupid\"_quotes"
393   ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
394   ,.num_headers= 0
395   ,.headers= { }
396   ,.body= ""
397   }
398 
399 #define APACHEBENCH_GET 13
400 /* The server receiving this request SHOULD NOT wait for EOF
401  * to know that content-length == 0.
402  * How to represent this in a unit test? message_complete_on_eof
403  * Compare with NO_CONTENT_LENGTH_RESPONSE.
404  */
405 , {.name = "apachebench get"
406   ,.type= HTTP_REQUEST
407   ,.raw= "GET /test HTTP/1.0\r\n"
408          "Host: 0.0.0.0:5000\r\n"
409          "User-Agent: ApacheBench/2.3\r\n"
410          "Accept: */*\r\n\r\n"
411   ,.should_keep_alive= FALSE
412   ,.message_complete_on_eof= FALSE
413   ,.http_major= 1
414   ,.http_minor= 0
415   ,.method= HTTP_GET
416   ,.query_string= ""
417   ,.fragment= ""
418   ,.request_path= "/test"
419   ,.request_url= "/test"
420   ,.num_headers= 3
421   ,.headers= { { "Host", "0.0.0.0:5000" }
422              , { "User-Agent", "ApacheBench/2.3" }
423              , { "Accept", "*/*" }
424              }
425   ,.body= ""
426   }
427 
428 #define QUERY_URL_WITH_QUESTION_MARK_GET 14
429 /* Some clients include '?' characters in query strings.
430  */
431 , {.name = "query url with question mark"
432   ,.type= HTTP_REQUEST
433   ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
434   ,.should_keep_alive= TRUE
435   ,.message_complete_on_eof= FALSE
436   ,.http_major= 1
437   ,.http_minor= 1
438   ,.method= HTTP_GET
439   ,.query_string= "foo=bar?baz"
440   ,.fragment= ""
441   ,.request_path= "/test.cgi"
442   ,.request_url= "/test.cgi?foo=bar?baz"
443   ,.num_headers= 0
444   ,.headers= {}
445   ,.body= ""
446   }
447 
448 #define PREFIX_NEWLINE_GET 15
449 /* Some clients, especially after a POST in a keep-alive connection,
450  * will send an extra CRLF before the next request
451  */
452 , {.name = "newline prefix get"
453   ,.type= HTTP_REQUEST
454   ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
455   ,.should_keep_alive= TRUE
456   ,.message_complete_on_eof= FALSE
457   ,.http_major= 1
458   ,.http_minor= 1
459   ,.method= HTTP_GET
460   ,.query_string= ""
461   ,.fragment= ""
462   ,.request_path= "/test"
463   ,.request_url= "/test"
464   ,.num_headers= 0
465   ,.headers= { }
466   ,.body= ""
467   }
468 
469 #define UPGRADE_REQUEST 16
470 , {.name = "upgrade request"
471   ,.type= HTTP_REQUEST
472   ,.raw= "GET /demo HTTP/1.1\r\n"
473          "Host: example.com\r\n"
474          "Connection: Upgrade\r\n"
475          "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
476          "Sec-WebSocket-Protocol: sample\r\n"
477          "Upgrade: WebSocket\r\n"
478          "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
479          "Origin: http://example.com\r\n"
480          "\r\n"
481          "Hot diggity dogg"
482   ,.should_keep_alive= TRUE
483   ,.message_complete_on_eof= FALSE
484   ,.http_major= 1
485   ,.http_minor= 1
486   ,.method= HTTP_GET
487   ,.query_string= ""
488   ,.fragment= ""
489   ,.request_path= "/demo"
490   ,.request_url= "/demo"
491   ,.num_headers= 7
492   ,.upgrade="Hot diggity dogg"
493   ,.headers= { { "Host", "example.com" }
494              , { "Connection", "Upgrade" }
495              , { "Sec-WebSocket-Key2", "12998 5 Y3 1  .P00" }
496              , { "Sec-WebSocket-Protocol", "sample" }
497              , { "Upgrade", "WebSocket" }
498              , { "Sec-WebSocket-Key1", "4 @1  46546xW%0l 1 5" }
499              , { "Origin", "http://example.com" }
500              }
501   ,.body= ""
502   }
503 
504 #define CONNECT_REQUEST 17
505 , {.name = "connect request"
506   ,.type= HTTP_REQUEST
507   ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
508          "User-agent: Mozilla/1.1N\r\n"
509          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
510          "\r\n"
511          "some data\r\n"
512          "and yet even more data"
513   ,.should_keep_alive= FALSE
514   ,.message_complete_on_eof= FALSE
515   ,.http_major= 1
516   ,.http_minor= 0
517   ,.method= HTTP_CONNECT
518   ,.query_string= ""
519   ,.fragment= ""
520   ,.request_path= ""
521   ,.request_url= "0-home0.netscape.com:443"
522   ,.num_headers= 2
523   ,.upgrade="some data\r\nand yet even more data"
524   ,.headers= { { "User-agent", "Mozilla/1.1N" }
525              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
526              }
527   ,.body= ""
528   }
529 
530 #define REPORT_REQ 18
531 , {.name= "report request"
532   ,.type= HTTP_REQUEST
533   ,.raw= "REPORT /test HTTP/1.1\r\n"
534          "\r\n"
535   ,.should_keep_alive= TRUE
536   ,.message_complete_on_eof= FALSE
537   ,.http_major= 1
538   ,.http_minor= 1
539   ,.method= HTTP_REPORT
540   ,.query_string= ""
541   ,.fragment= ""
542   ,.request_path= "/test"
543   ,.request_url= "/test"
544   ,.num_headers= 0
545   ,.headers= {}
546   ,.body= ""
547   }
548 
549 #define NO_HTTP_VERSION 19
550 , {.name= "request with no http version"
551   ,.type= HTTP_REQUEST
552   ,.raw= "GET /\r\n"
553          "\r\n"
554   ,.should_keep_alive= FALSE
555   ,.message_complete_on_eof= FALSE
556   ,.http_major= 0
557   ,.http_minor= 9
558   ,.method= HTTP_GET
559   ,.query_string= ""
560   ,.fragment= ""
561   ,.request_path= "/"
562   ,.request_url= "/"
563   ,.num_headers= 0
564   ,.headers= {}
565   ,.body= ""
566   }
567 
568 #define MSEARCH_REQ 20
569 , {.name= "m-search request"
570   ,.type= HTTP_REQUEST
571   ,.raw= "M-SEARCH * HTTP/1.1\r\n"
572          "HOST: 239.255.255.250:1900\r\n"
573          "MAN: \"ssdp:discover\"\r\n"
574          "ST: \"ssdp:all\"\r\n"
575          "\r\n"
576   ,.should_keep_alive= TRUE
577   ,.message_complete_on_eof= FALSE
578   ,.http_major= 1
579   ,.http_minor= 1
580   ,.method= HTTP_MSEARCH
581   ,.query_string= ""
582   ,.fragment= ""
583   ,.request_path= "*"
584   ,.request_url= "*"
585   ,.num_headers= 3
586   ,.headers= { { "HOST", "239.255.255.250:1900" }
587              , { "MAN", "\"ssdp:discover\"" }
588              , { "ST", "\"ssdp:all\"" }
589              }
590   ,.body= ""
591   }
592 
593 #define LINE_FOLDING_IN_HEADER 21
594 , {.name= "line folding in header value"
595   ,.type= HTTP_REQUEST
596   ,.raw= "GET / HTTP/1.1\r\n"
597          "Line1:   abc\r\n"
598          "\tdef\r\n"
599          " ghi\r\n"
600          "\t\tjkl\r\n"
601          "  mno \r\n"
602          "\t \tqrs\r\n"
603          "Line2: \t line2\t\r\n"
604          "\r\n"
605   ,.should_keep_alive= TRUE
606   ,.message_complete_on_eof= FALSE
607   ,.http_major= 1
608   ,.http_minor= 1
609   ,.method= HTTP_GET
610   ,.query_string= ""
611   ,.fragment= ""
612   ,.request_path= "/"
613   ,.request_url= "/"
614   ,.num_headers= 2
615   ,.headers= { { "Line1", "abcdefghijklmno qrs" }
616              , { "Line2", "line2\t" }
617              }
618   ,.body= ""
619   }
620 
621 
622 #define QUERY_TERMINATED_HOST 22
623 , {.name= "host terminated by a query string"
624   ,.type= HTTP_REQUEST
625   ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
626          "\r\n"
627   ,.should_keep_alive= TRUE
628   ,.message_complete_on_eof= FALSE
629   ,.http_major= 1
630   ,.http_minor= 1
631   ,.method= HTTP_GET
632   ,.query_string= "hail=all"
633   ,.fragment= ""
634   ,.request_path= ""
635   ,.request_url= "http://hypnotoad.org?hail=all"
636   ,.host= "hypnotoad.org"
637   ,.num_headers= 0
638   ,.headers= { }
639   ,.body= ""
640   }
641 
642 #define QUERY_TERMINATED_HOSTPORT 23
643 , {.name= "host:port terminated by a query string"
644   ,.type= HTTP_REQUEST
645   ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
646          "\r\n"
647   ,.should_keep_alive= TRUE
648   ,.message_complete_on_eof= FALSE
649   ,.http_major= 1
650   ,.http_minor= 1
651   ,.method= HTTP_GET
652   ,.query_string= "hail=all"
653   ,.fragment= ""
654   ,.request_path= ""
655   ,.request_url= "http://hypnotoad.org:1234?hail=all"
656   ,.host= "hypnotoad.org"
657   ,.port= 1234
658   ,.num_headers= 0
659   ,.headers= { }
660   ,.body= ""
661   }
662 
663 #define SPACE_TERMINATED_HOSTPORT 24
664 , {.name= "host:port terminated by a space"
665   ,.type= HTTP_REQUEST
666   ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
667          "\r\n"
668   ,.should_keep_alive= TRUE
669   ,.message_complete_on_eof= FALSE
670   ,.http_major= 1
671   ,.http_minor= 1
672   ,.method= HTTP_GET
673   ,.query_string= ""
674   ,.fragment= ""
675   ,.request_path= ""
676   ,.request_url= "http://hypnotoad.org:1234"
677   ,.host= "hypnotoad.org"
678   ,.port= 1234
679   ,.num_headers= 0
680   ,.headers= { }
681   ,.body= ""
682   }
683 
684 #define PATCH_REQ 25
685 , {.name = "PATCH request"
686   ,.type= HTTP_REQUEST
687   ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
688          "Host: www.example.com\r\n"
689          "Content-Type: application/example\r\n"
690          "If-Match: \"e0023aa4e\"\r\n"
691          "Content-Length: 10\r\n"
692          "\r\n"
693          "cccccccccc"
694   ,.should_keep_alive= TRUE
695   ,.message_complete_on_eof= FALSE
696   ,.http_major= 1
697   ,.http_minor= 1
698   ,.method= HTTP_PATCH
699   ,.query_string= ""
700   ,.fragment= ""
701   ,.request_path= "/file.txt"
702   ,.request_url= "/file.txt"
703   ,.num_headers= 4
704   ,.headers= { { "Host", "www.example.com" }
705              , { "Content-Type", "application/example" }
706              , { "If-Match", "\"e0023aa4e\"" }
707              , { "Content-Length", "10" }
708              }
709   ,.body= "cccccccccc"
710   }
711 
712 #define CONNECT_CAPS_REQUEST 26
713 , {.name = "connect caps request"
714   ,.type= HTTP_REQUEST
715   ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
716          "User-agent: Mozilla/1.1N\r\n"
717          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
718          "\r\n"
719   ,.should_keep_alive= FALSE
720   ,.message_complete_on_eof= FALSE
721   ,.http_major= 1
722   ,.http_minor= 0
723   ,.method= HTTP_CONNECT
724   ,.query_string= ""
725   ,.fragment= ""
726   ,.request_path= ""
727   ,.request_url= "HOME0.NETSCAPE.COM:443"
728   ,.num_headers= 2
729   ,.upgrade=""
730   ,.headers= { { "User-agent", "Mozilla/1.1N" }
731              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
732              }
733   ,.body= ""
734   }
735 
736 #if !HTTP_PARSER_STRICT
737 #define UTF8_PATH_REQ 27
738 , {.name= "utf-8 path request"
739   ,.type= HTTP_REQUEST
740   ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
741          "Host: github.com\r\n"
742          "\r\n"
743   ,.should_keep_alive= TRUE
744   ,.message_complete_on_eof= FALSE
745   ,.http_major= 1
746   ,.http_minor= 1
747   ,.method= HTTP_GET
748   ,.query_string= "q=1"
749   ,.fragment= "narf"
750   ,.request_path= "/δ¶/δt/pope"
751   ,.request_url= "/δ¶/δt/pope?q=1#narf"
752   ,.num_headers= 1
753   ,.headers= { {"Host", "github.com" }
754              }
755   ,.body= ""
756   }
757 
758 #define HOSTNAME_UNDERSCORE 28
759 , {.name = "hostname underscore"
760   ,.type= HTTP_REQUEST
761   ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
762          "User-agent: Mozilla/1.1N\r\n"
763          "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
764          "\r\n"
765   ,.should_keep_alive= FALSE
766   ,.message_complete_on_eof= FALSE
767   ,.http_major= 1
768   ,.http_minor= 0
769   ,.method= HTTP_CONNECT
770   ,.query_string= ""
771   ,.fragment= ""
772   ,.request_path= ""
773   ,.request_url= "home_0.netscape.com:443"
774   ,.num_headers= 2
775   ,.upgrade=""
776   ,.headers= { { "User-agent", "Mozilla/1.1N" }
777              , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
778              }
779   ,.body= ""
780   }
781 #endif  /* !HTTP_PARSER_STRICT */
782 
783 /* see https://github.com/ry/http-parser/issues/47 */
784 #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
785 , {.name = "eat CRLF between requests, no \"Connection: close\" header"
786   ,.raw= "POST / HTTP/1.1\r\n"
787          "Host: www.example.com\r\n"
788          "Content-Type: application/x-www-form-urlencoded\r\n"
789          "Content-Length: 4\r\n"
790          "\r\n"
791          "q=42\r\n" /* note the trailing CRLF */
792   ,.should_keep_alive= TRUE
793   ,.message_complete_on_eof= FALSE
794   ,.http_major= 1
795   ,.http_minor= 1
796   ,.method= HTTP_POST
797   ,.query_string= ""
798   ,.fragment= ""
799   ,.request_path= "/"
800   ,.request_url= "/"
801   ,.num_headers= 3
802   ,.upgrade= 0
803   ,.headers= { { "Host", "www.example.com" }
804              , { "Content-Type", "application/x-www-form-urlencoded" }
805              , { "Content-Length", "4" }
806              }
807   ,.body= "q=42"
808   }
809 
810 /* see https://github.com/ry/http-parser/issues/47 */
811 #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
812 , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
813   ,.raw= "POST / HTTP/1.1\r\n"
814          "Host: www.example.com\r\n"
815          "Content-Type: application/x-www-form-urlencoded\r\n"
816          "Content-Length: 4\r\n"
817          "Connection: close\r\n"
818          "\r\n"
819          "q=42\r\n" /* note the trailing CRLF */
820   ,.should_keep_alive= FALSE
821   ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
822   ,.http_major= 1
823   ,.http_minor= 1
824   ,.method= HTTP_POST
825   ,.query_string= ""
826   ,.fragment= ""
827   ,.request_path= "/"
828   ,.request_url= "/"
829   ,.num_headers= 4
830   ,.upgrade= 0
831   ,.headers= { { "Host", "www.example.com" }
832              , { "Content-Type", "application/x-www-form-urlencoded" }
833              , { "Content-Length", "4" }
834              , { "Connection", "close" }
835              }
836   ,.body= "q=42"
837   }
838 
839 #define PURGE_REQ 31
840 , {.name = "PURGE request"
841   ,.type= HTTP_REQUEST
842   ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
843          "Host: www.example.com\r\n"
844          "\r\n"
845   ,.should_keep_alive= TRUE
846   ,.message_complete_on_eof= FALSE
847   ,.http_major= 1
848   ,.http_minor= 1
849   ,.method= HTTP_PURGE
850   ,.query_string= ""
851   ,.fragment= ""
852   ,.request_path= "/file.txt"
853   ,.request_url= "/file.txt"
854   ,.num_headers= 1
855   ,.headers= { { "Host", "www.example.com" } }
856   ,.body= ""
857   }
858 
859 #define SEARCH_REQ 32
860 , {.name = "SEARCH request"
861   ,.type= HTTP_REQUEST
862   ,.raw= "SEARCH / HTTP/1.1\r\n"
863          "Host: www.example.com\r\n"
864          "\r\n"
865   ,.should_keep_alive= TRUE
866   ,.message_complete_on_eof= FALSE
867   ,.http_major= 1
868   ,.http_minor= 1
869   ,.method= HTTP_SEARCH
870   ,.query_string= ""
871   ,.fragment= ""
872   ,.request_path= "/"
873   ,.request_url= "/"
874   ,.num_headers= 1
875   ,.headers= { { "Host", "www.example.com" } }
876   ,.body= ""
877   }
878 
879 #define PROXY_WITH_BASIC_AUTH 33
880 , {.name= "host:port and basic_auth"
881   ,.type= HTTP_REQUEST
882   ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
883          "\r\n"
884   ,.should_keep_alive= TRUE
885   ,.message_complete_on_eof= FALSE
886   ,.http_major= 1
887   ,.http_minor= 1
888   ,.method= HTTP_GET
889   ,.fragment= ""
890   ,.request_path= "/toto"
891   ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
892   ,.host= "hypnotoad.org"
893   ,.userinfo= "a%12:b!&*$"
894   ,.port= 1234
895   ,.num_headers= 0
896   ,.headers= { }
897   ,.body= ""
898   }
899 
900 
901 , {.name= NULL } /* sentinel */
902 };
903 
904 /* * R E S P O N S E S * */
905 const struct message responses[] =
906 #define GOOGLE_301 0
907 { {.name= "google 301"
908   ,.type= HTTP_RESPONSE
909   ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
910          "Location: http://www.google.com/\r\n"
911          "Content-Type: text/html; charset=UTF-8\r\n"
912          "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
913          "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
914          "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
915          "Cache-Control: public, max-age=2592000\r\n"
916          "Server: gws\r\n"
917          "Content-Length:  219  \r\n"
918          "\r\n"
919          "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
920          "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
921          "<H1>301 Moved</H1>\n"
922          "The document has moved\n"
923          "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
924          "</BODY></HTML>\r\n"
925   ,.should_keep_alive= TRUE
926   ,.message_complete_on_eof= FALSE
927   ,.http_major= 1
928   ,.http_minor= 1
929   ,.status_code= 301
930   ,.num_headers= 8
931   ,.headers=
932     { { "Location", "http://www.google.com/" }
933     , { "Content-Type", "text/html; charset=UTF-8" }
934     , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
935     , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
936     , { "X-$PrototypeBI-Version", "1.6.0.3" }
937     , { "Cache-Control", "public, max-age=2592000" }
938     , { "Server", "gws" }
939     , { "Content-Length", "219  " }
940     }
941   ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
942           "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
943           "<H1>301 Moved</H1>\n"
944           "The document has moved\n"
945           "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
946           "</BODY></HTML>\r\n"
947   }
948 
949 #define NO_CONTENT_LENGTH_RESPONSE 1
950 /* The client should wait for the server's EOF. That is, when content-length
951  * is not specified, and "Connection: close", the end of body is specified
952  * by the EOF.
953  * Compare with APACHEBENCH_GET
954  */
955 , {.name= "no content-length response"
956   ,.type= HTTP_RESPONSE
957   ,.raw= "HTTP/1.1 200 OK\r\n"
958          "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
959          "Server: Apache\r\n"
960          "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
961          "Content-Type: text/xml; charset=utf-8\r\n"
962          "Connection: close\r\n"
963          "\r\n"
964          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
965          "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
966          "  <SOAP-ENV:Body>\n"
967          "    <SOAP-ENV:Fault>\n"
968          "       <faultcode>SOAP-ENV:Client</faultcode>\n"
969          "       <faultstring>Client Error</faultstring>\n"
970          "    </SOAP-ENV:Fault>\n"
971          "  </SOAP-ENV:Body>\n"
972          "</SOAP-ENV:Envelope>"
973   ,.should_keep_alive= FALSE
974   ,.message_complete_on_eof= TRUE
975   ,.http_major= 1
976   ,.http_minor= 1
977   ,.status_code= 200
978   ,.num_headers= 5
979   ,.headers=
980     { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
981     , { "Server", "Apache" }
982     , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
983     , { "Content-Type", "text/xml; charset=utf-8" }
984     , { "Connection", "close" }
985     }
986   ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
987           "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
988           "  <SOAP-ENV:Body>\n"
989           "    <SOAP-ENV:Fault>\n"
990           "       <faultcode>SOAP-ENV:Client</faultcode>\n"
991           "       <faultstring>Client Error</faultstring>\n"
992           "    </SOAP-ENV:Fault>\n"
993           "  </SOAP-ENV:Body>\n"
994           "</SOAP-ENV:Envelope>"
995   }
996 
997 #define NO_HEADERS_NO_BODY_404 2
998 , {.name= "404 no headers no body"
999   ,.type= HTTP_RESPONSE
1000   ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
1001   ,.should_keep_alive= FALSE
1002   ,.message_complete_on_eof= TRUE
1003   ,.http_major= 1
1004   ,.http_minor= 1
1005   ,.status_code= 404
1006   ,.num_headers= 0
1007   ,.headers= {}
1008   ,.body_size= 0
1009   ,.body= ""
1010   }
1011 
1012 #define NO_REASON_PHRASE 3
1013 , {.name= "301 no response phrase"
1014   ,.type= HTTP_RESPONSE
1015   ,.raw= "HTTP/1.1 301\r\n\r\n"
1016   ,.should_keep_alive = FALSE
1017   ,.message_complete_on_eof= TRUE
1018   ,.http_major= 1
1019   ,.http_minor= 1
1020   ,.status_code= 301
1021   ,.num_headers= 0
1022   ,.headers= {}
1023   ,.body= ""
1024   }
1025 
1026 #define TRAILING_SPACE_ON_CHUNKED_BODY 4
1027 , {.name="200 trailing space on chunked body"
1028   ,.type= HTTP_RESPONSE
1029   ,.raw= "HTTP/1.1 200 OK\r\n"
1030          "Content-Type: text/plain\r\n"
1031          "Transfer-Encoding: chunked\r\n"
1032          "\r\n"
1033          "25  \r\n"
1034          "This is the data in the first chunk\r\n"
1035          "\r\n"
1036          "1C\r\n"
1037          "and this is the second one\r\n"
1038          "\r\n"
1039          "0  \r\n"
1040          "\r\n"
1041   ,.should_keep_alive= TRUE
1042   ,.message_complete_on_eof= FALSE
1043   ,.http_major= 1
1044   ,.http_minor= 1
1045   ,.status_code= 200
1046   ,.num_headers= 2
1047   ,.headers=
1048     { {"Content-Type", "text/plain" }
1049     , {"Transfer-Encoding", "chunked" }
1050     }
1051   ,.body_size = 37+28
1052   ,.body =
1053          "This is the data in the first chunk\r\n"
1054          "and this is the second one\r\n"
1055 
1056   }
1057 
1058 #define NO_CARRIAGE_RET 5
1059 , {.name="no carriage ret"
1060   ,.type= HTTP_RESPONSE
1061   ,.raw= "HTTP/1.1 200 OK\n"
1062          "Content-Type: text/html; charset=utf-8\n"
1063          "Connection: close\n"
1064          "\n"
1065          "these headers are from http://news.ycombinator.com/"
1066   ,.should_keep_alive= FALSE
1067   ,.message_complete_on_eof= TRUE
1068   ,.http_major= 1
1069   ,.http_minor= 1
1070   ,.status_code= 200
1071   ,.num_headers= 2
1072   ,.headers=
1073     { {"Content-Type", "text/html; charset=utf-8" }
1074     , {"Connection", "close" }
1075     }
1076   ,.body= "these headers are from http://news.ycombinator.com/"
1077   }
1078 
1079 #define PROXY_CONNECTION 6
1080 , {.name="proxy connection"
1081   ,.type= HTTP_RESPONSE
1082   ,.raw= "HTTP/1.1 200 OK\r\n"
1083          "Content-Type: text/html; charset=UTF-8\r\n"
1084          "Content-Length: 11\r\n"
1085          "Proxy-Connection: close\r\n"
1086          "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
1087          "\r\n"
1088          "hello world"
1089   ,.should_keep_alive= FALSE
1090   ,.message_complete_on_eof= FALSE
1091   ,.http_major= 1
1092   ,.http_minor= 1
1093   ,.status_code= 200
1094   ,.num_headers= 4
1095   ,.headers=
1096     { {"Content-Type", "text/html; charset=UTF-8" }
1097     , {"Content-Length", "11" }
1098     , {"Proxy-Connection", "close" }
1099     , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
1100     }
1101   ,.body= "hello world"
1102   }
1103 
1104 #define UNDERSTORE_HEADER_KEY 7
1105   // shown by
1106   // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
1107 , {.name="underscore header key"
1108   ,.type= HTTP_RESPONSE
1109   ,.raw= "HTTP/1.1 200 OK\r\n"
1110          "Server: DCLK-AdSvr\r\n"
1111          "Content-Type: text/xml\r\n"
1112          "Content-Length: 0\r\n"
1113          "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
1114   ,.should_keep_alive= TRUE
1115   ,.message_complete_on_eof= FALSE
1116   ,.http_major= 1
1117   ,.http_minor= 1
1118   ,.status_code= 200
1119   ,.num_headers= 4
1120   ,.headers=
1121     { {"Server", "DCLK-AdSvr" }
1122     , {"Content-Type", "text/xml" }
1123     , {"Content-Length", "0" }
1124     , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1125     }
1126   ,.body= ""
1127   }
1128 
1129 #define BONJOUR_MADAME_FR 8
1130 /* The client should not merge two headers fields when the first one doesn't
1131  * have a value.
1132  */
1133 , {.name= "bonjourmadame.fr"
1134   ,.type= HTTP_RESPONSE
1135   ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
1136          "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
1137          "Server: Apache/2.2.3 (Red Hat)\r\n"
1138          "Cache-Control: public\r\n"
1139          "Pragma: \r\n"
1140          "Location: http://www.bonjourmadame.fr/\r\n"
1141          "Vary: Accept-Encoding\r\n"
1142          "Content-Length: 0\r\n"
1143          "Content-Type: text/html; charset=UTF-8\r\n"
1144          "Connection: keep-alive\r\n"
1145          "\r\n"
1146   ,.should_keep_alive= TRUE
1147   ,.message_complete_on_eof= FALSE
1148   ,.http_major= 1
1149   ,.http_minor= 0
1150   ,.status_code= 301
1151   ,.num_headers= 9
1152   ,.headers=
1153     { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1154     , { "Server", "Apache/2.2.3 (Red Hat)" }
1155     , { "Cache-Control", "public" }
1156     , { "Pragma", "" }
1157     , { "Location", "http://www.bonjourmadame.fr/" }
1158     , { "Vary",  "Accept-Encoding" }
1159     , { "Content-Length", "0" }
1160     , { "Content-Type", "text/html; charset=UTF-8" }
1161     , { "Connection", "keep-alive" }
1162     }
1163   ,.body= ""
1164   }
1165 
1166 #define RES_FIELD_UNDERSCORE 9
1167 /* Should handle spaces in header fields */
1168 , {.name= "field underscore"
1169   ,.type= HTTP_RESPONSE
1170   ,.raw= "HTTP/1.1 200 OK\r\n"
1171          "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1172          "Server: Apache\r\n"
1173          "Cache-Control: no-cache, must-revalidate\r\n"
1174          "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1175          ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1176          "Vary: Accept-Encoding\r\n"
1177          "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1178          "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1179          "Transfer-Encoding: chunked\r\n"
1180          "Content-Type: text/html\r\n"
1181          "Connection: close\r\n"
1182          "\r\n"
1183          "0\r\n\r\n"
1184   ,.should_keep_alive= FALSE
1185   ,.message_complete_on_eof= FALSE
1186   ,.http_major= 1
1187   ,.http_minor= 1
1188   ,.status_code= 200
1189   ,.num_headers= 11
1190   ,.headers=
1191     { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1192     , { "Server", "Apache" }
1193     , { "Cache-Control", "no-cache, must-revalidate" }
1194     , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1195     , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1196     , { "Vary", "Accept-Encoding" }
1197     , { "_eep-Alive", "timeout=45" }
1198     , { "_onnection", "Keep-Alive" }
1199     , { "Transfer-Encoding", "chunked" }
1200     , { "Content-Type", "text/html" }
1201     , { "Connection", "close" }
1202     }
1203   ,.body= ""
1204   }
1205 
1206 #define NON_ASCII_IN_STATUS_LINE 10
1207 /* Should handle non-ASCII in status line */
1208 , {.name= "non-ASCII in status line"
1209   ,.type= HTTP_RESPONSE
1210   ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1211          "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1212          "Content-Length: 0\r\n"
1213          "Connection: close\r\n"
1214          "\r\n"
1215   ,.should_keep_alive= FALSE
1216   ,.message_complete_on_eof= FALSE
1217   ,.http_major= 1
1218   ,.http_minor= 1
1219   ,.status_code= 500
1220   ,.num_headers= 3
1221   ,.headers=
1222     { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1223     , { "Content-Length", "0" }
1224     , { "Connection", "close" }
1225     }
1226   ,.body= ""
1227   }
1228 
1229 #define HTTP_VERSION_0_9 11
1230 /* Should handle HTTP/0.9 */
1231 , {.name= "http version 0.9"
1232   ,.type= HTTP_RESPONSE
1233   ,.raw= "HTTP/0.9 200 OK\r\n"
1234          "\r\n"
1235   ,.should_keep_alive= FALSE
1236   ,.message_complete_on_eof= TRUE
1237   ,.http_major= 0
1238   ,.http_minor= 9
1239   ,.status_code= 200
1240   ,.num_headers= 0
1241   ,.headers=
1242     {}
1243   ,.body= ""
1244   }
1245 
1246 #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1247 /* The client should wait for the server's EOF. That is, when neither
1248  * content-length nor transfer-encoding is specified, the end of body
1249  * is specified by the EOF.
1250  */
1251 , {.name= "neither content-length nor transfer-encoding response"
1252   ,.type= HTTP_RESPONSE
1253   ,.raw= "HTTP/1.1 200 OK\r\n"
1254          "Content-Type: text/plain\r\n"
1255          "\r\n"
1256          "hello world"
1257   ,.should_keep_alive= FALSE
1258   ,.message_complete_on_eof= TRUE
1259   ,.http_major= 1
1260   ,.http_minor= 1
1261   ,.status_code= 200
1262   ,.num_headers= 1
1263   ,.headers=
1264     { { "Content-Type", "text/plain" }
1265     }
1266   ,.body= "hello world"
1267   }
1268 
1269 #define NO_BODY_HTTP10_KA_200 13
1270 , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1271   ,.type= HTTP_RESPONSE
1272   ,.raw= "HTTP/1.0 200 OK\r\n"
1273          "Connection: keep-alive\r\n"
1274          "\r\n"
1275   ,.should_keep_alive= FALSE
1276   ,.message_complete_on_eof= TRUE
1277   ,.http_major= 1
1278   ,.http_minor= 0
1279   ,.status_code= 200
1280   ,.num_headers= 1
1281   ,.headers=
1282     { { "Connection", "keep-alive" }
1283     }
1284   ,.body_size= 0
1285   ,.body= ""
1286   }
1287 
1288 #define NO_BODY_HTTP10_KA_204 14
1289 , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1290   ,.type= HTTP_RESPONSE
1291   ,.raw= "HTTP/1.0 204 No content\r\n"
1292          "Connection: keep-alive\r\n"
1293          "\r\n"
1294   ,.should_keep_alive= TRUE
1295   ,.message_complete_on_eof= FALSE
1296   ,.http_major= 1
1297   ,.http_minor= 0
1298   ,.status_code= 204
1299   ,.num_headers= 1
1300   ,.headers=
1301     { { "Connection", "keep-alive" }
1302     }
1303   ,.body_size= 0
1304   ,.body= ""
1305   }
1306 
1307 #define NO_BODY_HTTP11_KA_200 15
1308 , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1309   ,.type= HTTP_RESPONSE
1310   ,.raw= "HTTP/1.1 200 OK\r\n"
1311          "\r\n"
1312   ,.should_keep_alive= FALSE
1313   ,.message_complete_on_eof= TRUE
1314   ,.http_major= 1
1315   ,.http_minor= 1
1316   ,.status_code= 200
1317   ,.num_headers= 0
1318   ,.headers={}
1319   ,.body_size= 0
1320   ,.body= ""
1321   }
1322 
1323 #define NO_BODY_HTTP11_KA_204 16
1324 , {.name= "HTTP/1.1 with a 204 status"
1325   ,.type= HTTP_RESPONSE
1326   ,.raw= "HTTP/1.1 204 No content\r\n"
1327          "\r\n"
1328   ,.should_keep_alive= TRUE
1329   ,.message_complete_on_eof= FALSE
1330   ,.http_major= 1
1331   ,.http_minor= 1
1332   ,.status_code= 204
1333   ,.num_headers= 0
1334   ,.headers={}
1335   ,.body_size= 0
1336   ,.body= ""
1337   }
1338 
1339 #define NO_BODY_HTTP11_NOKA_204 17
1340 , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1341   ,.type= HTTP_RESPONSE
1342   ,.raw= "HTTP/1.1 204 No content\r\n"
1343          "Connection: close\r\n"
1344          "\r\n"
1345   ,.should_keep_alive= FALSE
1346   ,.message_complete_on_eof= FALSE
1347   ,.http_major= 1
1348   ,.http_minor= 1
1349   ,.status_code= 204
1350   ,.num_headers= 1
1351   ,.headers=
1352     { { "Connection", "close" }
1353     }
1354   ,.body_size= 0
1355   ,.body= ""
1356   }
1357 
1358 #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1359 , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1360   ,.type= HTTP_RESPONSE
1361   ,.raw= "HTTP/1.1 200 OK\r\n"
1362          "Transfer-Encoding: chunked\r\n"
1363          "\r\n"
1364          "0\r\n"
1365          "\r\n"
1366   ,.should_keep_alive= TRUE
1367   ,.message_complete_on_eof= FALSE
1368   ,.http_major= 1
1369   ,.http_minor= 1
1370   ,.status_code= 200
1371   ,.num_headers= 1
1372   ,.headers=
1373     { { "Transfer-Encoding", "chunked" }
1374     }
1375   ,.body_size= 0
1376   ,.body= ""
1377   }
1378 
1379 #if !HTTP_PARSER_STRICT
1380 #define SPACE_IN_FIELD_RES 19
1381 /* Should handle spaces in header fields */
1382 , {.name= "field space"
1383   ,.type= HTTP_RESPONSE
1384   ,.raw= "HTTP/1.1 200 OK\r\n"
1385          "Server: Microsoft-IIS/6.0\r\n"
1386          "X-Powered-By: ASP.NET\r\n"
1387          "en-US Content-Type: text/xml\r\n" /* this is the problem */
1388          "Content-Type: text/xml\r\n"
1389          "Content-Length: 16\r\n"
1390          "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
1391          "Connection: keep-alive\r\n"
1392          "\r\n"
1393          "<xml>hello</xml>" /* fake body */
1394   ,.should_keep_alive= TRUE
1395   ,.message_complete_on_eof= FALSE
1396   ,.http_major= 1
1397   ,.http_minor= 1
1398   ,.status_code= 200
1399   ,.num_headers= 7
1400   ,.headers=
1401     { { "Server",  "Microsoft-IIS/6.0" }
1402     , { "X-Powered-By", "ASP.NET" }
1403     , { "en-US Content-Type", "text/xml" }
1404     , { "Content-Type", "text/xml" }
1405     , { "Content-Length", "16" }
1406     , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1407     , { "Connection", "keep-alive" }
1408     }
1409   ,.body= "<xml>hello</xml>"
1410   }
1411 #endif /* !HTTP_PARSER_STRICT */
1412 
1413 , {.name= NULL } /* sentinel */
1414 };
1415 
1416 /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1417  * define it ourselves.
1418  */
1419 size_t
strnlen(const char * s,size_t maxlen)1420 strnlen(const char *s, size_t maxlen)
1421 {
1422   const char *p;
1423 
1424   p = memchr(s, '\0', maxlen);
1425   if (p == NULL)
1426     return maxlen;
1427 
1428   return p - s;
1429 }
1430 
1431 size_t
strlncat(char * dst,size_t len,const char * src,size_t n)1432 strlncat(char *dst, size_t len, const char *src, size_t n)
1433 {
1434   size_t slen;
1435   size_t dlen;
1436   size_t rlen;
1437   size_t ncpy;
1438 
1439   slen = strnlen(src, n);
1440   dlen = strnlen(dst, len);
1441 
1442   if (dlen < len) {
1443     rlen = len - dlen;
1444     ncpy = slen < rlen ? slen : (rlen - 1);
1445     memcpy(dst + dlen, src, ncpy);
1446     dst[dlen + ncpy] = '\0';
1447   }
1448 
1449   assert(len > slen + dlen);
1450   return slen + dlen;
1451 }
1452 
1453 size_t
strlcat(char * dst,const char * src,size_t len)1454 strlcat(char *dst, const char *src, size_t len)
1455 {
1456   return strlncat(dst, len, src, (size_t) -1);
1457 }
1458 
1459 size_t
strlncpy(char * dst,size_t len,const char * src,size_t n)1460 strlncpy(char *dst, size_t len, const char *src, size_t n)
1461 {
1462   size_t slen;
1463   size_t ncpy;
1464 
1465   slen = strnlen(src, n);
1466 
1467   if (len > 0) {
1468     ncpy = slen < len ? slen : (len - 1);
1469     memcpy(dst, src, ncpy);
1470     dst[ncpy] = '\0';
1471   }
1472 
1473   assert(len > slen);
1474   return slen;
1475 }
1476 
1477 size_t
strlcpy(char * dst,const char * src,size_t len)1478 strlcpy(char *dst, const char *src, size_t len)
1479 {
1480   return strlncpy(dst, len, src, (size_t) -1);
1481 }
1482 
1483 int
request_url_cb(http_parser * p,const char * buf,size_t len)1484 request_url_cb (http_parser *p, const char *buf, size_t len)
1485 {
1486   assert(p == parser);
1487   strlncat(messages[num_messages].request_url,
1488            sizeof(messages[num_messages].request_url),
1489            buf,
1490            len);
1491   return 0;
1492 }
1493 
1494 int
status_complete_cb(http_parser * p)1495 status_complete_cb (http_parser *p) {
1496   assert(p == parser);
1497   p->data++;
1498   return 0;
1499 }
1500 
1501 int
header_field_cb(http_parser * p,const char * buf,size_t len)1502 header_field_cb (http_parser *p, const char *buf, size_t len)
1503 {
1504   assert(p == parser);
1505   struct message *m = &messages[num_messages];
1506 
1507   if (m->last_header_element != FIELD)
1508     m->num_headers++;
1509 
1510   strlncat(m->headers[m->num_headers-1][0],
1511            sizeof(m->headers[m->num_headers-1][0]),
1512            buf,
1513            len);
1514 
1515   m->last_header_element = FIELD;
1516 
1517   return 0;
1518 }
1519 
1520 int
header_value_cb(http_parser * p,const char * buf,size_t len)1521 header_value_cb (http_parser *p, const char *buf, size_t len)
1522 {
1523   assert(p == parser);
1524   struct message *m = &messages[num_messages];
1525 
1526   strlncat(m->headers[m->num_headers-1][1],
1527            sizeof(m->headers[m->num_headers-1][1]),
1528            buf,
1529            len);
1530 
1531   m->last_header_element = VALUE;
1532 
1533   return 0;
1534 }
1535 
1536 void
check_body_is_final(const http_parser * p)1537 check_body_is_final (const http_parser *p)
1538 {
1539   if (messages[num_messages].body_is_final) {
1540     fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1541                     "on last on_body callback call "
1542                     "but it doesn't! ***\n\n");
1543     assert(0);
1544     abort();
1545   }
1546   messages[num_messages].body_is_final = http_body_is_final(p);
1547 }
1548 
1549 int
body_cb(http_parser * p,const char * buf,size_t len)1550 body_cb (http_parser *p, const char *buf, size_t len)
1551 {
1552   assert(p == parser);
1553   strlncat(messages[num_messages].body,
1554            sizeof(messages[num_messages].body),
1555            buf,
1556            len);
1557   messages[num_messages].body_size += len;
1558   check_body_is_final(p);
1559  // printf("body_cb: '%s'\n", requests[num_messages].body);
1560   return 0;
1561 }
1562 
1563 int
count_body_cb(http_parser * p,const char * buf,size_t len)1564 count_body_cb (http_parser *p, const char *buf, size_t len)
1565 {
1566   assert(p == parser);
1567   assert(buf);
1568   messages[num_messages].body_size += len;
1569   check_body_is_final(p);
1570   return 0;
1571 }
1572 
1573 int
message_begin_cb(http_parser * p)1574 message_begin_cb (http_parser *p)
1575 {
1576   assert(p == parser);
1577   messages[num_messages].message_begin_cb_called = TRUE;
1578   return 0;
1579 }
1580 
1581 int
headers_complete_cb(http_parser * p)1582 headers_complete_cb (http_parser *p)
1583 {
1584   assert(p == parser);
1585   messages[num_messages].method = parser->method;
1586   messages[num_messages].status_code = parser->status_code;
1587   messages[num_messages].http_major = parser->http_major;
1588   messages[num_messages].http_minor = parser->http_minor;
1589   messages[num_messages].headers_complete_cb_called = TRUE;
1590   messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
1591   return 0;
1592 }
1593 
1594 int
message_complete_cb(http_parser * p)1595 message_complete_cb (http_parser *p)
1596 {
1597   assert(p == parser);
1598   if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
1599   {
1600     fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
1601                     "value in both on_message_complete and on_headers_complete "
1602                     "but it doesn't! ***\n\n");
1603     assert(0);
1604     abort();
1605   }
1606 
1607   if (messages[num_messages].body_size &&
1608       http_body_is_final(p) &&
1609       !messages[num_messages].body_is_final)
1610   {
1611     fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1612                     "on last on_body callback call "
1613                     "but it doesn't! ***\n\n");
1614     assert(0);
1615     abort();
1616   }
1617 
1618   messages[num_messages].message_complete_cb_called = TRUE;
1619 
1620   messages[num_messages].message_complete_on_eof = currently_parsing_eof;
1621 
1622   num_messages++;
1623   return 0;
1624 }
1625 
1626 /* These dontcall_* callbacks exist so that we can verify that when we're
1627  * paused, no additional callbacks are invoked */
1628 int
dontcall_message_begin_cb(http_parser * p)1629 dontcall_message_begin_cb (http_parser *p)
1630 {
1631   if (p) { } // gcc
1632   fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1633   abort();
1634 }
1635 
1636 int
dontcall_header_field_cb(http_parser * p,const char * buf,size_t len)1637 dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1638 {
1639   if (p || buf || len) { } // gcc
1640   fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1641   abort();
1642 }
1643 
1644 int
dontcall_header_value_cb(http_parser * p,const char * buf,size_t len)1645 dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1646 {
1647   if (p || buf || len) { } // gcc
1648   fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1649   abort();
1650 }
1651 
1652 int
dontcall_request_url_cb(http_parser * p,const char * buf,size_t len)1653 dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1654 {
1655   if (p || buf || len) { } // gcc
1656   fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1657   abort();
1658 }
1659 
1660 int
dontcall_body_cb(http_parser * p,const char * buf,size_t len)1661 dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1662 {
1663   if (p || buf || len) { } // gcc
1664   fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1665   abort();
1666 }
1667 
1668 int
dontcall_headers_complete_cb(http_parser * p)1669 dontcall_headers_complete_cb (http_parser *p)
1670 {
1671   if (p) { } // gcc
1672   fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1673                   "parser ***\n\n");
1674   abort();
1675 }
1676 
1677 int
dontcall_message_complete_cb(http_parser * p)1678 dontcall_message_complete_cb (http_parser *p)
1679 {
1680   if (p) { } // gcc
1681   fprintf(stderr, "\n\n*** on_message_complete() called on paused "
1682                   "parser ***\n\n");
1683   abort();
1684 }
1685 
1686 static http_parser_settings settings_dontcall =
1687   {.on_message_begin = dontcall_message_begin_cb
1688   ,.on_header_field = dontcall_header_field_cb
1689   ,.on_header_value = dontcall_header_value_cb
1690   ,.on_url = dontcall_request_url_cb
1691   ,.on_body = dontcall_body_cb
1692   ,.on_headers_complete = dontcall_headers_complete_cb
1693   ,.on_message_complete = dontcall_message_complete_cb
1694   };
1695 
1696 /* These pause_* callbacks always pause the parser and just invoke the regular
1697  * callback that tracks content. Before returning, we overwrite the parser
1698  * settings to point to the _dontcall variety so that we can verify that
1699  * the pause actually did, you know, pause. */
1700 int
pause_message_begin_cb(http_parser * p)1701 pause_message_begin_cb (http_parser *p)
1702 {
1703   http_parser_pause(p, 1);
1704   *current_pause_parser = settings_dontcall;
1705   return message_begin_cb(p);
1706 }
1707 
1708 int
pause_header_field_cb(http_parser * p,const char * buf,size_t len)1709 pause_header_field_cb (http_parser *p, const char *buf, size_t len)
1710 {
1711   http_parser_pause(p, 1);
1712   *current_pause_parser = settings_dontcall;
1713   return header_field_cb(p, buf, len);
1714 }
1715 
1716 int
pause_header_value_cb(http_parser * p,const char * buf,size_t len)1717 pause_header_value_cb (http_parser *p, const char *buf, size_t len)
1718 {
1719   http_parser_pause(p, 1);
1720   *current_pause_parser = settings_dontcall;
1721   return header_value_cb(p, buf, len);
1722 }
1723 
1724 int
pause_request_url_cb(http_parser * p,const char * buf,size_t len)1725 pause_request_url_cb (http_parser *p, const char *buf, size_t len)
1726 {
1727   http_parser_pause(p, 1);
1728   *current_pause_parser = settings_dontcall;
1729   return request_url_cb(p, buf, len);
1730 }
1731 
1732 int
pause_body_cb(http_parser * p,const char * buf,size_t len)1733 pause_body_cb (http_parser *p, const char *buf, size_t len)
1734 {
1735   http_parser_pause(p, 1);
1736   *current_pause_parser = settings_dontcall;
1737   return body_cb(p, buf, len);
1738 }
1739 
1740 int
pause_headers_complete_cb(http_parser * p)1741 pause_headers_complete_cb (http_parser *p)
1742 {
1743   http_parser_pause(p, 1);
1744   *current_pause_parser = settings_dontcall;
1745   return headers_complete_cb(p);
1746 }
1747 
1748 int
pause_message_complete_cb(http_parser * p)1749 pause_message_complete_cb (http_parser *p)
1750 {
1751   http_parser_pause(p, 1);
1752   *current_pause_parser = settings_dontcall;
1753   return message_complete_cb(p);
1754 }
1755 
1756 static http_parser_settings settings_pause =
1757   {.on_message_begin = pause_message_begin_cb
1758   ,.on_header_field = pause_header_field_cb
1759   ,.on_header_value = pause_header_value_cb
1760   ,.on_url = pause_request_url_cb
1761   ,.on_body = pause_body_cb
1762   ,.on_headers_complete = pause_headers_complete_cb
1763   ,.on_message_complete = pause_message_complete_cb
1764   };
1765 
1766 static http_parser_settings settings =
1767   {.on_message_begin = message_begin_cb
1768   ,.on_header_field = header_field_cb
1769   ,.on_header_value = header_value_cb
1770   ,.on_url = request_url_cb
1771   ,.on_body = body_cb
1772   ,.on_headers_complete = headers_complete_cb
1773   ,.on_message_complete = message_complete_cb
1774   };
1775 
1776 static http_parser_settings settings_count_body =
1777   {.on_message_begin = message_begin_cb
1778   ,.on_header_field = header_field_cb
1779   ,.on_header_value = header_value_cb
1780   ,.on_url = request_url_cb
1781   ,.on_body = count_body_cb
1782   ,.on_headers_complete = headers_complete_cb
1783   ,.on_message_complete = message_complete_cb
1784   };
1785 
1786 static http_parser_settings settings_null =
1787   {.on_message_begin = 0
1788   ,.on_header_field = 0
1789   ,.on_header_value = 0
1790   ,.on_url = 0
1791   ,.on_body = 0
1792   ,.on_headers_complete = 0
1793   ,.on_message_complete = 0
1794   };
1795 
1796 void
parser_init(enum http_parser_type type)1797 parser_init (enum http_parser_type type)
1798 {
1799   num_messages = 0;
1800 
1801   assert(parser == NULL);
1802 
1803   parser = malloc(sizeof(http_parser));
1804 
1805   http_parser_init(parser, type);
1806 
1807   memset(&messages, 0, sizeof messages);
1808 
1809 }
1810 
1811 void
parser_free()1812 parser_free ()
1813 {
1814   assert(parser);
1815   free(parser);
1816   parser = NULL;
1817 }
1818 
parse(const char * buf,size_t len)1819 size_t parse (const char *buf, size_t len)
1820 {
1821   size_t nparsed;
1822   currently_parsing_eof = (len == 0);
1823   nparsed = http_parser_execute(parser, &settings, buf, len);
1824   return nparsed;
1825 }
1826 
parse_count_body(const char * buf,size_t len)1827 size_t parse_count_body (const char *buf, size_t len)
1828 {
1829   size_t nparsed;
1830   currently_parsing_eof = (len == 0);
1831   nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
1832   return nparsed;
1833 }
1834 
parse_pause(const char * buf,size_t len)1835 size_t parse_pause (const char *buf, size_t len)
1836 {
1837   size_t nparsed;
1838   http_parser_settings s = settings_pause;
1839 
1840   currently_parsing_eof = (len == 0);
1841   current_pause_parser = &s;
1842   nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
1843   return nparsed;
1844 }
1845 
1846 static inline int
check_str_eq(const struct message * m,const char * prop,const char * expected,const char * found)1847 check_str_eq (const struct message *m,
1848               const char *prop,
1849               const char *expected,
1850               const char *found) {
1851   if ((expected == NULL) != (found == NULL)) {
1852     printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1853     printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
1854     printf("   found %s\n", (found == NULL) ? "NULL" : found);
1855     return 0;
1856   }
1857   if (expected != NULL && 0 != strcmp(expected, found)) {
1858     printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1859     printf("expected '%s'\n", expected);
1860     printf("   found '%s'\n", found);
1861     return 0;
1862   }
1863   return 1;
1864 }
1865 
1866 static inline int
check_num_eq(const struct message * m,const char * prop,int expected,int found)1867 check_num_eq (const struct message *m,
1868               const char *prop,
1869               int expected,
1870               int found) {
1871   if (expected != found) {
1872     printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1873     printf("expected %d\n", expected);
1874     printf("   found %d\n", found);
1875     return 0;
1876   }
1877   return 1;
1878 }
1879 
1880 #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
1881   if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
1882 
1883 #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1884   if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1885 
1886 #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn)           \
1887 do {                                                                 \
1888   char ubuf[256];                                                    \
1889                                                                      \
1890   if ((u)->field_set & (1 << (fn))) {                                \
1891     memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off,   \
1892       (u)->field_data[(fn)].len);                                    \
1893     ubuf[(u)->field_data[(fn)].len] = '\0';                          \
1894   } else {                                                           \
1895     ubuf[0] = '\0';                                                  \
1896   }                                                                  \
1897                                                                      \
1898   check_str_eq(expected, #prop, expected->prop, ubuf);               \
1899 } while(0)
1900 
1901 int
message_eq(int index,const struct message * expected)1902 message_eq (int index, const struct message *expected)
1903 {
1904   int i;
1905   struct message *m = &messages[index];
1906 
1907   MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
1908   MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
1909 
1910   if (expected->type == HTTP_REQUEST) {
1911     MESSAGE_CHECK_NUM_EQ(expected, m, method);
1912   } else {
1913     MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
1914   }
1915 
1916   MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
1917   MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
1918 
1919   assert(m->message_begin_cb_called);
1920   assert(m->headers_complete_cb_called);
1921   assert(m->message_complete_cb_called);
1922 
1923 
1924   MESSAGE_CHECK_STR_EQ(expected, m, request_url);
1925 
1926   /* Check URL components; we can't do this w/ CONNECT since it doesn't
1927    * send us a well-formed URL.
1928    */
1929   if (*m->request_url && m->method != HTTP_CONNECT) {
1930     struct http_parser_url u;
1931 
1932     if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
1933       fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
1934         m->request_url);
1935       abort();
1936     }
1937 
1938     if (expected->host) {
1939       MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
1940     }
1941 
1942     if (expected->userinfo) {
1943       MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
1944     }
1945 
1946     m->port = (u.field_set & (1 << UF_PORT)) ?
1947       u.port : 0;
1948 
1949     MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
1950     MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
1951     MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
1952     MESSAGE_CHECK_NUM_EQ(expected, m, port);
1953   }
1954 
1955   if (expected->body_size) {
1956     MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1957   } else {
1958     MESSAGE_CHECK_STR_EQ(expected, m, body);
1959   }
1960 
1961   MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
1962 
1963   int r;
1964   for (i = 0; i < m->num_headers; i++) {
1965     r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
1966     if (!r) return 0;
1967     r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
1968     if (!r) return 0;
1969   }
1970 
1971   MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
1972 
1973   return 1;
1974 }
1975 
1976 /* Given a sequence of varargs messages, return the number of them that the
1977  * parser should successfully parse, taking into account that upgraded
1978  * messages prevent all subsequent messages from being parsed.
1979  */
1980 size_t
count_parsed_messages(const size_t nmsgs,...)1981 count_parsed_messages(const size_t nmsgs, ...) {
1982   size_t i;
1983   va_list ap;
1984 
1985   va_start(ap, nmsgs);
1986 
1987   for (i = 0; i < nmsgs; i++) {
1988     struct message *m = va_arg(ap, struct message *);
1989 
1990     if (m->upgrade) {
1991       va_end(ap);
1992       return i + 1;
1993     }
1994   }
1995 
1996   va_end(ap);
1997   return nmsgs;
1998 }
1999 
2000 /* Given a sequence of bytes and the number of these that we were able to
2001  * parse, verify that upgrade bodies are correct.
2002  */
2003 void
upgrade_message_fix(char * body,const size_t nread,const size_t nmsgs,...)2004 upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2005   va_list ap;
2006   size_t i;
2007   size_t off = 0;
2008 
2009   va_start(ap, nmsgs);
2010 
2011   for (i = 0; i < nmsgs; i++) {
2012     struct message *m = va_arg(ap, struct message *);
2013 
2014     off += strlen(m->raw);
2015 
2016     if (m->upgrade) {
2017       off -= strlen(m->upgrade);
2018 
2019       /* Check the portion of the response after its specified upgrade */
2020       if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2021         abort();
2022       }
2023 
2024       /* Fix up the response so that message_eq() will verify the beginning
2025        * of the upgrade */
2026       *(body + nread + strlen(m->upgrade)) = '\0';
2027       messages[num_messages -1 ].upgrade = body + nread;
2028 
2029       va_end(ap);
2030       return;
2031     }
2032   }
2033 
2034   va_end(ap);
2035   printf("\n\n*** Error: expected a message with upgrade ***\n");
2036 
2037   abort();
2038 }
2039 
2040 static void
print_error(const char * raw,size_t error_location)2041 print_error (const char *raw, size_t error_location)
2042 {
2043   fprintf(stderr, "\n*** %s ***\n\n",
2044           http_errno_description(HTTP_PARSER_ERRNO(parser)));
2045 
2046   int this_line = 0, char_len = 0;
2047   size_t i, j, len = strlen(raw), error_location_line = 0;
2048   for (i = 0; i < len; i++) {
2049     if (i == error_location) this_line = 1;
2050     switch (raw[i]) {
2051       case '\r':
2052         char_len = 2;
2053         fprintf(stderr, "\\r");
2054         break;
2055 
2056       case '\n':
2057         char_len = 2;
2058         fprintf(stderr, "\\n\n");
2059 
2060         if (this_line) goto print;
2061 
2062         error_location_line = 0;
2063         continue;
2064 
2065       default:
2066         char_len = 1;
2067         fputc(raw[i], stderr);
2068         break;
2069     }
2070     if (!this_line) error_location_line += char_len;
2071   }
2072 
2073   fprintf(stderr, "[eof]\n");
2074 
2075  print:
2076   for (j = 0; j < error_location_line; j++) {
2077     fputc(' ', stderr);
2078   }
2079   fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2080 }
2081 
2082 void
test_preserve_data(void)2083 test_preserve_data (void)
2084 {
2085   char my_data[] = "application-specific data";
2086   http_parser parser;
2087   parser.data = my_data;
2088   http_parser_init(&parser, HTTP_REQUEST);
2089   if (parser.data != my_data) {
2090     printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2091     abort();
2092   }
2093 }
2094 
2095 struct url_test {
2096   const char *name;
2097   const char *url;
2098   int is_connect;
2099   struct http_parser_url u;
2100   int rv;
2101 };
2102 
2103 const struct url_test url_tests[] =
2104 { {.name="proxy request"
2105   ,.url="http://hostname/"
2106   ,.is_connect=0
2107   ,.u=
2108     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2109     ,.port=0
2110     ,.field_data=
2111       {{  0,  4 } /* UF_SCHEMA */
2112       ,{  7,  8 } /* UF_HOST */
2113       ,{  0,  0 } /* UF_PORT */
2114       ,{ 15,  1 } /* UF_PATH */
2115       ,{  0,  0 } /* UF_QUERY */
2116       ,{  0,  0 } /* UF_FRAGMENT */
2117       ,{  0,  0 } /* UF_USERINFO */
2118       }
2119     }
2120   ,.rv=0
2121   }
2122 
2123 , {.name="proxy request with port"
2124   ,.url="http://hostname:444/"
2125   ,.is_connect=0
2126   ,.u=
2127     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2128     ,.port=444
2129     ,.field_data=
2130       {{  0,  4 } /* UF_SCHEMA */
2131       ,{  7,  8 } /* UF_HOST */
2132       ,{ 16,  3 } /* UF_PORT */
2133       ,{ 19,  1 } /* UF_PATH */
2134       ,{  0,  0 } /* UF_QUERY */
2135       ,{  0,  0 } /* UF_FRAGMENT */
2136       ,{  0,  0 } /* UF_USERINFO */
2137       }
2138     }
2139   ,.rv=0
2140   }
2141 
2142 , {.name="CONNECT request"
2143   ,.url="hostname:443"
2144   ,.is_connect=1
2145   ,.u=
2146     {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2147     ,.port=443
2148     ,.field_data=
2149       {{  0,  0 } /* UF_SCHEMA */
2150       ,{  0,  8 } /* UF_HOST */
2151       ,{  9,  3 } /* UF_PORT */
2152       ,{  0,  0 } /* UF_PATH */
2153       ,{  0,  0 } /* UF_QUERY */
2154       ,{  0,  0 } /* UF_FRAGMENT */
2155       ,{  0,  0 } /* UF_USERINFO */
2156       }
2157     }
2158   ,.rv=0
2159   }
2160 
2161 , {.name="CONNECT request but not connect"
2162   ,.url="hostname:443"
2163   ,.is_connect=0
2164   ,.rv=1
2165   }
2166 
2167 , {.name="proxy ipv6 request"
2168   ,.url="http://[1:2::3:4]/"
2169   ,.is_connect=0
2170   ,.u=
2171     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2172     ,.port=0
2173     ,.field_data=
2174       {{  0,  4 } /* UF_SCHEMA */
2175       ,{  8,  8 } /* UF_HOST */
2176       ,{  0,  0 } /* UF_PORT */
2177       ,{ 17,  1 } /* UF_PATH */
2178       ,{  0,  0 } /* UF_QUERY */
2179       ,{  0,  0 } /* UF_FRAGMENT */
2180       ,{  0,  0 } /* UF_USERINFO */
2181       }
2182     }
2183   ,.rv=0
2184   }
2185 
2186 , {.name="proxy ipv6 request with port"
2187   ,.url="http://[1:2::3:4]:67/"
2188   ,.is_connect=0
2189   ,.u=
2190     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2191     ,.port=67
2192     ,.field_data=
2193       {{  0,  4 } /* UF_SCHEMA */
2194       ,{  8,  8 } /* UF_HOST */
2195       ,{ 18,  2 } /* UF_PORT */
2196       ,{ 20,  1 } /* UF_PATH */
2197       ,{  0,  0 } /* UF_QUERY */
2198       ,{  0,  0 } /* UF_FRAGMENT */
2199       ,{  0,  0 } /* UF_USERINFO */
2200       }
2201     }
2202   ,.rv=0
2203   }
2204 
2205 , {.name="CONNECT ipv6 address"
2206   ,.url="[1:2::3:4]:443"
2207   ,.is_connect=1
2208   ,.u=
2209     {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2210     ,.port=443
2211     ,.field_data=
2212       {{  0,  0 } /* UF_SCHEMA */
2213       ,{  1,  8 } /* UF_HOST */
2214       ,{ 11,  3 } /* UF_PORT */
2215       ,{  0,  0 } /* UF_PATH */
2216       ,{  0,  0 } /* UF_QUERY */
2217       ,{  0,  0 } /* UF_FRAGMENT */
2218       ,{  0,  0 } /* UF_USERINFO */
2219       }
2220     }
2221   ,.rv=0
2222   }
2223 
2224 , {.name="ipv4 in ipv6 address"
2225   ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2226   ,.is_connect=0
2227   ,.u=
2228     {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2229     ,.port=0
2230     ,.field_data=
2231       {{  0,  4 } /* UF_SCHEMA */
2232       ,{  8, 37 } /* UF_HOST */
2233       ,{  0,  0 } /* UF_PORT */
2234       ,{ 46,  1 } /* UF_PATH */
2235       ,{  0,  0 } /* UF_QUERY */
2236       ,{  0,  0 } /* UF_FRAGMENT */
2237       ,{  0,  0 } /* UF_USERINFO */
2238       }
2239     }
2240   ,.rv=0
2241   }
2242 
2243 , {.name="extra ? in query string"
2244   ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
2245   "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
2246   "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2247   ,.is_connect=0
2248   ,.u=
2249     {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2250     ,.port=0
2251     ,.field_data=
2252       {{  0,  4 } /* UF_SCHEMA */
2253       ,{  7, 10 } /* UF_HOST */
2254       ,{  0,  0 } /* UF_PORT */
2255       ,{ 17, 12 } /* UF_PATH */
2256       ,{ 30,187 } /* UF_QUERY */
2257       ,{  0,  0 } /* UF_FRAGMENT */
2258       ,{  0,  0 } /* UF_USERINFO */
2259       }
2260     }
2261   ,.rv=0
2262   }
2263 
2264 , {.name="space URL encoded"
2265   ,.url="/toto.html?toto=a%20b"
2266   ,.is_connect=0
2267   ,.u=
2268     {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2269     ,.port=0
2270     ,.field_data=
2271       {{  0,  0 } /* UF_SCHEMA */
2272       ,{  0,  0 } /* UF_HOST */
2273       ,{  0,  0 } /* UF_PORT */
2274       ,{  0, 10 } /* UF_PATH */
2275       ,{ 11, 10 } /* UF_QUERY */
2276       ,{  0,  0 } /* UF_FRAGMENT */
2277       ,{  0,  0 } /* UF_USERINFO */
2278       }
2279     }
2280   ,.rv=0
2281   }
2282 
2283 
2284 , {.name="URL fragment"
2285   ,.url="/toto.html#titi"
2286   ,.is_connect=0
2287   ,.u=
2288     {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2289     ,.port=0
2290     ,.field_data=
2291       {{  0,  0 } /* UF_SCHEMA */
2292       ,{  0,  0 } /* UF_HOST */
2293       ,{  0,  0 } /* UF_PORT */
2294       ,{  0, 10 } /* UF_PATH */
2295       ,{  0,  0 } /* UF_QUERY */
2296       ,{ 11,  4 } /* UF_FRAGMENT */
2297       ,{  0,  0 } /* UF_USERINFO */
2298       }
2299     }
2300   ,.rv=0
2301   }
2302 
2303 , {.name="complex URL fragment"
2304   ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
2305     "http://www.example.com/index.html?foo=bar&hello=world#midpage"
2306   ,.is_connect=0
2307   ,.u=
2308     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2309       (1<<UF_FRAGMENT)
2310     ,.port=0
2311     ,.field_data=
2312       {{  0,  4 } /* UF_SCHEMA */
2313       ,{  7, 22 } /* UF_HOST */
2314       ,{  0,  0 } /* UF_PORT */
2315       ,{ 29,  6 } /* UF_PATH */
2316       ,{ 36, 69 } /* UF_QUERY */
2317       ,{106,  7 } /* UF_FRAGMENT */
2318       ,{  0,  0 } /* UF_USERINFO */
2319       }
2320     }
2321   ,.rv=0
2322   }
2323 
2324 , {.name="complex URL from node js url parser doc"
2325   ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
2326   ,.is_connect=0
2327   ,.u=
2328     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2329       (1<<UF_QUERY) | (1<<UF_FRAGMENT)
2330     ,.port=8080
2331     ,.field_data=
2332       {{  0,  4 } /* UF_SCHEMA */
2333       ,{  7,  8 } /* UF_HOST */
2334       ,{ 16,  4 } /* UF_PORT */
2335       ,{ 20,  8 } /* UF_PATH */
2336       ,{ 29, 12 } /* UF_QUERY */
2337       ,{ 42,  4 } /* UF_FRAGMENT */
2338       ,{  0,  0 } /* UF_USERINFO */
2339       }
2340     }
2341   ,.rv=0
2342   }
2343 
2344 , {.name="complex URL with basic auth from node js url parser doc"
2345   ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
2346   ,.is_connect=0
2347   ,.u=
2348     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2349       (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
2350     ,.port=8080
2351     ,.field_data=
2352       {{  0,  4 } /* UF_SCHEMA */
2353       ,{ 11,  8 } /* UF_HOST */
2354       ,{ 20,  4 } /* UF_PORT */
2355       ,{ 24,  8 } /* UF_PATH */
2356       ,{ 33, 12 } /* UF_QUERY */
2357       ,{ 46,  4 } /* UF_FRAGMENT */
2358       ,{  7,  3 } /* UF_USERINFO */
2359       }
2360     }
2361   ,.rv=0
2362   }
2363 
2364 , {.name="double @"
2365   ,.url="http://a:b@@hostname:443/"
2366   ,.is_connect=0
2367   ,.rv=1
2368   }
2369 
2370 , {.name="proxy empty host"
2371   ,.url="http://:443/"
2372   ,.is_connect=0
2373   ,.rv=1
2374   }
2375 
2376 , {.name="proxy empty port"
2377   ,.url="http://hostname:/"
2378   ,.is_connect=0
2379   ,.rv=1
2380   }
2381 
2382 , {.name="CONNECT with basic auth"
2383   ,.url="a:b@hostname:443"
2384   ,.is_connect=1
2385   ,.rv=1
2386   }
2387 
2388 , {.name="CONNECT empty host"
2389   ,.url=":443"
2390   ,.is_connect=1
2391   ,.rv=1
2392   }
2393 
2394 , {.name="CONNECT empty port"
2395   ,.url="hostname:"
2396   ,.is_connect=1
2397   ,.rv=1
2398   }
2399 
2400 , {.name="CONNECT with extra bits"
2401   ,.url="hostname:443/"
2402   ,.is_connect=1
2403   ,.rv=1
2404   }
2405 
2406 , {.name="space in URL"
2407   ,.url="/foo bar/"
2408   ,.rv=1 /* s_dead */
2409   }
2410 
2411 , {.name="proxy basic auth with space url encoded"
2412   ,.url="http://a%20:b@host.com/"
2413   ,.is_connect=0
2414   ,.u=
2415     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2416     ,.port=0
2417     ,.field_data=
2418       {{  0,  4 } /* UF_SCHEMA */
2419       ,{ 14,  8 } /* UF_HOST */
2420       ,{  0,  0 } /* UF_PORT */
2421       ,{ 22,  1 } /* UF_PATH */
2422       ,{  0,  0 } /* UF_QUERY */
2423       ,{  0,  0 } /* UF_FRAGMENT */
2424       ,{  7,  6 } /* UF_USERINFO */
2425       }
2426     }
2427   ,.rv=0
2428   }
2429 
2430 , {.name="carriage return in URL"
2431   ,.url="/foo\rbar/"
2432   ,.rv=1 /* s_dead */
2433   }
2434 
2435 , {.name="proxy double : in URL"
2436   ,.url="http://hostname::443/"
2437   ,.rv=1 /* s_dead */
2438   }
2439 
2440 , {.name="proxy basic auth with double :"
2441   ,.url="http://a::b@host.com/"
2442   ,.is_connect=0
2443   ,.u=
2444     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2445     ,.port=0
2446     ,.field_data=
2447       {{  0,  4 } /* UF_SCHEMA */
2448       ,{ 12,  8 } /* UF_HOST */
2449       ,{  0,  0 } /* UF_PORT */
2450       ,{ 20,  1 } /* UF_PATH */
2451       ,{  0,  0 } /* UF_QUERY */
2452       ,{  0,  0 } /* UF_FRAGMENT */
2453       ,{  7,  4 } /* UF_USERINFO */
2454       }
2455     }
2456   ,.rv=0
2457   }
2458 
2459 , {.name="line feed in URL"
2460   ,.url="/foo\nbar/"
2461   ,.rv=1 /* s_dead */
2462   }
2463 
2464 , {.name="proxy empty basic auth"
2465   ,.url="http://@hostname/fo"
2466   ,.u=
2467     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
2468     ,.port=0
2469     ,.field_data=
2470       {{  0,  4 } /* UF_SCHEMA */
2471       ,{  8,  8 } /* UF_HOST */
2472       ,{  0,  0 } /* UF_PORT */
2473       ,{ 16,  3 } /* UF_PATH */
2474       ,{  0,  0 } /* UF_QUERY */
2475       ,{  0,  0 } /* UF_FRAGMENT */
2476       ,{  0,  0 } /* UF_USERINFO */
2477       }
2478     }
2479   ,.rv=0
2480   }
2481 , {.name="proxy line feed in hostname"
2482   ,.url="http://host\name/fo"
2483   ,.rv=1 /* s_dead */
2484   }
2485 
2486 , {.name="proxy % in hostname"
2487   ,.url="http://host%name/fo"
2488   ,.rv=1 /* s_dead */
2489   }
2490 
2491 , {.name="proxy ; in hostname"
2492   ,.url="http://host;ame/fo"
2493   ,.rv=1 /* s_dead */
2494   }
2495 
2496 , {.name="proxy basic auth with unreservedchars"
2497   ,.url="http://a!;-_!=+$@host.com/"
2498   ,.is_connect=0
2499   ,.u=
2500     {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2501     ,.port=0
2502     ,.field_data=
2503       {{  0,  4 } /* UF_SCHEMA */
2504       ,{ 17,  8 } /* UF_HOST */
2505       ,{  0,  0 } /* UF_PORT */
2506       ,{ 25,  1 } /* UF_PATH */
2507       ,{  0,  0 } /* UF_QUERY */
2508       ,{  0,  0 } /* UF_FRAGMENT */
2509       ,{  7,  9 } /* UF_USERINFO */
2510       }
2511     }
2512   ,.rv=0
2513   }
2514 
2515 , {.name="proxy only empty basic auth"
2516   ,.url="http://@/fo"
2517   ,.rv=1 /* s_dead */
2518   }
2519 
2520 , {.name="proxy only basic auth"
2521   ,.url="http://toto@/fo"
2522   ,.rv=1 /* s_dead */
2523   }
2524 
2525 , {.name="proxy emtpy hostname"
2526   ,.url="http:///fo"
2527   ,.rv=1 /* s_dead */
2528   }
2529 
2530 , {.name="proxy = in URL"
2531   ,.url="http://host=ame/fo"
2532   ,.rv=1 /* s_dead */
2533   }
2534 
2535 #if HTTP_PARSER_STRICT
2536 
2537 , {.name="tab in URL"
2538   ,.url="/foo\tbar/"
2539   ,.rv=1 /* s_dead */
2540   }
2541 
2542 , {.name="form feed in URL"
2543   ,.url="/foo\fbar/"
2544   ,.rv=1 /* s_dead */
2545   }
2546 
2547 #else /* !HTTP_PARSER_STRICT */
2548 
2549 , {.name="tab in URL"
2550   ,.url="/foo\tbar/"
2551   ,.u=
2552     {.field_set=(1 << UF_PATH)
2553     ,.field_data=
2554       {{  0,  0 } /* UF_SCHEMA */
2555       ,{  0,  0 } /* UF_HOST */
2556       ,{  0,  0 } /* UF_PORT */
2557       ,{  0,  9 } /* UF_PATH */
2558       ,{  0,  0 } /* UF_QUERY */
2559       ,{  0,  0 } /* UF_FRAGMENT */
2560       ,{  0,  0 } /* UF_USERINFO */
2561       }
2562     }
2563   ,.rv=0
2564   }
2565 
2566 , {.name="form feed in URL"
2567   ,.url="/foo\fbar/"
2568   ,.u=
2569     {.field_set=(1 << UF_PATH)
2570     ,.field_data=
2571       {{  0,  0 } /* UF_SCHEMA */
2572       ,{  0,  0 } /* UF_HOST */
2573       ,{  0,  0 } /* UF_PORT */
2574       ,{  0,  9 } /* UF_PATH */
2575       ,{  0,  0 } /* UF_QUERY */
2576       ,{  0,  0 } /* UF_FRAGMENT */
2577       ,{  0,  0 } /* UF_USERINFO */
2578       }
2579     }
2580   ,.rv=0
2581   }
2582 #endif
2583 };
2584 
2585 void
dump_url(const char * url,const struct http_parser_url * u)2586 dump_url (const char *url, const struct http_parser_url *u)
2587 {
2588   unsigned int i;
2589 
2590   printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
2591   for (i = 0; i < UF_MAX; i++) {
2592     if ((u->field_set & (1 << i)) == 0) {
2593       printf("\tfield_data[%u]: unset\n", i);
2594       continue;
2595     }
2596 
2597     printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
2598            i,
2599            u->field_data[i].off,
2600            u->field_data[i].len,
2601            u->field_data[i].len,
2602            url + u->field_data[i].off);
2603   }
2604 }
2605 
2606 void
test_parse_url(void)2607 test_parse_url (void)
2608 {
2609   struct http_parser_url u;
2610   const struct url_test *test;
2611   unsigned int i;
2612   int rv;
2613 
2614   for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
2615     test = &url_tests[i];
2616     memset(&u, 0, sizeof(u));
2617 
2618     rv = http_parser_parse_url(test->url,
2619                                strlen(test->url),
2620                                test->is_connect,
2621                                &u);
2622 
2623     if (test->rv == 0) {
2624       if (rv != 0) {
2625         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2626                "unexpected rv %d ***\n\n", test->url, test->name, rv);
2627         abort();
2628       }
2629 
2630       if (memcmp(&u, &test->u, sizeof(u)) != 0) {
2631         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
2632                test->url, test->name);
2633 
2634         printf("target http_parser_url:\n");
2635         dump_url(test->url, &test->u);
2636         printf("result http_parser_url:\n");
2637         dump_url(test->url, &u);
2638 
2639         abort();
2640       }
2641     } else {
2642       /* test->rv != 0 */
2643       if (rv == 0) {
2644         printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2645                "unexpected rv %d ***\n\n", test->url, test->name, rv);
2646         abort();
2647       }
2648     }
2649   }
2650 }
2651 
2652 void
test_method_str(void)2653 test_method_str (void)
2654 {
2655   assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
2656   assert(0 == strcmp("<unknown>", http_method_str(1337)));
2657 }
2658 
2659 void
test_message(const struct message * message)2660 test_message (const struct message *message)
2661 {
2662   size_t raw_len = strlen(message->raw);
2663   size_t msg1len;
2664   for (msg1len = 0; msg1len < raw_len; msg1len++) {
2665     parser_init(message->type);
2666 
2667     size_t read;
2668     const char *msg1 = message->raw;
2669     const char *msg2 = msg1 + msg1len;
2670     size_t msg2len = raw_len - msg1len;
2671 
2672     if (msg1len) {
2673       read = parse(msg1, msg1len);
2674 
2675       if (message->upgrade && parser->upgrade) {
2676         messages[num_messages - 1].upgrade = msg1 + read;
2677         goto test;
2678       }
2679 
2680       if (read != msg1len) {
2681         print_error(msg1, read);
2682         abort();
2683       }
2684     }
2685 
2686 
2687     read = parse(msg2, msg2len);
2688 
2689     if (message->upgrade && parser->upgrade) {
2690       messages[num_messages - 1].upgrade = msg2 + read;
2691       goto test;
2692     }
2693 
2694     if (read != msg2len) {
2695       print_error(msg2, read);
2696       abort();
2697     }
2698 
2699     read = parse(NULL, 0);
2700 
2701     if (read != 0) {
2702       print_error(message->raw, read);
2703       abort();
2704     }
2705 
2706   test:
2707 
2708     if (num_messages != 1) {
2709       printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2710       abort();
2711     }
2712 
2713     if(!message_eq(0, message)) abort();
2714 
2715     parser_free();
2716   }
2717 }
2718 
2719 void
test_message_count_body(const struct message * message)2720 test_message_count_body (const struct message *message)
2721 {
2722   parser_init(message->type);
2723 
2724   size_t read;
2725   size_t l = strlen(message->raw);
2726   size_t i, toread;
2727   size_t chunk = 4024;
2728 
2729   for (i = 0; i < l; i+= chunk) {
2730     toread = MIN(l-i, chunk);
2731     read = parse_count_body(message->raw + i, toread);
2732     if (read != toread) {
2733       print_error(message->raw, read);
2734       abort();
2735     }
2736   }
2737 
2738 
2739   read = parse_count_body(NULL, 0);
2740   if (read != 0) {
2741     print_error(message->raw, read);
2742     abort();
2743   }
2744 
2745   if (num_messages != 1) {
2746     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2747     abort();
2748   }
2749 
2750   if(!message_eq(0, message)) abort();
2751 
2752   parser_free();
2753 }
2754 
2755 void
test_simple(const char * buf,enum http_errno err_expected)2756 test_simple (const char *buf, enum http_errno err_expected)
2757 {
2758   parser_init(HTTP_REQUEST);
2759 
2760   size_t parsed;
2761   int pass;
2762   enum http_errno err;
2763 
2764   parsed = parse(buf, strlen(buf));
2765   pass = (parsed == strlen(buf));
2766   err = HTTP_PARSER_ERRNO(parser);
2767   parsed = parse(NULL, 0);
2768   pass &= (parsed == 0);
2769 
2770   parser_free();
2771 
2772   /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
2773    * long as the caller isn't expecting success.
2774    */
2775 #if HTTP_PARSER_STRICT
2776   if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
2777 #else
2778   if (err_expected != err) {
2779 #endif
2780     fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
2781         http_errno_name(err_expected), http_errno_name(err), buf);
2782     abort();
2783   }
2784 }
2785 
2786 void
2787 test_header_overflow_error (int req)
2788 {
2789   http_parser parser;
2790   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2791   size_t parsed;
2792   const char *buf;
2793   buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
2794   parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
2795   assert(parsed == strlen(buf));
2796 
2797   buf = "header-key: header-value\r\n";
2798   size_t buflen = strlen(buf);
2799 
2800   int i;
2801   for (i = 0; i < 10000; i++) {
2802     parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
2803     if (parsed != buflen) {
2804       //fprintf(stderr, "error found on iter %d\n", i);
2805       assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
2806       return;
2807     }
2808   }
2809 
2810   fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
2811   abort();
2812 }
2813 
2814 static void
2815 test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
2816 {
2817   http_parser parser;
2818   http_parser_init(&parser, HTTP_RESPONSE);
2819   http_parser_execute(&parser, &settings_null, buf, buflen);
2820 
2821   if (expect_ok)
2822     assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
2823   else
2824     assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
2825 }
2826 
2827 void
2828 test_header_content_length_overflow_error (void)
2829 {
2830 #define X(size)                                                               \
2831   "HTTP/1.1 200 OK\r\n"                                                       \
2832   "Content-Length: " #size "\r\n"                                             \
2833   "\r\n"
2834   const char a[] = X(18446744073709551614); /* 2^64-2 */
2835   const char b[] = X(18446744073709551615); /* 2^64-1 */
2836   const char c[] = X(18446744073709551616); /* 2^64   */
2837 #undef X
2838   test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
2839   test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2840   test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2841 }
2842 
2843 void
2844 test_chunk_content_length_overflow_error (void)
2845 {
2846 #define X(size)                                                               \
2847     "HTTP/1.1 200 OK\r\n"                                                     \
2848     "Transfer-Encoding: chunked\r\n"                                          \
2849     "\r\n"                                                                    \
2850     #size "\r\n"                                                              \
2851     "..."
2852   const char a[] = X(FFFFFFFFFFFFFFFE);  /* 2^64-2 */
2853   const char b[] = X(FFFFFFFFFFFFFFFF);  /* 2^64-1 */
2854   const char c[] = X(10000000000000000); /* 2^64   */
2855 #undef X
2856   test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
2857   test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2858   test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2859 }
2860 
2861 void
2862 test_no_overflow_long_body (int req, size_t length)
2863 {
2864   http_parser parser;
2865   http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2866   size_t parsed;
2867   size_t i;
2868   char buf1[3000];
2869   size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
2870       req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
2871   parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
2872   if (parsed != buf1len)
2873     goto err;
2874 
2875   for (i = 0; i < length; i++) {
2876     char foo = 'a';
2877     parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
2878     if (parsed != 1)
2879       goto err;
2880   }
2881 
2882   parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
2883   if (parsed != buf1len) goto err;
2884   return;
2885 
2886  err:
2887   fprintf(stderr,
2888           "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
2889           req ? "REQUEST" : "RESPONSE",
2890           (unsigned long)length);
2891   abort();
2892 }
2893 
2894 void
2895 test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
2896 {
2897   int message_count = count_parsed_messages(3, r1, r2, r3);
2898 
2899   char total[ strlen(r1->raw)
2900             + strlen(r2->raw)
2901             + strlen(r3->raw)
2902             + 1
2903             ];
2904   total[0] = '\0';
2905 
2906   strcat(total, r1->raw);
2907   strcat(total, r2->raw);
2908   strcat(total, r3->raw);
2909 
2910   parser_init(r1->type);
2911 
2912   size_t read;
2913 
2914   read = parse(total, strlen(total));
2915 
2916   if (parser->upgrade) {
2917     upgrade_message_fix(total, read, 3, r1, r2, r3);
2918     goto test;
2919   }
2920 
2921   if (read != strlen(total)) {
2922     print_error(total, read);
2923     abort();
2924   }
2925 
2926   read = parse(NULL, 0);
2927 
2928   if (read != 0) {
2929     print_error(total, read);
2930     abort();
2931   }
2932 
2933 test:
2934 
2935   if (message_count != num_messages) {
2936     fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
2937     abort();
2938   }
2939 
2940   if (!message_eq(0, r1)) abort();
2941   if (message_count > 1 && !message_eq(1, r2)) abort();
2942   if (message_count > 2 && !message_eq(2, r3)) abort();
2943 
2944   parser_free();
2945 }
2946 
2947 /* SCAN through every possible breaking to make sure the
2948  * parser can handle getting the content in any chunks that
2949  * might come from the socket
2950  */
2951 void
2952 test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
2953 {
2954   char total[80*1024] = "\0";
2955   char buf1[80*1024] = "\0";
2956   char buf2[80*1024] = "\0";
2957   char buf3[80*1024] = "\0";
2958 
2959   strcat(total, r1->raw);
2960   strcat(total, r2->raw);
2961   strcat(total, r3->raw);
2962 
2963   size_t read;
2964 
2965   int total_len = strlen(total);
2966 
2967   int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
2968   int ops = 0 ;
2969 
2970   size_t buf1_len, buf2_len, buf3_len;
2971   int message_count = count_parsed_messages(3, r1, r2, r3);
2972 
2973   int i,j,type_both;
2974   for (type_both = 0; type_both < 2; type_both ++ ) {
2975     for (j = 2; j < total_len; j ++ ) {
2976       for (i = 1; i < j; i ++ ) {
2977 
2978         if (ops % 1000 == 0)  {
2979           printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
2980           fflush(stdout);
2981         }
2982         ops += 1;
2983 
2984         parser_init(type_both ? HTTP_BOTH : r1->type);
2985 
2986         buf1_len = i;
2987         strlncpy(buf1, sizeof(buf1), total, buf1_len);
2988         buf1[buf1_len] = 0;
2989 
2990         buf2_len = j - i;
2991         strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
2992         buf2[buf2_len] = 0;
2993 
2994         buf3_len = total_len - j;
2995         strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
2996         buf3[buf3_len] = 0;
2997 
2998         read = parse(buf1, buf1_len);
2999 
3000         if (parser->upgrade) goto test;
3001 
3002         if (read != buf1_len) {
3003           print_error(buf1, read);
3004           goto error;
3005         }
3006 
3007         read += parse(buf2, buf2_len);
3008 
3009         if (parser->upgrade) goto test;
3010 
3011         if (read != buf1_len + buf2_len) {
3012           print_error(buf2, read);
3013           goto error;
3014         }
3015 
3016         read += parse(buf3, buf3_len);
3017 
3018         if (parser->upgrade) goto test;
3019 
3020         if (read != buf1_len + buf2_len + buf3_len) {
3021           print_error(buf3, read);
3022           goto error;
3023         }
3024 
3025         parse(NULL, 0);
3026 
3027 test:
3028         if (parser->upgrade) {
3029           upgrade_message_fix(total, read, 3, r1, r2, r3);
3030         }
3031 
3032         if (message_count != num_messages) {
3033           fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3034             message_count, num_messages);
3035           goto error;
3036         }
3037 
3038         if (!message_eq(0, r1)) {
3039           fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
3040           goto error;
3041         }
3042 
3043         if (message_count > 1 && !message_eq(1, r2)) {
3044           fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
3045           goto error;
3046         }
3047 
3048         if (message_count > 2 && !message_eq(2, r3)) {
3049           fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
3050           goto error;
3051         }
3052 
3053         parser_free();
3054       }
3055     }
3056   }
3057   puts("\b\b\b\b100%");
3058   return;
3059 
3060  error:
3061   fprintf(stderr, "i=%d  j=%d\n", i, j);
3062   fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
3063   fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
3064   fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
3065   abort();
3066 }
3067 
3068 // user required to free the result
3069 // string terminated by \0
3070 char *
3071 create_large_chunked_message (int body_size_in_kb, const char* headers)
3072 {
3073   int i;
3074   size_t wrote = 0;
3075   size_t headers_len = strlen(headers);
3076   size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
3077   char * buf = malloc(bufsize);
3078 
3079   memcpy(buf, headers, headers_len);
3080   wrote += headers_len;
3081 
3082   for (i = 0; i < body_size_in_kb; i++) {
3083     // write 1kb chunk into the body.
3084     memcpy(buf + wrote, "400\r\n", 5);
3085     wrote += 5;
3086     memset(buf + wrote, 'C', 1024);
3087     wrote += 1024;
3088     strcpy(buf + wrote, "\r\n");
3089     wrote += 2;
3090   }
3091 
3092   memcpy(buf + wrote, "0\r\n\r\n", 6);
3093   wrote += 6;
3094   assert(wrote == bufsize);
3095 
3096   return buf;
3097 }
3098 
3099 void
3100 test_status_complete (void)
3101 {
3102   parser_init(HTTP_RESPONSE);
3103   parser->data = 0;
3104   http_parser_settings settings = settings_null;
3105   settings.on_status_complete = status_complete_cb;
3106 
3107   char *response = "don't mind me, just a simple response";
3108   http_parser_execute(parser, &settings, response, strlen(response));
3109   assert(parser->data == (void*)0); // the status_complete callback was never called
3110   assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line
3111 }
3112 
3113 /* Verify that we can pause parsing at any of the bytes in the
3114  * message and still get the result that we're expecting. */
3115 void
3116 test_message_pause (const struct message *msg)
3117 {
3118   char *buf = (char*) msg->raw;
3119   size_t buflen = strlen(msg->raw);
3120   size_t nread;
3121 
3122   parser_init(msg->type);
3123 
3124   do {
3125     nread = parse_pause(buf, buflen);
3126 
3127     // We can only set the upgrade buffer once we've gotten our message
3128     // completion callback.
3129     if (messages[0].message_complete_cb_called &&
3130         msg->upgrade &&
3131         parser->upgrade) {
3132       messages[0].upgrade = buf + nread;
3133       goto test;
3134     }
3135 
3136     if (nread < buflen) {
3137 
3138       // Not much do to if we failed a strict-mode check
3139       if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
3140         parser_free();
3141         return;
3142       }
3143 
3144       assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
3145     }
3146 
3147     buf += nread;
3148     buflen -= nread;
3149     http_parser_pause(parser, 0);
3150   } while (buflen > 0);
3151 
3152   nread = parse_pause(NULL, 0);
3153   assert (nread == 0);
3154 
3155 test:
3156   if (num_messages != 1) {
3157     printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
3158     abort();
3159   }
3160 
3161   if(!message_eq(0, msg)) abort();
3162 
3163   parser_free();
3164 }
3165 
3166 int
3167 main (void)
3168 {
3169   parser = NULL;
3170   int i, j, k;
3171   int request_count;
3172   int response_count;
3173 
3174   printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
3175 
3176   for (request_count = 0; requests[request_count].name; request_count++);
3177   for (response_count = 0; responses[response_count].name; response_count++);
3178 
3179   //// API
3180   test_preserve_data();
3181   test_parse_url();
3182   test_method_str();
3183 
3184   //// OVERFLOW CONDITIONS
3185 
3186   test_header_overflow_error(HTTP_REQUEST);
3187   test_no_overflow_long_body(HTTP_REQUEST, 1000);
3188   test_no_overflow_long_body(HTTP_REQUEST, 100000);
3189 
3190   test_header_overflow_error(HTTP_RESPONSE);
3191   test_no_overflow_long_body(HTTP_RESPONSE, 1000);
3192   test_no_overflow_long_body(HTTP_RESPONSE, 100000);
3193 
3194   test_header_content_length_overflow_error();
3195   test_chunk_content_length_overflow_error();
3196 
3197   //// RESPONSES
3198 
3199   for (i = 0; i < response_count; i++) {
3200     test_message(&responses[i]);
3201   }
3202 
3203   for (i = 0; i < response_count; i++) {
3204     test_message_pause(&responses[i]);
3205   }
3206 
3207   for (i = 0; i < response_count; i++) {
3208     if (!responses[i].should_keep_alive) continue;
3209     for (j = 0; j < response_count; j++) {
3210       if (!responses[j].should_keep_alive) continue;
3211       for (k = 0; k < response_count; k++) {
3212         test_multiple3(&responses[i], &responses[j], &responses[k]);
3213       }
3214     }
3215   }
3216 
3217   test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
3218   test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
3219 
3220   // test very large chunked response
3221   {
3222     char * msg = create_large_chunked_message(31337,
3223       "HTTP/1.0 200 OK\r\n"
3224       "Transfer-Encoding: chunked\r\n"
3225       "Content-Type: text/plain\r\n"
3226       "\r\n");
3227     struct message large_chunked =
3228       {.name= "large chunked"
3229       ,.type= HTTP_RESPONSE
3230       ,.raw= msg
3231       ,.should_keep_alive= FALSE
3232       ,.message_complete_on_eof= FALSE
3233       ,.http_major= 1
3234       ,.http_minor= 0
3235       ,.status_code= 200
3236       ,.num_headers= 2
3237       ,.headers=
3238         { { "Transfer-Encoding", "chunked" }
3239         , { "Content-Type", "text/plain" }
3240         }
3241       ,.body_size= 31337*1024
3242       };
3243     test_message_count_body(&large_chunked);
3244     free(msg);
3245   }
3246 
3247 
3248 
3249   printf("response scan 1/2      ");
3250   test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
3251            , &responses[NO_BODY_HTTP10_KA_204]
3252            , &responses[NO_REASON_PHRASE]
3253            );
3254 
3255   printf("response scan 2/2      ");
3256   test_scan( &responses[BONJOUR_MADAME_FR]
3257            , &responses[UNDERSTORE_HEADER_KEY]
3258            , &responses[NO_CARRIAGE_RET]
3259            );
3260 
3261   puts("responses okay");
3262 
3263 
3264   /// REQUESTS
3265 
3266   test_simple("hello world", HPE_INVALID_METHOD);
3267   test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
3268 
3269 
3270   test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
3271   test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
3272   test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
3273 
3274   // Well-formed but incomplete
3275   test_simple("GET / HTTP/1.1\r\n"
3276               "Content-Type: text/plain\r\n"
3277               "Content-Length: 6\r\n"
3278               "\r\n"
3279               "fooba",
3280               HPE_OK);
3281 
3282   static const char *all_methods[] = {
3283     "DELETE",
3284     "GET",
3285     "HEAD",
3286     "POST",
3287     "PUT",
3288     //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
3289     "OPTIONS",
3290     "TRACE",
3291     "COPY",
3292     "LOCK",
3293     "MKCOL",
3294     "MOVE",
3295     "PROPFIND",
3296     "PROPPATCH",
3297     "UNLOCK",
3298     "REPORT",
3299     "MKACTIVITY",
3300     "CHECKOUT",
3301     "MERGE",
3302     "M-SEARCH",
3303     "NOTIFY",
3304     "SUBSCRIBE",
3305     "UNSUBSCRIBE",
3306     "PATCH",
3307     0 };
3308   const char **this_method;
3309   for (this_method = all_methods; *this_method; this_method++) {
3310     char buf[200];
3311     sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3312     test_simple(buf, HPE_OK);
3313   }
3314 
3315   static const char *bad_methods[] = {
3316       "C******",
3317       "M****",
3318       0 };
3319   for (this_method = bad_methods; *this_method; this_method++) {
3320     char buf[200];
3321     sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3322     test_simple(buf, HPE_UNKNOWN);
3323   }
3324 
3325   const char *dumbfuck2 =
3326     "GET / HTTP/1.1\r\n"
3327     "X-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n"
3328     "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
3329     "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
3330     "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
3331     "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
3332     "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
3333     "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
3334     "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
3335     "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
3336     "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
3337     "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
3338     "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
3339     "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
3340     "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
3341     "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
3342     "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
3343     "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
3344     "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
3345     "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
3346     "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
3347     "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
3348     "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
3349     "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
3350     "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
3351     "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
3352     "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
3353     "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
3354     "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
3355     "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
3356     "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
3357     "\tRA==\r\n"
3358     "\t-----END CERTIFICATE-----\r\n"
3359     "\r\n";
3360   test_simple(dumbfuck2, HPE_OK);
3361 
3362 #if 0
3363   // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
3364   // until EOF.
3365   //
3366   // no content-length
3367   // error if there is a body without content length
3368   const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
3369                                            "Accept: */*\r\n"
3370                                            "\r\n"
3371                                            "HELLO";
3372   test_simple(bad_get_no_headers_no_body, 0);
3373 #endif
3374   /* TODO sending junk and large headers gets rejected */
3375 
3376 
3377   /* check to make sure our predefined requests are okay */
3378   for (i = 0; requests[i].name; i++) {
3379     test_message(&requests[i]);
3380   }
3381 
3382   for (i = 0; i < request_count; i++) {
3383     test_message_pause(&requests[i]);
3384   }
3385 
3386   for (i = 0; i < request_count; i++) {
3387     if (!requests[i].should_keep_alive) continue;
3388     for (j = 0; j < request_count; j++) {
3389       if (!requests[j].should_keep_alive) continue;
3390       for (k = 0; k < request_count; k++) {
3391         test_multiple3(&requests[i], &requests[j], &requests[k]);
3392       }
3393     }
3394   }
3395 
3396   printf("request scan 1/4      ");
3397   test_scan( &requests[GET_NO_HEADERS_NO_BODY]
3398            , &requests[GET_ONE_HEADER_NO_BODY]
3399            , &requests[GET_NO_HEADERS_NO_BODY]
3400            );
3401 
3402   printf("request scan 2/4      ");
3403   test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
3404            , &requests[POST_IDENTITY_BODY_WORLD]
3405            , &requests[GET_FUNKY_CONTENT_LENGTH]
3406            );
3407 
3408   printf("request scan 3/4      ");
3409   test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
3410            , &requests[CHUNKED_W_TRAILING_HEADERS]
3411            , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
3412            );
3413 
3414   printf("request scan 4/4      ");
3415   test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
3416            , &requests[PREFIX_NEWLINE_GET ]
3417            , &requests[CONNECT_REQUEST]
3418            );
3419 
3420   test_status_complete();
3421 
3422   puts("requests okay");
3423 
3424   return 0;
3425 }
3426