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.
- data/.gitmodules +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +11 -4
- data/LICENSE-MIT +20 -0
- data/README.md +51 -46
- data/bench/standalone.rb +23 -0
- data/bench/thin.rb +1 -0
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +121 -56
- data/ext/ruby_http_parser/ruby_http_parser.c +89 -48
- data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
- data/ext/ruby_http_parser/vendor/http-parser/Makefile +34 -17
- data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1045 -589
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +148 -17
- data/ext/ruby_http_parser/vendor/http-parser/test.c +1138 -128
- data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +30 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/Makefile +34 -17
- data/ext/ruby_http_parser/vendor/http-parser-java/README.md +30 -18
- data/ext/ruby_http_parser/vendor/http-parser-java/TODO +9 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/ext/primitives.jar +0 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1075 -607
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +190 -40
- data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +67 -52
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +34 -35
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +839 -587
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +374 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +52 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +16 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +48 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +171 -446
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +62 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +3 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +27 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +236 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +59 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1318 -184
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +355 -49
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
- data/http_parser.rb.gemspec +10 -2
- data/lib/http_parser.rb +17 -0
- data/spec/parser_spec.rb +157 -23
- data/spec/support/requests.json +236 -24
- data/spec/support/responses.json +182 -36
- data/tasks/compile.rake +5 -3
- data/tasks/fixtures.rake +8 -2
- metadata +209 -127
- data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
- data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
/*
|
|
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,48 +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
|
|
24
|
+
#include "http_parser.h"
|
|
22
25
|
#include <assert.h>
|
|
23
26
|
#include <stddef.h>
|
|
27
|
+
#include <ctype.h>
|
|
28
|
+
#include <stdlib.h>
|
|
29
|
+
#include <string.h>
|
|
30
|
+
#include <limits.h>
|
|
24
31
|
|
|
32
|
+
#ifndef ULLONG_MAX
|
|
33
|
+
# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
|
|
34
|
+
#endif
|
|
25
35
|
|
|
26
36
|
#ifndef MIN
|
|
27
37
|
# define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
28
38
|
#endif
|
|
29
39
|
|
|
30
40
|
|
|
31
|
-
#
|
|
41
|
+
#if HTTP_PARSER_DEBUG
|
|
42
|
+
#define SET_ERRNO(e) \
|
|
32
43
|
do { \
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} \
|
|
44
|
+
parser->http_errno = (e); \
|
|
45
|
+
parser->error_lineno = __LINE__; \
|
|
36
46
|
} while (0)
|
|
47
|
+
#else
|
|
48
|
+
#define SET_ERRNO(e) \
|
|
49
|
+
do { \
|
|
50
|
+
parser->http_errno = (e); \
|
|
51
|
+
} while(0)
|
|
52
|
+
#endif
|
|
37
53
|
|
|
38
54
|
|
|
39
|
-
|
|
55
|
+
/* Run the notify callback FOR, returning ER if it fails */
|
|
56
|
+
#define CALLBACK_NOTIFY_(FOR, ER) \
|
|
40
57
|
do { \
|
|
41
|
-
|
|
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
|
+
} \
|
|
42
70
|
} while (0)
|
|
43
71
|
|
|
44
|
-
|
|
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) \
|
|
45
80
|
do { \
|
|
81
|
+
assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
|
|
82
|
+
\
|
|
46
83
|
if (FOR##_mark) { \
|
|
47
84
|
if (settings->on_##FOR) { \
|
|
48
|
-
if (0 != settings->on_##FOR(parser,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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); \
|
|
53
92
|
} \
|
|
54
93
|
} \
|
|
94
|
+
FOR##_mark = NULL; \
|
|
55
95
|
} \
|
|
56
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)
|
|
57
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)
|
|
58
105
|
|
|
59
|
-
|
|
106
|
+
/* Set the mark FOR; non-destructive if mark is already set */
|
|
107
|
+
#define MARK(FOR) \
|
|
60
108
|
do { \
|
|
61
|
-
|
|
62
|
-
|
|
109
|
+
if (!FOR##_mark) { \
|
|
110
|
+
FOR##_mark = p; \
|
|
111
|
+
} \
|
|
63
112
|
} while (0)
|
|
64
113
|
|
|
65
114
|
|
|
@@ -97,6 +146,8 @@ static const char *method_strings[] =
|
|
|
97
146
|
, "NOTIFY"
|
|
98
147
|
, "SUBSCRIBE"
|
|
99
148
|
, "UNSUBSCRIBE"
|
|
149
|
+
, "PATCH"
|
|
150
|
+
, "PURGE"
|
|
100
151
|
};
|
|
101
152
|
|
|
102
153
|
|
|
@@ -117,9 +168,9 @@ static const char tokens[256] = {
|
|
|
117
168
|
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
|
118
169
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
119
170
|
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
|
120
|
-
|
|
171
|
+
0, '!', 0, '#', '$', '%', '&', '\'',
|
|
121
172
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
|
122
|
-
0, 0, '*', '+', 0, '-', '.',
|
|
173
|
+
0, 0, '*', '+', 0, '-', '.', 0,
|
|
123
174
|
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
|
124
175
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
125
176
|
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
|
|
@@ -139,7 +190,7 @@ static const char tokens[256] = {
|
|
|
139
190
|
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
|
140
191
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
141
192
|
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
|
142
|
-
'x', 'y', 'z', 0, '|',
|
|
193
|
+
'x', 'y', 'z', 0, '|', 0, '~', 0 };
|
|
143
194
|
|
|
144
195
|
|
|
145
196
|
static const int8_t unhex[256] =
|
|
@@ -186,7 +237,7 @@ static const uint8_t normal_url_char[256] = {
|
|
|
186
237
|
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
|
187
238
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
188
239
|
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
|
189
|
-
1, 1, 1, 1, 1, 1, 1, 0 };
|
|
240
|
+
1, 1, 1, 1, 1, 1, 1, 0, };
|
|
190
241
|
|
|
191
242
|
|
|
192
243
|
enum state
|
|
@@ -215,7 +266,12 @@ enum state
|
|
|
215
266
|
, s_req_schema
|
|
216
267
|
, s_req_schema_slash
|
|
217
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
|
|
218
273
|
, s_req_host
|
|
274
|
+
, s_req_port_start
|
|
219
275
|
, s_req_port
|
|
220
276
|
, s_req_path
|
|
221
277
|
, s_req_query_string_start
|
|
@@ -237,28 +293,35 @@ enum state
|
|
|
237
293
|
, s_header_field
|
|
238
294
|
, s_header_value_start
|
|
239
295
|
, s_header_value
|
|
296
|
+
, s_header_value_lws
|
|
240
297
|
|
|
241
298
|
, s_header_almost_done
|
|
242
299
|
|
|
300
|
+
, s_chunk_size_start
|
|
301
|
+
, s_chunk_size
|
|
302
|
+
, s_chunk_parameters
|
|
303
|
+
, s_chunk_size_almost_done
|
|
304
|
+
|
|
243
305
|
, s_headers_almost_done
|
|
244
|
-
|
|
306
|
+
, s_headers_done
|
|
307
|
+
|
|
308
|
+
/* Important: 's_headers_done' must be the last 'header' state. All
|
|
245
309
|
* states beyond this must be 'body' states. It is used for overflow
|
|
246
310
|
* checking. See the PARSING_HEADER() macro.
|
|
247
311
|
*/
|
|
248
|
-
|
|
249
|
-
, s_chunk_size
|
|
250
|
-
, s_chunk_size_almost_done
|
|
251
|
-
, s_chunk_parameters
|
|
312
|
+
|
|
252
313
|
, s_chunk_data
|
|
253
314
|
, s_chunk_data_almost_done
|
|
254
315
|
, s_chunk_data_done
|
|
255
316
|
|
|
256
317
|
, s_body_identity
|
|
257
318
|
, s_body_identity_eof
|
|
319
|
+
|
|
320
|
+
, s_message_done
|
|
258
321
|
};
|
|
259
322
|
|
|
260
323
|
|
|
261
|
-
#define PARSING_HEADER(state) (state <=
|
|
324
|
+
#define PARSING_HEADER(state) (state <= s_headers_done)
|
|
262
325
|
|
|
263
326
|
|
|
264
327
|
enum header_states
|
|
@@ -288,27 +351,39 @@ enum header_states
|
|
|
288
351
|
};
|
|
289
352
|
|
|
290
353
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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'))
|
|
300
362
|
|
|
301
|
-
#
|
|
302
|
-
#define
|
|
303
|
-
#define
|
|
304
|
-
#define
|
|
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)
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
335
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
|
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)
|
|
661
|
+
if (parser->nread > HTTP_MAX_HEADER_SIZE) {
|
|
662
|
+
SET_ERRNO(HPE_HEADER_OVERFLOW);
|
|
663
|
+
goto error;
|
|
664
|
+
}
|
|
374
665
|
}
|
|
375
666
|
|
|
376
|
-
|
|
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 =
|
|
685
|
+
parser->content_length = ULLONG_MAX;
|
|
390
686
|
|
|
391
|
-
|
|
687
|
+
if (ch == 'H') {
|
|
688
|
+
parser->state = s_res_or_resp_H;
|
|
392
689
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
else {
|
|
690
|
+
CALLBACK_NOTIFY(message_begin);
|
|
691
|
+
} else {
|
|
396
692
|
parser->type = HTTP_REQUEST;
|
|
397
|
-
|
|
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')
|
|
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 =
|
|
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 < '
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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)
|
|
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,15 +896,15 @@ 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 =
|
|
899
|
+
parser->content_length = ULLONG_MAX;
|
|
568
900
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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;
|
|
@@ -582,336 +914,157 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
582
914
|
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
|
|
583
915
|
case 'N': parser->method = HTTP_NOTIFY; break;
|
|
584
916
|
case 'O': parser->method = HTTP_OPTIONS; break;
|
|
585
|
-
case 'P': parser->method = HTTP_POST;
|
|
917
|
+
case 'P': parser->method = HTTP_POST;
|
|
918
|
+
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
|
919
|
+
break;
|
|
586
920
|
case 'R': parser->method = HTTP_REPORT; break;
|
|
587
921
|
case 'S': parser->method = HTTP_SUBSCRIBE; break;
|
|
588
922
|
case 'T': parser->method = HTTP_TRACE; break;
|
|
589
923
|
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
|
|
590
|
-
default:
|
|
924
|
+
default:
|
|
925
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
|
926
|
+
goto error;
|
|
591
927
|
}
|
|
592
|
-
state = s_req_method;
|
|
928
|
+
parser->state = s_req_method;
|
|
929
|
+
|
|
930
|
+
CALLBACK_NOTIFY(message_begin);
|
|
931
|
+
|
|
593
932
|
break;
|
|
594
933
|
}
|
|
595
934
|
|
|
596
935
|
case s_req_method:
|
|
597
936
|
{
|
|
598
|
-
|
|
937
|
+
const char *matcher;
|
|
938
|
+
if (ch == '\0') {
|
|
939
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
|
599
940
|
goto error;
|
|
941
|
+
}
|
|
600
942
|
|
|
601
|
-
|
|
602
|
-
if (ch == ' ' && matcher[index] == '\0') {
|
|
603
|
-
state = s_req_spaces_before_url;
|
|
604
|
-
} 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]) {
|
|
605
947
|
; /* nada */
|
|
606
948
|
} else if (parser->method == HTTP_CONNECT) {
|
|
607
|
-
if (index == 1 && ch == 'H') {
|
|
949
|
+
if (parser->index == 1 && ch == 'H') {
|
|
608
950
|
parser->method = HTTP_CHECKOUT;
|
|
609
|
-
} else if (index == 2 && ch == 'P') {
|
|
951
|
+
} else if (parser->index == 2 && ch == 'P') {
|
|
610
952
|
parser->method = HTTP_COPY;
|
|
953
|
+
} else {
|
|
954
|
+
goto error;
|
|
611
955
|
}
|
|
612
956
|
} else if (parser->method == HTTP_MKCOL) {
|
|
613
|
-
if (index == 1 && ch == 'O') {
|
|
957
|
+
if (parser->index == 1 && ch == 'O') {
|
|
614
958
|
parser->method = HTTP_MOVE;
|
|
615
|
-
} else if (index == 1 && ch == 'E') {
|
|
959
|
+
} else if (parser->index == 1 && ch == 'E') {
|
|
616
960
|
parser->method = HTTP_MERGE;
|
|
617
|
-
} else if (index == 1 && ch == '-') {
|
|
961
|
+
} else if (parser->index == 1 && ch == '-') {
|
|
618
962
|
parser->method = HTTP_MSEARCH;
|
|
619
|
-
} else if (index == 2 && ch == 'A') {
|
|
963
|
+
} else if (parser->index == 2 && ch == 'A') {
|
|
620
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;
|
|
621
977
|
}
|
|
622
|
-
} else if (
|
|
623
|
-
parser->method
|
|
624
|
-
|
|
625
|
-
parser->method
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
} 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') {
|
|
629
985
|
parser->method = HTTP_PROPPATCH;
|
|
630
986
|
} else {
|
|
987
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
|
631
988
|
goto error;
|
|
632
989
|
}
|
|
633
990
|
|
|
634
|
-
++index;
|
|
991
|
+
++parser->index;
|
|
635
992
|
break;
|
|
636
993
|
}
|
|
994
|
+
|
|
637
995
|
case s_req_spaces_before_url:
|
|
638
996
|
{
|
|
639
997
|
if (ch == ' ') break;
|
|
640
998
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
state = s_req_path;
|
|
645
|
-
break;
|
|
999
|
+
MARK(url);
|
|
1000
|
+
if (parser->method == HTTP_CONNECT) {
|
|
1001
|
+
parser->state = s_req_host_start;
|
|
646
1002
|
}
|
|
647
1003
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
state = s_req_schema;
|
|
653
|
-
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;
|
|
654
1008
|
}
|
|
655
1009
|
|
|
656
|
-
|
|
1010
|
+
break;
|
|
657
1011
|
}
|
|
658
1012
|
|
|
659
1013
|
case s_req_schema:
|
|
660
|
-
{
|
|
661
|
-
c = LOWER(ch);
|
|
662
|
-
|
|
663
|
-
if (c >= 'a' && c <= 'z') break;
|
|
664
|
-
|
|
665
|
-
if (ch == ':') {
|
|
666
|
-
state = s_req_schema_slash;
|
|
667
|
-
break;
|
|
668
|
-
} else if (ch == '.') {
|
|
669
|
-
state = s_req_host;
|
|
670
|
-
break;
|
|
671
|
-
} else if ('0' <= ch && ch <= '9') {
|
|
672
|
-
state = s_req_host;
|
|
673
|
-
break;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
goto error;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
1014
|
case s_req_schema_slash:
|
|
680
|
-
STRICT_CHECK(ch != '/');
|
|
681
|
-
state = s_req_schema_slash_slash;
|
|
682
|
-
break;
|
|
683
|
-
|
|
684
1015
|
case s_req_schema_slash_slash:
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
case s_req_host:
|
|
690
|
-
{
|
|
691
|
-
c = LOWER(ch);
|
|
692
|
-
if (c >= 'a' && c <= 'z') break;
|
|
693
|
-
if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
|
|
694
|
-
switch (ch) {
|
|
695
|
-
case ':':
|
|
696
|
-
state = s_req_port;
|
|
697
|
-
break;
|
|
698
|
-
case '/':
|
|
699
|
-
MARK(path);
|
|
700
|
-
state = s_req_path;
|
|
701
|
-
break;
|
|
702
|
-
case ' ':
|
|
703
|
-
/* The request line looks like:
|
|
704
|
-
* "GET http://foo.bar.com HTTP/1.1"
|
|
705
|
-
* That is, there is no path.
|
|
706
|
-
*/
|
|
707
|
-
CALLBACK(url);
|
|
708
|
-
state = s_req_http_start;
|
|
709
|
-
break;
|
|
710
|
-
default:
|
|
711
|
-
goto error;
|
|
712
|
-
}
|
|
713
|
-
break;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
case s_req_port:
|
|
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:
|
|
717
1020
|
{
|
|
718
|
-
if (ch >= '0' && ch <= '9') break;
|
|
719
1021
|
switch (ch) {
|
|
720
|
-
|
|
721
|
-
MARK(path);
|
|
722
|
-
state = s_req_path;
|
|
723
|
-
break;
|
|
1022
|
+
/* No whitespace allowed here */
|
|
724
1023
|
case ' ':
|
|
725
|
-
/* The request line looks like:
|
|
726
|
-
* "GET http://foo.bar.com:1234 HTTP/1.1"
|
|
727
|
-
* That is, there is no path.
|
|
728
|
-
*/
|
|
729
|
-
CALLBACK(url);
|
|
730
|
-
state = s_req_http_start;
|
|
731
|
-
break;
|
|
732
|
-
default:
|
|
733
|
-
goto error;
|
|
734
|
-
}
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
case s_req_path:
|
|
739
|
-
{
|
|
740
|
-
if (normal_url_char[(unsigned char)ch]) break;
|
|
741
|
-
|
|
742
|
-
switch (ch) {
|
|
743
|
-
case ' ':
|
|
744
|
-
CALLBACK(url);
|
|
745
|
-
CALLBACK(path);
|
|
746
|
-
state = s_req_http_start;
|
|
747
|
-
break;
|
|
748
1024
|
case CR:
|
|
749
|
-
CALLBACK(url);
|
|
750
|
-
CALLBACK(path);
|
|
751
|
-
parser->http_major = 0;
|
|
752
|
-
parser->http_minor = 9;
|
|
753
|
-
state = s_req_line_almost_done;
|
|
754
|
-
break;
|
|
755
1025
|
case LF:
|
|
756
|
-
|
|
757
|
-
CALLBACK(path);
|
|
758
|
-
parser->http_major = 0;
|
|
759
|
-
parser->http_minor = 9;
|
|
760
|
-
state = s_header_field_start;
|
|
761
|
-
break;
|
|
762
|
-
case '?':
|
|
763
|
-
CALLBACK(path);
|
|
764
|
-
state = s_req_query_string_start;
|
|
765
|
-
break;
|
|
766
|
-
case '#':
|
|
767
|
-
CALLBACK(path);
|
|
768
|
-
state = s_req_fragment_start;
|
|
769
|
-
break;
|
|
770
|
-
default:
|
|
1026
|
+
SET_ERRNO(HPE_INVALID_URL);
|
|
771
1027
|
goto error;
|
|
772
|
-
}
|
|
773
|
-
break;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
case s_req_query_string_start:
|
|
777
|
-
{
|
|
778
|
-
if (normal_url_char[(unsigned char)ch]) {
|
|
779
|
-
MARK(query_string);
|
|
780
|
-
state = s_req_query_string;
|
|
781
|
-
break;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
switch (ch) {
|
|
785
|
-
case '?':
|
|
786
|
-
break; /* XXX ignore extra '?' ... is this right? */
|
|
787
|
-
case ' ':
|
|
788
|
-
CALLBACK(url);
|
|
789
|
-
state = s_req_http_start;
|
|
790
|
-
break;
|
|
791
|
-
case CR:
|
|
792
|
-
CALLBACK(url);
|
|
793
|
-
parser->http_major = 0;
|
|
794
|
-
parser->http_minor = 9;
|
|
795
|
-
state = s_req_line_almost_done;
|
|
796
|
-
break;
|
|
797
|
-
case LF:
|
|
798
|
-
CALLBACK(url);
|
|
799
|
-
parser->http_major = 0;
|
|
800
|
-
parser->http_minor = 9;
|
|
801
|
-
state = s_header_field_start;
|
|
802
|
-
break;
|
|
803
|
-
case '#':
|
|
804
|
-
state = s_req_fragment_start;
|
|
805
|
-
break;
|
|
806
1028
|
default:
|
|
807
|
-
|
|
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
|
+
}
|
|
808
1034
|
}
|
|
809
|
-
break;
|
|
810
|
-
}
|
|
811
1035
|
|
|
812
|
-
case s_req_query_string:
|
|
813
|
-
{
|
|
814
|
-
if (normal_url_char[(unsigned char)ch]) break;
|
|
815
|
-
|
|
816
|
-
switch (ch) {
|
|
817
|
-
case '?':
|
|
818
|
-
/* allow extra '?' in query string */
|
|
819
|
-
break;
|
|
820
|
-
case ' ':
|
|
821
|
-
CALLBACK(url);
|
|
822
|
-
CALLBACK(query_string);
|
|
823
|
-
state = s_req_http_start;
|
|
824
|
-
break;
|
|
825
|
-
case CR:
|
|
826
|
-
CALLBACK(url);
|
|
827
|
-
CALLBACK(query_string);
|
|
828
|
-
parser->http_major = 0;
|
|
829
|
-
parser->http_minor = 9;
|
|
830
|
-
state = s_req_line_almost_done;
|
|
831
|
-
break;
|
|
832
|
-
case LF:
|
|
833
|
-
CALLBACK(url);
|
|
834
|
-
CALLBACK(query_string);
|
|
835
|
-
parser->http_major = 0;
|
|
836
|
-
parser->http_minor = 9;
|
|
837
|
-
state = s_header_field_start;
|
|
838
|
-
break;
|
|
839
|
-
case '#':
|
|
840
|
-
CALLBACK(query_string);
|
|
841
|
-
state = s_req_fragment_start;
|
|
842
|
-
break;
|
|
843
|
-
default:
|
|
844
|
-
goto error;
|
|
845
|
-
}
|
|
846
1036
|
break;
|
|
847
1037
|
}
|
|
848
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:
|
|
849
1045
|
case s_req_fragment_start:
|
|
850
|
-
{
|
|
851
|
-
if (normal_url_char[(unsigned char)ch]) {
|
|
852
|
-
MARK(fragment);
|
|
853
|
-
state = s_req_fragment;
|
|
854
|
-
break;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
switch (ch) {
|
|
858
|
-
case ' ':
|
|
859
|
-
CALLBACK(url);
|
|
860
|
-
state = s_req_http_start;
|
|
861
|
-
break;
|
|
862
|
-
case CR:
|
|
863
|
-
CALLBACK(url);
|
|
864
|
-
parser->http_major = 0;
|
|
865
|
-
parser->http_minor = 9;
|
|
866
|
-
state = s_req_line_almost_done;
|
|
867
|
-
break;
|
|
868
|
-
case LF:
|
|
869
|
-
CALLBACK(url);
|
|
870
|
-
parser->http_major = 0;
|
|
871
|
-
parser->http_minor = 9;
|
|
872
|
-
state = s_header_field_start;
|
|
873
|
-
break;
|
|
874
|
-
case '?':
|
|
875
|
-
MARK(fragment);
|
|
876
|
-
state = s_req_fragment;
|
|
877
|
-
break;
|
|
878
|
-
case '#':
|
|
879
|
-
break;
|
|
880
|
-
default:
|
|
881
|
-
goto error;
|
|
882
|
-
}
|
|
883
|
-
break;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
1046
|
case s_req_fragment:
|
|
887
1047
|
{
|
|
888
|
-
if (normal_url_char[(unsigned char)ch]) break;
|
|
889
|
-
|
|
890
1048
|
switch (ch) {
|
|
891
1049
|
case ' ':
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
state = s_req_http_start;
|
|
1050
|
+
parser->state = s_req_http_start;
|
|
1051
|
+
CALLBACK_DATA(url);
|
|
895
1052
|
break;
|
|
896
1053
|
case CR:
|
|
897
|
-
CALLBACK(url);
|
|
898
|
-
CALLBACK(fragment);
|
|
899
|
-
parser->http_major = 0;
|
|
900
|
-
parser->http_minor = 9;
|
|
901
|
-
state = s_req_line_almost_done;
|
|
902
|
-
break;
|
|
903
1054
|
case LF:
|
|
904
|
-
CALLBACK(url);
|
|
905
|
-
CALLBACK(fragment);
|
|
906
1055
|
parser->http_major = 0;
|
|
907
1056
|
parser->http_minor = 9;
|
|
908
|
-
state =
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1057
|
+
parser->state = (ch == CR) ?
|
|
1058
|
+
s_req_line_almost_done :
|
|
1059
|
+
s_header_field_start;
|
|
1060
|
+
CALLBACK_DATA(url);
|
|
912
1061
|
break;
|
|
913
1062
|
default:
|
|
914
|
-
|
|
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
|
+
}
|
|
915
1068
|
}
|
|
916
1069
|
break;
|
|
917
1070
|
}
|
|
@@ -919,140 +1072,170 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
919
1072
|
case s_req_http_start:
|
|
920
1073
|
switch (ch) {
|
|
921
1074
|
case 'H':
|
|
922
|
-
state = s_req_http_H;
|
|
1075
|
+
parser->state = s_req_http_H;
|
|
923
1076
|
break;
|
|
924
1077
|
case ' ':
|
|
925
1078
|
break;
|
|
926
1079
|
default:
|
|
1080
|
+
SET_ERRNO(HPE_INVALID_CONSTANT);
|
|
927
1081
|
goto error;
|
|
928
1082
|
}
|
|
929
1083
|
break;
|
|
930
1084
|
|
|
931
1085
|
case s_req_http_H:
|
|
932
1086
|
STRICT_CHECK(ch != 'T');
|
|
933
|
-
state = s_req_http_HT;
|
|
1087
|
+
parser->state = s_req_http_HT;
|
|
934
1088
|
break;
|
|
935
1089
|
|
|
936
1090
|
case s_req_http_HT:
|
|
937
1091
|
STRICT_CHECK(ch != 'T');
|
|
938
|
-
state = s_req_http_HTT;
|
|
1092
|
+
parser->state = s_req_http_HTT;
|
|
939
1093
|
break;
|
|
940
1094
|
|
|
941
1095
|
case s_req_http_HTT:
|
|
942
1096
|
STRICT_CHECK(ch != 'P');
|
|
943
|
-
state = s_req_http_HTTP;
|
|
1097
|
+
parser->state = s_req_http_HTTP;
|
|
944
1098
|
break;
|
|
945
1099
|
|
|
946
1100
|
case s_req_http_HTTP:
|
|
947
1101
|
STRICT_CHECK(ch != '/');
|
|
948
|
-
state = s_req_first_http_major;
|
|
1102
|
+
parser->state = s_req_first_http_major;
|
|
949
1103
|
break;
|
|
950
1104
|
|
|
951
1105
|
/* first digit of major HTTP version */
|
|
952
1106
|
case s_req_first_http_major:
|
|
953
|
-
if (ch < '1' || ch > '9')
|
|
1107
|
+
if (ch < '1' || ch > '9') {
|
|
1108
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
|
1109
|
+
goto error;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
954
1112
|
parser->http_major = ch - '0';
|
|
955
|
-
state = s_req_http_major;
|
|
1113
|
+
parser->state = s_req_http_major;
|
|
956
1114
|
break;
|
|
957
1115
|
|
|
958
1116
|
/* major HTTP version or dot */
|
|
959
1117
|
case s_req_http_major:
|
|
960
1118
|
{
|
|
961
1119
|
if (ch == '.') {
|
|
962
|
-
state = s_req_first_http_minor;
|
|
1120
|
+
parser->state = s_req_first_http_minor;
|
|
963
1121
|
break;
|
|
964
1122
|
}
|
|
965
1123
|
|
|
966
|
-
if (ch
|
|
1124
|
+
if (!IS_NUM(ch)) {
|
|
1125
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
|
1126
|
+
goto error;
|
|
1127
|
+
}
|
|
967
1128
|
|
|
968
1129
|
parser->http_major *= 10;
|
|
969
1130
|
parser->http_major += ch - '0';
|
|
970
1131
|
|
|
971
|
-
if (parser->http_major > 999)
|
|
1132
|
+
if (parser->http_major > 999) {
|
|
1133
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
|
1134
|
+
goto error;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
972
1137
|
break;
|
|
973
1138
|
}
|
|
974
1139
|
|
|
975
1140
|
/* first digit of minor HTTP version */
|
|
976
1141
|
case s_req_first_http_minor:
|
|
977
|
-
if (ch
|
|
1142
|
+
if (!IS_NUM(ch)) {
|
|
1143
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
|
1144
|
+
goto error;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
978
1147
|
parser->http_minor = ch - '0';
|
|
979
|
-
state = s_req_http_minor;
|
|
1148
|
+
parser->state = s_req_http_minor;
|
|
980
1149
|
break;
|
|
981
1150
|
|
|
982
1151
|
/* minor HTTP version or end of request line */
|
|
983
1152
|
case s_req_http_minor:
|
|
984
1153
|
{
|
|
985
1154
|
if (ch == CR) {
|
|
986
|
-
state = s_req_line_almost_done;
|
|
1155
|
+
parser->state = s_req_line_almost_done;
|
|
987
1156
|
break;
|
|
988
1157
|
}
|
|
989
1158
|
|
|
990
1159
|
if (ch == LF) {
|
|
991
|
-
state = s_header_field_start;
|
|
1160
|
+
parser->state = s_header_field_start;
|
|
992
1161
|
break;
|
|
993
1162
|
}
|
|
994
1163
|
|
|
995
1164
|
/* XXX allow spaces after digit? */
|
|
996
1165
|
|
|
997
|
-
if (ch
|
|
1166
|
+
if (!IS_NUM(ch)) {
|
|
1167
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
|
1168
|
+
goto error;
|
|
1169
|
+
}
|
|
998
1170
|
|
|
999
1171
|
parser->http_minor *= 10;
|
|
1000
1172
|
parser->http_minor += ch - '0';
|
|
1001
1173
|
|
|
1002
|
-
if (parser->http_minor > 999)
|
|
1174
|
+
if (parser->http_minor > 999) {
|
|
1175
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
|
1176
|
+
goto error;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1003
1179
|
break;
|
|
1004
1180
|
}
|
|
1005
1181
|
|
|
1006
1182
|
/* end of request line */
|
|
1007
1183
|
case s_req_line_almost_done:
|
|
1008
1184
|
{
|
|
1009
|
-
if (ch != LF)
|
|
1010
|
-
|
|
1185
|
+
if (ch != LF) {
|
|
1186
|
+
SET_ERRNO(HPE_LF_EXPECTED);
|
|
1187
|
+
goto error;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
parser->state = s_header_field_start;
|
|
1011
1191
|
break;
|
|
1012
1192
|
}
|
|
1013
1193
|
|
|
1014
1194
|
case s_header_field_start:
|
|
1015
1195
|
{
|
|
1016
1196
|
if (ch == CR) {
|
|
1017
|
-
state = s_headers_almost_done;
|
|
1197
|
+
parser->state = s_headers_almost_done;
|
|
1018
1198
|
break;
|
|
1019
1199
|
}
|
|
1020
1200
|
|
|
1021
1201
|
if (ch == LF) {
|
|
1022
1202
|
/* they might be just sending \n instead of \r\n so this would be
|
|
1023
1203
|
* the second \n to denote the end of headers*/
|
|
1024
|
-
state = s_headers_almost_done;
|
|
1025
|
-
goto
|
|
1204
|
+
parser->state = s_headers_almost_done;
|
|
1205
|
+
goto reexecute_byte;
|
|
1026
1206
|
}
|
|
1027
1207
|
|
|
1028
1208
|
c = TOKEN(ch);
|
|
1029
1209
|
|
|
1030
|
-
if (!c)
|
|
1210
|
+
if (!c) {
|
|
1211
|
+
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
|
1212
|
+
goto error;
|
|
1213
|
+
}
|
|
1031
1214
|
|
|
1032
1215
|
MARK(header_field);
|
|
1033
1216
|
|
|
1034
|
-
index = 0;
|
|
1035
|
-
state = s_header_field;
|
|
1217
|
+
parser->index = 0;
|
|
1218
|
+
parser->state = s_header_field;
|
|
1036
1219
|
|
|
1037
1220
|
switch (c) {
|
|
1038
1221
|
case 'c':
|
|
1039
|
-
header_state = h_C;
|
|
1222
|
+
parser->header_state = h_C;
|
|
1040
1223
|
break;
|
|
1041
1224
|
|
|
1042
1225
|
case 'p':
|
|
1043
|
-
header_state = h_matching_proxy_connection;
|
|
1226
|
+
parser->header_state = h_matching_proxy_connection;
|
|
1044
1227
|
break;
|
|
1045
1228
|
|
|
1046
1229
|
case 't':
|
|
1047
|
-
header_state = h_matching_transfer_encoding;
|
|
1230
|
+
parser->header_state = h_matching_transfer_encoding;
|
|
1048
1231
|
break;
|
|
1049
1232
|
|
|
1050
1233
|
case 'u':
|
|
1051
|
-
header_state = h_matching_upgrade;
|
|
1234
|
+
parser->header_state = h_matching_upgrade;
|
|
1052
1235
|
break;
|
|
1053
1236
|
|
|
1054
1237
|
default:
|
|
1055
|
-
header_state = h_general;
|
|
1238
|
+
parser->header_state = h_general;
|
|
1056
1239
|
break;
|
|
1057
1240
|
}
|
|
1058
1241
|
break;
|
|
@@ -1063,31 +1246,31 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1063
1246
|
c = TOKEN(ch);
|
|
1064
1247
|
|
|
1065
1248
|
if (c) {
|
|
1066
|
-
switch (header_state) {
|
|
1249
|
+
switch (parser->header_state) {
|
|
1067
1250
|
case h_general:
|
|
1068
1251
|
break;
|
|
1069
1252
|
|
|
1070
1253
|
case h_C:
|
|
1071
|
-
index++;
|
|
1072
|
-
header_state = (c == 'o' ? h_CO : h_general);
|
|
1254
|
+
parser->index++;
|
|
1255
|
+
parser->header_state = (c == 'o' ? h_CO : h_general);
|
|
1073
1256
|
break;
|
|
1074
1257
|
|
|
1075
1258
|
case h_CO:
|
|
1076
|
-
index++;
|
|
1077
|
-
header_state = (c == 'n' ? h_CON : h_general);
|
|
1259
|
+
parser->index++;
|
|
1260
|
+
parser->header_state = (c == 'n' ? h_CON : h_general);
|
|
1078
1261
|
break;
|
|
1079
1262
|
|
|
1080
1263
|
case h_CON:
|
|
1081
|
-
index++;
|
|
1264
|
+
parser->index++;
|
|
1082
1265
|
switch (c) {
|
|
1083
1266
|
case 'n':
|
|
1084
|
-
header_state = h_matching_connection;
|
|
1267
|
+
parser->header_state = h_matching_connection;
|
|
1085
1268
|
break;
|
|
1086
1269
|
case 't':
|
|
1087
|
-
header_state = h_matching_content_length;
|
|
1270
|
+
parser->header_state = h_matching_content_length;
|
|
1088
1271
|
break;
|
|
1089
1272
|
default:
|
|
1090
|
-
header_state = h_general;
|
|
1273
|
+
parser->header_state = h_general;
|
|
1091
1274
|
break;
|
|
1092
1275
|
}
|
|
1093
1276
|
break;
|
|
@@ -1095,60 +1278,60 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1095
1278
|
/* connection */
|
|
1096
1279
|
|
|
1097
1280
|
case h_matching_connection:
|
|
1098
|
-
index++;
|
|
1099
|
-
if (index > sizeof(CONNECTION)-1
|
|
1100
|
-
|| c != CONNECTION[index]) {
|
|
1101
|
-
header_state = h_general;
|
|
1102
|
-
} else if (index == sizeof(CONNECTION)-2) {
|
|
1103
|
-
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;
|
|
1104
1287
|
}
|
|
1105
1288
|
break;
|
|
1106
1289
|
|
|
1107
1290
|
/* proxy-connection */
|
|
1108
1291
|
|
|
1109
1292
|
case h_matching_proxy_connection:
|
|
1110
|
-
index++;
|
|
1111
|
-
if (index > sizeof(PROXY_CONNECTION)-1
|
|
1112
|
-
|| c != PROXY_CONNECTION[index]) {
|
|
1113
|
-
header_state = h_general;
|
|
1114
|
-
} else if (index == sizeof(PROXY_CONNECTION)-2) {
|
|
1115
|
-
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;
|
|
1116
1299
|
}
|
|
1117
1300
|
break;
|
|
1118
1301
|
|
|
1119
1302
|
/* content-length */
|
|
1120
1303
|
|
|
1121
1304
|
case h_matching_content_length:
|
|
1122
|
-
index++;
|
|
1123
|
-
if (index > sizeof(CONTENT_LENGTH)-1
|
|
1124
|
-
|| c != CONTENT_LENGTH[index]) {
|
|
1125
|
-
header_state = h_general;
|
|
1126
|
-
} else if (index == sizeof(CONTENT_LENGTH)-2) {
|
|
1127
|
-
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;
|
|
1128
1311
|
}
|
|
1129
1312
|
break;
|
|
1130
1313
|
|
|
1131
1314
|
/* transfer-encoding */
|
|
1132
1315
|
|
|
1133
1316
|
case h_matching_transfer_encoding:
|
|
1134
|
-
index++;
|
|
1135
|
-
if (index > sizeof(TRANSFER_ENCODING)-1
|
|
1136
|
-
|| c != TRANSFER_ENCODING[index]) {
|
|
1137
|
-
header_state = h_general;
|
|
1138
|
-
} else if (index == sizeof(TRANSFER_ENCODING)-2) {
|
|
1139
|
-
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;
|
|
1140
1323
|
}
|
|
1141
1324
|
break;
|
|
1142
1325
|
|
|
1143
1326
|
/* upgrade */
|
|
1144
1327
|
|
|
1145
1328
|
case h_matching_upgrade:
|
|
1146
|
-
index++;
|
|
1147
|
-
if (index > sizeof(UPGRADE)-1
|
|
1148
|
-
|| c != UPGRADE[index]) {
|
|
1149
|
-
header_state = h_general;
|
|
1150
|
-
} else if (index == sizeof(UPGRADE)-2) {
|
|
1151
|
-
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;
|
|
1152
1335
|
}
|
|
1153
1336
|
break;
|
|
1154
1337
|
|
|
@@ -1156,7 +1339,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1156
1339
|
case h_content_length:
|
|
1157
1340
|
case h_transfer_encoding:
|
|
1158
1341
|
case h_upgrade:
|
|
1159
|
-
if (ch != ' ') header_state = h_general;
|
|
1342
|
+
if (ch != ' ') parser->header_state = h_general;
|
|
1160
1343
|
break;
|
|
1161
1344
|
|
|
1162
1345
|
default:
|
|
@@ -1167,84 +1350,89 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1167
1350
|
}
|
|
1168
1351
|
|
|
1169
1352
|
if (ch == ':') {
|
|
1170
|
-
|
|
1171
|
-
|
|
1353
|
+
parser->state = s_header_value_start;
|
|
1354
|
+
CALLBACK_DATA(header_field);
|
|
1172
1355
|
break;
|
|
1173
1356
|
}
|
|
1174
1357
|
|
|
1175
1358
|
if (ch == CR) {
|
|
1176
|
-
state = s_header_almost_done;
|
|
1177
|
-
|
|
1359
|
+
parser->state = s_header_almost_done;
|
|
1360
|
+
CALLBACK_DATA(header_field);
|
|
1178
1361
|
break;
|
|
1179
1362
|
}
|
|
1180
1363
|
|
|
1181
1364
|
if (ch == LF) {
|
|
1182
|
-
|
|
1183
|
-
|
|
1365
|
+
parser->state = s_header_field_start;
|
|
1366
|
+
CALLBACK_DATA(header_field);
|
|
1184
1367
|
break;
|
|
1185
1368
|
}
|
|
1186
1369
|
|
|
1370
|
+
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
|
1187
1371
|
goto error;
|
|
1188
1372
|
}
|
|
1189
1373
|
|
|
1190
1374
|
case s_header_value_start:
|
|
1191
1375
|
{
|
|
1192
|
-
if (ch == ' ') break;
|
|
1376
|
+
if (ch == ' ' || ch == '\t') break;
|
|
1193
1377
|
|
|
1194
1378
|
MARK(header_value);
|
|
1195
1379
|
|
|
1196
|
-
state = s_header_value;
|
|
1197
|
-
index = 0;
|
|
1198
|
-
|
|
1199
|
-
c = LOWER(ch);
|
|
1380
|
+
parser->state = s_header_value;
|
|
1381
|
+
parser->index = 0;
|
|
1200
1382
|
|
|
1201
1383
|
if (ch == CR) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1384
|
+
parser->header_state = h_general;
|
|
1385
|
+
parser->state = s_header_almost_done;
|
|
1386
|
+
CALLBACK_DATA(header_value);
|
|
1205
1387
|
break;
|
|
1206
1388
|
}
|
|
1207
1389
|
|
|
1208
1390
|
if (ch == LF) {
|
|
1209
|
-
|
|
1210
|
-
|
|
1391
|
+
parser->state = s_header_field_start;
|
|
1392
|
+
CALLBACK_DATA(header_value);
|
|
1211
1393
|
break;
|
|
1212
1394
|
}
|
|
1213
1395
|
|
|
1214
|
-
|
|
1396
|
+
c = LOWER(ch);
|
|
1397
|
+
|
|
1398
|
+
switch (parser->header_state) {
|
|
1215
1399
|
case h_upgrade:
|
|
1216
1400
|
parser->flags |= F_UPGRADE;
|
|
1217
|
-
header_state = h_general;
|
|
1401
|
+
parser->header_state = h_general;
|
|
1218
1402
|
break;
|
|
1219
1403
|
|
|
1220
1404
|
case h_transfer_encoding:
|
|
1221
1405
|
/* looking for 'Transfer-Encoding: chunked' */
|
|
1222
1406
|
if ('c' == c) {
|
|
1223
|
-
header_state = h_matching_transfer_encoding_chunked;
|
|
1407
|
+
parser->header_state = h_matching_transfer_encoding_chunked;
|
|
1224
1408
|
} else {
|
|
1225
|
-
header_state = h_general;
|
|
1409
|
+
parser->header_state = h_general;
|
|
1226
1410
|
}
|
|
1227
1411
|
break;
|
|
1228
1412
|
|
|
1229
1413
|
case h_content_length:
|
|
1230
|
-
if (ch
|
|
1414
|
+
if (!IS_NUM(ch)) {
|
|
1415
|
+
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
|
1416
|
+
goto error;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1231
1419
|
parser->content_length = ch - '0';
|
|
1232
1420
|
break;
|
|
1233
1421
|
|
|
1234
1422
|
case h_connection:
|
|
1235
1423
|
/* looking for 'Connection: keep-alive' */
|
|
1236
1424
|
if (c == 'k') {
|
|
1237
|
-
header_state = h_matching_connection_keep_alive;
|
|
1425
|
+
parser->header_state = h_matching_connection_keep_alive;
|
|
1238
1426
|
/* looking for 'Connection: close' */
|
|
1239
1427
|
} else if (c == 'c') {
|
|
1240
|
-
header_state = h_matching_connection_close;
|
|
1428
|
+
parser->header_state = h_matching_connection_close;
|
|
1241
1429
|
} else {
|
|
1242
|
-
header_state = h_general;
|
|
1430
|
+
parser->header_state = h_general;
|
|
1243
1431
|
}
|
|
1244
1432
|
break;
|
|
1245
1433
|
|
|
1246
1434
|
default:
|
|
1247
|
-
header_state = h_general;
|
|
1435
|
+
parser->header_state = h_general;
|
|
1248
1436
|
break;
|
|
1249
1437
|
}
|
|
1250
1438
|
break;
|
|
@@ -1252,20 +1440,22 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1252
1440
|
|
|
1253
1441
|
case s_header_value:
|
|
1254
1442
|
{
|
|
1255
|
-
c = LOWER(ch);
|
|
1256
1443
|
|
|
1257
1444
|
if (ch == CR) {
|
|
1258
|
-
|
|
1259
|
-
|
|
1445
|
+
parser->state = s_header_almost_done;
|
|
1446
|
+
CALLBACK_DATA(header_value);
|
|
1260
1447
|
break;
|
|
1261
1448
|
}
|
|
1262
1449
|
|
|
1263
1450
|
if (ch == LF) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1451
|
+
parser->state = s_header_almost_done;
|
|
1452
|
+
CALLBACK_DATA_NOADVANCE(header_value);
|
|
1453
|
+
goto reexecute_byte;
|
|
1266
1454
|
}
|
|
1267
1455
|
|
|
1268
|
-
|
|
1456
|
+
c = LOWER(ch);
|
|
1457
|
+
|
|
1458
|
+
switch (parser->header_state) {
|
|
1269
1459
|
case h_general:
|
|
1270
1460
|
break;
|
|
1271
1461
|
|
|
@@ -1275,66 +1465,83 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1275
1465
|
break;
|
|
1276
1466
|
|
|
1277
1467
|
case h_content_length:
|
|
1468
|
+
{
|
|
1469
|
+
uint64_t t;
|
|
1470
|
+
|
|
1278
1471
|
if (ch == ' ') break;
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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;
|
|
1282
1489
|
break;
|
|
1490
|
+
}
|
|
1283
1491
|
|
|
1284
1492
|
/* Transfer-Encoding: chunked */
|
|
1285
1493
|
case h_matching_transfer_encoding_chunked:
|
|
1286
|
-
index++;
|
|
1287
|
-
if (index > sizeof(CHUNKED)-1
|
|
1288
|
-
|| c != CHUNKED[index]) {
|
|
1289
|
-
header_state = h_general;
|
|
1290
|
-
} else if (index == sizeof(CHUNKED)-2) {
|
|
1291
|
-
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;
|
|
1292
1500
|
}
|
|
1293
1501
|
break;
|
|
1294
1502
|
|
|
1295
1503
|
/* looking for 'Connection: keep-alive' */
|
|
1296
1504
|
case h_matching_connection_keep_alive:
|
|
1297
|
-
index++;
|
|
1298
|
-
if (index > sizeof(KEEP_ALIVE)-1
|
|
1299
|
-
|| c != KEEP_ALIVE[index]) {
|
|
1300
|
-
header_state = h_general;
|
|
1301
|
-
} else if (index == sizeof(KEEP_ALIVE)-2) {
|
|
1302
|
-
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;
|
|
1303
1511
|
}
|
|
1304
1512
|
break;
|
|
1305
1513
|
|
|
1306
1514
|
/* looking for 'Connection: close' */
|
|
1307
1515
|
case h_matching_connection_close:
|
|
1308
|
-
index++;
|
|
1309
|
-
if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
|
|
1310
|
-
header_state = h_general;
|
|
1311
|
-
} else if (index == sizeof(CLOSE)-2) {
|
|
1312
|
-
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;
|
|
1313
1521
|
}
|
|
1314
1522
|
break;
|
|
1315
1523
|
|
|
1316
1524
|
case h_transfer_encoding_chunked:
|
|
1317
1525
|
case h_connection_keep_alive:
|
|
1318
1526
|
case h_connection_close:
|
|
1319
|
-
if (ch != ' ') header_state = h_general;
|
|
1527
|
+
if (ch != ' ') parser->header_state = h_general;
|
|
1320
1528
|
break;
|
|
1321
1529
|
|
|
1322
1530
|
default:
|
|
1323
|
-
state = s_header_value;
|
|
1324
|
-
header_state = h_general;
|
|
1531
|
+
parser->state = s_header_value;
|
|
1532
|
+
parser->header_state = h_general;
|
|
1325
1533
|
break;
|
|
1326
1534
|
}
|
|
1327
1535
|
break;
|
|
1328
1536
|
}
|
|
1329
1537
|
|
|
1330
1538
|
case s_header_almost_done:
|
|
1331
|
-
header_almost_done:
|
|
1332
1539
|
{
|
|
1333
1540
|
STRICT_CHECK(ch != LF);
|
|
1334
1541
|
|
|
1335
|
-
state =
|
|
1542
|
+
parser->state = s_header_value_lws;
|
|
1336
1543
|
|
|
1337
|
-
switch (header_state) {
|
|
1544
|
+
switch (parser->header_state) {
|
|
1338
1545
|
case h_connection_keep_alive:
|
|
1339
1546
|
parser->flags |= F_CONNECTION_KEEP_ALIVE;
|
|
1340
1547
|
break;
|
|
@@ -1347,32 +1554,47 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1347
1554
|
default:
|
|
1348
1555
|
break;
|
|
1349
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
|
+
}
|
|
1350
1570
|
break;
|
|
1351
1571
|
}
|
|
1352
1572
|
|
|
1353
1573
|
case s_headers_almost_done:
|
|
1354
|
-
headers_almost_done:
|
|
1355
1574
|
{
|
|
1356
1575
|
STRICT_CHECK(ch != LF);
|
|
1357
1576
|
|
|
1358
1577
|
if (parser->flags & F_TRAILING) {
|
|
1359
1578
|
/* End of a chunked request */
|
|
1360
|
-
|
|
1361
|
-
|
|
1579
|
+
parser->state = NEW_MESSAGE();
|
|
1580
|
+
CALLBACK_NOTIFY(message_complete);
|
|
1362
1581
|
break;
|
|
1363
1582
|
}
|
|
1364
1583
|
|
|
1365
|
-
|
|
1584
|
+
parser->state = s_headers_done;
|
|
1366
1585
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
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);
|
|
1370
1589
|
|
|
1371
1590
|
/* Here we call the headers_complete callback. This is somewhat
|
|
1372
1591
|
* different than other callbacks because if the user returns 1, we
|
|
1373
1592
|
* will interpret that as saying that this message has no body. This
|
|
1374
1593
|
* is needed for the annoying case of recieving a response to a HEAD
|
|
1375
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.
|
|
1376
1598
|
*/
|
|
1377
1599
|
if (settings->on_headers_complete) {
|
|
1378
1600
|
switch (settings->on_headers_complete(parser)) {
|
|
@@ -1384,39 +1606,54 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1384
1606
|
break;
|
|
1385
1607
|
|
|
1386
1608
|
default:
|
|
1387
|
-
|
|
1609
|
+
SET_ERRNO(HPE_CB_headers_complete);
|
|
1388
1610
|
return p - data; /* Error */
|
|
1389
1611
|
}
|
|
1390
1612
|
}
|
|
1391
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
|
+
|
|
1392
1627
|
/* Exit, the rest of the connect is in a different protocol. */
|
|
1393
1628
|
if (parser->upgrade) {
|
|
1394
|
-
|
|
1395
|
-
|
|
1629
|
+
parser->state = NEW_MESSAGE();
|
|
1630
|
+
CALLBACK_NOTIFY(message_complete);
|
|
1631
|
+
return (p - data) + 1;
|
|
1396
1632
|
}
|
|
1397
1633
|
|
|
1398
1634
|
if (parser->flags & F_SKIPBODY) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1635
|
+
parser->state = NEW_MESSAGE();
|
|
1636
|
+
CALLBACK_NOTIFY(message_complete);
|
|
1401
1637
|
} else if (parser->flags & F_CHUNKED) {
|
|
1402
1638
|
/* chunked encoding - ignore Content-Length header */
|
|
1403
|
-
state = s_chunk_size_start;
|
|
1639
|
+
parser->state = s_chunk_size_start;
|
|
1404
1640
|
} else {
|
|
1405
1641
|
if (parser->content_length == 0) {
|
|
1406
1642
|
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
} else if (parser->content_length
|
|
1643
|
+
parser->state = NEW_MESSAGE();
|
|
1644
|
+
CALLBACK_NOTIFY(message_complete);
|
|
1645
|
+
} else if (parser->content_length != ULLONG_MAX) {
|
|
1410
1646
|
/* Content-Length header given and non-zero */
|
|
1411
|
-
state = s_body_identity;
|
|
1647
|
+
parser->state = s_body_identity;
|
|
1412
1648
|
} else {
|
|
1413
|
-
if (parser->type == HTTP_REQUEST ||
|
|
1649
|
+
if (parser->type == HTTP_REQUEST ||
|
|
1650
|
+
!http_message_needs_eof(parser)) {
|
|
1414
1651
|
/* Assume content-length 0 - read the next */
|
|
1415
|
-
|
|
1416
|
-
|
|
1652
|
+
parser->state = NEW_MESSAGE();
|
|
1653
|
+
CALLBACK_NOTIFY(message_complete);
|
|
1417
1654
|
} else {
|
|
1418
1655
|
/* Read body until EOF */
|
|
1419
|
-
state = s_body_identity_eof;
|
|
1656
|
+
parser->state = s_body_identity_eof;
|
|
1420
1657
|
}
|
|
1421
1658
|
}
|
|
1422
1659
|
}
|
|
@@ -1425,59 +1662,103 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1425
1662
|
}
|
|
1426
1663
|
|
|
1427
1664
|
case s_body_identity:
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
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;
|
|
1437
1695
|
}
|
|
1696
|
+
|
|
1438
1697
|
break;
|
|
1698
|
+
}
|
|
1439
1699
|
|
|
1440
1700
|
/* read until EOF */
|
|
1441
1701
|
case s_body_identity_eof:
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
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);
|
|
1447
1710
|
break;
|
|
1448
1711
|
|
|
1449
1712
|
case s_chunk_size_start:
|
|
1450
1713
|
{
|
|
1714
|
+
assert(parser->nread == 1);
|
|
1451
1715
|
assert(parser->flags & F_CHUNKED);
|
|
1452
1716
|
|
|
1453
|
-
|
|
1454
|
-
if (
|
|
1455
|
-
|
|
1456
|
-
|
|
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;
|
|
1457
1725
|
break;
|
|
1458
1726
|
}
|
|
1459
1727
|
|
|
1460
1728
|
case s_chunk_size:
|
|
1461
1729
|
{
|
|
1730
|
+
uint64_t t;
|
|
1731
|
+
|
|
1462
1732
|
assert(parser->flags & F_CHUNKED);
|
|
1463
1733
|
|
|
1464
1734
|
if (ch == CR) {
|
|
1465
|
-
state = s_chunk_size_almost_done;
|
|
1735
|
+
parser->state = s_chunk_size_almost_done;
|
|
1466
1736
|
break;
|
|
1467
1737
|
}
|
|
1468
1738
|
|
|
1469
|
-
|
|
1739
|
+
unhex_val = unhex[(unsigned char)ch];
|
|
1470
1740
|
|
|
1471
|
-
if (
|
|
1741
|
+
if (unhex_val == -1) {
|
|
1472
1742
|
if (ch == ';' || ch == ' ') {
|
|
1473
|
-
state = s_chunk_parameters;
|
|
1743
|
+
parser->state = s_chunk_parameters;
|
|
1474
1744
|
break;
|
|
1475
1745
|
}
|
|
1746
|
+
|
|
1747
|
+
SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
|
|
1476
1748
|
goto error;
|
|
1477
1749
|
}
|
|
1478
1750
|
|
|
1479
|
-
parser->content_length
|
|
1480
|
-
|
|
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);
|
|
1758
|
+
goto error;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
parser->content_length = t;
|
|
1481
1762
|
break;
|
|
1482
1763
|
}
|
|
1483
1764
|
|
|
@@ -1486,7 +1767,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1486
1767
|
assert(parser->flags & F_CHUNKED);
|
|
1487
1768
|
/* just ignore this shit. TODO check for overflow */
|
|
1488
1769
|
if (ch == CR) {
|
|
1489
|
-
state = s_chunk_size_almost_done;
|
|
1770
|
+
parser->state = s_chunk_size_almost_done;
|
|
1490
1771
|
break;
|
|
1491
1772
|
}
|
|
1492
1773
|
break;
|
|
@@ -1497,72 +1778,117 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
1497
1778
|
assert(parser->flags & F_CHUNKED);
|
|
1498
1779
|
STRICT_CHECK(ch != LF);
|
|
1499
1780
|
|
|
1781
|
+
parser->nread = 0;
|
|
1782
|
+
|
|
1500
1783
|
if (parser->content_length == 0) {
|
|
1501
1784
|
parser->flags |= F_TRAILING;
|
|
1502
|
-
state = s_header_field_start;
|
|
1785
|
+
parser->state = s_header_field_start;
|
|
1503
1786
|
} else {
|
|
1504
|
-
state = s_chunk_data;
|
|
1787
|
+
parser->state = s_chunk_data;
|
|
1505
1788
|
}
|
|
1506
1789
|
break;
|
|
1507
1790
|
}
|
|
1508
1791
|
|
|
1509
1792
|
case s_chunk_data:
|
|
1510
1793
|
{
|
|
1511
|
-
|
|
1794
|
+
uint64_t to_read = MIN(parser->content_length,
|
|
1795
|
+
(uint64_t) ((data + len) - p));
|
|
1512
1796
|
|
|
1513
|
-
|
|
1797
|
+
assert(parser->flags & F_CHUNKED);
|
|
1798
|
+
assert(parser->content_length != 0
|
|
1799
|
+
&& parser->content_length != ULLONG_MAX);
|
|
1514
1800
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
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;
|
|
1519
1807
|
|
|
1520
|
-
if (
|
|
1521
|
-
state = s_chunk_data_almost_done;
|
|
1808
|
+
if (parser->content_length == 0) {
|
|
1809
|
+
parser->state = s_chunk_data_almost_done;
|
|
1522
1810
|
}
|
|
1523
1811
|
|
|
1524
|
-
parser->content_length -= to_read;
|
|
1525
1812
|
break;
|
|
1526
1813
|
}
|
|
1527
1814
|
|
|
1528
1815
|
case s_chunk_data_almost_done:
|
|
1529
1816
|
assert(parser->flags & F_CHUNKED);
|
|
1817
|
+
assert(parser->content_length == 0);
|
|
1530
1818
|
STRICT_CHECK(ch != CR);
|
|
1531
|
-
state = s_chunk_data_done;
|
|
1819
|
+
parser->state = s_chunk_data_done;
|
|
1820
|
+
CALLBACK_DATA(body);
|
|
1532
1821
|
break;
|
|
1533
1822
|
|
|
1534
1823
|
case s_chunk_data_done:
|
|
1535
1824
|
assert(parser->flags & F_CHUNKED);
|
|
1536
1825
|
STRICT_CHECK(ch != LF);
|
|
1537
|
-
|
|
1826
|
+
parser->nread = 0;
|
|
1827
|
+
parser->state = s_chunk_size_start;
|
|
1538
1828
|
break;
|
|
1539
1829
|
|
|
1540
1830
|
default:
|
|
1541
1831
|
assert(0 && "unhandled state");
|
|
1832
|
+
SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
|
|
1542
1833
|
goto error;
|
|
1543
1834
|
}
|
|
1544
1835
|
}
|
|
1545
1836
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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);
|
|
1552
1851
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1852
|
+
CALLBACK_DATA_NOADVANCE(header_field);
|
|
1853
|
+
CALLBACK_DATA_NOADVANCE(header_value);
|
|
1854
|
+
CALLBACK_DATA_NOADVANCE(url);
|
|
1855
|
+
CALLBACK_DATA_NOADVANCE(body);
|
|
1557
1856
|
|
|
1558
1857
|
return len;
|
|
1559
1858
|
|
|
1560
1859
|
error:
|
|
1561
|
-
parser
|
|
1860
|
+
if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
|
|
1861
|
+
SET_ERRNO(HPE_UNKNOWN);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1562
1864
|
return (p - data);
|
|
1563
1865
|
}
|
|
1564
1866
|
|
|
1565
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
|
+
|
|
1566
1892
|
int
|
|
1567
1893
|
http_should_keep_alive (http_parser *parser)
|
|
1568
1894
|
{
|
|
@@ -1570,17 +1896,15 @@ http_should_keep_alive (http_parser *parser)
|
|
|
1570
1896
|
/* HTTP/1.1 */
|
|
1571
1897
|
if (parser->flags & F_CONNECTION_CLOSE) {
|
|
1572
1898
|
return 0;
|
|
1573
|
-
} else {
|
|
1574
|
-
return 1;
|
|
1575
1899
|
}
|
|
1576
1900
|
} else {
|
|
1577
1901
|
/* HTTP/1.0 or earlier */
|
|
1578
|
-
if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
|
|
1579
|
-
return 1;
|
|
1580
|
-
} else {
|
|
1902
|
+
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
|
|
1581
1903
|
return 0;
|
|
1582
1904
|
}
|
|
1583
1905
|
}
|
|
1906
|
+
|
|
1907
|
+
return !http_message_needs_eof(parser);
|
|
1584
1908
|
}
|
|
1585
1909
|
|
|
1586
1910
|
|
|
@@ -1593,10 +1917,142 @@ const char * http_method_str (enum http_method m)
|
|
|
1593
1917
|
void
|
|
1594
1918
|
http_parser_init (http_parser *parser, enum http_parser_type t)
|
|
1595
1919
|
{
|
|
1920
|
+
void *data = parser->data; /* preserve application data */
|
|
1921
|
+
memset(parser, 0, sizeof(*parser));
|
|
1922
|
+
parser->data = data;
|
|
1596
1923
|
parser->type = t;
|
|
1597
1924
|
parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
|
|
1598
|
-
parser->
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
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
|
+
}
|
|
1602
2058
|
}
|