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