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