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,7 @@
1
- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
2
+ *
3
+ * Additional changes are licensed under the same terms as NGINX and
4
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
5
  *
3
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
7
  * of this software and associated documentation files (the "Software"), to
@@ -18,58 +21,94 @@
18
21
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
22
  * IN THE SOFTWARE.
20
23
  */
21
- #include <http_parser.h>
22
- #ifdef _WIN32
23
- typedef __int8 int8_t;
24
- typedef unsigned __int8 uint8_t;
25
- typedef __int16 int16_t;
26
- typedef unsigned __int16 uint16_t;
27
- typedef __int16 int32_t;
28
- typedef unsigned __int32 uint32_t;
29
- #else
30
- #include <stdint.h>
31
- #endif
24
+ #include "http_parser.h"
32
25
  #include <assert.h>
33
26
  #include <stddef.h>
27
+ #include <ctype.h>
28
+ #include <stdlib.h>
29
+ #include <string.h>
30
+ #include <limits.h>
34
31
 
32
+ #ifndef ULLONG_MAX
33
+ # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
34
+ #endif
35
35
 
36
36
  #ifndef MIN
37
37
  # define MIN(a,b) ((a) < (b) ? (a) : (b))
38
38
  #endif
39
39
 
40
40
 
41
- #define CALLBACK2(FOR) \
41
+ #if HTTP_PARSER_DEBUG
42
+ #define SET_ERRNO(e) \
42
43
  do { \
43
- if (settings->on_##FOR) { \
44
- if (0 != settings->on_##FOR(parser)) return (p - data); \
45
- } \
44
+ parser->http_errno = (e); \
45
+ parser->error_lineno = __LINE__; \
46
46
  } while (0)
47
+ #else
48
+ #define SET_ERRNO(e) \
49
+ do { \
50
+ parser->http_errno = (e); \
51
+ } while(0)
52
+ #endif
47
53
 
48
54
 
49
- #define MARK(FOR) \
55
+ /* Run the notify callback FOR, returning ER if it fails */
56
+ #define CALLBACK_NOTIFY_(FOR, ER) \
50
57
  do { \
51
- FOR##_mark = p; \
58
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
59
+ \
60
+ if (settings->on_##FOR) { \
61
+ if (0 != settings->on_##FOR(parser)) { \
62
+ SET_ERRNO(HPE_CB_##FOR); \
63
+ } \
64
+ \
65
+ /* We either errored above or got paused; get out */ \
66
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
67
+ return (ER); \
68
+ } \
69
+ } \
52
70
  } while (0)
53
71
 
54
- #define CALLBACK_NOCLEAR(FOR) \
72
+ /* Run the notify callback FOR and consume the current byte */
73
+ #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
74
+
75
+ /* Run the notify callback FOR and don't consume the current byte */
76
+ #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
77
+
78
+ /* Run data callback FOR with LEN bytes, returning ER if it fails */
79
+ #define CALLBACK_DATA_(FOR, LEN, ER) \
55
80
  do { \
81
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
82
+ \
56
83
  if (FOR##_mark) { \
57
84
  if (settings->on_##FOR) { \
58
- if (0 != settings->on_##FOR(parser, \
59
- FOR##_mark, \
60
- p - FOR##_mark)) \
61
- { \
62
- return (p - data); \
85
+ if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \
86
+ SET_ERRNO(HPE_CB_##FOR); \
87
+ } \
88
+ \
89
+ /* We either errored above or got paused; get out */ \
90
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
91
+ return (ER); \
63
92
  } \
64
93
  } \
94
+ FOR##_mark = NULL; \
65
95
  } \
66
96
  } while (0)
97
+
98
+ /* Run the data callback FOR and consume the current byte */
99
+ #define CALLBACK_DATA(FOR) \
100
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
67
101
 
102
+ /* Run the data callback FOR and don't consume the current byte */
103
+ #define CALLBACK_DATA_NOADVANCE(FOR) \
104
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
68
105
 
69
- #define CALLBACK(FOR) \
106
+ /* Set the mark FOR; non-destructive if mark is already set */
107
+ #define MARK(FOR) \
70
108
  do { \
71
- CALLBACK_NOCLEAR(FOR); \
72
- FOR##_mark = NULL; \
109
+ if (!FOR##_mark) { \
110
+ FOR##_mark = p; \
111
+ } \
73
112
  } while (0)
74
113
 
75
114
 
@@ -103,12 +142,23 @@ static const char *method_strings[] =
103
142
  , "MKACTIVITY"
104
143
  , "CHECKOUT"
105
144
  , "MERGE"
145
+ , "M-SEARCH"
146
+ , "NOTIFY"
147
+ , "SUBSCRIBE"
148
+ , "UNSUBSCRIBE"
149
+ , "PATCH"
150
+ , "PURGE"
106
151
  };
107
152
 
108
153
 
109
- /* ' ', '_', '-' and all alpha-numeric ascii characters are accepted by acceptable_header.
110
- The 'A'-'Z' are lower-cased. */
111
- static const char acceptable_header[256] = {
154
+ /* Tokens as defined by rfc 2616. Also lowercases them.
155
+ * token = 1*<any CHAR except CTLs or separators>
156
+ * separators = "(" | ")" | "<" | ">" | "@"
157
+ * | "," | ";" | ":" | "\" | <">
158
+ * | "/" | "[" | "]" | "?" | "="
159
+ * | "{" | "}" | SP | HT
160
+ */
161
+ static const char tokens[256] = {
112
162
  /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
113
163
  0, 0, 0, 0, 0, 0, 0, 0,
114
164
  /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
@@ -118,9 +168,9 @@ static const char acceptable_header[256] = {
118
168
  /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
119
169
  0, 0, 0, 0, 0, 0, 0, 0,
120
170
  /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
121
- ' ', 0, 0, 0, 0, 0, 0, 0,
171
+ 0, '!', 0, '#', '$', '%', '&', '\'',
122
172
  /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
123
- 0, 0, 0, 0, 0, '-', 0, 0,
173
+ 0, 0, '*', '+', 0, '-', '.', 0,
124
174
  /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
125
175
  '0', '1', '2', '3', '4', '5', '6', '7',
126
176
  /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
@@ -132,15 +182,15 @@ static const char acceptable_header[256] = {
132
182
  /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
133
183
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
134
184
  /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
135
- 'x', 'y', 'z', 0, 0, 0, 0, '_',
185
+ 'x', 'y', 'z', 0, 0, 0, '^', '_',
136
186
  /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
137
- 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
187
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
138
188
  /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
139
189
  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
140
190
  /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
141
191
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
142
192
  /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
143
- 'x', 'y', 'z', 0, 0, 0, 0, 0 };
193
+ 'x', 'y', 'z', 0, '|', 0, '~', 0 };
144
194
 
145
195
 
146
196
  static const int8_t unhex[256] =
@@ -187,7 +237,7 @@ static const uint8_t normal_url_char[256] = {
187
237
  /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
188
238
  1, 1, 1, 1, 1, 1, 1, 1,
189
239
  /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
190
- 1, 1, 1, 1, 1, 1, 1, 0 };
240
+ 1, 1, 1, 1, 1, 1, 1, 0, };
191
241
 
192
242
 
193
243
  enum state
@@ -216,7 +266,12 @@ enum state
216
266
  , s_req_schema
217
267
  , s_req_schema_slash
218
268
  , s_req_schema_slash_slash
269
+ , s_req_host_start
270
+ , s_req_host_v6_start
271
+ , s_req_host_v6
272
+ , s_req_host_v6_end
219
273
  , s_req_host
274
+ , s_req_port_start
220
275
  , s_req_port
221
276
  , s_req_path
222
277
  , s_req_query_string_start
@@ -238,28 +293,35 @@ enum state
238
293
  , s_header_field
239
294
  , s_header_value_start
240
295
  , s_header_value
296
+ , s_header_value_lws
241
297
 
242
298
  , s_header_almost_done
243
299
 
300
+ , s_chunk_size_start
301
+ , s_chunk_size
302
+ , s_chunk_parameters
303
+ , s_chunk_size_almost_done
304
+
244
305
  , s_headers_almost_done
245
- /* Important: 's_headers_almost_done' must be the last 'header' state. All
306
+ , s_headers_done
307
+
308
+ /* Important: 's_headers_done' must be the last 'header' state. All
246
309
  * states beyond this must be 'body' states. It is used for overflow
247
310
  * checking. See the PARSING_HEADER() macro.
248
311
  */
249
- , s_chunk_size_start
250
- , s_chunk_size
251
- , s_chunk_size_almost_done
252
- , s_chunk_parameters
312
+
253
313
  , s_chunk_data
254
314
  , s_chunk_data_almost_done
255
315
  , s_chunk_data_done
256
316
 
257
317
  , s_body_identity
258
318
  , s_body_identity_eof
319
+
320
+ , s_message_done
259
321
  };
260
322
 
261
323
 
262
- #define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
324
+ #define PARSING_HEADER(state) (state <= s_headers_done)
263
325
 
264
326
 
265
327
  enum header_states
@@ -289,26 +351,39 @@ enum header_states
289
351
  };
290
352
 
291
353
 
292
- enum flags
293
- { F_CHUNKED = 1 << 0
294
- , F_CONNECTION_KEEP_ALIVE = 1 << 1
295
- , F_CONNECTION_CLOSE = 1 << 2
296
- , F_TRAILING = 1 << 3
297
- , F_UPGRADE = 1 << 4
298
- , F_SKIPBODY = 1 << 5
299
- };
300
-
354
+ /* Macros for character classes; depends on strict-mode */
355
+ #define CR '\r'
356
+ #define LF '\n'
357
+ #define LOWER(c) (unsigned char)(c | 0x20)
358
+ #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
359
+ #define IS_NUM(c) ((c) >= '0' && (c) <= '9')
360
+ #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
361
+ #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
301
362
 
302
- #define CR '\r'
303
- #define LF '\n'
304
- #define LOWER(c) (unsigned char)(c | 0x20)
363
+ #if HTTP_PARSER_STRICT
364
+ #define TOKEN(c) (tokens[(unsigned char)c])
365
+ #define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)])
366
+ #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
367
+ #else
368
+ #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
369
+ #define IS_URL_CHAR(c) \
370
+ (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
371
+ #define IS_HOST_CHAR(c) \
372
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
373
+ #endif
305
374
 
306
375
 
307
376
  #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
308
377
 
309
378
 
310
379
  #if HTTP_PARSER_STRICT
311
- # define STRICT_CHECK(cond) if (cond) goto error
380
+ # define STRICT_CHECK(cond) \
381
+ do { \
382
+ if (cond) { \
383
+ SET_ERRNO(HPE_STRICT); \
384
+ goto error; \
385
+ } \
386
+ } while (0)
312
387
  # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
313
388
  #else
314
389
  # define STRICT_CHECK(cond)
@@ -316,69 +391,290 @@ enum flags
316
391
  #endif
317
392
 
318
393
 
394
+ /* Map errno values to strings for human-readable output */
395
+ #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
396
+ static struct {
397
+ const char *name;
398
+ const char *description;
399
+ } http_strerror_tab[] = {
400
+ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
401
+ };
402
+ #undef HTTP_STRERROR_GEN
403
+
404
+ int http_message_needs_eof(http_parser *parser);
405
+
406
+ /* Our URL parser.
407
+ *
408
+ * This is designed to be shared by http_parser_execute() for URL validation,
409
+ * hence it has a state transition + byte-for-byte interface. In addition, it
410
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
411
+ * work of turning state transitions URL components for its API.
412
+ *
413
+ * This function should only be invoked with non-space characters. It is
414
+ * assumed that the caller cares about (and can detect) the transition between
415
+ * URL and non-URL states by looking for these.
416
+ */
417
+ static enum state
418
+ parse_url_char(enum state s, const char ch)
419
+ {
420
+ assert(!isspace(ch));
421
+
422
+ switch (s) {
423
+ case s_req_spaces_before_url:
424
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
425
+ * All methods except CONNECT are followed by '/' or '*'.
426
+ */
427
+
428
+ if (ch == '/' || ch == '*') {
429
+ return s_req_path;
430
+ }
431
+
432
+ if (IS_ALPHA(ch)) {
433
+ return s_req_schema;
434
+ }
435
+
436
+ break;
437
+
438
+ case s_req_schema:
439
+ if (IS_ALPHA(ch)) {
440
+ return s;
441
+ }
442
+
443
+ if (ch == ':') {
444
+ return s_req_schema_slash;
445
+ }
446
+
447
+ break;
448
+
449
+ case s_req_schema_slash:
450
+ if (ch == '/') {
451
+ return s_req_schema_slash_slash;
452
+ }
453
+
454
+ break;
455
+
456
+ case s_req_schema_slash_slash:
457
+ if (ch == '/') {
458
+ return s_req_host_start;
459
+ }
460
+
461
+ break;
462
+
463
+ case s_req_host_start:
464
+ if (ch == '[') {
465
+ return s_req_host_v6_start;
466
+ }
467
+
468
+ if (IS_HOST_CHAR(ch)) {
469
+ return s_req_host;
470
+ }
471
+
472
+ break;
473
+
474
+ case s_req_host:
475
+ if (IS_HOST_CHAR(ch)) {
476
+ return s_req_host;
477
+ }
478
+
479
+ /* FALLTHROUGH */
480
+ case s_req_host_v6_end:
481
+ switch (ch) {
482
+ case ':':
483
+ return s_req_port_start;
484
+
485
+ case '/':
486
+ return s_req_path;
487
+
488
+ case '?':
489
+ return s_req_query_string_start;
490
+ }
491
+
492
+ break;
493
+
494
+ case s_req_host_v6:
495
+ if (ch == ']') {
496
+ return s_req_host_v6_end;
497
+ }
498
+
499
+ /* FALLTHROUGH */
500
+ case s_req_host_v6_start:
501
+ if (IS_HEX(ch) || ch == ':') {
502
+ return s_req_host_v6;
503
+ }
504
+ break;
505
+
506
+ case s_req_port:
507
+ switch (ch) {
508
+ case '/':
509
+ return s_req_path;
510
+
511
+ case '?':
512
+ return s_req_query_string_start;
513
+ }
514
+
515
+ /* FALLTHROUGH */
516
+ case s_req_port_start:
517
+ if (IS_NUM(ch)) {
518
+ return s_req_port;
519
+ }
520
+
521
+ break;
522
+
523
+ case s_req_path:
524
+ if (IS_URL_CHAR(ch)) {
525
+ return s;
526
+ }
527
+
528
+ switch (ch) {
529
+ case '?':
530
+ return s_req_query_string_start;
531
+
532
+ case '#':
533
+ return s_req_fragment_start;
534
+ }
535
+
536
+ break;
537
+
538
+ case s_req_query_string_start:
539
+ case s_req_query_string:
540
+ if (IS_URL_CHAR(ch)) {
541
+ return s_req_query_string;
542
+ }
543
+
544
+ switch (ch) {
545
+ case '?':
546
+ /* allow extra '?' in query string */
547
+ return s_req_query_string;
548
+
549
+ case '#':
550
+ return s_req_fragment_start;
551
+ }
552
+
553
+ break;
554
+
555
+ case s_req_fragment_start:
556
+ if (IS_URL_CHAR(ch)) {
557
+ return s_req_fragment;
558
+ }
559
+
560
+ switch (ch) {
561
+ case '?':
562
+ return s_req_fragment;
563
+
564
+ case '#':
565
+ return s;
566
+ }
567
+
568
+ break;
569
+
570
+ case s_req_fragment:
571
+ if (IS_URL_CHAR(ch)) {
572
+ return s;
573
+ }
574
+
575
+ switch (ch) {
576
+ case '?':
577
+ case '#':
578
+ return s;
579
+ }
580
+
581
+ break;
582
+
583
+ default:
584
+ break;
585
+ }
586
+
587
+ /* We should never fall out of the switch above unless there's an error */
588
+ return s_dead;
589
+ }
590
+
319
591
  size_t http_parser_execute (http_parser *parser,
320
592
  const http_parser_settings *settings,
321
593
  const char *data,
322
594
  size_t len)
323
595
  {
324
596
  char c, ch;
325
- const char *p = data, *pe;
326
- int64_t to_read;
597
+ int8_t unhex_val;
598
+ const char *p = data;
599
+ const char *header_field_mark = 0;
600
+ const char *header_value_mark = 0;
601
+ const char *url_mark = 0;
602
+ const char *body_mark = 0;
327
603
 
328
- enum state state = (enum state) parser->state;
329
- enum header_states header_state = (enum header_states) parser->header_state;
330
- uint64_t index = parser->index;
331
- uint64_t nread = parser->nread;
604
+ /* We're in an error state. Don't bother doing anything. */
605
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
606
+ return 0;
607
+ }
332
608
 
333
609
  if (len == 0) {
334
- if (state == s_body_identity_eof) {
335
- CALLBACK2(message_complete);
610
+ switch (parser->state) {
611
+ case s_body_identity_eof:
612
+ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
613
+ * we got paused.
614
+ */
615
+ CALLBACK_NOTIFY_NOADVANCE(message_complete);
616
+ return 0;
617
+
618
+ case s_dead:
619
+ case s_start_req_or_res:
620
+ case s_start_res:
621
+ case s_start_req:
622
+ return 0;
623
+
624
+ default:
625
+ SET_ERRNO(HPE_INVALID_EOF_STATE);
626
+ return 1;
336
627
  }
337
- return 0;
338
628
  }
339
629
 
340
- /* technically we could combine all of these (except for url_mark) into one
341
- variable, saving stack space, but it seems more clear to have them
342
- separated. */
343
- const char *header_field_mark = 0;
344
- const char *header_value_mark = 0;
345
- const char *fragment_mark = 0;
346
- const char *query_string_mark = 0;
347
- const char *path_mark = 0;
348
- const char *url_mark = 0;
349
630
 
350
- if (state == s_header_field)
631
+ if (parser->state == s_header_field)
351
632
  header_field_mark = data;
352
- if (state == s_header_value)
633
+ if (parser->state == s_header_value)
353
634
  header_value_mark = data;
354
- if (state == s_req_fragment)
355
- fragment_mark = data;
356
- if (state == s_req_query_string)
357
- query_string_mark = data;
358
- if (state == s_req_path)
359
- path_mark = data;
360
- if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
361
- || state == s_req_schema_slash_slash || state == s_req_port
362
- || state == s_req_query_string_start || state == s_req_query_string
363
- || state == s_req_host
364
- || state == s_req_fragment_start || state == s_req_fragment)
635
+ switch (parser->state) {
636
+ case s_req_path:
637
+ case s_req_schema:
638
+ case s_req_schema_slash:
639
+ case s_req_schema_slash_slash:
640
+ case s_req_host_start:
641
+ case s_req_host_v6_start:
642
+ case s_req_host_v6:
643
+ case s_req_host_v6_end:
644
+ case s_req_host:
645
+ case s_req_port_start:
646
+ case s_req_port:
647
+ case s_req_query_string_start:
648
+ case s_req_query_string:
649
+ case s_req_fragment_start:
650
+ case s_req_fragment:
365
651
  url_mark = data;
652
+ break;
653
+ }
366
654
 
367
- for (p=data, pe=data+len; p != pe; p++) {
655
+ for (p=data; p != data + len; p++) {
368
656
  ch = *p;
369
657
 
370
- if (PARSING_HEADER(state)) {
371
- ++nread;
658
+ if (PARSING_HEADER(parser->state)) {
659
+ ++parser->nread;
372
660
  /* Buffer overflow attack */
373
- if (nread > HTTP_MAX_HEADER_SIZE) goto error;
661
+ if (parser->nread > HTTP_MAX_HEADER_SIZE) {
662
+ SET_ERRNO(HPE_HEADER_OVERFLOW);
663
+ goto error;
664
+ }
374
665
  }
375
666
 
376
- switch (state) {
667
+ reexecute_byte:
668
+ switch (parser->state) {
377
669
 
378
670
  case s_dead:
379
671
  /* this state is used after a 'Connection: close' message
380
672
  * the parser will error out if it reads another message
381
673
  */
674
+ if (ch == CR || ch == LF)
675
+ break;
676
+
677
+ SET_ERRNO(HPE_CLOSED_CONNECTION);
382
678
  goto error;
383
679
 
384
680
  case s_start_req_or_res:
@@ -386,42 +682,46 @@ size_t http_parser_execute (http_parser *parser,
386
682
  if (ch == CR || ch == LF)
387
683
  break;
388
684
  parser->flags = 0;
389
- parser->content_length = -1;
685
+ parser->content_length = ULLONG_MAX;
390
686
 
391
- CALLBACK2(message_begin);
687
+ if (ch == 'H') {
688
+ parser->state = s_res_or_resp_H;
392
689
 
393
- if (ch == 'H')
394
- state = s_res_or_resp_H;
395
- else {
690
+ CALLBACK_NOTIFY(message_begin);
691
+ } else {
396
692
  parser->type = HTTP_REQUEST;
397
- goto start_req_method_assign;
693
+ parser->state = s_start_req;
694
+ goto reexecute_byte;
398
695
  }
696
+
399
697
  break;
400
698
  }
401
699
 
402
700
  case s_res_or_resp_H:
403
701
  if (ch == 'T') {
404
702
  parser->type = HTTP_RESPONSE;
405
- state = s_res_HT;
703
+ parser->state = s_res_HT;
406
704
  } else {
407
- if (ch != 'E') goto error;
705
+ if (ch != 'E') {
706
+ SET_ERRNO(HPE_INVALID_CONSTANT);
707
+ goto error;
708
+ }
709
+
408
710
  parser->type = HTTP_REQUEST;
409
711
  parser->method = HTTP_HEAD;
410
- index = 2;
411
- state = s_req_method;
712
+ parser->index = 2;
713
+ parser->state = s_req_method;
412
714
  }
413
715
  break;
414
716
 
415
717
  case s_start_res:
416
718
  {
417
719
  parser->flags = 0;
418
- parser->content_length = -1;
419
-
420
- CALLBACK2(message_begin);
720
+ parser->content_length = ULLONG_MAX;
421
721
 
422
722
  switch (ch) {
423
723
  case 'H':
424
- state = s_res_H;
724
+ parser->state = s_res_H;
425
725
  break;
426
726
 
427
727
  case CR:
@@ -429,105 +729,133 @@ size_t http_parser_execute (http_parser *parser,
429
729
  break;
430
730
 
431
731
  default:
732
+ SET_ERRNO(HPE_INVALID_CONSTANT);
432
733
  goto error;
433
734
  }
735
+
736
+ CALLBACK_NOTIFY(message_begin);
434
737
  break;
435
738
  }
436
739
 
437
740
  case s_res_H:
438
741
  STRICT_CHECK(ch != 'T');
439
- state = s_res_HT;
742
+ parser->state = s_res_HT;
440
743
  break;
441
744
 
442
745
  case s_res_HT:
443
746
  STRICT_CHECK(ch != 'T');
444
- state = s_res_HTT;
747
+ parser->state = s_res_HTT;
445
748
  break;
446
749
 
447
750
  case s_res_HTT:
448
751
  STRICT_CHECK(ch != 'P');
449
- state = s_res_HTTP;
752
+ parser->state = s_res_HTTP;
450
753
  break;
451
754
 
452
755
  case s_res_HTTP:
453
756
  STRICT_CHECK(ch != '/');
454
- state = s_res_first_http_major;
757
+ parser->state = s_res_first_http_major;
455
758
  break;
456
759
 
457
760
  case s_res_first_http_major:
458
- if (ch < '1' || ch > '9') goto error;
761
+ if (ch < '0' || ch > '9') {
762
+ SET_ERRNO(HPE_INVALID_VERSION);
763
+ goto error;
764
+ }
765
+
459
766
  parser->http_major = ch - '0';
460
- state = s_res_http_major;
767
+ parser->state = s_res_http_major;
461
768
  break;
462
769
 
463
770
  /* major HTTP version or dot */
464
771
  case s_res_http_major:
465
772
  {
466
773
  if (ch == '.') {
467
- state = s_res_first_http_minor;
774
+ parser->state = s_res_first_http_minor;
468
775
  break;
469
776
  }
470
777
 
471
- if (ch < '0' || ch > '9') goto error;
778
+ if (!IS_NUM(ch)) {
779
+ SET_ERRNO(HPE_INVALID_VERSION);
780
+ goto error;
781
+ }
472
782
 
473
783
  parser->http_major *= 10;
474
784
  parser->http_major += ch - '0';
475
785
 
476
- if (parser->http_major > 999) goto error;
786
+ if (parser->http_major > 999) {
787
+ SET_ERRNO(HPE_INVALID_VERSION);
788
+ goto error;
789
+ }
790
+
477
791
  break;
478
792
  }
479
793
 
480
794
  /* first digit of minor HTTP version */
481
795
  case s_res_first_http_minor:
482
- if (ch < '0' || ch > '9') goto error;
796
+ if (!IS_NUM(ch)) {
797
+ SET_ERRNO(HPE_INVALID_VERSION);
798
+ goto error;
799
+ }
800
+
483
801
  parser->http_minor = ch - '0';
484
- state = s_res_http_minor;
802
+ parser->state = s_res_http_minor;
485
803
  break;
486
804
 
487
805
  /* minor HTTP version or end of request line */
488
806
  case s_res_http_minor:
489
807
  {
490
808
  if (ch == ' ') {
491
- state = s_res_first_status_code;
809
+ parser->state = s_res_first_status_code;
492
810
  break;
493
811
  }
494
812
 
495
- if (ch < '0' || ch > '9') goto error;
813
+ if (!IS_NUM(ch)) {
814
+ SET_ERRNO(HPE_INVALID_VERSION);
815
+ goto error;
816
+ }
496
817
 
497
818
  parser->http_minor *= 10;
498
819
  parser->http_minor += ch - '0';
499
820
 
500
- if (parser->http_minor > 999) goto error;
821
+ if (parser->http_minor > 999) {
822
+ SET_ERRNO(HPE_INVALID_VERSION);
823
+ goto error;
824
+ }
825
+
501
826
  break;
502
827
  }
503
828
 
504
829
  case s_res_first_status_code:
505
830
  {
506
- if (ch < '0' || ch > '9') {
831
+ if (!IS_NUM(ch)) {
507
832
  if (ch == ' ') {
508
833
  break;
509
834
  }
835
+
836
+ SET_ERRNO(HPE_INVALID_STATUS);
510
837
  goto error;
511
838
  }
512
839
  parser->status_code = ch - '0';
513
- state = s_res_status_code;
840
+ parser->state = s_res_status_code;
514
841
  break;
515
842
  }
516
843
 
517
844
  case s_res_status_code:
518
845
  {
519
- if (ch < '0' || ch > '9') {
846
+ if (!IS_NUM(ch)) {
520
847
  switch (ch) {
521
848
  case ' ':
522
- state = s_res_status;
849
+ parser->state = s_res_status;
523
850
  break;
524
851
  case CR:
525
- state = s_res_line_almost_done;
852
+ parser->state = s_res_line_almost_done;
526
853
  break;
527
854
  case LF:
528
- state = s_header_field_start;
855
+ parser->state = s_header_field_start;
529
856
  break;
530
857
  default:
858
+ SET_ERRNO(HPE_INVALID_STATUS);
531
859
  goto error;
532
860
  }
533
861
  break;
@@ -536,7 +864,11 @@ size_t http_parser_execute (http_parser *parser,
536
864
  parser->status_code *= 10;
537
865
  parser->status_code += ch - '0';
538
866
 
539
- if (parser->status_code > 999) goto error;
867
+ if (parser->status_code > 999) {
868
+ SET_ERRNO(HPE_INVALID_STATUS);
869
+ goto error;
870
+ }
871
+
540
872
  break;
541
873
  }
542
874
 
@@ -544,19 +876,19 @@ size_t http_parser_execute (http_parser *parser,
544
876
  /* the human readable status. e.g. "NOT FOUND"
545
877
  * we are not humans so just ignore this */
546
878
  if (ch == CR) {
547
- state = s_res_line_almost_done;
879
+ parser->state = s_res_line_almost_done;
548
880
  break;
549
881
  }
550
882
 
551
883
  if (ch == LF) {
552
- state = s_header_field_start;
884
+ parser->state = s_header_field_start;
553
885
  break;
554
886
  }
555
887
  break;
556
888
 
557
889
  case s_res_line_almost_done:
558
890
  STRICT_CHECK(ch != LF);
559
- state = s_header_field_start;
891
+ parser->state = s_header_field_start;
560
892
  break;
561
893
 
562
894
  case s_start_req:
@@ -564,335 +896,175 @@ size_t http_parser_execute (http_parser *parser,
564
896
  if (ch == CR || ch == LF)
565
897
  break;
566
898
  parser->flags = 0;
567
- parser->content_length = -1;
899
+ parser->content_length = ULLONG_MAX;
568
900
 
569
- CALLBACK2(message_begin);
570
-
571
- if (ch < 'A' || 'Z' < ch) goto error;
901
+ if (!IS_ALPHA(ch)) {
902
+ SET_ERRNO(HPE_INVALID_METHOD);
903
+ goto error;
904
+ }
572
905
 
573
- start_req_method_assign:
574
906
  parser->method = (enum http_method) 0;
575
- index = 1;
907
+ parser->index = 1;
576
908
  switch (ch) {
577
909
  case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
578
910
  case 'D': parser->method = HTTP_DELETE; break;
579
911
  case 'G': parser->method = HTTP_GET; break;
580
912
  case 'H': parser->method = HTTP_HEAD; break;
581
913
  case 'L': parser->method = HTTP_LOCK; break;
582
- case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE */ break;
914
+ case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
915
+ case 'N': parser->method = HTTP_NOTIFY; break;
583
916
  case 'O': parser->method = HTTP_OPTIONS; break;
584
- case 'P': parser->method = HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
917
+ case 'P': parser->method = HTTP_POST;
918
+ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
919
+ break;
585
920
  case 'R': parser->method = HTTP_REPORT; break;
921
+ case 'S': parser->method = HTTP_SUBSCRIBE; break;
586
922
  case 'T': parser->method = HTTP_TRACE; break;
587
- case 'U': parser->method = HTTP_UNLOCK; break;
588
- default: goto error;
923
+ case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
924
+ default:
925
+ SET_ERRNO(HPE_INVALID_METHOD);
926
+ goto error;
589
927
  }
590
- state = s_req_method;
928
+ parser->state = s_req_method;
929
+
930
+ CALLBACK_NOTIFY(message_begin);
931
+
591
932
  break;
592
933
  }
593
934
 
594
935
  case s_req_method:
595
936
  {
596
- if (ch == '\0')
937
+ const char *matcher;
938
+ if (ch == '\0') {
939
+ SET_ERRNO(HPE_INVALID_METHOD);
597
940
  goto error;
941
+ }
598
942
 
599
- const char *matcher = method_strings[parser->method];
600
- if (ch == ' ' && matcher[index] == '\0') {
601
- state = s_req_spaces_before_url;
602
- } else if (ch == matcher[index]) {
943
+ matcher = method_strings[parser->method];
944
+ if (ch == ' ' && matcher[parser->index] == '\0') {
945
+ parser->state = s_req_spaces_before_url;
946
+ } else if (ch == matcher[parser->index]) {
603
947
  ; /* nada */
604
948
  } else if (parser->method == HTTP_CONNECT) {
605
- if (index == 1 && ch == 'H') {
949
+ if (parser->index == 1 && ch == 'H') {
606
950
  parser->method = HTTP_CHECKOUT;
607
- } else if (index == 2 && ch == 'P') {
951
+ } else if (parser->index == 2 && ch == 'P') {
608
952
  parser->method = HTTP_COPY;
953
+ } else {
954
+ goto error;
609
955
  }
610
956
  } else if (parser->method == HTTP_MKCOL) {
611
- if (index == 1 && ch == 'O') {
957
+ if (parser->index == 1 && ch == 'O') {
612
958
  parser->method = HTTP_MOVE;
613
- } else if (index == 1 && ch == 'E') {
959
+ } else if (parser->index == 1 && ch == 'E') {
614
960
  parser->method = HTTP_MERGE;
615
- } else if (index == 2 && ch == 'A') {
961
+ } else if (parser->index == 1 && ch == '-') {
962
+ parser->method = HTTP_MSEARCH;
963
+ } else if (parser->index == 2 && ch == 'A') {
616
964
  parser->method = HTTP_MKACTIVITY;
965
+ } else {
966
+ goto error;
967
+ }
968
+ } else if (parser->index == 1 && parser->method == HTTP_POST) {
969
+ if (ch == 'R') {
970
+ parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
971
+ } else if (ch == 'U') {
972
+ parser->method = HTTP_PUT; /* or HTTP_PURGE */
973
+ } else if (ch == 'A') {
974
+ parser->method = HTTP_PATCH;
975
+ } else {
976
+ goto error;
617
977
  }
618
- } else if (index == 1 && parser->method == HTTP_POST && ch == 'R') {
619
- parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
620
- } else if (index == 1 && parser->method == HTTP_POST && ch == 'U') {
621
- parser->method = HTTP_PUT;
622
- } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
978
+ } else if (parser->index == 2) {
979
+ if (parser->method == HTTP_PUT) {
980
+ if (ch == 'R') parser->method = HTTP_PURGE;
981
+ } else if (parser->method == HTTP_UNLOCK) {
982
+ if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE;
983
+ }
984
+ } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
623
985
  parser->method = HTTP_PROPPATCH;
624
986
  } else {
987
+ SET_ERRNO(HPE_INVALID_METHOD);
625
988
  goto error;
626
989
  }
627
990
 
628
- ++index;
991
+ ++parser->index;
629
992
  break;
630
993
  }
994
+
631
995
  case s_req_spaces_before_url:
632
996
  {
633
997
  if (ch == ' ') break;
634
998
 
635
- if (ch == '/') {
636
- MARK(url);
637
- MARK(path);
638
- state = s_req_path;
639
- break;
999
+ MARK(url);
1000
+ if (parser->method == HTTP_CONNECT) {
1001
+ parser->state = s_req_host_start;
640
1002
  }
641
1003
 
642
- c = LOWER(ch);
643
-
644
- if (c >= 'a' && c <= 'z') {
645
- MARK(url);
646
- state = s_req_schema;
647
- break;
1004
+ parser->state = parse_url_char((enum state)parser->state, ch);
1005
+ if (parser->state == s_dead) {
1006
+ SET_ERRNO(HPE_INVALID_URL);
1007
+ goto error;
648
1008
  }
649
1009
 
650
- goto error;
1010
+ break;
651
1011
  }
652
1012
 
653
1013
  case s_req_schema:
654
- {
655
- c = LOWER(ch);
656
-
657
- if (c >= 'a' && c <= 'z') break;
658
-
659
- if (ch == ':') {
660
- state = s_req_schema_slash;
661
- break;
662
- } else if (ch == '.') {
663
- state = s_req_host;
664
- break;
665
- }
666
-
667
- goto error;
668
- }
669
-
670
1014
  case s_req_schema_slash:
671
- STRICT_CHECK(ch != '/');
672
- state = s_req_schema_slash_slash;
673
- break;
674
-
675
1015
  case s_req_schema_slash_slash:
676
- STRICT_CHECK(ch != '/');
677
- state = s_req_host;
678
- break;
679
-
680
- case s_req_host:
1016
+ case s_req_host_start:
1017
+ case s_req_host_v6_start:
1018
+ case s_req_host_v6:
1019
+ case s_req_port_start:
681
1020
  {
682
- c = LOWER(ch);
683
- if (c >= 'a' && c <= 'z') break;
684
- if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
685
1021
  switch (ch) {
686
- case ':':
687
- state = s_req_port;
688
- break;
689
- case '/':
690
- MARK(path);
691
- state = s_req_path;
692
- break;
1022
+ /* No whitespace allowed here */
693
1023
  case ' ':
694
- /* The request line looks like:
695
- * "GET http://foo.bar.com HTTP/1.1"
696
- * That is, there is no path.
697
- */
698
- CALLBACK(url);
699
- state = s_req_http_start;
700
- break;
701
- default:
702
- goto error;
703
- }
704
- break;
705
- }
706
-
707
- case s_req_port:
708
- {
709
- if (ch >= '0' && ch <= '9') break;
710
- switch (ch) {
711
- case '/':
712
- MARK(path);
713
- state = s_req_path;
714
- break;
715
- case ' ':
716
- /* The request line looks like:
717
- * "GET http://foo.bar.com:1234 HTTP/1.1"
718
- * That is, there is no path.
719
- */
720
- CALLBACK(url);
721
- state = s_req_http_start;
722
- break;
723
- default:
724
- goto error;
725
- }
726
- break;
727
- }
728
-
729
- case s_req_path:
730
- {
731
- if (normal_url_char[(unsigned char)ch]) break;
732
-
733
- switch (ch) {
734
- case ' ':
735
- CALLBACK(url);
736
- CALLBACK(path);
737
- state = s_req_http_start;
738
- break;
739
1024
  case CR:
740
- CALLBACK(url);
741
- CALLBACK(path);
742
- parser->http_minor = 9;
743
- state = s_req_line_almost_done;
744
- break;
745
1025
  case LF:
746
- CALLBACK(url);
747
- CALLBACK(path);
748
- parser->http_minor = 9;
749
- state = s_header_field_start;
750
- break;
751
- case '?':
752
- CALLBACK(path);
753
- state = s_req_query_string_start;
754
- break;
755
- case '#':
756
- CALLBACK(path);
757
- state = s_req_fragment_start;
758
- break;
759
- default:
1026
+ SET_ERRNO(HPE_INVALID_URL);
760
1027
  goto error;
761
- }
762
- break;
763
- }
764
-
765
- case s_req_query_string_start:
766
- {
767
- if (normal_url_char[(unsigned char)ch]) {
768
- MARK(query_string);
769
- state = s_req_query_string;
770
- break;
771
- }
772
-
773
- switch (ch) {
774
- case '?':
775
- break; /* XXX ignore extra '?' ... is this right? */
776
- case ' ':
777
- CALLBACK(url);
778
- state = s_req_http_start;
779
- break;
780
- case CR:
781
- CALLBACK(url);
782
- parser->http_minor = 9;
783
- state = s_req_line_almost_done;
784
- break;
785
- case LF:
786
- CALLBACK(url);
787
- parser->http_minor = 9;
788
- state = s_header_field_start;
789
- break;
790
- case '#':
791
- state = s_req_fragment_start;
792
- break;
793
1028
  default:
794
- goto error;
1029
+ parser->state = parse_url_char((enum state)parser->state, ch);
1030
+ if (parser->state == s_dead) {
1031
+ SET_ERRNO(HPE_INVALID_URL);
1032
+ goto error;
1033
+ }
795
1034
  }
796
- break;
797
- }
798
1035
 
799
- case s_req_query_string:
800
- {
801
- if (normal_url_char[(unsigned char)ch]) break;
802
-
803
- switch (ch) {
804
- case '?':
805
- /* allow extra '?' in query string */
806
- break;
807
- case ' ':
808
- CALLBACK(url);
809
- CALLBACK(query_string);
810
- state = s_req_http_start;
811
- break;
812
- case CR:
813
- CALLBACK(url);
814
- CALLBACK(query_string);
815
- parser->http_minor = 9;
816
- state = s_req_line_almost_done;
817
- break;
818
- case LF:
819
- CALLBACK(url);
820
- CALLBACK(query_string);
821
- parser->http_minor = 9;
822
- state = s_header_field_start;
823
- break;
824
- case '#':
825
- CALLBACK(query_string);
826
- state = s_req_fragment_start;
827
- break;
828
- default:
829
- goto error;
830
- }
831
1036
  break;
832
1037
  }
833
1038
 
1039
+ case s_req_host:
1040
+ case s_req_host_v6_end:
1041
+ case s_req_port:
1042
+ case s_req_path:
1043
+ case s_req_query_string_start:
1044
+ case s_req_query_string:
834
1045
  case s_req_fragment_start:
835
- {
836
- if (normal_url_char[(unsigned char)ch]) {
837
- MARK(fragment);
838
- state = s_req_fragment;
839
- break;
840
- }
841
-
842
- switch (ch) {
843
- case ' ':
844
- CALLBACK(url);
845
- state = s_req_http_start;
846
- break;
847
- case CR:
848
- CALLBACK(url);
849
- parser->http_minor = 9;
850
- state = s_req_line_almost_done;
851
- break;
852
- case LF:
853
- CALLBACK(url);
854
- parser->http_minor = 9;
855
- state = s_header_field_start;
856
- break;
857
- case '?':
858
- MARK(fragment);
859
- state = s_req_fragment;
860
- break;
861
- case '#':
862
- break;
863
- default:
864
- goto error;
865
- }
866
- break;
867
- }
868
-
869
1046
  case s_req_fragment:
870
1047
  {
871
- if (normal_url_char[(unsigned char)ch]) break;
872
-
873
1048
  switch (ch) {
874
1049
  case ' ':
875
- CALLBACK(url);
876
- CALLBACK(fragment);
877
- state = s_req_http_start;
1050
+ parser->state = s_req_http_start;
1051
+ CALLBACK_DATA(url);
878
1052
  break;
879
1053
  case CR:
880
- CALLBACK(url);
881
- CALLBACK(fragment);
882
- parser->http_minor = 9;
883
- state = s_req_line_almost_done;
884
- break;
885
1054
  case LF:
886
- CALLBACK(url);
887
- CALLBACK(fragment);
1055
+ parser->http_major = 0;
888
1056
  parser->http_minor = 9;
889
- state = s_header_field_start;
890
- break;
891
- case '?':
892
- case '#':
1057
+ parser->state = (ch == CR) ?
1058
+ s_req_line_almost_done :
1059
+ s_header_field_start;
1060
+ CALLBACK_DATA(url);
893
1061
  break;
894
1062
  default:
895
- goto error;
1063
+ parser->state = parse_url_char((enum state)parser->state, ch);
1064
+ if (parser->state == s_dead) {
1065
+ SET_ERRNO(HPE_INVALID_URL);
1066
+ goto error;
1067
+ }
896
1068
  }
897
1069
  break;
898
1070
  }
@@ -900,140 +1072,170 @@ size_t http_parser_execute (http_parser *parser,
900
1072
  case s_req_http_start:
901
1073
  switch (ch) {
902
1074
  case 'H':
903
- state = s_req_http_H;
1075
+ parser->state = s_req_http_H;
904
1076
  break;
905
1077
  case ' ':
906
1078
  break;
907
1079
  default:
1080
+ SET_ERRNO(HPE_INVALID_CONSTANT);
908
1081
  goto error;
909
1082
  }
910
1083
  break;
911
1084
 
912
1085
  case s_req_http_H:
913
1086
  STRICT_CHECK(ch != 'T');
914
- state = s_req_http_HT;
1087
+ parser->state = s_req_http_HT;
915
1088
  break;
916
1089
 
917
1090
  case s_req_http_HT:
918
1091
  STRICT_CHECK(ch != 'T');
919
- state = s_req_http_HTT;
1092
+ parser->state = s_req_http_HTT;
920
1093
  break;
921
1094
 
922
1095
  case s_req_http_HTT:
923
1096
  STRICT_CHECK(ch != 'P');
924
- state = s_req_http_HTTP;
1097
+ parser->state = s_req_http_HTTP;
925
1098
  break;
926
1099
 
927
1100
  case s_req_http_HTTP:
928
1101
  STRICT_CHECK(ch != '/');
929
- state = s_req_first_http_major;
1102
+ parser->state = s_req_first_http_major;
930
1103
  break;
931
1104
 
932
1105
  /* first digit of major HTTP version */
933
1106
  case s_req_first_http_major:
934
- if (ch < '1' || ch > '9') goto error;
1107
+ if (ch < '1' || ch > '9') {
1108
+ SET_ERRNO(HPE_INVALID_VERSION);
1109
+ goto error;
1110
+ }
1111
+
935
1112
  parser->http_major = ch - '0';
936
- state = s_req_http_major;
1113
+ parser->state = s_req_http_major;
937
1114
  break;
938
1115
 
939
1116
  /* major HTTP version or dot */
940
1117
  case s_req_http_major:
941
1118
  {
942
1119
  if (ch == '.') {
943
- state = s_req_first_http_minor;
1120
+ parser->state = s_req_first_http_minor;
944
1121
  break;
945
1122
  }
946
1123
 
947
- if (ch < '0' || ch > '9') goto error;
1124
+ if (!IS_NUM(ch)) {
1125
+ SET_ERRNO(HPE_INVALID_VERSION);
1126
+ goto error;
1127
+ }
948
1128
 
949
1129
  parser->http_major *= 10;
950
1130
  parser->http_major += ch - '0';
951
1131
 
952
- if (parser->http_major > 999) goto error;
1132
+ if (parser->http_major > 999) {
1133
+ SET_ERRNO(HPE_INVALID_VERSION);
1134
+ goto error;
1135
+ }
1136
+
953
1137
  break;
954
1138
  }
955
1139
 
956
1140
  /* first digit of minor HTTP version */
957
1141
  case s_req_first_http_minor:
958
- if (ch < '0' || ch > '9') goto error;
1142
+ if (!IS_NUM(ch)) {
1143
+ SET_ERRNO(HPE_INVALID_VERSION);
1144
+ goto error;
1145
+ }
1146
+
959
1147
  parser->http_minor = ch - '0';
960
- state = s_req_http_minor;
1148
+ parser->state = s_req_http_minor;
961
1149
  break;
962
1150
 
963
1151
  /* minor HTTP version or end of request line */
964
1152
  case s_req_http_minor:
965
1153
  {
966
1154
  if (ch == CR) {
967
- state = s_req_line_almost_done;
1155
+ parser->state = s_req_line_almost_done;
968
1156
  break;
969
1157
  }
970
1158
 
971
1159
  if (ch == LF) {
972
- state = s_header_field_start;
1160
+ parser->state = s_header_field_start;
973
1161
  break;
974
1162
  }
975
1163
 
976
1164
  /* XXX allow spaces after digit? */
977
1165
 
978
- if (ch < '0' || ch > '9') goto error;
1166
+ if (!IS_NUM(ch)) {
1167
+ SET_ERRNO(HPE_INVALID_VERSION);
1168
+ goto error;
1169
+ }
979
1170
 
980
1171
  parser->http_minor *= 10;
981
1172
  parser->http_minor += ch - '0';
982
1173
 
983
- if (parser->http_minor > 999) goto error;
1174
+ if (parser->http_minor > 999) {
1175
+ SET_ERRNO(HPE_INVALID_VERSION);
1176
+ goto error;
1177
+ }
1178
+
984
1179
  break;
985
1180
  }
986
1181
 
987
1182
  /* end of request line */
988
1183
  case s_req_line_almost_done:
989
1184
  {
990
- if (ch != LF) goto error;
991
- state = s_header_field_start;
1185
+ if (ch != LF) {
1186
+ SET_ERRNO(HPE_LF_EXPECTED);
1187
+ goto error;
1188
+ }
1189
+
1190
+ parser->state = s_header_field_start;
992
1191
  break;
993
1192
  }
994
1193
 
995
1194
  case s_header_field_start:
996
1195
  {
997
1196
  if (ch == CR) {
998
- state = s_headers_almost_done;
1197
+ parser->state = s_headers_almost_done;
999
1198
  break;
1000
1199
  }
1001
1200
 
1002
1201
  if (ch == LF) {
1003
1202
  /* they might be just sending \n instead of \r\n so this would be
1004
1203
  * the second \n to denote the end of headers*/
1005
- state = s_headers_almost_done;
1006
- goto headers_almost_done;
1204
+ parser->state = s_headers_almost_done;
1205
+ goto reexecute_byte;
1007
1206
  }
1008
1207
 
1009
- c = LOWER(ch);
1208
+ c = TOKEN(ch);
1010
1209
 
1011
- if (c < 'a' || 'z' < c) goto error;
1210
+ if (!c) {
1211
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
1212
+ goto error;
1213
+ }
1012
1214
 
1013
1215
  MARK(header_field);
1014
1216
 
1015
- index = 0;
1016
- state = s_header_field;
1217
+ parser->index = 0;
1218
+ parser->state = s_header_field;
1017
1219
 
1018
1220
  switch (c) {
1019
1221
  case 'c':
1020
- header_state = h_C;
1222
+ parser->header_state = h_C;
1021
1223
  break;
1022
1224
 
1023
1225
  case 'p':
1024
- header_state = h_matching_proxy_connection;
1226
+ parser->header_state = h_matching_proxy_connection;
1025
1227
  break;
1026
1228
 
1027
1229
  case 't':
1028
- header_state = h_matching_transfer_encoding;
1230
+ parser->header_state = h_matching_transfer_encoding;
1029
1231
  break;
1030
1232
 
1031
1233
  case 'u':
1032
- header_state = h_matching_upgrade;
1234
+ parser->header_state = h_matching_upgrade;
1033
1235
  break;
1034
1236
 
1035
1237
  default:
1036
- header_state = h_general;
1238
+ parser->header_state = h_general;
1037
1239
  break;
1038
1240
  }
1039
1241
  break;
@@ -1041,34 +1243,34 @@ size_t http_parser_execute (http_parser *parser,
1041
1243
 
1042
1244
  case s_header_field:
1043
1245
  {
1044
- c = acceptable_header[(unsigned char)ch];
1246
+ c = TOKEN(ch);
1045
1247
 
1046
1248
  if (c) {
1047
- switch (header_state) {
1249
+ switch (parser->header_state) {
1048
1250
  case h_general:
1049
1251
  break;
1050
1252
 
1051
1253
  case h_C:
1052
- index++;
1053
- header_state = (c == 'o' ? h_CO : h_general);
1254
+ parser->index++;
1255
+ parser->header_state = (c == 'o' ? h_CO : h_general);
1054
1256
  break;
1055
1257
 
1056
1258
  case h_CO:
1057
- index++;
1058
- header_state = (c == 'n' ? h_CON : h_general);
1259
+ parser->index++;
1260
+ parser->header_state = (c == 'n' ? h_CON : h_general);
1059
1261
  break;
1060
1262
 
1061
1263
  case h_CON:
1062
- index++;
1264
+ parser->index++;
1063
1265
  switch (c) {
1064
1266
  case 'n':
1065
- header_state = h_matching_connection;
1267
+ parser->header_state = h_matching_connection;
1066
1268
  break;
1067
1269
  case 't':
1068
- header_state = h_matching_content_length;
1270
+ parser->header_state = h_matching_content_length;
1069
1271
  break;
1070
1272
  default:
1071
- header_state = h_general;
1273
+ parser->header_state = h_general;
1072
1274
  break;
1073
1275
  }
1074
1276
  break;
@@ -1076,60 +1278,60 @@ size_t http_parser_execute (http_parser *parser,
1076
1278
  /* connection */
1077
1279
 
1078
1280
  case h_matching_connection:
1079
- index++;
1080
- if (index > sizeof(CONNECTION)-1
1081
- || c != CONNECTION[index]) {
1082
- header_state = h_general;
1083
- } else if (index == sizeof(CONNECTION)-2) {
1084
- header_state = h_connection;
1281
+ parser->index++;
1282
+ if (parser->index > sizeof(CONNECTION)-1
1283
+ || c != CONNECTION[parser->index]) {
1284
+ parser->header_state = h_general;
1285
+ } else if (parser->index == sizeof(CONNECTION)-2) {
1286
+ parser->header_state = h_connection;
1085
1287
  }
1086
1288
  break;
1087
1289
 
1088
1290
  /* proxy-connection */
1089
1291
 
1090
1292
  case h_matching_proxy_connection:
1091
- index++;
1092
- if (index > sizeof(PROXY_CONNECTION)-1
1093
- || c != PROXY_CONNECTION[index]) {
1094
- header_state = h_general;
1095
- } else if (index == sizeof(PROXY_CONNECTION)-2) {
1096
- header_state = h_connection;
1293
+ parser->index++;
1294
+ if (parser->index > sizeof(PROXY_CONNECTION)-1
1295
+ || c != PROXY_CONNECTION[parser->index]) {
1296
+ parser->header_state = h_general;
1297
+ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
1298
+ parser->header_state = h_connection;
1097
1299
  }
1098
1300
  break;
1099
1301
 
1100
1302
  /* content-length */
1101
1303
 
1102
1304
  case h_matching_content_length:
1103
- index++;
1104
- if (index > sizeof(CONTENT_LENGTH)-1
1105
- || c != CONTENT_LENGTH[index]) {
1106
- header_state = h_general;
1107
- } else if (index == sizeof(CONTENT_LENGTH)-2) {
1108
- header_state = h_content_length;
1305
+ parser->index++;
1306
+ if (parser->index > sizeof(CONTENT_LENGTH)-1
1307
+ || c != CONTENT_LENGTH[parser->index]) {
1308
+ parser->header_state = h_general;
1309
+ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
1310
+ parser->header_state = h_content_length;
1109
1311
  }
1110
1312
  break;
1111
1313
 
1112
1314
  /* transfer-encoding */
1113
1315
 
1114
1316
  case h_matching_transfer_encoding:
1115
- index++;
1116
- if (index > sizeof(TRANSFER_ENCODING)-1
1117
- || c != TRANSFER_ENCODING[index]) {
1118
- header_state = h_general;
1119
- } else if (index == sizeof(TRANSFER_ENCODING)-2) {
1120
- header_state = h_transfer_encoding;
1317
+ parser->index++;
1318
+ if (parser->index > sizeof(TRANSFER_ENCODING)-1
1319
+ || c != TRANSFER_ENCODING[parser->index]) {
1320
+ parser->header_state = h_general;
1321
+ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
1322
+ parser->header_state = h_transfer_encoding;
1121
1323
  }
1122
1324
  break;
1123
1325
 
1124
1326
  /* upgrade */
1125
1327
 
1126
1328
  case h_matching_upgrade:
1127
- index++;
1128
- if (index > sizeof(UPGRADE)-1
1129
- || c != UPGRADE[index]) {
1130
- header_state = h_general;
1131
- } else if (index == sizeof(UPGRADE)-2) {
1132
- header_state = h_upgrade;
1329
+ parser->index++;
1330
+ if (parser->index > sizeof(UPGRADE)-1
1331
+ || c != UPGRADE[parser->index]) {
1332
+ parser->header_state = h_general;
1333
+ } else if (parser->index == sizeof(UPGRADE)-2) {
1334
+ parser->header_state = h_upgrade;
1133
1335
  }
1134
1336
  break;
1135
1337
 
@@ -1137,7 +1339,7 @@ size_t http_parser_execute (http_parser *parser,
1137
1339
  case h_content_length:
1138
1340
  case h_transfer_encoding:
1139
1341
  case h_upgrade:
1140
- if (ch != ' ') header_state = h_general;
1342
+ if (ch != ' ') parser->header_state = h_general;
1141
1343
  break;
1142
1344
 
1143
1345
  default:
@@ -1148,89 +1350,89 @@ size_t http_parser_execute (http_parser *parser,
1148
1350
  }
1149
1351
 
1150
1352
  if (ch == ':') {
1151
- CALLBACK(header_field);
1152
- state = s_header_value_start;
1353
+ parser->state = s_header_value_start;
1354
+ CALLBACK_DATA(header_field);
1153
1355
  break;
1154
1356
  }
1155
1357
 
1156
1358
  if (ch == CR) {
1157
- state = s_header_almost_done;
1158
- CALLBACK(header_field);
1359
+ parser->state = s_header_almost_done;
1360
+ CALLBACK_DATA(header_field);
1159
1361
  break;
1160
1362
  }
1161
1363
 
1162
1364
  if (ch == LF) {
1163
- CALLBACK(header_field);
1164
- state = s_header_field_start;
1365
+ parser->state = s_header_field_start;
1366
+ CALLBACK_DATA(header_field);
1165
1367
  break;
1166
1368
  }
1167
1369
 
1370
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
1168
1371
  goto error;
1169
1372
  }
1170
1373
 
1171
1374
  case s_header_value_start:
1172
1375
  {
1173
- if (ch == ' ') break;
1376
+ if (ch == ' ' || ch == '\t') break;
1174
1377
 
1175
1378
  MARK(header_value);
1176
1379
 
1177
- state = s_header_value;
1178
- index = 0;
1179
-
1180
- c = acceptable_header[(unsigned char)ch];
1181
-
1182
- if (!c) {
1183
- if (ch == CR) {
1184
- CALLBACK(header_value);
1185
- header_state = h_general;
1186
- state = s_header_almost_done;
1187
- break;
1188
- }
1380
+ parser->state = s_header_value;
1381
+ parser->index = 0;
1189
1382
 
1190
- if (ch == LF) {
1191
- CALLBACK(header_value);
1192
- state = s_header_field_start;
1193
- break;
1194
- }
1383
+ if (ch == CR) {
1384
+ parser->header_state = h_general;
1385
+ parser->state = s_header_almost_done;
1386
+ CALLBACK_DATA(header_value);
1387
+ break;
1388
+ }
1195
1389
 
1196
- header_state = h_general;
1390
+ if (ch == LF) {
1391
+ parser->state = s_header_field_start;
1392
+ CALLBACK_DATA(header_value);
1197
1393
  break;
1198
1394
  }
1199
1395
 
1200
- switch (header_state) {
1396
+ c = LOWER(ch);
1397
+
1398
+ switch (parser->header_state) {
1201
1399
  case h_upgrade:
1202
1400
  parser->flags |= F_UPGRADE;
1203
- header_state = h_general;
1401
+ parser->header_state = h_general;
1204
1402
  break;
1205
1403
 
1206
1404
  case h_transfer_encoding:
1207
1405
  /* looking for 'Transfer-Encoding: chunked' */
1208
1406
  if ('c' == c) {
1209
- header_state = h_matching_transfer_encoding_chunked;
1407
+ parser->header_state = h_matching_transfer_encoding_chunked;
1210
1408
  } else {
1211
- header_state = h_general;
1409
+ parser->header_state = h_general;
1212
1410
  }
1213
1411
  break;
1214
1412
 
1215
1413
  case h_content_length:
1216
- if (ch < '0' || ch > '9') goto error;
1414
+ if (!IS_NUM(ch)) {
1415
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1416
+ goto error;
1417
+ }
1418
+
1217
1419
  parser->content_length = ch - '0';
1218
1420
  break;
1219
1421
 
1220
1422
  case h_connection:
1221
1423
  /* looking for 'Connection: keep-alive' */
1222
1424
  if (c == 'k') {
1223
- header_state = h_matching_connection_keep_alive;
1425
+ parser->header_state = h_matching_connection_keep_alive;
1224
1426
  /* looking for 'Connection: close' */
1225
1427
  } else if (c == 'c') {
1226
- header_state = h_matching_connection_close;
1428
+ parser->header_state = h_matching_connection_close;
1227
1429
  } else {
1228
- header_state = h_general;
1430
+ parser->header_state = h_general;
1229
1431
  }
1230
1432
  break;
1231
1433
 
1232
1434
  default:
1233
- header_state = h_general;
1435
+ parser->header_state = h_general;
1234
1436
  break;
1235
1437
  }
1236
1438
  break;
@@ -1238,23 +1440,22 @@ size_t http_parser_execute (http_parser *parser,
1238
1440
 
1239
1441
  case s_header_value:
1240
1442
  {
1241
- c = acceptable_header[(unsigned char)ch];
1242
-
1243
- if (!c) {
1244
- if (ch == CR) {
1245
- CALLBACK(header_value);
1246
- state = s_header_almost_done;
1247
- break;
1248
- }
1249
1443
 
1250
- if (ch == LF) {
1251
- CALLBACK(header_value);
1252
- goto header_almost_done;
1253
- }
1444
+ if (ch == CR) {
1445
+ parser->state = s_header_almost_done;
1446
+ CALLBACK_DATA(header_value);
1254
1447
  break;
1255
1448
  }
1256
1449
 
1257
- switch (header_state) {
1450
+ if (ch == LF) {
1451
+ parser->state = s_header_almost_done;
1452
+ CALLBACK_DATA_NOADVANCE(header_value);
1453
+ goto reexecute_byte;
1454
+ }
1455
+
1456
+ c = LOWER(ch);
1457
+
1458
+ switch (parser->header_state) {
1258
1459
  case h_general:
1259
1460
  break;
1260
1461
 
@@ -1264,66 +1465,83 @@ size_t http_parser_execute (http_parser *parser,
1264
1465
  break;
1265
1466
 
1266
1467
  case h_content_length:
1468
+ {
1469
+ uint64_t t;
1470
+
1267
1471
  if (ch == ' ') break;
1268
- if (ch < '0' || ch > '9') goto error;
1269
- parser->content_length *= 10;
1270
- parser->content_length += ch - '0';
1472
+
1473
+ if (!IS_NUM(ch)) {
1474
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1475
+ goto error;
1476
+ }
1477
+
1478
+ t = parser->content_length;
1479
+ t *= 10;
1480
+ t += ch - '0';
1481
+
1482
+ /* Overflow? */
1483
+ if (t < parser->content_length || t == ULLONG_MAX) {
1484
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1485
+ goto error;
1486
+ }
1487
+
1488
+ parser->content_length = t;
1271
1489
  break;
1490
+ }
1272
1491
 
1273
1492
  /* Transfer-Encoding: chunked */
1274
1493
  case h_matching_transfer_encoding_chunked:
1275
- index++;
1276
- if (index > sizeof(CHUNKED)-1
1277
- || c != CHUNKED[index]) {
1278
- header_state = h_general;
1279
- } else if (index == sizeof(CHUNKED)-2) {
1280
- header_state = h_transfer_encoding_chunked;
1494
+ parser->index++;
1495
+ if (parser->index > sizeof(CHUNKED)-1
1496
+ || c != CHUNKED[parser->index]) {
1497
+ parser->header_state = h_general;
1498
+ } else if (parser->index == sizeof(CHUNKED)-2) {
1499
+ parser->header_state = h_transfer_encoding_chunked;
1281
1500
  }
1282
1501
  break;
1283
1502
 
1284
1503
  /* looking for 'Connection: keep-alive' */
1285
1504
  case h_matching_connection_keep_alive:
1286
- index++;
1287
- if (index > sizeof(KEEP_ALIVE)-1
1288
- || c != KEEP_ALIVE[index]) {
1289
- header_state = h_general;
1290
- } else if (index == sizeof(KEEP_ALIVE)-2) {
1291
- header_state = h_connection_keep_alive;
1505
+ parser->index++;
1506
+ if (parser->index > sizeof(KEEP_ALIVE)-1
1507
+ || c != KEEP_ALIVE[parser->index]) {
1508
+ parser->header_state = h_general;
1509
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
1510
+ parser->header_state = h_connection_keep_alive;
1292
1511
  }
1293
1512
  break;
1294
1513
 
1295
1514
  /* looking for 'Connection: close' */
1296
1515
  case h_matching_connection_close:
1297
- index++;
1298
- if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
1299
- header_state = h_general;
1300
- } else if (index == sizeof(CLOSE)-2) {
1301
- header_state = h_connection_close;
1516
+ parser->index++;
1517
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
1518
+ parser->header_state = h_general;
1519
+ } else if (parser->index == sizeof(CLOSE)-2) {
1520
+ parser->header_state = h_connection_close;
1302
1521
  }
1303
1522
  break;
1304
1523
 
1305
1524
  case h_transfer_encoding_chunked:
1306
1525
  case h_connection_keep_alive:
1307
1526
  case h_connection_close:
1308
- if (ch != ' ') header_state = h_general;
1527
+ if (ch != ' ') parser->header_state = h_general;
1309
1528
  break;
1310
1529
 
1311
1530
  default:
1312
- state = s_header_value;
1313
- header_state = h_general;
1531
+ parser->state = s_header_value;
1532
+ parser->header_state = h_general;
1314
1533
  break;
1315
1534
  }
1316
1535
  break;
1317
1536
  }
1318
1537
 
1319
1538
  case s_header_almost_done:
1320
- header_almost_done:
1321
1539
  {
1322
1540
  STRICT_CHECK(ch != LF);
1323
1541
 
1324
- state = s_header_field_start;
1542
+ parser->state = s_header_value_lws;
1325
1543
 
1326
- switch (header_state) {
1544
+ switch (parser->header_state) {
1327
1545
  case h_connection_keep_alive:
1328
1546
  parser->flags |= F_CONNECTION_KEEP_ALIVE;
1329
1547
  break;
@@ -1336,32 +1554,47 @@ size_t http_parser_execute (http_parser *parser,
1336
1554
  default:
1337
1555
  break;
1338
1556
  }
1557
+
1558
+ break;
1559
+ }
1560
+
1561
+ case s_header_value_lws:
1562
+ {
1563
+ if (ch == ' ' || ch == '\t')
1564
+ parser->state = s_header_value_start;
1565
+ else
1566
+ {
1567
+ parser->state = s_header_field_start;
1568
+ goto reexecute_byte;
1569
+ }
1339
1570
  break;
1340
1571
  }
1341
1572
 
1342
1573
  case s_headers_almost_done:
1343
- headers_almost_done:
1344
1574
  {
1345
1575
  STRICT_CHECK(ch != LF);
1346
1576
 
1347
1577
  if (parser->flags & F_TRAILING) {
1348
1578
  /* End of a chunked request */
1349
- CALLBACK2(message_complete);
1350
- state = NEW_MESSAGE();
1579
+ parser->state = NEW_MESSAGE();
1580
+ CALLBACK_NOTIFY(message_complete);
1351
1581
  break;
1352
1582
  }
1353
1583
 
1354
- nread = 0;
1584
+ parser->state = s_headers_done;
1355
1585
 
1356
- if (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT) {
1357
- parser->upgrade = 1;
1358
- }
1586
+ /* Set this here so that on_headers_complete() callbacks can see it */
1587
+ parser->upgrade =
1588
+ (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
1359
1589
 
1360
1590
  /* Here we call the headers_complete callback. This is somewhat
1361
1591
  * different than other callbacks because if the user returns 1, we
1362
1592
  * will interpret that as saying that this message has no body. This
1363
1593
  * is needed for the annoying case of recieving a response to a HEAD
1364
1594
  * request.
1595
+ *
1596
+ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
1597
+ * we have to simulate it by handling a change in errno below.
1365
1598
  */
1366
1599
  if (settings->on_headers_complete) {
1367
1600
  switch (settings->on_headers_complete(parser)) {
@@ -1373,38 +1606,54 @@ size_t http_parser_execute (http_parser *parser,
1373
1606
  break;
1374
1607
 
1375
1608
  default:
1609
+ SET_ERRNO(HPE_CB_headers_complete);
1376
1610
  return p - data; /* Error */
1377
1611
  }
1378
1612
  }
1379
1613
 
1614
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
1615
+ return p - data;
1616
+ }
1617
+
1618
+ goto reexecute_byte;
1619
+ }
1620
+
1621
+ case s_headers_done:
1622
+ {
1623
+ STRICT_CHECK(ch != LF);
1624
+
1625
+ parser->nread = 0;
1626
+
1380
1627
  /* Exit, the rest of the connect is in a different protocol. */
1381
1628
  if (parser->upgrade) {
1382
- CALLBACK2(message_complete);
1383
- return (p - data);
1629
+ parser->state = NEW_MESSAGE();
1630
+ CALLBACK_NOTIFY(message_complete);
1631
+ return (p - data) + 1;
1384
1632
  }
1385
1633
 
1386
1634
  if (parser->flags & F_SKIPBODY) {
1387
- CALLBACK2(message_complete);
1388
- state = NEW_MESSAGE();
1635
+ parser->state = NEW_MESSAGE();
1636
+ CALLBACK_NOTIFY(message_complete);
1389
1637
  } else if (parser->flags & F_CHUNKED) {
1390
1638
  /* chunked encoding - ignore Content-Length header */
1391
- state = s_chunk_size_start;
1639
+ parser->state = s_chunk_size_start;
1392
1640
  } else {
1393
1641
  if (parser->content_length == 0) {
1394
1642
  /* Content-Length header given but zero: Content-Length: 0\r\n */
1395
- CALLBACK2(message_complete);
1396
- state = NEW_MESSAGE();
1397
- } else if (parser->content_length > 0) {
1643
+ parser->state = NEW_MESSAGE();
1644
+ CALLBACK_NOTIFY(message_complete);
1645
+ } else if (parser->content_length != ULLONG_MAX) {
1398
1646
  /* Content-Length header given and non-zero */
1399
- state = s_body_identity;
1647
+ parser->state = s_body_identity;
1400
1648
  } else {
1401
- if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {
1649
+ if (parser->type == HTTP_REQUEST ||
1650
+ !http_message_needs_eof(parser)) {
1402
1651
  /* Assume content-length 0 - read the next */
1403
- CALLBACK2(message_complete);
1404
- state = NEW_MESSAGE();
1652
+ parser->state = NEW_MESSAGE();
1653
+ CALLBACK_NOTIFY(message_complete);
1405
1654
  } else {
1406
1655
  /* Read body until EOF */
1407
- state = s_body_identity_eof;
1656
+ parser->state = s_body_identity_eof;
1408
1657
  }
1409
1658
  }
1410
1659
  }
@@ -1413,59 +1662,103 @@ size_t http_parser_execute (http_parser *parser,
1413
1662
  }
1414
1663
 
1415
1664
  case s_body_identity:
1416
- to_read = MIN(pe - p, (int64_t)parser->content_length);
1417
- if (to_read > 0) {
1418
- if (settings->on_body) settings->on_body(parser, p, to_read);
1419
- p += to_read - 1;
1420
- parser->content_length -= to_read;
1421
- if (parser->content_length == 0) {
1422
- CALLBACK2(message_complete);
1423
- state = NEW_MESSAGE();
1424
- }
1665
+ {
1666
+ uint64_t to_read = MIN(parser->content_length,
1667
+ (uint64_t) ((data + len) - p));
1668
+
1669
+ assert(parser->content_length != 0
1670
+ && parser->content_length != ULLONG_MAX);
1671
+
1672
+ /* The difference between advancing content_length and p is because
1673
+ * the latter will automaticaly advance on the next loop iteration.
1674
+ * Further, if content_length ends up at 0, we want to see the last
1675
+ * byte again for our message complete callback.
1676
+ */
1677
+ MARK(body);
1678
+ parser->content_length -= to_read;
1679
+ p += to_read - 1;
1680
+
1681
+ if (parser->content_length == 0) {
1682
+ parser->state = s_message_done;
1683
+
1684
+ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
1685
+ *
1686
+ * The alternative to doing this is to wait for the next byte to
1687
+ * trigger the data callback, just as in every other case. The
1688
+ * problem with this is that this makes it difficult for the test
1689
+ * harness to distinguish between complete-on-EOF and
1690
+ * complete-on-length. It's not clear that this distinction is
1691
+ * important for applications, but let's keep it for now.
1692
+ */
1693
+ CALLBACK_DATA_(body, p - body_mark + 1, p - data);
1694
+ goto reexecute_byte;
1425
1695
  }
1696
+
1426
1697
  break;
1698
+ }
1427
1699
 
1428
1700
  /* read until EOF */
1429
1701
  case s_body_identity_eof:
1430
- to_read = pe - p;
1431
- if (to_read > 0) {
1432
- if (settings->on_body) settings->on_body(parser, p, to_read);
1433
- p += to_read - 1;
1434
- }
1702
+ MARK(body);
1703
+ p = data + len - 1;
1704
+
1705
+ break;
1706
+
1707
+ case s_message_done:
1708
+ parser->state = NEW_MESSAGE();
1709
+ CALLBACK_NOTIFY(message_complete);
1435
1710
  break;
1436
1711
 
1437
1712
  case s_chunk_size_start:
1438
1713
  {
1714
+ assert(parser->nread == 1);
1439
1715
  assert(parser->flags & F_CHUNKED);
1440
1716
 
1441
- c = unhex[(unsigned char)ch];
1442
- if (c == -1) goto error;
1443
- parser->content_length = c;
1444
- state = s_chunk_size;
1717
+ unhex_val = unhex[(unsigned char)ch];
1718
+ if (unhex_val == -1) {
1719
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
1720
+ goto error;
1721
+ }
1722
+
1723
+ parser->content_length = unhex_val;
1724
+ parser->state = s_chunk_size;
1445
1725
  break;
1446
1726
  }
1447
1727
 
1448
1728
  case s_chunk_size:
1449
1729
  {
1730
+ uint64_t t;
1731
+
1450
1732
  assert(parser->flags & F_CHUNKED);
1451
1733
 
1452
1734
  if (ch == CR) {
1453
- state = s_chunk_size_almost_done;
1735
+ parser->state = s_chunk_size_almost_done;
1454
1736
  break;
1455
1737
  }
1456
1738
 
1457
- c = unhex[(unsigned char)ch];
1739
+ unhex_val = unhex[(unsigned char)ch];
1458
1740
 
1459
- if (c == -1) {
1741
+ if (unhex_val == -1) {
1460
1742
  if (ch == ';' || ch == ' ') {
1461
- state = s_chunk_parameters;
1743
+ parser->state = s_chunk_parameters;
1462
1744
  break;
1463
1745
  }
1746
+
1747
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
1748
+ goto error;
1749
+ }
1750
+
1751
+ t = parser->content_length;
1752
+ t *= 16;
1753
+ t += unhex_val;
1754
+
1755
+ /* Overflow? */
1756
+ if (t < parser->content_length || t == ULLONG_MAX) {
1757
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1464
1758
  goto error;
1465
1759
  }
1466
1760
 
1467
- parser->content_length *= 16;
1468
- parser->content_length += c;
1761
+ parser->content_length = t;
1469
1762
  break;
1470
1763
  }
1471
1764
 
@@ -1474,7 +1767,7 @@ size_t http_parser_execute (http_parser *parser,
1474
1767
  assert(parser->flags & F_CHUNKED);
1475
1768
  /* just ignore this shit. TODO check for overflow */
1476
1769
  if (ch == CR) {
1477
- state = s_chunk_size_almost_done;
1770
+ parser->state = s_chunk_size_almost_done;
1478
1771
  break;
1479
1772
  }
1480
1773
  break;
@@ -1485,72 +1778,117 @@ size_t http_parser_execute (http_parser *parser,
1485
1778
  assert(parser->flags & F_CHUNKED);
1486
1779
  STRICT_CHECK(ch != LF);
1487
1780
 
1781
+ parser->nread = 0;
1782
+
1488
1783
  if (parser->content_length == 0) {
1489
1784
  parser->flags |= F_TRAILING;
1490
- state = s_header_field_start;
1785
+ parser->state = s_header_field_start;
1491
1786
  } else {
1492
- state = s_chunk_data;
1787
+ parser->state = s_chunk_data;
1493
1788
  }
1494
1789
  break;
1495
1790
  }
1496
1791
 
1497
1792
  case s_chunk_data:
1498
1793
  {
1499
- assert(parser->flags & F_CHUNKED);
1794
+ uint64_t to_read = MIN(parser->content_length,
1795
+ (uint64_t) ((data + len) - p));
1500
1796
 
1501
- to_read = MIN(pe - p, (int64_t)(parser->content_length));
1797
+ assert(parser->flags & F_CHUNKED);
1798
+ assert(parser->content_length != 0
1799
+ && parser->content_length != ULLONG_MAX);
1502
1800
 
1503
- if (to_read > 0) {
1504
- if (settings->on_body) settings->on_body(parser, p, to_read);
1505
- p += to_read - 1;
1506
- }
1801
+ /* See the explanation in s_body_identity for why the content
1802
+ * length and data pointers are managed this way.
1803
+ */
1804
+ MARK(body);
1805
+ parser->content_length -= to_read;
1806
+ p += to_read - 1;
1507
1807
 
1508
- if (to_read == parser->content_length) {
1509
- state = s_chunk_data_almost_done;
1808
+ if (parser->content_length == 0) {
1809
+ parser->state = s_chunk_data_almost_done;
1510
1810
  }
1511
1811
 
1512
- parser->content_length -= to_read;
1513
1812
  break;
1514
1813
  }
1515
1814
 
1516
1815
  case s_chunk_data_almost_done:
1517
1816
  assert(parser->flags & F_CHUNKED);
1817
+ assert(parser->content_length == 0);
1518
1818
  STRICT_CHECK(ch != CR);
1519
- state = s_chunk_data_done;
1819
+ parser->state = s_chunk_data_done;
1820
+ CALLBACK_DATA(body);
1520
1821
  break;
1521
1822
 
1522
1823
  case s_chunk_data_done:
1523
1824
  assert(parser->flags & F_CHUNKED);
1524
1825
  STRICT_CHECK(ch != LF);
1525
- state = s_chunk_size_start;
1826
+ parser->nread = 0;
1827
+ parser->state = s_chunk_size_start;
1526
1828
  break;
1527
1829
 
1528
1830
  default:
1529
1831
  assert(0 && "unhandled state");
1832
+ SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
1530
1833
  goto error;
1531
1834
  }
1532
1835
  }
1533
1836
 
1534
- CALLBACK_NOCLEAR(header_field);
1535
- CALLBACK_NOCLEAR(header_value);
1536
- CALLBACK_NOCLEAR(fragment);
1537
- CALLBACK_NOCLEAR(query_string);
1538
- CALLBACK_NOCLEAR(path);
1539
- CALLBACK_NOCLEAR(url);
1837
+ /* Run callbacks for any marks that we have leftover after we ran our of
1838
+ * bytes. There should be at most one of these set, so it's OK to invoke
1839
+ * them in series (unset marks will not result in callbacks).
1840
+ *
1841
+ * We use the NOADVANCE() variety of callbacks here because 'p' has already
1842
+ * overflowed 'data' and this allows us to correct for the off-by-one that
1843
+ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
1844
+ * value that's in-bounds).
1845
+ */
1846
+
1847
+ assert(((header_field_mark ? 1 : 0) +
1848
+ (header_value_mark ? 1 : 0) +
1849
+ (url_mark ? 1 : 0) +
1850
+ (body_mark ? 1 : 0)) <= 1);
1540
1851
 
1541
- parser->state = state;
1542
- parser->header_state = header_state;
1543
- parser->index = index;
1544
- parser->nread = nread;
1852
+ CALLBACK_DATA_NOADVANCE(header_field);
1853
+ CALLBACK_DATA_NOADVANCE(header_value);
1854
+ CALLBACK_DATA_NOADVANCE(url);
1855
+ CALLBACK_DATA_NOADVANCE(body);
1545
1856
 
1546
1857
  return len;
1547
1858
 
1548
1859
  error:
1549
- parser->state = s_dead;
1860
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
1861
+ SET_ERRNO(HPE_UNKNOWN);
1862
+ }
1863
+
1550
1864
  return (p - data);
1551
1865
  }
1552
1866
 
1553
1867
 
1868
+ /* Does the parser need to see an EOF to find the end of the message? */
1869
+ int
1870
+ http_message_needs_eof (http_parser *parser)
1871
+ {
1872
+ if (parser->type == HTTP_REQUEST) {
1873
+ return 0;
1874
+ }
1875
+
1876
+ /* See RFC 2616 section 4.4 */
1877
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
1878
+ parser->status_code == 204 || /* No Content */
1879
+ parser->status_code == 304 || /* Not Modified */
1880
+ parser->flags & F_SKIPBODY) { /* response to a HEAD request */
1881
+ return 0;
1882
+ }
1883
+
1884
+ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
1885
+ return 0;
1886
+ }
1887
+
1888
+ return 1;
1889
+ }
1890
+
1891
+
1554
1892
  int
1555
1893
  http_should_keep_alive (http_parser *parser)
1556
1894
  {
@@ -1558,17 +1896,15 @@ http_should_keep_alive (http_parser *parser)
1558
1896
  /* HTTP/1.1 */
1559
1897
  if (parser->flags & F_CONNECTION_CLOSE) {
1560
1898
  return 0;
1561
- } else {
1562
- return 1;
1563
1899
  }
1564
1900
  } else {
1565
1901
  /* HTTP/1.0 or earlier */
1566
- if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
1567
- return 1;
1568
- } else {
1902
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
1569
1903
  return 0;
1570
1904
  }
1571
1905
  }
1906
+
1907
+ return !http_message_needs_eof(parser);
1572
1908
  }
1573
1909
 
1574
1910
 
@@ -1581,10 +1917,142 @@ const char * http_method_str (enum http_method m)
1581
1917
  void
1582
1918
  http_parser_init (http_parser *parser, enum http_parser_type t)
1583
1919
  {
1920
+ void *data = parser->data; /* preserve application data */
1921
+ memset(parser, 0, sizeof(*parser));
1922
+ parser->data = data;
1584
1923
  parser->type = t;
1585
1924
  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
1586
- parser->nread = 0;
1587
- parser->upgrade = 0;
1588
- parser->flags = 0;
1589
- parser->method = 0;
1925
+ parser->http_errno = HPE_OK;
1926
+ }
1927
+
1928
+ const char *
1929
+ http_errno_name(enum http_errno err) {
1930
+ assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
1931
+ return http_strerror_tab[err].name;
1932
+ }
1933
+
1934
+ const char *
1935
+ http_errno_description(enum http_errno err) {
1936
+ assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
1937
+ return http_strerror_tab[err].description;
1938
+ }
1939
+
1940
+ int
1941
+ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
1942
+ struct http_parser_url *u)
1943
+ {
1944
+ enum state s;
1945
+ const char *p;
1946
+ enum http_parser_url_fields uf, old_uf;
1947
+
1948
+ u->port = u->field_set = 0;
1949
+ s = is_connect ? s_req_host_start : s_req_spaces_before_url;
1950
+ uf = old_uf = UF_MAX;
1951
+
1952
+ for (p = buf; p < buf + buflen; p++) {
1953
+ s = parse_url_char(s, *p);
1954
+
1955
+ /* Figure out the next field that we're operating on */
1956
+ switch (s) {
1957
+ case s_dead:
1958
+ return 1;
1959
+
1960
+ /* Skip delimeters */
1961
+ case s_req_schema_slash:
1962
+ case s_req_schema_slash_slash:
1963
+ case s_req_host_start:
1964
+ case s_req_host_v6_start:
1965
+ case s_req_host_v6_end:
1966
+ case s_req_port_start:
1967
+ case s_req_query_string_start:
1968
+ case s_req_fragment_start:
1969
+ continue;
1970
+
1971
+ case s_req_schema:
1972
+ uf = UF_SCHEMA;
1973
+ break;
1974
+
1975
+ case s_req_host:
1976
+ case s_req_host_v6:
1977
+ uf = UF_HOST;
1978
+ break;
1979
+
1980
+ case s_req_port:
1981
+ uf = UF_PORT;
1982
+ break;
1983
+
1984
+ case s_req_path:
1985
+ uf = UF_PATH;
1986
+ break;
1987
+
1988
+ case s_req_query_string:
1989
+ uf = UF_QUERY;
1990
+ break;
1991
+
1992
+ case s_req_fragment:
1993
+ uf = UF_FRAGMENT;
1994
+ break;
1995
+
1996
+ default:
1997
+ assert(!"Unexpected state");
1998
+ return 1;
1999
+ }
2000
+
2001
+ /* Nothing's changed; soldier on */
2002
+ if (uf == old_uf) {
2003
+ u->field_data[uf].len++;
2004
+ continue;
2005
+ }
2006
+
2007
+ u->field_data[uf].off = p - buf;
2008
+ u->field_data[uf].len = 1;
2009
+
2010
+ u->field_set |= (1 << uf);
2011
+ old_uf = uf;
2012
+ }
2013
+
2014
+ /* CONNECT requests can only contain "hostname:port" */
2015
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
2016
+ return 1;
2017
+ }
2018
+
2019
+ /* Make sure we don't end somewhere unexpected */
2020
+ switch (s) {
2021
+ case s_req_host_v6_start:
2022
+ case s_req_host_v6:
2023
+ case s_req_host_v6_end:
2024
+ case s_req_host:
2025
+ case s_req_port_start:
2026
+ return 1;
2027
+ default:
2028
+ break;
2029
+ }
2030
+
2031
+ if (u->field_set & (1 << UF_PORT)) {
2032
+ /* Don't bother with endp; we've already validated the string */
2033
+ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
2034
+
2035
+ /* Ports have a max value of 2^16 */
2036
+ if (v > 0xffff) {
2037
+ return 1;
2038
+ }
2039
+
2040
+ u->port = (uint16_t) v;
2041
+ }
2042
+
2043
+ return 0;
2044
+ }
2045
+
2046
+ void
2047
+ http_parser_pause(http_parser *parser, int paused) {
2048
+ /* Users should only be pausing/unpausing a parser that is not in an error
2049
+ * state. In non-debug builds, there's not much that we can do about this
2050
+ * other than ignore it.
2051
+ */
2052
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
2053
+ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
2054
+ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
2055
+ } else {
2056
+ assert(0 && "Attempting to pause parser in error state");
2057
+ }
1590
2058
  }