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 #define AMAZON_COM 20
1414 , {.name= "amazon.com"
1415 ,.type= HTTP_RESPONSE
1416 ,.raw= "HTTP/1.1 301 MovedPermanently\r\n"
1417 "Date: Wed, 15 May 2013 17:06:33 GMT\r\n"
1418 "Server: Server\r\n"
1419 "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n"
1420 "p3p: policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"\r\n"
1421 "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n"
1422 "Location: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\r\n"
1423 "Vary: Accept-Encoding,User-Agent\r\n"
1424 "Content-Type: text/html; charset=ISO-8859-1\r\n"
1425 "Transfer-Encoding: chunked\r\n"
1426 "\r\n"
1427 "1\r\n"
1428 "\n\r\n"
1429 "0\r\n"
1430 "\r\n"
1431 ,.should_keep_alive= TRUE
1432 ,.message_complete_on_eof= FALSE
1433 ,.http_major= 1
1434 ,.http_minor= 1
1435 ,.status_code= 301
1436 ,.num_headers= 9
1437 ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" }
1438 , { "Server", "Server" }
1439 , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" }
1440 , { "p3p", "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"" }
1441 , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" }
1442 , { "Location", "http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846" }
1443 , { "Vary", "Accept-Encoding,User-Agent" }
1444 , { "Content-Type", "text/html; charset=ISO-8859-1" }
1445 , { "Transfer-Encoding", "chunked" }
1446 }
1447 ,.body= "\n"
1448 }
1449
1450 , {.name= NULL } /* sentinel */
1451 };
1452
1453 /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1454 * define it ourselves.
1455 */
1456 size_t
strnlen(const char * s,size_t maxlen)1457 strnlen(const char *s, size_t maxlen)
1458 {
1459 const char *p;
1460
1461 p = memchr(s, '\0', maxlen);
1462 if (p == NULL)
1463 return maxlen;
1464
1465 return p - s;
1466 }
1467
1468 size_t
strlncat(char * dst,size_t len,const char * src,size_t n)1469 strlncat(char *dst, size_t len, const char *src, size_t n)
1470 {
1471 size_t slen;
1472 size_t dlen;
1473 size_t rlen;
1474 size_t ncpy;
1475
1476 slen = strnlen(src, n);
1477 dlen = strnlen(dst, len);
1478
1479 if (dlen < len) {
1480 rlen = len - dlen;
1481 ncpy = slen < rlen ? slen : (rlen - 1);
1482 memcpy(dst + dlen, src, ncpy);
1483 dst[dlen + ncpy] = '\0';
1484 }
1485
1486 assert(len > slen + dlen);
1487 return slen + dlen;
1488 }
1489
1490 size_t
strlcat(char * dst,const char * src,size_t len)1491 strlcat(char *dst, const char *src, size_t len)
1492 {
1493 return strlncat(dst, len, src, (size_t) -1);
1494 }
1495
1496 size_t
strlncpy(char * dst,size_t len,const char * src,size_t n)1497 strlncpy(char *dst, size_t len, const char *src, size_t n)
1498 {
1499 size_t slen;
1500 size_t ncpy;
1501
1502 slen = strnlen(src, n);
1503
1504 if (len > 0) {
1505 ncpy = slen < len ? slen : (len - 1);
1506 memcpy(dst, src, ncpy);
1507 dst[ncpy] = '\0';
1508 }
1509
1510 assert(len > slen);
1511 return slen;
1512 }
1513
1514 size_t
strlcpy(char * dst,const char * src,size_t len)1515 strlcpy(char *dst, const char *src, size_t len)
1516 {
1517 return strlncpy(dst, len, src, (size_t) -1);
1518 }
1519
1520 int
request_url_cb(http_parser * p,const char * buf,size_t len)1521 request_url_cb (http_parser *p, const char *buf, size_t len)
1522 {
1523 assert(p == parser);
1524 strlncat(messages[num_messages].request_url,
1525 sizeof(messages[num_messages].request_url),
1526 buf,
1527 len);
1528 return 0;
1529 }
1530
1531 int
status_complete_cb(http_parser * p)1532 status_complete_cb (http_parser *p) {
1533 assert(p == parser);
1534 p->data++;
1535 return 0;
1536 }
1537
1538 int
header_field_cb(http_parser * p,const char * buf,size_t len)1539 header_field_cb (http_parser *p, const char *buf, size_t len)
1540 {
1541 assert(p == parser);
1542 struct message *m = &messages[num_messages];
1543
1544 if (m->last_header_element != FIELD)
1545 m->num_headers++;
1546
1547 strlncat(m->headers[m->num_headers-1][0],
1548 sizeof(m->headers[m->num_headers-1][0]),
1549 buf,
1550 len);
1551
1552 m->last_header_element = FIELD;
1553
1554 return 0;
1555 }
1556
1557 int
header_value_cb(http_parser * p,const char * buf,size_t len)1558 header_value_cb (http_parser *p, const char *buf, size_t len)
1559 {
1560 assert(p == parser);
1561 struct message *m = &messages[num_messages];
1562
1563 strlncat(m->headers[m->num_headers-1][1],
1564 sizeof(m->headers[m->num_headers-1][1]),
1565 buf,
1566 len);
1567
1568 m->last_header_element = VALUE;
1569
1570 return 0;
1571 }
1572
1573 void
check_body_is_final(const http_parser * p)1574 check_body_is_final (const http_parser *p)
1575 {
1576 if (messages[num_messages].body_is_final) {
1577 fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1578 "on last on_body callback call "
1579 "but it doesn't! ***\n\n");
1580 assert(0);
1581 abort();
1582 }
1583 messages[num_messages].body_is_final = http_body_is_final(p);
1584 }
1585
1586 int
body_cb(http_parser * p,const char * buf,size_t len)1587 body_cb (http_parser *p, const char *buf, size_t len)
1588 {
1589 assert(p == parser);
1590 strlncat(messages[num_messages].body,
1591 sizeof(messages[num_messages].body),
1592 buf,
1593 len);
1594 messages[num_messages].body_size += len;
1595 check_body_is_final(p);
1596 // printf("body_cb: '%s'\n", requests[num_messages].body);
1597 return 0;
1598 }
1599
1600 int
count_body_cb(http_parser * p,const char * buf,size_t len)1601 count_body_cb (http_parser *p, const char *buf, size_t len)
1602 {
1603 assert(p == parser);
1604 assert(buf);
1605 messages[num_messages].body_size += len;
1606 check_body_is_final(p);
1607 return 0;
1608 }
1609
1610 int
message_begin_cb(http_parser * p)1611 message_begin_cb (http_parser *p)
1612 {
1613 assert(p == parser);
1614 messages[num_messages].message_begin_cb_called = TRUE;
1615 return 0;
1616 }
1617
1618 int
headers_complete_cb(http_parser * p)1619 headers_complete_cb (http_parser *p)
1620 {
1621 assert(p == parser);
1622 messages[num_messages].method = parser->method;
1623 messages[num_messages].status_code = parser->status_code;
1624 messages[num_messages].http_major = parser->http_major;
1625 messages[num_messages].http_minor = parser->http_minor;
1626 messages[num_messages].headers_complete_cb_called = TRUE;
1627 messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
1628 return 0;
1629 }
1630
1631 int
message_complete_cb(http_parser * p)1632 message_complete_cb (http_parser *p)
1633 {
1634 assert(p == parser);
1635 if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
1636 {
1637 fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
1638 "value in both on_message_complete and on_headers_complete "
1639 "but it doesn't! ***\n\n");
1640 assert(0);
1641 abort();
1642 }
1643
1644 if (messages[num_messages].body_size &&
1645 http_body_is_final(p) &&
1646 !messages[num_messages].body_is_final)
1647 {
1648 fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1649 "on last on_body callback call "
1650 "but it doesn't! ***\n\n");
1651 assert(0);
1652 abort();
1653 }
1654
1655 messages[num_messages].message_complete_cb_called = TRUE;
1656
1657 messages[num_messages].message_complete_on_eof = currently_parsing_eof;
1658
1659 num_messages++;
1660 return 0;
1661 }
1662
1663 /* These dontcall_* callbacks exist so that we can verify that when we're
1664 * paused, no additional callbacks are invoked */
1665 int
dontcall_message_begin_cb(http_parser * p)1666 dontcall_message_begin_cb (http_parser *p)
1667 {
1668 if (p) { } // gcc
1669 fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1670 abort();
1671 }
1672
1673 int
dontcall_header_field_cb(http_parser * p,const char * buf,size_t len)1674 dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1675 {
1676 if (p || buf || len) { } // gcc
1677 fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1678 abort();
1679 }
1680
1681 int
dontcall_header_value_cb(http_parser * p,const char * buf,size_t len)1682 dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1683 {
1684 if (p || buf || len) { } // gcc
1685 fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1686 abort();
1687 }
1688
1689 int
dontcall_request_url_cb(http_parser * p,const char * buf,size_t len)1690 dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1691 {
1692 if (p || buf || len) { } // gcc
1693 fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1694 abort();
1695 }
1696
1697 int
dontcall_body_cb(http_parser * p,const char * buf,size_t len)1698 dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1699 {
1700 if (p || buf || len) { } // gcc
1701 fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1702 abort();
1703 }
1704
1705 int
dontcall_headers_complete_cb(http_parser * p)1706 dontcall_headers_complete_cb (http_parser *p)
1707 {
1708 if (p) { } // gcc
1709 fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1710 "parser ***\n\n");
1711 abort();
1712 }
1713
1714 int
dontcall_message_complete_cb(http_parser * p)1715 dontcall_message_complete_cb (http_parser *p)
1716 {
1717 if (p) { } // gcc
1718 fprintf(stderr, "\n\n*** on_message_complete() called on paused "
1719 "parser ***\n\n");
1720 abort();
1721 }
1722
1723 static http_parser_settings settings_dontcall =
1724 {.on_message_begin = dontcall_message_begin_cb
1725 ,.on_header_field = dontcall_header_field_cb
1726 ,.on_header_value = dontcall_header_value_cb
1727 ,.on_url = dontcall_request_url_cb
1728 ,.on_body = dontcall_body_cb
1729 ,.on_headers_complete = dontcall_headers_complete_cb
1730 ,.on_message_complete = dontcall_message_complete_cb
1731 };
1732
1733 /* These pause_* callbacks always pause the parser and just invoke the regular
1734 * callback that tracks content. Before returning, we overwrite the parser
1735 * settings to point to the _dontcall variety so that we can verify that
1736 * the pause actually did, you know, pause. */
1737 int
pause_message_begin_cb(http_parser * p)1738 pause_message_begin_cb (http_parser *p)
1739 {
1740 http_parser_pause(p, 1);
1741 *current_pause_parser = settings_dontcall;
1742 return message_begin_cb(p);
1743 }
1744
1745 int
pause_header_field_cb(http_parser * p,const char * buf,size_t len)1746 pause_header_field_cb (http_parser *p, const char *buf, size_t len)
1747 {
1748 http_parser_pause(p, 1);
1749 *current_pause_parser = settings_dontcall;
1750 return header_field_cb(p, buf, len);
1751 }
1752
1753 int
pause_header_value_cb(http_parser * p,const char * buf,size_t len)1754 pause_header_value_cb (http_parser *p, const char *buf, size_t len)
1755 {
1756 http_parser_pause(p, 1);
1757 *current_pause_parser = settings_dontcall;
1758 return header_value_cb(p, buf, len);
1759 }
1760
1761 int
pause_request_url_cb(http_parser * p,const char * buf,size_t len)1762 pause_request_url_cb (http_parser *p, const char *buf, size_t len)
1763 {
1764 http_parser_pause(p, 1);
1765 *current_pause_parser = settings_dontcall;
1766 return request_url_cb(p, buf, len);
1767 }
1768
1769 int
pause_body_cb(http_parser * p,const char * buf,size_t len)1770 pause_body_cb (http_parser *p, const char *buf, size_t len)
1771 {
1772 http_parser_pause(p, 1);
1773 *current_pause_parser = settings_dontcall;
1774 return body_cb(p, buf, len);
1775 }
1776
1777 int
pause_headers_complete_cb(http_parser * p)1778 pause_headers_complete_cb (http_parser *p)
1779 {
1780 http_parser_pause(p, 1);
1781 *current_pause_parser = settings_dontcall;
1782 return headers_complete_cb(p);
1783 }
1784
1785 int
pause_message_complete_cb(http_parser * p)1786 pause_message_complete_cb (http_parser *p)
1787 {
1788 http_parser_pause(p, 1);
1789 *current_pause_parser = settings_dontcall;
1790 return message_complete_cb(p);
1791 }
1792
1793 static http_parser_settings settings_pause =
1794 {.on_message_begin = pause_message_begin_cb
1795 ,.on_header_field = pause_header_field_cb
1796 ,.on_header_value = pause_header_value_cb
1797 ,.on_url = pause_request_url_cb
1798 ,.on_body = pause_body_cb
1799 ,.on_headers_complete = pause_headers_complete_cb
1800 ,.on_message_complete = pause_message_complete_cb
1801 };
1802
1803 static http_parser_settings settings =
1804 {.on_message_begin = message_begin_cb
1805 ,.on_header_field = header_field_cb
1806 ,.on_header_value = header_value_cb
1807 ,.on_url = request_url_cb
1808 ,.on_body = body_cb
1809 ,.on_headers_complete = headers_complete_cb
1810 ,.on_message_complete = message_complete_cb
1811 };
1812
1813 static http_parser_settings settings_count_body =
1814 {.on_message_begin = message_begin_cb
1815 ,.on_header_field = header_field_cb
1816 ,.on_header_value = header_value_cb
1817 ,.on_url = request_url_cb
1818 ,.on_body = count_body_cb
1819 ,.on_headers_complete = headers_complete_cb
1820 ,.on_message_complete = message_complete_cb
1821 };
1822
1823 static http_parser_settings settings_null =
1824 {.on_message_begin = 0
1825 ,.on_header_field = 0
1826 ,.on_header_value = 0
1827 ,.on_url = 0
1828 ,.on_body = 0
1829 ,.on_headers_complete = 0
1830 ,.on_message_complete = 0
1831 };
1832
1833 void
parser_init(enum http_parser_type type)1834 parser_init (enum http_parser_type type)
1835 {
1836 num_messages = 0;
1837
1838 assert(parser == NULL);
1839
1840 parser = malloc(sizeof(http_parser));
1841
1842 http_parser_init(parser, type);
1843
1844 memset(&messages, 0, sizeof messages);
1845
1846 }
1847
1848 void
parser_free()1849 parser_free ()
1850 {
1851 assert(parser);
1852 free(parser);
1853 parser = NULL;
1854 }
1855
parse(const char * buf,size_t len)1856 size_t parse (const char *buf, size_t len)
1857 {
1858 size_t nparsed;
1859 currently_parsing_eof = (len == 0);
1860 nparsed = http_parser_execute(parser, &settings, buf, len);
1861 return nparsed;
1862 }
1863
parse_count_body(const char * buf,size_t len)1864 size_t parse_count_body (const char *buf, size_t len)
1865 {
1866 size_t nparsed;
1867 currently_parsing_eof = (len == 0);
1868 nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
1869 return nparsed;
1870 }
1871
parse_pause(const char * buf,size_t len)1872 size_t parse_pause (const char *buf, size_t len)
1873 {
1874 size_t nparsed;
1875 http_parser_settings s = settings_pause;
1876
1877 currently_parsing_eof = (len == 0);
1878 current_pause_parser = &s;
1879 nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
1880 return nparsed;
1881 }
1882
1883 static inline int
check_str_eq(const struct message * m,const char * prop,const char * expected,const char * found)1884 check_str_eq (const struct message *m,
1885 const char *prop,
1886 const char *expected,
1887 const char *found) {
1888 if ((expected == NULL) != (found == NULL)) {
1889 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1890 printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
1891 printf(" found %s\n", (found == NULL) ? "NULL" : found);
1892 return 0;
1893 }
1894 if (expected != NULL && 0 != strcmp(expected, found)) {
1895 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1896 printf("expected '%s'\n", expected);
1897 printf(" found '%s'\n", found);
1898 return 0;
1899 }
1900 return 1;
1901 }
1902
1903 static inline int
check_num_eq(const struct message * m,const char * prop,int expected,int found)1904 check_num_eq (const struct message *m,
1905 const char *prop,
1906 int expected,
1907 int found) {
1908 if (expected != found) {
1909 printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1910 printf("expected %d\n", expected);
1911 printf(" found %d\n", found);
1912 return 0;
1913 }
1914 return 1;
1915 }
1916
1917 #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
1918 if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
1919
1920 #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1921 if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1922
1923 #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
1924 do { \
1925 char ubuf[256]; \
1926 \
1927 if ((u)->field_set & (1 << (fn))) { \
1928 memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
1929 (u)->field_data[(fn)].len); \
1930 ubuf[(u)->field_data[(fn)].len] = '\0'; \
1931 } else { \
1932 ubuf[0] = '\0'; \
1933 } \
1934 \
1935 check_str_eq(expected, #prop, expected->prop, ubuf); \
1936 } while(0)
1937
1938 int
message_eq(int index,const struct message * expected)1939 message_eq (int index, const struct message *expected)
1940 {
1941 int i;
1942 struct message *m = &messages[index];
1943
1944 MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
1945 MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
1946
1947 if (expected->type == HTTP_REQUEST) {
1948 MESSAGE_CHECK_NUM_EQ(expected, m, method);
1949 } else {
1950 MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
1951 }
1952
1953 MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
1954 MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
1955
1956 assert(m->message_begin_cb_called);
1957 assert(m->headers_complete_cb_called);
1958 assert(m->message_complete_cb_called);
1959
1960
1961 MESSAGE_CHECK_STR_EQ(expected, m, request_url);
1962
1963 /* Check URL components; we can't do this w/ CONNECT since it doesn't
1964 * send us a well-formed URL.
1965 */
1966 if (*m->request_url && m->method != HTTP_CONNECT) {
1967 struct http_parser_url u;
1968
1969 if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
1970 fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
1971 m->request_url);
1972 abort();
1973 }
1974
1975 if (expected->host) {
1976 MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
1977 }
1978
1979 if (expected->userinfo) {
1980 MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
1981 }
1982
1983 m->port = (u.field_set & (1 << UF_PORT)) ?
1984 u.port : 0;
1985
1986 MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
1987 MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
1988 MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
1989 MESSAGE_CHECK_NUM_EQ(expected, m, port);
1990 }
1991
1992 if (expected->body_size) {
1993 MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1994 } else {
1995 MESSAGE_CHECK_STR_EQ(expected, m, body);
1996 }
1997
1998 MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
1999
2000 int r;
2001 for (i = 0; i < m->num_headers; i++) {
2002 r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
2003 if (!r) return 0;
2004 r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
2005 if (!r) return 0;
2006 }
2007
2008 MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
2009
2010 return 1;
2011 }
2012
2013 /* Given a sequence of varargs messages, return the number of them that the
2014 * parser should successfully parse, taking into account that upgraded
2015 * messages prevent all subsequent messages from being parsed.
2016 */
2017 size_t
count_parsed_messages(const size_t nmsgs,...)2018 count_parsed_messages(const size_t nmsgs, ...) {
2019 size_t i;
2020 va_list ap;
2021
2022 va_start(ap, nmsgs);
2023
2024 for (i = 0; i < nmsgs; i++) {
2025 struct message *m = va_arg(ap, struct message *);
2026
2027 if (m->upgrade) {
2028 va_end(ap);
2029 return i + 1;
2030 }
2031 }
2032
2033 va_end(ap);
2034 return nmsgs;
2035 }
2036
2037 /* Given a sequence of bytes and the number of these that we were able to
2038 * parse, verify that upgrade bodies are correct.
2039 */
2040 void
upgrade_message_fix(char * body,const size_t nread,const size_t nmsgs,...)2041 upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2042 va_list ap;
2043 size_t i;
2044 size_t off = 0;
2045
2046 va_start(ap, nmsgs);
2047
2048 for (i = 0; i < nmsgs; i++) {
2049 struct message *m = va_arg(ap, struct message *);
2050
2051 off += strlen(m->raw);
2052
2053 if (m->upgrade) {
2054 off -= strlen(m->upgrade);
2055
2056 /* Check the portion of the response after its specified upgrade */
2057 if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2058 abort();
2059 }
2060
2061 /* Fix up the response so that message_eq() will verify the beginning
2062 * of the upgrade */
2063 *(body + nread + strlen(m->upgrade)) = '\0';
2064 messages[num_messages -1 ].upgrade = body + nread;
2065
2066 va_end(ap);
2067 return;
2068 }
2069 }
2070
2071 va_end(ap);
2072 printf("\n\n*** Error: expected a message with upgrade ***\n");
2073
2074 abort();
2075 }
2076
2077 static void
print_error(const char * raw,size_t error_location)2078 print_error (const char *raw, size_t error_location)
2079 {
2080 fprintf(stderr, "\n*** %s ***\n\n",
2081 http_errno_description(HTTP_PARSER_ERRNO(parser)));
2082
2083 int this_line = 0, char_len = 0;
2084 size_t i, j, len = strlen(raw), error_location_line = 0;
2085 for (i = 0; i < len; i++) {
2086 if (i == error_location) this_line = 1;
2087 switch (raw[i]) {
2088 case '\r':
2089 char_len = 2;
2090 fprintf(stderr, "\\r");
2091 break;
2092
2093 case '\n':
2094 char_len = 2;
2095 fprintf(stderr, "\\n\n");
2096
2097 if (this_line) goto print;
2098
2099 error_location_line = 0;
2100 continue;
2101
2102 default:
2103 char_len = 1;
2104 fputc(raw[i], stderr);
2105 break;
2106 }
2107 if (!this_line) error_location_line += char_len;
2108 }
2109
2110 fprintf(stderr, "[eof]\n");
2111
2112 print:
2113 for (j = 0; j < error_location_line; j++) {
2114 fputc(' ', stderr);
2115 }
2116 fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2117 }
2118
2119 void
test_preserve_data(void)2120 test_preserve_data (void)
2121 {
2122 char my_data[] = "application-specific data";
2123 http_parser parser;
2124 parser.data = my_data;
2125 http_parser_init(&parser, HTTP_REQUEST);
2126 if (parser.data != my_data) {
2127 printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2128 abort();
2129 }
2130 }
2131
2132 struct url_test {
2133 const char *name;
2134 const char *url;
2135 int is_connect;
2136 struct http_parser_url u;
2137 int rv;
2138 };
2139
2140 const struct url_test url_tests[] =
2141 { {.name="proxy request"
2142 ,.url="http://hostname/"
2143 ,.is_connect=0
2144 ,.u=
2145 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2146 ,.port=0
2147 ,.field_data=
2148 {{ 0, 4 } /* UF_SCHEMA */
2149 ,{ 7, 8 } /* UF_HOST */
2150 ,{ 0, 0 } /* UF_PORT */
2151 ,{ 15, 1 } /* UF_PATH */
2152 ,{ 0, 0 } /* UF_QUERY */
2153 ,{ 0, 0 } /* UF_FRAGMENT */
2154 ,{ 0, 0 } /* UF_USERINFO */
2155 }
2156 }
2157 ,.rv=0
2158 }
2159
2160 , {.name="proxy request with port"
2161 ,.url="http://hostname:444/"
2162 ,.is_connect=0
2163 ,.u=
2164 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2165 ,.port=444
2166 ,.field_data=
2167 {{ 0, 4 } /* UF_SCHEMA */
2168 ,{ 7, 8 } /* UF_HOST */
2169 ,{ 16, 3 } /* UF_PORT */
2170 ,{ 19, 1 } /* UF_PATH */
2171 ,{ 0, 0 } /* UF_QUERY */
2172 ,{ 0, 0 } /* UF_FRAGMENT */
2173 ,{ 0, 0 } /* UF_USERINFO */
2174 }
2175 }
2176 ,.rv=0
2177 }
2178
2179 , {.name="CONNECT request"
2180 ,.url="hostname:443"
2181 ,.is_connect=1
2182 ,.u=
2183 {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2184 ,.port=443
2185 ,.field_data=
2186 {{ 0, 0 } /* UF_SCHEMA */
2187 ,{ 0, 8 } /* UF_HOST */
2188 ,{ 9, 3 } /* UF_PORT */
2189 ,{ 0, 0 } /* UF_PATH */
2190 ,{ 0, 0 } /* UF_QUERY */
2191 ,{ 0, 0 } /* UF_FRAGMENT */
2192 ,{ 0, 0 } /* UF_USERINFO */
2193 }
2194 }
2195 ,.rv=0
2196 }
2197
2198 , {.name="CONNECT request but not connect"
2199 ,.url="hostname:443"
2200 ,.is_connect=0
2201 ,.rv=1
2202 }
2203
2204 , {.name="proxy ipv6 request"
2205 ,.url="http://[1:2::3:4]/"
2206 ,.is_connect=0
2207 ,.u=
2208 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2209 ,.port=0
2210 ,.field_data=
2211 {{ 0, 4 } /* UF_SCHEMA */
2212 ,{ 8, 8 } /* UF_HOST */
2213 ,{ 0, 0 } /* UF_PORT */
2214 ,{ 17, 1 } /* UF_PATH */
2215 ,{ 0, 0 } /* UF_QUERY */
2216 ,{ 0, 0 } /* UF_FRAGMENT */
2217 ,{ 0, 0 } /* UF_USERINFO */
2218 }
2219 }
2220 ,.rv=0
2221 }
2222
2223 , {.name="proxy ipv6 request with port"
2224 ,.url="http://[1:2::3:4]:67/"
2225 ,.is_connect=0
2226 ,.u=
2227 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2228 ,.port=67
2229 ,.field_data=
2230 {{ 0, 4 } /* UF_SCHEMA */
2231 ,{ 8, 8 } /* UF_HOST */
2232 ,{ 18, 2 } /* UF_PORT */
2233 ,{ 20, 1 } /* UF_PATH */
2234 ,{ 0, 0 } /* UF_QUERY */
2235 ,{ 0, 0 } /* UF_FRAGMENT */
2236 ,{ 0, 0 } /* UF_USERINFO */
2237 }
2238 }
2239 ,.rv=0
2240 }
2241
2242 , {.name="CONNECT ipv6 address"
2243 ,.url="[1:2::3:4]:443"
2244 ,.is_connect=1
2245 ,.u=
2246 {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2247 ,.port=443
2248 ,.field_data=
2249 {{ 0, 0 } /* UF_SCHEMA */
2250 ,{ 1, 8 } /* UF_HOST */
2251 ,{ 11, 3 } /* UF_PORT */
2252 ,{ 0, 0 } /* UF_PATH */
2253 ,{ 0, 0 } /* UF_QUERY */
2254 ,{ 0, 0 } /* UF_FRAGMENT */
2255 ,{ 0, 0 } /* UF_USERINFO */
2256 }
2257 }
2258 ,.rv=0
2259 }
2260
2261 , {.name="ipv4 in ipv6 address"
2262 ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2263 ,.is_connect=0
2264 ,.u=
2265 {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2266 ,.port=0
2267 ,.field_data=
2268 {{ 0, 4 } /* UF_SCHEMA */
2269 ,{ 8, 37 } /* UF_HOST */
2270 ,{ 0, 0 } /* UF_PORT */
2271 ,{ 46, 1 } /* UF_PATH */
2272 ,{ 0, 0 } /* UF_QUERY */
2273 ,{ 0, 0 } /* UF_FRAGMENT */
2274 ,{ 0, 0 } /* UF_USERINFO */
2275 }
2276 }
2277 ,.rv=0
2278 }
2279
2280 , {.name="extra ? in query string"
2281 ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
2282 "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
2283 "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2284 ,.is_connect=0
2285 ,.u=
2286 {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2287 ,.port=0
2288 ,.field_data=
2289 {{ 0, 4 } /* UF_SCHEMA */
2290 ,{ 7, 10 } /* UF_HOST */
2291 ,{ 0, 0 } /* UF_PORT */
2292 ,{ 17, 12 } /* UF_PATH */
2293 ,{ 30,187 } /* UF_QUERY */
2294 ,{ 0, 0 } /* UF_FRAGMENT */
2295 ,{ 0, 0 } /* UF_USERINFO */
2296 }
2297 }
2298 ,.rv=0
2299 }
2300
2301 , {.name="space URL encoded"
2302 ,.url="/toto.html?toto=a%20b"
2303 ,.is_connect=0
2304 ,.u=
2305 {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2306 ,.port=0
2307 ,.field_data=
2308 {{ 0, 0 } /* UF_SCHEMA */
2309 ,{ 0, 0 } /* UF_HOST */
2310 ,{ 0, 0 } /* UF_PORT */
2311 ,{ 0, 10 } /* UF_PATH */
2312 ,{ 11, 10 } /* UF_QUERY */
2313 ,{ 0, 0 } /* UF_FRAGMENT */
2314 ,{ 0, 0 } /* UF_USERINFO */
2315 }
2316 }
2317 ,.rv=0
2318 }
2319
2320
2321 , {.name="URL fragment"
2322 ,.url="/toto.html#titi"
2323 ,.is_connect=0
2324 ,.u=
2325 {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2326 ,.port=0
2327 ,.field_data=
2328 {{ 0, 0 } /* UF_SCHEMA */
2329 ,{ 0, 0 } /* UF_HOST */
2330 ,{ 0, 0 } /* UF_PORT */
2331 ,{ 0, 10 } /* UF_PATH */
2332 ,{ 0, 0 } /* UF_QUERY */
2333 ,{ 11, 4 } /* UF_FRAGMENT */
2334 ,{ 0, 0 } /* UF_USERINFO */
2335 }
2336 }
2337 ,.rv=0
2338 }
2339
2340 , {.name="complex URL fragment"
2341 ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
2342 "http://www.example.com/index.html?foo=bar&hello=world#midpage"
2343 ,.is_connect=0
2344 ,.u=
2345 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2346 (1<<UF_FRAGMENT)
2347 ,.port=0
2348 ,.field_data=
2349 {{ 0, 4 } /* UF_SCHEMA */
2350 ,{ 7, 22 } /* UF_HOST */
2351 ,{ 0, 0 } /* UF_PORT */
2352 ,{ 29, 6 } /* UF_PATH */
2353 ,{ 36, 69 } /* UF_QUERY */
2354 ,{106, 7 } /* UF_FRAGMENT */
2355 ,{ 0, 0 } /* UF_USERINFO */
2356 }
2357 }
2358 ,.rv=0
2359 }
2360
2361 , {.name="complex URL from node js url parser doc"
2362 ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
2363 ,.is_connect=0
2364 ,.u=
2365 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2366 (1<<UF_QUERY) | (1<<UF_FRAGMENT)
2367 ,.port=8080
2368 ,.field_data=
2369 {{ 0, 4 } /* UF_SCHEMA */
2370 ,{ 7, 8 } /* UF_HOST */
2371 ,{ 16, 4 } /* UF_PORT */
2372 ,{ 20, 8 } /* UF_PATH */
2373 ,{ 29, 12 } /* UF_QUERY */
2374 ,{ 42, 4 } /* UF_FRAGMENT */
2375 ,{ 0, 0 } /* UF_USERINFO */
2376 }
2377 }
2378 ,.rv=0
2379 }
2380
2381 , {.name="complex URL with basic auth from node js url parser doc"
2382 ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
2383 ,.is_connect=0
2384 ,.u=
2385 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2386 (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
2387 ,.port=8080
2388 ,.field_data=
2389 {{ 0, 4 } /* UF_SCHEMA */
2390 ,{ 11, 8 } /* UF_HOST */
2391 ,{ 20, 4 } /* UF_PORT */
2392 ,{ 24, 8 } /* UF_PATH */
2393 ,{ 33, 12 } /* UF_QUERY */
2394 ,{ 46, 4 } /* UF_FRAGMENT */
2395 ,{ 7, 3 } /* UF_USERINFO */
2396 }
2397 }
2398 ,.rv=0
2399 }
2400
2401 , {.name="double @"
2402 ,.url="http://a:b@@hostname:443/"
2403 ,.is_connect=0
2404 ,.rv=1
2405 }
2406
2407 , {.name="proxy empty host"
2408 ,.url="http://:443/"
2409 ,.is_connect=0
2410 ,.rv=1
2411 }
2412
2413 , {.name="proxy empty port"
2414 ,.url="http://hostname:/"
2415 ,.is_connect=0
2416 ,.rv=1
2417 }
2418
2419 , {.name="CONNECT with basic auth"
2420 ,.url="a:b@hostname:443"
2421 ,.is_connect=1
2422 ,.rv=1
2423 }
2424
2425 , {.name="CONNECT empty host"
2426 ,.url=":443"
2427 ,.is_connect=1
2428 ,.rv=1
2429 }
2430
2431 , {.name="CONNECT empty port"
2432 ,.url="hostname:"
2433 ,.is_connect=1
2434 ,.rv=1
2435 }
2436
2437 , {.name="CONNECT with extra bits"
2438 ,.url="hostname:443/"
2439 ,.is_connect=1
2440 ,.rv=1
2441 }
2442
2443 , {.name="space in URL"
2444 ,.url="/foo bar/"
2445 ,.rv=1 /* s_dead */
2446 }
2447
2448 , {.name="proxy basic auth with space url encoded"
2449 ,.url="http://a%20:b@host.com/"
2450 ,.is_connect=0
2451 ,.u=
2452 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2453 ,.port=0
2454 ,.field_data=
2455 {{ 0, 4 } /* UF_SCHEMA */
2456 ,{ 14, 8 } /* UF_HOST */
2457 ,{ 0, 0 } /* UF_PORT */
2458 ,{ 22, 1 } /* UF_PATH */
2459 ,{ 0, 0 } /* UF_QUERY */
2460 ,{ 0, 0 } /* UF_FRAGMENT */
2461 ,{ 7, 6 } /* UF_USERINFO */
2462 }
2463 }
2464 ,.rv=0
2465 }
2466
2467 , {.name="carriage return in URL"
2468 ,.url="/foo\rbar/"
2469 ,.rv=1 /* s_dead */
2470 }
2471
2472 , {.name="proxy double : in URL"
2473 ,.url="http://hostname::443/"
2474 ,.rv=1 /* s_dead */
2475 }
2476
2477 , {.name="proxy basic auth with double :"
2478 ,.url="http://a::b@host.com/"
2479 ,.is_connect=0
2480 ,.u=
2481 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2482 ,.port=0
2483 ,.field_data=
2484 {{ 0, 4 } /* UF_SCHEMA */
2485 ,{ 12, 8 } /* UF_HOST */
2486 ,{ 0, 0 } /* UF_PORT */
2487 ,{ 20, 1 } /* UF_PATH */
2488 ,{ 0, 0 } /* UF_QUERY */
2489 ,{ 0, 0 } /* UF_FRAGMENT */
2490 ,{ 7, 4 } /* UF_USERINFO */
2491 }
2492 }
2493 ,.rv=0
2494 }
2495
2496 , {.name="line feed in URL"
2497 ,.url="/foo\nbar/"
2498 ,.rv=1 /* s_dead */
2499 }
2500
2501 , {.name="proxy empty basic auth"
2502 ,.url="http://@hostname/fo"
2503 ,.u=
2504 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
2505 ,.port=0
2506 ,.field_data=
2507 {{ 0, 4 } /* UF_SCHEMA */
2508 ,{ 8, 8 } /* UF_HOST */
2509 ,{ 0, 0 } /* UF_PORT */
2510 ,{ 16, 3 } /* UF_PATH */
2511 ,{ 0, 0 } /* UF_QUERY */
2512 ,{ 0, 0 } /* UF_FRAGMENT */
2513 ,{ 0, 0 } /* UF_USERINFO */
2514 }
2515 }
2516 ,.rv=0
2517 }
2518 , {.name="proxy line feed in hostname"
2519 ,.url="http://host\name/fo"
2520 ,.rv=1 /* s_dead */
2521 }
2522
2523 , {.name="proxy % in hostname"
2524 ,.url="http://host%name/fo"
2525 ,.rv=1 /* s_dead */
2526 }
2527
2528 , {.name="proxy ; in hostname"
2529 ,.url="http://host;ame/fo"
2530 ,.rv=1 /* s_dead */
2531 }
2532
2533 , {.name="proxy basic auth with unreservedchars"
2534 ,.url="http://a!;-_!=+$@host.com/"
2535 ,.is_connect=0
2536 ,.u=
2537 {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2538 ,.port=0
2539 ,.field_data=
2540 {{ 0, 4 } /* UF_SCHEMA */
2541 ,{ 17, 8 } /* UF_HOST */
2542 ,{ 0, 0 } /* UF_PORT */
2543 ,{ 25, 1 } /* UF_PATH */
2544 ,{ 0, 0 } /* UF_QUERY */
2545 ,{ 0, 0 } /* UF_FRAGMENT */
2546 ,{ 7, 9 } /* UF_USERINFO */
2547 }
2548 }
2549 ,.rv=0
2550 }
2551
2552 , {.name="proxy only empty basic auth"
2553 ,.url="http://@/fo"
2554 ,.rv=1 /* s_dead */
2555 }
2556
2557 , {.name="proxy only basic auth"
2558 ,.url="http://toto@/fo"
2559 ,.rv=1 /* s_dead */
2560 }
2561
2562 , {.name="proxy emtpy hostname"
2563 ,.url="http:///fo"
2564 ,.rv=1 /* s_dead */
2565 }
2566
2567 , {.name="proxy = in URL"
2568 ,.url="http://host=ame/fo"
2569 ,.rv=1 /* s_dead */
2570 }
2571
2572 #if HTTP_PARSER_STRICT
2573
2574 , {.name="tab in URL"
2575 ,.url="/foo\tbar/"
2576 ,.rv=1 /* s_dead */
2577 }
2578
2579 , {.name="form feed in URL"
2580 ,.url="/foo\fbar/"
2581 ,.rv=1 /* s_dead */
2582 }
2583
2584 #else /* !HTTP_PARSER_STRICT */
2585
2586 , {.name="tab in URL"
2587 ,.url="/foo\tbar/"
2588 ,.u=
2589 {.field_set=(1 << UF_PATH)
2590 ,.field_data=
2591 {{ 0, 0 } /* UF_SCHEMA */
2592 ,{ 0, 0 } /* UF_HOST */
2593 ,{ 0, 0 } /* UF_PORT */
2594 ,{ 0, 9 } /* UF_PATH */
2595 ,{ 0, 0 } /* UF_QUERY */
2596 ,{ 0, 0 } /* UF_FRAGMENT */
2597 ,{ 0, 0 } /* UF_USERINFO */
2598 }
2599 }
2600 ,.rv=0
2601 }
2602
2603 , {.name="form feed in URL"
2604 ,.url="/foo\fbar/"
2605 ,.u=
2606 {.field_set=(1 << UF_PATH)
2607 ,.field_data=
2608 {{ 0, 0 } /* UF_SCHEMA */
2609 ,{ 0, 0 } /* UF_HOST */
2610 ,{ 0, 0 } /* UF_PORT */
2611 ,{ 0, 9 } /* UF_PATH */
2612 ,{ 0, 0 } /* UF_QUERY */
2613 ,{ 0, 0 } /* UF_FRAGMENT */
2614 ,{ 0, 0 } /* UF_USERINFO */
2615 }
2616 }
2617 ,.rv=0
2618 }
2619 #endif
2620 };
2621
2622 void
dump_url(const char * url,const struct http_parser_url * u)2623 dump_url (const char *url, const struct http_parser_url *u)
2624 {
2625 unsigned int i;
2626
2627 printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
2628 for (i = 0; i < UF_MAX; i++) {
2629 if ((u->field_set & (1 << i)) == 0) {
2630 printf("\tfield_data[%u]: unset\n", i);
2631 continue;
2632 }
2633
2634 printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
2635 i,
2636 u->field_data[i].off,
2637 u->field_data[i].len,
2638 u->field_data[i].len,
2639 url + u->field_data[i].off);
2640 }
2641 }
2642
2643 void
test_parse_url(void)2644 test_parse_url (void)
2645 {
2646 struct http_parser_url u;
2647 const struct url_test *test;
2648 unsigned int i;
2649 int rv;
2650
2651 for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
2652 test = &url_tests[i];
2653 memset(&u, 0, sizeof(u));
2654
2655 rv = http_parser_parse_url(test->url,
2656 strlen(test->url),
2657 test->is_connect,
2658 &u);
2659
2660 if (test->rv == 0) {
2661 if (rv != 0) {
2662 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2663 "unexpected rv %d ***\n\n", test->url, test->name, rv);
2664 abort();
2665 }
2666
2667 if (memcmp(&u, &test->u, sizeof(u)) != 0) {
2668 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
2669 test->url, test->name);
2670
2671 printf("target http_parser_url:\n");
2672 dump_url(test->url, &test->u);
2673 printf("result http_parser_url:\n");
2674 dump_url(test->url, &u);
2675
2676 abort();
2677 }
2678 } else {
2679 /* test->rv != 0 */
2680 if (rv == 0) {
2681 printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2682 "unexpected rv %d ***\n\n", test->url, test->name, rv);
2683 abort();
2684 }
2685 }
2686 }
2687 }
2688
2689 void
test_method_str(void)2690 test_method_str (void)
2691 {
2692 assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
2693 assert(0 == strcmp("<unknown>", http_method_str(1337)));
2694 }
2695
2696 void
test_message(const struct message * message)2697 test_message (const struct message *message)
2698 {
2699 size_t raw_len = strlen(message->raw);
2700 size_t msg1len;
2701 for (msg1len = 0; msg1len < raw_len; msg1len++) {
2702 parser_init(message->type);
2703
2704 size_t read;
2705 const char *msg1 = message->raw;
2706 const char *msg2 = msg1 + msg1len;
2707 size_t msg2len = raw_len - msg1len;
2708
2709 if (msg1len) {
2710 read = parse(msg1, msg1len);
2711
2712 if (message->upgrade && parser->upgrade) {
2713 messages[num_messages - 1].upgrade = msg1 + read;
2714 goto test;
2715 }
2716
2717 if (read != msg1len) {
2718 print_error(msg1, read);
2719 abort();
2720 }
2721 }
2722
2723
2724 read = parse(msg2, msg2len);
2725
2726 if (message->upgrade && parser->upgrade) {
2727 messages[num_messages - 1].upgrade = msg2 + read;
2728 goto test;
2729 }
2730
2731 if (read != msg2len) {
2732 print_error(msg2, read);
2733 abort();
2734 }
2735
2736 read = parse(NULL, 0);
2737
2738 if (read != 0) {
2739 print_error(message->raw, read);
2740 abort();
2741 }
2742
2743 test:
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
2756 void
test_message_count_body(const struct message * message)2757 test_message_count_body (const struct message *message)
2758 {
2759 parser_init(message->type);
2760
2761 size_t read;
2762 size_t l = strlen(message->raw);
2763 size_t i, toread;
2764 size_t chunk = 4024;
2765
2766 for (i = 0; i < l; i+= chunk) {
2767 toread = MIN(l-i, chunk);
2768 read = parse_count_body(message->raw + i, toread);
2769 if (read != toread) {
2770 print_error(message->raw, read);
2771 abort();
2772 }
2773 }
2774
2775
2776 read = parse_count_body(NULL, 0);
2777 if (read != 0) {
2778 print_error(message->raw, read);
2779 abort();
2780 }
2781
2782 if (num_messages != 1) {
2783 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2784 abort();
2785 }
2786
2787 if(!message_eq(0, message)) abort();
2788
2789 parser_free();
2790 }
2791
2792 void
test_simple(const char * buf,enum http_errno err_expected)2793 test_simple (const char *buf, enum http_errno err_expected)
2794 {
2795 parser_init(HTTP_REQUEST);
2796
2797 size_t parsed;
2798 int pass;
2799 enum http_errno err;
2800
2801 parsed = parse(buf, strlen(buf));
2802 pass = (parsed == strlen(buf));
2803 err = HTTP_PARSER_ERRNO(parser);
2804 parsed = parse(NULL, 0);
2805 pass &= (parsed == 0);
2806
2807 parser_free();
2808
2809 /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
2810 * long as the caller isn't expecting success.
2811 */
2812 #if HTTP_PARSER_STRICT
2813 if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
2814 #else
2815 if (err_expected != err) {
2816 #endif
2817 fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
2818 http_errno_name(err_expected), http_errno_name(err), buf);
2819 abort();
2820 }
2821 }
2822
2823 void
2824 test_header_overflow_error (int req)
2825 {
2826 http_parser parser;
2827 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2828 size_t parsed;
2829 const char *buf;
2830 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
2831 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
2832 assert(parsed == strlen(buf));
2833
2834 buf = "header-key: header-value\r\n";
2835 size_t buflen = strlen(buf);
2836
2837 int i;
2838 for (i = 0; i < 10000; i++) {
2839 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
2840 if (parsed != buflen) {
2841 //fprintf(stderr, "error found on iter %d\n", i);
2842 assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
2843 return;
2844 }
2845 }
2846
2847 fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
2848 abort();
2849 }
2850
2851 static void
2852 test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
2853 {
2854 http_parser parser;
2855 http_parser_init(&parser, HTTP_RESPONSE);
2856 http_parser_execute(&parser, &settings_null, buf, buflen);
2857
2858 if (expect_ok)
2859 assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
2860 else
2861 assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
2862 }
2863
2864 void
2865 test_header_content_length_overflow_error (void)
2866 {
2867 #define X(size) \
2868 "HTTP/1.1 200 OK\r\n" \
2869 "Content-Length: " #size "\r\n" \
2870 "\r\n"
2871 const char a[] = X(18446744073709551614); /* 2^64-2 */
2872 const char b[] = X(18446744073709551615); /* 2^64-1 */
2873 const char c[] = X(18446744073709551616); /* 2^64 */
2874 #undef X
2875 test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
2876 test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2877 test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2878 }
2879
2880 void
2881 test_chunk_content_length_overflow_error (void)
2882 {
2883 #define X(size) \
2884 "HTTP/1.1 200 OK\r\n" \
2885 "Transfer-Encoding: chunked\r\n" \
2886 "\r\n" \
2887 #size "\r\n" \
2888 "..."
2889 const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */
2890 const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
2891 const char c[] = X(10000000000000000); /* 2^64 */
2892 #undef X
2893 test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
2894 test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2895 test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2896 }
2897
2898 void
2899 test_no_overflow_long_body (int req, size_t length)
2900 {
2901 http_parser parser;
2902 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2903 size_t parsed;
2904 size_t i;
2905 char buf1[3000];
2906 size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
2907 req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
2908 parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
2909 if (parsed != buf1len)
2910 goto err;
2911
2912 for (i = 0; i < length; i++) {
2913 char foo = 'a';
2914 parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
2915 if (parsed != 1)
2916 goto err;
2917 }
2918
2919 parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
2920 if (parsed != buf1len) goto err;
2921 return;
2922
2923 err:
2924 fprintf(stderr,
2925 "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
2926 req ? "REQUEST" : "RESPONSE",
2927 (unsigned long)length);
2928 abort();
2929 }
2930
2931 void
2932 test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
2933 {
2934 int message_count = count_parsed_messages(3, r1, r2, r3);
2935
2936 char total[ strlen(r1->raw)
2937 + strlen(r2->raw)
2938 + strlen(r3->raw)
2939 + 1
2940 ];
2941 total[0] = '\0';
2942
2943 strcat(total, r1->raw);
2944 strcat(total, r2->raw);
2945 strcat(total, r3->raw);
2946
2947 parser_init(r1->type);
2948
2949 size_t read;
2950
2951 read = parse(total, strlen(total));
2952
2953 if (parser->upgrade) {
2954 upgrade_message_fix(total, read, 3, r1, r2, r3);
2955 goto test;
2956 }
2957
2958 if (read != strlen(total)) {
2959 print_error(total, read);
2960 abort();
2961 }
2962
2963 read = parse(NULL, 0);
2964
2965 if (read != 0) {
2966 print_error(total, read);
2967 abort();
2968 }
2969
2970 test:
2971
2972 if (message_count != num_messages) {
2973 fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
2974 abort();
2975 }
2976
2977 if (!message_eq(0, r1)) abort();
2978 if (message_count > 1 && !message_eq(1, r2)) abort();
2979 if (message_count > 2 && !message_eq(2, r3)) abort();
2980
2981 parser_free();
2982 }
2983
2984 /* SCAN through every possible breaking to make sure the
2985 * parser can handle getting the content in any chunks that
2986 * might come from the socket
2987 */
2988 void
2989 test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
2990 {
2991 char total[80*1024] = "\0";
2992 char buf1[80*1024] = "\0";
2993 char buf2[80*1024] = "\0";
2994 char buf3[80*1024] = "\0";
2995
2996 strcat(total, r1->raw);
2997 strcat(total, r2->raw);
2998 strcat(total, r3->raw);
2999
3000 size_t read;
3001
3002 int total_len = strlen(total);
3003
3004 int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
3005 int ops = 0 ;
3006
3007 size_t buf1_len, buf2_len, buf3_len;
3008 int message_count = count_parsed_messages(3, r1, r2, r3);
3009
3010 int i,j,type_both;
3011 for (type_both = 0; type_both < 2; type_both ++ ) {
3012 for (j = 2; j < total_len; j ++ ) {
3013 for (i = 1; i < j; i ++ ) {
3014
3015 if (ops % 1000 == 0) {
3016 printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
3017 fflush(stdout);
3018 }
3019 ops += 1;
3020
3021 parser_init(type_both ? HTTP_BOTH : r1->type);
3022
3023 buf1_len = i;
3024 strlncpy(buf1, sizeof(buf1), total, buf1_len);
3025 buf1[buf1_len] = 0;
3026
3027 buf2_len = j - i;
3028 strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
3029 buf2[buf2_len] = 0;
3030
3031 buf3_len = total_len - j;
3032 strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
3033 buf3[buf3_len] = 0;
3034
3035 read = parse(buf1, buf1_len);
3036
3037 if (parser->upgrade) goto test;
3038
3039 if (read != buf1_len) {
3040 print_error(buf1, read);
3041 goto error;
3042 }
3043
3044 read += parse(buf2, buf2_len);
3045
3046 if (parser->upgrade) goto test;
3047
3048 if (read != buf1_len + buf2_len) {
3049 print_error(buf2, read);
3050 goto error;
3051 }
3052
3053 read += parse(buf3, buf3_len);
3054
3055 if (parser->upgrade) goto test;
3056
3057 if (read != buf1_len + buf2_len + buf3_len) {
3058 print_error(buf3, read);
3059 goto error;
3060 }
3061
3062 parse(NULL, 0);
3063
3064 test:
3065 if (parser->upgrade) {
3066 upgrade_message_fix(total, read, 3, r1, r2, r3);
3067 }
3068
3069 if (message_count != num_messages) {
3070 fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3071 message_count, num_messages);
3072 goto error;
3073 }
3074
3075 if (!message_eq(0, r1)) {
3076 fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
3077 goto error;
3078 }
3079
3080 if (message_count > 1 && !message_eq(1, r2)) {
3081 fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
3082 goto error;
3083 }
3084
3085 if (message_count > 2 && !message_eq(2, r3)) {
3086 fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
3087 goto error;
3088 }
3089
3090 parser_free();
3091 }
3092 }
3093 }
3094 puts("\b\b\b\b100%");
3095 return;
3096
3097 error:
3098 fprintf(stderr, "i=%d j=%d\n", i, j);
3099 fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
3100 fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
3101 fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
3102 abort();
3103 }
3104
3105 // user required to free the result
3106 // string terminated by \0
3107 char *
3108 create_large_chunked_message (int body_size_in_kb, const char* headers)
3109 {
3110 int i;
3111 size_t wrote = 0;
3112 size_t headers_len = strlen(headers);
3113 size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
3114 char * buf = malloc(bufsize);
3115
3116 memcpy(buf, headers, headers_len);
3117 wrote += headers_len;
3118
3119 for (i = 0; i < body_size_in_kb; i++) {
3120 // write 1kb chunk into the body.
3121 memcpy(buf + wrote, "400\r\n", 5);
3122 wrote += 5;
3123 memset(buf + wrote, 'C', 1024);
3124 wrote += 1024;
3125 strcpy(buf + wrote, "\r\n");
3126 wrote += 2;
3127 }
3128
3129 memcpy(buf + wrote, "0\r\n\r\n", 6);
3130 wrote += 6;
3131 assert(wrote == bufsize);
3132
3133 return buf;
3134 }
3135
3136 void
3137 test_status_complete (void)
3138 {
3139 parser_init(HTTP_RESPONSE);
3140 parser->data = 0;
3141 http_parser_settings settings = settings_null;
3142 settings.on_status_complete = status_complete_cb;
3143
3144 char *response = "don't mind me, just a simple response";
3145 http_parser_execute(parser, &settings, response, strlen(response));
3146 assert(parser->data == (void*)0); // the status_complete callback was never called
3147 assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line
3148 }
3149
3150 /* Verify that we can pause parsing at any of the bytes in the
3151 * message and still get the result that we're expecting. */
3152 void
3153 test_message_pause (const struct message *msg)
3154 {
3155 char *buf = (char*) msg->raw;
3156 size_t buflen = strlen(msg->raw);
3157 size_t nread;
3158
3159 parser_init(msg->type);
3160
3161 do {
3162 nread = parse_pause(buf, buflen);
3163
3164 // We can only set the upgrade buffer once we've gotten our message
3165 // completion callback.
3166 if (messages[0].message_complete_cb_called &&
3167 msg->upgrade &&
3168 parser->upgrade) {
3169 messages[0].upgrade = buf + nread;
3170 goto test;
3171 }
3172
3173 if (nread < buflen) {
3174
3175 // Not much do to if we failed a strict-mode check
3176 if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
3177 parser_free();
3178 return;
3179 }
3180
3181 assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
3182 }
3183
3184 buf += nread;
3185 buflen -= nread;
3186 http_parser_pause(parser, 0);
3187 } while (buflen > 0);
3188
3189 nread = parse_pause(NULL, 0);
3190 assert (nread == 0);
3191
3192 test:
3193 if (num_messages != 1) {
3194 printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
3195 abort();
3196 }
3197
3198 if(!message_eq(0, msg)) abort();
3199
3200 parser_free();
3201 }
3202
3203 int
3204 main (void)
3205 {
3206 parser = NULL;
3207 int i, j, k;
3208 int request_count;
3209 int response_count;
3210 unsigned long version;
3211 unsigned major;
3212 unsigned minor;
3213 unsigned patch;
3214
3215 version = http_parser_version();
3216 major = (version >> 16) & 255;
3217 minor = (version >> 8) & 255;
3218 patch = version & 255;
3219 printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version);
3220
3221 printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
3222
3223 for (request_count = 0; requests[request_count].name; request_count++);
3224 for (response_count = 0; responses[response_count].name; response_count++);
3225
3226 //// API
3227 test_preserve_data();
3228 test_parse_url();
3229 test_method_str();
3230
3231 //// OVERFLOW CONDITIONS
3232
3233 test_header_overflow_error(HTTP_REQUEST);
3234 test_no_overflow_long_body(HTTP_REQUEST, 1000);
3235 test_no_overflow_long_body(HTTP_REQUEST, 100000);
3236
3237 test_header_overflow_error(HTTP_RESPONSE);
3238 test_no_overflow_long_body(HTTP_RESPONSE, 1000);
3239 test_no_overflow_long_body(HTTP_RESPONSE, 100000);
3240
3241 test_header_content_length_overflow_error();
3242 test_chunk_content_length_overflow_error();
3243
3244 //// RESPONSES
3245
3246 for (i = 0; i < response_count; i++) {
3247 test_message(&responses[i]);
3248 }
3249
3250 for (i = 0; i < response_count; i++) {
3251 test_message_pause(&responses[i]);
3252 }
3253
3254 for (i = 0; i < response_count; i++) {
3255 if (!responses[i].should_keep_alive) continue;
3256 for (j = 0; j < response_count; j++) {
3257 if (!responses[j].should_keep_alive) continue;
3258 for (k = 0; k < response_count; k++) {
3259 test_multiple3(&responses[i], &responses[j], &responses[k]);
3260 }
3261 }
3262 }
3263
3264 test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
3265 test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
3266
3267 // test very large chunked response
3268 {
3269 char * msg = create_large_chunked_message(31337,
3270 "HTTP/1.0 200 OK\r\n"
3271 "Transfer-Encoding: chunked\r\n"
3272 "Content-Type: text/plain\r\n"
3273 "\r\n");
3274 struct message large_chunked =
3275 {.name= "large chunked"
3276 ,.type= HTTP_RESPONSE
3277 ,.raw= msg
3278 ,.should_keep_alive= FALSE
3279 ,.message_complete_on_eof= FALSE
3280 ,.http_major= 1
3281 ,.http_minor= 0
3282 ,.status_code= 200
3283 ,.num_headers= 2
3284 ,.headers=
3285 { { "Transfer-Encoding", "chunked" }
3286 , { "Content-Type", "text/plain" }
3287 }
3288 ,.body_size= 31337*1024
3289 };
3290 test_message_count_body(&large_chunked);
3291 free(msg);
3292 }
3293
3294
3295
3296 printf("response scan 1/2 ");
3297 test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
3298 , &responses[NO_BODY_HTTP10_KA_204]
3299 , &responses[NO_REASON_PHRASE]
3300 );
3301
3302 printf("response scan 2/2 ");
3303 test_scan( &responses[BONJOUR_MADAME_FR]
3304 , &responses[UNDERSTORE_HEADER_KEY]
3305 , &responses[NO_CARRIAGE_RET]
3306 );
3307
3308 puts("responses okay");
3309
3310
3311 /// REQUESTS
3312
3313 test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
3314
3315 // Well-formed but incomplete
3316 test_simple("GET / HTTP/1.1\r\n"
3317 "Content-Type: text/plain\r\n"
3318 "Content-Length: 6\r\n"
3319 "\r\n"
3320 "fooba",
3321 HPE_OK);
3322
3323 static const char *all_methods[] = {
3324 "DELETE",
3325 "GET",
3326 "HEAD",
3327 "POST",
3328 "PUT",
3329 //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
3330 "OPTIONS",
3331 "TRACE",
3332 "COPY",
3333 "LOCK",
3334 "MKCOL",
3335 "MOVE",
3336 "PROPFIND",
3337 "PROPPATCH",
3338 "UNLOCK",
3339 "REPORT",
3340 "MKACTIVITY",
3341 "CHECKOUT",
3342 "MERGE",
3343 "M-SEARCH",
3344 "NOTIFY",
3345 "SUBSCRIBE",
3346 "UNSUBSCRIBE",
3347 "PATCH",
3348 0 };
3349 const char **this_method;
3350 for (this_method = all_methods; *this_method; this_method++) {
3351 char buf[200];
3352 sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3353 test_simple(buf, HPE_OK);
3354 }
3355
3356 static const char *bad_methods[] = {
3357 "ASDF",
3358 "C******",
3359 "COLA",
3360 "GEM",
3361 "GETA",
3362 "M****",
3363 "MKCOLA",
3364 "PROPPATCHA",
3365 "PUN",
3366 "PX",
3367 "SA",
3368 "hello world",
3369 0 };
3370 for (this_method = bad_methods; *this_method; this_method++) {
3371 char buf[200];
3372 sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3373 test_simple(buf, HPE_INVALID_METHOD);
3374 }
3375
3376 const char *dumbfuck2 =
3377 "GET / HTTP/1.1\r\n"
3378 "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
3379 "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
3380 "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
3381 "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
3382 "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
3383 "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
3384 "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
3385 "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
3386 "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
3387 "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
3388 "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
3389 "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
3390 "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
3391 "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
3392 "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
3393 "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
3394 "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
3395 "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
3396 "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
3397 "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
3398 "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
3399 "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
3400 "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
3401 "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
3402 "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
3403 "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
3404 "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
3405 "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
3406 "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
3407 "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
3408 "\tRA==\r\n"
3409 "\t-----END CERTIFICATE-----\r\n"
3410 "\r\n";
3411 test_simple(dumbfuck2, HPE_OK);
3412
3413 #if 0
3414 // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
3415 // until EOF.
3416 //
3417 // no content-length
3418 // error if there is a body without content length
3419 const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
3420 "Accept: */*\r\n"
3421 "\r\n"
3422 "HELLO";
3423 test_simple(bad_get_no_headers_no_body, 0);
3424 #endif
3425 /* TODO sending junk and large headers gets rejected */
3426
3427
3428 /* check to make sure our predefined requests are okay */
3429 for (i = 0; requests[i].name; i++) {
3430 test_message(&requests[i]);
3431 }
3432
3433 for (i = 0; i < request_count; i++) {
3434 test_message_pause(&requests[i]);
3435 }
3436
3437 for (i = 0; i < request_count; i++) {
3438 if (!requests[i].should_keep_alive) continue;
3439 for (j = 0; j < request_count; j++) {
3440 if (!requests[j].should_keep_alive) continue;
3441 for (k = 0; k < request_count; k++) {
3442 test_multiple3(&requests[i], &requests[j], &requests[k]);
3443 }
3444 }
3445 }
3446
3447 printf("request scan 1/4 ");
3448 test_scan( &requests[GET_NO_HEADERS_NO_BODY]
3449 , &requests[GET_ONE_HEADER_NO_BODY]
3450 , &requests[GET_NO_HEADERS_NO_BODY]
3451 );
3452
3453 printf("request scan 2/4 ");
3454 test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
3455 , &requests[POST_IDENTITY_BODY_WORLD]
3456 , &requests[GET_FUNKY_CONTENT_LENGTH]
3457 );
3458
3459 printf("request scan 3/4 ");
3460 test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
3461 , &requests[CHUNKED_W_TRAILING_HEADERS]
3462 , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
3463 );
3464
3465 printf("request scan 4/4 ");
3466 test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
3467 , &requests[PREFIX_NEWLINE_GET ]
3468 , &requests[CONNECT_REQUEST]
3469 );
3470
3471 test_status_complete();
3472
3473 puts("requests okay");
3474
3475 return 0;
3476 }
3477