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,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
|
|
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
|
-
#
|
|
41
|
+
#if HTTP_PARSER_DEBUG
|
|
42
|
+
#define SET_ERRNO(e) \
|
|
42
43
|
do { \
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
55
|
+
/* Run the notify callback FOR, returning ER if it fails */
|
|
56
|
+
#define CALLBACK_NOTIFY_(FOR, ER) \
|
|
50
57
|
do { \
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
106
|
+
/* Set the mark FOR; non-destructive if mark is already set */
|
|
107
|
+
#define MARK(FOR) \
|
|
70
108
|
do { \
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
/*
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
171
|
+
0, '!', 0, '#', '$', '%', '&', '\'',
|
|
122
172
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
|
123
|
-
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 <=
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
#
|
|
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,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 =
|
|
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;
|
|
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;
|
|
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:
|
|
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
|
-
|
|
937
|
+
const char *matcher;
|
|
938
|
+
if (ch == '\0') {
|
|
939
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
|
597
940
|
goto error;
|
|
941
|
+
}
|
|
598
942
|
|
|
599
|
-
|
|
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 ==
|
|
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 (
|
|
619
|
-
parser->method
|
|
620
|
-
|
|
621
|
-
parser->method
|
|
622
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
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
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
876
|
-
|
|
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
|
-
|
|
887
|
-
CALLBACK(fragment);
|
|
1055
|
+
parser->http_major = 0;
|
|
888
1056
|
parser->http_minor = 9;
|
|
889
|
-
state =
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
|
|
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')
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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)
|
|
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)
|
|
991
|
-
|
|
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
|
|
1204
|
+
parser->state = s_headers_almost_done;
|
|
1205
|
+
goto reexecute_byte;
|
|
1007
1206
|
}
|
|
1008
1207
|
|
|
1009
|
-
c =
|
|
1208
|
+
c = TOKEN(ch);
|
|
1010
1209
|
|
|
1011
|
-
if (c
|
|
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 =
|
|
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
|
-
|
|
1152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1164
|
-
|
|
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
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
-
|
|
1390
|
+
if (ch == LF) {
|
|
1391
|
+
parser->state = s_header_field_start;
|
|
1392
|
+
CALLBACK_DATA(header_value);
|
|
1197
1393
|
break;
|
|
1198
1394
|
}
|
|
1199
1395
|
|
|
1200
|
-
|
|
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
|
|
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
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
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 =
|
|
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
|
-
|
|
1350
|
-
|
|
1579
|
+
parser->state = NEW_MESSAGE();
|
|
1580
|
+
CALLBACK_NOTIFY(message_complete);
|
|
1351
1581
|
break;
|
|
1352
1582
|
}
|
|
1353
1583
|
|
|
1354
|
-
|
|
1584
|
+
parser->state = s_headers_done;
|
|
1355
1585
|
|
|
1356
|
-
|
|
1357
|
-
|
|
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
|
-
|
|
1383
|
-
|
|
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
|
-
|
|
1388
|
-
|
|
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
|
-
|
|
1396
|
-
|
|
1397
|
-
} else if (parser->content_length
|
|
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 ||
|
|
1649
|
+
if (parser->type == HTTP_REQUEST ||
|
|
1650
|
+
!http_message_needs_eof(parser)) {
|
|
1402
1651
|
/* Assume content-length 0 - read the next */
|
|
1403
|
-
|
|
1404
|
-
|
|
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
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
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
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
-
|
|
1442
|
-
if (
|
|
1443
|
-
|
|
1444
|
-
|
|
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
|
-
|
|
1739
|
+
unhex_val = unhex[(unsigned char)ch];
|
|
1458
1740
|
|
|
1459
|
-
if (
|
|
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
|
|
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
|
-
|
|
1794
|
+
uint64_t to_read = MIN(parser->content_length,
|
|
1795
|
+
(uint64_t) ((data + len) - p));
|
|
1500
1796
|
|
|
1501
|
-
|
|
1797
|
+
assert(parser->flags & F_CHUNKED);
|
|
1798
|
+
assert(parser->content_length != 0
|
|
1799
|
+
&& parser->content_length != ULLONG_MAX);
|
|
1502
1800
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
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
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
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
|
|
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->
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
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
|
}
|