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
@@ -31,7 +31,7 @@
31
31
  #undef FALSE
32
32
  #define FALSE 0
33
33
 
34
- #define MAX_HEADERS 10
34
+ #define MAX_HEADERS 13
35
35
  #define MAX_ELEMENT_SIZE 500
36
36
 
37
37
  #define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -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 home.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= "home.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
  }
@@ -538,6 +543,313 @@ const struct message requests[] =
538
543
  ,.body= ""
539
544
  }
540
545
 
546
+ #define NO_HTTP_VERSION 19
547
+ , {.name= "request with no http version"
548
+ ,.type= HTTP_REQUEST
549
+ ,.raw= "GET /\r\n"
550
+ "\r\n"
551
+ ,.should_keep_alive= FALSE
552
+ ,.message_complete_on_eof= FALSE
553
+ ,.http_major= 0
554
+ ,.http_minor= 9
555
+ ,.method= HTTP_GET
556
+ ,.query_string= ""
557
+ ,.fragment= ""
558
+ ,.request_path= "/"
559
+ ,.request_url= "/"
560
+ ,.num_headers= 0
561
+ ,.headers= {}
562
+ ,.body= ""
563
+ }
564
+
565
+ #define MSEARCH_REQ 20
566
+ , {.name= "m-search request"
567
+ ,.type= HTTP_REQUEST
568
+ ,.raw= "M-SEARCH * HTTP/1.1\r\n"
569
+ "HOST: 239.255.255.250:1900\r\n"
570
+ "MAN: \"ssdp:discover\"\r\n"
571
+ "ST: \"ssdp:all\"\r\n"
572
+ "\r\n"
573
+ ,.should_keep_alive= TRUE
574
+ ,.message_complete_on_eof= FALSE
575
+ ,.http_major= 1
576
+ ,.http_minor= 1
577
+ ,.method= HTTP_MSEARCH
578
+ ,.query_string= ""
579
+ ,.fragment= ""
580
+ ,.request_path= "*"
581
+ ,.request_url= "*"
582
+ ,.num_headers= 3
583
+ ,.headers= { { "HOST", "239.255.255.250:1900" }
584
+ , { "MAN", "\"ssdp:discover\"" }
585
+ , { "ST", "\"ssdp:all\"" }
586
+ }
587
+ ,.body= ""
588
+ }
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
+
541
853
  , {.name= NULL } /* sentinel */
542
854
  };
543
855
 
@@ -551,9 +863,10 @@ const struct message responses[] =
551
863
  "Content-Type: text/html; charset=UTF-8\r\n"
552
864
  "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
553
865
  "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
866
+ "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
554
867
  "Cache-Control: public, max-age=2592000\r\n"
555
868
  "Server: gws\r\n"
556
- "Content-Length: 219\r\n"
869
+ "Content-Length: 219 \r\n"
557
870
  "\r\n"
558
871
  "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
559
872
  "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
@@ -566,15 +879,16 @@ const struct message responses[] =
566
879
  ,.http_major= 1
567
880
  ,.http_minor= 1
568
881
  ,.status_code= 301
569
- ,.num_headers= 7
882
+ ,.num_headers= 8
570
883
  ,.headers=
571
884
  { { "Location", "http://www.google.com/" }
572
885
  , { "Content-Type", "text/html; charset=UTF-8" }
573
886
  , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
574
887
  , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
888
+ , { "X-$PrototypeBI-Version", "1.6.0.3" }
575
889
  , { "Cache-Control", "public, max-age=2592000" }
576
890
  , { "Server", "gws" }
577
- , { "Content-Length", "219" }
891
+ , { "Content-Length", "219 " }
578
892
  }
579
893
  ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
580
894
  "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
@@ -636,8 +950,8 @@ const struct message responses[] =
636
950
  , {.name= "404 no headers no body"
637
951
  ,.type= HTTP_RESPONSE
638
952
  ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
639
- ,.should_keep_alive= TRUE
640
- ,.message_complete_on_eof= FALSE
953
+ ,.should_keep_alive= FALSE
954
+ ,.message_complete_on_eof= TRUE
641
955
  ,.http_major= 1
642
956
  ,.http_minor= 1
643
957
  ,.status_code= 404
@@ -651,8 +965,8 @@ const struct message responses[] =
651
965
  , {.name= "301 no response phrase"
652
966
  ,.type= HTTP_RESPONSE
653
967
  ,.raw= "HTTP/1.1 301\r\n\r\n"
654
- ,.should_keep_alive = TRUE
655
- ,.message_complete_on_eof= FALSE
968
+ ,.should_keep_alive = FALSE
969
+ ,.message_complete_on_eof= TRUE
656
970
  ,.http_major= 1
657
971
  ,.http_minor= 1
658
972
  ,.status_code= 301
@@ -801,7 +1115,221 @@ const struct message responses[] =
801
1115
  ,.body= ""
802
1116
  }
803
1117
 
804
- #define SPACE_IN_FIELD_RES 9
1118
+ #define RES_FIELD_UNDERSCORE 9
1119
+ /* Should handle spaces in header fields */
1120
+ , {.name= "field underscore"
1121
+ ,.type= HTTP_RESPONSE
1122
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1123
+ "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1124
+ "Server: Apache\r\n"
1125
+ "Cache-Control: no-cache, must-revalidate\r\n"
1126
+ "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1127
+ ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1128
+ "Vary: Accept-Encoding\r\n"
1129
+ "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1130
+ "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1131
+ "Transfer-Encoding: chunked\r\n"
1132
+ "Content-Type: text/html\r\n"
1133
+ "Connection: close\r\n"
1134
+ "\r\n"
1135
+ "0\r\n\r\n"
1136
+ ,.should_keep_alive= FALSE
1137
+ ,.message_complete_on_eof= FALSE
1138
+ ,.http_major= 1
1139
+ ,.http_minor= 1
1140
+ ,.status_code= 200
1141
+ ,.num_headers= 11
1142
+ ,.headers=
1143
+ { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1144
+ , { "Server", "Apache" }
1145
+ , { "Cache-Control", "no-cache, must-revalidate" }
1146
+ , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1147
+ , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1148
+ , { "Vary", "Accept-Encoding" }
1149
+ , { "_eep-Alive", "timeout=45" }
1150
+ , { "_onnection", "Keep-Alive" }
1151
+ , { "Transfer-Encoding", "chunked" }
1152
+ , { "Content-Type", "text/html" }
1153
+ , { "Connection", "close" }
1154
+ }
1155
+ ,.body= ""
1156
+ }
1157
+
1158
+ #define NON_ASCII_IN_STATUS_LINE 10
1159
+ /* Should handle non-ASCII in status line */
1160
+ , {.name= "non-ASCII in status line"
1161
+ ,.type= HTTP_RESPONSE
1162
+ ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1163
+ "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1164
+ "Content-Length: 0\r\n"
1165
+ "Connection: close\r\n"
1166
+ "\r\n"
1167
+ ,.should_keep_alive= FALSE
1168
+ ,.message_complete_on_eof= FALSE
1169
+ ,.http_major= 1
1170
+ ,.http_minor= 1
1171
+ ,.status_code= 500
1172
+ ,.num_headers= 3
1173
+ ,.headers=
1174
+ { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1175
+ , { "Content-Length", "0" }
1176
+ , { "Connection", "close" }
1177
+ }
1178
+ ,.body= ""
1179
+ }
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
805
1333
  /* Should handle spaces in header fields */
806
1334
  , {.name= "field space"
807
1335
  ,.type= HTTP_RESPONSE
@@ -832,18 +1360,11 @@ const struct message responses[] =
832
1360
  }
833
1361
  ,.body= "<xml>hello</xml>"
834
1362
  }
1363
+ #endif /* !HTTP_PARSER_STRICT */
835
1364
 
836
1365
  , {.name= NULL } /* sentinel */
837
1366
  };
838
1367
 
839
- int
840
- request_path_cb (http_parser *p, const char *buf, size_t len)
841
- {
842
- assert(p == parser);
843
- strncat(messages[num_messages].request_path, buf, len);
844
- return 0;
845
- }
846
-
847
1368
  int
848
1369
  request_url_cb (http_parser *p, const char *buf, size_t len)
849
1370
  {
@@ -852,22 +1373,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
852
1373
  return 0;
853
1374
  }
854
1375
 
855
- int
856
- query_string_cb (http_parser *p, const char *buf, size_t len)
857
- {
858
- assert(p == parser);
859
- strncat(messages[num_messages].query_string, buf, len);
860
- return 0;
861
- }
862
-
863
- int
864
- fragment_cb (http_parser *p, const char *buf, size_t len)
865
- {
866
- assert(p == parser);
867
- strncat(messages[num_messages].fragment, buf, len);
868
- return 0;
869
- }
870
-
871
1376
  int
872
1377
  header_field_cb (http_parser *p, const char *buf, size_t len)
873
1378
  {
@@ -947,7 +1452,7 @@ message_complete_cb (http_parser *p)
947
1452
  "value in both on_message_complete and on_headers_complete "
948
1453
  "but it doesn't! ***\n\n");
949
1454
  assert(0);
950
- exit(1);
1455
+ abort();
951
1456
  }
952
1457
  messages[num_messages].message_complete_cb_called = TRUE;
953
1458
 
@@ -957,14 +1462,151 @@ message_complete_cb (http_parser *p)
957
1462
  return 0;
958
1463
  }
959
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
+ abort();
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
+ abort();
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
+ abort();
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
+ abort();
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
+ abort();
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
+ abort();
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
+ abort();
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
+
960
1605
  static http_parser_settings settings =
961
1606
  {.on_message_begin = message_begin_cb
962
1607
  ,.on_header_field = header_field_cb
963
1608
  ,.on_header_value = header_value_cb
964
- ,.on_path = request_path_cb
965
1609
  ,.on_url = request_url_cb
966
- ,.on_fragment = fragment_cb
967
- ,.on_query_string = query_string_cb
968
1610
  ,.on_body = body_cb
969
1611
  ,.on_headers_complete = headers_complete_cb
970
1612
  ,.on_message_complete = message_complete_cb
@@ -974,10 +1616,7 @@ static http_parser_settings settings_count_body =
974
1616
  {.on_message_begin = message_begin_cb
975
1617
  ,.on_header_field = header_field_cb
976
1618
  ,.on_header_value = header_value_cb
977
- ,.on_path = request_path_cb
978
1619
  ,.on_url = request_url_cb
979
- ,.on_fragment = fragment_cb
980
- ,.on_query_string = query_string_cb
981
1620
  ,.on_body = count_body_cb
982
1621
  ,.on_headers_complete = headers_complete_cb
983
1622
  ,.on_message_complete = message_complete_cb
@@ -987,10 +1626,7 @@ static http_parser_settings settings_null =
987
1626
  {.on_message_begin = 0
988
1627
  ,.on_header_field = 0
989
1628
  ,.on_header_value = 0
990
- ,.on_path = 0
991
1629
  ,.on_url = 0
992
- ,.on_fragment = 0
993
- ,.on_query_string = 0
994
1630
  ,.on_body = 0
995
1631
  ,.on_headers_complete = 0
996
1632
  ,.on_message_complete = 0
@@ -1035,12 +1671,29 @@ size_t parse_count_body (const char *buf, size_t len)
1035
1671
  return nparsed;
1036
1672
  }
1037
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
+
1038
1685
  static inline int
1039
1686
  check_str_eq (const struct message *m,
1040
1687
  const char *prop,
1041
1688
  const char *expected,
1042
1689
  const char *found) {
1043
- 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)) {
1044
1697
  printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1045
1698
  printf("expected '%s'\n", expected);
1046
1699
  printf(" found '%s'\n", found);
@@ -1069,6 +1722,20 @@ check_num_eq (const struct message *m,
1069
1722
  #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1070
1723
  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1071
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)
1072
1739
 
1073
1740
  int
1074
1741
  message_eq (int index, const struct message *expected)
@@ -1093,10 +1760,29 @@ message_eq (int index, const struct message *expected)
1093
1760
  assert(m->message_complete_cb_called);
1094
1761
 
1095
1762
 
1096
- MESSAGE_CHECK_STR_EQ(expected, m, request_path);
1097
- MESSAGE_CHECK_STR_EQ(expected, m, query_string);
1098
- MESSAGE_CHECK_STR_EQ(expected, m, fragment);
1099
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
+ abort();
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
+
1100
1786
  if (expected->body_size) {
1101
1787
  MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1102
1788
  } else {
@@ -1113,13 +1799,81 @@ message_eq (int index, const struct message *expected)
1113
1799
  if (!r) return 0;
1114
1800
  }
1115
1801
 
1802
+ MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
1803
+
1116
1804
  return 1;
1117
1805
  }
1118
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
+ abort();
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
+ abort();
1869
+ }
1870
+
1119
1871
  static void
1120
1872
  print_error (const char *raw, size_t error_location)
1121
1873
  {
1122
- 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)));
1123
1877
 
1124
1878
  int this_line = 0, char_len = 0;
1125
1879
  size_t i, j, len = strlen(raw), error_location_line = 0;
@@ -1157,6 +1911,218 @@ print_error (const char *raw, size_t error_location)
1157
1911
  fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
1158
1912
  }
1159
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
+ abort();
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
+ abort();
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
+ abort();
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
+ abort();
2122
+ }
2123
+ }
2124
+ }
2125
+ }
1160
2126
 
1161
2127
  void
1162
2128
  test_message (const struct message *message)
@@ -1174,41 +2140,45 @@ test_message (const struct message *message)
1174
2140
  if (msg1len) {
1175
2141
  read = parse(msg1, msg1len);
1176
2142
 
1177
- 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
+ }
1178
2147
 
1179
2148
  if (read != msg1len) {
1180
2149
  print_error(msg1, read);
1181
- exit(1);
2150
+ abort();
1182
2151
  }
1183
2152
  }
1184
2153
 
1185
2154
 
1186
2155
  read = parse(msg2, msg2len);
1187
2156
 
1188
- 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
+ }
1189
2161
 
1190
2162
  if (read != msg2len) {
1191
2163
  print_error(msg2, read);
1192
- exit(1);
2164
+ abort();
1193
2165
  }
1194
2166
 
1195
2167
  read = parse(NULL, 0);
1196
2168
 
1197
- if (message->upgrade && parser->upgrade) goto test;
1198
-
1199
2169
  if (read != 0) {
1200
2170
  print_error(message->raw, read);
1201
- exit(1);
2171
+ abort();
1202
2172
  }
1203
2173
 
1204
2174
  test:
1205
2175
 
1206
2176
  if (num_messages != 1) {
1207
2177
  printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
1208
- exit(1);
2178
+ abort();
1209
2179
  }
1210
2180
 
1211
- if(!message_eq(0, message)) exit(1);
2181
+ if(!message_eq(0, message)) abort();
1212
2182
 
1213
2183
  parser_free();
1214
2184
  }
@@ -1229,7 +2199,7 @@ test_message_count_body (const struct message *message)
1229
2199
  read = parse_count_body(message->raw + i, toread);
1230
2200
  if (read != toread) {
1231
2201
  print_error(message->raw, read);
1232
- exit(1);
2202
+ abort();
1233
2203
  }
1234
2204
  }
1235
2205
 
@@ -1237,36 +2207,47 @@ test_message_count_body (const struct message *message)
1237
2207
  read = parse_count_body(NULL, 0);
1238
2208
  if (read != 0) {
1239
2209
  print_error(message->raw, read);
1240
- exit(1);
2210
+ abort();
1241
2211
  }
1242
2212
 
1243
2213
  if (num_messages != 1) {
1244
2214
  printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
1245
- exit(1);
2215
+ abort();
1246
2216
  }
1247
2217
 
1248
- if(!message_eq(0, message)) exit(1);
2218
+ if(!message_eq(0, message)) abort();
1249
2219
 
1250
2220
  parser_free();
1251
2221
  }
1252
2222
 
1253
2223
  void
1254
- test_simple (const char *buf, int should_pass)
2224
+ test_simple (const char *buf, enum http_errno err_expected)
1255
2225
  {
1256
2226
  parser_init(HTTP_REQUEST);
1257
2227
 
1258
2228
  size_t parsed;
1259
2229
  int pass;
2230
+ enum http_errno err;
2231
+
1260
2232
  parsed = parse(buf, strlen(buf));
1261
2233
  pass = (parsed == strlen(buf));
2234
+ err = HTTP_PARSER_ERRNO(parser);
1262
2235
  parsed = parse(NULL, 0);
1263
2236
  pass &= (parsed == 0);
1264
2237
 
1265
2238
  parser_free();
1266
2239
 
1267
- if (pass != should_pass) {
1268
- fprintf(stderr, "\n*** test_simple expected %s ***\n\n%s", should_pass ? "success" : "error", buf);
1269
- exit(1);
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);
2250
+ abort();
1270
2251
  }
1271
2252
  }
1272
2253
 
@@ -1282,16 +2263,67 @@ test_header_overflow_error (int req)
1282
2263
  assert(parsed == strlen(buf));
1283
2264
 
1284
2265
  buf = "header-key: header-value\r\n";
2266
+ size_t buflen = strlen(buf);
2267
+
1285
2268
  int i;
1286
2269
  for (i = 0; i < 10000; i++) {
1287
- 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) {
1288
2272
  //fprintf(stderr, "error found on iter %d\n", i);
2273
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
1289
2274
  return;
1290
2275
  }
1291
2276
  }
1292
2277
 
1293
2278
  fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
1294
- exit(1);
2279
+ abort();
2280
+ }
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 */
1295
2327
  }
1296
2328
 
1297
2329
  void
@@ -1324,18 +2356,13 @@ test_no_overflow_long_body (int req, size_t length)
1324
2356
  "\n*** error in test_no_overflow_long_body %s of length %zu ***\n",
1325
2357
  req ? "REQUEST" : "RESPONSE",
1326
2358
  length);
1327
- exit(1);
2359
+ abort();
1328
2360
  }
1329
2361
 
1330
2362
  void
1331
2363
  test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
1332
2364
  {
1333
- int message_count = 1;
1334
- if (!r1->upgrade) {
1335
- message_count++;
1336
- if (!r2->upgrade) message_count++;
1337
- }
1338
- int has_upgrade = (message_count < 3 || r3->upgrade);
2365
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1339
2366
 
1340
2367
  char total[ strlen(r1->raw)
1341
2368
  + strlen(r2->raw)
@@ -1354,36 +2381,33 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
1354
2381
 
1355
2382
  read = parse(total, strlen(total));
1356
2383
 
1357
- 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
+ }
1358
2388
 
1359
2389
  if (read != strlen(total)) {
1360
2390
  print_error(total, read);
1361
- exit(1);
2391
+ abort();
1362
2392
  }
1363
2393
 
1364
2394
  read = parse(NULL, 0);
1365
2395
 
1366
- if (has_upgrade && parser->upgrade) goto test;
1367
-
1368
2396
  if (read != 0) {
1369
2397
  print_error(total, read);
1370
- exit(1);
2398
+ abort();
1371
2399
  }
1372
2400
 
1373
2401
  test:
1374
2402
 
1375
2403
  if (message_count != num_messages) {
1376
2404
  fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
1377
- exit(1);
2405
+ abort();
1378
2406
  }
1379
2407
 
1380
- if (!message_eq(0, r1)) exit(1);
1381
- if (message_count > 1) {
1382
- if (!message_eq(1, r2)) exit(1);
1383
- if (message_count > 2) {
1384
- if (!message_eq(2, r3)) exit(1);
1385
- }
1386
- }
2408
+ if (!message_eq(0, r1)) abort();
2409
+ if (message_count > 1 && !message_eq(1, r2)) abort();
2410
+ if (message_count > 2 && !message_eq(2, r3)) abort();
1387
2411
 
1388
2412
  parser_free();
1389
2413
  }
@@ -1412,6 +2436,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1412
2436
  int ops = 0 ;
1413
2437
 
1414
2438
  size_t buf1_len, buf2_len, buf3_len;
2439
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1415
2440
 
1416
2441
  int i,j,type_both;
1417
2442
  for (type_both = 0; type_both < 2; type_both ++ ) {
@@ -1440,27 +2465,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1440
2465
 
1441
2466
  read = parse(buf1, buf1_len);
1442
2467
 
1443
- if (r3->upgrade && parser->upgrade) goto test;
2468
+ if (parser->upgrade) goto test;
1444
2469
 
1445
2470
  if (read != buf1_len) {
1446
2471
  print_error(buf1, read);
1447
2472
  goto error;
1448
2473
  }
1449
2474
 
1450
- read = parse(buf2, buf2_len);
2475
+ read += parse(buf2, buf2_len);
1451
2476
 
1452
- if (r3->upgrade && parser->upgrade) goto test;
2477
+ if (parser->upgrade) goto test;
1453
2478
 
1454
- if (read != buf2_len) {
2479
+ if (read != buf1_len + buf2_len) {
1455
2480
  print_error(buf2, read);
1456
2481
  goto error;
1457
2482
  }
1458
2483
 
1459
- read = parse(buf3, buf3_len);
2484
+ read += parse(buf3, buf3_len);
1460
2485
 
1461
- if (r3->upgrade && parser->upgrade) goto test;
2486
+ if (parser->upgrade) goto test;
1462
2487
 
1463
- if (read != buf3_len) {
2488
+ if (read != buf1_len + buf2_len + buf3_len) {
1464
2489
  print_error(buf3, read);
1465
2490
  goto error;
1466
2491
  }
@@ -1468,9 +2493,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1468
2493
  parse(NULL, 0);
1469
2494
 
1470
2495
  test:
2496
+ if (parser->upgrade) {
2497
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
2498
+ }
1471
2499
 
1472
- if (3 != num_messages) {
1473
- 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);
1474
2503
  goto error;
1475
2504
  }
1476
2505
 
@@ -1479,12 +2508,12 @@ test:
1479
2508
  goto error;
1480
2509
  }
1481
2510
 
1482
- if (!message_eq(1, r2)) {
2511
+ if (message_count > 1 && !message_eq(1, r2)) {
1483
2512
  fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
1484
2513
  goto error;
1485
2514
  }
1486
2515
 
1487
- if (!message_eq(2, r3)) {
2516
+ if (message_count > 2 && !message_eq(2, r3)) {
1488
2517
  fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
1489
2518
  goto error;
1490
2519
  }
@@ -1501,7 +2530,7 @@ test:
1501
2530
  fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
1502
2531
  fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
1503
2532
  fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
1504
- exit(1);
2533
+ abort();
1505
2534
  }
1506
2535
 
1507
2536
  // user required to free the result
@@ -1535,100 +2564,111 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
1535
2564
  return buf;
1536
2565
  }
1537
2566
 
1538
- char *
2567
+ char *
1539
2568
  quote(const char * orig) {
1540
- size_t j, i, len = strlen(orig);
1541
- char * quoted = malloc(len*2); // hm..
1542
- bzero(quoted, len*2);
1543
- for (i=0, j=0; i!=len; ++i) {
1544
- switch (orig[i]){
1545
- case '\n':
1546
- quoted[j++] = '\\';
1547
- quoted[j++] = 'n';
1548
- break;
1549
- case '\r':
1550
- quoted[j++] = '\\';
1551
- quoted[j++] = 'r';
1552
- break;
1553
- case '"':
1554
- quoted[j++] = '\\';
1555
- quoted[j++] = '"';
1556
- break;
1557
- default :
1558
- quoted[j++] = orig[i];
1559
- }
1560
- }
1561
- return quoted;
2569
+ if (!orig) return NULL;
2570
+ size_t j, i, len = strlen(orig);
2571
+ char * quoted = malloc(len == 0 ? 1 : len*2); // hm..
2572
+ bzero(quoted, len == 0 ? 1 : len*2);
2573
+ for (i=0, j=0; i!=len; ++i) {
2574
+ switch (orig[i]){
2575
+ case '\n':
2576
+ quoted[j++] = '\\';
2577
+ quoted[j++] = 'n';
2578
+ break;
2579
+ case '\r':
2580
+ quoted[j++] = '\\';
2581
+ quoted[j++] = 'r';
2582
+ break;
2583
+ case '"':
2584
+ quoted[j++] = '\\';
2585
+ quoted[j++] = '"';
2586
+ break;
2587
+ default :
2588
+ quoted[j++] = orig[i];
2589
+ }
2590
+ }
2591
+ return quoted;
1562
2592
  }
1563
2593
 
1564
- void
2594
+ void
1565
2595
  dump_message(const struct message * m)
1566
2596
  {
1567
- int i;
1568
- printf("name :%s\n", m->name);
1569
- char * bla = quote(m->raw);
1570
- printf("raw :\"%s\"\n", bla);
1571
- free(bla);
1572
- switch (m->type){
1573
- case HTTP_REQUEST:
1574
- printf("type :HTTP_REQUEST\n");break;
1575
- case HTTP_RESPONSE:
1576
- printf("type :HTTP_RESPONSE\n"); break;
1577
- case HTTP_BOTH:
1578
- printf("type :HTTP_BOTH\n");
1579
- }
1580
- switch (m->method) {
1581
- case HTTP_DELETE: printf("method: HTTP_DELETE\n");break;
1582
- case HTTP_GET: printf("method: HTTP_GET\n");break;
1583
- case HTTP_HEAD: printf("method: HTTP_HEAD\n");break;
1584
- case HTTP_POST: printf("method: HTTP_POST\n");break;
1585
- case HTTP_PUT: printf("method: HTTP_PUT\n");break;
1586
- case HTTP_CONNECT: printf("method: HTTP_CONNECT\n");break;
1587
- case HTTP_OPTIONS: printf("method: HTTP_OPTIONS\n");break;
1588
- case HTTP_TRACE: printf("method: HTTP_TRACE\n");break;
1589
- case HTTP_COPY: printf("method: HTTP_COPY\n");break;
1590
- case HTTP_LOCK: printf("method: HTTP_LOCK\n");break;
1591
- case HTTP_MKCOL: printf("method: HTTP_MKCOL\n");break;
1592
- case HTTP_MOVE: printf("method: HTTP_MOVE\n");break;
1593
- case HTTP_PROPFIND: printf("method: HTTP_PROPFIND\n");break;
1594
- case HTTP_PROPPATCH: printf("method: HTTP_PROPPATCH\n");break;
1595
- case HTTP_UNLOCK: printf("method: HTTP_UNLOCK\n");break;
2597
+ int i;
2598
+ printf("name :%s\n", m->name);
2599
+ char * bla = quote(m->raw);
2600
+ printf("raw :\"%s\"\n", bla);
2601
+ free(bla);
2602
+ switch (m->type){
2603
+ case HTTP_REQUEST:
2604
+ printf("type :HTTP_REQUEST\n");break;
2605
+ case HTTP_RESPONSE:
2606
+ printf("type :HTTP_RESPONSE\n"); break;
2607
+ case HTTP_BOTH:
2608
+ printf("type :HTTP_BOTH\n");
2609
+ }
2610
+ switch (m->method) {
2611
+ case HTTP_DELETE: printf("method: HTTP_DELETE\n");break;
2612
+ case HTTP_GET: printf("method: HTTP_GET\n");break;
2613
+ case HTTP_HEAD: printf("method: HTTP_HEAD\n");break;
2614
+ case HTTP_POST: printf("method: HTTP_POST\n");break;
2615
+ case HTTP_PUT: printf("method: HTTP_PUT\n");break;
2616
+ case HTTP_CONNECT: printf("method: HTTP_CONNECT\n");break;
2617
+ case HTTP_OPTIONS: printf("method: HTTP_OPTIONS\n");break;
2618
+ case HTTP_TRACE: printf("method: HTTP_TRACE\n");break;
2619
+ case HTTP_COPY: printf("method: HTTP_COPY\n");break;
2620
+ case HTTP_LOCK: printf("method: HTTP_LOCK\n");break;
2621
+ case HTTP_MKCOL: printf("method: HTTP_MKCOL\n");break;
2622
+ case HTTP_MOVE: printf("method: HTTP_MOVE\n");break;
2623
+ case HTTP_PROPFIND: printf("method: HTTP_PROPFIND\n");break;
2624
+ case HTTP_PROPPATCH: printf("method: HTTP_PROPPATCH\n");break;
2625
+ case HTTP_UNLOCK: printf("method: HTTP_UNLOCK\n");break;
1596
2626
  /* subversion */
1597
- case HTTP_REPORT: printf("method: REPORT\n"); break;
1598
- case HTTP_MKACTIVITY: printf("method: MKACTIVITY\n"); break;
1599
- case HTTP_CHECKOUT: printf("method: CHECKOUT\n"); break;
1600
- case HTTP_MERGE: printf("method: MERGE\n"); break;
1601
- default:
1602
- break;
1603
- }
1604
- printf("status_code :%d\n", m->status_code);
2627
+ case HTTP_REPORT: printf("method: HTTP_REPORT\n"); break;
2628
+ case HTTP_MKACTIVITY: printf("method: HTTP_MKACTIVITY\n"); break;
2629
+ case HTTP_CHECKOUT: printf("method: HTTP_CHECKOUT\n"); break;
2630
+ case HTTP_MERGE: printf("method: HTTP_MERGE\n"); break;
2631
+
2632
+ case HTTP_MSEARCH: printf("method: HTTP_MSEARCH\n"); break;
2633
+ case HTTP_NOTIFY: printf("method: HTTP_NOTIFY\n"); break;
2634
+ case HTTP_SUBSCRIBE: printf("method: HTTP_SUBSCRIBE\n"); break;
2635
+ case HTTP_UNSUBSCRIBE: printf("method: HTTP_UNSUBSCRIBE\n"); break;
2636
+ default:
2637
+ printf("method: UNKNOWN\n"); break;
2638
+ break;
2639
+ }
2640
+ printf("status_code :%d\n", m->status_code);
1605
2641
  printf("request_path:%s\n", m->request_path);
1606
2642
  printf("request_url :%s\n", m->request_url);
1607
2643
  printf("fragment :%s\n", m->fragment);
1608
2644
  printf("query_string:%s\n", m->query_string);
1609
2645
 
1610
- bla = quote(m->body);
2646
+ bla = quote(m->body);
1611
2647
  printf("body :\"%s\"\n", bla);
1612
- free(bla);
2648
+ free(bla);
1613
2649
  printf("body_size :%zu\n", m->body_size);
1614
2650
 
1615
- for (i=0; i!=m->num_headers; ++i){
1616
- printf("header_%d :{ \"%s\": \"%s\"}\n", i, m->headers[i][0], m->headers[i][1]);
1617
- }
2651
+ for (i=0; i!=m->num_headers; ++i){
2652
+ printf("header_%d :{ \"%s\": \"%s\"}\n", i, m->headers[i][0], m->headers[i][1]);
2653
+ }
1618
2654
 
1619
2655
  printf("should_keep_alive :%d\n", m->should_keep_alive);
1620
- printf("upgrade :%d\n", m->upgrade);
2656
+ bla = quote(m->upgrade);
2657
+ if (bla) {
2658
+ printf("upgrade :\"%s\"\n", bla);
2659
+ }
2660
+ free(bla);
1621
2661
  printf("http_major :%d\n", m->http_major);
1622
2662
  printf("http_minor :%d\n", m->http_minor);
1623
2663
  // printf("message_begin_cb_called :%d\n", m->message_begin_cb_called);
1624
2664
  // printf("headers_complete_cb_called:%d\n", m->headers_complete_cb_called);
1625
2665
  // printf("message_complete_cb_called:%d\n", m->message_complete_cb_called);
1626
2666
  // printf("message_complete_on_eof :%d\n", m->message_complete_on_eof);
1627
- printf("\n");
2667
+ printf("\n");
1628
2668
  }
1629
2669
 
1630
- void
1631
- dump_messages(void)
2670
+ void
2671
+ dump_messages(void)
1632
2672
  {
1633
2673
  int request_count;
1634
2674
  for (request_count = 0; requests[request_count].name; request_count++){
@@ -1639,6 +2679,60 @@ dump_messages(void)
1639
2679
  }
1640
2680
  }
1641
2681
 
2682
+ /* Verify that we can pause parsing at any of the bytes in the
2683
+ * message and still get the result that we're expecting. */
2684
+ void
2685
+ test_message_pause (const struct message *msg)
2686
+ {
2687
+ char *buf = (char*) msg->raw;
2688
+ size_t buflen = strlen(msg->raw);
2689
+ size_t nread;
2690
+
2691
+ parser_init(msg->type);
2692
+
2693
+ do {
2694
+ nread = parse_pause(buf, buflen);
2695
+
2696
+ // We can only set the upgrade buffer once we've gotten our message
2697
+ // completion callback.
2698
+ if (messages[0].message_complete_cb_called &&
2699
+ msg->upgrade &&
2700
+ parser->upgrade) {
2701
+ messages[0].upgrade = buf + nread;
2702
+ goto test;
2703
+ }
2704
+
2705
+ if (nread < buflen) {
2706
+
2707
+ // Not much do to if we failed a strict-mode check
2708
+ if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
2709
+ parser_free();
2710
+ return;
2711
+ }
2712
+
2713
+ assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
2714
+ }
2715
+
2716
+ buf += nread;
2717
+ buflen -= nread;
2718
+ http_parser_pause(parser, 0);
2719
+ } while (buflen > 0);
2720
+
2721
+ nread = parse_pause(NULL, 0);
2722
+ assert (nread == 0);
2723
+
2724
+ test:
2725
+ if (num_messages != 1) {
2726
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
2727
+ abort();
2728
+ }
2729
+
2730
+ if(!message_eq(0, msg)) abort();
2731
+
2732
+ parser_free();
2733
+
2734
+ }
2735
+
1642
2736
  int
1643
2737
  main (int argc, char * argv[])
1644
2738
  {
@@ -1659,6 +2753,10 @@ main (int argc, char * argv[])
1659
2753
  for (request_count = 0; requests[request_count].name; request_count++);
1660
2754
  for (response_count = 0; responses[response_count].name; response_count++);
1661
2755
 
2756
+ //// API
2757
+ test_preserve_data();
2758
+ test_parse_url();
2759
+
1662
2760
  //// OVERFLOW CONDITIONS
1663
2761
 
1664
2762
  test_header_overflow_error(HTTP_REQUEST);
@@ -1669,12 +2767,19 @@ main (int argc, char * argv[])
1669
2767
  test_no_overflow_long_body(HTTP_RESPONSE, 1000);
1670
2768
  test_no_overflow_long_body(HTTP_RESPONSE, 100000);
1671
2769
 
2770
+ test_header_content_length_overflow_error();
2771
+ test_chunk_content_length_overflow_error();
2772
+
1672
2773
  //// RESPONSES
1673
2774
 
1674
2775
  for (i = 0; i < response_count; i++) {
1675
2776
  test_message(&responses[i]);
1676
2777
  }
1677
2778
 
2779
+ for (i = 0; i < response_count; i++) {
2780
+ test_message_pause(&responses[i]);
2781
+ }
2782
+
1678
2783
  for (i = 0; i < response_count; i++) {
1679
2784
  if (!responses[i].should_keep_alive) continue;
1680
2785
  for (j = 0; j < response_count; j++) {
@@ -1719,7 +2824,7 @@ main (int argc, char * argv[])
1719
2824
 
1720
2825
  printf("response scan 1/2 ");
1721
2826
  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
1722
- , &responses[NO_HEADERS_NO_BODY_404]
2827
+ , &responses[NO_BODY_HTTP10_KA_204]
1723
2828
  , &responses[NO_REASON_PHRASE]
1724
2829
  );
1725
2830
 
@@ -1734,13 +2839,21 @@ main (int argc, char * argv[])
1734
2839
 
1735
2840
  /// REQUESTS
1736
2841
 
2842
+ test_simple("hello world", HPE_INVALID_METHOD);
2843
+ test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
1737
2844
 
1738
- test_simple("hello world", 0);
1739
- test_simple("GET / HTP/1.1\r\n\r\n", 0);
1740
2845
 
1741
- test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1742
- test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1743
- test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
2846
+ test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
2847
+ test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
2848
+ test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
2849
+
2850
+ // Well-formed but incomplete
2851
+ test_simple("GET / HTTP/1.1\r\n"
2852
+ "Content-Type: text/plain\r\n"
2853
+ "Content-Length: 6\r\n"
2854
+ "\r\n"
2855
+ "fooba",
2856
+ HPE_OK);
1744
2857
 
1745
2858
  static const char *all_methods[] = {
1746
2859
  "DELETE",
@@ -1758,12 +2871,31 @@ main (int argc, char * argv[])
1758
2871
  "PROPFIND",
1759
2872
  "PROPPATCH",
1760
2873
  "UNLOCK",
2874
+ "REPORT",
2875
+ "MKACTIVITY",
2876
+ "CHECKOUT",
2877
+ "MERGE",
2878
+ "M-SEARCH",
2879
+ "NOTIFY",
2880
+ "SUBSCRIBE",
2881
+ "UNSUBSCRIBE",
2882
+ "PATCH",
1761
2883
  0 };
1762
2884
  const char **this_method;
1763
2885
  for (this_method = all_methods; *this_method; this_method++) {
1764
2886
  char buf[200];
1765
2887
  sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
1766
- test_simple(buf, 1);
2888
+ test_simple(buf, HPE_OK);
2889
+ }
2890
+
2891
+ static const char *bad_methods[] = {
2892
+ "C******",
2893
+ "M****",
2894
+ 0 };
2895
+ for (this_method = bad_methods; *this_method; this_method++) {
2896
+ char buf[200];
2897
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
2898
+ test_simple(buf, HPE_UNKNOWN);
1767
2899
  }
1768
2900
 
1769
2901
  const char *dumbfuck2 =
@@ -1801,7 +2933,7 @@ main (int argc, char * argv[])
1801
2933
  "\tRA==\r\n"
1802
2934
  "\t-----END CERTIFICATE-----\r\n"
1803
2935
  "\r\n";
1804
- test_simple(dumbfuck2, 0);
2936
+ test_simple(dumbfuck2, HPE_OK);
1805
2937
 
1806
2938
  #if 0
1807
2939
  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
@@ -1823,7 +2955,9 @@ main (int argc, char * argv[])
1823
2955
  test_message(&requests[i]);
1824
2956
  }
1825
2957
 
1826
-
2958
+ for (i = 0; i < request_count; i++) {
2959
+ test_message_pause(&requests[i]);
2960
+ }
1827
2961
 
1828
2962
  for (i = 0; i < request_count; i++) {
1829
2963
  if (!requests[i].should_keep_alive) continue;