http_parser.rb 0.5.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/linux.yml +23 -0
  3. data/.github/workflows/windows.yml +23 -0
  4. data/.gitignore +5 -4
  5. data/.gitmodules +4 -4
  6. data/Gemfile +1 -1
  7. data/README.md +52 -47
  8. data/Rakefile +1 -0
  9. data/bench/standalone.rb +23 -0
  10. data/bench/thin.rb +1 -0
  11. data/ext/ruby_http_parser/extconf.rb +1 -1
  12. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +139 -83
  13. data/ext/ruby_http_parser/ruby_http_parser.c +40 -41
  14. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
  16. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
  17. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1202 -671
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +172 -51
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +8 -3
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +35 -102
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +775 -682
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +8 -4
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +70 -20
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
  42. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1637 -280
  43. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
  44. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +68 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
  46. data/ext/ruby_http_parser/vendor/http-parser/README.md +113 -38
  47. data/ext/ruby_http_parser/vendor/http-parser/bench.c +128 -0
  48. data/ext/ruby_http_parser/vendor/http-parser/contrib/parsertrace.c +157 -0
  49. data/ext/ruby_http_parser/vendor/http-parser/contrib/url_parser.c +47 -0
  50. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1576 -780
  51. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +111 -0
  52. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +308 -58
  53. data/ext/ruby_http_parser/vendor/http-parser/test.c +2964 -460
  54. data/http_parser.rb.gemspec +14 -7
  55. data/spec/parser_spec.rb +196 -102
  56. data/spec/support/requests.json +236 -24
  57. data/spec/support/responses.json +202 -36
  58. data/tasks/compile.rake +2 -2
  59. data/tasks/fixtures.rake +8 -2
  60. data/tasks/spec.rake +1 -1
  61. metadata +141 -134
  62. data/Gemfile.lock +0 -32
  63. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  64. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  65. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
  66. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
  67. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +0 -4
@@ -26,16 +26,23 @@
26
26
  #include <string.h>
27
27
  #include <stdarg.h>
28
28
 
29
+ #if defined(__APPLE__)
30
+ # undef strlncpy
31
+ #endif /* defined(__APPLE__) */
32
+
29
33
  #undef TRUE
30
34
  #define TRUE 1
31
35
  #undef FALSE
32
36
  #define FALSE 0
33
37
 
34
38
  #define MAX_HEADERS 13
35
- #define MAX_ELEMENT_SIZE 500
39
+ #define MAX_ELEMENT_SIZE 2048
40
+ #define MAX_CHUNKS 16
36
41
 
37
42
  #define MIN(a,b) ((a) < (b) ? (a) : (b))
38
43
 
44
+ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
45
+
39
46
  static http_parser *parser;
40
47
 
41
48
  struct message {
@@ -44,18 +51,26 @@ struct message {
44
51
  enum http_parser_type type;
45
52
  enum http_method method;
46
53
  int status_code;
54
+ char response_status[MAX_ELEMENT_SIZE];
47
55
  char request_path[MAX_ELEMENT_SIZE];
48
56
  char request_url[MAX_ELEMENT_SIZE];
49
57
  char fragment[MAX_ELEMENT_SIZE];
50
58
  char query_string[MAX_ELEMENT_SIZE];
51
59
  char body[MAX_ELEMENT_SIZE];
52
60
  size_t body_size;
61
+ const char *host;
62
+ const char *userinfo;
63
+ uint16_t port;
53
64
  int num_headers;
54
65
  enum { NONE=0, FIELD, VALUE } last_header_element;
55
66
  char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
56
67
  int should_keep_alive;
57
68
 
58
- int upgrade;
69
+ int num_chunks;
70
+ int num_chunks_complete;
71
+ int chunk_lengths[MAX_CHUNKS];
72
+
73
+ const char *upgrade; // upgraded body
59
74
 
60
75
  unsigned short http_major;
61
76
  unsigned short http_minor;
@@ -63,13 +78,16 @@ struct message {
63
78
  int message_begin_cb_called;
64
79
  int headers_complete_cb_called;
65
80
  int message_complete_cb_called;
81
+ int status_cb_called;
66
82
  int message_complete_on_eof;
83
+ int body_is_final;
67
84
  };
68
85
 
69
86
  static int currently_parsing_eof;
70
87
 
71
88
  static struct message messages[5];
72
89
  static int num_messages;
90
+ static http_parser_settings *current_pause_parser;
73
91
 
74
92
  /* * R E Q U E S T S * */
75
93
  const struct message requests[] =
@@ -289,6 +307,8 @@ const struct message requests[] =
289
307
  { { "Transfer-Encoding" , "chunked" }
290
308
  }
291
309
  ,.body= "all your base are belong to us"
310
+ ,.num_chunks_complete= 2
311
+ ,.chunk_lengths= { 0x1e }
292
312
  }
293
313
 
294
314
  #define TWO_CHUNKS_MULT_ZERO_END 9
@@ -315,6 +335,8 @@ const struct message requests[] =
315
335
  { { "Transfer-Encoding", "chunked" }
316
336
  }
317
337
  ,.body= "hello world"
338
+ ,.num_chunks_complete= 3
339
+ ,.chunk_lengths= { 5, 6 }
318
340
  }
319
341
 
320
342
  #define CHUNKED_W_TRAILING_HEADERS 10
@@ -345,6 +367,8 @@ const struct message requests[] =
345
367
  , { "Content-Type", "text/plain" }
346
368
  }
347
369
  ,.body= "hello world"
370
+ ,.num_chunks_complete= 3
371
+ ,.chunk_lengths= { 5, 6 }
348
372
  }
349
373
 
350
374
  #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
@@ -371,6 +395,8 @@ const struct message requests[] =
371
395
  { { "Transfer-Encoding", "chunked" }
372
396
  }
373
397
  ,.body= "hello world"
398
+ ,.num_chunks_complete= 3
399
+ ,.chunk_lengths= { 5, 6 }
374
400
  }
375
401
 
376
402
  #define WITH_QUOTES 12
@@ -473,6 +499,7 @@ const struct message requests[] =
473
499
  "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
474
500
  "Origin: http://example.com\r\n"
475
501
  "\r\n"
502
+ "Hot diggity dogg"
476
503
  ,.should_keep_alive= TRUE
477
504
  ,.message_complete_on_eof= FALSE
478
505
  ,.http_major= 1
@@ -483,7 +510,7 @@ const struct message requests[] =
483
510
  ,.request_path= "/demo"
484
511
  ,.request_url= "/demo"
485
512
  ,.num_headers= 7
486
- ,.upgrade=1
513
+ ,.upgrade="Hot diggity dogg"
487
514
  ,.headers= { { "Host", "example.com" }
488
515
  , { "Connection", "Upgrade" }
489
516
  , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
@@ -498,10 +525,12 @@ const struct message requests[] =
498
525
  #define CONNECT_REQUEST 17
499
526
  , {.name = "connect request"
500
527
  ,.type= HTTP_REQUEST
501
- ,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n"
528
+ ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
502
529
  "User-agent: Mozilla/1.1N\r\n"
503
530
  "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
504
531
  "\r\n"
532
+ "some data\r\n"
533
+ "and yet even more data"
505
534
  ,.should_keep_alive= FALSE
506
535
  ,.message_complete_on_eof= FALSE
507
536
  ,.http_major= 1
@@ -510,9 +539,9 @@ const struct message requests[] =
510
539
  ,.query_string= ""
511
540
  ,.fragment= ""
512
541
  ,.request_path= ""
513
- ,.request_url= "home0.netscape.com:443"
542
+ ,.request_url= "0-home0.netscape.com:443"
514
543
  ,.num_headers= 2
515
- ,.upgrade=1
544
+ ,.upgrade="some data\r\nand yet even more data"
516
545
  ,.headers= { { "User-agent", "Mozilla/1.1N" }
517
546
  , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
518
547
  }
@@ -582,27 +611,44 @@ const struct message requests[] =
582
611
  ,.body= ""
583
612
  }
584
613
 
585
- #define UTF8_PATH_REQ 21
586
- , {.name= "utf-8 path request"
614
+ #define LINE_FOLDING_IN_HEADER 21
615
+ , {.name= "line folding in header value"
587
616
  ,.type= HTTP_REQUEST
588
- ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
589
- "Host: github.com\r\n"
617
+ ,.raw= "GET / HTTP/1.1\r\n"
618
+ "Line1: abc\r\n"
619
+ "\tdef\r\n"
620
+ " ghi\r\n"
621
+ "\t\tjkl\r\n"
622
+ " mno \r\n"
623
+ "\t \tqrs\r\n"
624
+ "Line2: \t line2\t\r\n"
625
+ "Line3:\r\n"
626
+ " line3\r\n"
627
+ "Line4: \r\n"
628
+ " \r\n"
629
+ "Connection:\r\n"
630
+ " close\r\n"
590
631
  "\r\n"
591
- ,.should_keep_alive= TRUE
632
+ ,.should_keep_alive= FALSE
592
633
  ,.message_complete_on_eof= FALSE
593
634
  ,.http_major= 1
594
635
  ,.http_minor= 1
595
636
  ,.method= HTTP_GET
596
- ,.query_string= "q=1"
597
- ,.fragment= "narf"
598
- ,.request_path= "/δ¶/δt/pope"
599
- ,.request_url= "/δ¶/δt/pope?q=1#narf"
600
- ,.num_headers= 1
601
- ,.headers= { {"Host", "github.com" }
637
+ ,.query_string= ""
638
+ ,.fragment= ""
639
+ ,.request_path= "/"
640
+ ,.request_url= "/"
641
+ ,.num_headers= 5
642
+ ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
643
+ , { "Line2", "line2\t" }
644
+ , { "Line3", "line3" }
645
+ , { "Line4", "" }
646
+ , { "Connection", "close" },
602
647
  }
603
648
  ,.body= ""
604
649
  }
605
650
 
651
+
606
652
  #define QUERY_TERMINATED_HOST 22
607
653
  , {.name= "host terminated by a query string"
608
654
  ,.type= HTTP_REQUEST
@@ -617,6 +663,7 @@ const struct message requests[] =
617
663
  ,.fragment= ""
618
664
  ,.request_path= ""
619
665
  ,.request_url= "http://hypnotoad.org?hail=all"
666
+ ,.host= "hypnotoad.org"
620
667
  ,.num_headers= 0
621
668
  ,.headers= { }
622
669
  ,.body= ""
@@ -636,6 +683,8 @@ const struct message requests[] =
636
683
  ,.fragment= ""
637
684
  ,.request_path= ""
638
685
  ,.request_url= "http://hypnotoad.org:1234?hail=all"
686
+ ,.host= "hypnotoad.org"
687
+ ,.port= 1234
639
688
  ,.num_headers= 0
640
689
  ,.headers= { }
641
690
  ,.body= ""
@@ -655,405 +704,1317 @@ const struct message requests[] =
655
704
  ,.fragment= ""
656
705
  ,.request_path= ""
657
706
  ,.request_url= "http://hypnotoad.org:1234"
707
+ ,.host= "hypnotoad.org"
708
+ ,.port= 1234
658
709
  ,.num_headers= 0
659
710
  ,.headers= { }
660
711
  ,.body= ""
661
712
  }
662
713
 
663
- , {.name= NULL } /* sentinel */
664
- };
665
-
666
- /* * R E S P O N S E S * */
667
- const struct message responses[] =
668
- #define GOOGLE_301 0
669
- { {.name= "google 301"
670
- ,.type= HTTP_RESPONSE
671
- ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
672
- "Location: http://www.google.com/\r\n"
673
- "Content-Type: text/html; charset=UTF-8\r\n"
674
- "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
675
- "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
676
- "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
677
- "Cache-Control: public, max-age=2592000\r\n"
678
- "Server: gws\r\n"
679
- "Content-Length: 219 \r\n"
714
+ #define PATCH_REQ 25
715
+ , {.name = "PATCH request"
716
+ ,.type= HTTP_REQUEST
717
+ ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
718
+ "Host: www.example.com\r\n"
719
+ "Content-Type: application/example\r\n"
720
+ "If-Match: \"e0023aa4e\"\r\n"
721
+ "Content-Length: 10\r\n"
680
722
  "\r\n"
681
- "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
682
- "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
683
- "<H1>301 Moved</H1>\n"
684
- "The document has moved\n"
685
- "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
686
- "</BODY></HTML>\r\n"
723
+ "cccccccccc"
687
724
  ,.should_keep_alive= TRUE
688
725
  ,.message_complete_on_eof= FALSE
689
726
  ,.http_major= 1
690
727
  ,.http_minor= 1
691
- ,.status_code= 301
692
- ,.num_headers= 8
693
- ,.headers=
694
- { { "Location", "http://www.google.com/" }
695
- , { "Content-Type", "text/html; charset=UTF-8" }
696
- , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
697
- , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
698
- , { "X-$PrototypeBI-Version", "1.6.0.3" }
699
- , { "Cache-Control", "public, max-age=2592000" }
700
- , { "Server", "gws" }
701
- , { "Content-Length", "219 " }
702
- }
703
- ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
704
- "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
705
- "<H1>301 Moved</H1>\n"
706
- "The document has moved\n"
707
- "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
708
- "</BODY></HTML>\r\n"
728
+ ,.method= HTTP_PATCH
729
+ ,.query_string= ""
730
+ ,.fragment= ""
731
+ ,.request_path= "/file.txt"
732
+ ,.request_url= "/file.txt"
733
+ ,.num_headers= 4
734
+ ,.headers= { { "Host", "www.example.com" }
735
+ , { "Content-Type", "application/example" }
736
+ , { "If-Match", "\"e0023aa4e\"" }
737
+ , { "Content-Length", "10" }
738
+ }
739
+ ,.body= "cccccccccc"
709
740
  }
710
741
 
711
- #define NO_CONTENT_LENGTH_RESPONSE 1
712
- /* The client should wait for the server's EOF. That is, when content-length
713
- * is not specified, and "Connection: close", the end of body is specified
714
- * by the EOF.
715
- * Compare with APACHEBENCH_GET
716
- */
717
- , {.name= "no content-length response"
718
- ,.type= HTTP_RESPONSE
719
- ,.raw= "HTTP/1.1 200 OK\r\n"
720
- "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
721
- "Server: Apache\r\n"
722
- "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
723
- "Content-Type: text/xml; charset=utf-8\r\n"
724
- "Connection: close\r\n"
742
+ #define CONNECT_CAPS_REQUEST 26
743
+ , {.name = "connect caps request"
744
+ ,.type= HTTP_REQUEST
745
+ ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
746
+ "User-agent: Mozilla/1.1N\r\n"
747
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
725
748
  "\r\n"
726
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
727
- "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
728
- " <SOAP-ENV:Body>\n"
729
- " <SOAP-ENV:Fault>\n"
730
- " <faultcode>SOAP-ENV:Client</faultcode>\n"
731
- " <faultstring>Client Error</faultstring>\n"
732
- " </SOAP-ENV:Fault>\n"
733
- " </SOAP-ENV:Body>\n"
734
- "</SOAP-ENV:Envelope>"
735
749
  ,.should_keep_alive= FALSE
736
- ,.message_complete_on_eof= TRUE
750
+ ,.message_complete_on_eof= FALSE
737
751
  ,.http_major= 1
738
- ,.http_minor= 1
739
- ,.status_code= 200
740
- ,.num_headers= 5
741
- ,.headers=
742
- { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
743
- , { "Server", "Apache" }
744
- , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
745
- , { "Content-Type", "text/xml; charset=utf-8" }
746
- , { "Connection", "close" }
747
- }
748
- ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
749
- "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
750
- " <SOAP-ENV:Body>\n"
751
- " <SOAP-ENV:Fault>\n"
752
- " <faultcode>SOAP-ENV:Client</faultcode>\n"
753
- " <faultstring>Client Error</faultstring>\n"
754
- " </SOAP-ENV:Fault>\n"
755
- " </SOAP-ENV:Body>\n"
756
- "</SOAP-ENV:Envelope>"
752
+ ,.http_minor= 0
753
+ ,.method= HTTP_CONNECT
754
+ ,.query_string= ""
755
+ ,.fragment= ""
756
+ ,.request_path= ""
757
+ ,.request_url= "HOME0.NETSCAPE.COM:443"
758
+ ,.num_headers= 2
759
+ ,.upgrade=""
760
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
761
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
762
+ }
763
+ ,.body= ""
757
764
  }
758
765
 
759
- #define NO_HEADERS_NO_BODY_404 2
760
- , {.name= "404 no headers no body"
761
- ,.type= HTTP_RESPONSE
762
- ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
766
+ #if !HTTP_PARSER_STRICT
767
+ #define UTF8_PATH_REQ 27
768
+ , {.name= "utf-8 path request"
769
+ ,.type= HTTP_REQUEST
770
+ ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
771
+ "Host: github.com\r\n"
772
+ "\r\n"
763
773
  ,.should_keep_alive= TRUE
764
774
  ,.message_complete_on_eof= FALSE
765
775
  ,.http_major= 1
766
776
  ,.http_minor= 1
767
- ,.status_code= 404
768
- ,.num_headers= 0
769
- ,.headers= {}
770
- ,.body_size= 0
777
+ ,.method= HTTP_GET
778
+ ,.query_string= "q=1"
779
+ ,.fragment= "narf"
780
+ ,.request_path= "/δ¶/δt/pope"
781
+ ,.request_url= "/δ¶/δt/pope?q=1#narf"
782
+ ,.num_headers= 1
783
+ ,.headers= { {"Host", "github.com" }
784
+ }
771
785
  ,.body= ""
772
786
  }
773
787
 
774
- #define NO_REASON_PHRASE 3
775
- , {.name= "301 no response phrase"
776
- ,.type= HTTP_RESPONSE
777
- ,.raw= "HTTP/1.1 301\r\n\r\n"
778
- ,.should_keep_alive = TRUE
788
+ #define HOSTNAME_UNDERSCORE 28
789
+ , {.name = "hostname underscore"
790
+ ,.type= HTTP_REQUEST
791
+ ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
792
+ "User-agent: Mozilla/1.1N\r\n"
793
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
794
+ "\r\n"
795
+ ,.should_keep_alive= FALSE
779
796
  ,.message_complete_on_eof= FALSE
780
797
  ,.http_major= 1
781
- ,.http_minor= 1
782
- ,.status_code= 301
783
- ,.num_headers= 0
784
- ,.headers= {}
798
+ ,.http_minor= 0
799
+ ,.method= HTTP_CONNECT
800
+ ,.query_string= ""
801
+ ,.fragment= ""
802
+ ,.request_path= ""
803
+ ,.request_url= "home_0.netscape.com:443"
804
+ ,.num_headers= 2
805
+ ,.upgrade=""
806
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
807
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
808
+ }
785
809
  ,.body= ""
786
810
  }
787
-
788
- #define TRAILING_SPACE_ON_CHUNKED_BODY 4
789
- , {.name="200 trailing space on chunked body"
790
- ,.type= HTTP_RESPONSE
791
- ,.raw= "HTTP/1.1 200 OK\r\n"
792
- "Content-Type: text/plain\r\n"
793
- "Transfer-Encoding: chunked\r\n"
794
- "\r\n"
795
- "25 \r\n"
796
- "This is the data in the first chunk\r\n"
797
- "\r\n"
798
- "1C\r\n"
799
- "and this is the second one\r\n"
800
- "\r\n"
801
- "0 \r\n"
811
+ #endif /* !HTTP_PARSER_STRICT */
812
+
813
+ /* see https://github.com/ry/http-parser/issues/47 */
814
+ #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
815
+ , {.name = "eat CRLF between requests, no \"Connection: close\" header"
816
+ ,.raw= "POST / HTTP/1.1\r\n"
817
+ "Host: www.example.com\r\n"
818
+ "Content-Type: application/x-www-form-urlencoded\r\n"
819
+ "Content-Length: 4\r\n"
802
820
  "\r\n"
821
+ "q=42\r\n" /* note the trailing CRLF */
803
822
  ,.should_keep_alive= TRUE
804
823
  ,.message_complete_on_eof= FALSE
805
824
  ,.http_major= 1
806
825
  ,.http_minor= 1
807
- ,.status_code= 200
808
- ,.num_headers= 2
809
- ,.headers=
810
- { {"Content-Type", "text/plain" }
811
- , {"Transfer-Encoding", "chunked" }
812
- }
813
- ,.body_size = 37+28
814
- ,.body =
815
- "This is the data in the first chunk\r\n"
816
- "and this is the second one\r\n"
817
-
818
- }
819
-
820
- #define NO_CARRIAGE_RET 5
821
- , {.name="no carriage ret"
822
- ,.type= HTTP_RESPONSE
823
- ,.raw= "HTTP/1.1 200 OK\n"
824
- "Content-Type: text/html; charset=utf-8\n"
825
- "Connection: close\n"
826
- "\n"
827
- "these headers are from http://news.ycombinator.com/"
828
- ,.should_keep_alive= FALSE
829
- ,.message_complete_on_eof= TRUE
830
- ,.http_major= 1
831
- ,.http_minor= 1
832
- ,.status_code= 200
833
- ,.num_headers= 2
834
- ,.headers=
835
- { {"Content-Type", "text/html; charset=utf-8" }
836
- , {"Connection", "close" }
837
- }
838
- ,.body= "these headers are from http://news.ycombinator.com/"
826
+ ,.method= HTTP_POST
827
+ ,.query_string= ""
828
+ ,.fragment= ""
829
+ ,.request_path= "/"
830
+ ,.request_url= "/"
831
+ ,.num_headers= 3
832
+ ,.upgrade= 0
833
+ ,.headers= { { "Host", "www.example.com" }
834
+ , { "Content-Type", "application/x-www-form-urlencoded" }
835
+ , { "Content-Length", "4" }
836
+ }
837
+ ,.body= "q=42"
839
838
  }
840
839
 
841
- #define PROXY_CONNECTION 6
842
- , {.name="proxy connection"
843
- ,.type= HTTP_RESPONSE
844
- ,.raw= "HTTP/1.1 200 OK\r\n"
845
- "Content-Type: text/html; charset=UTF-8\r\n"
846
- "Content-Length: 11\r\n"
847
- "Proxy-Connection: close\r\n"
848
- "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
840
+ /* see https://github.com/ry/http-parser/issues/47 */
841
+ #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
842
+ , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
843
+ ,.raw= "POST / HTTP/1.1\r\n"
844
+ "Host: www.example.com\r\n"
845
+ "Content-Type: application/x-www-form-urlencoded\r\n"
846
+ "Content-Length: 4\r\n"
847
+ "Connection: close\r\n"
849
848
  "\r\n"
850
- "hello world"
849
+ "q=42\r\n" /* note the trailing CRLF */
851
850
  ,.should_keep_alive= FALSE
852
- ,.message_complete_on_eof= FALSE
851
+ ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
853
852
  ,.http_major= 1
854
853
  ,.http_minor= 1
855
- ,.status_code= 200
854
+ ,.method= HTTP_POST
855
+ ,.query_string= ""
856
+ ,.fragment= ""
857
+ ,.request_path= "/"
858
+ ,.request_url= "/"
856
859
  ,.num_headers= 4
857
- ,.headers=
858
- { {"Content-Type", "text/html; charset=UTF-8" }
859
- , {"Content-Length", "11" }
860
- , {"Proxy-Connection", "close" }
861
- , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
862
- }
863
- ,.body= "hello world"
860
+ ,.upgrade= 0
861
+ ,.headers= { { "Host", "www.example.com" }
862
+ , { "Content-Type", "application/x-www-form-urlencoded" }
863
+ , { "Content-Length", "4" }
864
+ , { "Connection", "close" }
865
+ }
866
+ ,.body= "q=42"
864
867
  }
865
868
 
866
- #define UNDERSTORE_HEADER_KEY 7
867
- // shown by
868
- // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
869
- , {.name="underscore header key"
870
- ,.type= HTTP_RESPONSE
871
- ,.raw= "HTTP/1.1 200 OK\r\n"
872
- "Server: DCLK-AdSvr\r\n"
873
- "Content-Type: text/xml\r\n"
874
- "Content-Length: 0\r\n"
875
- "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
869
+ #define PURGE_REQ 31
870
+ , {.name = "PURGE request"
871
+ ,.type= HTTP_REQUEST
872
+ ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
873
+ "Host: www.example.com\r\n"
874
+ "\r\n"
876
875
  ,.should_keep_alive= TRUE
877
876
  ,.message_complete_on_eof= FALSE
878
877
  ,.http_major= 1
879
878
  ,.http_minor= 1
879
+ ,.method= HTTP_PURGE
880
+ ,.query_string= ""
881
+ ,.fragment= ""
882
+ ,.request_path= "/file.txt"
883
+ ,.request_url= "/file.txt"
884
+ ,.num_headers= 1
885
+ ,.headers= { { "Host", "www.example.com" } }
886
+ ,.body= ""
887
+ }
888
+
889
+ #define SEARCH_REQ 32
890
+ , {.name = "SEARCH request"
891
+ ,.type= HTTP_REQUEST
892
+ ,.raw= "SEARCH / HTTP/1.1\r\n"
893
+ "Host: www.example.com\r\n"
894
+ "\r\n"
895
+ ,.should_keep_alive= TRUE
896
+ ,.message_complete_on_eof= FALSE
897
+ ,.http_major= 1
898
+ ,.http_minor= 1
899
+ ,.method= HTTP_SEARCH
900
+ ,.query_string= ""
901
+ ,.fragment= ""
902
+ ,.request_path= "/"
903
+ ,.request_url= "/"
904
+ ,.num_headers= 1
905
+ ,.headers= { { "Host", "www.example.com" } }
906
+ ,.body= ""
907
+ }
908
+
909
+ #define PROXY_WITH_BASIC_AUTH 33
910
+ , {.name= "host:port and basic_auth"
911
+ ,.type= HTTP_REQUEST
912
+ ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
913
+ "\r\n"
914
+ ,.should_keep_alive= TRUE
915
+ ,.message_complete_on_eof= FALSE
916
+ ,.http_major= 1
917
+ ,.http_minor= 1
918
+ ,.method= HTTP_GET
919
+ ,.fragment= ""
920
+ ,.request_path= "/toto"
921
+ ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
922
+ ,.host= "hypnotoad.org"
923
+ ,.userinfo= "a%12:b!&*$"
924
+ ,.port= 1234
925
+ ,.num_headers= 0
926
+ ,.headers= { }
927
+ ,.body= ""
928
+ }
929
+
930
+ #define LINE_FOLDING_IN_HEADER_WITH_LF 34
931
+ , {.name= "line folding in header value"
932
+ ,.type= HTTP_REQUEST
933
+ ,.raw= "GET / HTTP/1.1\n"
934
+ "Line1: abc\n"
935
+ "\tdef\n"
936
+ " ghi\n"
937
+ "\t\tjkl\n"
938
+ " mno \n"
939
+ "\t \tqrs\n"
940
+ "Line2: \t line2\t\n"
941
+ "Line3:\n"
942
+ " line3\n"
943
+ "Line4: \n"
944
+ " \n"
945
+ "Connection:\n"
946
+ " close\n"
947
+ "\n"
948
+ ,.should_keep_alive= FALSE
949
+ ,.message_complete_on_eof= FALSE
950
+ ,.http_major= 1
951
+ ,.http_minor= 1
952
+ ,.method= HTTP_GET
953
+ ,.query_string= ""
954
+ ,.fragment= ""
955
+ ,.request_path= "/"
956
+ ,.request_url= "/"
957
+ ,.num_headers= 5
958
+ ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
959
+ , { "Line2", "line2\t" }
960
+ , { "Line3", "line3" }
961
+ , { "Line4", "" }
962
+ , { "Connection", "close" },
963
+ }
964
+ ,.body= ""
965
+ }
966
+
967
+ #define CONNECTION_MULTI 35
968
+ , {.name = "multiple connection header values with folding"
969
+ ,.type= HTTP_REQUEST
970
+ ,.raw= "GET /demo HTTP/1.1\r\n"
971
+ "Host: example.com\r\n"
972
+ "Connection: Something,\r\n"
973
+ " Upgrade, ,Keep-Alive\r\n"
974
+ "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
975
+ "Sec-WebSocket-Protocol: sample\r\n"
976
+ "Upgrade: WebSocket\r\n"
977
+ "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
978
+ "Origin: http://example.com\r\n"
979
+ "\r\n"
980
+ "Hot diggity dogg"
981
+ ,.should_keep_alive= TRUE
982
+ ,.message_complete_on_eof= FALSE
983
+ ,.http_major= 1
984
+ ,.http_minor= 1
985
+ ,.method= HTTP_GET
986
+ ,.query_string= ""
987
+ ,.fragment= ""
988
+ ,.request_path= "/demo"
989
+ ,.request_url= "/demo"
990
+ ,.num_headers= 7
991
+ ,.upgrade="Hot diggity dogg"
992
+ ,.headers= { { "Host", "example.com" }
993
+ , { "Connection", "Something, Upgrade, ,Keep-Alive" }
994
+ , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
995
+ , { "Sec-WebSocket-Protocol", "sample" }
996
+ , { "Upgrade", "WebSocket" }
997
+ , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
998
+ , { "Origin", "http://example.com" }
999
+ }
1000
+ ,.body= ""
1001
+ }
1002
+
1003
+ #define CONNECTION_MULTI_LWS 36
1004
+ , {.name = "multiple connection header values with folding and lws"
1005
+ ,.type= HTTP_REQUEST
1006
+ ,.raw= "GET /demo HTTP/1.1\r\n"
1007
+ "Connection: keep-alive, upgrade\r\n"
1008
+ "Upgrade: WebSocket\r\n"
1009
+ "\r\n"
1010
+ "Hot diggity dogg"
1011
+ ,.should_keep_alive= TRUE
1012
+ ,.message_complete_on_eof= FALSE
1013
+ ,.http_major= 1
1014
+ ,.http_minor= 1
1015
+ ,.method= HTTP_GET
1016
+ ,.query_string= ""
1017
+ ,.fragment= ""
1018
+ ,.request_path= "/demo"
1019
+ ,.request_url= "/demo"
1020
+ ,.num_headers= 2
1021
+ ,.upgrade="Hot diggity dogg"
1022
+ ,.headers= { { "Connection", "keep-alive, upgrade" }
1023
+ , { "Upgrade", "WebSocket" }
1024
+ }
1025
+ ,.body= ""
1026
+ }
1027
+
1028
+ #define CONNECTION_MULTI_LWS_CRLF 37
1029
+ , {.name = "multiple connection header values with folding and lws"
1030
+ ,.type= HTTP_REQUEST
1031
+ ,.raw= "GET /demo HTTP/1.1\r\n"
1032
+ "Connection: keep-alive, \r\n upgrade\r\n"
1033
+ "Upgrade: WebSocket\r\n"
1034
+ "\r\n"
1035
+ "Hot diggity dogg"
1036
+ ,.should_keep_alive= TRUE
1037
+ ,.message_complete_on_eof= FALSE
1038
+ ,.http_major= 1
1039
+ ,.http_minor= 1
1040
+ ,.method= HTTP_GET
1041
+ ,.query_string= ""
1042
+ ,.fragment= ""
1043
+ ,.request_path= "/demo"
1044
+ ,.request_url= "/demo"
1045
+ ,.num_headers= 2
1046
+ ,.upgrade="Hot diggity dogg"
1047
+ ,.headers= { { "Connection", "keep-alive, upgrade" }
1048
+ , { "Upgrade", "WebSocket" }
1049
+ }
1050
+ ,.body= ""
1051
+ }
1052
+
1053
+ #define UPGRADE_POST_REQUEST 38
1054
+ , {.name = "upgrade post request"
1055
+ ,.type= HTTP_REQUEST
1056
+ ,.raw= "POST /demo HTTP/1.1\r\n"
1057
+ "Host: example.com\r\n"
1058
+ "Connection: Upgrade\r\n"
1059
+ "Upgrade: HTTP/2.0\r\n"
1060
+ "Content-Length: 15\r\n"
1061
+ "\r\n"
1062
+ "sweet post body"
1063
+ "Hot diggity dogg"
1064
+ ,.should_keep_alive= TRUE
1065
+ ,.message_complete_on_eof= FALSE
1066
+ ,.http_major= 1
1067
+ ,.http_minor= 1
1068
+ ,.method= HTTP_POST
1069
+ ,.request_path= "/demo"
1070
+ ,.request_url= "/demo"
1071
+ ,.num_headers= 4
1072
+ ,.upgrade="Hot diggity dogg"
1073
+ ,.headers= { { "Host", "example.com" }
1074
+ , { "Connection", "Upgrade" }
1075
+ , { "Upgrade", "HTTP/2.0" }
1076
+ , { "Content-Length", "15" }
1077
+ }
1078
+ ,.body= "sweet post body"
1079
+ }
1080
+
1081
+ #define CONNECT_WITH_BODY_REQUEST 39
1082
+ , {.name = "connect with body request"
1083
+ ,.type= HTTP_REQUEST
1084
+ ,.raw= "CONNECT foo.bar.com:443 HTTP/1.0\r\n"
1085
+ "User-agent: Mozilla/1.1N\r\n"
1086
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
1087
+ "Content-Length: 10\r\n"
1088
+ "\r\n"
1089
+ "blarfcicle"
1090
+ ,.should_keep_alive= FALSE
1091
+ ,.message_complete_on_eof= FALSE
1092
+ ,.http_major= 1
1093
+ ,.http_minor= 0
1094
+ ,.method= HTTP_CONNECT
1095
+ ,.request_url= "foo.bar.com:443"
1096
+ ,.num_headers= 3
1097
+ ,.upgrade="blarfcicle"
1098
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
1099
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
1100
+ , { "Content-Length", "10" }
1101
+ }
1102
+ ,.body= ""
1103
+ }
1104
+
1105
+ /* Examples from the Internet draft for LINK/UNLINK methods:
1106
+ * https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5
1107
+ */
1108
+
1109
+ #define LINK_REQUEST 40
1110
+ , {.name = "link request"
1111
+ ,.type= HTTP_REQUEST
1112
+ ,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n"
1113
+ "Host: example.com\r\n"
1114
+ "Link: <http://example.com/profiles/joe>; rel=\"tag\"\r\n"
1115
+ "Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
1116
+ "\r\n"
1117
+ ,.should_keep_alive= TRUE
1118
+ ,.message_complete_on_eof= FALSE
1119
+ ,.http_major= 1
1120
+ ,.http_minor= 1
1121
+ ,.method= HTTP_LINK
1122
+ ,.request_path= "/images/my_dog.jpg"
1123
+ ,.request_url= "/images/my_dog.jpg"
1124
+ ,.query_string= ""
1125
+ ,.fragment= ""
1126
+ ,.num_headers= 3
1127
+ ,.headers= { { "Host", "example.com" }
1128
+ , { "Link", "<http://example.com/profiles/joe>; rel=\"tag\"" }
1129
+ , { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
1130
+ }
1131
+ ,.body= ""
1132
+ }
1133
+
1134
+ #define UNLINK_REQUEST 41
1135
+ , {.name = "unlink request"
1136
+ ,.type= HTTP_REQUEST
1137
+ ,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
1138
+ "Host: example.com\r\n"
1139
+ "Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
1140
+ "\r\n"
1141
+ ,.should_keep_alive= TRUE
1142
+ ,.message_complete_on_eof= FALSE
1143
+ ,.http_major= 1
1144
+ ,.http_minor= 1
1145
+ ,.method= HTTP_UNLINK
1146
+ ,.request_path= "/images/my_dog.jpg"
1147
+ ,.request_url= "/images/my_dog.jpg"
1148
+ ,.query_string= ""
1149
+ ,.fragment= ""
1150
+ ,.num_headers= 2
1151
+ ,.headers= { { "Host", "example.com" }
1152
+ , { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
1153
+ }
1154
+ ,.body= ""
1155
+ }
1156
+
1157
+ #define SOURCE_REQUEST 42
1158
+ , {.name = "source request"
1159
+ ,.type= HTTP_REQUEST
1160
+ ,.raw= "SOURCE /music/sweet/music HTTP/1.1\r\n"
1161
+ "Host: example.com\r\n"
1162
+ "\r\n"
1163
+ ,.should_keep_alive= TRUE
1164
+ ,.message_complete_on_eof= FALSE
1165
+ ,.http_major= 1
1166
+ ,.http_minor= 1
1167
+ ,.method= HTTP_SOURCE
1168
+ ,.request_path= "/music/sweet/music"
1169
+ ,.request_url= "/music/sweet/music"
1170
+ ,.query_string= ""
1171
+ ,.fragment= ""
1172
+ ,.num_headers= 1
1173
+ ,.headers= { { "Host", "example.com" } }
1174
+ ,.body= ""
1175
+ }
1176
+ };
1177
+
1178
+ /* * R E S P O N S E S * */
1179
+ const struct message responses[] =
1180
+ #define GOOGLE_301 0
1181
+ { {.name= "google 301"
1182
+ ,.type= HTTP_RESPONSE
1183
+ ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
1184
+ "Location: http://www.google.com/\r\n"
1185
+ "Content-Type: text/html; charset=UTF-8\r\n"
1186
+ "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
1187
+ "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
1188
+ "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
1189
+ "Cache-Control: public, max-age=2592000\r\n"
1190
+ "Server: gws\r\n"
1191
+ "Content-Length: 219 \r\n"
1192
+ "\r\n"
1193
+ "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1194
+ "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1195
+ "<H1>301 Moved</H1>\n"
1196
+ "The document has moved\n"
1197
+ "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1198
+ "</BODY></HTML>\r\n"
1199
+ ,.should_keep_alive= TRUE
1200
+ ,.message_complete_on_eof= FALSE
1201
+ ,.http_major= 1
1202
+ ,.http_minor= 1
1203
+ ,.status_code= 301
1204
+ ,.response_status= "Moved Permanently"
1205
+ ,.num_headers= 8
1206
+ ,.headers=
1207
+ { { "Location", "http://www.google.com/" }
1208
+ , { "Content-Type", "text/html; charset=UTF-8" }
1209
+ , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
1210
+ , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
1211
+ , { "X-$PrototypeBI-Version", "1.6.0.3" }
1212
+ , { "Cache-Control", "public, max-age=2592000" }
1213
+ , { "Server", "gws" }
1214
+ , { "Content-Length", "219 " }
1215
+ }
1216
+ ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
1217
+ "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
1218
+ "<H1>301 Moved</H1>\n"
1219
+ "The document has moved\n"
1220
+ "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
1221
+ "</BODY></HTML>\r\n"
1222
+ }
1223
+
1224
+ #define NO_CONTENT_LENGTH_RESPONSE 1
1225
+ /* The client should wait for the server's EOF. That is, when content-length
1226
+ * is not specified, and "Connection: close", the end of body is specified
1227
+ * by the EOF.
1228
+ * Compare with APACHEBENCH_GET
1229
+ */
1230
+ , {.name= "no content-length response"
1231
+ ,.type= HTTP_RESPONSE
1232
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1233
+ "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
1234
+ "Server: Apache\r\n"
1235
+ "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
1236
+ "Content-Type: text/xml; charset=utf-8\r\n"
1237
+ "Connection: close\r\n"
1238
+ "\r\n"
1239
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1240
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1241
+ " <SOAP-ENV:Body>\n"
1242
+ " <SOAP-ENV:Fault>\n"
1243
+ " <faultcode>SOAP-ENV:Client</faultcode>\n"
1244
+ " <faultstring>Client Error</faultstring>\n"
1245
+ " </SOAP-ENV:Fault>\n"
1246
+ " </SOAP-ENV:Body>\n"
1247
+ "</SOAP-ENV:Envelope>"
1248
+ ,.should_keep_alive= FALSE
1249
+ ,.message_complete_on_eof= TRUE
1250
+ ,.http_major= 1
1251
+ ,.http_minor= 1
1252
+ ,.status_code= 200
1253
+ ,.response_status= "OK"
1254
+ ,.num_headers= 5
1255
+ ,.headers=
1256
+ { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
1257
+ , { "Server", "Apache" }
1258
+ , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
1259
+ , { "Content-Type", "text/xml; charset=utf-8" }
1260
+ , { "Connection", "close" }
1261
+ }
1262
+ ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1263
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
1264
+ " <SOAP-ENV:Body>\n"
1265
+ " <SOAP-ENV:Fault>\n"
1266
+ " <faultcode>SOAP-ENV:Client</faultcode>\n"
1267
+ " <faultstring>Client Error</faultstring>\n"
1268
+ " </SOAP-ENV:Fault>\n"
1269
+ " </SOAP-ENV:Body>\n"
1270
+ "</SOAP-ENV:Envelope>"
1271
+ }
1272
+
1273
+ #define NO_HEADERS_NO_BODY_404 2
1274
+ , {.name= "404 no headers no body"
1275
+ ,.type= HTTP_RESPONSE
1276
+ ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
1277
+ ,.should_keep_alive= FALSE
1278
+ ,.message_complete_on_eof= TRUE
1279
+ ,.http_major= 1
1280
+ ,.http_minor= 1
1281
+ ,.status_code= 404
1282
+ ,.response_status= "Not Found"
1283
+ ,.num_headers= 0
1284
+ ,.headers= {}
1285
+ ,.body_size= 0
1286
+ ,.body= ""
1287
+ }
1288
+
1289
+ #define NO_REASON_PHRASE 3
1290
+ , {.name= "301 no response phrase"
1291
+ ,.type= HTTP_RESPONSE
1292
+ ,.raw= "HTTP/1.1 301\r\n\r\n"
1293
+ ,.should_keep_alive = FALSE
1294
+ ,.message_complete_on_eof= TRUE
1295
+ ,.http_major= 1
1296
+ ,.http_minor= 1
1297
+ ,.status_code= 301
1298
+ ,.response_status= ""
1299
+ ,.num_headers= 0
1300
+ ,.headers= {}
1301
+ ,.body= ""
1302
+ }
1303
+
1304
+ #define TRAILING_SPACE_ON_CHUNKED_BODY 4
1305
+ , {.name="200 trailing space on chunked body"
1306
+ ,.type= HTTP_RESPONSE
1307
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1308
+ "Content-Type: text/plain\r\n"
1309
+ "Transfer-Encoding: chunked\r\n"
1310
+ "\r\n"
1311
+ "25 \r\n"
1312
+ "This is the data in the first chunk\r\n"
1313
+ "\r\n"
1314
+ "1C\r\n"
1315
+ "and this is the second one\r\n"
1316
+ "\r\n"
1317
+ "0 \r\n"
1318
+ "\r\n"
1319
+ ,.should_keep_alive= TRUE
1320
+ ,.message_complete_on_eof= FALSE
1321
+ ,.http_major= 1
1322
+ ,.http_minor= 1
1323
+ ,.status_code= 200
1324
+ ,.response_status= "OK"
1325
+ ,.num_headers= 2
1326
+ ,.headers=
1327
+ { {"Content-Type", "text/plain" }
1328
+ , {"Transfer-Encoding", "chunked" }
1329
+ }
1330
+ ,.body_size = 37+28
1331
+ ,.body =
1332
+ "This is the data in the first chunk\r\n"
1333
+ "and this is the second one\r\n"
1334
+ ,.num_chunks_complete= 3
1335
+ ,.chunk_lengths= { 0x25, 0x1c }
1336
+ }
1337
+
1338
+ #define NO_CARRIAGE_RET 5
1339
+ , {.name="no carriage ret"
1340
+ ,.type= HTTP_RESPONSE
1341
+ ,.raw= "HTTP/1.1 200 OK\n"
1342
+ "Content-Type: text/html; charset=utf-8\n"
1343
+ "Connection: close\n"
1344
+ "\n"
1345
+ "these headers are from http://news.ycombinator.com/"
1346
+ ,.should_keep_alive= FALSE
1347
+ ,.message_complete_on_eof= TRUE
1348
+ ,.http_major= 1
1349
+ ,.http_minor= 1
1350
+ ,.status_code= 200
1351
+ ,.response_status= "OK"
1352
+ ,.num_headers= 2
1353
+ ,.headers=
1354
+ { {"Content-Type", "text/html; charset=utf-8" }
1355
+ , {"Connection", "close" }
1356
+ }
1357
+ ,.body= "these headers are from http://news.ycombinator.com/"
1358
+ }
1359
+
1360
+ #define PROXY_CONNECTION 6
1361
+ , {.name="proxy connection"
1362
+ ,.type= HTTP_RESPONSE
1363
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1364
+ "Content-Type: text/html; charset=UTF-8\r\n"
1365
+ "Content-Length: 11\r\n"
1366
+ "Proxy-Connection: close\r\n"
1367
+ "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
1368
+ "\r\n"
1369
+ "hello world"
1370
+ ,.should_keep_alive= FALSE
1371
+ ,.message_complete_on_eof= FALSE
1372
+ ,.http_major= 1
1373
+ ,.http_minor= 1
1374
+ ,.status_code= 200
1375
+ ,.response_status= "OK"
1376
+ ,.num_headers= 4
1377
+ ,.headers=
1378
+ { {"Content-Type", "text/html; charset=UTF-8" }
1379
+ , {"Content-Length", "11" }
1380
+ , {"Proxy-Connection", "close" }
1381
+ , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
1382
+ }
1383
+ ,.body= "hello world"
1384
+ }
1385
+
1386
+ #define UNDERSTORE_HEADER_KEY 7
1387
+ // shown by
1388
+ // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
1389
+ , {.name="underscore header key"
1390
+ ,.type= HTTP_RESPONSE
1391
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1392
+ "Server: DCLK-AdSvr\r\n"
1393
+ "Content-Type: text/xml\r\n"
1394
+ "Content-Length: 0\r\n"
1395
+ "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
1396
+ ,.should_keep_alive= TRUE
1397
+ ,.message_complete_on_eof= FALSE
1398
+ ,.http_major= 1
1399
+ ,.http_minor= 1
1400
+ ,.status_code= 200
1401
+ ,.response_status= "OK"
1402
+ ,.num_headers= 4
1403
+ ,.headers=
1404
+ { {"Server", "DCLK-AdSvr" }
1405
+ , {"Content-Type", "text/xml" }
1406
+ , {"Content-Length", "0" }
1407
+ , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1408
+ }
1409
+ ,.body= ""
1410
+ }
1411
+
1412
+ #define BONJOUR_MADAME_FR 8
1413
+ /* The client should not merge two headers fields when the first one doesn't
1414
+ * have a value.
1415
+ */
1416
+ , {.name= "bonjourmadame.fr"
1417
+ ,.type= HTTP_RESPONSE
1418
+ ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
1419
+ "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
1420
+ "Server: Apache/2.2.3 (Red Hat)\r\n"
1421
+ "Cache-Control: public\r\n"
1422
+ "Pragma: \r\n"
1423
+ "Location: http://www.bonjourmadame.fr/\r\n"
1424
+ "Vary: Accept-Encoding\r\n"
1425
+ "Content-Length: 0\r\n"
1426
+ "Content-Type: text/html; charset=UTF-8\r\n"
1427
+ "Connection: keep-alive\r\n"
1428
+ "\r\n"
1429
+ ,.should_keep_alive= TRUE
1430
+ ,.message_complete_on_eof= FALSE
1431
+ ,.http_major= 1
1432
+ ,.http_minor= 0
1433
+ ,.status_code= 301
1434
+ ,.response_status= "Moved Permanently"
1435
+ ,.num_headers= 9
1436
+ ,.headers=
1437
+ { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1438
+ , { "Server", "Apache/2.2.3 (Red Hat)" }
1439
+ , { "Cache-Control", "public" }
1440
+ , { "Pragma", "" }
1441
+ , { "Location", "http://www.bonjourmadame.fr/" }
1442
+ , { "Vary", "Accept-Encoding" }
1443
+ , { "Content-Length", "0" }
1444
+ , { "Content-Type", "text/html; charset=UTF-8" }
1445
+ , { "Connection", "keep-alive" }
1446
+ }
1447
+ ,.body= ""
1448
+ }
1449
+
1450
+ #define RES_FIELD_UNDERSCORE 9
1451
+ /* Should handle spaces in header fields */
1452
+ , {.name= "field underscore"
1453
+ ,.type= HTTP_RESPONSE
1454
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1455
+ "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1456
+ "Server: Apache\r\n"
1457
+ "Cache-Control: no-cache, must-revalidate\r\n"
1458
+ "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1459
+ ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1460
+ "Vary: Accept-Encoding\r\n"
1461
+ "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1462
+ "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1463
+ "Transfer-Encoding: chunked\r\n"
1464
+ "Content-Type: text/html\r\n"
1465
+ "Connection: close\r\n"
1466
+ "\r\n"
1467
+ "0\r\n\r\n"
1468
+ ,.should_keep_alive= FALSE
1469
+ ,.message_complete_on_eof= FALSE
1470
+ ,.http_major= 1
1471
+ ,.http_minor= 1
880
1472
  ,.status_code= 200
881
- ,.num_headers= 4
1473
+ ,.response_status= "OK"
1474
+ ,.num_headers= 11
882
1475
  ,.headers=
883
- { {"Server", "DCLK-AdSvr" }
884
- , {"Content-Type", "text/xml" }
885
- , {"Content-Length", "0" }
886
- , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1476
+ { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1477
+ , { "Server", "Apache" }
1478
+ , { "Cache-Control", "no-cache, must-revalidate" }
1479
+ , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1480
+ , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1481
+ , { "Vary", "Accept-Encoding" }
1482
+ , { "_eep-Alive", "timeout=45" }
1483
+ , { "_onnection", "Keep-Alive" }
1484
+ , { "Transfer-Encoding", "chunked" }
1485
+ , { "Content-Type", "text/html" }
1486
+ , { "Connection", "close" }
887
1487
  }
888
1488
  ,.body= ""
1489
+ ,.num_chunks_complete= 1
1490
+ ,.chunk_lengths= {}
889
1491
  }
890
1492
 
891
- #define BONJOUR_MADAME_FR 8
892
- /* The client should not merge two headers fields when the first one doesn't
893
- * have a value.
894
- */
895
- , {.name= "bonjourmadame.fr"
1493
+ #define NON_ASCII_IN_STATUS_LINE 10
1494
+ /* Should handle non-ASCII in status line */
1495
+ , {.name= "non-ASCII in status line"
896
1496
  ,.type= HTTP_RESPONSE
897
- ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
898
- "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
899
- "Server: Apache/2.2.3 (Red Hat)\r\n"
900
- "Cache-Control: public\r\n"
901
- "Pragma: \r\n"
902
- "Location: http://www.bonjourmadame.fr/\r\n"
903
- "Vary: Accept-Encoding\r\n"
1497
+ ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1498
+ "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
904
1499
  "Content-Length: 0\r\n"
905
- "Content-Type: text/html; charset=UTF-8\r\n"
1500
+ "Connection: close\r\n"
1501
+ "\r\n"
1502
+ ,.should_keep_alive= FALSE
1503
+ ,.message_complete_on_eof= FALSE
1504
+ ,.http_major= 1
1505
+ ,.http_minor= 1
1506
+ ,.status_code= 500
1507
+ ,.response_status= "Oriëntatieprobleem"
1508
+ ,.num_headers= 3
1509
+ ,.headers=
1510
+ { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1511
+ , { "Content-Length", "0" }
1512
+ , { "Connection", "close" }
1513
+ }
1514
+ ,.body= ""
1515
+ }
1516
+
1517
+ #define HTTP_VERSION_0_9 11
1518
+ /* Should handle HTTP/0.9 */
1519
+ , {.name= "http version 0.9"
1520
+ ,.type= HTTP_RESPONSE
1521
+ ,.raw= "HTTP/0.9 200 OK\r\n"
1522
+ "\r\n"
1523
+ ,.should_keep_alive= FALSE
1524
+ ,.message_complete_on_eof= TRUE
1525
+ ,.http_major= 0
1526
+ ,.http_minor= 9
1527
+ ,.status_code= 200
1528
+ ,.response_status= "OK"
1529
+ ,.num_headers= 0
1530
+ ,.headers=
1531
+ {}
1532
+ ,.body= ""
1533
+ }
1534
+
1535
+ #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1536
+ /* The client should wait for the server's EOF. That is, when neither
1537
+ * content-length nor transfer-encoding is specified, the end of body
1538
+ * is specified by the EOF.
1539
+ */
1540
+ , {.name= "neither content-length nor transfer-encoding response"
1541
+ ,.type= HTTP_RESPONSE
1542
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1543
+ "Content-Type: text/plain\r\n"
1544
+ "\r\n"
1545
+ "hello world"
1546
+ ,.should_keep_alive= FALSE
1547
+ ,.message_complete_on_eof= TRUE
1548
+ ,.http_major= 1
1549
+ ,.http_minor= 1
1550
+ ,.status_code= 200
1551
+ ,.response_status= "OK"
1552
+ ,.num_headers= 1
1553
+ ,.headers=
1554
+ { { "Content-Type", "text/plain" }
1555
+ }
1556
+ ,.body= "hello world"
1557
+ }
1558
+
1559
+ #define NO_BODY_HTTP10_KA_200 13
1560
+ , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1561
+ ,.type= HTTP_RESPONSE
1562
+ ,.raw= "HTTP/1.0 200 OK\r\n"
1563
+ "Connection: keep-alive\r\n"
1564
+ "\r\n"
1565
+ ,.should_keep_alive= FALSE
1566
+ ,.message_complete_on_eof= TRUE
1567
+ ,.http_major= 1
1568
+ ,.http_minor= 0
1569
+ ,.status_code= 200
1570
+ ,.response_status= "OK"
1571
+ ,.num_headers= 1
1572
+ ,.headers=
1573
+ { { "Connection", "keep-alive" }
1574
+ }
1575
+ ,.body_size= 0
1576
+ ,.body= ""
1577
+ }
1578
+
1579
+ #define NO_BODY_HTTP10_KA_204 14
1580
+ , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1581
+ ,.type= HTTP_RESPONSE
1582
+ ,.raw= "HTTP/1.0 204 No content\r\n"
1583
+ "Connection: keep-alive\r\n"
1584
+ "\r\n"
1585
+ ,.should_keep_alive= TRUE
1586
+ ,.message_complete_on_eof= FALSE
1587
+ ,.http_major= 1
1588
+ ,.http_minor= 0
1589
+ ,.status_code= 204
1590
+ ,.response_status= "No content"
1591
+ ,.num_headers= 1
1592
+ ,.headers=
1593
+ { { "Connection", "keep-alive" }
1594
+ }
1595
+ ,.body_size= 0
1596
+ ,.body= ""
1597
+ }
1598
+
1599
+ #define NO_BODY_HTTP11_KA_200 15
1600
+ , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1601
+ ,.type= HTTP_RESPONSE
1602
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1603
+ "\r\n"
1604
+ ,.should_keep_alive= FALSE
1605
+ ,.message_complete_on_eof= TRUE
1606
+ ,.http_major= 1
1607
+ ,.http_minor= 1
1608
+ ,.status_code= 200
1609
+ ,.response_status= "OK"
1610
+ ,.num_headers= 0
1611
+ ,.headers={}
1612
+ ,.body_size= 0
1613
+ ,.body= ""
1614
+ }
1615
+
1616
+ #define NO_BODY_HTTP11_KA_204 16
1617
+ , {.name= "HTTP/1.1 with a 204 status"
1618
+ ,.type= HTTP_RESPONSE
1619
+ ,.raw= "HTTP/1.1 204 No content\r\n"
1620
+ "\r\n"
1621
+ ,.should_keep_alive= TRUE
1622
+ ,.message_complete_on_eof= FALSE
1623
+ ,.http_major= 1
1624
+ ,.http_minor= 1
1625
+ ,.status_code= 204
1626
+ ,.response_status= "No content"
1627
+ ,.num_headers= 0
1628
+ ,.headers={}
1629
+ ,.body_size= 0
1630
+ ,.body= ""
1631
+ }
1632
+
1633
+ #define NO_BODY_HTTP11_NOKA_204 17
1634
+ , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1635
+ ,.type= HTTP_RESPONSE
1636
+ ,.raw= "HTTP/1.1 204 No content\r\n"
1637
+ "Connection: close\r\n"
1638
+ "\r\n"
1639
+ ,.should_keep_alive= FALSE
1640
+ ,.message_complete_on_eof= FALSE
1641
+ ,.http_major= 1
1642
+ ,.http_minor= 1
1643
+ ,.status_code= 204
1644
+ ,.response_status= "No content"
1645
+ ,.num_headers= 1
1646
+ ,.headers=
1647
+ { { "Connection", "close" }
1648
+ }
1649
+ ,.body_size= 0
1650
+ ,.body= ""
1651
+ }
1652
+
1653
+ #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1654
+ , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1655
+ ,.type= HTTP_RESPONSE
1656
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1657
+ "Transfer-Encoding: chunked\r\n"
1658
+ "\r\n"
1659
+ "0\r\n"
1660
+ "\r\n"
1661
+ ,.should_keep_alive= TRUE
1662
+ ,.message_complete_on_eof= FALSE
1663
+ ,.http_major= 1
1664
+ ,.http_minor= 1
1665
+ ,.status_code= 200
1666
+ ,.response_status= "OK"
1667
+ ,.num_headers= 1
1668
+ ,.headers=
1669
+ { { "Transfer-Encoding", "chunked" }
1670
+ }
1671
+ ,.body_size= 0
1672
+ ,.body= ""
1673
+ ,.num_chunks_complete= 1
1674
+ }
1675
+
1676
+ #if !HTTP_PARSER_STRICT
1677
+ #define SPACE_IN_FIELD_RES 19
1678
+ /* Should handle spaces in header fields */
1679
+ , {.name= "field space"
1680
+ ,.type= HTTP_RESPONSE
1681
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1682
+ "Server: Microsoft-IIS/6.0\r\n"
1683
+ "X-Powered-By: ASP.NET\r\n"
1684
+ "en-US Content-Type: text/xml\r\n" /* this is the problem */
1685
+ "Content-Type: text/xml\r\n"
1686
+ "Content-Length: 16\r\n"
1687
+ "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
906
1688
  "Connection: keep-alive\r\n"
907
1689
  "\r\n"
1690
+ "<xml>hello</xml>" /* fake body */
1691
+ ,.should_keep_alive= TRUE
1692
+ ,.message_complete_on_eof= FALSE
1693
+ ,.http_major= 1
1694
+ ,.http_minor= 1
1695
+ ,.status_code= 200
1696
+ ,.response_status= "OK"
1697
+ ,.num_headers= 7
1698
+ ,.headers=
1699
+ { { "Server", "Microsoft-IIS/6.0" }
1700
+ , { "X-Powered-By", "ASP.NET" }
1701
+ , { "en-US Content-Type", "text/xml" }
1702
+ , { "Content-Type", "text/xml" }
1703
+ , { "Content-Length", "16" }
1704
+ , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1705
+ , { "Connection", "keep-alive" }
1706
+ }
1707
+ ,.body= "<xml>hello</xml>"
1708
+ }
1709
+ #endif /* !HTTP_PARSER_STRICT */
1710
+
1711
+ #define AMAZON_COM 20
1712
+ , {.name= "amazon.com"
1713
+ ,.type= HTTP_RESPONSE
1714
+ ,.raw= "HTTP/1.1 301 MovedPermanently\r\n"
1715
+ "Date: Wed, 15 May 2013 17:06:33 GMT\r\n"
1716
+ "Server: Server\r\n"
1717
+ "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n"
1718
+ "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"
1719
+ "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n"
1720
+ "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"
1721
+ "Vary: Accept-Encoding,User-Agent\r\n"
1722
+ "Content-Type: text/html; charset=ISO-8859-1\r\n"
1723
+ "Transfer-Encoding: chunked\r\n"
1724
+ "\r\n"
1725
+ "1\r\n"
1726
+ "\n\r\n"
1727
+ "0\r\n"
1728
+ "\r\n"
1729
+ ,.should_keep_alive= TRUE
1730
+ ,.message_complete_on_eof= FALSE
1731
+ ,.http_major= 1
1732
+ ,.http_minor= 1
1733
+ ,.status_code= 301
1734
+ ,.response_status= "MovedPermanently"
1735
+ ,.num_headers= 9
1736
+ ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" }
1737
+ , { "Server", "Server" }
1738
+ , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" }
1739
+ , { "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 \"" }
1740
+ , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" }
1741
+ , { "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" }
1742
+ , { "Vary", "Accept-Encoding,User-Agent" }
1743
+ , { "Content-Type", "text/html; charset=ISO-8859-1" }
1744
+ , { "Transfer-Encoding", "chunked" }
1745
+ }
1746
+ ,.body= "\n"
1747
+ ,.num_chunks_complete= 2
1748
+ ,.chunk_lengths= { 1 }
1749
+ }
1750
+
1751
+ #define EMPTY_REASON_PHRASE_AFTER_SPACE 20
1752
+ , {.name= "empty reason phrase after space"
1753
+ ,.type= HTTP_RESPONSE
1754
+ ,.raw= "HTTP/1.1 200 \r\n"
1755
+ "\r\n"
1756
+ ,.should_keep_alive= FALSE
1757
+ ,.message_complete_on_eof= TRUE
1758
+ ,.http_major= 1
1759
+ ,.http_minor= 1
1760
+ ,.status_code= 200
1761
+ ,.response_status= ""
1762
+ ,.num_headers= 0
1763
+ ,.headers= {}
1764
+ ,.body= ""
1765
+ }
1766
+
1767
+ #define CONTENT_LENGTH_X 21
1768
+ , {.name= "Content-Length-X"
1769
+ ,.type= HTTP_RESPONSE
1770
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1771
+ "Content-Length-X: 0\r\n"
1772
+ "Transfer-Encoding: chunked\r\n"
1773
+ "\r\n"
1774
+ "2\r\n"
1775
+ "OK\r\n"
1776
+ "0\r\n"
1777
+ "\r\n"
1778
+ ,.should_keep_alive= TRUE
1779
+ ,.message_complete_on_eof= FALSE
1780
+ ,.http_major= 1
1781
+ ,.http_minor= 1
1782
+ ,.status_code= 200
1783
+ ,.response_status= "OK"
1784
+ ,.num_headers= 2
1785
+ ,.headers= { { "Content-Length-X", "0" }
1786
+ , { "Transfer-Encoding", "chunked" }
1787
+ }
1788
+ ,.body= "OK"
1789
+ ,.num_chunks_complete= 2
1790
+ ,.chunk_lengths= { 2 }
1791
+ }
1792
+
1793
+ #define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER 22
1794
+ , {.name= "HTTP 101 response with Upgrade header"
1795
+ ,.type= HTTP_RESPONSE
1796
+ ,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
1797
+ "Connection: upgrade\r\n"
1798
+ "Upgrade: h2c\r\n"
1799
+ "\r\n"
1800
+ "proto"
1801
+ ,.should_keep_alive= TRUE
1802
+ ,.message_complete_on_eof= FALSE
1803
+ ,.http_major= 1
1804
+ ,.http_minor= 1
1805
+ ,.status_code= 101
1806
+ ,.response_status= "Switching Protocols"
1807
+ ,.upgrade= "proto"
1808
+ ,.num_headers= 2
1809
+ ,.headers=
1810
+ { { "Connection", "upgrade" }
1811
+ , { "Upgrade", "h2c" }
1812
+ }
1813
+ }
1814
+
1815
+ #define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 23
1816
+ , {.name= "HTTP 101 response with Upgrade and Content-Length header"
1817
+ ,.type= HTTP_RESPONSE
1818
+ ,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
1819
+ "Connection: upgrade\r\n"
1820
+ "Upgrade: h2c\r\n"
1821
+ "Content-Length: 4\r\n"
1822
+ "\r\n"
1823
+ "body"
1824
+ "proto"
908
1825
  ,.should_keep_alive= TRUE
909
1826
  ,.message_complete_on_eof= FALSE
910
1827
  ,.http_major= 1
911
- ,.http_minor= 0
912
- ,.status_code= 301
913
- ,.num_headers= 9
1828
+ ,.http_minor= 1
1829
+ ,.status_code= 101
1830
+ ,.response_status= "Switching Protocols"
1831
+ ,.body= "body"
1832
+ ,.upgrade= "proto"
1833
+ ,.num_headers= 3
914
1834
  ,.headers=
915
- { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
916
- , { "Server", "Apache/2.2.3 (Red Hat)" }
917
- , { "Cache-Control", "public" }
918
- , { "Pragma", "" }
919
- , { "Location", "http://www.bonjourmadame.fr/" }
920
- , { "Vary", "Accept-Encoding" }
921
- , { "Content-Length", "0" }
922
- , { "Content-Type", "text/html; charset=UTF-8" }
923
- , { "Connection", "keep-alive" }
1835
+ { { "Connection", "upgrade" }
1836
+ , { "Upgrade", "h2c" }
1837
+ , { "Content-Length", "4" }
924
1838
  }
925
- ,.body= ""
926
1839
  }
927
1840
 
928
- #define SPACE_IN_FIELD_RES 9
929
- /* Should handle spaces in header fields */
930
- , {.name= "field space"
1841
+ #define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 24
1842
+ , {.name= "HTTP 101 response with Upgrade and Transfer-Encoding header"
931
1843
  ,.type= HTTP_RESPONSE
932
- ,.raw= "HTTP/1.1 200 OK\r\n"
933
- "Server: Microsoft-IIS/6.0\r\n"
934
- "X-Powered-By: ASP.NET\r\n"
935
- "en-US Content-Type: text/xml\r\n" /* this is the problem */
936
- "Content-Type: text/xml\r\n"
937
- "Content-Length: 16\r\n"
938
- "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
939
- "Connection: keep-alive\r\n"
1844
+ ,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
1845
+ "Connection: upgrade\r\n"
1846
+ "Upgrade: h2c\r\n"
1847
+ "Transfer-Encoding: chunked\r\n"
940
1848
  "\r\n"
941
- "<xml>hello</xml>" /* fake body */
1849
+ "2\r\n"
1850
+ "bo\r\n"
1851
+ "2\r\n"
1852
+ "dy\r\n"
1853
+ "0\r\n"
1854
+ "\r\n"
1855
+ "proto"
942
1856
  ,.should_keep_alive= TRUE
943
1857
  ,.message_complete_on_eof= FALSE
944
1858
  ,.http_major= 1
945
1859
  ,.http_minor= 1
946
- ,.status_code= 200
947
- ,.num_headers= 7
1860
+ ,.status_code= 101
1861
+ ,.response_status= "Switching Protocols"
1862
+ ,.body= "body"
1863
+ ,.upgrade= "proto"
1864
+ ,.num_headers= 3
948
1865
  ,.headers=
949
- { { "Server", "Microsoft-IIS/6.0" }
950
- , { "X-Powered-By", "ASP.NET" }
951
- , { "en-US Content-Type", "text/xml" }
952
- , { "Content-Type", "text/xml" }
953
- , { "Content-Length", "16" }
954
- , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
955
- , { "Connection", "keep-alive" }
1866
+ { { "Connection", "upgrade" }
1867
+ , { "Upgrade", "h2c" }
1868
+ , { "Transfer-Encoding", "chunked" }
956
1869
  }
957
- ,.body= "<xml>hello</xml>"
1870
+ ,.num_chunks_complete= 3
1871
+ ,.chunk_lengths= { 2, 2 }
958
1872
  }
959
1873
 
960
-
961
- #define RES_FIELD_UNDERSCORE 10
962
- /* Should handle spaces in header fields */
963
- , {.name= "field underscore"
1874
+ #define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER 25
1875
+ , {.name= "HTTP 200 response with Upgrade header"
964
1876
  ,.type= HTTP_RESPONSE
965
1877
  ,.raw= "HTTP/1.1 200 OK\r\n"
966
- "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
967
- "Server: Apache\r\n"
968
- "Cache-Control: no-cache, must-revalidate\r\n"
969
- "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
970
- ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
971
- "Vary: Accept-Encoding\r\n"
972
- "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
973
- "_onnection: Keep-Alive\r\n" /* semantic value ignored */
974
- "Transfer-Encoding: chunked\r\n"
975
- "Content-Type: text/html\r\n"
976
- "Connection: close\r\n"
1878
+ "Connection: upgrade\r\n"
1879
+ "Upgrade: h2c\r\n"
977
1880
  "\r\n"
978
- "0\r\n\r\n"
1881
+ "body"
979
1882
  ,.should_keep_alive= FALSE
980
- ,.message_complete_on_eof= FALSE
1883
+ ,.message_complete_on_eof= TRUE
981
1884
  ,.http_major= 1
982
1885
  ,.http_minor= 1
983
1886
  ,.status_code= 200
984
- ,.num_headers= 11
1887
+ ,.response_status= "OK"
1888
+ ,.body= "body"
1889
+ ,.upgrade= NULL
1890
+ ,.num_headers= 2
985
1891
  ,.headers=
986
- { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
987
- , { "Server", "Apache" }
988
- , { "Cache-Control", "no-cache, must-revalidate" }
989
- , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
990
- , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
991
- , { "Vary", "Accept-Encoding" }
992
- , { "_eep-Alive", "timeout=45" }
993
- , { "_onnection", "Keep-Alive" }
994
- , { "Transfer-Encoding", "chunked" }
995
- , { "Content-Type", "text/html" }
996
- , { "Connection", "close" }
1892
+ { { "Connection", "upgrade" }
1893
+ , { "Upgrade", "h2c" }
997
1894
  }
998
- ,.body= ""
999
1895
  }
1000
1896
 
1001
- #define NON_ASCII_IN_STATUS_LINE 11
1002
- /* Should handle non-ASCII in status line */
1003
- , {.name= "non-ASCII in status line"
1897
+ #define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 26
1898
+ , {.name= "HTTP 200 response with Upgrade and Content-Length header"
1004
1899
  ,.type= HTTP_RESPONSE
1005
- ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1006
- "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1007
- "Content-Length: 0\r\n"
1008
- "Connection: close\r\n"
1900
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1901
+ "Connection: upgrade\r\n"
1902
+ "Upgrade: h2c\r\n"
1903
+ "Content-Length: 4\r\n"
1009
1904
  "\r\n"
1010
- ,.should_keep_alive= FALSE
1905
+ "body"
1906
+ ,.should_keep_alive= TRUE
1011
1907
  ,.message_complete_on_eof= FALSE
1012
1908
  ,.http_major= 1
1013
1909
  ,.http_minor= 1
1014
- ,.status_code= 500
1910
+ ,.status_code= 200
1911
+ ,.response_status= "OK"
1015
1912
  ,.num_headers= 3
1913
+ ,.body= "body"
1914
+ ,.upgrade= NULL
1016
1915
  ,.headers=
1017
- { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1018
- , { "Content-Length", "0" }
1019
- , { "Connection", "close" }
1916
+ { { "Connection", "upgrade" }
1917
+ , { "Upgrade", "h2c" }
1918
+ , { "Content-Length", "4" }
1020
1919
  }
1021
- ,.body= ""
1022
1920
  }
1023
1921
 
1024
-
1025
- , {.name= NULL } /* sentinel */
1922
+ #define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 27
1923
+ , {.name= "HTTP 200 response with Upgrade and Transfer-Encoding header"
1924
+ ,.type= HTTP_RESPONSE
1925
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1926
+ "Connection: upgrade\r\n"
1927
+ "Upgrade: h2c\r\n"
1928
+ "Transfer-Encoding: chunked\r\n"
1929
+ "\r\n"
1930
+ "2\r\n"
1931
+ "bo\r\n"
1932
+ "2\r\n"
1933
+ "dy\r\n"
1934
+ "0\r\n"
1935
+ "\r\n"
1936
+ ,.should_keep_alive= TRUE
1937
+ ,.message_complete_on_eof= FALSE
1938
+ ,.http_major= 1
1939
+ ,.http_minor= 1
1940
+ ,.status_code= 200
1941
+ ,.response_status= "OK"
1942
+ ,.num_headers= 3
1943
+ ,.body= "body"
1944
+ ,.upgrade= NULL
1945
+ ,.headers=
1946
+ { { "Connection", "upgrade" }
1947
+ , { "Upgrade", "h2c" }
1948
+ , { "Transfer-Encoding", "chunked" }
1949
+ }
1950
+ ,.num_chunks_complete= 3
1951
+ ,.chunk_lengths= { 2, 2 }
1952
+ }
1026
1953
  };
1027
1954
 
1028
- int
1029
- request_path_cb (http_parser *p, const char *buf, size_t len)
1955
+ /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1956
+ * define it ourselves.
1957
+ */
1958
+ size_t
1959
+ strnlen(const char *s, size_t maxlen)
1030
1960
  {
1031
- assert(p == parser);
1032
- strncat(messages[num_messages].request_path, buf, len);
1033
- return 0;
1961
+ const char *p;
1962
+
1963
+ p = memchr(s, '\0', maxlen);
1964
+ if (p == NULL)
1965
+ return maxlen;
1966
+
1967
+ return p - s;
1034
1968
  }
1035
1969
 
1036
- int
1037
- request_url_cb (http_parser *p, const char *buf, size_t len)
1970
+ size_t
1971
+ strlncat(char *dst, size_t len, const char *src, size_t n)
1038
1972
  {
1039
- assert(p == parser);
1040
- strncat(messages[num_messages].request_url, buf, len);
1041
- return 0;
1973
+ size_t slen;
1974
+ size_t dlen;
1975
+ size_t rlen;
1976
+ size_t ncpy;
1977
+
1978
+ slen = strnlen(src, n);
1979
+ dlen = strnlen(dst, len);
1980
+
1981
+ if (dlen < len) {
1982
+ rlen = len - dlen;
1983
+ ncpy = slen < rlen ? slen : (rlen - 1);
1984
+ memcpy(dst + dlen, src, ncpy);
1985
+ dst[dlen + ncpy] = '\0';
1986
+ }
1987
+
1988
+ assert(len > slen + dlen);
1989
+ return slen + dlen;
1042
1990
  }
1043
1991
 
1044
- int
1045
- query_string_cb (http_parser *p, const char *buf, size_t len)
1992
+ size_t
1993
+ strlncpy(char *dst, size_t len, const char *src, size_t n)
1046
1994
  {
1047
- assert(p == parser);
1048
- strncat(messages[num_messages].query_string, buf, len);
1049
- return 0;
1995
+ size_t slen;
1996
+ size_t ncpy;
1997
+
1998
+ slen = strnlen(src, n);
1999
+
2000
+ if (len > 0) {
2001
+ ncpy = slen < len ? slen : (len - 1);
2002
+ memcpy(dst, src, ncpy);
2003
+ dst[ncpy] = '\0';
2004
+ }
2005
+
2006
+ assert(len > slen);
2007
+ return slen;
1050
2008
  }
1051
2009
 
1052
2010
  int
1053
- fragment_cb (http_parser *p, const char *buf, size_t len)
2011
+ request_url_cb (http_parser *p, const char *buf, size_t len)
1054
2012
  {
1055
2013
  assert(p == parser);
1056
- strncat(messages[num_messages].fragment, buf, len);
2014
+ strlncat(messages[num_messages].request_url,
2015
+ sizeof(messages[num_messages].request_url),
2016
+ buf,
2017
+ len);
1057
2018
  return 0;
1058
2019
  }
1059
2020
 
@@ -1066,7 +2027,10 @@ header_field_cb (http_parser *p, const char *buf, size_t len)
1066
2027
  if (m->last_header_element != FIELD)
1067
2028
  m->num_headers++;
1068
2029
 
1069
- strncat(m->headers[m->num_headers-1][0], buf, len);
2030
+ strlncat(m->headers[m->num_headers-1][0],
2031
+ sizeof(m->headers[m->num_headers-1][0]),
2032
+ buf,
2033
+ len);
1070
2034
 
1071
2035
  m->last_header_element = FIELD;
1072
2036
 
@@ -1079,19 +2043,39 @@ header_value_cb (http_parser *p, const char *buf, size_t len)
1079
2043
  assert(p == parser);
1080
2044
  struct message *m = &messages[num_messages];
1081
2045
 
1082
- strncat(m->headers[m->num_headers-1][1], buf, len);
2046
+ strlncat(m->headers[m->num_headers-1][1],
2047
+ sizeof(m->headers[m->num_headers-1][1]),
2048
+ buf,
2049
+ len);
1083
2050
 
1084
2051
  m->last_header_element = VALUE;
1085
2052
 
1086
2053
  return 0;
1087
2054
  }
1088
2055
 
2056
+ void
2057
+ check_body_is_final (const http_parser *p)
2058
+ {
2059
+ if (messages[num_messages].body_is_final) {
2060
+ fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
2061
+ "on last on_body callback call "
2062
+ "but it doesn't! ***\n\n");
2063
+ assert(0);
2064
+ abort();
2065
+ }
2066
+ messages[num_messages].body_is_final = http_body_is_final(p);
2067
+ }
2068
+
1089
2069
  int
1090
2070
  body_cb (http_parser *p, const char *buf, size_t len)
1091
2071
  {
1092
2072
  assert(p == parser);
1093
- strncat(messages[num_messages].body, buf, len);
2073
+ strlncat(messages[num_messages].body,
2074
+ sizeof(messages[num_messages].body),
2075
+ buf,
2076
+ len);
1094
2077
  messages[num_messages].body_size += len;
2078
+ check_body_is_final(p);
1095
2079
  // printf("body_cb: '%s'\n", requests[num_messages].body);
1096
2080
  return 0;
1097
2081
  }
@@ -1102,6 +2086,7 @@ count_body_cb (http_parser *p, const char *buf, size_t len)
1102
2086
  assert(p == parser);
1103
2087
  assert(buf);
1104
2088
  messages[num_messages].body_size += len;
2089
+ check_body_is_final(p);
1105
2090
  return 0;
1106
2091
  }
1107
2092
 
@@ -1136,53 +2121,330 @@ message_complete_cb (http_parser *p)
1136
2121
  "value in both on_message_complete and on_headers_complete "
1137
2122
  "but it doesn't! ***\n\n");
1138
2123
  assert(0);
1139
- exit(1);
2124
+ abort();
1140
2125
  }
2126
+
2127
+ if (messages[num_messages].body_size &&
2128
+ http_body_is_final(p) &&
2129
+ !messages[num_messages].body_is_final)
2130
+ {
2131
+ fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
2132
+ "on last on_body callback call "
2133
+ "but it doesn't! ***\n\n");
2134
+ assert(0);
2135
+ abort();
2136
+ }
2137
+
1141
2138
  messages[num_messages].message_complete_cb_called = TRUE;
1142
2139
 
1143
- messages[num_messages].message_complete_on_eof = currently_parsing_eof;
2140
+ messages[num_messages].message_complete_on_eof = currently_parsing_eof;
2141
+
2142
+ num_messages++;
2143
+ return 0;
2144
+ }
2145
+
2146
+ int
2147
+ response_status_cb (http_parser *p, const char *buf, size_t len)
2148
+ {
2149
+ assert(p == parser);
2150
+
2151
+ messages[num_messages].status_cb_called = TRUE;
2152
+
2153
+ strlncat(messages[num_messages].response_status,
2154
+ sizeof(messages[num_messages].response_status),
2155
+ buf,
2156
+ len);
2157
+ return 0;
2158
+ }
2159
+
2160
+ int
2161
+ chunk_header_cb (http_parser *p)
2162
+ {
2163
+ assert(p == parser);
2164
+ int chunk_idx = messages[num_messages].num_chunks;
2165
+ messages[num_messages].num_chunks++;
2166
+ if (chunk_idx < MAX_CHUNKS) {
2167
+ messages[num_messages].chunk_lengths[chunk_idx] = p->content_length;
2168
+ }
2169
+
2170
+ return 0;
2171
+ }
2172
+
2173
+ int
2174
+ chunk_complete_cb (http_parser *p)
2175
+ {
2176
+ assert(p == parser);
2177
+
2178
+ /* Here we want to verify that each chunk_header_cb is matched by a
2179
+ * chunk_complete_cb, so not only should the total number of calls to
2180
+ * both callbacks be the same, but they also should be interleaved
2181
+ * properly */
2182
+ assert(messages[num_messages].num_chunks ==
2183
+ messages[num_messages].num_chunks_complete + 1);
2184
+
2185
+ messages[num_messages].num_chunks_complete++;
2186
+ return 0;
2187
+ }
2188
+
2189
+ /* These dontcall_* callbacks exist so that we can verify that when we're
2190
+ * paused, no additional callbacks are invoked */
2191
+ int
2192
+ dontcall_message_begin_cb (http_parser *p)
2193
+ {
2194
+ if (p) { } // gcc
2195
+ fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
2196
+ abort();
2197
+ }
2198
+
2199
+ int
2200
+ dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
2201
+ {
2202
+ if (p || buf || len) { } // gcc
2203
+ fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
2204
+ abort();
2205
+ }
2206
+
2207
+ int
2208
+ dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
2209
+ {
2210
+ if (p || buf || len) { } // gcc
2211
+ fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
2212
+ abort();
2213
+ }
2214
+
2215
+ int
2216
+ dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
2217
+ {
2218
+ if (p || buf || len) { } // gcc
2219
+ fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
2220
+ abort();
2221
+ }
2222
+
2223
+ int
2224
+ dontcall_body_cb (http_parser *p, const char *buf, size_t len)
2225
+ {
2226
+ if (p || buf || len) { } // gcc
2227
+ fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
2228
+ abort();
2229
+ }
2230
+
2231
+ int
2232
+ dontcall_headers_complete_cb (http_parser *p)
2233
+ {
2234
+ if (p) { } // gcc
2235
+ fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
2236
+ "parser ***\n\n");
2237
+ abort();
2238
+ }
2239
+
2240
+ int
2241
+ dontcall_message_complete_cb (http_parser *p)
2242
+ {
2243
+ if (p) { } // gcc
2244
+ fprintf(stderr, "\n\n*** on_message_complete() called on paused "
2245
+ "parser ***\n\n");
2246
+ abort();
2247
+ }
2248
+
2249
+ int
2250
+ dontcall_response_status_cb (http_parser *p, const char *buf, size_t len)
2251
+ {
2252
+ if (p || buf || len) { } // gcc
2253
+ fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n");
2254
+ abort();
2255
+ }
2256
+
2257
+ int
2258
+ dontcall_chunk_header_cb (http_parser *p)
2259
+ {
2260
+ if (p) { } // gcc
2261
+ fprintf(stderr, "\n\n*** on_chunk_header() called on paused parser ***\n\n");
2262
+ exit(1);
2263
+ }
2264
+
2265
+ int
2266
+ dontcall_chunk_complete_cb (http_parser *p)
2267
+ {
2268
+ if (p) { } // gcc
2269
+ fprintf(stderr, "\n\n*** on_chunk_complete() "
2270
+ "called on paused parser ***\n\n");
2271
+ exit(1);
2272
+ }
2273
+
2274
+ static http_parser_settings settings_dontcall =
2275
+ {.on_message_begin = dontcall_message_begin_cb
2276
+ ,.on_header_field = dontcall_header_field_cb
2277
+ ,.on_header_value = dontcall_header_value_cb
2278
+ ,.on_url = dontcall_request_url_cb
2279
+ ,.on_status = dontcall_response_status_cb
2280
+ ,.on_body = dontcall_body_cb
2281
+ ,.on_headers_complete = dontcall_headers_complete_cb
2282
+ ,.on_message_complete = dontcall_message_complete_cb
2283
+ ,.on_chunk_header = dontcall_chunk_header_cb
2284
+ ,.on_chunk_complete = dontcall_chunk_complete_cb
2285
+ };
2286
+
2287
+ /* These pause_* callbacks always pause the parser and just invoke the regular
2288
+ * callback that tracks content. Before returning, we overwrite the parser
2289
+ * settings to point to the _dontcall variety so that we can verify that
2290
+ * the pause actually did, you know, pause. */
2291
+ int
2292
+ pause_message_begin_cb (http_parser *p)
2293
+ {
2294
+ http_parser_pause(p, 1);
2295
+ *current_pause_parser = settings_dontcall;
2296
+ return message_begin_cb(p);
2297
+ }
2298
+
2299
+ int
2300
+ pause_header_field_cb (http_parser *p, const char *buf, size_t len)
2301
+ {
2302
+ http_parser_pause(p, 1);
2303
+ *current_pause_parser = settings_dontcall;
2304
+ return header_field_cb(p, buf, len);
2305
+ }
2306
+
2307
+ int
2308
+ pause_header_value_cb (http_parser *p, const char *buf, size_t len)
2309
+ {
2310
+ http_parser_pause(p, 1);
2311
+ *current_pause_parser = settings_dontcall;
2312
+ return header_value_cb(p, buf, len);
2313
+ }
2314
+
2315
+ int
2316
+ pause_request_url_cb (http_parser *p, const char *buf, size_t len)
2317
+ {
2318
+ http_parser_pause(p, 1);
2319
+ *current_pause_parser = settings_dontcall;
2320
+ return request_url_cb(p, buf, len);
2321
+ }
2322
+
2323
+ int
2324
+ pause_body_cb (http_parser *p, const char *buf, size_t len)
2325
+ {
2326
+ http_parser_pause(p, 1);
2327
+ *current_pause_parser = settings_dontcall;
2328
+ return body_cb(p, buf, len);
2329
+ }
2330
+
2331
+ int
2332
+ pause_headers_complete_cb (http_parser *p)
2333
+ {
2334
+ http_parser_pause(p, 1);
2335
+ *current_pause_parser = settings_dontcall;
2336
+ return headers_complete_cb(p);
2337
+ }
2338
+
2339
+ int
2340
+ pause_message_complete_cb (http_parser *p)
2341
+ {
2342
+ http_parser_pause(p, 1);
2343
+ *current_pause_parser = settings_dontcall;
2344
+ return message_complete_cb(p);
2345
+ }
2346
+
2347
+ int
2348
+ pause_response_status_cb (http_parser *p, const char *buf, size_t len)
2349
+ {
2350
+ http_parser_pause(p, 1);
2351
+ *current_pause_parser = settings_dontcall;
2352
+ return response_status_cb(p, buf, len);
2353
+ }
2354
+
2355
+ int
2356
+ pause_chunk_header_cb (http_parser *p)
2357
+ {
2358
+ http_parser_pause(p, 1);
2359
+ *current_pause_parser = settings_dontcall;
2360
+ return chunk_header_cb(p);
2361
+ }
2362
+
2363
+ int
2364
+ pause_chunk_complete_cb (http_parser *p)
2365
+ {
2366
+ http_parser_pause(p, 1);
2367
+ *current_pause_parser = settings_dontcall;
2368
+ return chunk_complete_cb(p);
2369
+ }
2370
+
2371
+ int
2372
+ connect_headers_complete_cb (http_parser *p)
2373
+ {
2374
+ headers_complete_cb(p);
2375
+ return 1;
2376
+ }
1144
2377
 
1145
- num_messages++;
1146
- return 0;
2378
+ int
2379
+ connect_message_complete_cb (http_parser *p)
2380
+ {
2381
+ messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
2382
+ return message_complete_cb(p);
1147
2383
  }
1148
2384
 
2385
+ static http_parser_settings settings_pause =
2386
+ {.on_message_begin = pause_message_begin_cb
2387
+ ,.on_header_field = pause_header_field_cb
2388
+ ,.on_header_value = pause_header_value_cb
2389
+ ,.on_url = pause_request_url_cb
2390
+ ,.on_status = pause_response_status_cb
2391
+ ,.on_body = pause_body_cb
2392
+ ,.on_headers_complete = pause_headers_complete_cb
2393
+ ,.on_message_complete = pause_message_complete_cb
2394
+ ,.on_chunk_header = pause_chunk_header_cb
2395
+ ,.on_chunk_complete = pause_chunk_complete_cb
2396
+ };
2397
+
1149
2398
  static http_parser_settings settings =
1150
2399
  {.on_message_begin = message_begin_cb
1151
2400
  ,.on_header_field = header_field_cb
1152
2401
  ,.on_header_value = header_value_cb
1153
- ,.on_path = request_path_cb
1154
2402
  ,.on_url = request_url_cb
1155
- ,.on_fragment = fragment_cb
1156
- ,.on_query_string = query_string_cb
2403
+ ,.on_status = response_status_cb
1157
2404
  ,.on_body = body_cb
1158
2405
  ,.on_headers_complete = headers_complete_cb
1159
2406
  ,.on_message_complete = message_complete_cb
2407
+ ,.on_chunk_header = chunk_header_cb
2408
+ ,.on_chunk_complete = chunk_complete_cb
1160
2409
  };
1161
2410
 
1162
2411
  static http_parser_settings settings_count_body =
1163
2412
  {.on_message_begin = message_begin_cb
1164
2413
  ,.on_header_field = header_field_cb
1165
2414
  ,.on_header_value = header_value_cb
1166
- ,.on_path = request_path_cb
1167
2415
  ,.on_url = request_url_cb
1168
- ,.on_fragment = fragment_cb
1169
- ,.on_query_string = query_string_cb
2416
+ ,.on_status = response_status_cb
1170
2417
  ,.on_body = count_body_cb
1171
2418
  ,.on_headers_complete = headers_complete_cb
1172
2419
  ,.on_message_complete = message_complete_cb
2420
+ ,.on_chunk_header = chunk_header_cb
2421
+ ,.on_chunk_complete = chunk_complete_cb
2422
+ };
2423
+
2424
+ static http_parser_settings settings_connect =
2425
+ {.on_message_begin = message_begin_cb
2426
+ ,.on_header_field = header_field_cb
2427
+ ,.on_header_value = header_value_cb
2428
+ ,.on_url = request_url_cb
2429
+ ,.on_status = response_status_cb
2430
+ ,.on_body = dontcall_body_cb
2431
+ ,.on_headers_complete = connect_headers_complete_cb
2432
+ ,.on_message_complete = connect_message_complete_cb
2433
+ ,.on_chunk_header = chunk_header_cb
2434
+ ,.on_chunk_complete = chunk_complete_cb
1173
2435
  };
1174
2436
 
1175
2437
  static http_parser_settings settings_null =
1176
2438
  {.on_message_begin = 0
1177
2439
  ,.on_header_field = 0
1178
2440
  ,.on_header_value = 0
1179
- ,.on_path = 0
1180
2441
  ,.on_url = 0
1181
- ,.on_fragment = 0
1182
- ,.on_query_string = 0
2442
+ ,.on_status = 0
1183
2443
  ,.on_body = 0
1184
2444
  ,.on_headers_complete = 0
1185
2445
  ,.on_message_complete = 0
2446
+ ,.on_chunk_header = 0
2447
+ ,.on_chunk_complete = 0
1186
2448
  };
1187
2449
 
1188
2450
  void
@@ -1224,12 +2486,37 @@ size_t parse_count_body (const char *buf, size_t len)
1224
2486
  return nparsed;
1225
2487
  }
1226
2488
 
2489
+ size_t parse_pause (const char *buf, size_t len)
2490
+ {
2491
+ size_t nparsed;
2492
+ http_parser_settings s = settings_pause;
2493
+
2494
+ currently_parsing_eof = (len == 0);
2495
+ current_pause_parser = &s;
2496
+ nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
2497
+ return nparsed;
2498
+ }
2499
+
2500
+ size_t parse_connect (const char *buf, size_t len)
2501
+ {
2502
+ size_t nparsed;
2503
+ currently_parsing_eof = (len == 0);
2504
+ nparsed = http_parser_execute(parser, &settings_connect, buf, len);
2505
+ return nparsed;
2506
+ }
2507
+
1227
2508
  static inline int
1228
2509
  check_str_eq (const struct message *m,
1229
2510
  const char *prop,
1230
2511
  const char *expected,
1231
2512
  const char *found) {
1232
- if (0 != strcmp(expected, found)) {
2513
+ if ((expected == NULL) != (found == NULL)) {
2514
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
2515
+ printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
2516
+ printf(" found %s\n", (found == NULL) ? "NULL" : found);
2517
+ return 0;
2518
+ }
2519
+ if (expected != NULL && 0 != strcmp(expected, found)) {
1233
2520
  printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1234
2521
  printf("expected '%s'\n", expected);
1235
2522
  printf(" found '%s'\n", found);
@@ -1258,9 +2545,23 @@ check_num_eq (const struct message *m,
1258
2545
  #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1259
2546
  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1260
2547
 
2548
+ #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
2549
+ do { \
2550
+ char ubuf[256]; \
2551
+ \
2552
+ if ((u)->field_set & (1 << (fn))) { \
2553
+ memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
2554
+ (u)->field_data[(fn)].len); \
2555
+ ubuf[(u)->field_data[(fn)].len] = '\0'; \
2556
+ } else { \
2557
+ ubuf[0] = '\0'; \
2558
+ } \
2559
+ \
2560
+ check_str_eq(expected, #prop, expected->prop, ubuf); \
2561
+ } while(0)
1261
2562
 
1262
2563
  int
1263
- message_eq (int index, const struct message *expected)
2564
+ message_eq (int index, int connect, const struct message *expected)
1264
2565
  {
1265
2566
  int i;
1266
2567
  struct message *m = &messages[index];
@@ -1272,80 +2573,820 @@ message_eq (int index, const struct message *expected)
1272
2573
  MESSAGE_CHECK_NUM_EQ(expected, m, method);
1273
2574
  } else {
1274
2575
  MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
2576
+ MESSAGE_CHECK_STR_EQ(expected, m, response_status);
2577
+ assert(m->status_cb_called);
1275
2578
  }
1276
2579
 
1277
- MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
1278
- MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
2580
+ if (!connect) {
2581
+ MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
2582
+ MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
2583
+ }
1279
2584
 
1280
2585
  assert(m->message_begin_cb_called);
1281
2586
  assert(m->headers_complete_cb_called);
1282
2587
  assert(m->message_complete_cb_called);
1283
2588
 
1284
2589
 
1285
- MESSAGE_CHECK_STR_EQ(expected, m, request_path);
1286
- MESSAGE_CHECK_STR_EQ(expected, m, query_string);
1287
- MESSAGE_CHECK_STR_EQ(expected, m, fragment);
1288
2590
  MESSAGE_CHECK_STR_EQ(expected, m, request_url);
1289
- if (expected->body_size) {
2591
+
2592
+ /* Check URL components; we can't do this w/ CONNECT since it doesn't
2593
+ * send us a well-formed URL.
2594
+ */
2595
+ if (*m->request_url && m->method != HTTP_CONNECT) {
2596
+ struct http_parser_url u;
2597
+
2598
+ if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
2599
+ fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
2600
+ m->request_url);
2601
+ abort();
2602
+ }
2603
+
2604
+ if (expected->host) {
2605
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
2606
+ }
2607
+
2608
+ if (expected->userinfo) {
2609
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
2610
+ }
2611
+
2612
+ m->port = (u.field_set & (1 << UF_PORT)) ?
2613
+ u.port : 0;
2614
+
2615
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
2616
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
2617
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
2618
+ MESSAGE_CHECK_NUM_EQ(expected, m, port);
2619
+ }
2620
+
2621
+ if (connect) {
2622
+ check_num_eq(m, "body_size", 0, m->body_size);
2623
+ } else if (expected->body_size) {
1290
2624
  MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1291
2625
  } else {
1292
2626
  MESSAGE_CHECK_STR_EQ(expected, m, body);
1293
2627
  }
1294
2628
 
1295
- MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
2629
+ if (connect) {
2630
+ check_num_eq(m, "num_chunks_complete", 0, m->num_chunks_complete);
2631
+ } else {
2632
+ assert(m->num_chunks == m->num_chunks_complete);
2633
+ MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete);
2634
+ for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) {
2635
+ MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]);
2636
+ }
2637
+ }
2638
+
2639
+ MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
2640
+
2641
+ int r;
2642
+ for (i = 0; i < m->num_headers; i++) {
2643
+ r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
2644
+ if (!r) return 0;
2645
+ r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
2646
+ if (!r) return 0;
2647
+ }
2648
+
2649
+ if (!connect) {
2650
+ MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
2651
+ }
2652
+
2653
+ return 1;
2654
+ }
2655
+
2656
+ /* Given a sequence of varargs messages, return the number of them that the
2657
+ * parser should successfully parse, taking into account that upgraded
2658
+ * messages prevent all subsequent messages from being parsed.
2659
+ */
2660
+ size_t
2661
+ count_parsed_messages(const size_t nmsgs, ...) {
2662
+ size_t i;
2663
+ va_list ap;
2664
+
2665
+ va_start(ap, nmsgs);
2666
+
2667
+ for (i = 0; i < nmsgs; i++) {
2668
+ struct message *m = va_arg(ap, struct message *);
2669
+
2670
+ if (m->upgrade) {
2671
+ va_end(ap);
2672
+ return i + 1;
2673
+ }
2674
+ }
2675
+
2676
+ va_end(ap);
2677
+ return nmsgs;
2678
+ }
2679
+
2680
+ /* Given a sequence of bytes and the number of these that we were able to
2681
+ * parse, verify that upgrade bodies are correct.
2682
+ */
2683
+ void
2684
+ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2685
+ va_list ap;
2686
+ size_t i;
2687
+ size_t off = 0;
2688
+
2689
+ va_start(ap, nmsgs);
2690
+
2691
+ for (i = 0; i < nmsgs; i++) {
2692
+ struct message *m = va_arg(ap, struct message *);
2693
+
2694
+ off += strlen(m->raw);
2695
+
2696
+ if (m->upgrade) {
2697
+ off -= strlen(m->upgrade);
2698
+
2699
+ /* Check the portion of the response after its specified upgrade */
2700
+ if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2701
+ abort();
2702
+ }
2703
+
2704
+ /* Fix up the response so that message_eq() will verify the beginning
2705
+ * of the upgrade */
2706
+ *(body + nread + strlen(m->upgrade)) = '\0';
2707
+ messages[num_messages -1 ].upgrade = body + nread;
2708
+
2709
+ va_end(ap);
2710
+ return;
2711
+ }
2712
+ }
2713
+
2714
+ va_end(ap);
2715
+ printf("\n\n*** Error: expected a message with upgrade ***\n");
2716
+
2717
+ abort();
2718
+ }
2719
+
2720
+ static void
2721
+ print_error (const char *raw, size_t error_location)
2722
+ {
2723
+ fprintf(stderr, "\n*** %s ***\n\n",
2724
+ http_errno_description(HTTP_PARSER_ERRNO(parser)));
2725
+
2726
+ int this_line = 0, char_len = 0;
2727
+ size_t i, j, len = strlen(raw), error_location_line = 0;
2728
+ for (i = 0; i < len; i++) {
2729
+ if (i == error_location) this_line = 1;
2730
+ switch (raw[i]) {
2731
+ case '\r':
2732
+ char_len = 2;
2733
+ fprintf(stderr, "\\r");
2734
+ break;
2735
+
2736
+ case '\n':
2737
+ fprintf(stderr, "\\n\n");
2738
+
2739
+ if (this_line) goto print;
2740
+
2741
+ error_location_line = 0;
2742
+ continue;
2743
+
2744
+ default:
2745
+ char_len = 1;
2746
+ fputc(raw[i], stderr);
2747
+ break;
2748
+ }
2749
+ if (!this_line) error_location_line += char_len;
2750
+ }
2751
+
2752
+ fprintf(stderr, "[eof]\n");
2753
+
2754
+ print:
2755
+ for (j = 0; j < error_location_line; j++) {
2756
+ fputc(' ', stderr);
2757
+ }
2758
+ fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2759
+ }
2760
+
2761
+ void
2762
+ test_preserve_data (void)
2763
+ {
2764
+ char my_data[] = "application-specific data";
2765
+ http_parser parser;
2766
+ parser.data = my_data;
2767
+ http_parser_init(&parser, HTTP_REQUEST);
2768
+ if (parser.data != my_data) {
2769
+ printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2770
+ abort();
2771
+ }
2772
+ }
2773
+
2774
+ struct url_test {
2775
+ const char *name;
2776
+ const char *url;
2777
+ int is_connect;
2778
+ struct http_parser_url u;
2779
+ int rv;
2780
+ };
2781
+
2782
+ const struct url_test url_tests[] =
2783
+ { {.name="proxy request"
2784
+ ,.url="http://hostname/"
2785
+ ,.is_connect=0
2786
+ ,.u=
2787
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2788
+ ,.port=0
2789
+ ,.field_data=
2790
+ {{ 0, 4 } /* UF_SCHEMA */
2791
+ ,{ 7, 8 } /* UF_HOST */
2792
+ ,{ 0, 0 } /* UF_PORT */
2793
+ ,{ 15, 1 } /* UF_PATH */
2794
+ ,{ 0, 0 } /* UF_QUERY */
2795
+ ,{ 0, 0 } /* UF_FRAGMENT */
2796
+ ,{ 0, 0 } /* UF_USERINFO */
2797
+ }
2798
+ }
2799
+ ,.rv=0
2800
+ }
2801
+
2802
+ , {.name="proxy request with port"
2803
+ ,.url="http://hostname:444/"
2804
+ ,.is_connect=0
2805
+ ,.u=
2806
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2807
+ ,.port=444
2808
+ ,.field_data=
2809
+ {{ 0, 4 } /* UF_SCHEMA */
2810
+ ,{ 7, 8 } /* UF_HOST */
2811
+ ,{ 16, 3 } /* UF_PORT */
2812
+ ,{ 19, 1 } /* UF_PATH */
2813
+ ,{ 0, 0 } /* UF_QUERY */
2814
+ ,{ 0, 0 } /* UF_FRAGMENT */
2815
+ ,{ 0, 0 } /* UF_USERINFO */
2816
+ }
2817
+ }
2818
+ ,.rv=0
2819
+ }
2820
+
2821
+ , {.name="CONNECT request"
2822
+ ,.url="hostname:443"
2823
+ ,.is_connect=1
2824
+ ,.u=
2825
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2826
+ ,.port=443
2827
+ ,.field_data=
2828
+ {{ 0, 0 } /* UF_SCHEMA */
2829
+ ,{ 0, 8 } /* UF_HOST */
2830
+ ,{ 9, 3 } /* UF_PORT */
2831
+ ,{ 0, 0 } /* UF_PATH */
2832
+ ,{ 0, 0 } /* UF_QUERY */
2833
+ ,{ 0, 0 } /* UF_FRAGMENT */
2834
+ ,{ 0, 0 } /* UF_USERINFO */
2835
+ }
2836
+ }
2837
+ ,.rv=0
2838
+ }
2839
+
2840
+ , {.name="CONNECT request but not connect"
2841
+ ,.url="hostname:443"
2842
+ ,.is_connect=0
2843
+ ,.rv=1
2844
+ }
2845
+
2846
+ , {.name="proxy ipv6 request"
2847
+ ,.url="http://[1:2::3:4]/"
2848
+ ,.is_connect=0
2849
+ ,.u=
2850
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2851
+ ,.port=0
2852
+ ,.field_data=
2853
+ {{ 0, 4 } /* UF_SCHEMA */
2854
+ ,{ 8, 8 } /* UF_HOST */
2855
+ ,{ 0, 0 } /* UF_PORT */
2856
+ ,{ 17, 1 } /* UF_PATH */
2857
+ ,{ 0, 0 } /* UF_QUERY */
2858
+ ,{ 0, 0 } /* UF_FRAGMENT */
2859
+ ,{ 0, 0 } /* UF_USERINFO */
2860
+ }
2861
+ }
2862
+ ,.rv=0
2863
+ }
2864
+
2865
+ , {.name="proxy ipv6 request with port"
2866
+ ,.url="http://[1:2::3:4]:67/"
2867
+ ,.is_connect=0
2868
+ ,.u=
2869
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2870
+ ,.port=67
2871
+ ,.field_data=
2872
+ {{ 0, 4 } /* UF_SCHEMA */
2873
+ ,{ 8, 8 } /* UF_HOST */
2874
+ ,{ 18, 2 } /* UF_PORT */
2875
+ ,{ 20, 1 } /* UF_PATH */
2876
+ ,{ 0, 0 } /* UF_QUERY */
2877
+ ,{ 0, 0 } /* UF_FRAGMENT */
2878
+ ,{ 0, 0 } /* UF_USERINFO */
2879
+ }
2880
+ }
2881
+ ,.rv=0
2882
+ }
2883
+
2884
+ , {.name="CONNECT ipv6 address"
2885
+ ,.url="[1:2::3:4]:443"
2886
+ ,.is_connect=1
2887
+ ,.u=
2888
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2889
+ ,.port=443
2890
+ ,.field_data=
2891
+ {{ 0, 0 } /* UF_SCHEMA */
2892
+ ,{ 1, 8 } /* UF_HOST */
2893
+ ,{ 11, 3 } /* UF_PORT */
2894
+ ,{ 0, 0 } /* UF_PATH */
2895
+ ,{ 0, 0 } /* UF_QUERY */
2896
+ ,{ 0, 0 } /* UF_FRAGMENT */
2897
+ ,{ 0, 0 } /* UF_USERINFO */
2898
+ }
2899
+ }
2900
+ ,.rv=0
2901
+ }
2902
+
2903
+ , {.name="ipv4 in ipv6 address"
2904
+ ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2905
+ ,.is_connect=0
2906
+ ,.u=
2907
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2908
+ ,.port=0
2909
+ ,.field_data=
2910
+ {{ 0, 4 } /* UF_SCHEMA */
2911
+ ,{ 8, 37 } /* UF_HOST */
2912
+ ,{ 0, 0 } /* UF_PORT */
2913
+ ,{ 46, 1 } /* UF_PATH */
2914
+ ,{ 0, 0 } /* UF_QUERY */
2915
+ ,{ 0, 0 } /* UF_FRAGMENT */
2916
+ ,{ 0, 0 } /* UF_USERINFO */
2917
+ }
2918
+ }
2919
+ ,.rv=0
2920
+ }
2921
+
2922
+ , {.name="extra ? in query string"
2923
+ ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
2924
+ "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
2925
+ "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2926
+ ,.is_connect=0
2927
+ ,.u=
2928
+ {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2929
+ ,.port=0
2930
+ ,.field_data=
2931
+ {{ 0, 4 } /* UF_SCHEMA */
2932
+ ,{ 7, 10 } /* UF_HOST */
2933
+ ,{ 0, 0 } /* UF_PORT */
2934
+ ,{ 17, 12 } /* UF_PATH */
2935
+ ,{ 30,187 } /* UF_QUERY */
2936
+ ,{ 0, 0 } /* UF_FRAGMENT */
2937
+ ,{ 0, 0 } /* UF_USERINFO */
2938
+ }
2939
+ }
2940
+ ,.rv=0
2941
+ }
2942
+
2943
+ , {.name="space URL encoded"
2944
+ ,.url="/toto.html?toto=a%20b"
2945
+ ,.is_connect=0
2946
+ ,.u=
2947
+ {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2948
+ ,.port=0
2949
+ ,.field_data=
2950
+ {{ 0, 0 } /* UF_SCHEMA */
2951
+ ,{ 0, 0 } /* UF_HOST */
2952
+ ,{ 0, 0 } /* UF_PORT */
2953
+ ,{ 0, 10 } /* UF_PATH */
2954
+ ,{ 11, 10 } /* UF_QUERY */
2955
+ ,{ 0, 0 } /* UF_FRAGMENT */
2956
+ ,{ 0, 0 } /* UF_USERINFO */
2957
+ }
2958
+ }
2959
+ ,.rv=0
2960
+ }
2961
+
2962
+
2963
+ , {.name="URL fragment"
2964
+ ,.url="/toto.html#titi"
2965
+ ,.is_connect=0
2966
+ ,.u=
2967
+ {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2968
+ ,.port=0
2969
+ ,.field_data=
2970
+ {{ 0, 0 } /* UF_SCHEMA */
2971
+ ,{ 0, 0 } /* UF_HOST */
2972
+ ,{ 0, 0 } /* UF_PORT */
2973
+ ,{ 0, 10 } /* UF_PATH */
2974
+ ,{ 0, 0 } /* UF_QUERY */
2975
+ ,{ 11, 4 } /* UF_FRAGMENT */
2976
+ ,{ 0, 0 } /* UF_USERINFO */
2977
+ }
2978
+ }
2979
+ ,.rv=0
2980
+ }
2981
+
2982
+ , {.name="complex URL fragment"
2983
+ ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
2984
+ "http://www.example.com/index.html?foo=bar&hello=world#midpage"
2985
+ ,.is_connect=0
2986
+ ,.u=
2987
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2988
+ (1<<UF_FRAGMENT)
2989
+ ,.port=0
2990
+ ,.field_data=
2991
+ {{ 0, 4 } /* UF_SCHEMA */
2992
+ ,{ 7, 22 } /* UF_HOST */
2993
+ ,{ 0, 0 } /* UF_PORT */
2994
+ ,{ 29, 6 } /* UF_PATH */
2995
+ ,{ 36, 69 } /* UF_QUERY */
2996
+ ,{106, 7 } /* UF_FRAGMENT */
2997
+ ,{ 0, 0 } /* UF_USERINFO */
2998
+ }
2999
+ }
3000
+ ,.rv=0
3001
+ }
3002
+
3003
+ , {.name="complex URL from node js url parser doc"
3004
+ ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
3005
+ ,.is_connect=0
3006
+ ,.u=
3007
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
3008
+ (1<<UF_QUERY) | (1<<UF_FRAGMENT)
3009
+ ,.port=8080
3010
+ ,.field_data=
3011
+ {{ 0, 4 } /* UF_SCHEMA */
3012
+ ,{ 7, 8 } /* UF_HOST */
3013
+ ,{ 16, 4 } /* UF_PORT */
3014
+ ,{ 20, 8 } /* UF_PATH */
3015
+ ,{ 29, 12 } /* UF_QUERY */
3016
+ ,{ 42, 4 } /* UF_FRAGMENT */
3017
+ ,{ 0, 0 } /* UF_USERINFO */
3018
+ }
3019
+ }
3020
+ ,.rv=0
3021
+ }
3022
+
3023
+ , {.name="complex URL with basic auth from node js url parser doc"
3024
+ ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
3025
+ ,.is_connect=0
3026
+ ,.u=
3027
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
3028
+ (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
3029
+ ,.port=8080
3030
+ ,.field_data=
3031
+ {{ 0, 4 } /* UF_SCHEMA */
3032
+ ,{ 11, 8 } /* UF_HOST */
3033
+ ,{ 20, 4 } /* UF_PORT */
3034
+ ,{ 24, 8 } /* UF_PATH */
3035
+ ,{ 33, 12 } /* UF_QUERY */
3036
+ ,{ 46, 4 } /* UF_FRAGMENT */
3037
+ ,{ 7, 3 } /* UF_USERINFO */
3038
+ }
3039
+ }
3040
+ ,.rv=0
3041
+ }
3042
+
3043
+ , {.name="double @"
3044
+ ,.url="http://a:b@@hostname:443/"
3045
+ ,.is_connect=0
3046
+ ,.rv=1
3047
+ }
3048
+
3049
+ , {.name="proxy empty host"
3050
+ ,.url="http://:443/"
3051
+ ,.is_connect=0
3052
+ ,.rv=1
3053
+ }
3054
+
3055
+ , {.name="proxy empty port"
3056
+ ,.url="http://hostname:/"
3057
+ ,.is_connect=0
3058
+ ,.rv=1
3059
+ }
3060
+
3061
+ , {.name="CONNECT with basic auth"
3062
+ ,.url="a:b@hostname:443"
3063
+ ,.is_connect=1
3064
+ ,.rv=1
3065
+ }
3066
+
3067
+ , {.name="CONNECT empty host"
3068
+ ,.url=":443"
3069
+ ,.is_connect=1
3070
+ ,.rv=1
3071
+ }
3072
+
3073
+ , {.name="CONNECT empty port"
3074
+ ,.url="hostname:"
3075
+ ,.is_connect=1
3076
+ ,.rv=1
3077
+ }
3078
+
3079
+ , {.name="CONNECT with extra bits"
3080
+ ,.url="hostname:443/"
3081
+ ,.is_connect=1
3082
+ ,.rv=1
3083
+ }
3084
+
3085
+ , {.name="space in URL"
3086
+ ,.url="/foo bar/"
3087
+ ,.rv=1 /* s_dead */
3088
+ }
3089
+
3090
+ , {.name="proxy basic auth with space url encoded"
3091
+ ,.url="http://a%20:b@host.com/"
3092
+ ,.is_connect=0
3093
+ ,.u=
3094
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
3095
+ ,.port=0
3096
+ ,.field_data=
3097
+ {{ 0, 4 } /* UF_SCHEMA */
3098
+ ,{ 14, 8 } /* UF_HOST */
3099
+ ,{ 0, 0 } /* UF_PORT */
3100
+ ,{ 22, 1 } /* UF_PATH */
3101
+ ,{ 0, 0 } /* UF_QUERY */
3102
+ ,{ 0, 0 } /* UF_FRAGMENT */
3103
+ ,{ 7, 6 } /* UF_USERINFO */
3104
+ }
3105
+ }
3106
+ ,.rv=0
3107
+ }
3108
+
3109
+ , {.name="carriage return in URL"
3110
+ ,.url="/foo\rbar/"
3111
+ ,.rv=1 /* s_dead */
3112
+ }
3113
+
3114
+ , {.name="proxy double : in URL"
3115
+ ,.url="http://hostname::443/"
3116
+ ,.rv=1 /* s_dead */
3117
+ }
3118
+
3119
+ , {.name="proxy basic auth with double :"
3120
+ ,.url="http://a::b@host.com/"
3121
+ ,.is_connect=0
3122
+ ,.u=
3123
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
3124
+ ,.port=0
3125
+ ,.field_data=
3126
+ {{ 0, 4 } /* UF_SCHEMA */
3127
+ ,{ 12, 8 } /* UF_HOST */
3128
+ ,{ 0, 0 } /* UF_PORT */
3129
+ ,{ 20, 1 } /* UF_PATH */
3130
+ ,{ 0, 0 } /* UF_QUERY */
3131
+ ,{ 0, 0 } /* UF_FRAGMENT */
3132
+ ,{ 7, 4 } /* UF_USERINFO */
3133
+ }
3134
+ }
3135
+ ,.rv=0
3136
+ }
3137
+
3138
+ , {.name="line feed in URL"
3139
+ ,.url="/foo\nbar/"
3140
+ ,.rv=1 /* s_dead */
3141
+ }
3142
+
3143
+ , {.name="proxy empty basic auth"
3144
+ ,.url="http://@hostname/fo"
3145
+ ,.u=
3146
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
3147
+ ,.port=0
3148
+ ,.field_data=
3149
+ {{ 0, 4 } /* UF_SCHEMA */
3150
+ ,{ 8, 8 } /* UF_HOST */
3151
+ ,{ 0, 0 } /* UF_PORT */
3152
+ ,{ 16, 3 } /* UF_PATH */
3153
+ ,{ 0, 0 } /* UF_QUERY */
3154
+ ,{ 0, 0 } /* UF_FRAGMENT */
3155
+ ,{ 0, 0 } /* UF_USERINFO */
3156
+ }
3157
+ }
3158
+ ,.rv=0
3159
+ }
3160
+ , {.name="proxy line feed in hostname"
3161
+ ,.url="http://host\name/fo"
3162
+ ,.rv=1 /* s_dead */
3163
+ }
3164
+
3165
+ , {.name="proxy % in hostname"
3166
+ ,.url="http://host%name/fo"
3167
+ ,.rv=1 /* s_dead */
3168
+ }
3169
+
3170
+ , {.name="proxy ; in hostname"
3171
+ ,.url="http://host;ame/fo"
3172
+ ,.rv=1 /* s_dead */
3173
+ }
3174
+
3175
+ , {.name="proxy basic auth with unreservedchars"
3176
+ ,.url="http://a!;-_!=+$@host.com/"
3177
+ ,.is_connect=0
3178
+ ,.u=
3179
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
3180
+ ,.port=0
3181
+ ,.field_data=
3182
+ {{ 0, 4 } /* UF_SCHEMA */
3183
+ ,{ 17, 8 } /* UF_HOST */
3184
+ ,{ 0, 0 } /* UF_PORT */
3185
+ ,{ 25, 1 } /* UF_PATH */
3186
+ ,{ 0, 0 } /* UF_QUERY */
3187
+ ,{ 0, 0 } /* UF_FRAGMENT */
3188
+ ,{ 7, 9 } /* UF_USERINFO */
3189
+ }
3190
+ }
3191
+ ,.rv=0
3192
+ }
3193
+
3194
+ , {.name="proxy only empty basic auth"
3195
+ ,.url="http://@/fo"
3196
+ ,.rv=1 /* s_dead */
3197
+ }
3198
+
3199
+ , {.name="proxy only basic auth"
3200
+ ,.url="http://toto@/fo"
3201
+ ,.rv=1 /* s_dead */
3202
+ }
3203
+
3204
+ , {.name="proxy emtpy hostname"
3205
+ ,.url="http:///fo"
3206
+ ,.rv=1 /* s_dead */
3207
+ }
3208
+
3209
+ , {.name="proxy = in URL"
3210
+ ,.url="http://host=ame/fo"
3211
+ ,.rv=1 /* s_dead */
3212
+ }
3213
+
3214
+ , {.name="ipv6 address with Zone ID"
3215
+ ,.url="http://[fe80::a%25eth0]/"
3216
+ ,.is_connect=0
3217
+ ,.u=
3218
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
3219
+ ,.port=0
3220
+ ,.field_data=
3221
+ {{ 0, 4 } /* UF_SCHEMA */
3222
+ ,{ 8, 14 } /* UF_HOST */
3223
+ ,{ 0, 0 } /* UF_PORT */
3224
+ ,{ 23, 1 } /* UF_PATH */
3225
+ ,{ 0, 0 } /* UF_QUERY */
3226
+ ,{ 0, 0 } /* UF_FRAGMENT */
3227
+ ,{ 0, 0 } /* UF_USERINFO */
3228
+ }
3229
+ }
3230
+ ,.rv=0
3231
+ }
3232
+
3233
+ , {.name="ipv6 address with Zone ID, but '%' is not percent-encoded"
3234
+ ,.url="http://[fe80::a%eth0]/"
3235
+ ,.is_connect=0
3236
+ ,.u=
3237
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
3238
+ ,.port=0
3239
+ ,.field_data=
3240
+ {{ 0, 4 } /* UF_SCHEMA */
3241
+ ,{ 8, 12 } /* UF_HOST */
3242
+ ,{ 0, 0 } /* UF_PORT */
3243
+ ,{ 21, 1 } /* UF_PATH */
3244
+ ,{ 0, 0 } /* UF_QUERY */
3245
+ ,{ 0, 0 } /* UF_FRAGMENT */
3246
+ ,{ 0, 0 } /* UF_USERINFO */
3247
+ }
3248
+ }
3249
+ ,.rv=0
3250
+ }
3251
+
3252
+ , {.name="ipv6 address ending with '%'"
3253
+ ,.url="http://[fe80::a%]/"
3254
+ ,.rv=1 /* s_dead */
3255
+ }
3256
+
3257
+ , {.name="ipv6 address with Zone ID including bad character"
3258
+ ,.url="http://[fe80::a%$HOME]/"
3259
+ ,.rv=1 /* s_dead */
3260
+ }
1296
3261
 
1297
- int r;
1298
- for (i = 0; i < m->num_headers; i++) {
1299
- r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
1300
- if (!r) return 0;
1301
- r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
1302
- if (!r) return 0;
3262
+ , {.name="just ipv6 Zone ID"
3263
+ ,.url="http://[%eth0]/"
3264
+ ,.rv=1 /* s_dead */
1303
3265
  }
1304
3266
 
1305
- return 1;
1306
- }
3267
+ #if HTTP_PARSER_STRICT
1307
3268
 
1308
- static void
1309
- print_error (const char *raw, size_t error_location)
1310
- {
1311
- fprintf(stderr, "\n*** parse error ***\n\n");
3269
+ , {.name="tab in URL"
3270
+ ,.url="/foo\tbar/"
3271
+ ,.rv=1 /* s_dead */
3272
+ }
1312
3273
 
1313
- int this_line = 0, char_len = 0;
1314
- size_t i, j, len = strlen(raw), error_location_line = 0;
1315
- for (i = 0; i < len; i++) {
1316
- if (i == error_location) this_line = 1;
1317
- switch (raw[i]) {
1318
- case '\r':
1319
- char_len = 2;
1320
- fprintf(stderr, "\\r");
1321
- break;
3274
+ , {.name="form feed in URL"
3275
+ ,.url="/foo\fbar/"
3276
+ ,.rv=1 /* s_dead */
3277
+ }
1322
3278
 
1323
- case '\n':
1324
- char_len = 2;
1325
- fprintf(stderr, "\\n\n");
3279
+ #else /* !HTTP_PARSER_STRICT */
3280
+
3281
+ , {.name="tab in URL"
3282
+ ,.url="/foo\tbar/"
3283
+ ,.u=
3284
+ {.field_set=(1 << UF_PATH)
3285
+ ,.field_data=
3286
+ {{ 0, 0 } /* UF_SCHEMA */
3287
+ ,{ 0, 0 } /* UF_HOST */
3288
+ ,{ 0, 0 } /* UF_PORT */
3289
+ ,{ 0, 9 } /* UF_PATH */
3290
+ ,{ 0, 0 } /* UF_QUERY */
3291
+ ,{ 0, 0 } /* UF_FRAGMENT */
3292
+ ,{ 0, 0 } /* UF_USERINFO */
3293
+ }
3294
+ }
3295
+ ,.rv=0
3296
+ }
1326
3297
 
1327
- if (this_line) goto print;
3298
+ , {.name="form feed in URL"
3299
+ ,.url="/foo\fbar/"
3300
+ ,.u=
3301
+ {.field_set=(1 << UF_PATH)
3302
+ ,.field_data=
3303
+ {{ 0, 0 } /* UF_SCHEMA */
3304
+ ,{ 0, 0 } /* UF_HOST */
3305
+ ,{ 0, 0 } /* UF_PORT */
3306
+ ,{ 0, 9 } /* UF_PATH */
3307
+ ,{ 0, 0 } /* UF_QUERY */
3308
+ ,{ 0, 0 } /* UF_FRAGMENT */
3309
+ ,{ 0, 0 } /* UF_USERINFO */
3310
+ }
3311
+ }
3312
+ ,.rv=0
3313
+ }
3314
+ #endif
3315
+ };
1328
3316
 
1329
- error_location_line = 0;
1330
- continue;
3317
+ void
3318
+ dump_url (const char *url, const struct http_parser_url *u)
3319
+ {
3320
+ unsigned int i;
1331
3321
 
1332
- default:
1333
- char_len = 1;
1334
- fputc(raw[i], stderr);
1335
- break;
3322
+ printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
3323
+ for (i = 0; i < UF_MAX; i++) {
3324
+ if ((u->field_set & (1 << i)) == 0) {
3325
+ printf("\tfield_data[%u]: unset\n", i);
3326
+ continue;
1336
3327
  }
1337
- if (!this_line) error_location_line += char_len;
3328
+
3329
+ printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
3330
+ i,
3331
+ u->field_data[i].off,
3332
+ u->field_data[i].len,
3333
+ u->field_data[i].len,
3334
+ url + u->field_data[i].off);
1338
3335
  }
3336
+ }
1339
3337
 
1340
- fprintf(stderr, "[eof]\n");
3338
+ void
3339
+ test_parse_url (void)
3340
+ {
3341
+ struct http_parser_url u;
3342
+ const struct url_test *test;
3343
+ unsigned int i;
3344
+ int rv;
3345
+
3346
+ for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
3347
+ test = &url_tests[i];
3348
+ memset(&u, 0, sizeof(u));
3349
+
3350
+ rv = http_parser_parse_url(test->url,
3351
+ strlen(test->url),
3352
+ test->is_connect,
3353
+ &u);
3354
+
3355
+ if (test->rv == 0) {
3356
+ if (rv != 0) {
3357
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
3358
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
3359
+ abort();
3360
+ }
1341
3361
 
1342
- print:
1343
- for (j = 0; j < error_location_line; j++) {
1344
- fputc(' ', stderr);
3362
+ if (memcmp(&u, &test->u, sizeof(u)) != 0) {
3363
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
3364
+ test->url, test->name);
3365
+
3366
+ printf("target http_parser_url:\n");
3367
+ dump_url(test->url, &test->u);
3368
+ printf("result http_parser_url:\n");
3369
+ dump_url(test->url, &u);
3370
+
3371
+ abort();
3372
+ }
3373
+ } else {
3374
+ /* test->rv != 0 */
3375
+ if (rv == 0) {
3376
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
3377
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
3378
+ abort();
3379
+ }
3380
+ }
1345
3381
  }
1346
- fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
1347
3382
  }
1348
3383
 
3384
+ void
3385
+ test_method_str (void)
3386
+ {
3387
+ assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
3388
+ assert(0 == strcmp("<unknown>", http_method_str(1337)));
3389
+ }
1349
3390
 
1350
3391
  void
1351
3392
  test_message (const struct message *message)
@@ -1363,41 +3404,45 @@ test_message (const struct message *message)
1363
3404
  if (msg1len) {
1364
3405
  read = parse(msg1, msg1len);
1365
3406
 
1366
- if (message->upgrade && parser->upgrade) goto test;
3407
+ if (message->upgrade && parser->upgrade && num_messages > 0) {
3408
+ messages[num_messages - 1].upgrade = msg1 + read;
3409
+ goto test;
3410
+ }
1367
3411
 
1368
3412
  if (read != msg1len) {
1369
3413
  print_error(msg1, read);
1370
- exit(1);
3414
+ abort();
1371
3415
  }
1372
3416
  }
1373
3417
 
1374
3418
 
1375
3419
  read = parse(msg2, msg2len);
1376
3420
 
1377
- if (message->upgrade && parser->upgrade) goto test;
3421
+ if (message->upgrade && parser->upgrade) {
3422
+ messages[num_messages - 1].upgrade = msg2 + read;
3423
+ goto test;
3424
+ }
1378
3425
 
1379
3426
  if (read != msg2len) {
1380
3427
  print_error(msg2, read);
1381
- exit(1);
3428
+ abort();
1382
3429
  }
1383
3430
 
1384
3431
  read = parse(NULL, 0);
1385
3432
 
1386
- if (message->upgrade && parser->upgrade) goto test;
1387
-
1388
3433
  if (read != 0) {
1389
3434
  print_error(message->raw, read);
1390
- exit(1);
3435
+ abort();
1391
3436
  }
1392
3437
 
1393
3438
  test:
1394
3439
 
1395
3440
  if (num_messages != 1) {
1396
3441
  printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
1397
- exit(1);
3442
+ abort();
1398
3443
  }
1399
3444
 
1400
- if(!message_eq(0, message)) exit(1);
3445
+ if(!message_eq(0, 0, message)) abort();
1401
3446
 
1402
3447
  parser_free();
1403
3448
  }
@@ -1418,7 +3463,7 @@ test_message_count_body (const struct message *message)
1418
3463
  read = parse_count_body(message->raw + i, toread);
1419
3464
  if (read != toread) {
1420
3465
  print_error(message->raw, read);
1421
- exit(1);
3466
+ abort();
1422
3467
  }
1423
3468
  }
1424
3469
 
@@ -1426,36 +3471,224 @@ test_message_count_body (const struct message *message)
1426
3471
  read = parse_count_body(NULL, 0);
1427
3472
  if (read != 0) {
1428
3473
  print_error(message->raw, read);
1429
- exit(1);
3474
+ abort();
1430
3475
  }
1431
3476
 
1432
3477
  if (num_messages != 1) {
1433
3478
  printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
1434
- exit(1);
3479
+ abort();
1435
3480
  }
1436
3481
 
1437
- if(!message_eq(0, message)) exit(1);
3482
+ if(!message_eq(0, 0, message)) abort();
1438
3483
 
1439
3484
  parser_free();
1440
3485
  }
1441
3486
 
1442
3487
  void
1443
- test_simple (const char *buf, int should_pass)
3488
+ test_simple_type (const char *buf,
3489
+ enum http_errno err_expected,
3490
+ enum http_parser_type type)
1444
3491
  {
1445
- parser_init(HTTP_REQUEST);
3492
+ parser_init(type);
1446
3493
 
1447
- size_t parsed;
1448
- int pass;
1449
- parsed = parse(buf, strlen(buf));
1450
- pass = (parsed == strlen(buf));
1451
- parsed = parse(NULL, 0);
1452
- pass &= (parsed == 0);
3494
+ enum http_errno err;
3495
+
3496
+ parse(buf, strlen(buf));
3497
+ err = HTTP_PARSER_ERRNO(parser);
3498
+ parse(NULL, 0);
1453
3499
 
1454
3500
  parser_free();
1455
3501
 
1456
- if (pass != should_pass) {
1457
- fprintf(stderr, "\n*** test_simple expected %s ***\n\n%s", should_pass ? "success" : "error", buf);
1458
- exit(1);
3502
+ /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
3503
+ * long as the caller isn't expecting success.
3504
+ */
3505
+ #if HTTP_PARSER_STRICT
3506
+ if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
3507
+ #else
3508
+ if (err_expected != err) {
3509
+ #endif
3510
+ fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
3511
+ http_errno_name(err_expected), http_errno_name(err), buf);
3512
+ abort();
3513
+ }
3514
+ }
3515
+
3516
+ void
3517
+ test_simple (const char *buf, enum http_errno err_expected)
3518
+ {
3519
+ test_simple_type(buf, err_expected, HTTP_REQUEST);
3520
+ }
3521
+
3522
+ void
3523
+ test_invalid_header_content (int req, const char* str)
3524
+ {
3525
+ http_parser parser;
3526
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3527
+ size_t parsed;
3528
+ const char *buf;
3529
+ buf = req ?
3530
+ "GET / HTTP/1.1\r\n" :
3531
+ "HTTP/1.1 200 OK\r\n";
3532
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3533
+ assert(parsed == strlen(buf));
3534
+
3535
+ buf = str;
3536
+ size_t buflen = strlen(buf);
3537
+
3538
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3539
+ if (parsed != buflen) {
3540
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3541
+ return;
3542
+ }
3543
+
3544
+ fprintf(stderr,
3545
+ "\n*** Error expected but none in invalid header content test ***\n");
3546
+ abort();
3547
+ }
3548
+
3549
+ void
3550
+ test_invalid_header_field_content_error (int req)
3551
+ {
3552
+ test_invalid_header_content(req, "Foo: F\01ailure");
3553
+ test_invalid_header_content(req, "Foo: B\02ar");
3554
+ }
3555
+
3556
+ void
3557
+ test_invalid_header_field (int req, const char* str)
3558
+ {
3559
+ http_parser parser;
3560
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3561
+ size_t parsed;
3562
+ const char *buf;
3563
+ buf = req ?
3564
+ "GET / HTTP/1.1\r\n" :
3565
+ "HTTP/1.1 200 OK\r\n";
3566
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3567
+ assert(parsed == strlen(buf));
3568
+
3569
+ buf = str;
3570
+ size_t buflen = strlen(buf);
3571
+
3572
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3573
+ if (parsed != buflen) {
3574
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3575
+ return;
3576
+ }
3577
+
3578
+ fprintf(stderr,
3579
+ "\n*** Error expected but none in invalid header token test ***\n");
3580
+ abort();
3581
+ }
3582
+
3583
+ void
3584
+ test_invalid_header_field_token_error (int req)
3585
+ {
3586
+ test_invalid_header_field(req, "Fo@: Failure");
3587
+ test_invalid_header_field(req, "Foo\01\test: Bar");
3588
+ }
3589
+
3590
+ void
3591
+ test_double_content_length_error (int req)
3592
+ {
3593
+ http_parser parser;
3594
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3595
+ size_t parsed;
3596
+ const char *buf;
3597
+ buf = req ?
3598
+ "GET / HTTP/1.1\r\n" :
3599
+ "HTTP/1.1 200 OK\r\n";
3600
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3601
+ assert(parsed == strlen(buf));
3602
+
3603
+ buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
3604
+ size_t buflen = strlen(buf);
3605
+
3606
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3607
+ if (parsed != buflen) {
3608
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
3609
+ return;
3610
+ }
3611
+
3612
+ fprintf(stderr,
3613
+ "\n*** Error expected but none in double content-length test ***\n");
3614
+ abort();
3615
+ }
3616
+
3617
+ void
3618
+ test_chunked_content_length_error (int req)
3619
+ {
3620
+ http_parser parser;
3621
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3622
+ size_t parsed;
3623
+ const char *buf;
3624
+ buf = req ?
3625
+ "GET / HTTP/1.1\r\n" :
3626
+ "HTTP/1.1 200 OK\r\n";
3627
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3628
+ assert(parsed == strlen(buf));
3629
+
3630
+ buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
3631
+ size_t buflen = strlen(buf);
3632
+
3633
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3634
+ if (parsed != buflen) {
3635
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_UNEXPECTED_CONTENT_LENGTH);
3636
+ return;
3637
+ }
3638
+
3639
+ fprintf(stderr,
3640
+ "\n*** Error expected but none in chunked content-length test ***\n");
3641
+ abort();
3642
+ }
3643
+
3644
+ void
3645
+ test_header_cr_no_lf_error (int req)
3646
+ {
3647
+ http_parser parser;
3648
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3649
+ size_t parsed;
3650
+ const char *buf;
3651
+ buf = req ?
3652
+ "GET / HTTP/1.1\r\n" :
3653
+ "HTTP/1.1 200 OK\r\n";
3654
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3655
+ assert(parsed == strlen(buf));
3656
+
3657
+ buf = "Foo: 1\rBar: 1\r\n\r\n";
3658
+ size_t buflen = strlen(buf);
3659
+
3660
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3661
+ if (parsed != buflen) {
3662
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED);
3663
+ return;
3664
+ }
3665
+
3666
+ fprintf(stderr,
3667
+ "\n*** Error expected but none in header whitespace test ***\n");
3668
+ abort();
3669
+ }
3670
+
3671
+ void
3672
+ test_no_overflow_parse_url (void)
3673
+ {
3674
+ int rv;
3675
+ struct http_parser_url u;
3676
+
3677
+ http_parser_url_init(&u);
3678
+ rv = http_parser_parse_url("http://example.com:8001", 22, 0, &u);
3679
+
3680
+ if (rv != 0) {
3681
+ fprintf(stderr,
3682
+ "\n*** test_no_overflow_parse_url invalid return value=%d\n",
3683
+ rv);
3684
+ abort();
3685
+ }
3686
+
3687
+ if (u.port != 800) {
3688
+ fprintf(stderr,
3689
+ "\n*** test_no_overflow_parse_url invalid port number=%d\n",
3690
+ u.port);
3691
+ abort();
1459
3692
  }
1460
3693
  }
1461
3694
 
@@ -1471,16 +3704,83 @@ test_header_overflow_error (int req)
1471
3704
  assert(parsed == strlen(buf));
1472
3705
 
1473
3706
  buf = "header-key: header-value\r\n";
3707
+ size_t buflen = strlen(buf);
3708
+
1474
3709
  int i;
1475
3710
  for (i = 0; i < 10000; i++) {
1476
- if (http_parser_execute(&parser, &settings_null, buf, strlen(buf)) != strlen(buf)) {
3711
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3712
+ if (parsed != buflen) {
1477
3713
  //fprintf(stderr, "error found on iter %d\n", i);
3714
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
1478
3715
  return;
1479
3716
  }
1480
3717
  }
1481
3718
 
1482
3719
  fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
1483
- exit(1);
3720
+ abort();
3721
+ }
3722
+
3723
+
3724
+ void
3725
+ test_header_nread_value ()
3726
+ {
3727
+ http_parser parser;
3728
+ http_parser_init(&parser, HTTP_REQUEST);
3729
+ size_t parsed;
3730
+ const char *buf;
3731
+ buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n";
3732
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3733
+ assert(parsed == strlen(buf));
3734
+
3735
+ assert(parser.nread == strlen(buf));
3736
+ }
3737
+
3738
+
3739
+ static void
3740
+ test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
3741
+ {
3742
+ http_parser parser;
3743
+ http_parser_init(&parser, HTTP_RESPONSE);
3744
+ http_parser_execute(&parser, &settings_null, buf, buflen);
3745
+
3746
+ if (expect_ok)
3747
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
3748
+ else
3749
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
3750
+ }
3751
+
3752
+ void
3753
+ test_header_content_length_overflow_error (void)
3754
+ {
3755
+ #define X(size) \
3756
+ "HTTP/1.1 200 OK\r\n" \
3757
+ "Content-Length: " #size "\r\n" \
3758
+ "\r\n"
3759
+ const char a[] = X(1844674407370955160); /* 2^64 / 10 - 1 */
3760
+ const char b[] = X(18446744073709551615); /* 2^64-1 */
3761
+ const char c[] = X(18446744073709551616); /* 2^64 */
3762
+ #undef X
3763
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
3764
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
3765
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
3766
+ }
3767
+
3768
+ void
3769
+ test_chunk_content_length_overflow_error (void)
3770
+ {
3771
+ #define X(size) \
3772
+ "HTTP/1.1 200 OK\r\n" \
3773
+ "Transfer-Encoding: chunked\r\n" \
3774
+ "\r\n" \
3775
+ #size "\r\n" \
3776
+ "..."
3777
+ const char a[] = X(FFFFFFFFFFFFFFE); /* 2^64 / 16 - 1 */
3778
+ const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
3779
+ const char c[] = X(10000000000000000); /* 2^64 */
3780
+ #undef X
3781
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
3782
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
3783
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
1484
3784
  }
1485
3785
 
1486
3786
  void
@@ -1491,8 +3791,8 @@ test_no_overflow_long_body (int req, size_t length)
1491
3791
  size_t parsed;
1492
3792
  size_t i;
1493
3793
  char buf1[3000];
1494
- size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %zu\r\n\r\n",
1495
- req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", length);
3794
+ size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
3795
+ req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
1496
3796
  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
1497
3797
  if (parsed != buf1len)
1498
3798
  goto err;
@@ -1510,21 +3810,16 @@ test_no_overflow_long_body (int req, size_t length)
1510
3810
 
1511
3811
  err:
1512
3812
  fprintf(stderr,
1513
- "\n*** error in test_no_overflow_long_body %s of length %zu ***\n",
3813
+ "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
1514
3814
  req ? "REQUEST" : "RESPONSE",
1515
- length);
1516
- exit(1);
3815
+ (unsigned long)length);
3816
+ abort();
1517
3817
  }
1518
3818
 
1519
3819
  void
1520
3820
  test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
1521
3821
  {
1522
- int message_count = 1;
1523
- if (!r1->upgrade) {
1524
- message_count++;
1525
- if (!r2->upgrade) message_count++;
1526
- }
1527
- int has_upgrade = (message_count < 3 || r3->upgrade);
3822
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1528
3823
 
1529
3824
  char total[ strlen(r1->raw)
1530
3825
  + strlen(r2->raw)
@@ -1543,36 +3838,33 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
1543
3838
 
1544
3839
  read = parse(total, strlen(total));
1545
3840
 
1546
- if (has_upgrade && parser->upgrade) goto test;
3841
+ if (parser->upgrade) {
3842
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
3843
+ goto test;
3844
+ }
1547
3845
 
1548
3846
  if (read != strlen(total)) {
1549
3847
  print_error(total, read);
1550
- exit(1);
3848
+ abort();
1551
3849
  }
1552
3850
 
1553
3851
  read = parse(NULL, 0);
1554
3852
 
1555
- if (has_upgrade && parser->upgrade) goto test;
1556
-
1557
3853
  if (read != 0) {
1558
3854
  print_error(total, read);
1559
- exit(1);
3855
+ abort();
1560
3856
  }
1561
3857
 
1562
3858
  test:
1563
3859
 
1564
3860
  if (message_count != num_messages) {
1565
3861
  fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
1566
- exit(1);
3862
+ abort();
1567
3863
  }
1568
3864
 
1569
- if (!message_eq(0, r1)) exit(1);
1570
- if (message_count > 1) {
1571
- if (!message_eq(1, r2)) exit(1);
1572
- if (message_count > 2) {
1573
- if (!message_eq(2, r3)) exit(1);
1574
- }
1575
- }
3865
+ if (!message_eq(0, 0, r1)) abort();
3866
+ if (message_count > 1 && !message_eq(1, 0, r2)) abort();
3867
+ if (message_count > 2 && !message_eq(2, 0, r3)) abort();
1576
3868
 
1577
3869
  parser_free();
1578
3870
  }
@@ -1601,6 +3893,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1601
3893
  int ops = 0 ;
1602
3894
 
1603
3895
  size_t buf1_len, buf2_len, buf3_len;
3896
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1604
3897
 
1605
3898
  int i,j,type_both;
1606
3899
  for (type_both = 0; type_both < 2; type_both ++ ) {
@@ -1616,40 +3909,40 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1616
3909
  parser_init(type_both ? HTTP_BOTH : r1->type);
1617
3910
 
1618
3911
  buf1_len = i;
1619
- strncpy(buf1, total, buf1_len);
3912
+ strlncpy(buf1, sizeof(buf1), total, buf1_len);
1620
3913
  buf1[buf1_len] = 0;
1621
3914
 
1622
3915
  buf2_len = j - i;
1623
- strncpy(buf2, total+i, buf2_len);
3916
+ strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
1624
3917
  buf2[buf2_len] = 0;
1625
3918
 
1626
3919
  buf3_len = total_len - j;
1627
- strncpy(buf3, total+j, buf3_len);
3920
+ strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
1628
3921
  buf3[buf3_len] = 0;
1629
3922
 
1630
3923
  read = parse(buf1, buf1_len);
1631
3924
 
1632
- if (r3->upgrade && parser->upgrade) goto test;
3925
+ if (parser->upgrade) goto test;
1633
3926
 
1634
3927
  if (read != buf1_len) {
1635
3928
  print_error(buf1, read);
1636
3929
  goto error;
1637
3930
  }
1638
3931
 
1639
- read = parse(buf2, buf2_len);
3932
+ read += parse(buf2, buf2_len);
1640
3933
 
1641
- if (r3->upgrade && parser->upgrade) goto test;
3934
+ if (parser->upgrade) goto test;
1642
3935
 
1643
- if (read != buf2_len) {
3936
+ if (read != buf1_len + buf2_len) {
1644
3937
  print_error(buf2, read);
1645
3938
  goto error;
1646
3939
  }
1647
3940
 
1648
- read = parse(buf3, buf3_len);
3941
+ read += parse(buf3, buf3_len);
1649
3942
 
1650
- if (r3->upgrade && parser->upgrade) goto test;
3943
+ if (parser->upgrade) goto test;
1651
3944
 
1652
- if (read != buf3_len) {
3945
+ if (read != buf1_len + buf2_len + buf3_len) {
1653
3946
  print_error(buf3, read);
1654
3947
  goto error;
1655
3948
  }
@@ -1657,23 +3950,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1657
3950
  parse(NULL, 0);
1658
3951
 
1659
3952
  test:
3953
+ if (parser->upgrade) {
3954
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
3955
+ }
1660
3956
 
1661
- if (3 != num_messages) {
1662
- fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages);
3957
+ if (message_count != num_messages) {
3958
+ fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3959
+ message_count, num_messages);
1663
3960
  goto error;
1664
3961
  }
1665
3962
 
1666
- if (!message_eq(0, r1)) {
3963
+ if (!message_eq(0, 0, r1)) {
1667
3964
  fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
1668
3965
  goto error;
1669
3966
  }
1670
3967
 
1671
- if (!message_eq(1, r2)) {
3968
+ if (message_count > 1 && !message_eq(1, 0, r2)) {
1672
3969
  fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
1673
3970
  goto error;
1674
3971
  }
1675
3972
 
1676
- if (!message_eq(2, r3)) {
3973
+ if (message_count > 2 && !message_eq(2, 0, r3)) {
1677
3974
  fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
1678
3975
  goto error;
1679
3976
  }
@@ -1690,7 +3987,7 @@ test:
1690
3987
  fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
1691
3988
  fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
1692
3989
  fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
1693
- exit(1);
3990
+ abort();
1694
3991
  }
1695
3992
 
1696
3993
  // user required to free the result
@@ -1724,21 +4021,108 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
1724
4021
  return buf;
1725
4022
  }
1726
4023
 
4024
+ /* Verify that we can pause parsing at any of the bytes in the
4025
+ * message and still get the result that we're expecting. */
4026
+ void
4027
+ test_message_pause (const struct message *msg)
4028
+ {
4029
+ char *buf = (char*) msg->raw;
4030
+ size_t buflen = strlen(msg->raw);
4031
+ size_t nread;
4032
+
4033
+ parser_init(msg->type);
4034
+
4035
+ do {
4036
+ nread = parse_pause(buf, buflen);
4037
+
4038
+ // We can only set the upgrade buffer once we've gotten our message
4039
+ // completion callback.
4040
+ if (messages[0].message_complete_cb_called &&
4041
+ msg->upgrade &&
4042
+ parser->upgrade) {
4043
+ messages[0].upgrade = buf + nread;
4044
+ goto test;
4045
+ }
4046
+
4047
+ if (nread < buflen) {
4048
+
4049
+ // Not much do to if we failed a strict-mode check
4050
+ if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
4051
+ parser_free();
4052
+ return;
4053
+ }
4054
+
4055
+ assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
4056
+ }
4057
+
4058
+ buf += nread;
4059
+ buflen -= nread;
4060
+ http_parser_pause(parser, 0);
4061
+ } while (buflen > 0);
4062
+
4063
+ nread = parse_pause(NULL, 0);
4064
+ assert (nread == 0);
4065
+
4066
+ test:
4067
+ if (num_messages != 1) {
4068
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
4069
+ abort();
4070
+ }
4071
+
4072
+ if(!message_eq(0, 0, msg)) abort();
4073
+
4074
+ parser_free();
4075
+ }
4076
+
4077
+ /* Verify that body and next message won't be parsed in responses to CONNECT */
4078
+ void
4079
+ test_message_connect (const struct message *msg)
4080
+ {
4081
+ char *buf = (char*) msg->raw;
4082
+ size_t buflen = strlen(msg->raw);
4083
+
4084
+ parser_init(msg->type);
4085
+
4086
+ parse_connect(buf, buflen);
4087
+
4088
+ if (num_messages != 1) {
4089
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
4090
+ abort();
4091
+ }
4092
+
4093
+ if(!message_eq(0, 1, msg)) abort();
4094
+
4095
+ parser_free();
4096
+ }
1727
4097
 
1728
4098
  int
1729
4099
  main (void)
1730
4100
  {
1731
4101
  parser = NULL;
1732
- int i, j, k;
1733
- int request_count;
1734
- int response_count;
4102
+ unsigned i, j, k;
4103
+ unsigned long version;
4104
+ unsigned major;
4105
+ unsigned minor;
4106
+ unsigned patch;
4107
+
4108
+ version = http_parser_version();
4109
+ major = (version >> 16) & 255;
4110
+ minor = (version >> 8) & 255;
4111
+ patch = version & 255;
4112
+ printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version);
1735
4113
 
1736
4114
  printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
1737
4115
 
1738
- for (request_count = 0; requests[request_count].name; request_count++);
1739
- for (response_count = 0; responses[response_count].name; response_count++);
4116
+ //// API
4117
+ test_preserve_data();
4118
+ test_parse_url();
4119
+ test_method_str();
4120
+
4121
+ //// NREAD
4122
+ test_header_nread_value();
1740
4123
 
1741
4124
  //// OVERFLOW CONDITIONS
4125
+ test_no_overflow_parse_url();
1742
4126
 
1743
4127
  test_header_overflow_error(HTTP_REQUEST);
1744
4128
  test_no_overflow_long_body(HTTP_REQUEST, 1000);
@@ -1748,17 +4132,67 @@ main (void)
1748
4132
  test_no_overflow_long_body(HTTP_RESPONSE, 1000);
1749
4133
  test_no_overflow_long_body(HTTP_RESPONSE, 100000);
1750
4134
 
4135
+ test_header_content_length_overflow_error();
4136
+ test_chunk_content_length_overflow_error();
4137
+
4138
+ //// HEADER FIELD CONDITIONS
4139
+ test_double_content_length_error(HTTP_REQUEST);
4140
+ test_chunked_content_length_error(HTTP_REQUEST);
4141
+ test_header_cr_no_lf_error(HTTP_REQUEST);
4142
+ test_invalid_header_field_token_error(HTTP_REQUEST);
4143
+ test_invalid_header_field_content_error(HTTP_REQUEST);
4144
+ test_double_content_length_error(HTTP_RESPONSE);
4145
+ test_chunked_content_length_error(HTTP_RESPONSE);
4146
+ test_header_cr_no_lf_error(HTTP_RESPONSE);
4147
+ test_invalid_header_field_token_error(HTTP_RESPONSE);
4148
+ test_invalid_header_field_content_error(HTTP_RESPONSE);
4149
+
4150
+ test_simple_type(
4151
+ "POST / HTTP/1.1\r\n"
4152
+ "Content-Length: 42 \r\n" // Note the surrounding whitespace.
4153
+ "\r\n",
4154
+ HPE_OK,
4155
+ HTTP_REQUEST);
4156
+
4157
+ test_simple_type(
4158
+ "POST / HTTP/1.1\r\n"
4159
+ "Content-Length: 4 2\r\n"
4160
+ "\r\n",
4161
+ HPE_INVALID_CONTENT_LENGTH,
4162
+ HTTP_REQUEST);
4163
+
4164
+ test_simple_type(
4165
+ "POST / HTTP/1.1\r\n"
4166
+ "Content-Length: 13 37\r\n"
4167
+ "\r\n",
4168
+ HPE_INVALID_CONTENT_LENGTH,
4169
+ HTTP_REQUEST);
4170
+
1751
4171
  //// RESPONSES
1752
4172
 
1753
- for (i = 0; i < response_count; i++) {
4173
+ test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4174
+ test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4175
+ test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4176
+ test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4177
+ test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
4178
+
4179
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
1754
4180
  test_message(&responses[i]);
1755
4181
  }
1756
4182
 
1757
- for (i = 0; i < response_count; i++) {
4183
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
4184
+ test_message_pause(&responses[i]);
4185
+ }
4186
+
4187
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
4188
+ test_message_connect(&responses[i]);
4189
+ }
4190
+
4191
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
1758
4192
  if (!responses[i].should_keep_alive) continue;
1759
- for (j = 0; j < response_count; j++) {
4193
+ for (j = 0; j < ARRAY_SIZE(responses); j++) {
1760
4194
  if (!responses[j].should_keep_alive) continue;
1761
- for (k = 0; k < response_count; k++) {
4195
+ for (k = 0; k < ARRAY_SIZE(responses); k++) {
1762
4196
  test_multiple3(&responses[i], &responses[j], &responses[k]);
1763
4197
  }
1764
4198
  }
@@ -1783,13 +4217,18 @@ main (void)
1783
4217
  ,.http_major= 1
1784
4218
  ,.http_minor= 0
1785
4219
  ,.status_code= 200
4220
+ ,.response_status= "OK"
1786
4221
  ,.num_headers= 2
1787
4222
  ,.headers=
1788
4223
  { { "Transfer-Encoding", "chunked" }
1789
4224
  , { "Content-Type", "text/plain" }
1790
4225
  }
1791
4226
  ,.body_size= 31337*1024
4227
+ ,.num_chunks_complete= 31338
1792
4228
  };
4229
+ for (i = 0; i < MAX_CHUNKS; i++) {
4230
+ large_chunked.chunk_lengths[i] = 1024;
4231
+ }
1793
4232
  test_message_count_body(&large_chunked);
1794
4233
  free(msg);
1795
4234
  }
@@ -1798,7 +4237,7 @@ main (void)
1798
4237
 
1799
4238
  printf("response scan 1/2 ");
1800
4239
  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
1801
- , &responses[NO_HEADERS_NO_BODY_404]
4240
+ , &responses[NO_BODY_HTTP10_KA_204]
1802
4241
  , &responses[NO_REASON_PHRASE]
1803
4242
  );
1804
4243
 
@@ -1813,13 +4252,15 @@ main (void)
1813
4252
 
1814
4253
  /// REQUESTS
1815
4254
 
1816
- test_simple("hello world", 0);
1817
- test_simple("GET / HTP/1.1\r\n\r\n", 0);
4255
+ test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
4256
+ test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION);
4257
+ test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION);
4258
+ test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION);
1818
4259
 
1819
-
1820
- test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1821
- test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1822
- test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
4260
+ // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
4261
+ test_simple("GET / HTTP/1.1\r\n"
4262
+ "Test: Düsseldorf\r\n",
4263
+ HPE_OK);
1823
4264
 
1824
4265
  // Well-formed but incomplete
1825
4266
  test_simple("GET / HTTP/1.1\r\n"
@@ -1827,7 +4268,7 @@ main (void)
1827
4268
  "Content-Length: 6\r\n"
1828
4269
  "\r\n"
1829
4270
  "fooba",
1830
- 0);
4271
+ HPE_OK);
1831
4272
 
1832
4273
  static const char *all_methods[] = {
1833
4274
  "DELETE",
@@ -1844,15 +4285,60 @@ main (void)
1844
4285
  "MOVE",
1845
4286
  "PROPFIND",
1846
4287
  "PROPPATCH",
4288
+ "SEARCH",
1847
4289
  "UNLOCK",
4290
+ "BIND",
4291
+ "REBIND",
4292
+ "UNBIND",
4293
+ "ACL",
4294
+ "REPORT",
4295
+ "MKACTIVITY",
4296
+ "CHECKOUT",
4297
+ "MERGE",
4298
+ "M-SEARCH",
4299
+ "NOTIFY",
4300
+ "SUBSCRIBE",
4301
+ "UNSUBSCRIBE",
4302
+ "PATCH",
4303
+ "PURGE",
4304
+ "MKCALENDAR",
4305
+ "LINK",
4306
+ "UNLINK",
1848
4307
  0 };
1849
4308
  const char **this_method;
1850
4309
  for (this_method = all_methods; *this_method; this_method++) {
1851
4310
  char buf[200];
1852
4311
  sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
1853
- test_simple(buf, 1);
4312
+ test_simple(buf, HPE_OK);
4313
+ }
4314
+
4315
+ static const char *bad_methods[] = {
4316
+ "ASDF",
4317
+ "C******",
4318
+ "COLA",
4319
+ "GEM",
4320
+ "GETA",
4321
+ "M****",
4322
+ "MKCOLA",
4323
+ "PROPPATCHA",
4324
+ "PUN",
4325
+ "PX",
4326
+ "SA",
4327
+ "hello world",
4328
+ 0 };
4329
+ for (this_method = bad_methods; *this_method; this_method++) {
4330
+ char buf[200];
4331
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
4332
+ test_simple(buf, HPE_INVALID_METHOD);
1854
4333
  }
1855
4334
 
4335
+ // illegal header field name line folding
4336
+ test_simple("GET / HTTP/1.1\r\n"
4337
+ "name\r\n"
4338
+ " : value\r\n"
4339
+ "\r\n",
4340
+ HPE_INVALID_HEADER_TOKEN);
4341
+
1856
4342
  const char *dumbfuck2 =
1857
4343
  "GET / HTTP/1.1\r\n"
1858
4344
  "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
@@ -1888,7 +4374,23 @@ main (void)
1888
4374
  "\tRA==\r\n"
1889
4375
  "\t-----END CERTIFICATE-----\r\n"
1890
4376
  "\r\n";
1891
- test_simple(dumbfuck2, 0);
4377
+ test_simple(dumbfuck2, HPE_OK);
4378
+
4379
+ const char *corrupted_connection =
4380
+ "GET / HTTP/1.1\r\n"
4381
+ "Host: www.example.com\r\n"
4382
+ "Connection\r\033\065\325eep-Alive\r\n"
4383
+ "Accept-Encoding: gzip\r\n"
4384
+ "\r\n";
4385
+ test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN);
4386
+
4387
+ const char *corrupted_header_name =
4388
+ "GET / HTTP/1.1\r\n"
4389
+ "Host: www.example.com\r\n"
4390
+ "X-Some-Header\r\033\065\325eep-Alive\r\n"
4391
+ "Accept-Encoding: gzip\r\n"
4392
+ "\r\n";
4393
+ test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN);
1892
4394
 
1893
4395
  #if 0
1894
4396
  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
@@ -1906,17 +4408,19 @@ main (void)
1906
4408
 
1907
4409
 
1908
4410
  /* check to make sure our predefined requests are okay */
1909
- for (i = 0; requests[i].name; i++) {
4411
+ for (i = 0; i < ARRAY_SIZE(requests); i++) {
1910
4412
  test_message(&requests[i]);
1911
4413
  }
1912
4414
 
4415
+ for (i = 0; i < ARRAY_SIZE(requests); i++) {
4416
+ test_message_pause(&requests[i]);
4417
+ }
1913
4418
 
1914
-
1915
- for (i = 0; i < request_count; i++) {
4419
+ for (i = 0; i < ARRAY_SIZE(requests); i++) {
1916
4420
  if (!requests[i].should_keep_alive) continue;
1917
- for (j = 0; j < request_count; j++) {
4421
+ for (j = 0; j < ARRAY_SIZE(requests); j++) {
1918
4422
  if (!requests[j].should_keep_alive) continue;
1919
- for (k = 0; k < request_count; k++) {
4423
+ for (k = 0; k < ARRAY_SIZE(requests); k++) {
1920
4424
  test_multiple3(&requests[i], &requests[j], &requests[k]);
1921
4425
  }
1922
4426
  }