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
data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
package http_parser.lolevel;
|
|
2
2
|
|
|
3
|
+
import java.io.UnsupportedEncodingException;
|
|
3
4
|
import java.nio.ByteBuffer;
|
|
4
5
|
import http_parser.HTTPException;
|
|
5
6
|
import http_parser.HTTPMethod;
|
|
7
|
+
import http_parser.HTTPParserUrl;
|
|
6
8
|
import http_parser.ParserType;
|
|
7
9
|
import static http_parser.lolevel.HTTPParser.C.*;
|
|
8
10
|
import static http_parser.lolevel.HTTPParser.State.*;
|
|
@@ -20,21 +22,22 @@ public class HTTPParser {
|
|
|
20
22
|
int flags; // TODO
|
|
21
23
|
|
|
22
24
|
int nread;
|
|
23
|
-
|
|
25
|
+
long content_length;
|
|
26
|
+
|
|
27
|
+
int p_start; // updated each call to execute to indicate where the buffer was before we began calling it.
|
|
24
28
|
|
|
25
|
-
|
|
26
29
|
/** READ-ONLY **/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
public int http_major;
|
|
31
|
+
public int http_minor;
|
|
32
|
+
public int status_code; /* responses only */
|
|
33
|
+
public HTTPMethod method; /* requests only */
|
|
31
34
|
|
|
32
35
|
/* true = Upgrade header was present and the parser has exited because of that.
|
|
33
36
|
* false = No upgrade header present.
|
|
34
37
|
* Should be checked when http_parser_execute() returns in addition to
|
|
35
38
|
* error checking.
|
|
36
39
|
*/
|
|
37
|
-
|
|
40
|
+
public boolean upgrade;
|
|
38
41
|
|
|
39
42
|
/** PUBLIC **/
|
|
40
43
|
// TODO : this is used in c to maintain application state.
|
|
@@ -50,10 +53,8 @@ public class HTTPParser {
|
|
|
50
53
|
*/
|
|
51
54
|
int header_field_mark = -1;
|
|
52
55
|
int header_value_mark = -1;
|
|
53
|
-
int fragment_mark = -1;
|
|
54
|
-
int query_string_mark = -1;
|
|
55
|
-
int path_mark = -1;
|
|
56
56
|
int url_mark = -1;
|
|
57
|
+
int body_mark = -1;
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* Construct a Parser for ParserType.HTTP_BOTH, meaning it
|
|
@@ -77,7 +78,7 @@ public class HTTPParser {
|
|
|
77
78
|
this.state = State.start_res;
|
|
78
79
|
break;
|
|
79
80
|
case HTTP_BOTH:
|
|
80
|
-
this.state = State.
|
|
81
|
+
this.state = State.start_req_or_res;
|
|
81
82
|
break;
|
|
82
83
|
default:
|
|
83
84
|
throw new HTTPException("can't happen, invalid ParserType enum");
|
|
@@ -89,27 +90,210 @@ public class HTTPParser {
|
|
|
89
90
|
*/
|
|
90
91
|
static void p(Object o) {System.out.println(o);}
|
|
91
92
|
|
|
93
|
+
/** Comment from C version follows
|
|
94
|
+
*
|
|
95
|
+
* Our URL parser.
|
|
96
|
+
*
|
|
97
|
+
* This is designed to be shared by http_parser_execute() for URL validation,
|
|
98
|
+
* hence it has a state transition + byte-for-byte interface. In addition, it
|
|
99
|
+
* is meant to be embedded in http_parser_parse_url(), which does the dirty
|
|
100
|
+
* work of turning state transitions URL components for its API.
|
|
101
|
+
*
|
|
102
|
+
* This function should only be invoked with non-space characters. It is
|
|
103
|
+
* assumed that the caller cares about (and can detect) the transition between
|
|
104
|
+
* URL and non-URL states by looking for these.
|
|
105
|
+
*/
|
|
106
|
+
public State parse_url_char(byte ch) {
|
|
107
|
+
|
|
108
|
+
int chi = ch & 0xff; // utility, ch without signedness for table lookups.
|
|
109
|
+
|
|
110
|
+
if(SPACE == ch){
|
|
111
|
+
throw new HTTPException("space as url char");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
switch(state) {
|
|
115
|
+
case req_spaces_before_url:
|
|
116
|
+
/* Proxied requests are followed by scheme of an absolute URI (alpha).
|
|
117
|
+
* All methods except CONNECT are followed by '/' or '*'.
|
|
118
|
+
*/
|
|
119
|
+
if(SLASH == ch || STAR == ch){
|
|
120
|
+
return req_path;
|
|
121
|
+
}
|
|
122
|
+
if(isAtoZ(ch)){
|
|
123
|
+
return req_schema;
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
case req_schema:
|
|
127
|
+
if(isAtoZ(ch)){
|
|
128
|
+
return req_schema;
|
|
129
|
+
}
|
|
130
|
+
if(COLON == ch){
|
|
131
|
+
return req_schema_slash;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case req_schema_slash:
|
|
135
|
+
if(SLASH == ch){
|
|
136
|
+
return req_schema_slash_slash;
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case req_schema_slash_slash:
|
|
140
|
+
if(SLASH == ch){
|
|
141
|
+
return req_host_start;
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case req_host_start:
|
|
145
|
+
if (ch == (byte)'[') {
|
|
146
|
+
return req_host_v6_start;
|
|
147
|
+
}
|
|
148
|
+
if (isHostChar(ch)) {
|
|
149
|
+
return req_host;
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case req_host:
|
|
154
|
+
if (isHostChar(ch)) {
|
|
155
|
+
return req_host;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* FALLTHROUGH */
|
|
159
|
+
case req_host_v6_end:
|
|
160
|
+
switch (ch) {
|
|
161
|
+
case ':':
|
|
162
|
+
return req_port_start;
|
|
163
|
+
case '/':
|
|
164
|
+
return req_path;
|
|
165
|
+
case '?':
|
|
166
|
+
return req_query_string_start;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case req_host_v6:
|
|
171
|
+
if (ch == ']') {
|
|
172
|
+
return req_host_v6_end;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* FALLTHROUGH */
|
|
176
|
+
case req_host_v6_start:
|
|
177
|
+
if (isHex(ch) || ch == ':') {
|
|
178
|
+
return req_host_v6;
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
|
|
182
|
+
case req_port:
|
|
183
|
+
switch (ch) {
|
|
184
|
+
case '/':
|
|
185
|
+
return req_path;
|
|
186
|
+
case '?':
|
|
187
|
+
return req_query_string_start;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* FALLTHROUGH */
|
|
191
|
+
case req_port_start:
|
|
192
|
+
if (isDigit(ch)) {
|
|
193
|
+
return req_port;
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case req_path:
|
|
198
|
+
if (isNormalUrlChar(chi)) {
|
|
199
|
+
return req_path;
|
|
200
|
+
}
|
|
201
|
+
switch (ch) {
|
|
202
|
+
case '?':
|
|
203
|
+
return req_query_string_start;
|
|
204
|
+
case '#':
|
|
205
|
+
return req_fragment_start;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case req_query_string_start:
|
|
211
|
+
case req_query_string:
|
|
212
|
+
if (isNormalUrlChar(chi)) {
|
|
213
|
+
return req_query_string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
switch (ch) {
|
|
217
|
+
case '?':
|
|
218
|
+
/* allow extra '?' in query string */
|
|
219
|
+
return req_query_string;
|
|
220
|
+
|
|
221
|
+
case '#':
|
|
222
|
+
return req_fragment_start;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case req_fragment_start:
|
|
228
|
+
if (isNormalUrlChar(chi)) {
|
|
229
|
+
return req_fragment;
|
|
230
|
+
}
|
|
231
|
+
switch (ch) {
|
|
232
|
+
case '?':
|
|
233
|
+
return req_fragment;
|
|
234
|
+
|
|
235
|
+
case '#':
|
|
236
|
+
return req_fragment_start;
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
|
|
240
|
+
case req_fragment:
|
|
241
|
+
if (isNormalUrlChar(ch)) {
|
|
242
|
+
return req_fragment;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
switch (ch) {
|
|
246
|
+
case '?':
|
|
247
|
+
case '#':
|
|
248
|
+
return req_fragment;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
break;
|
|
252
|
+
default:
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* We should never fall out of the switch above unless there's an error */
|
|
257
|
+
return dead;
|
|
258
|
+
}
|
|
92
259
|
|
|
93
260
|
/** Execute the parser with the currently available data contained in
|
|
94
261
|
* the buffer. The buffers position() and limit() need to be set
|
|
95
262
|
* correctly (obviously) and a will be updated approriately when the
|
|
96
263
|
* method returns to reflect the consumed data.
|
|
97
264
|
*/
|
|
98
|
-
public
|
|
265
|
+
public int execute(ParserSettings settings, ByteBuffer data) {
|
|
99
266
|
|
|
100
267
|
int p = data.position();
|
|
101
|
-
|
|
268
|
+
this.p_start = p; // this is used for pretty printing errors.
|
|
269
|
+
// and returning the amount of processed bytes.
|
|
270
|
+
|
|
102
271
|
|
|
103
272
|
// In case the headers don't provide information about the content
|
|
104
273
|
// length, `execute` needs to be called with an empty buffer to
|
|
105
274
|
// indicate that all the data has been send be the client/server,
|
|
106
275
|
// else there is no way of knowing the message is complete.
|
|
107
276
|
int len = (data.limit() - data.position());
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
277
|
+
if (0 == len) {
|
|
278
|
+
// if (State.body_identity_eof == state) {
|
|
279
|
+
// settings.call_on_message_complete(this);
|
|
280
|
+
// }
|
|
281
|
+
switch (state) {
|
|
282
|
+
case body_identity_eof:
|
|
283
|
+
settings.call_on_message_complete(this);
|
|
284
|
+
return data.position() - this.p_start;
|
|
285
|
+
|
|
286
|
+
case dead:
|
|
287
|
+
case start_req_or_res:
|
|
288
|
+
case start_res:
|
|
289
|
+
case start_req:
|
|
290
|
+
return data.position() - this.p_start;
|
|
291
|
+
|
|
292
|
+
default:
|
|
293
|
+
// should we really consider this an error!?
|
|
294
|
+
throw new HTTPException("empty bytes! "+state); // error
|
|
295
|
+
}
|
|
296
|
+
}
|
|
113
297
|
|
|
114
298
|
|
|
115
299
|
// in case the _previous_ call to the parser only has data to get to
|
|
@@ -122,44 +306,52 @@ public class HTTPParser {
|
|
|
122
306
|
case header_value:
|
|
123
307
|
header_value_mark = p;
|
|
124
308
|
break;
|
|
125
|
-
case req_fragment:
|
|
126
|
-
fragment_mark = p;
|
|
127
|
-
url_mark = p;
|
|
128
|
-
break;
|
|
129
|
-
case req_query_string:
|
|
130
|
-
query_string_mark = p;
|
|
131
|
-
url_mark = p;
|
|
132
|
-
break;
|
|
133
309
|
case req_path:
|
|
134
|
-
path_mark = p;
|
|
135
|
-
|
|
136
|
-
case req_host:
|
|
137
310
|
case req_schema:
|
|
138
311
|
case req_schema_slash:
|
|
139
312
|
case req_schema_slash_slash:
|
|
313
|
+
case req_host_start:
|
|
314
|
+
case req_host_v6_start:
|
|
315
|
+
case req_host_v6:
|
|
316
|
+
case req_host_v6_end:
|
|
317
|
+
case req_host:
|
|
318
|
+
case req_port_start:
|
|
140
319
|
case req_port:
|
|
141
320
|
case req_query_string_start:
|
|
321
|
+
case req_query_string:
|
|
142
322
|
case req_fragment_start:
|
|
323
|
+
case req_fragment:
|
|
143
324
|
url_mark = p;
|
|
144
325
|
break;
|
|
145
326
|
}
|
|
327
|
+
boolean reexecute = false;
|
|
328
|
+
int pe = 0;
|
|
329
|
+
byte ch = 0;
|
|
330
|
+
int chi = 0;
|
|
331
|
+
byte c = -1;
|
|
332
|
+
int to_read = 0;
|
|
146
333
|
|
|
147
334
|
// this is where the work gets done, traverse the available data...
|
|
148
|
-
while (data.position() != data.limit()) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
335
|
+
while (data.position() != data.limit() || reexecute) {
|
|
336
|
+
// p(state + ": r: " + reexecute + " :: " +p );
|
|
337
|
+
|
|
338
|
+
if(!reexecute){
|
|
339
|
+
p = data.position();
|
|
340
|
+
pe = data.limit();
|
|
341
|
+
ch = data.get(); // the current character to process.
|
|
342
|
+
chi = ch & 0xff; // utility, ch without signedness for table lookups.
|
|
343
|
+
c = -1; // utility variably used for up- and downcasing etc.
|
|
344
|
+
to_read = 0; // used to keep track of how much of body, etc. is left to read
|
|
345
|
+
|
|
346
|
+
if (parsing_header(state)) {
|
|
347
|
+
++nread;
|
|
348
|
+
if (nread > HTTP_MAX_HEADER_SIZE) {
|
|
349
|
+
return error(settings, "possible buffer overflow", data);
|
|
350
|
+
}
|
|
161
351
|
}
|
|
162
352
|
}
|
|
353
|
+
reexecute = false;
|
|
354
|
+
// p(state + " ::: " + ch + " : " + (((CR == ch) || (LF == ch)) ? ch : ("'" + (char)ch + "'")) +": "+p );
|
|
163
355
|
|
|
164
356
|
switch (state) {
|
|
165
357
|
/*
|
|
@@ -167,29 +359,28 @@ public class HTTPParser {
|
|
|
167
359
|
* the parser will error out if it reads another message
|
|
168
360
|
*/
|
|
169
361
|
case dead:
|
|
170
|
-
|
|
362
|
+
if (CR == ch || LF == ch){
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
return error(settings, "Connection already closed", data);
|
|
171
366
|
|
|
172
367
|
|
|
173
368
|
|
|
174
|
-
case
|
|
369
|
+
case start_req_or_res:
|
|
175
370
|
if (CR == ch || LF == ch){
|
|
176
371
|
break;
|
|
177
372
|
}
|
|
178
373
|
flags = 0;
|
|
179
374
|
content_length = -1;
|
|
180
375
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (H == ch) {
|
|
376
|
+
if (H == ch) {
|
|
184
377
|
state = State.res_or_resp_H;
|
|
378
|
+
settings.call_on_message_begin(this);
|
|
185
379
|
} else {
|
|
186
|
-
type = ParserType.HTTP_REQUEST;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
index = 1;
|
|
192
|
-
state = State.req_method;
|
|
380
|
+
type = ParserType.HTTP_REQUEST;
|
|
381
|
+
state = State.start_req;
|
|
382
|
+
index = 1;
|
|
383
|
+
reexecute = true;
|
|
193
384
|
}
|
|
194
385
|
break;
|
|
195
386
|
|
|
@@ -201,7 +392,7 @@ public class HTTPParser {
|
|
|
201
392
|
state = State.res_HT;
|
|
202
393
|
} else {
|
|
203
394
|
if (E != ch) {
|
|
204
|
-
settings
|
|
395
|
+
return error(settings, "not E", data);
|
|
205
396
|
}
|
|
206
397
|
type = ParserType.HTTP_REQUEST;
|
|
207
398
|
method = HTTPMethod.HTTP_HEAD;
|
|
@@ -216,8 +407,6 @@ public class HTTPParser {
|
|
|
216
407
|
flags = 0;
|
|
217
408
|
content_length = -1;
|
|
218
409
|
|
|
219
|
-
settings.call_on_message_begin(this);
|
|
220
|
-
|
|
221
410
|
switch(ch) {
|
|
222
411
|
case H:
|
|
223
412
|
state = State.res_H;
|
|
@@ -226,33 +415,35 @@ public class HTTPParser {
|
|
|
226
415
|
case LF:
|
|
227
416
|
break;
|
|
228
417
|
default:
|
|
229
|
-
settings
|
|
418
|
+
return error(settings, "Not H or CR/LF", data);
|
|
230
419
|
}
|
|
420
|
+
|
|
421
|
+
settings.call_on_message_begin(this);
|
|
231
422
|
break;
|
|
232
423
|
|
|
233
424
|
|
|
234
425
|
|
|
235
426
|
case res_H:
|
|
236
427
|
if (strict && T != ch) {
|
|
237
|
-
settings
|
|
428
|
+
return error(settings, "Not T", data);
|
|
238
429
|
}
|
|
239
430
|
state = State.res_HT;
|
|
240
431
|
break;
|
|
241
432
|
case res_HT:
|
|
242
433
|
if (strict && T != ch) {
|
|
243
|
-
|
|
434
|
+
return error(settings, "Not T2", data);
|
|
244
435
|
}
|
|
245
436
|
state = State.res_HTT;
|
|
246
437
|
break;
|
|
247
438
|
case res_HTT:
|
|
248
439
|
if (strict && P != ch) {
|
|
249
|
-
|
|
440
|
+
return error(settings, "Not P", data);
|
|
250
441
|
}
|
|
251
442
|
state = State.res_HTTP;
|
|
252
443
|
break;
|
|
253
444
|
case res_HTTP:
|
|
254
445
|
if (strict && SLASH != ch) {
|
|
255
|
-
|
|
446
|
+
return error(settings, "Not '/'", data);
|
|
256
447
|
}
|
|
257
448
|
state = State.res_first_http_major;
|
|
258
449
|
break;
|
|
@@ -261,7 +452,7 @@ public class HTTPParser {
|
|
|
261
452
|
|
|
262
453
|
case res_first_http_major:
|
|
263
454
|
if (!isDigit(ch)) {
|
|
264
|
-
|
|
455
|
+
return error(settings, "Not a digit", data);
|
|
265
456
|
}
|
|
266
457
|
http_major = (int) ch - 0x30;
|
|
267
458
|
state = State.res_http_major;
|
|
@@ -270,24 +461,24 @@ public class HTTPParser {
|
|
|
270
461
|
/* major HTTP version or dot */
|
|
271
462
|
case res_http_major:
|
|
272
463
|
if (DOT == ch) {
|
|
273
|
-
state = State.
|
|
464
|
+
state = State.res_first_http_minor;
|
|
274
465
|
break;
|
|
275
466
|
}
|
|
276
467
|
if (!isDigit(ch)) {
|
|
277
|
-
|
|
468
|
+
return error(settings, "Not a digit", data);
|
|
278
469
|
}
|
|
279
470
|
http_major *= 10;
|
|
280
471
|
http_major += (ch - 0x30);
|
|
281
472
|
|
|
282
473
|
if (http_major > 999) {
|
|
283
|
-
|
|
474
|
+
return error(settings, "invalid http major version: ", data);
|
|
284
475
|
}
|
|
285
476
|
break;
|
|
286
477
|
|
|
287
478
|
/* first digit of minor HTTP version */
|
|
288
479
|
case res_first_http_minor:
|
|
289
480
|
if (!isDigit(ch)) {
|
|
290
|
-
|
|
481
|
+
return error(settings, "Not a digit", data);
|
|
291
482
|
}
|
|
292
483
|
http_minor = (int)ch - 0x30;
|
|
293
484
|
state = State.res_http_minor;
|
|
@@ -300,13 +491,12 @@ public class HTTPParser {
|
|
|
300
491
|
break;
|
|
301
492
|
}
|
|
302
493
|
if (!isDigit(ch)) {
|
|
303
|
-
|
|
494
|
+
return error(settings, "Not a digit", data);
|
|
304
495
|
}
|
|
305
496
|
http_minor *= 10;
|
|
306
497
|
http_minor += (ch - 0x30);
|
|
307
|
-
|
|
308
498
|
if (http_minor > 999) {
|
|
309
|
-
|
|
499
|
+
return error(settings, "invalid http minor version: ", data);
|
|
310
500
|
}
|
|
311
501
|
break;
|
|
312
502
|
|
|
@@ -317,7 +507,7 @@ public class HTTPParser {
|
|
|
317
507
|
if (SPACE == ch) {
|
|
318
508
|
break;
|
|
319
509
|
}
|
|
320
|
-
|
|
510
|
+
return error(settings, "Not a digit (status code)", data);
|
|
321
511
|
}
|
|
322
512
|
status_code = (int)ch - 0x30;
|
|
323
513
|
state = State.res_status_code;
|
|
@@ -336,14 +526,14 @@ public class HTTPParser {
|
|
|
336
526
|
state = State.header_field_start;
|
|
337
527
|
break;
|
|
338
528
|
default:
|
|
339
|
-
|
|
529
|
+
return error(settings, "not a valid status code", data);
|
|
340
530
|
}
|
|
341
531
|
break;
|
|
342
532
|
}
|
|
343
533
|
status_code *= 10;
|
|
344
534
|
status_code += (int)ch - 0x30;
|
|
345
535
|
if (status_code > 999) {
|
|
346
|
-
|
|
536
|
+
return error(settings, "ridiculous status code:", data);
|
|
347
537
|
}
|
|
348
538
|
break;
|
|
349
539
|
|
|
@@ -364,7 +554,7 @@ public class HTTPParser {
|
|
|
364
554
|
|
|
365
555
|
case res_line_almost_done:
|
|
366
556
|
if (strict && LF != ch) {
|
|
367
|
-
|
|
557
|
+
return error(settings, "not LF", data);
|
|
368
558
|
}
|
|
369
559
|
state = State.header_field_start;
|
|
370
560
|
break;
|
|
@@ -372,25 +562,31 @@ public class HTTPParser {
|
|
|
372
562
|
|
|
373
563
|
|
|
374
564
|
case start_req:
|
|
375
|
-
if (CR==ch || LF ==
|
|
565
|
+
if (CR==ch || LF == ch) {
|
|
376
566
|
break;
|
|
377
567
|
}
|
|
378
568
|
flags = 0;
|
|
379
569
|
content_length = -1;
|
|
380
|
-
|
|
570
|
+
|
|
571
|
+
if(!isAtoZ(ch)){
|
|
572
|
+
return error(settings, "invalid method", data);
|
|
573
|
+
}
|
|
574
|
+
|
|
381
575
|
method = start_req_method_assign(ch);
|
|
382
576
|
if (null == method) {
|
|
383
|
-
settings
|
|
577
|
+
return error(settings, "invalid method", data);
|
|
384
578
|
}
|
|
385
579
|
index = 1;
|
|
386
580
|
state = State.req_method;
|
|
581
|
+
|
|
582
|
+
settings.call_on_message_begin(this);
|
|
387
583
|
break;
|
|
388
584
|
|
|
389
585
|
|
|
390
586
|
|
|
391
587
|
case req_method:
|
|
392
588
|
if (0 == ch) {
|
|
393
|
-
settings
|
|
589
|
+
return error(settings, "NULL in method", data);
|
|
394
590
|
}
|
|
395
591
|
|
|
396
592
|
byte [] arr = method.bytes;
|
|
@@ -410,17 +606,33 @@ public class HTTPParser {
|
|
|
410
606
|
method = HTTPMethod.HTTP_MOVE;
|
|
411
607
|
} else if (1 == index && E == ch) {
|
|
412
608
|
method = HTTPMethod.HTTP_MERGE;
|
|
609
|
+
} else if (1 == index && DASH == ch) { /* M-SEARCH */
|
|
610
|
+
method = HTTPMethod.HTTP_MSEARCH;
|
|
413
611
|
} else if (2 == index && A == ch) {
|
|
414
612
|
method = HTTPMethod.HTTP_MKACTIVITY;
|
|
415
613
|
}
|
|
416
|
-
} else if (1 == index && HTTPMethod.HTTP_POST == method
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
614
|
+
} else if (1 == index && HTTPMethod.HTTP_POST == method) {
|
|
615
|
+
if(R == ch) {
|
|
616
|
+
method = HTTPMethod.HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
|
617
|
+
}else if(U == ch){
|
|
618
|
+
method = HTTPMethod.HTTP_PUT; /* or HTTP_PURGE */
|
|
619
|
+
}else if(A == ch){
|
|
620
|
+
method = HTTPMethod.HTTP_PATCH;
|
|
621
|
+
}
|
|
622
|
+
} else if (2 == index) {
|
|
623
|
+
if(HTTPMethod.HTTP_PUT == method) {
|
|
624
|
+
if(R == ch){
|
|
625
|
+
method = HTTPMethod.HTTP_PURGE;
|
|
626
|
+
}
|
|
627
|
+
}else if(HTTPMethod.HTTP_UNLOCK == method){
|
|
628
|
+
if(S == ch){
|
|
629
|
+
method = HTTPMethod.HTTP_UNSUBSCRIBE;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}else if(4 == index && HTTPMethod.HTTP_PROPFIND == method && P == ch){
|
|
421
633
|
method = HTTPMethod.HTTP_PROPPATCH;
|
|
422
634
|
} else {
|
|
423
|
-
settings
|
|
635
|
+
return error(settings, "Invalid HTTP method", data);
|
|
424
636
|
}
|
|
425
637
|
|
|
426
638
|
++index;
|
|
@@ -433,307 +645,68 @@ public class HTTPParser {
|
|
|
433
645
|
if (SPACE == ch) {
|
|
434
646
|
break;
|
|
435
647
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
state = State.req_path;
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
if (isAtoZ(ch)) {
|
|
443
|
-
url_mark = p;
|
|
444
|
-
state = State.req_schema;
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
settings.call_on_error(this, "Invalid something", data, p_err);
|
|
448
|
-
|
|
449
|
-
case req_schema:
|
|
450
|
-
if (isAtoZ(ch)){
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
if (COLON == ch) {
|
|
454
|
-
state = State.req_schema_slash;
|
|
455
|
-
break;
|
|
456
|
-
} else if (DOT == ch) {
|
|
457
|
-
state = State.req_host;
|
|
458
|
-
break;
|
|
648
|
+
url_mark = p;
|
|
649
|
+
if(HTTPMethod.HTTP_CONNECT == method){
|
|
650
|
+
state = req_host_start;
|
|
459
651
|
}
|
|
460
|
-
settings.call_on_error(this, "invalid char in schema: "+ch, data, p_err);
|
|
461
652
|
|
|
462
|
-
|
|
463
|
-
if
|
|
464
|
-
settings
|
|
653
|
+
state = parse_url_char(ch);
|
|
654
|
+
if(state == dead){
|
|
655
|
+
return error(settings, "Invalid something", data);
|
|
465
656
|
}
|
|
466
|
-
state = State.req_schema_slash_slash;
|
|
467
657
|
break;
|
|
468
658
|
|
|
469
|
-
case req_schema_slash_slash:
|
|
470
|
-
if (strict && SLASH != ch) {
|
|
471
|
-
settings.call_on_error(this, "invalid char in schema, not /", data, p_err);
|
|
472
|
-
}
|
|
473
|
-
state = State.req_host;
|
|
474
|
-
break;
|
|
475
|
-
|
|
476
|
-
case req_host:
|
|
477
|
-
if (isAtoZ(ch)) {
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
if (isDigit(ch) || DOT == ch || DASH == ch) break;
|
|
481
|
-
switch (ch) {
|
|
482
|
-
case COLON:
|
|
483
|
-
state = State.req_port;
|
|
484
|
-
break;
|
|
485
|
-
case SLASH:
|
|
486
|
-
path_mark = p;
|
|
487
|
-
break;
|
|
488
|
-
case SPACE:
|
|
489
|
-
/* The request line looks like:
|
|
490
|
-
* "GET http://foo.bar.com HTTP/1.1"
|
|
491
|
-
* That is, there is no path.
|
|
492
|
-
*/
|
|
493
|
-
settings.call_on_url(this, data, url_mark, p-url_mark);
|
|
494
|
-
url_mark = -1;
|
|
495
|
-
state = State.req_http_start;
|
|
496
|
-
break;
|
|
497
|
-
default:
|
|
498
|
-
settings.call_on_error(this, "host error in method line", data, p_err);
|
|
499
|
-
}
|
|
500
|
-
break;
|
|
501
659
|
|
|
502
|
-
case
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
case SPACE:
|
|
510
|
-
/* The request line looks like:
|
|
511
|
-
* "GET http://foo.bar.com:1234 HTTP/1.1"
|
|
512
|
-
* That is, there is no path.
|
|
513
|
-
*/
|
|
514
|
-
settings.call_on_url(this,data,url_mark,p-url_mark);
|
|
515
|
-
url_mark = -1;
|
|
516
|
-
state = State.req_http_start;
|
|
517
|
-
break;
|
|
518
|
-
default:
|
|
519
|
-
settings.call_on_error(this, "invalid port", data, p_err);
|
|
520
|
-
}
|
|
521
|
-
break;
|
|
522
|
-
|
|
523
|
-
case req_path:
|
|
524
|
-
if (normal_url_char[ch]) break;
|
|
660
|
+
case req_schema:
|
|
661
|
+
case req_schema_slash:
|
|
662
|
+
case req_schema_slash_slash:
|
|
663
|
+
case req_host_start:
|
|
664
|
+
case req_host_v6_start:
|
|
665
|
+
case req_host_v6:
|
|
666
|
+
case req_port_start:
|
|
525
667
|
switch (ch) {
|
|
668
|
+
/* No whitespace allowed here */
|
|
526
669
|
case SPACE:
|
|
527
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
528
|
-
url_mark = -1;
|
|
529
|
-
|
|
530
|
-
settings.call_on_path(this,data,path_mark, p-path_mark);
|
|
531
|
-
path_mark = -1;
|
|
532
|
-
|
|
533
|
-
state = State.req_http_start;
|
|
534
|
-
break;
|
|
535
|
-
|
|
536
|
-
case CR:
|
|
537
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
538
|
-
url_mark = -1;
|
|
539
|
-
|
|
540
|
-
settings.call_on_path(this,data,path_mark, p-path_mark);
|
|
541
|
-
path_mark = -1;
|
|
542
|
-
|
|
543
|
-
http_minor = 9;
|
|
544
|
-
state = State.res_line_almost_done;
|
|
545
|
-
break;
|
|
546
|
-
|
|
547
|
-
case LF:
|
|
548
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
549
|
-
url_mark = -1;
|
|
550
|
-
|
|
551
|
-
settings.call_on_path(this,data,path_mark, p-path_mark);
|
|
552
|
-
path_mark = -1;
|
|
553
|
-
|
|
554
|
-
http_minor = 9;
|
|
555
|
-
state = State.header_field_start;
|
|
556
|
-
break;
|
|
557
|
-
|
|
558
|
-
case QMARK:
|
|
559
|
-
settings.call_on_path(this,data,path_mark, p-path_mark);
|
|
560
|
-
path_mark = -1;
|
|
561
|
-
|
|
562
|
-
state = State.req_query_string_start;
|
|
563
|
-
break;
|
|
564
|
-
|
|
565
|
-
case HASH:
|
|
566
|
-
settings.call_on_path(this,data,path_mark, p-path_mark);
|
|
567
|
-
path_mark = -1;
|
|
568
|
-
|
|
569
|
-
state = State.req_fragment_start;
|
|
570
|
-
break;
|
|
571
|
-
|
|
572
|
-
default:
|
|
573
|
-
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
|
574
|
-
}
|
|
575
|
-
break;
|
|
576
|
-
|
|
577
|
-
case req_query_string_start:
|
|
578
|
-
if (normal_url_char[ch]) {
|
|
579
|
-
query_string_mark = p;
|
|
580
|
-
state = State.req_query_string;
|
|
581
|
-
break;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
switch (ch) {
|
|
585
|
-
case QMARK: break;
|
|
586
|
-
case SPACE:
|
|
587
|
-
settings.call_on_url(this, data, url_mark, p-url_mark);
|
|
588
|
-
url_mark = -1;
|
|
589
|
-
state = State.req_http_start;
|
|
590
|
-
break;
|
|
591
|
-
case CR:
|
|
592
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
593
|
-
url_mark = -1;
|
|
594
|
-
http_minor = 9;
|
|
595
|
-
state = State.res_line_almost_done;
|
|
596
|
-
break;
|
|
597
|
-
case LF:
|
|
598
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
599
|
-
url_mark = -1;
|
|
600
|
-
http_minor = 9;
|
|
601
|
-
state = State.header_field_start;
|
|
602
|
-
break;
|
|
603
|
-
case HASH:
|
|
604
|
-
state = State.req_fragment_start;
|
|
605
|
-
break;
|
|
606
|
-
default:
|
|
607
|
-
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
|
608
|
-
}
|
|
609
|
-
break;
|
|
610
|
-
|
|
611
|
-
case req_query_string:
|
|
612
|
-
if (normal_url_char[ch]) {
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
switch (ch) {
|
|
617
|
-
case QMARK: break; // allow extra '?' in query string
|
|
618
|
-
case SPACE:
|
|
619
|
-
settings.call_on_url(this, data, url_mark, p-url_mark);
|
|
620
|
-
url_mark = -1;
|
|
621
|
-
|
|
622
|
-
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
|
623
|
-
query_string_mark = -1;
|
|
624
|
-
|
|
625
|
-
state = State.req_http_start;
|
|
626
|
-
break;
|
|
627
|
-
case CR:
|
|
628
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
629
|
-
url_mark = -1;
|
|
630
|
-
|
|
631
|
-
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
|
632
|
-
query_string_mark = -1;
|
|
633
|
-
|
|
634
|
-
http_minor = 9;
|
|
635
|
-
state = State.res_line_almost_done;
|
|
636
|
-
break;
|
|
637
|
-
case LF:
|
|
638
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
639
|
-
url_mark = -1;
|
|
640
|
-
|
|
641
|
-
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
|
642
|
-
query_string_mark = -1;
|
|
643
|
-
http_minor = 9;
|
|
644
|
-
|
|
645
|
-
state = State.header_field_start;
|
|
646
|
-
break;
|
|
647
|
-
case HASH:
|
|
648
|
-
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
|
649
|
-
query_string_mark = -1;
|
|
650
|
-
|
|
651
|
-
state = State.req_fragment_start;
|
|
652
|
-
break;
|
|
653
|
-
default:
|
|
654
|
-
settings.call_on_error(this, "unexpected char in path", data, p_err);
|
|
655
|
-
}
|
|
656
|
-
break;
|
|
657
|
-
|
|
658
|
-
case req_fragment_start:
|
|
659
|
-
if (normal_url_char[ch]) {
|
|
660
|
-
fragment_mark = p;
|
|
661
|
-
state = State.req_fragment;
|
|
662
|
-
break;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
switch (ch) {
|
|
666
|
-
case SPACE:
|
|
667
|
-
settings.call_on_url(this, data, url_mark, p-url_mark);
|
|
668
|
-
url_mark = -1;
|
|
669
|
-
|
|
670
|
-
state = State.req_http_start;
|
|
671
|
-
break;
|
|
672
670
|
case CR:
|
|
673
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
674
|
-
url_mark = -1;
|
|
675
|
-
|
|
676
|
-
http_minor = 9;
|
|
677
|
-
state = State.res_line_almost_done;
|
|
678
|
-
break;
|
|
679
671
|
case LF:
|
|
680
|
-
settings
|
|
681
|
-
url_mark = -1;
|
|
682
|
-
|
|
683
|
-
http_minor = 9;
|
|
684
|
-
state = State.header_field_start;
|
|
685
|
-
break;
|
|
686
|
-
case QMARK:
|
|
687
|
-
fragment_mark = p;
|
|
688
|
-
state = State.req_fragment;
|
|
689
|
-
break;
|
|
690
|
-
case HASH:
|
|
691
|
-
break;
|
|
672
|
+
return error(settings, "unexpected char in path", data);
|
|
692
673
|
default:
|
|
693
|
-
|
|
674
|
+
state = parse_url_char(ch);
|
|
675
|
+
if(dead == state){
|
|
676
|
+
return error(settings, "unexpected char in path", data);
|
|
677
|
+
}
|
|
694
678
|
}
|
|
695
679
|
break;
|
|
696
680
|
|
|
681
|
+
case req_host:
|
|
682
|
+
case req_host_v6_end:
|
|
683
|
+
case req_port:
|
|
684
|
+
case req_path:
|
|
685
|
+
case req_query_string_start:
|
|
686
|
+
case req_query_string:
|
|
687
|
+
case req_fragment_start:
|
|
697
688
|
case req_fragment:
|
|
698
|
-
if (normal_url_char[ch]) {
|
|
699
|
-
break;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
689
|
switch (ch) {
|
|
703
690
|
case SPACE:
|
|
704
691
|
settings.call_on_url(this, data, url_mark, p-url_mark);
|
|
692
|
+
settings.call_on_path(this, data, url_mark, p - url_mark);
|
|
705
693
|
url_mark = -1;
|
|
706
|
-
|
|
707
|
-
settings.call_on_fragment(this, data, fragment_mark, p-fragment_mark);
|
|
708
|
-
fragment_mark = -1;
|
|
709
|
-
|
|
710
694
|
state = State.req_http_start;
|
|
711
695
|
break;
|
|
712
696
|
case CR:
|
|
713
|
-
settings.call_on_url(this,data,url_mark, p-url_mark);
|
|
714
|
-
url_mark = -1;
|
|
715
|
-
|
|
716
|
-
settings.call_on_fragment(this, data, query_string_mark, p-query_string_mark);
|
|
717
|
-
fragment_mark = -1;
|
|
718
|
-
|
|
719
|
-
http_minor = 9;
|
|
720
|
-
state = State.res_line_almost_done;
|
|
721
|
-
break;
|
|
722
697
|
case LF:
|
|
723
|
-
|
|
724
|
-
url_mark = -1;
|
|
725
|
-
|
|
726
|
-
settings.call_on_fragment(this, data, query_string_mark, p-query_string_mark);
|
|
727
|
-
fragment_mark = -1;
|
|
728
|
-
|
|
698
|
+
http_major = 0;
|
|
729
699
|
http_minor = 9;
|
|
730
|
-
state =
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
700
|
+
state = (CR == ch) ? req_line_almost_done : header_field_start;
|
|
701
|
+
settings.call_on_url(this, data, url_mark, p-url_mark); //TODO check params!!!
|
|
702
|
+
settings.call_on_path(this, data, url_mark, p-url_mark);
|
|
703
|
+
url_mark = -1;
|
|
734
704
|
break;
|
|
735
705
|
default:
|
|
736
|
-
|
|
706
|
+
state = parse_url_char(ch);
|
|
707
|
+
if(dead == state){
|
|
708
|
+
return error(settings, "unexpected char in path", data);
|
|
709
|
+
}
|
|
737
710
|
}
|
|
738
711
|
break;
|
|
739
712
|
/******************* URL *******************/
|
|
@@ -749,34 +722,34 @@ public class HTTPParser {
|
|
|
749
722
|
case SPACE:
|
|
750
723
|
break;
|
|
751
724
|
default:
|
|
752
|
-
settings
|
|
725
|
+
return error(settings, "error in req_http_H", data);
|
|
753
726
|
}
|
|
754
727
|
break;
|
|
755
728
|
|
|
756
729
|
case req_http_H:
|
|
757
730
|
if (strict && T != ch) {
|
|
758
|
-
settings
|
|
731
|
+
return error(settings, "unexpected char", data);
|
|
759
732
|
}
|
|
760
733
|
state = State.req_http_HT;
|
|
761
734
|
break;
|
|
762
735
|
|
|
763
736
|
case req_http_HT:
|
|
764
737
|
if (strict && T != ch) {
|
|
765
|
-
settings
|
|
738
|
+
return error(settings, "unexpected char", data);
|
|
766
739
|
}
|
|
767
740
|
state = State.req_http_HTT;
|
|
768
741
|
break;
|
|
769
742
|
|
|
770
743
|
case req_http_HTT:
|
|
771
744
|
if (strict && P != ch) {
|
|
772
|
-
settings
|
|
745
|
+
return error(settings, "unexpected char", data);
|
|
773
746
|
}
|
|
774
747
|
state = State.req_http_HTTP;
|
|
775
748
|
break;
|
|
776
749
|
|
|
777
750
|
case req_http_HTTP:
|
|
778
751
|
if (strict && SLASH != ch) {
|
|
779
|
-
settings
|
|
752
|
+
return error(settings, "unexpected char", data);
|
|
780
753
|
}
|
|
781
754
|
state = req_first_http_major;
|
|
782
755
|
break;
|
|
@@ -784,7 +757,7 @@ public class HTTPParser {
|
|
|
784
757
|
/* first digit of major HTTP version */
|
|
785
758
|
case req_first_http_major:
|
|
786
759
|
if (!isDigit(ch)) {
|
|
787
|
-
|
|
760
|
+
return error(settings, "non digit in http major", data);
|
|
788
761
|
}
|
|
789
762
|
http_major = (int)ch - 0x30;
|
|
790
763
|
state = State.req_http_major;
|
|
@@ -798,21 +771,21 @@ public class HTTPParser {
|
|
|
798
771
|
}
|
|
799
772
|
|
|
800
773
|
if (!isDigit(ch)) {
|
|
801
|
-
|
|
774
|
+
return error(settings, "non digit in http major", data);
|
|
802
775
|
}
|
|
803
776
|
|
|
804
777
|
http_major *= 10;
|
|
805
778
|
http_major += (int)ch - 0x30;
|
|
806
779
|
|
|
807
780
|
if (http_major > 999) {
|
|
808
|
-
|
|
781
|
+
return error(settings, "ridiculous http major", data);
|
|
809
782
|
};
|
|
810
783
|
break;
|
|
811
784
|
|
|
812
785
|
/* first digit of minor HTTP version */
|
|
813
786
|
case req_first_http_minor:
|
|
814
787
|
if (!isDigit(ch)) {
|
|
815
|
-
|
|
788
|
+
return error(settings, "non digit in http minor", data);
|
|
816
789
|
}
|
|
817
790
|
http_minor = (int)ch - 0x30;
|
|
818
791
|
state = State.req_http_minor;
|
|
@@ -832,7 +805,7 @@ public class HTTPParser {
|
|
|
832
805
|
/* XXX allow spaces after digit? */
|
|
833
806
|
|
|
834
807
|
if (!isDigit(ch)) {
|
|
835
|
-
|
|
808
|
+
return error(settings, "non digit in http minor", data);
|
|
836
809
|
}
|
|
837
810
|
|
|
838
811
|
http_minor *= 10;
|
|
@@ -840,7 +813,7 @@ public class HTTPParser {
|
|
|
840
813
|
|
|
841
814
|
|
|
842
815
|
if (http_minor > 999) {
|
|
843
|
-
|
|
816
|
+
return error(settings, "ridiculous http minor", data);
|
|
844
817
|
};
|
|
845
818
|
|
|
846
819
|
break;
|
|
@@ -849,9 +822,9 @@ public class HTTPParser {
|
|
|
849
822
|
case req_line_almost_done:
|
|
850
823
|
{
|
|
851
824
|
if (ch != LF) {
|
|
852
|
-
|
|
825
|
+
return error(settings, "missing LF after request line", data);
|
|
853
826
|
}
|
|
854
|
-
state =
|
|
827
|
+
state = header_field_start;
|
|
855
828
|
break;
|
|
856
829
|
}
|
|
857
830
|
|
|
@@ -863,7 +836,7 @@ public class HTTPParser {
|
|
|
863
836
|
case header_field_start:
|
|
864
837
|
{
|
|
865
838
|
if (ch == CR) {
|
|
866
|
-
state =
|
|
839
|
+
state = headers_almost_done;
|
|
867
840
|
break;
|
|
868
841
|
}
|
|
869
842
|
|
|
@@ -871,17 +844,15 @@ public class HTTPParser {
|
|
|
871
844
|
/* they might be just sending \n instead of \r\n so this would be
|
|
872
845
|
* the second \n to denote the end of headers*/
|
|
873
846
|
state = State.headers_almost_done;
|
|
874
|
-
|
|
875
|
-
settings.call_on_error(this, "header not properly completed", data, p_err);
|
|
876
|
-
}
|
|
847
|
+
reexecute = true;
|
|
877
848
|
break;
|
|
878
849
|
}
|
|
879
850
|
|
|
880
|
-
c =
|
|
851
|
+
c = token(ch);
|
|
881
852
|
|
|
882
|
-
if (
|
|
883
|
-
settings
|
|
884
|
-
}
|
|
853
|
+
if (0 == c) {
|
|
854
|
+
return error(settings, "invalid char in header:", data);
|
|
855
|
+
}
|
|
885
856
|
|
|
886
857
|
header_field_mark = p;
|
|
887
858
|
|
|
@@ -916,7 +887,7 @@ public class HTTPParser {
|
|
|
916
887
|
|
|
917
888
|
case header_field:
|
|
918
889
|
{
|
|
919
|
-
c = (
|
|
890
|
+
c = token(ch);
|
|
920
891
|
if (0 != c) {
|
|
921
892
|
switch (header_state) {
|
|
922
893
|
case general:
|
|
@@ -1010,7 +981,7 @@ public class HTTPParser {
|
|
|
1010
981
|
break;
|
|
1011
982
|
|
|
1012
983
|
default:
|
|
1013
|
-
|
|
984
|
+
return error(settings, "Unknown Header State", data);
|
|
1014
985
|
} // switch: header_state
|
|
1015
986
|
break;
|
|
1016
987
|
} // 0 != c
|
|
@@ -1039,44 +1010,41 @@ public class HTTPParser {
|
|
|
1039
1010
|
break;
|
|
1040
1011
|
}
|
|
1041
1012
|
|
|
1042
|
-
|
|
1013
|
+
return error(settings, "invalid header field", data);
|
|
1043
1014
|
}
|
|
1044
1015
|
|
|
1045
1016
|
|
|
1046
1017
|
|
|
1047
1018
|
case header_value_start:
|
|
1048
1019
|
{
|
|
1049
|
-
if (SPACE == ch) break;
|
|
1020
|
+
if ((SPACE == ch) || (TAB == ch)) break;
|
|
1050
1021
|
|
|
1051
1022
|
header_value_mark = p;
|
|
1052
1023
|
|
|
1053
1024
|
state = State.header_value;
|
|
1054
1025
|
index = 0;
|
|
1055
1026
|
|
|
1056
|
-
c = (byte)acceptable_header[ch];
|
|
1057
|
-
|
|
1058
|
-
if (c == 0) {
|
|
1059
|
-
if (CR == ch) {
|
|
1060
|
-
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
|
1061
|
-
header_value_mark = -1;
|
|
1062
|
-
|
|
1063
|
-
header_state = HState.general;
|
|
1064
|
-
state = State.header_almost_done;
|
|
1065
|
-
break;
|
|
1066
|
-
}
|
|
1067
1027
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
state = State.header_field_start;
|
|
1073
|
-
break;
|
|
1074
|
-
}
|
|
1028
|
+
if (CR == ch) {
|
|
1029
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
|
1030
|
+
header_value_mark = -1;
|
|
1075
1031
|
|
|
1076
1032
|
header_state = HState.general;
|
|
1033
|
+
state = State.header_almost_done;
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if (LF == ch) {
|
|
1038
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
|
1039
|
+
header_value_mark = -1;
|
|
1040
|
+
|
|
1041
|
+
state = State.header_field_start;
|
|
1077
1042
|
break;
|
|
1078
1043
|
}
|
|
1079
1044
|
|
|
1045
|
+
|
|
1046
|
+
c = upper(ch);
|
|
1047
|
+
|
|
1080
1048
|
switch (header_state) {
|
|
1081
1049
|
case upgrade:
|
|
1082
1050
|
flags |= F_UPGRADE;
|
|
@@ -1094,7 +1062,7 @@ public class HTTPParser {
|
|
|
1094
1062
|
|
|
1095
1063
|
case content_length:
|
|
1096
1064
|
if (!isDigit(ch)) {
|
|
1097
|
-
|
|
1065
|
+
return error(settings, "Content-Length not numeric", data);
|
|
1098
1066
|
}
|
|
1099
1067
|
content_length = (int)ch - 0x30;
|
|
1100
1068
|
break;
|
|
@@ -1122,47 +1090,50 @@ public class HTTPParser {
|
|
|
1122
1090
|
|
|
1123
1091
|
case header_value:
|
|
1124
1092
|
{
|
|
1125
|
-
c = (byte)acceptable_header[ch];
|
|
1126
1093
|
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
header_value_mark = -1;
|
|
1094
|
+
if (CR == ch) {
|
|
1095
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
|
1096
|
+
header_value_mark = -1;
|
|
1131
1097
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1098
|
+
state = State.header_almost_done;
|
|
1099
|
+
break;
|
|
1100
|
+
}
|
|
1135
1101
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
settings.call_on_error(this,"incorrect header ending, expection LF", data, p_err);
|
|
1142
|
-
}
|
|
1143
|
-
break;
|
|
1144
|
-
}
|
|
1102
|
+
if (LF == ch) {
|
|
1103
|
+
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
|
1104
|
+
header_value_mark = -1;
|
|
1105
|
+
state = header_almost_done;
|
|
1106
|
+
reexecute = true;
|
|
1145
1107
|
break;
|
|
1146
1108
|
}
|
|
1147
1109
|
|
|
1110
|
+
c = upper(ch);
|
|
1148
1111
|
switch (header_state) {
|
|
1149
1112
|
case general:
|
|
1150
1113
|
break;
|
|
1151
1114
|
|
|
1152
1115
|
case connection:
|
|
1153
1116
|
case transfer_encoding:
|
|
1154
|
-
|
|
1117
|
+
return error(settings, "Shouldn't be here", data);
|
|
1155
1118
|
|
|
1156
1119
|
case content_length:
|
|
1157
1120
|
if (SPACE == ch) {
|
|
1158
1121
|
break;
|
|
1159
1122
|
}
|
|
1160
1123
|
if (!isDigit(ch)) {
|
|
1161
|
-
|
|
1124
|
+
return error(settings, "Content-Length not numeric", data);
|
|
1162
1125
|
}
|
|
1163
1126
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1127
|
+
long t = content_length;
|
|
1128
|
+
t *= 10;
|
|
1129
|
+
t += (long)ch - 0x30;
|
|
1130
|
+
|
|
1131
|
+
/* Overflow? */
|
|
1132
|
+
// t will wrap and become negative ...
|
|
1133
|
+
if (t < content_length) {
|
|
1134
|
+
return error(settings, "Invalid content length", data);
|
|
1135
|
+
}
|
|
1136
|
+
content_length = t;
|
|
1166
1137
|
break;
|
|
1167
1138
|
|
|
1168
1139
|
/* Transfer-Encoding: chunked */
|
|
@@ -1213,16 +1184,121 @@ public class HTTPParser {
|
|
|
1213
1184
|
|
|
1214
1185
|
case header_almost_done:
|
|
1215
1186
|
if (!header_almost_done(ch)) {
|
|
1216
|
-
settings
|
|
1187
|
+
return error(settings, "incorrect header ending, expecting LF", data);
|
|
1217
1188
|
}
|
|
1218
1189
|
break;
|
|
1219
1190
|
|
|
1220
|
-
case
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
1191
|
+
case header_value_lws:
|
|
1192
|
+
if (SPACE == ch || TAB == ch ){
|
|
1193
|
+
state = header_value_start;
|
|
1194
|
+
} else {
|
|
1195
|
+
state = header_field_start;
|
|
1196
|
+
reexecute = true;
|
|
1223
1197
|
}
|
|
1224
1198
|
break;
|
|
1225
1199
|
|
|
1200
|
+
case headers_almost_done:
|
|
1201
|
+
if (LF != ch) {
|
|
1202
|
+
return error(settings, "header not properly completed", data);
|
|
1203
|
+
}
|
|
1204
|
+
if (0 != (flags & F_TRAILING)) {
|
|
1205
|
+
/* End of a chunked request */
|
|
1206
|
+
state = new_message();
|
|
1207
|
+
settings.call_on_headers_complete(this);
|
|
1208
|
+
settings.call_on_message_complete(this);
|
|
1209
|
+
break;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
state = headers_done;
|
|
1213
|
+
|
|
1214
|
+
if (0 != (flags & F_UPGRADE) || HTTPMethod.HTTP_CONNECT == method) {
|
|
1215
|
+
upgrade = true;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/* Here we call the headers_complete callback. This is somewhat
|
|
1219
|
+
* different than other callbacks because if the user returns 1, we
|
|
1220
|
+
* will interpret that as saying that this message has no body. This
|
|
1221
|
+
* is needed for the annoying case of receiving a response to a HEAD
|
|
1222
|
+
* request.
|
|
1223
|
+
*/
|
|
1224
|
+
|
|
1225
|
+
/* (responses to HEAD request contain a CONTENT-LENGTH header
|
|
1226
|
+
* but no content)
|
|
1227
|
+
*
|
|
1228
|
+
* Consider what to do here: I don't like the idea of the callback
|
|
1229
|
+
* interface having a different contract in the case of HEAD
|
|
1230
|
+
* responses. The alternatives would be either to:
|
|
1231
|
+
*
|
|
1232
|
+
* a.) require the header_complete callback to implement a different
|
|
1233
|
+
* interface or
|
|
1234
|
+
*
|
|
1235
|
+
* b.) provide an overridden execute(bla, bla, boolean
|
|
1236
|
+
* parsingHeader) implementation ...
|
|
1237
|
+
*/
|
|
1238
|
+
|
|
1239
|
+
// /*TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO */
|
|
1240
|
+
// if (null != settings.on_headers_complete) {
|
|
1241
|
+
// settings.call_on_headers_complete(this);
|
|
1242
|
+
// //return;
|
|
1243
|
+
// }
|
|
1244
|
+
|
|
1245
|
+
if (null != settings.on_headers_complete) {
|
|
1246
|
+
switch (settings.on_headers_complete.cb(this)) {
|
|
1247
|
+
case 0:
|
|
1248
|
+
break;
|
|
1249
|
+
|
|
1250
|
+
case 1:
|
|
1251
|
+
flags |= F_SKIPBODY;
|
|
1252
|
+
break;
|
|
1253
|
+
|
|
1254
|
+
default:
|
|
1255
|
+
return error(settings, "HPE_CB_headers_complete", data); /* Error */
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
reexecute = true;
|
|
1259
|
+
break;
|
|
1260
|
+
|
|
1261
|
+
case headers_done:
|
|
1262
|
+
if (strict && (LF != ch)) {
|
|
1263
|
+
return error(settings, "STRICT CHECK", data); //TODO correct error msg
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
nread = 0;
|
|
1267
|
+
|
|
1268
|
+
// Exit, the rest of the connect is in a different protocol.
|
|
1269
|
+
if (upgrade) {
|
|
1270
|
+
state = new_message();
|
|
1271
|
+
settings.call_on_message_complete(this);
|
|
1272
|
+
return data.position()-this.p_start;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
if (0 != (flags & F_SKIPBODY)) {
|
|
1276
|
+
state = new_message();
|
|
1277
|
+
settings.call_on_message_complete(this);
|
|
1278
|
+
} else if (0 != (flags & F_CHUNKED)) {
|
|
1279
|
+
/* chunked encoding - ignore Content-Length header */
|
|
1280
|
+
state = State.chunk_size_start;
|
|
1281
|
+
} else {
|
|
1282
|
+
if (content_length == 0) {
|
|
1283
|
+
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
|
1284
|
+
state = new_message();
|
|
1285
|
+
settings.call_on_message_complete(this);
|
|
1286
|
+
} else if (content_length != -1) {
|
|
1287
|
+
/* Content-Length header given and non-zero */
|
|
1288
|
+
state = State.body_identity;
|
|
1289
|
+
} else {
|
|
1290
|
+
if (type == ParserType.HTTP_REQUEST || !http_message_needs_eof()) {
|
|
1291
|
+
/* Assume content-length 0 - read the next */
|
|
1292
|
+
state = new_message();
|
|
1293
|
+
settings.call_on_message_complete(this);
|
|
1294
|
+
} else {
|
|
1295
|
+
/* Read body until EOF */
|
|
1296
|
+
state = State.body_identity_eof;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
break;
|
|
1226
1302
|
/******************* Header *******************/
|
|
1227
1303
|
|
|
1228
1304
|
|
|
@@ -1230,15 +1306,18 @@ public class HTTPParser {
|
|
|
1230
1306
|
|
|
1231
1307
|
/******************* Body *******************/
|
|
1232
1308
|
case body_identity:
|
|
1233
|
-
to_read = min(pe - p, content_length); //TODO change to use buffer?
|
|
1309
|
+
to_read = min(pe - p, content_length); //TODO change to use buffer?
|
|
1310
|
+
body_mark = p;
|
|
1234
1311
|
|
|
1235
1312
|
if (to_read > 0) {
|
|
1236
|
-
settings.call_on_body(this, data, p, to_read);
|
|
1313
|
+
settings.call_on_body(this, data, p, to_read);
|
|
1237
1314
|
data.position(p+to_read);
|
|
1238
1315
|
content_length -= to_read;
|
|
1316
|
+
|
|
1239
1317
|
if (content_length == 0) {
|
|
1240
|
-
|
|
1241
|
-
|
|
1318
|
+
state = message_done;
|
|
1319
|
+
p += to_read;
|
|
1320
|
+
reexecute = true;
|
|
1242
1321
|
}
|
|
1243
1322
|
}
|
|
1244
1323
|
break;
|
|
@@ -1248,23 +1327,32 @@ public class HTTPParser {
|
|
|
1248
1327
|
case body_identity_eof:
|
|
1249
1328
|
to_read = pe - p; // TODO change to use buffer ?
|
|
1250
1329
|
if (to_read > 0) {
|
|
1251
|
-
settings.call_on_body(this, data, p, to_read);
|
|
1330
|
+
settings.call_on_body(this, data, p, to_read);
|
|
1252
1331
|
data.position(p+to_read);
|
|
1253
1332
|
}
|
|
1254
1333
|
break;
|
|
1334
|
+
|
|
1335
|
+
case message_done:
|
|
1336
|
+
state = new_message();
|
|
1337
|
+
settings.call_on_message_complete(this);
|
|
1338
|
+
break;
|
|
1255
1339
|
/******************* Body *******************/
|
|
1256
1340
|
|
|
1257
1341
|
|
|
1258
1342
|
|
|
1259
1343
|
/******************* Chunk *******************/
|
|
1260
1344
|
case chunk_size_start:
|
|
1345
|
+
if (1 != this.nread) {
|
|
1346
|
+
return error(settings, "nread != 1 (chunking)", data);
|
|
1347
|
+
|
|
1348
|
+
}
|
|
1261
1349
|
if (0 == (flags & F_CHUNKED)) {
|
|
1262
|
-
|
|
1350
|
+
return error(settings, "not chunked", data);
|
|
1263
1351
|
}
|
|
1264
1352
|
|
|
1265
|
-
c = UNHEX[
|
|
1353
|
+
c = UNHEX[chi];
|
|
1266
1354
|
if (c == -1) {
|
|
1267
|
-
|
|
1355
|
+
return error(settings, "invalid hex char in chunk content length", data);
|
|
1268
1356
|
}
|
|
1269
1357
|
content_length = c;
|
|
1270
1358
|
state = State.chunk_size;
|
|
@@ -1274,7 +1362,7 @@ public class HTTPParser {
|
|
|
1274
1362
|
|
|
1275
1363
|
case chunk_size:
|
|
1276
1364
|
if (0 == (flags & F_CHUNKED)) {
|
|
1277
|
-
settings
|
|
1365
|
+
return error(settings, "not chunked", data);
|
|
1278
1366
|
}
|
|
1279
1367
|
|
|
1280
1368
|
if (CR == ch) {
|
|
@@ -1282,25 +1370,30 @@ public class HTTPParser {
|
|
|
1282
1370
|
break;
|
|
1283
1371
|
}
|
|
1284
1372
|
|
|
1285
|
-
c = UNHEX[
|
|
1373
|
+
c = UNHEX[chi];
|
|
1286
1374
|
|
|
1287
1375
|
if (c == -1) {
|
|
1288
1376
|
if (SEMI == ch || SPACE == ch) {
|
|
1289
1377
|
state = State.chunk_parameters;
|
|
1290
1378
|
break;
|
|
1291
1379
|
}
|
|
1292
|
-
settings
|
|
1380
|
+
return error(settings, "invalid hex char in chunk content length", data);
|
|
1293
1381
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1382
|
+
long t = content_length;
|
|
1383
|
+
|
|
1384
|
+
t *= 16;
|
|
1385
|
+
t += c;
|
|
1386
|
+
if(t < content_length){
|
|
1387
|
+
return error(settings, "invalid content length", data);
|
|
1388
|
+
}
|
|
1389
|
+
content_length = t;
|
|
1297
1390
|
break;
|
|
1298
1391
|
|
|
1299
1392
|
|
|
1300
1393
|
|
|
1301
1394
|
case chunk_parameters:
|
|
1302
1395
|
if (0 == (flags & F_CHUNKED)) {
|
|
1303
|
-
|
|
1396
|
+
return error(settings, "not chunked", data);
|
|
1304
1397
|
}
|
|
1305
1398
|
/* just ignore this shit. TODO check for overflow */
|
|
1306
1399
|
if (CR == ch) {
|
|
@@ -1313,12 +1406,14 @@ public class HTTPParser {
|
|
|
1313
1406
|
|
|
1314
1407
|
case chunk_size_almost_done:
|
|
1315
1408
|
if (0 == (flags & F_CHUNKED)) {
|
|
1316
|
-
|
|
1409
|
+
return error(settings, "not chunked", data);
|
|
1317
1410
|
}
|
|
1318
1411
|
if (strict && LF != ch) {
|
|
1319
|
-
|
|
1412
|
+
return error(settings, "expected LF at end of chunk size", data);
|
|
1320
1413
|
}
|
|
1321
1414
|
|
|
1415
|
+
this.nread = 0;
|
|
1416
|
+
|
|
1322
1417
|
if (0 == content_length) {
|
|
1323
1418
|
flags |= F_TRAILING;
|
|
1324
1419
|
state = State.header_field_start;
|
|
@@ -1330,9 +1425,9 @@ public class HTTPParser {
|
|
|
1330
1425
|
|
|
1331
1426
|
|
|
1332
1427
|
case chunk_data:
|
|
1333
|
-
|
|
1428
|
+
//TODO Apply changes from C version for s_chunk_data
|
|
1334
1429
|
if (0 == (flags & F_CHUNKED)) {
|
|
1335
|
-
settings
|
|
1430
|
+
return error(settings, "not chunked", data);
|
|
1336
1431
|
}
|
|
1337
1432
|
|
|
1338
1433
|
to_read = min(pe-p, content_length);
|
|
@@ -1347,28 +1442,29 @@ public class HTTPParser {
|
|
|
1347
1442
|
|
|
1348
1443
|
content_length -= to_read;
|
|
1349
1444
|
break;
|
|
1350
|
-
}
|
|
1351
1445
|
|
|
1352
1446
|
|
|
1353
1447
|
|
|
1354
1448
|
case chunk_data_almost_done:
|
|
1355
1449
|
if (0 == (flags & F_CHUNKED)) {
|
|
1356
|
-
|
|
1450
|
+
return error(settings, "not chunked", data);
|
|
1357
1451
|
}
|
|
1358
1452
|
if (strict && CR != ch) {
|
|
1359
|
-
|
|
1453
|
+
return error(settings, "chunk data terminated incorrectly, expected CR", data);
|
|
1360
1454
|
}
|
|
1361
1455
|
state = State.chunk_data_done;
|
|
1456
|
+
//TODO CALLBACK_DATA(body)
|
|
1457
|
+
// settings.call_on_body(this, data,p,?);
|
|
1362
1458
|
break;
|
|
1363
1459
|
|
|
1364
1460
|
|
|
1365
1461
|
|
|
1366
1462
|
case chunk_data_done:
|
|
1367
1463
|
if (0 == (flags & F_CHUNKED)) {
|
|
1368
|
-
|
|
1464
|
+
return error(settings, "not chunked", data);
|
|
1369
1465
|
}
|
|
1370
1466
|
if (strict && LF != ch) {
|
|
1371
|
-
|
|
1467
|
+
return error(settings, "chunk data terminated incorrectly, expected LF", data);
|
|
1372
1468
|
}
|
|
1373
1469
|
state = State.chunk_size_start;
|
|
1374
1470
|
break;
|
|
@@ -1377,7 +1473,7 @@ public class HTTPParser {
|
|
|
1377
1473
|
|
|
1378
1474
|
|
|
1379
1475
|
default:
|
|
1380
|
-
|
|
1476
|
+
return error(settings, "unhandled state", data);
|
|
1381
1477
|
|
|
1382
1478
|
} // switch
|
|
1383
1479
|
} // while
|
|
@@ -1390,13 +1486,36 @@ public class HTTPParser {
|
|
|
1390
1486
|
|
|
1391
1487
|
settings.call_on_header_field(this, data, header_field_mark, p-header_field_mark);
|
|
1392
1488
|
settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
|
|
1393
|
-
settings.call_on_fragment (this, data, fragment_mark, p-fragment_mark);
|
|
1394
|
-
settings.call_on_query_string(this, data, query_string_mark, p-query_string_mark);
|
|
1395
|
-
settings.call_on_path (this, data, path_mark, p-path_mark);
|
|
1396
1489
|
settings.call_on_url (this, data, url_mark, p-url_mark);
|
|
1397
|
-
|
|
1490
|
+
settings.call_on_path (this, data, url_mark, p-url_mark);
|
|
1491
|
+
|
|
1492
|
+
return data.position()-this.p_start;
|
|
1398
1493
|
} // execute
|
|
1399
1494
|
|
|
1495
|
+
int error (ParserSettings settings, String mes, ByteBuffer data) {
|
|
1496
|
+
settings.call_on_error(this, mes, data, this.p_start);
|
|
1497
|
+
this.state = State.dead;
|
|
1498
|
+
return data.position()-this.p_start;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
public boolean http_message_needs_eof() {
|
|
1502
|
+
if(type == ParserType.HTTP_REQUEST){
|
|
1503
|
+
return false;
|
|
1504
|
+
}
|
|
1505
|
+
/* See RFC 2616 section 4.4 */
|
|
1506
|
+
if ((status_code / 100 == 1) || /* 1xx e.g. Continue */
|
|
1507
|
+
(status_code == 204) || /* No Content */
|
|
1508
|
+
(status_code == 304) || /* Not Modified */
|
|
1509
|
+
(flags & F_SKIPBODY) != 0) { /* response to a HEAD request */
|
|
1510
|
+
return false;
|
|
1511
|
+
}
|
|
1512
|
+
if ((flags & F_CHUNKED) != 0 || content_length != -1) {
|
|
1513
|
+
return false;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return true;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1400
1519
|
/* If http_should_keep_alive() in the on_headers_complete or
|
|
1401
1520
|
* on_message_complete callback returns true, then this will be should be
|
|
1402
1521
|
* the last message on the connection.
|
|
@@ -1408,19 +1527,153 @@ public class HTTPParser {
|
|
|
1408
1527
|
/* HTTP/1.1 */
|
|
1409
1528
|
if ( 0 != (flags & F_CONNECTION_CLOSE) ) {
|
|
1410
1529
|
return false;
|
|
1411
|
-
} else {
|
|
1412
|
-
return true;
|
|
1413
1530
|
}
|
|
1414
1531
|
} else {
|
|
1415
1532
|
/* HTTP/1.0 or earlier */
|
|
1416
|
-
if ( 0
|
|
1417
|
-
return true;
|
|
1418
|
-
} else {
|
|
1533
|
+
if ( 0 == (flags & F_CONNECTION_KEEP_ALIVE) ) {
|
|
1419
1534
|
return false;
|
|
1420
1535
|
}
|
|
1421
1536
|
}
|
|
1537
|
+
return !http_message_needs_eof();
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
public int parse_url(ByteBuffer data, boolean is_connect, HTTPParserUrl u) {
|
|
1541
|
+
|
|
1542
|
+
UrlFields uf = UrlFields.UF_MAX;
|
|
1543
|
+
UrlFields old_uf = UrlFields.UF_MAX;
|
|
1544
|
+
u.port = 0;
|
|
1545
|
+
u.field_set = 0;
|
|
1546
|
+
state = (is_connect ? State.req_host_start : State.req_spaces_before_url);
|
|
1547
|
+
int p_init = data.position();
|
|
1548
|
+
int p = 0;
|
|
1549
|
+
byte ch = 0;
|
|
1550
|
+
while (data.position() != data.limit()) {
|
|
1551
|
+
p = data.position();
|
|
1552
|
+
ch = data.get();
|
|
1553
|
+
state = parse_url_char(ch);
|
|
1554
|
+
switch(state) {
|
|
1555
|
+
case dead:
|
|
1556
|
+
return 1;
|
|
1557
|
+
|
|
1558
|
+
/* Skip delimeters */
|
|
1559
|
+
case req_schema_slash:
|
|
1560
|
+
case req_schema_slash_slash:
|
|
1561
|
+
case req_host_start:
|
|
1562
|
+
case req_host_v6_start:
|
|
1563
|
+
case req_host_v6_end:
|
|
1564
|
+
case req_port_start:
|
|
1565
|
+
case req_query_string_start:
|
|
1566
|
+
case req_fragment_start:
|
|
1567
|
+
continue;
|
|
1568
|
+
|
|
1569
|
+
case req_schema:
|
|
1570
|
+
uf = UrlFields.UF_SCHEMA;
|
|
1571
|
+
break;
|
|
1572
|
+
|
|
1573
|
+
case req_host:
|
|
1574
|
+
case req_host_v6:
|
|
1575
|
+
uf = UrlFields.UF_HOST;
|
|
1576
|
+
break;
|
|
1577
|
+
|
|
1578
|
+
case req_port:
|
|
1579
|
+
uf = UrlFields.UF_PORT;
|
|
1580
|
+
break;
|
|
1581
|
+
|
|
1582
|
+
case req_path:
|
|
1583
|
+
uf = UrlFields.UF_PATH;
|
|
1584
|
+
break;
|
|
1585
|
+
|
|
1586
|
+
case req_query_string:
|
|
1587
|
+
uf = UrlFields.UF_QUERY;
|
|
1588
|
+
break;
|
|
1589
|
+
|
|
1590
|
+
case req_fragment:
|
|
1591
|
+
uf = UrlFields.UF_FRAGMENT;
|
|
1592
|
+
break;
|
|
1593
|
+
|
|
1594
|
+
default:
|
|
1595
|
+
return 1;
|
|
1596
|
+
}
|
|
1597
|
+
/* Nothing's changed; soldier on */
|
|
1598
|
+
if (uf == old_uf) {
|
|
1599
|
+
u.field_data[uf.getIndex()].len++;
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
u.field_data[uf.getIndex()].off = p - p_init;
|
|
1604
|
+
u.field_data[uf.getIndex()].len = 1;
|
|
1605
|
+
|
|
1606
|
+
u.field_set |= (1 << uf.getIndex());
|
|
1607
|
+
old_uf = uf;
|
|
1608
|
+
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/* CONNECT requests can only contain "hostname:port" */
|
|
1612
|
+
if (is_connect && u.field_set != ((1 << UrlFields.UF_HOST.getIndex())|(1 << UrlFields.UF_PORT.getIndex()))) {
|
|
1613
|
+
return 1;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/* Make sure we don't end somewhere unexpected */
|
|
1617
|
+
switch (state) {
|
|
1618
|
+
case req_host_v6_start:
|
|
1619
|
+
case req_host_v6:
|
|
1620
|
+
case req_host_v6_end:
|
|
1621
|
+
case req_host:
|
|
1622
|
+
case req_port_start:
|
|
1623
|
+
return 1;
|
|
1624
|
+
default:
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
if (0 != (u.field_set & (1 << UrlFields.UF_PORT.getIndex()))) {
|
|
1629
|
+
/* Don't bother with endp; we've already validated the string */
|
|
1630
|
+
int v = strtoi(data, p_init + u.field_data[UrlFields.UF_PORT.getIndex()].off);
|
|
1631
|
+
|
|
1632
|
+
/* Ports have a max value of 2^16 */
|
|
1633
|
+
if (v > 0xffff) {
|
|
1634
|
+
return 1;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
u.port = v;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
return 0;
|
|
1422
1641
|
}
|
|
1423
1642
|
|
|
1643
|
+
//hacky reimplementation of srttoul, tailored for our simple needs
|
|
1644
|
+
//we only need to parse port val, so no negative values etc
|
|
1645
|
+
int strtoi(ByteBuffer data, int start_pos) {
|
|
1646
|
+
data.position(start_pos);
|
|
1647
|
+
byte ch;
|
|
1648
|
+
int start = data.position();
|
|
1649
|
+
int end = data.limit();
|
|
1650
|
+
while(data.position() < data.limit()) {
|
|
1651
|
+
ch = data.get();
|
|
1652
|
+
if(Character.isWhitespace((char)ch)){
|
|
1653
|
+
start++;
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
if(isDigit(ch)){
|
|
1657
|
+
continue;
|
|
1658
|
+
}else{
|
|
1659
|
+
end = data.position() - 1;
|
|
1660
|
+
break;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
byte[] s = new byte[end - start];
|
|
1664
|
+
int cur_pos = data.position();
|
|
1665
|
+
data.position(start);
|
|
1666
|
+
data.get(s);
|
|
1667
|
+
int result = 0;
|
|
1668
|
+
try{
|
|
1669
|
+
result = Integer.parseInt(new String(s, "UTF8"));
|
|
1670
|
+
} catch (UnsupportedEncodingException e) {
|
|
1671
|
+
result = 0;
|
|
1672
|
+
}
|
|
1673
|
+
data.position(cur_pos);
|
|
1674
|
+
return result;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1424
1677
|
boolean isDigit(byte b) {
|
|
1425
1678
|
if (b >= 0x30 && b <=0x39) {
|
|
1426
1679
|
return true;
|
|
@@ -1428,6 +1681,10 @@ public class HTTPParser {
|
|
|
1428
1681
|
return false;
|
|
1429
1682
|
}
|
|
1430
1683
|
|
|
1684
|
+
boolean isHex(byte b) {
|
|
1685
|
+
return isDigit(b) || (lower(b) >= 0x61 /*a*/ && lower(b) <= 0x66 /*f*/);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1431
1688
|
boolean isAtoZ(byte b) {
|
|
1432
1689
|
byte c = lower(b);
|
|
1433
1690
|
return (c>= 0x61 /*a*/ && c <= 0x7a /*z*/);
|
|
@@ -1442,7 +1699,30 @@ public class HTTPParser {
|
|
|
1442
1699
|
char c = (char)(b);
|
|
1443
1700
|
return (byte)Character.toUpperCase(c);
|
|
1444
1701
|
}
|
|
1445
|
-
|
|
1702
|
+
|
|
1703
|
+
byte token(byte b) {
|
|
1704
|
+
if(!strict){
|
|
1705
|
+
return (b == (byte)' ') ? (byte)' ' : (byte)tokens[b] ;
|
|
1706
|
+
}else{
|
|
1707
|
+
return (byte)tokens[b];
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
boolean isHostChar(byte ch){
|
|
1712
|
+
if(!strict){
|
|
1713
|
+
return (isAtoZ(ch)) || isDigit(ch) || DOT == ch || DASH == ch || UNDER == ch ;
|
|
1714
|
+
}else{
|
|
1715
|
+
return (isAtoZ(ch)) || isDigit(ch) || DOT == ch || DASH == ch;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
boolean isNormalUrlChar(int chi) {
|
|
1720
|
+
if(!strict){
|
|
1721
|
+
return (chi > 0x80) || normal_url_char[chi];
|
|
1722
|
+
}else{
|
|
1723
|
+
return normal_url_char[chi];
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1446
1726
|
|
|
1447
1727
|
HTTPMethod start_req_method_assign(byte c){
|
|
1448
1728
|
switch (c) {
|
|
@@ -1451,12 +1731,14 @@ public class HTTPParser {
|
|
|
1451
1731
|
case G: return HTTPMethod.HTTP_GET;
|
|
1452
1732
|
case H: return HTTPMethod.HTTP_HEAD;
|
|
1453
1733
|
case L: return HTTPMethod.HTTP_LOCK;
|
|
1454
|
-
case M: return HTTPMethod.HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE */
|
|
1734
|
+
case M: return HTTPMethod.HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */
|
|
1735
|
+
case N: return HTTPMethod.HTTP_NOTIFY;
|
|
1455
1736
|
case O: return HTTPMethod.HTTP_OPTIONS;
|
|
1456
|
-
case P: return HTTPMethod.HTTP_POST; /* or PROPFIND
|
|
1737
|
+
case P: return HTTPMethod.HTTP_POST; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
|
1457
1738
|
case R: return HTTPMethod.HTTP_REPORT;
|
|
1739
|
+
case S: return HTTPMethod.HTTP_SUBSCRIBE;
|
|
1458
1740
|
case T: return HTTPMethod.HTTP_TRACE;
|
|
1459
|
-
case U: return HTTPMethod.HTTP_UNLOCK;
|
|
1741
|
+
case U: return HTTPMethod.HTTP_UNLOCK; /* or UNSUBSCRIBE */
|
|
1460
1742
|
}
|
|
1461
1743
|
return null; // ugh.
|
|
1462
1744
|
}
|
|
@@ -1466,7 +1748,7 @@ public class HTTPParser {
|
|
|
1466
1748
|
return false;
|
|
1467
1749
|
}
|
|
1468
1750
|
|
|
1469
|
-
state = State.
|
|
1751
|
+
state = State.header_value_lws;
|
|
1470
1752
|
// TODO java enums support some sort of bitflag mechanism !?
|
|
1471
1753
|
switch (header_state) {
|
|
1472
1754
|
case connection_keep_alive:
|
|
@@ -1484,111 +1766,17 @@ public class HTTPParser {
|
|
|
1484
1766
|
return true;
|
|
1485
1767
|
}
|
|
1486
1768
|
|
|
1487
|
-
boolean headers_almost_done (byte ch, ParserSettings settings) {
|
|
1488
|
-
|
|
1489
|
-
if (LF != ch) {
|
|
1490
|
-
return false;
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
if (0 != (flags & F_TRAILING)) {
|
|
1494
|
-
/* End of a chunked request */
|
|
1495
|
-
|
|
1496
|
-
settings.call_on_headers_complete(this);
|
|
1497
|
-
settings.call_on_message_complete(this);
|
|
1498
|
-
|
|
1499
|
-
state = new_message();
|
|
1500
|
-
|
|
1501
|
-
return true;
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
nread = 0;
|
|
1505
|
-
|
|
1506
|
-
if (0 != (flags & F_UPGRADE) || HTTPMethod.HTTP_CONNECT == method) {
|
|
1507
|
-
upgrade = true;
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
/* Here we call the headers_complete callback. This is somewhat
|
|
1512
|
-
* different than other callbacks because if the user returns 1, we
|
|
1513
|
-
* will interpret that as saying that this message has no body. This
|
|
1514
|
-
* is needed for the annoying case of recieving a response to a HEAD
|
|
1515
|
-
* request.
|
|
1516
|
-
*/
|
|
1517
|
-
|
|
1518
|
-
/* (responses to HEAD request contain a CONTENT-LENGTH header
|
|
1519
|
-
* but no content)
|
|
1520
|
-
*
|
|
1521
|
-
* Consider what to do here: I don't like the idea of the callback
|
|
1522
|
-
* interface having a different contract in the case of HEAD
|
|
1523
|
-
* responses. The alternatives would be either to:
|
|
1524
|
-
*
|
|
1525
|
-
* a.) require the header_complete callback to implement a different
|
|
1526
|
-
* interface or
|
|
1527
|
-
*
|
|
1528
|
-
* b.) provide an overridden execute(bla, bla, boolean
|
|
1529
|
-
* parsingHeader) implementation ...
|
|
1530
|
-
*/
|
|
1531
|
-
|
|
1532
|
-
/*TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO */
|
|
1533
|
-
if (null != settings.on_headers_complete) {
|
|
1534
|
-
settings.call_on_headers_complete(this);
|
|
1535
|
-
//return;
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
// if (null != settings.on_headers_complete) {
|
|
1539
|
-
// switch (settings.on_headers_complete.cb(parser)) {
|
|
1540
|
-
// case 0:
|
|
1541
|
-
// break;
|
|
1542
|
-
//
|
|
1543
|
-
// case 1:
|
|
1544
|
-
// flags |= F_SKIPBODY;
|
|
1545
|
-
// break;
|
|
1546
|
-
//
|
|
1547
|
-
// default:
|
|
1548
|
-
// return p - data; /* Error */ // TODO // RuntimeException ?
|
|
1549
|
-
// }
|
|
1550
|
-
// }
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
// Exit, the rest of the connect is in a different protocol.
|
|
1554
|
-
if (upgrade) {
|
|
1555
|
-
settings.call_on_message_complete(this);
|
|
1556
|
-
return true;
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
if (0 != (flags & F_SKIPBODY)) {
|
|
1560
|
-
settings.call_on_message_complete(this);
|
|
1561
|
-
state = new_message();
|
|
1562
|
-
} else if (0 != (flags & F_CHUNKED)) {
|
|
1563
|
-
/* chunked encoding - ignore Content-Length header */
|
|
1564
|
-
state = State.chunk_size_start;
|
|
1565
|
-
} else {
|
|
1566
|
-
if (content_length == 0) {
|
|
1567
|
-
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
|
1568
|
-
settings.call_on_message_complete(this);
|
|
1569
|
-
state = new_message();
|
|
1570
|
-
} else if (content_length > 0) {
|
|
1571
|
-
/* Content-Length header given and non-zero */
|
|
1572
|
-
state = State.body_identity;
|
|
1573
|
-
} else {
|
|
1574
|
-
if (type == ParserType.HTTP_REQUEST || http_should_keep_alive()) {
|
|
1575
|
-
/* Assume content-length 0 - read the next */
|
|
1576
|
-
settings.call_on_message_complete(this);
|
|
1577
|
-
state = new_message();
|
|
1578
|
-
} else {
|
|
1579
|
-
/* Read body until EOF */
|
|
1580
|
-
state = State.body_identity_eof;
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
return true;
|
|
1585
|
-
} // headers_almost_fone
|
|
1769
|
+
// boolean headers_almost_done (byte ch, ParserSettings settings) {
|
|
1770
|
+
// } // headers_almost_done
|
|
1586
1771
|
|
|
1587
1772
|
|
|
1588
1773
|
final int min (int a, int b) {
|
|
1589
1774
|
return a < b ? a : b;
|
|
1590
1775
|
}
|
|
1591
1776
|
|
|
1777
|
+
final int min (int a, long b) {
|
|
1778
|
+
return a < b ? a : (int)b;
|
|
1779
|
+
}
|
|
1592
1780
|
/* probably not the best place to hide this ... */
|
|
1593
1781
|
public boolean HTTP_PARSER_STRICT;
|
|
1594
1782
|
State new_message() {
|
|
@@ -1608,19 +1796,16 @@ public class HTTPParser {
|
|
|
1608
1796
|
boolean parsing_header(State state) {
|
|
1609
1797
|
|
|
1610
1798
|
switch (state) {
|
|
1611
|
-
case chunk_size_start :
|
|
1612
|
-
case chunk_size :
|
|
1613
|
-
case chunk_size_almost_done :
|
|
1614
|
-
case chunk_parameters :
|
|
1615
1799
|
case chunk_data :
|
|
1616
1800
|
case chunk_data_almost_done :
|
|
1617
1801
|
case chunk_data_done :
|
|
1618
1802
|
case body_identity :
|
|
1619
1803
|
case body_identity_eof :
|
|
1804
|
+
case message_done :
|
|
1620
1805
|
return false;
|
|
1621
1806
|
|
|
1622
1807
|
}
|
|
1623
|
-
|
|
1808
|
+
return true;
|
|
1624
1809
|
}
|
|
1625
1810
|
|
|
1626
1811
|
/* "Dial C for Constants" */
|
|
@@ -1676,12 +1861,16 @@ public class HTTPParser {
|
|
|
1676
1861
|
static final byte [] CLOSE = {
|
|
1677
1862
|
0x43, 0x4c, 0x4f, 0x53, 0x45,
|
|
1678
1863
|
};
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
*
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1864
|
+
|
|
1865
|
+
/* Tokens as defined by rfc 2616. Also lowercases them.
|
|
1866
|
+
* token = 1*<any CHAR except CTLs or separators>
|
|
1867
|
+
* separators = "(" | ")" | "<" | ">" | "@"
|
|
1868
|
+
* | "," | ";" | ":" | "\" | <">
|
|
1869
|
+
* | "/" | "[" | "]" | "?" | "="
|
|
1870
|
+
* | "{" | "}" | SP | HT
|
|
1871
|
+
*/
|
|
1872
|
+
|
|
1873
|
+
static final char [] tokens = {
|
|
1685
1874
|
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
|
|
1686
1875
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1687
1876
|
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
|
|
@@ -1691,9 +1880,9 @@ public class HTTPParser {
|
|
|
1691
1880
|
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
|
1692
1881
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1693
1882
|
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
|
1694
|
-
|
|
1883
|
+
0, '!', 0, '#', '$', '%', '&', '\'',
|
|
1695
1884
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
|
1696
|
-
0, 0,
|
|
1885
|
+
0, 0, '*', '+', 0, '-', '.', 0 ,
|
|
1697
1886
|
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
|
1698
1887
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
1699
1888
|
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
|
|
@@ -1713,7 +1902,24 @@ public class HTTPParser {
|
|
|
1713
1902
|
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
|
1714
1903
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
1715
1904
|
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
|
1716
|
-
'X', 'Y', 'Z', 0,
|
|
1905
|
+
'X', 'Y', 'Z', 0, '|', 0, '~', 0,
|
|
1906
|
+
/* hi bit set, not ascii */
|
|
1907
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1908
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1909
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1910
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1911
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1912
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1913
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1914
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1915
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1916
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1917
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1918
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1919
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1920
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1921
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1922
|
+
0, 0, 0, 0, 0, 0, 0, 0, };
|
|
1717
1923
|
|
|
1718
1924
|
static final byte [] UNHEX =
|
|
1719
1925
|
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
@@ -1724,6 +1930,14 @@ public class HTTPParser {
|
|
|
1724
1930
|
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1725
1931
|
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1726
1932
|
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1933
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1934
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1935
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1936
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1937
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1938
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1939
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1940
|
+
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
1727
1941
|
};
|
|
1728
1942
|
|
|
1729
1943
|
static final boolean [] normal_url_char = {
|
|
@@ -1758,7 +1972,8 @@ public class HTTPParser {
|
|
|
1758
1972
|
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
|
1759
1973
|
true, true, true, true, true, true, true, true,
|
|
1760
1974
|
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
|
1761
|
-
true, true, true, true, true, true, true, false
|
|
1975
|
+
true, true, true, true, true, true, true, false,
|
|
1976
|
+
};
|
|
1762
1977
|
|
|
1763
1978
|
public static final byte A = 0x41;
|
|
1764
1979
|
public static final byte B = 0x42;
|
|
@@ -1786,16 +2001,19 @@ public class HTTPParser {
|
|
|
1786
2001
|
public static final byte X = 0x58;
|
|
1787
2002
|
public static final byte Y = 0x59;
|
|
1788
2003
|
public static final byte Z = 0x5a;
|
|
2004
|
+
public static final byte UNDER = 0x5f;
|
|
1789
2005
|
public static final byte CR = 0x0d;
|
|
1790
2006
|
public static final byte LF = 0x0a;
|
|
1791
2007
|
public static final byte DOT = 0x2e;
|
|
1792
2008
|
public static final byte SPACE = 0x20;
|
|
2009
|
+
public static final byte TAB = 0x09;
|
|
1793
2010
|
public static final byte SEMI = 0x3b;
|
|
1794
2011
|
public static final byte COLON = 0x3a;
|
|
1795
2012
|
public static final byte HASH = 0x23;
|
|
1796
2013
|
public static final byte QMARK = 0x3f;
|
|
1797
2014
|
public static final byte SLASH = 0x2f;
|
|
1798
2015
|
public static final byte DASH = 0x2d;
|
|
2016
|
+
public static final byte STAR = 0x2a;
|
|
1799
2017
|
public static final byte NULL = 0x00;
|
|
1800
2018
|
}
|
|
1801
2019
|
|
|
@@ -1803,7 +2021,7 @@ public class HTTPParser {
|
|
|
1803
2021
|
|
|
1804
2022
|
dead
|
|
1805
2023
|
|
|
1806
|
-
,
|
|
2024
|
+
, start_req_or_res
|
|
1807
2025
|
, res_or_resp_H
|
|
1808
2026
|
, start_res
|
|
1809
2027
|
, res_H
|
|
@@ -1826,7 +2044,12 @@ public class HTTPParser {
|
|
|
1826
2044
|
, req_schema
|
|
1827
2045
|
, req_schema_slash
|
|
1828
2046
|
, req_schema_slash_slash
|
|
2047
|
+
, req_host_start
|
|
2048
|
+
, req_host_v6_start
|
|
2049
|
+
, req_host_v6
|
|
2050
|
+
, req_host_v6_end
|
|
1829
2051
|
, req_host
|
|
2052
|
+
, req_port_start
|
|
1830
2053
|
, req_port
|
|
1831
2054
|
, req_path
|
|
1832
2055
|
, req_query_string_start
|
|
@@ -1848,22 +2071,31 @@ public class HTTPParser {
|
|
|
1848
2071
|
, header_field
|
|
1849
2072
|
, header_value_start
|
|
1850
2073
|
, header_value
|
|
2074
|
+
, header_value_lws
|
|
1851
2075
|
|
|
1852
2076
|
, header_almost_done
|
|
1853
2077
|
|
|
1854
|
-
, headers_almost_done
|
|
1855
|
-
|
|
1856
2078
|
, chunk_size_start
|
|
1857
2079
|
, chunk_size
|
|
1858
|
-
, chunk_size_almost_done
|
|
1859
2080
|
, chunk_parameters
|
|
2081
|
+
, chunk_size_almost_done
|
|
2082
|
+
|
|
2083
|
+
, headers_almost_done
|
|
2084
|
+
, headers_done
|
|
2085
|
+
// This space intentionally not left blank, comment from c, for orientation...
|
|
2086
|
+
// the c version uses <= s_header_almost_done in java, we list the states explicitly
|
|
2087
|
+
// in `parsing_header()`
|
|
2088
|
+
/* Important: 's_headers_done' must be the last 'header' state. All
|
|
2089
|
+
* states beyond this must be 'body' states. It is used for overflow
|
|
2090
|
+
* checking. See the PARSING_HEADER() macro.
|
|
2091
|
+
*/
|
|
1860
2092
|
, chunk_data
|
|
1861
2093
|
, chunk_data_almost_done
|
|
1862
2094
|
, chunk_data_done
|
|
1863
2095
|
|
|
1864
2096
|
, body_identity
|
|
1865
|
-
, body_identity_eof
|
|
1866
|
-
|
|
2097
|
+
, body_identity_eof
|
|
2098
|
+
, message_done
|
|
1867
2099
|
|
|
1868
2100
|
}
|
|
1869
2101
|
enum HState {
|
|
@@ -1891,4 +2123,24 @@ public class HTTPParser {
|
|
|
1891
2123
|
, connection_keep_alive
|
|
1892
2124
|
, connection_close
|
|
1893
2125
|
}
|
|
2126
|
+
public enum UrlFields {
|
|
2127
|
+
UF_SCHEMA(0)
|
|
2128
|
+
, UF_HOST(1)
|
|
2129
|
+
, UF_PORT(2)
|
|
2130
|
+
, UF_PATH(3)
|
|
2131
|
+
, UF_QUERY(4)
|
|
2132
|
+
, UF_FRAGMENT(5)
|
|
2133
|
+
, UF_MAX(6);
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
private final int index;
|
|
2137
|
+
|
|
2138
|
+
private UrlFields(int index) {
|
|
2139
|
+
this.index = index;
|
|
2140
|
+
}
|
|
2141
|
+
public int getIndex() {
|
|
2142
|
+
return index;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
}
|
|
1894
2146
|
}
|