http_parser.rb 0.5.3 → 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 (58) hide show
  1. data/.gitmodules +3 -3
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +9 -2
  4. data/README.md +50 -45
  5. data/bench/standalone.rb +23 -0
  6. data/bench/thin.rb +1 -0
  7. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +66 -58
  8. data/ext/ruby_http_parser/ruby_http_parser.c +10 -41
  9. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  10. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
  11. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
  12. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1029 -615
  14. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +177 -43
  16. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +4 -1
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +2 -2
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +715 -637
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -1
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +71 -21
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
  39. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1141 -210
  40. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
  41. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
  43. data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1029 -615
  45. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
  46. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +145 -16
  47. data/ext/ruby_http_parser/vendor/http-parser/test.c +1065 -141
  48. data/http_parser.rb.gemspec +3 -1
  49. data/spec/parser_spec.rb +41 -17
  50. data/spec/support/requests.json +236 -24
  51. data/spec/support/responses.json +182 -36
  52. data/tasks/compile.rake +2 -2
  53. data/tasks/fixtures.rake +7 -1
  54. metadata +57 -19
  55. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  57. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
  58. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
@@ -0,0 +1,79 @@
1
+ # This file is used with the GYP meta build system.
2
+ # http://code.google.com/p/gyp/
3
+ # To build try this:
4
+ # svn co http://gyp.googlecode.com/svn/trunk gyp
5
+ # ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
6
+ # ./out/Debug/test
7
+ {
8
+ 'target_defaults': {
9
+ 'default_configuration': 'Debug',
10
+ 'configurations': {
11
+ # TODO: hoist these out and put them somewhere common, because
12
+ # RuntimeLibrary MUST MATCH across the entire project
13
+ 'Debug': {
14
+ 'defines': [ 'DEBUG', '_DEBUG' ],
15
+ 'msvs_settings': {
16
+ 'VCCLCompilerTool': {
17
+ 'RuntimeLibrary': 1, # static debug
18
+ },
19
+ },
20
+ },
21
+ 'Release': {
22
+ 'defines': [ 'NDEBUG' ],
23
+ 'msvs_settings': {
24
+ 'VCCLCompilerTool': {
25
+ 'RuntimeLibrary': 0, # static release
26
+ },
27
+ },
28
+ }
29
+ },
30
+ 'msvs_settings': {
31
+ 'VCCLCompilerTool': {
32
+ },
33
+ 'VCLibrarianTool': {
34
+ },
35
+ 'VCLinkerTool': {
36
+ 'GenerateDebugInformation': 'true',
37
+ },
38
+ },
39
+ 'conditions': [
40
+ ['OS == "win"', {
41
+ 'defines': [
42
+ 'WIN32'
43
+ ],
44
+ }]
45
+ ],
46
+ },
47
+
48
+ 'targets': [
49
+ {
50
+ 'target_name': 'http_parser',
51
+ 'type': 'static_library',
52
+ 'include_dirs': [ '.' ],
53
+ 'direct_dependent_settings': {
54
+ 'include_dirs': [ '.' ],
55
+ },
56
+ 'defines': [ 'HTTP_PARSER_STRICT=0' ],
57
+ 'sources': [ './http_parser.c', ],
58
+ 'conditions': [
59
+ ['OS=="win"', {
60
+ 'msvs_settings': {
61
+ 'VCCLCompilerTool': {
62
+ # Compile as C++. http_parser.c is actually C99, but C++ is
63
+ # close enough in this case.
64
+ 'CompileAs': 2,
65
+ },
66
+ },
67
+ }]
68
+ ],
69
+ },
70
+
71
+ {
72
+ 'target_name': 'test',
73
+ 'type': 'executable',
74
+ 'dependencies': [ 'http_parser' ],
75
+ 'sources': [ 'test.c' ]
76
+ }
77
+ ]
78
+ }
79
+
@@ -28,7 +28,7 @@ extern "C" {
28
28
  #define HTTP_PARSER_VERSION_MINOR 0
29
29
 
30
30
  #include <sys/types.h>
31
- #if defined(_WIN32) && !defined(__MINGW32__)
31
+ #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
32
32
  typedef __int8 int8_t;
33
33
  typedef unsigned __int8 uint8_t;
34
34
  typedef __int16 int16_t;
@@ -49,8 +49,13 @@ typedef int ssize_t;
49
49
  */
50
50
  #ifndef HTTP_PARSER_STRICT
51
51
  # define HTTP_PARSER_STRICT 1
52
- #else
53
- # define HTTP_PARSER_STRICT 0
52
+ #endif
53
+
54
+ /* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
55
+ * the error reporting facility.
56
+ */
57
+ #ifndef HTTP_PARSER_DEBUG
58
+ # define HTTP_PARSER_DEBUG 0
54
59
  #endif
55
60
 
56
61
 
@@ -60,6 +65,7 @@ typedef int ssize_t;
60
65
 
61
66
  typedef struct http_parser http_parser;
62
67
  typedef struct http_parser_settings http_parser_settings;
68
+ typedef struct http_parser_result http_parser_result;
63
69
 
64
70
 
65
71
  /* Callbacks should return non-zero to indicate an error. The parser will
@@ -108,35 +114,118 @@ enum http_method
108
114
  , HTTP_NOTIFY
109
115
  , HTTP_SUBSCRIBE
110
116
  , HTTP_UNSUBSCRIBE
117
+ /* RFC-5789 */
118
+ , HTTP_PATCH
119
+ , HTTP_PURGE
111
120
  };
112
121
 
113
122
 
114
123
  enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
115
124
 
116
125
 
126
+ /* Flag values for http_parser.flags field */
127
+ enum flags
128
+ { F_CHUNKED = 1 << 0
129
+ , F_CONNECTION_KEEP_ALIVE = 1 << 1
130
+ , F_CONNECTION_CLOSE = 1 << 2
131
+ , F_TRAILING = 1 << 3
132
+ , F_UPGRADE = 1 << 4
133
+ , F_SKIPBODY = 1 << 5
134
+ };
135
+
136
+
137
+ /* Map for errno-related constants
138
+ *
139
+ * The provided argument should be a macro that takes 2 arguments.
140
+ */
141
+ #define HTTP_ERRNO_MAP(XX) \
142
+ /* No error */ \
143
+ XX(OK, "success") \
144
+ \
145
+ /* Callback-related errors */ \
146
+ XX(CB_message_begin, "the on_message_begin callback failed") \
147
+ XX(CB_url, "the on_url callback failed") \
148
+ XX(CB_header_field, "the on_header_field callback failed") \
149
+ XX(CB_header_value, "the on_header_value callback failed") \
150
+ XX(CB_headers_complete, "the on_headers_complete callback failed") \
151
+ XX(CB_body, "the on_body callback failed") \
152
+ XX(CB_message_complete, "the on_message_complete callback failed") \
153
+ \
154
+ /* Parsing-related errors */ \
155
+ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
156
+ XX(HEADER_OVERFLOW, \
157
+ "too many header bytes seen; overflow detected") \
158
+ XX(CLOSED_CONNECTION, \
159
+ "data received after completed connection: close message") \
160
+ XX(INVALID_VERSION, "invalid HTTP version") \
161
+ XX(INVALID_STATUS, "invalid HTTP status code") \
162
+ XX(INVALID_METHOD, "invalid HTTP method") \
163
+ XX(INVALID_URL, "invalid URL") \
164
+ XX(INVALID_HOST, "invalid host") \
165
+ XX(INVALID_PORT, "invalid port") \
166
+ XX(INVALID_PATH, "invalid path") \
167
+ XX(INVALID_QUERY_STRING, "invalid query string") \
168
+ XX(INVALID_FRAGMENT, "invalid fragment") \
169
+ XX(LF_EXPECTED, "LF character expected") \
170
+ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
171
+ XX(INVALID_CONTENT_LENGTH, \
172
+ "invalid character in content-length header") \
173
+ XX(INVALID_CHUNK_SIZE, \
174
+ "invalid character in chunk size header") \
175
+ XX(INVALID_CONSTANT, "invalid constant string") \
176
+ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
177
+ XX(STRICT, "strict mode assertion failed") \
178
+ XX(PAUSED, "parser is paused") \
179
+ XX(UNKNOWN, "an unknown error occurred")
180
+
181
+
182
+ /* Define HPE_* values for each errno value above */
183
+ #define HTTP_ERRNO_GEN(n, s) HPE_##n,
184
+ enum http_errno {
185
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
186
+ };
187
+ #undef HTTP_ERRNO_GEN
188
+
189
+
190
+ /* Get an http_errno value from an http_parser */
191
+ #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
192
+
193
+ /* Get the line number that generated the current error */
194
+ #if HTTP_PARSER_DEBUG
195
+ #define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
196
+ #else
197
+ #define HTTP_PARSER_ERRNO_LINE(p) 0
198
+ #endif
199
+
200
+
117
201
  struct http_parser {
118
202
  /** PRIVATE **/
119
- unsigned char type : 2;
120
- unsigned char flags : 6;
121
- unsigned char state;
122
- unsigned char header_state;
123
- unsigned char index;
203
+ unsigned char type : 2; /* enum http_parser_type */
204
+ unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
205
+ unsigned char state; /* enum state from http_parser.c */
206
+ unsigned char header_state; /* enum header_state from http_parser.c */
207
+ unsigned char index; /* index into current matcher */
124
208
 
125
- uint32_t nread;
126
- int64_t content_length;
209
+ uint32_t nread; /* # bytes read in various scenarios */
210
+ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
127
211
 
128
212
  /** READ-ONLY **/
129
213
  unsigned short http_major;
130
214
  unsigned short http_minor;
131
215
  unsigned short status_code; /* responses only */
132
- unsigned char method; /* requests only */
216
+ unsigned char method; /* requests only */
217
+ unsigned char http_errno : 7;
133
218
 
134
219
  /* 1 = Upgrade header was present and the parser has exited because of that.
135
220
  * 0 = No upgrade header present.
136
221
  * Should be checked when http_parser_execute() returns in addition to
137
222
  * error checking.
138
223
  */
139
- char upgrade;
224
+ unsigned char upgrade : 1;
225
+
226
+ #if HTTP_PARSER_DEBUG
227
+ uint32_t error_lineno;
228
+ #endif
140
229
 
141
230
  /** PUBLIC **/
142
231
  void *data; /* A pointer to get hook to the "connection" or "socket" object */
@@ -145,10 +234,7 @@ struct http_parser {
145
234
 
146
235
  struct http_parser_settings {
147
236
  http_cb on_message_begin;
148
- http_data_cb on_path;
149
- http_data_cb on_query_string;
150
237
  http_data_cb on_url;
151
- http_data_cb on_fragment;
152
238
  http_data_cb on_header_field;
153
239
  http_data_cb on_header_value;
154
240
  http_cb on_headers_complete;
@@ -157,6 +243,35 @@ struct http_parser_settings {
157
243
  };
158
244
 
159
245
 
246
+ enum http_parser_url_fields
247
+ { UF_SCHEMA = 0
248
+ , UF_HOST = 1
249
+ , UF_PORT = 2
250
+ , UF_PATH = 3
251
+ , UF_QUERY = 4
252
+ , UF_FRAGMENT = 5
253
+ , UF_MAX = 6
254
+ };
255
+
256
+
257
+ /* Result structure for http_parser_parse_url().
258
+ *
259
+ * Callers should index into field_data[] with UF_* values iff field_set
260
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
261
+ * because we probably have padding left over), we convert any port to
262
+ * a uint16_t.
263
+ */
264
+ struct http_parser_url {
265
+ uint16_t field_set; /* Bitmask of (1 << UF_*) values */
266
+ uint16_t port; /* Converted UF_PORT string */
267
+
268
+ struct {
269
+ uint16_t off; /* Offset into buffer in which field starts */
270
+ uint16_t len; /* Length of run in buffer */
271
+ } field_data[UF_MAX];
272
+ };
273
+
274
+
160
275
  void http_parser_init(http_parser *parser, enum http_parser_type type);
161
276
 
162
277
 
@@ -175,7 +290,21 @@ size_t http_parser_execute(http_parser *parser,
175
290
  int http_should_keep_alive(http_parser *parser);
176
291
 
177
292
  /* Returns a string version of the HTTP method. */
178
- const char *http_method_str(enum http_method);
293
+ const char *http_method_str(enum http_method m);
294
+
295
+ /* Return a string name of the given error */
296
+ const char *http_errno_name(enum http_errno err);
297
+
298
+ /* Return a string description of the given error */
299
+ const char *http_errno_description(enum http_errno err);
300
+
301
+ /* Parse a URL; return nonzero on failure */
302
+ int http_parser_parse_url(const char *buf, size_t buflen,
303
+ int is_connect,
304
+ struct http_parser_url *u);
305
+
306
+ /* Pause or un-pause the parser; a nonzero value pauses */
307
+ void http_parser_pause(http_parser *parser, int paused);
179
308
 
180
309
  #ifdef __cplusplus
181
310
  }
@@ -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
  }
@@ -582,28 +587,36 @@ const struct message requests[] =
582
587
  ,.body= ""
583
588
  }
584
589
 
585
- #define UTF8_PATH_REQ 21
586
- , {.name= "utf-8 path request"
590
+ #define LINE_FOLDING_IN_HEADER 20
591
+ , {.name= "line folding in header value"
587
592
  ,.type= HTTP_REQUEST
588
- ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
589
- "Host: github.com\r\n"
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"
590
601
  "\r\n"
591
602
  ,.should_keep_alive= TRUE
592
603
  ,.message_complete_on_eof= FALSE
593
604
  ,.http_major= 1
594
605
  ,.http_minor= 1
595
606
  ,.method= HTTP_GET
596
- ,.query_string= "q=1"
597
- ,.fragment= "narf"
598
- ,.request_path= "/δ¶/δt/pope"
599
- ,.request_url= "/δ¶/δt/pope?q=1#narf"
600
- ,.num_headers= 1
601
- ,.headers= { {"Host", "github.com" }
607
+ ,.query_string= ""
608
+ ,.fragment= ""
609
+ ,.request_path= "/"
610
+ ,.request_url= "/"
611
+ ,.num_headers= 2
612
+ ,.headers= { { "Line1", "abcdefghijklmno qrs" }
613
+ , { "Line2", "line2\t" }
602
614
  }
603
615
  ,.body= ""
604
616
  }
605
617
 
606
- #define QUERY_TERMINATED_HOST 22
618
+
619
+ #define QUERY_TERMINATED_HOST 21
607
620
  , {.name= "host terminated by a query string"
608
621
  ,.type= HTTP_REQUEST
609
622
  ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
@@ -622,7 +635,7 @@ const struct message requests[] =
622
635
  ,.body= ""
623
636
  }
624
637
 
625
- #define QUERY_TERMINATED_HOSTPORT 23
638
+ #define QUERY_TERMINATED_HOSTPORT 22
626
639
  , {.name= "host:port terminated by a query string"
627
640
  ,.type= HTTP_REQUEST
628
641
  ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
@@ -636,12 +649,13 @@ const struct message requests[] =
636
649
  ,.fragment= ""
637
650
  ,.request_path= ""
638
651
  ,.request_url= "http://hypnotoad.org:1234?hail=all"
652
+ ,.port= 1234
639
653
  ,.num_headers= 0
640
654
  ,.headers= { }
641
655
  ,.body= ""
642
656
  }
643
657
 
644
- #define SPACE_TERMINATED_HOSTPORT 24
658
+ #define SPACE_TERMINATED_HOSTPORT 23
645
659
  , {.name= "host:port terminated by a space"
646
660
  ,.type= HTTP_REQUEST
647
661
  ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
@@ -655,11 +669,187 @@ const struct message requests[] =
655
669
  ,.fragment= ""
656
670
  ,.request_path= ""
657
671
  ,.request_url= "http://hypnotoad.org:1234"
672
+ ,.port= 1234
658
673
  ,.num_headers= 0
659
674
  ,.headers= { }
660
675
  ,.body= ""
661
676
  }
662
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
+
663
853
  , {.name= NULL } /* sentinel */
664
854
  };
665
855
 
@@ -760,8 +950,8 @@ const struct message responses[] =
760
950
  , {.name= "404 no headers no body"
761
951
  ,.type= HTTP_RESPONSE
762
952
  ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
763
- ,.should_keep_alive= TRUE
764
- ,.message_complete_on_eof= FALSE
953
+ ,.should_keep_alive= FALSE
954
+ ,.message_complete_on_eof= TRUE
765
955
  ,.http_major= 1
766
956
  ,.http_minor= 1
767
957
  ,.status_code= 404
@@ -775,8 +965,8 @@ const struct message responses[] =
775
965
  , {.name= "301 no response phrase"
776
966
  ,.type= HTTP_RESPONSE
777
967
  ,.raw= "HTTP/1.1 301\r\n\r\n"
778
- ,.should_keep_alive = TRUE
779
- ,.message_complete_on_eof= FALSE
968
+ ,.should_keep_alive = FALSE
969
+ ,.message_complete_on_eof= TRUE
780
970
  ,.http_major= 1
781
971
  ,.http_minor= 1
782
972
  ,.status_code= 301
@@ -925,40 +1115,7 @@ const struct message responses[] =
925
1115
  ,.body= ""
926
1116
  }
927
1117
 
928
- #define SPACE_IN_FIELD_RES 9
929
- /* Should handle spaces in header fields */
930
- , {.name= "field space"
931
- ,.type= HTTP_RESPONSE
932
- ,.raw= "HTTP/1.1 200 OK\r\n"
933
- "Server: Microsoft-IIS/6.0\r\n"
934
- "X-Powered-By: ASP.NET\r\n"
935
- "en-US Content-Type: text/xml\r\n" /* this is the problem */
936
- "Content-Type: text/xml\r\n"
937
- "Content-Length: 16\r\n"
938
- "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
939
- "Connection: keep-alive\r\n"
940
- "\r\n"
941
- "<xml>hello</xml>" /* fake body */
942
- ,.should_keep_alive= TRUE
943
- ,.message_complete_on_eof= FALSE
944
- ,.http_major= 1
945
- ,.http_minor= 1
946
- ,.status_code= 200
947
- ,.num_headers= 7
948
- ,.headers=
949
- { { "Server", "Microsoft-IIS/6.0" }
950
- , { "X-Powered-By", "ASP.NET" }
951
- , { "en-US Content-Type", "text/xml" }
952
- , { "Content-Type", "text/xml" }
953
- , { "Content-Length", "16" }
954
- , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
955
- , { "Connection", "keep-alive" }
956
- }
957
- ,.body= "<xml>hello</xml>"
958
- }
959
-
960
-
961
- #define RES_FIELD_UNDERSCORE 10
1118
+ #define RES_FIELD_UNDERSCORE 9
962
1119
  /* Should handle spaces in header fields */
963
1120
  , {.name= "field underscore"
964
1121
  ,.type= HTTP_RESPONSE
@@ -998,7 +1155,7 @@ const struct message responses[] =
998
1155
  ,.body= ""
999
1156
  }
1000
1157
 
1001
- #define NON_ASCII_IN_STATUS_LINE 11
1158
+ #define NON_ASCII_IN_STATUS_LINE 10
1002
1159
  /* Should handle non-ASCII in status line */
1003
1160
  , {.name= "non-ASCII in status line"
1004
1161
  ,.type= HTTP_RESPONSE
@@ -1018,21 +1175,196 @@ const struct message responses[] =
1018
1175
  , { "Content-Length", "0" }
1019
1176
  , { "Connection", "close" }
1020
1177
  }
1021
- ,.body= ""
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
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>"
1022
1362
  }
1023
-
1363
+ #endif /* !HTTP_PARSER_STRICT */
1024
1364
 
1025
1365
  , {.name= NULL } /* sentinel */
1026
1366
  };
1027
1367
 
1028
- int
1029
- request_path_cb (http_parser *p, const char *buf, size_t len)
1030
- {
1031
- assert(p == parser);
1032
- strncat(messages[num_messages].request_path, buf, len);
1033
- return 0;
1034
- }
1035
-
1036
1368
  int
1037
1369
  request_url_cb (http_parser *p, const char *buf, size_t len)
1038
1370
  {
@@ -1041,22 +1373,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
1041
1373
  return 0;
1042
1374
  }
1043
1375
 
1044
- int
1045
- query_string_cb (http_parser *p, const char *buf, size_t len)
1046
- {
1047
- assert(p == parser);
1048
- strncat(messages[num_messages].query_string, buf, len);
1049
- return 0;
1050
- }
1051
-
1052
- int
1053
- fragment_cb (http_parser *p, const char *buf, size_t len)
1054
- {
1055
- assert(p == parser);
1056
- strncat(messages[num_messages].fragment, buf, len);
1057
- return 0;
1058
- }
1059
-
1060
1376
  int
1061
1377
  header_field_cb (http_parser *p, const char *buf, size_t len)
1062
1378
  {
@@ -1146,14 +1462,151 @@ message_complete_cb (http_parser *p)
1146
1462
  return 0;
1147
1463
  }
1148
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
+
1149
1605
  static http_parser_settings settings =
1150
1606
  {.on_message_begin = message_begin_cb
1151
1607
  ,.on_header_field = header_field_cb
1152
1608
  ,.on_header_value = header_value_cb
1153
- ,.on_path = request_path_cb
1154
1609
  ,.on_url = request_url_cb
1155
- ,.on_fragment = fragment_cb
1156
- ,.on_query_string = query_string_cb
1157
1610
  ,.on_body = body_cb
1158
1611
  ,.on_headers_complete = headers_complete_cb
1159
1612
  ,.on_message_complete = message_complete_cb
@@ -1163,10 +1616,7 @@ static http_parser_settings settings_count_body =
1163
1616
  {.on_message_begin = message_begin_cb
1164
1617
  ,.on_header_field = header_field_cb
1165
1618
  ,.on_header_value = header_value_cb
1166
- ,.on_path = request_path_cb
1167
1619
  ,.on_url = request_url_cb
1168
- ,.on_fragment = fragment_cb
1169
- ,.on_query_string = query_string_cb
1170
1620
  ,.on_body = count_body_cb
1171
1621
  ,.on_headers_complete = headers_complete_cb
1172
1622
  ,.on_message_complete = message_complete_cb
@@ -1176,10 +1626,7 @@ static http_parser_settings settings_null =
1176
1626
  {.on_message_begin = 0
1177
1627
  ,.on_header_field = 0
1178
1628
  ,.on_header_value = 0
1179
- ,.on_path = 0
1180
1629
  ,.on_url = 0
1181
- ,.on_fragment = 0
1182
- ,.on_query_string = 0
1183
1630
  ,.on_body = 0
1184
1631
  ,.on_headers_complete = 0
1185
1632
  ,.on_message_complete = 0
@@ -1224,12 +1671,29 @@ size_t parse_count_body (const char *buf, size_t len)
1224
1671
  return nparsed;
1225
1672
  }
1226
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
+
1227
1685
  static inline int
1228
1686
  check_str_eq (const struct message *m,
1229
1687
  const char *prop,
1230
1688
  const char *expected,
1231
1689
  const char *found) {
1232
- 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)) {
1233
1697
  printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1234
1698
  printf("expected '%s'\n", expected);
1235
1699
  printf(" found '%s'\n", found);
@@ -1258,6 +1722,20 @@ check_num_eq (const struct message *m,
1258
1722
  #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1259
1723
  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1260
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)
1261
1739
 
1262
1740
  int
1263
1741
  message_eq (int index, const struct message *expected)
@@ -1282,10 +1760,29 @@ message_eq (int index, const struct message *expected)
1282
1760
  assert(m->message_complete_cb_called);
1283
1761
 
1284
1762
 
1285
- MESSAGE_CHECK_STR_EQ(expected, m, request_path);
1286
- MESSAGE_CHECK_STR_EQ(expected, m, query_string);
1287
- MESSAGE_CHECK_STR_EQ(expected, m, fragment);
1288
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
+
1289
1786
  if (expected->body_size) {
1290
1787
  MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1291
1788
  } else {
@@ -1302,13 +1799,81 @@ message_eq (int index, const struct message *expected)
1302
1799
  if (!r) return 0;
1303
1800
  }
1304
1801
 
1802
+ MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
1803
+
1305
1804
  return 1;
1306
1805
  }
1307
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
+
1308
1871
  static void
1309
1872
  print_error (const char *raw, size_t error_location)
1310
1873
  {
1311
- 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)));
1312
1877
 
1313
1878
  int this_line = 0, char_len = 0;
1314
1879
  size_t i, j, len = strlen(raw), error_location_line = 0;
@@ -1346,6 +1911,218 @@ print_error (const char *raw, size_t error_location)
1346
1911
  fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
1347
1912
  }
1348
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
+ }
1349
2126
 
1350
2127
  void
1351
2128
  test_message (const struct message *message)
@@ -1363,7 +2140,10 @@ test_message (const struct message *message)
1363
2140
  if (msg1len) {
1364
2141
  read = parse(msg1, msg1len);
1365
2142
 
1366
- 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
+ }
1367
2147
 
1368
2148
  if (read != msg1len) {
1369
2149
  print_error(msg1, read);
@@ -1374,7 +2154,10 @@ test_message (const struct message *message)
1374
2154
 
1375
2155
  read = parse(msg2, msg2len);
1376
2156
 
1377
- 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
+ }
1378
2161
 
1379
2162
  if (read != msg2len) {
1380
2163
  print_error(msg2, read);
@@ -1383,8 +2166,6 @@ test_message (const struct message *message)
1383
2166
 
1384
2167
  read = parse(NULL, 0);
1385
2168
 
1386
- if (message->upgrade && parser->upgrade) goto test;
1387
-
1388
2169
  if (read != 0) {
1389
2170
  print_error(message->raw, read);
1390
2171
  exit(1);
@@ -1440,21 +2221,32 @@ test_message_count_body (const struct message *message)
1440
2221
  }
1441
2222
 
1442
2223
  void
1443
- test_simple (const char *buf, int should_pass)
2224
+ test_simple (const char *buf, enum http_errno err_expected)
1444
2225
  {
1445
2226
  parser_init(HTTP_REQUEST);
1446
2227
 
1447
2228
  size_t parsed;
1448
2229
  int pass;
2230
+ enum http_errno err;
2231
+
1449
2232
  parsed = parse(buf, strlen(buf));
1450
2233
  pass = (parsed == strlen(buf));
2234
+ err = HTTP_PARSER_ERRNO(parser);
1451
2235
  parsed = parse(NULL, 0);
1452
2236
  pass &= (parsed == 0);
1453
2237
 
1454
2238
  parser_free();
1455
2239
 
1456
- if (pass != should_pass) {
1457
- 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);
1458
2250
  exit(1);
1459
2251
  }
1460
2252
  }
@@ -1471,10 +2263,14 @@ test_header_overflow_error (int req)
1471
2263
  assert(parsed == strlen(buf));
1472
2264
 
1473
2265
  buf = "header-key: header-value\r\n";
2266
+ size_t buflen = strlen(buf);
2267
+
1474
2268
  int i;
1475
2269
  for (i = 0; i < 10000; i++) {
1476
- 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) {
1477
2272
  //fprintf(stderr, "error found on iter %d\n", i);
2273
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
1478
2274
  return;
1479
2275
  }
1480
2276
  }
@@ -1483,6 +2279,53 @@ test_header_overflow_error (int req)
1483
2279
  exit(1);
1484
2280
  }
1485
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
+
1486
2329
  void
1487
2330
  test_no_overflow_long_body (int req, size_t length)
1488
2331
  {
@@ -1519,12 +2362,7 @@ test_no_overflow_long_body (int req, size_t length)
1519
2362
  void
1520
2363
  test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
1521
2364
  {
1522
- int message_count = 1;
1523
- if (!r1->upgrade) {
1524
- message_count++;
1525
- if (!r2->upgrade) message_count++;
1526
- }
1527
- int has_upgrade = (message_count < 3 || r3->upgrade);
2365
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1528
2366
 
1529
2367
  char total[ strlen(r1->raw)
1530
2368
  + strlen(r2->raw)
@@ -1543,7 +2381,10 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
1543
2381
 
1544
2382
  read = parse(total, strlen(total));
1545
2383
 
1546
- 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
+ }
1547
2388
 
1548
2389
  if (read != strlen(total)) {
1549
2390
  print_error(total, read);
@@ -1552,8 +2393,6 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
1552
2393
 
1553
2394
  read = parse(NULL, 0);
1554
2395
 
1555
- if (has_upgrade && parser->upgrade) goto test;
1556
-
1557
2396
  if (read != 0) {
1558
2397
  print_error(total, read);
1559
2398
  exit(1);
@@ -1567,12 +2406,8 @@ test:
1567
2406
  }
1568
2407
 
1569
2408
  if (!message_eq(0, r1)) exit(1);
1570
- if (message_count > 1) {
1571
- if (!message_eq(1, r2)) exit(1);
1572
- if (message_count > 2) {
1573
- if (!message_eq(2, r3)) exit(1);
1574
- }
1575
- }
2409
+ if (message_count > 1 && !message_eq(1, r2)) exit(1);
2410
+ if (message_count > 2 && !message_eq(2, r3)) exit(1);
1576
2411
 
1577
2412
  parser_free();
1578
2413
  }
@@ -1601,6 +2436,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1601
2436
  int ops = 0 ;
1602
2437
 
1603
2438
  size_t buf1_len, buf2_len, buf3_len;
2439
+ int message_count = count_parsed_messages(3, r1, r2, r3);
1604
2440
 
1605
2441
  int i,j,type_both;
1606
2442
  for (type_both = 0; type_both < 2; type_both ++ ) {
@@ -1629,27 +2465,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1629
2465
 
1630
2466
  read = parse(buf1, buf1_len);
1631
2467
 
1632
- if (r3->upgrade && parser->upgrade) goto test;
2468
+ if (parser->upgrade) goto test;
1633
2469
 
1634
2470
  if (read != buf1_len) {
1635
2471
  print_error(buf1, read);
1636
2472
  goto error;
1637
2473
  }
1638
2474
 
1639
- read = parse(buf2, buf2_len);
2475
+ read += parse(buf2, buf2_len);
1640
2476
 
1641
- if (r3->upgrade && parser->upgrade) goto test;
2477
+ if (parser->upgrade) goto test;
1642
2478
 
1643
- if (read != buf2_len) {
2479
+ if (read != buf1_len + buf2_len) {
1644
2480
  print_error(buf2, read);
1645
2481
  goto error;
1646
2482
  }
1647
2483
 
1648
- read = parse(buf3, buf3_len);
2484
+ read += parse(buf3, buf3_len);
1649
2485
 
1650
- if (r3->upgrade && parser->upgrade) goto test;
2486
+ if (parser->upgrade) goto test;
1651
2487
 
1652
- if (read != buf3_len) {
2488
+ if (read != buf1_len + buf2_len + buf3_len) {
1653
2489
  print_error(buf3, read);
1654
2490
  goto error;
1655
2491
  }
@@ -1657,9 +2493,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
1657
2493
  parse(NULL, 0);
1658
2494
 
1659
2495
  test:
2496
+ if (parser->upgrade) {
2497
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
2498
+ }
1660
2499
 
1661
- if (3 != num_messages) {
1662
- 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);
1663
2503
  goto error;
1664
2504
  }
1665
2505
 
@@ -1668,12 +2508,12 @@ test:
1668
2508
  goto error;
1669
2509
  }
1670
2510
 
1671
- if (!message_eq(1, r2)) {
2511
+ if (message_count > 1 && !message_eq(1, r2)) {
1672
2512
  fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
1673
2513
  goto error;
1674
2514
  }
1675
2515
 
1676
- if (!message_eq(2, r3)) {
2516
+ if (message_count > 2 && !message_eq(2, r3)) {
1677
2517
  fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
1678
2518
  goto error;
1679
2519
  }
@@ -1724,6 +2564,58 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
1724
2564
  return buf;
1725
2565
  }
1726
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
+ }
1727
2619
 
1728
2620
  int
1729
2621
  main (void)
@@ -1738,6 +2630,10 @@ main (void)
1738
2630
  for (request_count = 0; requests[request_count].name; request_count++);
1739
2631
  for (response_count = 0; responses[response_count].name; response_count++);
1740
2632
 
2633
+ //// API
2634
+ test_preserve_data();
2635
+ test_parse_url();
2636
+
1741
2637
  //// OVERFLOW CONDITIONS
1742
2638
 
1743
2639
  test_header_overflow_error(HTTP_REQUEST);
@@ -1748,12 +2644,19 @@ main (void)
1748
2644
  test_no_overflow_long_body(HTTP_RESPONSE, 1000);
1749
2645
  test_no_overflow_long_body(HTTP_RESPONSE, 100000);
1750
2646
 
2647
+ test_header_content_length_overflow_error();
2648
+ test_chunk_content_length_overflow_error();
2649
+
1751
2650
  //// RESPONSES
1752
2651
 
1753
2652
  for (i = 0; i < response_count; i++) {
1754
2653
  test_message(&responses[i]);
1755
2654
  }
1756
2655
 
2656
+ for (i = 0; i < response_count; i++) {
2657
+ test_message_pause(&responses[i]);
2658
+ }
2659
+
1757
2660
  for (i = 0; i < response_count; i++) {
1758
2661
  if (!responses[i].should_keep_alive) continue;
1759
2662
  for (j = 0; j < response_count; j++) {
@@ -1798,7 +2701,7 @@ main (void)
1798
2701
 
1799
2702
  printf("response scan 1/2 ");
1800
2703
  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
1801
- , &responses[NO_HEADERS_NO_BODY_404]
2704
+ , &responses[NO_BODY_HTTP10_KA_204]
1802
2705
  , &responses[NO_REASON_PHRASE]
1803
2706
  );
1804
2707
 
@@ -1813,13 +2716,13 @@ main (void)
1813
2716
 
1814
2717
  /// REQUESTS
1815
2718
 
1816
- test_simple("hello world", 0);
1817
- test_simple("GET / HTP/1.1\r\n\r\n", 0);
2719
+ test_simple("hello world", HPE_INVALID_METHOD);
2720
+ test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
1818
2721
 
1819
2722
 
1820
- test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1821
- test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1822
- test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
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);
1823
2726
 
1824
2727
  // Well-formed but incomplete
1825
2728
  test_simple("GET / HTTP/1.1\r\n"
@@ -1827,7 +2730,7 @@ main (void)
1827
2730
  "Content-Length: 6\r\n"
1828
2731
  "\r\n"
1829
2732
  "fooba",
1830
- 0);
2733
+ HPE_OK);
1831
2734
 
1832
2735
  static const char *all_methods[] = {
1833
2736
  "DELETE",
@@ -1845,12 +2748,31 @@ main (void)
1845
2748
  "PROPFIND",
1846
2749
  "PROPPATCH",
1847
2750
  "UNLOCK",
2751
+ "REPORT",
2752
+ "MKACTIVITY",
2753
+ "CHECKOUT",
2754
+ "MERGE",
2755
+ "M-SEARCH",
2756
+ "NOTIFY",
2757
+ "SUBSCRIBE",
2758
+ "UNSUBSCRIBE",
2759
+ "PATCH",
1848
2760
  0 };
1849
2761
  const char **this_method;
1850
2762
  for (this_method = all_methods; *this_method; this_method++) {
1851
2763
  char buf[200];
1852
2764
  sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
1853
- 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);
1854
2776
  }
1855
2777
 
1856
2778
  const char *dumbfuck2 =
@@ -1888,7 +2810,7 @@ main (void)
1888
2810
  "\tRA==\r\n"
1889
2811
  "\t-----END CERTIFICATE-----\r\n"
1890
2812
  "\r\n";
1891
- test_simple(dumbfuck2, 0);
2813
+ test_simple(dumbfuck2, HPE_OK);
1892
2814
 
1893
2815
  #if 0
1894
2816
  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
@@ -1910,7 +2832,9 @@ main (void)
1910
2832
  test_message(&requests[i]);
1911
2833
  }
1912
2834
 
1913
-
2835
+ for (i = 0; i < request_count; i++) {
2836
+ test_message_pause(&requests[i]);
2837
+ }
1914
2838
 
1915
2839
  for (i = 0; i < request_count; i++) {
1916
2840
  if (!requests[i].should_keep_alive) continue;