http_parser.rb 0.5.1 → 0.6.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.gitmodules +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +11 -4
  4. data/LICENSE-MIT +20 -0
  5. data/README.md +51 -46
  6. data/bench/standalone.rb +23 -0
  7. data/bench/thin.rb +1 -0
  8. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +121 -56
  9. data/ext/ruby_http_parser/ruby_http_parser.c +89 -48
  10. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
  11. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
  12. data/ext/ruby_http_parser/vendor/http-parser/Makefile +34 -17
  13. data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
  14. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1045 -589
  15. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
  16. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +148 -17
  17. data/ext/ruby_http_parser/vendor/http-parser/test.c +1138 -128
  18. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +30 -1
  20. data/ext/ruby_http_parser/vendor/http-parser-java/Makefile +34 -17
  21. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +30 -18
  22. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +9 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/ext/primitives.jar +0 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1075 -607
  26. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +190 -40
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +67 -52
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +34 -35
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +839 -587
  42. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +2 -1
  43. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +374 -0
  44. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  45. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  46. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +52 -0
  47. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +16 -0
  48. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +48 -0
  49. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +171 -446
  50. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +62 -0
  51. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +3 -1
  52. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +27 -0
  53. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  54. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +236 -0
  55. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +59 -0
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1318 -184
  57. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +355 -49
  58. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  59. data/http_parser.rb.gemspec +10 -2
  60. data/lib/http_parser.rb +17 -0
  61. data/spec/parser_spec.rb +157 -23
  62. data/spec/support/requests.json +236 -24
  63. data/spec/support/responses.json +182 -36
  64. data/tasks/compile.rake +5 -3
  65. data/tasks/fixtures.rake +8 -2
  66. metadata +209 -127
  67. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
  68. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  69. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  70. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
@@ -1,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
- int content_length;
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
- protected int http_major;
28
- protected int http_minor;
29
- protected int status_code; /* responses only */
30
- protected HTTPMethod method; /* requests only */
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
- protected boolean upgrade;
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.start_res_or_res;
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 void execute(ParserSettings settings, ByteBuffer data) {
265
+ public int execute(ParserSettings settings, ByteBuffer data) {
99
266
 
100
267
  int p = data.position();
101
- int p_err = p; // this is used for pretty printing errors.
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
- if (0 == len) {
109
- if (State.body_identity_eof == state) {
110
- settings.call_on_message_complete(this);
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
- p = data.position();
151
- int pe = data.limit();
152
-
153
- byte ch = data.get(); // the current character to process.
154
- byte c = -1; // utility variably used for up- and downcasing etc.
155
- int to_read = 0; // used to keep track of how much of body, etc. is left to read
156
-
157
- if (parsing_header(state)) {
158
- ++nread;
159
- if (nread > HTTP_MAX_HEADER_SIZE) {
160
- settings.call_on_error(this, "possible buffer overflow", data, p_err);
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
- settings.call_on_error(this, "Connection already closed", data, p_err);
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 start_res_or_res:
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
- settings.call_on_message_begin(this);
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
- method = start_req_method_assign(ch);
188
- if (null == method) {
189
- settings.call_on_error(this, "invalid method", data, p_err);
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.call_on_error(this, "not E", data, p_err);
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.call_on_error(this, "Not H or CR/LF", data, p_err);
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.call_on_error(this, "Not T", data, p_err);
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
- settings.call_on_error(this, "Not T2", data, p_err);
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
- settings.call_on_error(this, "Not P", data, p_err);
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
- settings.call_on_error(this, "Not '/'", data, p_err);
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
- settings.call_on_error(this, "Not a digit", data, p_err);
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.res_http_minor;
464
+ state = State.res_first_http_minor;
274
465
  break;
275
466
  }
276
467
  if (!isDigit(ch)) {
277
- settings.call_on_error(this, "Not a digit", data, p_err);
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
- settings.call_on_error(this, "invalid http major version: "+http_major, data, p_err);
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
- settings.call_on_error(this, "Not a digit", data, p_err);
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
- settings.call_on_error(this, "Not a digit", data, p_err);
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
- settings.call_on_error(this, "invalid http minor version: "+http_minor, data, p_err);
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
- settings.call_on_error(this, "Not a digit (status code)", data, p_err);
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
- settings.call_on_error(this, "not a valid status code", data, p_err);
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
- settings.call_on_error(this, "ridiculous status code:"+status_code, data, p_err);
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
- settings.call_on_error(this, "not LF", data, p_err);
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 == LF) {
565
+ if (CR==ch || LF == ch) {
376
566
  break;
377
567
  }
378
568
  flags = 0;
379
569
  content_length = -1;
380
- settings.call_on_message_begin(this);
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.call_on_error(this, "invalid method", data, p_err);
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.call_on_error(this, "NULL in method", data, p_err);
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 && R == ch) {
417
- method = HTTPMethod.HTTP_PROPFIND;
418
- } else if (1 == index && HTTPMethod.HTTP_POST == method && U == ch) {
419
- method = HTTPMethod.HTTP_PUT;
420
- } else if (4 == index && HTTPMethod.HTTP_PROPFIND == method && P == ch) {
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.call_on_error(this, "Invalid HTTP method", data, p_err);
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
- if (SLASH == ch) {
437
- url_mark = p;
438
- path_mark = p;
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
- case req_schema_slash:
463
- if (strict && SLASH != ch) {
464
- settings.call_on_error(this, "invalid char in schema, not /", data, p_err);
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 req_port:
503
- if (isDigit(ch)) break;
504
- switch (ch) {
505
- case SLASH:
506
- path_mark = p;
507
- state = State.req_path;
508
- break;
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.call_on_url(this,data,url_mark, p-url_mark);
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
- settings.call_on_error(this, "unexpected char in path", data, p_err);
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
- settings.call_on_url(this,data,url_mark, p-url_mark);
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 = State.header_field_start;
731
- break;
732
- case QMARK:
733
- case HASH:
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
- settings.call_on_error(this, "unexpected char in path", data, p_err);
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.call_on_error(this, "error in req_http_H", data, p_err);
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.call_on_error(this, "unexpected char", data, p_err);
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.call_on_error(this, "unexpected char", data, p_err);
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.call_on_error(this, "unexpected char", data, p_err);
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.call_on_error(this, "unexpected char", data, p_err);
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
- settings.call_on_error(this, "non digit in http major", data, p_err);
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
- settings.call_on_error(this, "non digit in http major", data, p_err);
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
- settings.call_on_error(this, "ridiculous http major", data, p_err);
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
- settings.call_on_error(this, "non digit in http minor", data, p_err);
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
- settings.call_on_error(this, "non digit in http minor", data, p_err);
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
- settings.call_on_error(this, "ridiculous http minor", data, p_err);
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
- settings.call_on_error(this, "missing LF after request line", data, p_err);
825
+ return error(settings, "missing LF after request line", data);
853
826
  }
854
- state = State.header_field_start;
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 = State.headers_almost_done;
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
- if (!headers_almost_done(ch, settings)) {
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 = upper(ch);
851
+ c = token(ch);
881
852
 
882
- if (c < A || Z < c) {
883
- settings.call_on_error(this, "invalid char in header", data, p_err);
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 = (byte) acceptable_header[ch];
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
- settings.call_on_error(this, "Unknown Header State", data, p_err);
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
- settings.call_on_error(this, "invalid header field", data, p_err);
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
- if (LF == ch) {
1069
- settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
1070
- header_value_mark = -1;
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
- settings.call_on_error(this, "Content-Length not numeric", data, p_err);
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 (c == 0) {
1128
- if (CR == ch) {
1129
- settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
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
- state = State.header_almost_done;
1133
- break;
1134
- }
1098
+ state = State.header_almost_done;
1099
+ break;
1100
+ }
1135
1101
 
1136
- if (LF == ch) {
1137
- settings.call_on_header_value(this, data, header_value_mark, p-header_value_mark);
1138
- header_value_mark = -1;
1139
-
1140
- if (!header_almost_done(ch)) {
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
- settings.call_on_error(this, "Shouldn't be here", data, p_err);
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
- settings.call_on_error(this, "Content-Length not numeric", data, p_err);
1124
+ return error(settings, "Content-Length not numeric", data);
1162
1125
  }
1163
1126
 
1164
- content_length *= 10;
1165
- content_length += (int)ch - 0x30;
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.call_on_error(this,"incorrect header ending, expection LF", data, p_err);
1187
+ return error(settings, "incorrect header ending, expecting LF", data);
1217
1188
  }
1218
1189
  break;
1219
1190
 
1220
- case headers_almost_done:
1221
- if (!headers_almost_done(ch, settings)) {
1222
- settings.call_on_error(this, "header not properly completed", data, p_err);
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
- settings.call_on_message_complete(this);
1241
- state = new_message();
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
- settings.call_on_error(this, "not chunked", data, p_err);
1350
+ return error(settings, "not chunked", data);
1263
1351
  }
1264
1352
 
1265
- c = UNHEX[ch];
1353
+ c = UNHEX[chi];
1266
1354
  if (c == -1) {
1267
- settings.call_on_error(this, "invalid hex char in chunk content length", data, p_err);
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.call_on_error(this, "not chunked", data, p_err);
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[ch];
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.call_on_error(this, "invalid hex char in chunk content length", data, p_err);
1380
+ return error(settings, "invalid hex char in chunk content length", data);
1293
1381
  }
1294
-
1295
- content_length *= 16;
1296
- content_length += c;
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
- settings.call_on_error(this, "not chunked", data, p_err);
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
- settings.call_on_error(this, "not chunked", data, p_err);
1409
+ return error(settings, "not chunked", data);
1317
1410
  }
1318
1411
  if (strict && LF != ch) {
1319
- settings.call_on_error(this, "expected LF at end of chunk size", data, p_err);
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.call_on_error(this, "not chunked", data, p_err);
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
- settings.call_on_error(this, "not chunked", data, p_err);
1450
+ return error(settings, "not chunked", data);
1357
1451
  }
1358
1452
  if (strict && CR != ch) {
1359
- settings.call_on_error(this, "chunk data terminated incorrectly, expected CR", data, p_err);
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
- settings.call_on_error(this, "not chunked", data, p_err);
1464
+ return error(settings, "not chunked", data);
1369
1465
  }
1370
1466
  if (strict && LF != ch) {
1371
- settings.call_on_error(this, "chunk data terminated incorrectly, expected LF", data, p_err);
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
- settings.call_on_error(this, "unhandled state", data, p_err);
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 != (flags & F_CONNECTION_KEEP_ALIVE) ) {
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, PROPPATH, PUT */
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.header_field_start;
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
- return (0==(flags & F_TRAILING));
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
- * ' ', '_', '-' and all alpha-numeric ascii characters are accepted by acceptable_header.
1681
- * The 'A'-'Z' are upper-cased.
1682
- */
1683
-
1684
- static final char [] acceptable_header = {
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
- ' ', 0, 0, 0, 0, 0, 0, 0,
1883
+ 0, '!', 0, '#', '$', '%', '&', '\'',
1695
1884
  /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
1696
- 0, 0, 0, 0, 0, '-', 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, 0, 0, 0, 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
- , start_res_or_res
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
  }