http_parser.rb 0.5.1 → 0.6.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.gitmodules +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +11 -4
  4. data/LICENSE-MIT +20 -0
  5. data/README.md +51 -46
  6. data/bench/standalone.rb +23 -0
  7. data/bench/thin.rb +1 -0
  8. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +121 -56
  9. data/ext/ruby_http_parser/ruby_http_parser.c +89 -48
  10. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
  11. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
  12. data/ext/ruby_http_parser/vendor/http-parser/Makefile +34 -17
  13. data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
  14. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1045 -589
  15. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
  16. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +148 -17
  17. data/ext/ruby_http_parser/vendor/http-parser/test.c +1138 -128
  18. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +30 -1
  20. data/ext/ruby_http_parser/vendor/http-parser-java/Makefile +34 -17
  21. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +30 -18
  22. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +9 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/ext/primitives.jar +0 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1075 -607
  26. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +190 -40
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +67 -52
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +34 -35
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +839 -587
  42. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +2 -1
  43. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +374 -0
  44. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  45. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  46. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +52 -0
  47. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +16 -0
  48. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +48 -0
  49. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +171 -446
  50. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +62 -0
  51. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +3 -1
  52. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +27 -0
  53. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  54. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +236 -0
  55. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +59 -0
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1318 -184
  57. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +355 -49
  58. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  59. data/http_parser.rb.gemspec +10 -2
  60. data/lib/http_parser.rb +17 -0
  61. data/spec/parser_spec.rb +157 -23
  62. data/spec/support/requests.json +236 -24
  63. data/spec/support/responses.json +182 -36
  64. data/tasks/compile.rake +5 -3
  65. data/tasks/fixtures.rake +8 -2
  66. metadata +209 -127
  67. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
  68. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  69. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  70. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
@@ -1,4 +1,4 @@
1
- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
2
  *
3
3
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  * of this software and associated documentation files (the "Software"), to
@@ -50,12 +50,13 @@ struct message {
50
50
  char query_string[MAX_ELEMENT_SIZE];
51
51
  char body[MAX_ELEMENT_SIZE];
52
52
  size_t body_size;
53
+ uint16_t port;
53
54
  int num_headers;
54
55
  enum { NONE=0, FIELD, VALUE } last_header_element;
55
56
  char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
56
57
  int should_keep_alive;
57
58
 
58
- int upgrade;
59
+ const char *upgrade; // upgraded body
59
60
 
60
61
  unsigned short http_major;
61
62
  unsigned short http_minor;
@@ -70,6 +71,7 @@ static int currently_parsing_eof;
70
71
 
71
72
  static struct message messages[5];
72
73
  static int num_messages;
74
+ static http_parser_settings *current_pause_parser;
73
75
 
74
76
  /* * R E Q U E S T S * */
75
77
  const struct message requests[] =
@@ -473,6 +475,7 @@ const struct message requests[] =
473
475
  "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
474
476
  "Origin: http://example.com\r\n"
475
477
  "\r\n"
478
+ "Hot diggity dogg"
476
479
  ,.should_keep_alive= TRUE
477
480
  ,.message_complete_on_eof= FALSE
478
481
  ,.http_major= 1
@@ -483,7 +486,7 @@ const struct message requests[] =
483
486
  ,.request_path= "/demo"
484
487
  ,.request_url= "/demo"
485
488
  ,.num_headers= 7
486
- ,.upgrade=1
489
+ ,.upgrade="Hot diggity dogg"
487
490
  ,.headers= { { "Host", "example.com" }
488
491
  , { "Connection", "Upgrade" }
489
492
  , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
@@ -498,10 +501,12 @@ const struct message requests[] =
498
501
  #define CONNECT_REQUEST 17
499
502
  , {.name = "connect request"
500
503
  ,.type= HTTP_REQUEST
501
- ,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n"
504
+ ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
502
505
  "User-agent: Mozilla/1.1N\r\n"
503
506
  "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
504
507
  "\r\n"
508
+ "some data\r\n"
509
+ "and yet even more data"
505
510
  ,.should_keep_alive= FALSE
506
511
  ,.message_complete_on_eof= FALSE
507
512
  ,.http_major= 1
@@ -510,9 +515,9 @@ const struct message requests[] =
510
515
  ,.query_string= ""
511
516
  ,.fragment= ""
512
517
  ,.request_path= ""
513
- ,.request_url= "home0.netscape.com:443"
518
+ ,.request_url= "0-home0.netscape.com:443"
514
519
  ,.num_headers= 2
515
- ,.upgrade=1
520
+ ,.upgrade="some data\r\nand yet even more data"
516
521
  ,.headers= { { "User-agent", "Mozilla/1.1N" }
517
522
  , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
518
523
  }
@@ -557,7 +562,7 @@ const struct message requests[] =
557
562
  ,.body= ""
558
563
  }
559
564
 
560
- #define MSEARCH_REQ 19
565
+ #define MSEARCH_REQ 20
561
566
  , {.name= "m-search request"
562
567
  ,.type= HTTP_REQUEST
563
568
  ,.raw= "M-SEARCH * HTTP/1.1\r\n"
@@ -582,6 +587,269 @@ const struct message requests[] =
582
587
  ,.body= ""
583
588
  }
584
589
 
590
+ #define LINE_FOLDING_IN_HEADER 20
591
+ , {.name= "line folding in header value"
592
+ ,.type= HTTP_REQUEST
593
+ ,.raw= "GET / HTTP/1.1\r\n"
594
+ "Line1: abc\r\n"
595
+ "\tdef\r\n"
596
+ " ghi\r\n"
597
+ "\t\tjkl\r\n"
598
+ " mno \r\n"
599
+ "\t \tqrs\r\n"
600
+ "Line2: \t line2\t\r\n"
601
+ "\r\n"
602
+ ,.should_keep_alive= TRUE
603
+ ,.message_complete_on_eof= FALSE
604
+ ,.http_major= 1
605
+ ,.http_minor= 1
606
+ ,.method= HTTP_GET
607
+ ,.query_string= ""
608
+ ,.fragment= ""
609
+ ,.request_path= "/"
610
+ ,.request_url= "/"
611
+ ,.num_headers= 2
612
+ ,.headers= { { "Line1", "abcdefghijklmno qrs" }
613
+ , { "Line2", "line2\t" }
614
+ }
615
+ ,.body= ""
616
+ }
617
+
618
+
619
+ #define QUERY_TERMINATED_HOST 21
620
+ , {.name= "host terminated by a query string"
621
+ ,.type= HTTP_REQUEST
622
+ ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
623
+ "\r\n"
624
+ ,.should_keep_alive= TRUE
625
+ ,.message_complete_on_eof= FALSE
626
+ ,.http_major= 1
627
+ ,.http_minor= 1
628
+ ,.method= HTTP_GET
629
+ ,.query_string= "hail=all"
630
+ ,.fragment= ""
631
+ ,.request_path= ""
632
+ ,.request_url= "http://hypnotoad.org?hail=all"
633
+ ,.num_headers= 0
634
+ ,.headers= { }
635
+ ,.body= ""
636
+ }
637
+
638
+ #define QUERY_TERMINATED_HOSTPORT 22
639
+ , {.name= "host:port terminated by a query string"
640
+ ,.type= HTTP_REQUEST
641
+ ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
642
+ "\r\n"
643
+ ,.should_keep_alive= TRUE
644
+ ,.message_complete_on_eof= FALSE
645
+ ,.http_major= 1
646
+ ,.http_minor= 1
647
+ ,.method= HTTP_GET
648
+ ,.query_string= "hail=all"
649
+ ,.fragment= ""
650
+ ,.request_path= ""
651
+ ,.request_url= "http://hypnotoad.org:1234?hail=all"
652
+ ,.port= 1234
653
+ ,.num_headers= 0
654
+ ,.headers= { }
655
+ ,.body= ""
656
+ }
657
+
658
+ #define SPACE_TERMINATED_HOSTPORT 23
659
+ , {.name= "host:port terminated by a space"
660
+ ,.type= HTTP_REQUEST
661
+ ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
662
+ "\r\n"
663
+ ,.should_keep_alive= TRUE
664
+ ,.message_complete_on_eof= FALSE
665
+ ,.http_major= 1
666
+ ,.http_minor= 1
667
+ ,.method= HTTP_GET
668
+ ,.query_string= ""
669
+ ,.fragment= ""
670
+ ,.request_path= ""
671
+ ,.request_url= "http://hypnotoad.org:1234"
672
+ ,.port= 1234
673
+ ,.num_headers= 0
674
+ ,.headers= { }
675
+ ,.body= ""
676
+ }
677
+
678
+ #define PATCH_REQ 24
679
+ , {.name = "PATCH request"
680
+ ,.type= HTTP_REQUEST
681
+ ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
682
+ "Host: www.example.com\r\n"
683
+ "Content-Type: application/example\r\n"
684
+ "If-Match: \"e0023aa4e\"\r\n"
685
+ "Content-Length: 10\r\n"
686
+ "\r\n"
687
+ "cccccccccc"
688
+ ,.should_keep_alive= TRUE
689
+ ,.message_complete_on_eof= FALSE
690
+ ,.http_major= 1
691
+ ,.http_minor= 1
692
+ ,.method= HTTP_PATCH
693
+ ,.query_string= ""
694
+ ,.fragment= ""
695
+ ,.request_path= "/file.txt"
696
+ ,.request_url= "/file.txt"
697
+ ,.num_headers= 4
698
+ ,.headers= { { "Host", "www.example.com" }
699
+ , { "Content-Type", "application/example" }
700
+ , { "If-Match", "\"e0023aa4e\"" }
701
+ , { "Content-Length", "10" }
702
+ }
703
+ ,.body= "cccccccccc"
704
+ }
705
+
706
+ #define CONNECT_CAPS_REQUEST 25
707
+ , {.name = "connect caps request"
708
+ ,.type= HTTP_REQUEST
709
+ ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
710
+ "User-agent: Mozilla/1.1N\r\n"
711
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
712
+ "\r\n"
713
+ ,.should_keep_alive= FALSE
714
+ ,.message_complete_on_eof= FALSE
715
+ ,.http_major= 1
716
+ ,.http_minor= 0
717
+ ,.method= HTTP_CONNECT
718
+ ,.query_string= ""
719
+ ,.fragment= ""
720
+ ,.request_path= ""
721
+ ,.request_url= "HOME0.NETSCAPE.COM:443"
722
+ ,.num_headers= 2
723
+ ,.upgrade=""
724
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
725
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
726
+ }
727
+ ,.body= ""
728
+ }
729
+
730
+ #if !HTTP_PARSER_STRICT
731
+ #define UTF8_PATH_REQ 26
732
+ , {.name= "utf-8 path request"
733
+ ,.type= HTTP_REQUEST
734
+ ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
735
+ "Host: github.com\r\n"
736
+ "\r\n"
737
+ ,.should_keep_alive= TRUE
738
+ ,.message_complete_on_eof= FALSE
739
+ ,.http_major= 1
740
+ ,.http_minor= 1
741
+ ,.method= HTTP_GET
742
+ ,.query_string= "q=1"
743
+ ,.fragment= "narf"
744
+ ,.request_path= "/δ¶/δt/pope"
745
+ ,.request_url= "/δ¶/δt/pope?q=1#narf"
746
+ ,.num_headers= 1
747
+ ,.headers= { {"Host", "github.com" }
748
+ }
749
+ ,.body= ""
750
+ }
751
+
752
+ #define HOSTNAME_UNDERSCORE 27
753
+ , {.name = "hostname underscore"
754
+ ,.type= HTTP_REQUEST
755
+ ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
756
+ "User-agent: Mozilla/1.1N\r\n"
757
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
758
+ "\r\n"
759
+ ,.should_keep_alive= FALSE
760
+ ,.message_complete_on_eof= FALSE
761
+ ,.http_major= 1
762
+ ,.http_minor= 0
763
+ ,.method= HTTP_CONNECT
764
+ ,.query_string= ""
765
+ ,.fragment= ""
766
+ ,.request_path= ""
767
+ ,.request_url= "home_0.netscape.com:443"
768
+ ,.num_headers= 2
769
+ ,.upgrade=""
770
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
771
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
772
+ }
773
+ ,.body= ""
774
+ }
775
+ #endif /* !HTTP_PARSER_STRICT */
776
+
777
+ /* see https://github.com/ry/http-parser/issues/47 */
778
+ #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 28
779
+ , {.name = "eat CRLF between requests, no \"Connection: close\" header"
780
+ ,.raw= "POST / HTTP/1.1\r\n"
781
+ "Host: www.example.com\r\n"
782
+ "Content-Type: application/x-www-form-urlencoded\r\n"
783
+ "Content-Length: 4\r\n"
784
+ "\r\n"
785
+ "q=42\r\n" /* note the trailing CRLF */
786
+ ,.should_keep_alive= TRUE
787
+ ,.message_complete_on_eof= FALSE
788
+ ,.http_major= 1
789
+ ,.http_minor= 1
790
+ ,.method= HTTP_POST
791
+ ,.query_string= ""
792
+ ,.fragment= ""
793
+ ,.request_path= "/"
794
+ ,.request_url= "/"
795
+ ,.num_headers= 3
796
+ ,.upgrade= 0
797
+ ,.headers= { { "Host", "www.example.com" }
798
+ , { "Content-Type", "application/x-www-form-urlencoded" }
799
+ , { "Content-Length", "4" }
800
+ }
801
+ ,.body= "q=42"
802
+ }
803
+
804
+ /* see https://github.com/ry/http-parser/issues/47 */
805
+ #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 29
806
+ , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
807
+ ,.raw= "POST / HTTP/1.1\r\n"
808
+ "Host: www.example.com\r\n"
809
+ "Content-Type: application/x-www-form-urlencoded\r\n"
810
+ "Content-Length: 4\r\n"
811
+ "Connection: close\r\n"
812
+ "\r\n"
813
+ "q=42\r\n" /* note the trailing CRLF */
814
+ ,.should_keep_alive= FALSE
815
+ ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
816
+ ,.http_major= 1
817
+ ,.http_minor= 1
818
+ ,.method= HTTP_POST
819
+ ,.query_string= ""
820
+ ,.fragment= ""
821
+ ,.request_path= "/"
822
+ ,.request_url= "/"
823
+ ,.num_headers= 4
824
+ ,.upgrade= 0
825
+ ,.headers= { { "Host", "www.example.com" }
826
+ , { "Content-Type", "application/x-www-form-urlencoded" }
827
+ , { "Content-Length", "4" }
828
+ , { "Connection", "close" }
829
+ }
830
+ ,.body= "q=42"
831
+ }
832
+
833
+ #define PURGE_REQ 30
834
+ , {.name = "PURGE request"
835
+ ,.type= HTTP_REQUEST
836
+ ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
837
+ "Host: www.example.com\r\n"
838
+ "\r\n"
839
+ ,.should_keep_alive= TRUE
840
+ ,.message_complete_on_eof= FALSE
841
+ ,.http_major= 1
842
+ ,.http_minor= 1
843
+ ,.method= HTTP_PURGE
844
+ ,.query_string= ""
845
+ ,.fragment= ""
846
+ ,.request_path= "/file.txt"
847
+ ,.request_url= "/file.txt"
848
+ ,.num_headers= 1
849
+ ,.headers= { { "Host", "www.example.com" } }
850
+ ,.body= ""
851
+ }
852
+
585
853
  , {.name= NULL } /* sentinel */
586
854
  };
587
855
 
@@ -682,8 +950,8 @@ const struct message responses[] =
682
950
  , {.name= "404 no headers no body"
683
951
  ,.type= HTTP_RESPONSE
684
952
  ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
685
- ,.should_keep_alive= TRUE
686
- ,.message_complete_on_eof= FALSE
953
+ ,.should_keep_alive= FALSE
954
+ ,.message_complete_on_eof= TRUE
687
955
  ,.http_major= 1
688
956
  ,.http_minor= 1
689
957
  ,.status_code= 404
@@ -697,8 +965,8 @@ const struct message responses[] =
697
965
  , {.name= "301 no response phrase"
698
966
  ,.type= HTTP_RESPONSE
699
967
  ,.raw= "HTTP/1.1 301\r\n\r\n"
700
- ,.should_keep_alive = TRUE
701
- ,.message_complete_on_eof= FALSE
968
+ ,.should_keep_alive = FALSE
969
+ ,.message_complete_on_eof= TRUE
702
970
  ,.http_major= 1
703
971
  ,.http_minor= 1
704
972
  ,.status_code= 301
@@ -847,40 +1115,7 @@ const struct message responses[] =
847
1115
  ,.body= ""
848
1116
  }
849
1117
 
850
- #define SPACE_IN_FIELD_RES 9
851
- /* Should handle spaces in header fields */
852
- , {.name= "field space"
853
- ,.type= HTTP_RESPONSE
854
- ,.raw= "HTTP/1.1 200 OK\r\n"
855
- "Server: Microsoft-IIS/6.0\r\n"
856
- "X-Powered-By: ASP.NET\r\n"
857
- "en-US Content-Type: text/xml\r\n" /* this is the problem */
858
- "Content-Type: text/xml\r\n"
859
- "Content-Length: 16\r\n"
860
- "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
861
- "Connection: keep-alive\r\n"
862
- "\r\n"
863
- "<xml>hello</xml>" /* fake body */
864
- ,.should_keep_alive= TRUE
865
- ,.message_complete_on_eof= FALSE
866
- ,.http_major= 1
867
- ,.http_minor= 1
868
- ,.status_code= 200
869
- ,.num_headers= 7
870
- ,.headers=
871
- { { "Server", "Microsoft-IIS/6.0" }
872
- , { "X-Powered-By", "ASP.NET" }
873
- , { "en-US Content-Type", "text/xml" }
874
- , { "Content-Type", "text/xml" }
875
- , { "Content-Length", "16" }
876
- , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
877
- , { "Connection", "keep-alive" }
878
- }
879
- ,.body= "<xml>hello</xml>"
880
- }
881
-
882
-
883
- #define RES_FIELD_UNDERSCORE 10
1118
+ #define RES_FIELD_UNDERSCORE 9
884
1119
  /* Should handle spaces in header fields */
885
1120
  , {.name= "field underscore"
886
1121
  ,.type= HTTP_RESPONSE
@@ -920,7 +1155,7 @@ const struct message responses[] =
920
1155
  ,.body= ""
921
1156
  }
922
1157
 
923
- #define NON_ASCII_IN_STATUS_LINE 11
1158
+ #define NON_ASCII_IN_STATUS_LINE 10
924
1159
  /* Should handle non-ASCII in status line */
925
1160
  , {.name= "non-ASCII in status line"
926
1161
  ,.type= HTTP_RESPONSE
@@ -942,19 +1177,194 @@ const struct message responses[] =
942
1177
  }
943
1178
  ,.body= ""
944
1179
  }
945
-
1180
+
1181
+ #define HTTP_VERSION_0_9 11
1182
+ /* Should handle HTTP/0.9 */
1183
+ , {.name= "http version 0.9"
1184
+ ,.type= HTTP_RESPONSE
1185
+ ,.raw= "HTTP/0.9 200 OK\r\n"
1186
+ "\r\n"
1187
+ ,.should_keep_alive= FALSE
1188
+ ,.message_complete_on_eof= TRUE
1189
+ ,.http_major= 0
1190
+ ,.http_minor= 9
1191
+ ,.status_code= 200
1192
+ ,.num_headers= 0
1193
+ ,.headers=
1194
+ {}
1195
+ ,.body= ""
1196
+ }
1197
+
1198
+ #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1199
+ /* The client should wait for the server's EOF. That is, when neither
1200
+ * content-length nor transfer-encoding is specified, the end of body
1201
+ * is specified by the EOF.
1202
+ */
1203
+ , {.name= "neither content-length nor transfer-encoding response"
1204
+ ,.type= HTTP_RESPONSE
1205
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1206
+ "Content-Type: text/plain\r\n"
1207
+ "\r\n"
1208
+ "hello world"
1209
+ ,.should_keep_alive= FALSE
1210
+ ,.message_complete_on_eof= TRUE
1211
+ ,.http_major= 1
1212
+ ,.http_minor= 1
1213
+ ,.status_code= 200
1214
+ ,.num_headers= 1
1215
+ ,.headers=
1216
+ { { "Content-Type", "text/plain" }
1217
+ }
1218
+ ,.body= "hello world"
1219
+ }
1220
+
1221
+ #define NO_BODY_HTTP10_KA_200 13
1222
+ , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1223
+ ,.type= HTTP_RESPONSE
1224
+ ,.raw= "HTTP/1.0 200 OK\r\n"
1225
+ "Connection: keep-alive\r\n"
1226
+ "\r\n"
1227
+ ,.should_keep_alive= FALSE
1228
+ ,.message_complete_on_eof= TRUE
1229
+ ,.http_major= 1
1230
+ ,.http_minor= 0
1231
+ ,.status_code= 200
1232
+ ,.num_headers= 1
1233
+ ,.headers=
1234
+ { { "Connection", "keep-alive" }
1235
+ }
1236
+ ,.body_size= 0
1237
+ ,.body= ""
1238
+ }
1239
+
1240
+ #define NO_BODY_HTTP10_KA_204 14
1241
+ , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1242
+ ,.type= HTTP_RESPONSE
1243
+ ,.raw= "HTTP/1.0 204 No content\r\n"
1244
+ "Connection: keep-alive\r\n"
1245
+ "\r\n"
1246
+ ,.should_keep_alive= TRUE
1247
+ ,.message_complete_on_eof= FALSE
1248
+ ,.http_major= 1
1249
+ ,.http_minor= 0
1250
+ ,.status_code= 204
1251
+ ,.num_headers= 1
1252
+ ,.headers=
1253
+ { { "Connection", "keep-alive" }
1254
+ }
1255
+ ,.body_size= 0
1256
+ ,.body= ""
1257
+ }
1258
+
1259
+ #define NO_BODY_HTTP11_KA_200 15
1260
+ , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1261
+ ,.type= HTTP_RESPONSE
1262
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1263
+ "\r\n"
1264
+ ,.should_keep_alive= FALSE
1265
+ ,.message_complete_on_eof= TRUE
1266
+ ,.http_major= 1
1267
+ ,.http_minor= 1
1268
+ ,.status_code= 200
1269
+ ,.num_headers= 0
1270
+ ,.headers={}
1271
+ ,.body_size= 0
1272
+ ,.body= ""
1273
+ }
1274
+
1275
+ #define NO_BODY_HTTP11_KA_204 16
1276
+ , {.name= "HTTP/1.1 with a 204 status"
1277
+ ,.type= HTTP_RESPONSE
1278
+ ,.raw= "HTTP/1.1 204 No content\r\n"
1279
+ "\r\n"
1280
+ ,.should_keep_alive= TRUE
1281
+ ,.message_complete_on_eof= FALSE
1282
+ ,.http_major= 1
1283
+ ,.http_minor= 1
1284
+ ,.status_code= 204
1285
+ ,.num_headers= 0
1286
+ ,.headers={}
1287
+ ,.body_size= 0
1288
+ ,.body= ""
1289
+ }
1290
+
1291
+ #define NO_BODY_HTTP11_NOKA_204 17
1292
+ , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1293
+ ,.type= HTTP_RESPONSE
1294
+ ,.raw= "HTTP/1.1 204 No content\r\n"
1295
+ "Connection: close\r\n"
1296
+ "\r\n"
1297
+ ,.should_keep_alive= FALSE
1298
+ ,.message_complete_on_eof= FALSE
1299
+ ,.http_major= 1
1300
+ ,.http_minor= 1
1301
+ ,.status_code= 204
1302
+ ,.num_headers= 1
1303
+ ,.headers=
1304
+ { { "Connection", "close" }
1305
+ }
1306
+ ,.body_size= 0
1307
+ ,.body= ""
1308
+ }
1309
+
1310
+ #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1311
+ , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1312
+ ,.type= HTTP_RESPONSE
1313
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1314
+ "Transfer-Encoding: chunked\r\n"
1315
+ "\r\n"
1316
+ "0\r\n"
1317
+ "\r\n"
1318
+ ,.should_keep_alive= TRUE
1319
+ ,.message_complete_on_eof= FALSE
1320
+ ,.http_major= 1
1321
+ ,.http_minor= 1
1322
+ ,.status_code= 200
1323
+ ,.num_headers= 1
1324
+ ,.headers=
1325
+ { { "Transfer-Encoding", "chunked" }
1326
+ }
1327
+ ,.body_size= 0
1328
+ ,.body= ""
1329
+ }
1330
+
1331
+ #if !HTTP_PARSER_STRICT
1332
+ #define SPACE_IN_FIELD_RES 19
1333
+ /* Should handle spaces in header fields */
1334
+ , {.name= "field space"
1335
+ ,.type= HTTP_RESPONSE
1336
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1337
+ "Server: Microsoft-IIS/6.0\r\n"
1338
+ "X-Powered-By: ASP.NET\r\n"
1339
+ "en-US Content-Type: text/xml\r\n" /* this is the problem */
1340
+ "Content-Type: text/xml\r\n"
1341
+ "Content-Length: 16\r\n"
1342
+ "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
1343
+ "Connection: keep-alive\r\n"
1344
+ "\r\n"
1345
+ "<xml>hello</xml>" /* fake body */
1346
+ ,.should_keep_alive= TRUE
1347
+ ,.message_complete_on_eof= FALSE
1348
+ ,.http_major= 1
1349
+ ,.http_minor= 1
1350
+ ,.status_code= 200
1351
+ ,.num_headers= 7
1352
+ ,.headers=
1353
+ { { "Server", "Microsoft-IIS/6.0" }
1354
+ , { "X-Powered-By", "ASP.NET" }
1355
+ , { "en-US Content-Type", "text/xml" }
1356
+ , { "Content-Type", "text/xml" }
1357
+ , { "Content-Length", "16" }
1358
+ , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1359
+ , { "Connection", "keep-alive" }
1360
+ }
1361
+ ,.body= "<xml>hello</xml>"
1362
+ }
1363
+ #endif /* !HTTP_PARSER_STRICT */
946
1364
 
947
1365
  , {.name= NULL } /* sentinel */
948
1366
  };
949
1367
 
950
- int
951
- request_path_cb (http_parser *p, const char *buf, size_t len)
952
- {
953
- assert(p == parser);
954
- strncat(messages[num_messages].request_path, buf, len);
955
- return 0;
956
- }
957
-
958
1368
  int
959
1369
  request_url_cb (http_parser *p, const char *buf, size_t len)
960
1370
  {
@@ -963,22 +1373,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
963
1373
  return 0;
964
1374
  }
965
1375
 
966
- int
967
- query_string_cb (http_parser *p, const char *buf, size_t len)
968
- {
969
- assert(p == parser);
970
- strncat(messages[num_messages].query_string, buf, len);
971
- return 0;
972
- }
973
-
974
- int
975
- fragment_cb (http_parser *p, const char *buf, size_t len)
976
- {
977
- assert(p == parser);
978
- strncat(messages[num_messages].fragment, buf, len);
979
- return 0;
980
- }
981
-
982
1376
  int
983
1377
  header_field_cb (http_parser *p, const char *buf, size_t len)
984
1378
  {
@@ -1068,14 +1462,151 @@ message_complete_cb (http_parser *p)
1068
1462
  return 0;
1069
1463
  }
1070
1464
 
1465
+ /* These dontcall_* callbacks exist so that we can verify that when we're
1466
+ * paused, no additional callbacks are invoked */
1467
+ int
1468
+ dontcall_message_begin_cb (http_parser *p)
1469
+ {
1470
+ if (p) { } // gcc
1471
+ fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1472
+ exit(1);
1473
+ }
1474
+
1475
+ int
1476
+ dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1477
+ {
1478
+ if (p || buf || len) { } // gcc
1479
+ fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1480
+ exit(1);
1481
+ }
1482
+
1483
+ int
1484
+ dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1485
+ {
1486
+ if (p || buf || len) { } // gcc
1487
+ fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1488
+ exit(1);
1489
+ }
1490
+
1491
+ int
1492
+ dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1493
+ {
1494
+ if (p || buf || len) { } // gcc
1495
+ fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1496
+ exit(1);
1497
+ }
1498
+
1499
+ int
1500
+ dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1501
+ {
1502
+ if (p || buf || len) { } // gcc
1503
+ fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1504
+ exit(1);
1505
+ }
1506
+
1507
+ int
1508
+ dontcall_headers_complete_cb (http_parser *p)
1509
+ {
1510
+ if (p) { } // gcc
1511
+ fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1512
+ "parser ***\n\n");
1513
+ exit(1);
1514
+ }
1515
+
1516
+ int
1517
+ dontcall_message_complete_cb (http_parser *p)
1518
+ {
1519
+ if (p) { } // gcc
1520
+ fprintf(stderr, "\n\n*** on_message_complete() called on paused "
1521
+ "parser ***\n\n");
1522
+ exit(1);
1523
+ }
1524
+
1525
+ static http_parser_settings settings_dontcall =
1526
+ {.on_message_begin = dontcall_message_begin_cb
1527
+ ,.on_header_field = dontcall_header_field_cb
1528
+ ,.on_header_value = dontcall_header_value_cb
1529
+ ,.on_url = dontcall_request_url_cb
1530
+ ,.on_body = dontcall_body_cb
1531
+ ,.on_headers_complete = dontcall_headers_complete_cb
1532
+ ,.on_message_complete = dontcall_message_complete_cb
1533
+ };
1534
+
1535
+ /* These pause_* callbacks always pause the parser and just invoke the regular
1536
+ * callback that tracks content. Before returning, we overwrite the parser
1537
+ * settings to point to the _dontcall variety so that we can verify that
1538
+ * the pause actually did, you know, pause. */
1539
+ int
1540
+ pause_message_begin_cb (http_parser *p)
1541
+ {
1542
+ http_parser_pause(p, 1);
1543
+ *current_pause_parser = settings_dontcall;
1544
+ return message_begin_cb(p);
1545
+ }
1546
+
1547
+ int
1548
+ pause_header_field_cb (http_parser *p, const char *buf, size_t len)
1549
+ {
1550
+ http_parser_pause(p, 1);
1551
+ *current_pause_parser = settings_dontcall;
1552
+ return header_field_cb(p, buf, len);
1553
+ }
1554
+
1555
+ int
1556
+ pause_header_value_cb (http_parser *p, const char *buf, size_t len)
1557
+ {
1558
+ http_parser_pause(p, 1);
1559
+ *current_pause_parser = settings_dontcall;
1560
+ return header_value_cb(p, buf, len);
1561
+ }
1562
+
1563
+ int
1564
+ pause_request_url_cb (http_parser *p, const char *buf, size_t len)
1565
+ {
1566
+ http_parser_pause(p, 1);
1567
+ *current_pause_parser = settings_dontcall;
1568
+ return request_url_cb(p, buf, len);
1569
+ }
1570
+
1571
+ int
1572
+ pause_body_cb (http_parser *p, const char *buf, size_t len)
1573
+ {
1574
+ http_parser_pause(p, 1);
1575
+ *current_pause_parser = settings_dontcall;
1576
+ return body_cb(p, buf, len);
1577
+ }
1578
+
1579
+ int
1580
+ pause_headers_complete_cb (http_parser *p)
1581
+ {
1582
+ http_parser_pause(p, 1);
1583
+ *current_pause_parser = settings_dontcall;
1584
+ return headers_complete_cb(p);
1585
+ }
1586
+
1587
+ int
1588
+ pause_message_complete_cb (http_parser *p)
1589
+ {
1590
+ http_parser_pause(p, 1);
1591
+ *current_pause_parser = settings_dontcall;
1592
+ return message_complete_cb(p);
1593
+ }
1594
+
1595
+ static http_parser_settings settings_pause =
1596
+ {.on_message_begin = pause_message_begin_cb
1597
+ ,.on_header_field = pause_header_field_cb
1598
+ ,.on_header_value = pause_header_value_cb
1599
+ ,.on_url = pause_request_url_cb
1600
+ ,.on_body = pause_body_cb
1601
+ ,.on_headers_complete = pause_headers_complete_cb
1602
+ ,.on_message_complete = pause_message_complete_cb
1603
+ };
1604
+
1071
1605
  static http_parser_settings settings =
1072
1606
  {.on_message_begin = message_begin_cb
1073
1607
  ,.on_header_field = header_field_cb
1074
1608
  ,.on_header_value = header_value_cb
1075
- ,.on_path = request_path_cb
1076
1609
  ,.on_url = request_url_cb
1077
- ,.on_fragment = fragment_cb
1078
- ,.on_query_string = query_string_cb
1079
1610
  ,.on_body = body_cb
1080
1611
  ,.on_headers_complete = headers_complete_cb
1081
1612
  ,.on_message_complete = message_complete_cb
@@ -1085,10 +1616,7 @@ static http_parser_settings settings_count_body =
1085
1616
  {.on_message_begin = message_begin_cb
1086
1617
  ,.on_header_field = header_field_cb
1087
1618
  ,.on_header_value = header_value_cb
1088
- ,.on_path = request_path_cb
1089
1619
  ,.on_url = request_url_cb
1090
- ,.on_fragment = fragment_cb
1091
- ,.on_query_string = query_string_cb
1092
1620
  ,.on_body = count_body_cb
1093
1621
  ,.on_headers_complete = headers_complete_cb
1094
1622
  ,.on_message_complete = message_complete_cb
@@ -1098,10 +1626,7 @@ static http_parser_settings settings_null =
1098
1626
  {.on_message_begin = 0
1099
1627
  ,.on_header_field = 0
1100
1628
  ,.on_header_value = 0
1101
- ,.on_path = 0
1102
1629
  ,.on_url = 0
1103
- ,.on_fragment = 0
1104
- ,.on_query_string = 0
1105
1630
  ,.on_body = 0
1106
1631
  ,.on_headers_complete = 0
1107
1632
  ,.on_message_complete = 0
@@ -1146,12 +1671,29 @@ size_t parse_count_body (const char *buf, size_t len)
1146
1671
  return nparsed;
1147
1672
  }
1148
1673
 
1674
+ size_t parse_pause (const char *buf, size_t len)
1675
+ {
1676
+ size_t nparsed;
1677
+ http_parser_settings s = settings_pause;
1678
+
1679
+ currently_parsing_eof = (len == 0);
1680
+ current_pause_parser = &s;
1681
+ nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
1682
+ return nparsed;
1683
+ }
1684
+
1149
1685
  static inline int
1150
1686
  check_str_eq (const struct message *m,
1151
1687
  const char *prop,
1152
1688
  const char *expected,
1153
1689
  const char *found) {
1154
- if (0 != strcmp(expected, found)) {
1690
+ if ((expected == NULL) != (found == NULL)) {
1691
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1692
+ printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
1693
+ printf(" found %s\n", (found == NULL) ? "NULL" : found);
1694
+ return 0;
1695
+ }
1696
+ if (expected != NULL && 0 != strcmp(expected, found)) {
1155
1697
  printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1156
1698
  printf("expected '%s'\n", expected);
1157
1699
  printf(" found '%s'\n", found);
@@ -1180,6 +1722,20 @@ check_num_eq (const struct message *m,
1180
1722
  #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1181
1723
  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1182
1724
 
1725
+ #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
1726
+ do { \
1727
+ char ubuf[256]; \
1728
+ \
1729
+ if ((u)->field_set & (1 << (fn))) { \
1730
+ memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
1731
+ (u)->field_data[(fn)].len); \
1732
+ ubuf[(u)->field_data[(fn)].len] = '\0'; \
1733
+ } else { \
1734
+ ubuf[0] = '\0'; \
1735
+ } \
1736
+ \
1737
+ check_str_eq(expected, #prop, expected->prop, ubuf); \
1738
+ } while(0)
1183
1739
 
1184
1740
  int
1185
1741
  message_eq (int index, const struct message *expected)
@@ -1204,10 +1760,29 @@ message_eq (int index, const struct message *expected)
1204
1760
  assert(m->message_complete_cb_called);
1205
1761
 
1206
1762
 
1207
- MESSAGE_CHECK_STR_EQ(expected, m, request_path);
1208
- MESSAGE_CHECK_STR_EQ(expected, m, query_string);
1209
- MESSAGE_CHECK_STR_EQ(expected, m, fragment);
1210
1763
  MESSAGE_CHECK_STR_EQ(expected, m, request_url);
1764
+
1765
+ /* Check URL components; we can't do this w/ CONNECT since it doesn't
1766
+ * send us a well-formed URL.
1767
+ */
1768
+ if (*m->request_url && m->method != HTTP_CONNECT) {
1769
+ struct http_parser_url u;
1770
+
1771
+ if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
1772
+ fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
1773
+ m->request_url);
1774
+ exit(1);
1775
+ }
1776
+
1777
+ m->port = (u.field_set & (1 << UF_PORT)) ?
1778
+ u.port : 0;
1779
+
1780
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
1781
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
1782
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
1783
+ MESSAGE_CHECK_NUM_EQ(expected, m, port);
1784
+ }
1785
+
1211
1786
  if (expected->body_size) {
1212
1787
  MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1213
1788
  } else {
@@ -1224,13 +1799,81 @@ message_eq (int index, const struct message *expected)
1224
1799
  if (!r) return 0;
1225
1800
  }
1226
1801
 
1802
+ MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
1803
+
1227
1804
  return 1;
1228
1805
  }
1229
1806
 
1807
+ /* Given a sequence of varargs messages, return the number of them that the
1808
+ * parser should successfully parse, taking into account that upgraded
1809
+ * messages prevent all subsequent messages from being parsed.
1810
+ */
1811
+ size_t
1812
+ count_parsed_messages(const size_t nmsgs, ...) {
1813
+ size_t i;
1814
+ va_list ap;
1815
+
1816
+ va_start(ap, nmsgs);
1817
+
1818
+ for (i = 0; i < nmsgs; i++) {
1819
+ struct message *m = va_arg(ap, struct message *);
1820
+
1821
+ if (m->upgrade) {
1822
+ va_end(ap);
1823
+ return i + 1;
1824
+ }
1825
+ }
1826
+
1827
+ va_end(ap);
1828
+ return nmsgs;
1829
+ }
1830
+
1831
+ /* Given a sequence of bytes and the number of these that we were able to
1832
+ * parse, verify that upgrade bodies are correct.
1833
+ */
1834
+ void
1835
+ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
1836
+ va_list ap;
1837
+ size_t i;
1838
+ size_t off = 0;
1839
+
1840
+ va_start(ap, nmsgs);
1841
+
1842
+ for (i = 0; i < nmsgs; i++) {
1843
+ struct message *m = va_arg(ap, struct message *);
1844
+
1845
+ off += strlen(m->raw);
1846
+
1847
+ if (m->upgrade) {
1848
+ off -= strlen(m->upgrade);
1849
+
1850
+ /* Check the portion of the response after its specified upgrade */
1851
+ if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
1852
+ exit(1);
1853
+ }
1854
+
1855
+ /* Fix up the response so that message_eq() will verify the beginning
1856
+ * of the upgrade */
1857
+ *(body + nread + strlen(m->upgrade)) = '\0';
1858
+ messages[num_messages -1 ].upgrade = body + nread;
1859
+
1860
+ va_end(ap);
1861
+ return;
1862
+ }
1863
+ }
1864
+
1865
+ va_end(ap);
1866
+ printf("\n\n*** Error: expected a message with upgrade ***\n");
1867
+
1868
+ exit(1);
1869
+ }
1870
+
1230
1871
  static void
1231
1872
  print_error (const char *raw, size_t error_location)
1232
1873
  {
1233
- fprintf(stderr, "\n*** parse error ***\n\n");
1874
+ fprintf(stderr, "\n*** %s:%d -- %s ***\n\n",
1875
+ "http_parser.c", HTTP_PARSER_ERRNO_LINE(parser),
1876
+ http_errno_description(HTTP_PARSER_ERRNO(parser)));
1234
1877
 
1235
1878
  int this_line = 0, char_len = 0;
1236
1879
  size_t i, j, len = strlen(raw), error_location_line = 0;
@@ -1268,6 +1911,218 @@ print_error (const char *raw, size_t error_location)
1268
1911
  fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
1269
1912
  }
1270
1913
 
1914
+ void
1915
+ test_preserve_data (void)
1916
+ {
1917
+ char my_data[] = "application-specific data";
1918
+ http_parser parser;
1919
+ parser.data = my_data;
1920
+ http_parser_init(&parser, HTTP_REQUEST);
1921
+ if (parser.data != my_data) {
1922
+ printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
1923
+ exit(1);
1924
+ }
1925
+ }
1926
+
1927
+ struct url_test {
1928
+ const char *name;
1929
+ const char *url;
1930
+ int is_connect;
1931
+ struct http_parser_url u;
1932
+ int rv;
1933
+ };
1934
+
1935
+ const struct url_test url_tests[] =
1936
+ { {.name="proxy request"
1937
+ ,.url="http://hostname/"
1938
+ ,.is_connect=0
1939
+ ,.u=
1940
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
1941
+ ,.port=0
1942
+ ,.field_data=
1943
+ {{ 0, 4 } /* UF_SCHEMA */
1944
+ ,{ 7, 8 } /* UF_HOST */
1945
+ ,{ 0, 0 } /* UF_PORT */
1946
+ ,{ 15, 1 } /* UF_PATH */
1947
+ ,{ 0, 0 } /* UF_QUERY */
1948
+ ,{ 0, 0 } /* UF_FRAGMENT */
1949
+ }
1950
+ }
1951
+ ,.rv=0
1952
+ }
1953
+
1954
+ , {.name="CONNECT request"
1955
+ ,.url="hostname:443"
1956
+ ,.is_connect=1
1957
+ ,.u=
1958
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
1959
+ ,.port=443
1960
+ ,.field_data=
1961
+ {{ 0, 0 } /* UF_SCHEMA */
1962
+ ,{ 0, 8 } /* UF_HOST */
1963
+ ,{ 9, 3 } /* UF_PORT */
1964
+ ,{ 0, 0 } /* UF_PATH */
1965
+ ,{ 0, 0 } /* UF_QUERY */
1966
+ ,{ 0, 0 } /* UF_FRAGMENT */
1967
+ }
1968
+ }
1969
+ ,.rv=0
1970
+ }
1971
+
1972
+ , {.name="proxy ipv6 request"
1973
+ ,.url="http://[1:2::3:4]/"
1974
+ ,.is_connect=0
1975
+ ,.u=
1976
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
1977
+ ,.port=0
1978
+ ,.field_data=
1979
+ {{ 0, 4 } /* UF_SCHEMA */
1980
+ ,{ 8, 8 } /* UF_HOST */
1981
+ ,{ 0, 0 } /* UF_PORT */
1982
+ ,{ 17, 1 } /* UF_PATH */
1983
+ ,{ 0, 0 } /* UF_QUERY */
1984
+ ,{ 0, 0 } /* UF_FRAGMENT */
1985
+ }
1986
+ }
1987
+ ,.rv=0
1988
+ }
1989
+
1990
+ , {.name="CONNECT ipv6 address"
1991
+ ,.url="[1:2::3:4]:443"
1992
+ ,.is_connect=1
1993
+ ,.u=
1994
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
1995
+ ,.port=443
1996
+ ,.field_data=
1997
+ {{ 0, 0 } /* UF_SCHEMA */
1998
+ ,{ 1, 8 } /* UF_HOST */
1999
+ ,{ 11, 3 } /* UF_PORT */
2000
+ ,{ 0, 0 } /* UF_PATH */
2001
+ ,{ 0, 0 } /* UF_QUERY */
2002
+ ,{ 0, 0 } /* UF_FRAGMENT */
2003
+ }
2004
+ }
2005
+ ,.rv=0
2006
+ }
2007
+
2008
+ , {.name="extra ? in query string"
2009
+ ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2010
+ ,.is_connect=0
2011
+ ,.u=
2012
+ {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2013
+ ,.port=0
2014
+ ,.field_data=
2015
+ {{ 0, 4 } /* UF_SCHEMA */
2016
+ ,{ 7, 10 } /* UF_HOST */
2017
+ ,{ 0, 0 } /* UF_PORT */
2018
+ ,{ 17, 12 } /* UF_PATH */
2019
+ ,{ 30,187 } /* UF_QUERY */
2020
+ ,{ 0, 0 } /* UF_FRAGMENT */
2021
+ }
2022
+ }
2023
+ ,.rv=0
2024
+ }
2025
+
2026
+ , {.name="proxy empty host"
2027
+ ,.url="http://:443/"
2028
+ ,.is_connect=0
2029
+ ,.rv=1
2030
+ }
2031
+
2032
+ , {.name="proxy empty port"
2033
+ ,.url="http://hostname:/"
2034
+ ,.is_connect=0
2035
+ ,.rv=1
2036
+ }
2037
+
2038
+ , {.name="CONNECT empty host"
2039
+ ,.url=":443"
2040
+ ,.is_connect=1
2041
+ ,.rv=1
2042
+ }
2043
+
2044
+ , {.name="CONNECT empty port"
2045
+ ,.url="hostname:"
2046
+ ,.is_connect=1
2047
+ ,.rv=1
2048
+ }
2049
+
2050
+ , {.name="CONNECT with extra bits"
2051
+ ,.url="hostname:443/"
2052
+ ,.is_connect=1
2053
+ ,.rv=1
2054
+ }
2055
+ };
2056
+
2057
+ void
2058
+ dump_url (const char *url, const struct http_parser_url *u)
2059
+ {
2060
+ char part[512];
2061
+ unsigned int i;
2062
+
2063
+ printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
2064
+ for (i = 0; i < UF_MAX; i++) {
2065
+ if ((u->field_set & (1 << i)) == 0) {
2066
+ printf("\tfield_data[%u]: unset\n", i);
2067
+ continue;
2068
+ }
2069
+
2070
+ memcpy(part, url + u->field_data[i].off, u->field_data[i].len);
2071
+ part[u->field_data[i].len] = '\0';
2072
+
2073
+ printf("\tfield_data[%u]: off: %u len: %u part: \"%s\"\n",
2074
+ i,
2075
+ u->field_data[i].off,
2076
+ u->field_data[i].len,
2077
+ part);
2078
+ }
2079
+ }
2080
+
2081
+ void
2082
+ test_parse_url (void)
2083
+ {
2084
+ struct http_parser_url u;
2085
+ const struct url_test *test;
2086
+ unsigned int i;
2087
+ int rv;
2088
+
2089
+ for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
2090
+ test = &url_tests[i];
2091
+ memset(&u, 0, sizeof(u));
2092
+
2093
+ rv = http_parser_parse_url(test->url,
2094
+ strlen(test->url),
2095
+ test->is_connect,
2096
+ &u);
2097
+
2098
+ if (test->rv == 0) {
2099
+ if (rv != 0) {
2100
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2101
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
2102
+ exit(1);
2103
+ }
2104
+
2105
+ if (memcmp(&u, &test->u, sizeof(u)) != 0) {
2106
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
2107
+ test->url, test->name);
2108
+
2109
+ printf("target http_parser_url:\n");
2110
+ dump_url(test->url, &test->u);
2111
+ printf("result http_parser_url:\n");
2112
+ dump_url(test->url, &u);
2113
+
2114
+ exit(1);
2115
+ }
2116
+ } else {
2117
+ /* test->rv != 0 */
2118
+ if (rv == 0) {
2119
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2120
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
2121
+ exit(1);
2122
+ }
2123
+ }
2124
+ }
2125
+ }
1271
2126
 
1272
2127
  void
1273
2128
  test_message (const struct message *message)
@@ -1285,7 +2140,10 @@ test_message (const struct message *message)
1285
2140
  if (msg1len) {
1286
2141
  read = parse(msg1, msg1len);
1287
2142
 
1288
- if (message->upgrade && parser->upgrade) goto test;
2143
+ if (message->upgrade && parser->upgrade) {
2144
+ messages[num_messages - 1].upgrade = msg1 + read;
2145
+ goto test;
2146
+ }
1289
2147
 
1290
2148
  if (read != msg1len) {
1291
2149
  print_error(msg1, read);
@@ -1296,7 +2154,10 @@ test_message (const struct message *message)
1296
2154
 
1297
2155
  read = parse(msg2, msg2len);
1298
2156
 
1299
- if (message->upgrade && parser->upgrade) goto test;
2157
+ if (message->upgrade && parser->upgrade) {
2158
+ messages[num_messages - 1].upgrade = msg2 + read;
2159
+ goto test;
2160
+ }
1300
2161
 
1301
2162
  if (read != msg2len) {
1302
2163
  print_error(msg2, read);
@@ -1305,8 +2166,6 @@ test_message (const struct message *message)
1305
2166
 
1306
2167
  read = parse(NULL, 0);
1307
2168
 
1308
- if (message->upgrade && parser->upgrade) goto test;
1309
-
1310
2169
  if (read != 0) {
1311
2170
  print_error(message->raw, read);
1312
2171
  exit(1);
@@ -1362,21 +2221,32 @@ test_message_count_body (const struct message *message)
1362
2221
  }
1363
2222
 
1364
2223
  void
1365
- test_simple (const char *buf, int should_pass)
2224
+ test_simple (const char *buf, enum http_errno err_expected)
1366
2225
  {
1367
2226
  parser_init(HTTP_REQUEST);
1368
2227
 
1369
2228
  size_t parsed;
1370
2229
  int pass;
2230
+ enum http_errno err;
2231
+
1371
2232
  parsed = parse(buf, strlen(buf));
1372
2233
  pass = (parsed == strlen(buf));
2234
+ err = HTTP_PARSER_ERRNO(parser);
1373
2235
  parsed = parse(NULL, 0);
1374
2236
  pass &= (parsed == 0);
1375
2237
 
1376
2238
  parser_free();
1377
2239
 
1378
- if (pass != should_pass) {
1379
- fprintf(stderr, "\n*** test_simple expected %s ***\n\n%s", should_pass ? "success" : "error", buf);
2240
+ /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
2241
+ * long as the caller isn't expecting success.
2242
+ */
2243
+ #if HTTP_PARSER_STRICT
2244
+ if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
2245
+ #else
2246
+ if (err_expected != err) {
2247
+ #endif
2248
+ fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
2249
+ http_errno_name(err_expected), http_errno_name(err), buf);
1380
2250
  exit(1);
1381
2251
  }
1382
2252
  }
@@ -1393,10 +2263,14 @@ test_header_overflow_error (int req)
1393
2263
  assert(parsed == strlen(buf));
1394
2264
 
1395
2265
  buf = "header-key: header-value\r\n";
2266
+ size_t buflen = strlen(buf);
2267
+
1396
2268
  int i;
1397
2269
  for (i = 0; i < 10000; i++) {
1398
- if (http_parser_execute(&parser, &settings_null, buf, strlen(buf)) != strlen(buf)) {
2270
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
2271
+ if (parsed != buflen) {
1399
2272
  //fprintf(stderr, "error found on iter %d\n", i);
2273
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
1400
2274
  return;
1401
2275
  }
1402
2276
  }
@@ -1405,6 +2279,53 @@ test_header_overflow_error (int req)
1405
2279
  exit(1);
1406
2280
  }
1407
2281
 
2282
+ static void
2283
+ test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
2284
+ {
2285
+ http_parser parser;
2286
+ http_parser_init(&parser, HTTP_RESPONSE);
2287
+ http_parser_execute(&parser, &settings_null, buf, buflen);
2288
+
2289
+ if (expect_ok)
2290
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
2291
+ else
2292
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
2293
+ }
2294
+
2295
+ void
2296
+ test_header_content_length_overflow_error (void)
2297
+ {
2298
+ #define X(size) \
2299
+ "HTTP/1.1 200 OK\r\n" \
2300
+ "Content-Length: " #size "\r\n" \
2301
+ "\r\n"
2302
+ const char a[] = X(18446744073709551614); /* 2^64-2 */
2303
+ const char b[] = X(18446744073709551615); /* 2^64-1 */
2304
+ const char c[] = X(18446744073709551616); /* 2^64 */
2305
+ #undef X
2306
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
2307
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2308
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2309
+ }
2310
+
2311
+ void
2312
+ test_chunk_content_length_overflow_error (void)
2313
+ {
2314
+ #define X(size) \
2315
+ "HTTP/1.1 200 OK\r\n" \
2316
+ "Transfer-Encoding: chunked\r\n" \
2317
+ "\r\n" \
2318
+ #size "\r\n" \
2319
+ "..."
2320
+ const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */
2321
+ const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
2322
+ const char c[] = X(10000000000000000); /* 2^64 */
2323
+ #undef X
2324
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
2325
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2326
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2327
+ }
2328
+
1408
2329
  void
1409
2330
  test_no_overflow_long_body (int req, size_t length)
1410
2331
  {
@@ -1441,12 +2362,7 @@ test_no_overflow_long_body (int req, size_t length)
1441
2362
  void
1442
2363
  test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
1443
2364
  {
1444
- int message_count = 1;
1445
- if (!r1->upgrade) {
1446
- message_count++;
1447
- if (!r2->upgrade) message_count++;
1448
- }
1449
- int has_upgrade = (message_count < 3 || r3->upgrade);
2365
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1450
2366
 
1451
2367
  char total[ strlen(r1->raw)
1452
2368
  + strlen(r2->raw)
@@ -1465,7 +2381,10 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
1465
2381
 
1466
2382
  read = parse(total, strlen(total));
1467
2383
 
1468
- if (has_upgrade && parser->upgrade) goto test;
2384
+ if (parser->upgrade) {
2385
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
2386
+ goto test;
2387
+ }
1469
2388
 
1470
2389
  if (read != strlen(total)) {
1471
2390
  print_error(total, read);
@@ -1474,8 +2393,6 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
1474
2393
 
1475
2394
  read = parse(NULL, 0);
1476
2395
 
1477
- if (has_upgrade && parser->upgrade) goto test;
1478
-
1479
2396
  if (read != 0) {
1480
2397
  print_error(total, read);
1481
2398
  exit(1);
@@ -1489,12 +2406,8 @@ test:
1489
2406
  }
1490
2407
 
1491
2408
  if (!message_eq(0, r1)) exit(1);
1492
- if (message_count > 1) {
1493
- if (!message_eq(1, r2)) exit(1);
1494
- if (message_count > 2) {
1495
- if (!message_eq(2, r3)) exit(1);
1496
- }
1497
- }
2409
+ if (message_count > 1 && !message_eq(1, r2)) exit(1);
2410
+ if (message_count > 2 && !message_eq(2, r3)) exit(1);
1498
2411
 
1499
2412
  parser_free();
1500
2413
  }
@@ -1523,6 +2436,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1523
2436
  int ops = 0 ;
1524
2437
 
1525
2438
  size_t buf1_len, buf2_len, buf3_len;
2439
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1526
2440
 
1527
2441
  int i,j,type_both;
1528
2442
  for (type_both = 0; type_both < 2; type_both ++ ) {
@@ -1551,27 +2465,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1551
2465
 
1552
2466
  read = parse(buf1, buf1_len);
1553
2467
 
1554
- if (r3->upgrade && parser->upgrade) goto test;
2468
+ if (parser->upgrade) goto test;
1555
2469
 
1556
2470
  if (read != buf1_len) {
1557
2471
  print_error(buf1, read);
1558
2472
  goto error;
1559
2473
  }
1560
2474
 
1561
- read = parse(buf2, buf2_len);
2475
+ read += parse(buf2, buf2_len);
1562
2476
 
1563
- if (r3->upgrade && parser->upgrade) goto test;
2477
+ if (parser->upgrade) goto test;
1564
2478
 
1565
- if (read != buf2_len) {
2479
+ if (read != buf1_len + buf2_len) {
1566
2480
  print_error(buf2, read);
1567
2481
  goto error;
1568
2482
  }
1569
2483
 
1570
- read = parse(buf3, buf3_len);
2484
+ read += parse(buf3, buf3_len);
1571
2485
 
1572
- if (r3->upgrade && parser->upgrade) goto test;
2486
+ if (parser->upgrade) goto test;
1573
2487
 
1574
- if (read != buf3_len) {
2488
+ if (read != buf1_len + buf2_len + buf3_len) {
1575
2489
  print_error(buf3, read);
1576
2490
  goto error;
1577
2491
  }
@@ -1579,9 +2493,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1579
2493
  parse(NULL, 0);
1580
2494
 
1581
2495
  test:
2496
+ if (parser->upgrade) {
2497
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
2498
+ }
1582
2499
 
1583
- if (3 != num_messages) {
1584
- fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages);
2500
+ if (message_count != num_messages) {
2501
+ fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
2502
+ message_count, num_messages);
1585
2503
  goto error;
1586
2504
  }
1587
2505
 
@@ -1590,12 +2508,12 @@ test:
1590
2508
  goto error;
1591
2509
  }
1592
2510
 
1593
- if (!message_eq(1, r2)) {
2511
+ if (message_count > 1 && !message_eq(1, r2)) {
1594
2512
  fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
1595
2513
  goto error;
1596
2514
  }
1597
2515
 
1598
- if (!message_eq(2, r3)) {
2516
+ if (message_count > 2 && !message_eq(2, r3)) {
1599
2517
  fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
1600
2518
  goto error;
1601
2519
  }
@@ -1646,6 +2564,58 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
1646
2564
  return buf;
1647
2565
  }
1648
2566
 
2567
+ /* Verify that we can pause parsing at any of the bytes in the
2568
+ * message and still get the result that we're expecting. */
2569
+ void
2570
+ test_message_pause (const struct message *msg)
2571
+ {
2572
+ char *buf = (char*) msg->raw;
2573
+ size_t buflen = strlen(msg->raw);
2574
+ size_t nread;
2575
+
2576
+ parser_init(msg->type);
2577
+
2578
+ do {
2579
+ nread = parse_pause(buf, buflen);
2580
+
2581
+ // We can only set the upgrade buffer once we've gotten our message
2582
+ // completion callback.
2583
+ if (messages[0].message_complete_cb_called &&
2584
+ msg->upgrade &&
2585
+ parser->upgrade) {
2586
+ messages[0].upgrade = buf + nread;
2587
+ goto test;
2588
+ }
2589
+
2590
+ if (nread < buflen) {
2591
+
2592
+ // Not much do to if we failed a strict-mode check
2593
+ if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
2594
+ parser_free();
2595
+ return;
2596
+ }
2597
+
2598
+ assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
2599
+ }
2600
+
2601
+ buf += nread;
2602
+ buflen -= nread;
2603
+ http_parser_pause(parser, 0);
2604
+ } while (buflen > 0);
2605
+
2606
+ nread = parse_pause(NULL, 0);
2607
+ assert (nread == 0);
2608
+
2609
+ test:
2610
+ if (num_messages != 1) {
2611
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
2612
+ exit(1);
2613
+ }
2614
+
2615
+ if(!message_eq(0, msg)) exit(1);
2616
+
2617
+ parser_free();
2618
+ }
1649
2619
 
1650
2620
  int
1651
2621
  main (void)
@@ -1660,6 +2630,10 @@ main (void)
1660
2630
  for (request_count = 0; requests[request_count].name; request_count++);
1661
2631
  for (response_count = 0; responses[response_count].name; response_count++);
1662
2632
 
2633
+ //// API
2634
+ test_preserve_data();
2635
+ test_parse_url();
2636
+
1663
2637
  //// OVERFLOW CONDITIONS
1664
2638
 
1665
2639
  test_header_overflow_error(HTTP_REQUEST);
@@ -1670,12 +2644,19 @@ main (void)
1670
2644
  test_no_overflow_long_body(HTTP_RESPONSE, 1000);
1671
2645
  test_no_overflow_long_body(HTTP_RESPONSE, 100000);
1672
2646
 
2647
+ test_header_content_length_overflow_error();
2648
+ test_chunk_content_length_overflow_error();
2649
+
1673
2650
  //// RESPONSES
1674
2651
 
1675
2652
  for (i = 0; i < response_count; i++) {
1676
2653
  test_message(&responses[i]);
1677
2654
  }
1678
2655
 
2656
+ for (i = 0; i < response_count; i++) {
2657
+ test_message_pause(&responses[i]);
2658
+ }
2659
+
1679
2660
  for (i = 0; i < response_count; i++) {
1680
2661
  if (!responses[i].should_keep_alive) continue;
1681
2662
  for (j = 0; j < response_count; j++) {
@@ -1720,7 +2701,7 @@ main (void)
1720
2701
 
1721
2702
  printf("response scan 1/2 ");
1722
2703
  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
1723
- , &responses[NO_HEADERS_NO_BODY_404]
2704
+ , &responses[NO_BODY_HTTP10_KA_204]
1724
2705
  , &responses[NO_REASON_PHRASE]
1725
2706
  );
1726
2707
 
@@ -1735,13 +2716,21 @@ main (void)
1735
2716
 
1736
2717
  /// REQUESTS
1737
2718
 
2719
+ test_simple("hello world", HPE_INVALID_METHOD);
2720
+ test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
1738
2721
 
1739
- test_simple("hello world", 0);
1740
- test_simple("GET / HTP/1.1\r\n\r\n", 0);
1741
2722
 
1742
- test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1743
- test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1744
- test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
2723
+ test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
2724
+ test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
2725
+ test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
2726
+
2727
+ // Well-formed but incomplete
2728
+ test_simple("GET / HTTP/1.1\r\n"
2729
+ "Content-Type: text/plain\r\n"
2730
+ "Content-Length: 6\r\n"
2731
+ "\r\n"
2732
+ "fooba",
2733
+ HPE_OK);
1745
2734
 
1746
2735
  static const char *all_methods[] = {
1747
2736
  "DELETE",
@@ -1759,12 +2748,31 @@ main (void)
1759
2748
  "PROPFIND",
1760
2749
  "PROPPATCH",
1761
2750
  "UNLOCK",
2751
+ "REPORT",
2752
+ "MKACTIVITY",
2753
+ "CHECKOUT",
2754
+ "MERGE",
2755
+ "M-SEARCH",
2756
+ "NOTIFY",
2757
+ "SUBSCRIBE",
2758
+ "UNSUBSCRIBE",
2759
+ "PATCH",
1762
2760
  0 };
1763
2761
  const char **this_method;
1764
2762
  for (this_method = all_methods; *this_method; this_method++) {
1765
2763
  char buf[200];
1766
2764
  sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
1767
- test_simple(buf, 1);
2765
+ test_simple(buf, HPE_OK);
2766
+ }
2767
+
2768
+ static const char *bad_methods[] = {
2769
+ "C******",
2770
+ "M****",
2771
+ 0 };
2772
+ for (this_method = bad_methods; *this_method; this_method++) {
2773
+ char buf[200];
2774
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
2775
+ test_simple(buf, HPE_UNKNOWN);
1768
2776
  }
1769
2777
 
1770
2778
  const char *dumbfuck2 =
@@ -1802,7 +2810,7 @@ main (void)
1802
2810
  "\tRA==\r\n"
1803
2811
  "\t-----END CERTIFICATE-----\r\n"
1804
2812
  "\r\n";
1805
- test_simple(dumbfuck2, 0);
2813
+ test_simple(dumbfuck2, HPE_OK);
1806
2814
 
1807
2815
  #if 0
1808
2816
  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
@@ -1824,7 +2832,9 @@ main (void)
1824
2832
  test_message(&requests[i]);
1825
2833
  }
1826
2834
 
1827
-
2835
+ for (i = 0; i < request_count; i++) {
2836
+ test_message_pause(&requests[i]);
2837
+ }
1828
2838
 
1829
2839
  for (i = 0; i < request_count; i++) {
1830
2840
  if (!requests[i].should_keep_alive) continue;