http_parser.rb 0.5.1-x86-mswin32-60 → 0.5.2-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/Gemfile.lock +16 -16
  2. data/LICENSE-MIT +20 -0
  3. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +68 -11
  4. data/ext/ruby_http_parser/ruby_http_parser.c +74 -6
  5. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +26 -1
  6. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +23 -143
  7. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +3 -0
  8. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  9. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +115 -61
  10. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +19 -3
  11. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  12. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
  15. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +64 -52
  16. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +27 -28
  19. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +259 -85
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +324 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +51 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +15 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +47 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +183 -447
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +61 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +2 -1
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +26 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +165 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +58 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +232 -29
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -1
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -1
  35. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +1 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +154 -7
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  38. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
  39. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +52 -10
  40. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +3 -1
  41. data/ext/ruby_http_parser/vendor/http-parser/test.c +89 -3
  42. data/http_parser.rb.gemspec +8 -2
  43. data/lib/http_parser.rb +17 -0
  44. data/spec/parser_spec.rb +97 -6
  45. data/tasks/compile.rake +3 -1
  46. metadata +83 -20
  47. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
data/Gemfile.lock CHANGED
@@ -1,26 +1,25 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- http_parser.rb (0.5.1)
4
+ http_parser.rb (0.5.2)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
9
  diff-lcs (1.1.2)
10
- json (1.4.6)
11
- rake (0.8.7)
12
- rake-compiler (0.7.0)
13
- rake (>= 0.8.3, < 0.9)
14
- rspec (2.0.1)
15
- rspec-core (~> 2.0.1)
16
- rspec-expectations (~> 2.0.1)
17
- rspec-mocks (~> 2.0.1)
18
- rspec-core (2.0.1)
19
- rspec-expectations (2.0.1)
20
- diff-lcs (>= 1.1.2)
21
- rspec-mocks (2.0.1)
22
- rspec-core (~> 2.0.1)
23
- rspec-expectations (~> 2.0.1)
10
+ json (1.5.1)
11
+ rake (0.9.2)
12
+ rake-compiler (0.7.9)
13
+ rake
14
+ rspec (2.4.0)
15
+ rspec-core (~> 2.4.0)
16
+ rspec-expectations (~> 2.4.0)
17
+ rspec-mocks (~> 2.4.0)
18
+ rspec-core (2.4.0)
19
+ rspec-expectations (2.4.0)
20
+ diff-lcs (~> 1.1.2)
21
+ rspec-mocks (2.4.0)
22
+ yajl-ruby (0.8.2)
24
23
 
25
24
  PLATFORMS
26
25
  ruby
@@ -28,5 +27,6 @@ PLATFORMS
28
27
  DEPENDENCIES
29
28
  http_parser.rb!
30
29
  json (>= 1.4.6)
31
- rake-compiler (>= 0.7.0)
30
+ rake-compiler (>= 0.7.9)
32
31
  rspec (>= 2.0.1)
32
+ yajl-ruby (>= 0.8.1)
data/LICENSE-MIT ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2009,2010 Marc-André Cournoyer <macournoyer@gmail.com>
2
+ Copyright 2010,2011 Aman Gupta <aman@tmm1.net>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to
6
+ deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
+ sell copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
+ IN THE SOFTWARE.
@@ -1,6 +1,7 @@
1
1
  package org.ruby_http_parser;
2
2
 
3
3
  import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
4
5
  import org.jruby.RubyClass;
5
6
  import org.jruby.RubyHash;
6
7
  import org.jruby.RubyModule;
@@ -59,6 +60,9 @@ public class RubyHttpParser extends RubyObject {
59
60
  private IRubyObject queryString;
60
61
  private IRubyObject fragment;
61
62
 
63
+ private IRubyObject header_value_type;
64
+ private IRubyObject upgradeData;
65
+
62
66
  private IRubyObject callback_object;
63
67
 
64
68
  private String _current_header;
@@ -77,6 +81,8 @@ public class RubyHttpParser extends RubyObject {
77
81
 
78
82
  this.callback_object = null;
79
83
 
84
+ this.header_value_type = runtime.getModule("HTTP").getClass("Parser").getInstanceVariable("@default_header_value_type");
85
+
80
86
  initSettings();
81
87
  init();
82
88
  }
@@ -130,25 +136,45 @@ public class RubyHttpParser extends RubyObject {
130
136
  byte[] data = fetchBytes(buf, pos, len);
131
137
  ThreadContext context = headers.getRuntime().getCurrentContext();
132
138
  IRubyObject key, val;
139
+ int new_field = 0;
133
140
 
134
141
  if (_current_header != null) {
142
+ new_field = 1;
135
143
  _last_header = _current_header;
136
144
  _current_header = null;
145
+ }
137
146
 
138
- key = (IRubyObject)runtime.newString(_last_header);
147
+ key = (IRubyObject)runtime.newString(_last_header);
148
+ val = headers.op_aref(context, key);
149
+
150
+ if (new_field == 1) {
151
+ if (val.isNil()) {
152
+ if (header_value_type == runtime.newSymbol("arrays")) {
153
+ headers.op_aset(context, key, RubyArray.newArrayLight(runtime, runtime.newString("")));
154
+ } else {
155
+ headers.op_aset(context, key, runtime.newString(""));
156
+ }
157
+ } else {
158
+ if (header_value_type == runtime.newSymbol("mixed")) {
159
+ if (val instanceof RubyString) {
160
+ headers.op_aset(context, key, RubyArray.newArrayLight(runtime, val, runtime.newString("")));
161
+ } else {
162
+ ((RubyArray)val).add(runtime.newString(""));
163
+ }
164
+ } else if (header_value_type == runtime.newSymbol("arrays")) {
165
+ ((RubyArray)val).add(runtime.newString(""));
166
+ } else {
167
+ ((RubyString)val).cat(", ".getBytes());
168
+ }
169
+ }
139
170
  val = headers.op_aref(context, key);
171
+ }
140
172
 
141
- if (!val.isNil())
142
- ((RubyString)val).cat(", ".getBytes());
143
- } else {
144
- key = (IRubyObject)runtime.newString(_last_header);
145
- val = headers.op_aref(context, key);
173
+ if (val instanceof RubyArray) {
174
+ val = ((RubyArray)val).entry(-1);
146
175
  }
147
176
 
148
- if (val.isNil())
149
- headers.op_aset(context, key, runtime.newString(new String(data)));
150
- else
151
- ((RubyString)val).cat(data);
177
+ ((RubyString)val).cat(data);
152
178
 
153
179
  return 0;
154
180
  }
@@ -163,6 +189,8 @@ public class RubyHttpParser extends RubyObject {
163
189
  queryString = runtime.newString("");
164
190
  fragment = runtime.newString("");
165
191
 
192
+ upgradeData = runtime.newString("");
193
+
166
194
  IRubyObject ret = runtime.getNil();
167
195
 
168
196
  if (callback_object != null) {
@@ -256,6 +284,8 @@ public class RubyHttpParser extends RubyObject {
256
284
  this.requestPath = runtime.getNil();
257
285
  this.queryString = runtime.getNil();
258
286
  this.fragment = runtime.getNil();
287
+
288
+ this.upgradeData = runtime.getNil();
259
289
  }
260
290
 
261
291
  @JRubyMethod(name = "initialize")
@@ -269,6 +299,12 @@ public class RubyHttpParser extends RubyObject {
269
299
  return initialize();
270
300
  }
271
301
 
302
+ @JRubyMethod(name = "initialize")
303
+ public IRubyObject initialize(IRubyObject arg, IRubyObject arg2) {
304
+ header_value_type = arg2;
305
+ return initialize(arg);
306
+ }
307
+
272
308
  @JRubyMethod(name = "on_message_begin=")
273
309
  public IRubyObject set_on_message_begin(IRubyObject cb) {
274
310
  on_message_begin = cb;
@@ -308,7 +344,9 @@ public class RubyHttpParser extends RubyObject {
308
344
  }
309
345
 
310
346
  if (parser.getUpgrade()) {
311
- // upgrade request
347
+ byte[] upData = fetchBytes(buf, buf.position(), buf.limit() - buf.position());
348
+ ((RubyString)upgradeData).concat(runtime.newString(new String(upData)));
349
+
312
350
  } else if (buf.hasRemaining()) {
313
351
  if (!stopped)
314
352
  throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
@@ -394,6 +432,25 @@ public class RubyHttpParser extends RubyObject {
394
432
  return fragment == null ? runtime.getNil() : fragment;
395
433
  }
396
434
 
435
+ @JRubyMethod(name = "header_value_type")
436
+ public IRubyObject getHeaderValueType() {
437
+ return header_value_type == null ? runtime.getNil() : header_value_type;
438
+ }
439
+
440
+ @JRubyMethod(name = "header_value_type=")
441
+ public IRubyObject set_header_value_type(IRubyObject val) {
442
+ if (val != runtime.newSymbol("mixed") && val != runtime.newSymbol("arrays") && val != runtime.newSymbol("strings")) {
443
+ throw runtime.newArgumentError("Invalid header value type");
444
+ }
445
+ header_value_type = val;
446
+ return val;
447
+ }
448
+
449
+ @JRubyMethod(name = "upgrade_data")
450
+ public IRubyObject upgradeData() {
451
+ return upgradeData == null ? runtime.getNil() : upgradeData;
452
+ }
453
+
397
454
  @JRubyMethod(name = "reset!")
398
455
  public IRubyObject reset() {
399
456
  init();
@@ -23,6 +23,8 @@ typedef struct ParserWrapper {
23
23
 
24
24
  VALUE headers;
25
25
 
26
+ VALUE upgrade_data;
27
+
26
28
  VALUE on_message_begin;
27
29
  VALUE on_headers_complete;
28
30
  VALUE on_body;
@@ -32,6 +34,8 @@ typedef struct ParserWrapper {
32
34
  VALUE stopped;
33
35
  VALUE completed;
34
36
 
37
+ VALUE header_value_type;
38
+
35
39
  VALUE last_field_name;
36
40
  VALUE curr_field_name;
37
41
 
@@ -49,6 +53,8 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
49
53
  wrapper->query_string = Qnil;
50
54
  wrapper->fragment = Qnil;
51
55
 
56
+ wrapper->upgrade_data = Qnil;
57
+
52
58
  wrapper->headers = Qnil;
53
59
  wrapper->completed = Qfalse;
54
60
 
@@ -63,6 +69,7 @@ void ParserWrapper_mark(void *data) {
63
69
  rb_gc_mark_maybe(wrapper->request_path);
64
70
  rb_gc_mark_maybe(wrapper->query_string);
65
71
  rb_gc_mark_maybe(wrapper->fragment);
72
+ rb_gc_mark_maybe(wrapper->upgrade_data);
66
73
  rb_gc_mark_maybe(wrapper->headers);
67
74
  rb_gc_mark_maybe(wrapper->on_message_begin);
68
75
  rb_gc_mark_maybe(wrapper->on_headers_complete);
@@ -93,6 +100,9 @@ static ID Ion_body;
93
100
  static ID Ion_message_complete;
94
101
 
95
102
  static VALUE Sstop;
103
+ static VALUE Sarrays;
104
+ static VALUE Sstrings;
105
+ static VALUE Smixed;
96
106
 
97
107
  /** Callbacks **/
98
108
 
@@ -104,6 +114,7 @@ int on_message_begin(ryah_http_parser *parser) {
104
114
  wrapper->query_string = rb_str_new2("");
105
115
  wrapper->fragment = rb_str_new2("");
106
116
  wrapper->headers = rb_hash_new();
117
+ wrapper->upgrade_data = rb_str_new2("");
107
118
 
108
119
  VALUE ret = Qnil;
109
120
 
@@ -161,17 +172,45 @@ int on_header_field(ryah_http_parser *parser, const char *at, size_t length) {
161
172
  int on_header_value(ryah_http_parser *parser, const char *at, size_t length) {
162
173
  GET_WRAPPER(wrapper, parser);
163
174
 
175
+ int new_field = 0;
176
+ VALUE current_value;
177
+
164
178
  if (wrapper->last_field_name == Qnil) {
179
+ new_field = 1;
165
180
  wrapper->last_field_name = wrapper->curr_field_name;
166
181
  wrapper->curr_field_name = Qnil;
182
+ }
167
183
 
168
- VALUE val = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
169
- if (val != Qnil) {
170
- rb_str_cat(val, ", ", 2);
184
+ current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
185
+
186
+ if (new_field == 1) {
187
+ if (current_value == Qnil) {
188
+ if (wrapper->header_value_type == Sarrays) {
189
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(1, rb_str_new2("")));
190
+ } else {
191
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_str_new2(""));
192
+ }
193
+ } else {
194
+ if (wrapper->header_value_type == Smixed) {
195
+ if (TYPE(current_value) == T_STRING) {
196
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(2, current_value, rb_str_new2("")));
197
+ } else {
198
+ rb_ary_push(current_value, rb_str_new2(""));
199
+ }
200
+ } else if (wrapper->header_value_type == Sarrays) {
201
+ rb_ary_push(current_value, rb_str_new2(""));
202
+ } else {
203
+ rb_str_cat(current_value, ", ", 2);
204
+ }
171
205
  }
206
+ current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
172
207
  }
173
208
 
174
- HASH_CAT(wrapper->headers, wrapper->last_field_name, at, length);
209
+ if (TYPE(current_value) == T_ARRAY) {
210
+ current_value = rb_ary_entry(current_value, -1);
211
+ }
212
+
213
+ rb_str_cat(current_value, at, length);
175
214
 
176
215
  return 0;
177
216
  }
@@ -280,8 +319,16 @@ VALUE Parser_initialize(int argc, VALUE *argv, VALUE self) {
280
319
  ParserWrapper *wrapper = NULL;
281
320
  DATA_GET(self, ParserWrapper, wrapper);
282
321
 
283
- if (argc == 1)
322
+ wrapper->header_value_type = rb_iv_get(CLASS_OF(self), "@default_header_value_type");
323
+
324
+ if (argc == 1) {
325
+ wrapper->callback_object = argv[0];
326
+ }
327
+
328
+ if (argc == 2) {
284
329
  wrapper->callback_object = argv[0];
330
+ wrapper->header_value_type = argv[1];
331
+ }
285
332
 
286
333
  return self;
287
334
  }
@@ -299,7 +346,8 @@ VALUE Parser_execute(VALUE self, VALUE data) {
299
346
  size_t nparsed = ryah_http_parser_execute(&wrapper->parser, &settings, ptr, len);
300
347
 
301
348
  if (wrapper->parser.upgrade) {
302
- // upgrade request
349
+ rb_str_cat(wrapper->upgrade_data, ptr + nparsed + 1, len - nparsed - 1);
350
+
303
351
  } else if (nparsed != len) {
304
352
  if (!RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
305
353
  rb_raise(eParserError, "Could not parse data entirely");
@@ -418,6 +466,19 @@ DEFINE_GETTER(request_path);
418
466
  DEFINE_GETTER(query_string);
419
467
  DEFINE_GETTER(fragment);
420
468
  DEFINE_GETTER(headers);
469
+ DEFINE_GETTER(upgrade_data);
470
+ DEFINE_GETTER(header_value_type);
471
+
472
+ VALUE Parser_set_header_value_type(VALUE self, VALUE val) {
473
+ if (val != Sarrays && val != Sstrings && val != Smixed) {
474
+ rb_raise(rb_eArgError, "Invalid header value type");
475
+ }
476
+
477
+ ParserWrapper *wrapper = NULL;
478
+ DATA_GET(self, ParserWrapper, wrapper);
479
+ wrapper->header_value_type = val;
480
+ return wrapper->header_value_type;
481
+ }
421
482
 
422
483
  VALUE Parser_reset(VALUE self) {
423
484
  ParserWrapper *wrapper = NULL;
@@ -442,6 +503,10 @@ void Init_ruby_http_parser() {
442
503
  Ion_message_complete = rb_intern("on_message_complete");
443
504
  Sstop = ID2SYM(rb_intern("stop"));
444
505
 
506
+ Sarrays = ID2SYM(rb_intern("arrays"));
507
+ Sstrings = ID2SYM(rb_intern("strings"));
508
+ Smixed = ID2SYM(rb_intern("mixed"));
509
+
445
510
  rb_define_alloc_func(cParser, Parser_alloc);
446
511
  rb_define_alloc_func(cRequestParser, RequestParser_alloc);
447
512
  rb_define_alloc_func(cResponseParser, ResponseParser_alloc);
@@ -469,6 +534,9 @@ void Init_ruby_http_parser() {
469
534
  rb_define_method(cParser, "query_string", Parser_query_string, 0);
470
535
  rb_define_method(cParser, "fragment", Parser_fragment, 0);
471
536
  rb_define_method(cParser, "headers", Parser_headers, 0);
537
+ rb_define_method(cParser, "upgrade_data", Parser_upgrade_data, 0);
538
+ rb_define_method(cParser, "header_value_type", Parser_header_value_type, 0);
539
+ rb_define_method(cParser, "header_value_type=", Parser_set_header_value_type, 1);
472
540
 
473
541
  rb_define_method(cParser, "reset!", Parser_reset, 0);
474
542
  }
@@ -1,4 +1,29 @@
1
- Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ Copyright 2010 Tim Becker <tim.becker@kuriositaet.de>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
+ IN THE SOFTWARE.
20
+
21
+ --- END OF LICENSE
22
+
23
+ This code mainly based on code with the following license:
24
+
25
+
26
+ Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
27
 
3
28
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
29
  of this software and associated documentation files (the "Software"), to
@@ -1,20 +1,20 @@
1
1
  HTTP Parser
2
2
  ===========
3
3
 
4
- This is a parser for HTTP messages written in C. It parses both requests and
5
- responses. The parser is designed to be used in performance HTTP
6
- applications. It does not make any syscalls nor allocations, it does not
7
- buffer data, it can be interrupted at anytime. Depending on your
8
- architecture, it only requires about 40 bytes of data per message
9
- stream (in a web server that is per connection).
4
+ This is a parser for HTTP written in Java, based quite heavily on
5
+ the Ryan Dahl's C Version: `http-parser` available here:
6
+
7
+ http://github.com/ry/http-parser
8
+
9
+ It parses both requests and responses. The parser is designed to be used
10
+ in performance HTTP applications.
10
11
 
11
12
  Features:
12
13
 
13
- * No dependencies
14
+ * No dependencies (probably won't be able to keep it up)
14
15
  * Handles persistent streams (keep-alive).
15
16
  * Decodes chunked encoding.
16
17
  * Upgrade support
17
- * Defends against buffer overflow attacks.
18
18
 
19
19
  The parser extracts the following information from HTTP messages:
20
20
 
@@ -27,145 +27,25 @@ The parser extracts the following information from HTTP messages:
27
27
  * Request path, query string, fragment
28
28
  * Message body
29
29
 
30
+ Building
31
+ --------
32
+
33
+ use `ant compile|test|jar`
30
34
 
31
35
  Usage
32
36
  -----
33
37
 
34
- One `http_parser` object is used per TCP connection. Initialize the struct
35
- using `http_parser_init()` and set the callbacks. That might look something
36
- like this for a request parser:
37
-
38
- http_parser_settings settings;
39
- settings.on_path = my_path_callback;
40
- settings.on_header_field = my_header_field_callback;
41
- /* ... */
42
- settings.data = my_socket;
43
-
44
- http_parser *parser = malloc(sizeof(http_parser));
45
- http_parser_init(parser, HTTP_REQUEST);
46
-
47
- When data is received on the socket execute the parser and check for errors.
48
-
49
- size_t len = 80*1024, nparsed;
50
- char buf[len];
51
- ssize_t recved;
52
-
53
- recved = recv(fd, buf, len, 0);
54
-
55
- if (recved < 0) {
56
- /* Handle error. */
57
- }
58
-
59
- /* Start up / continue the parser.
60
- * Note we pass recved==0 to signal that EOF has been recieved.
61
- */
62
- nparsed = http_parser_execute(parser, &settings, buf, recved);
63
-
64
- if (parser->upgrade) {
65
- /* handle new protocol */
66
- } else if (nparsed != recved) {
67
- /* Handle error. Usually just close the connection. */
68
- }
69
-
70
- HTTP needs to know where the end of the stream is. For example, sometimes
71
- servers send responses without Content-Length and expect the client to
72
- consume input (for the body) until EOF. To tell http_parser about EOF, give
73
- `0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
74
- can still be encountered during an EOF, so one must still be prepared
75
- to receive them.
76
-
77
- Scalar valued message information such as `status_code`, `method`, and the
78
- HTTP version are stored in the parser structure. This data is only
79
- temporally stored in `http_parser` and gets reset on each new message. If
80
- this information is needed later, copy it out of the structure during the
81
- `headers_complete` callback.
82
-
83
- The parser decodes the transfer-encoding for both requests and responses
84
- transparently. That is, a chunked encoding is decoded before being sent to
85
- the on_body callback.
86
-
87
-
88
- The Special Problem of Upgrade
89
- ------------------------------
90
-
91
- HTTP supports upgrading the connection to a different protocol. An
92
- increasingly common example of this is the Web Socket protocol which sends
93
- a request like
94
-
95
- GET /demo HTTP/1.1
96
- Upgrade: WebSocket
97
- Connection: Upgrade
98
- Host: example.com
99
- Origin: http://example.com
100
- WebSocket-Protocol: sample
101
-
102
- followed by non-HTTP data.
103
-
104
- (See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
105
- information the Web Socket protocol.)
106
-
107
- To support this, the parser will treat this as a normal HTTP message without a
108
- body. Issuing both on_headers_complete and on_message_complete callbacks. However
109
- http_parser_execute() will stop parsing at the end of the headers and return.
110
-
111
- The user is expected to check if `parser->upgrade` has been set to 1 after
112
- `http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
113
- offset by the return value of `http_parser_execute()`.
114
-
115
-
116
- Callbacks
117
- ---------
118
-
119
- During the `http_parser_execute()` call, the callbacks set in
120
- `http_parser_settings` will be executed. The parser maintains state and
121
- never looks behind, so buffering the data is not necessary. If you need to
122
- save certain data for later usage, you can do that from the callbacks.
123
-
124
- There are two types of callbacks:
125
-
126
- * notification `typedef int (*http_cb) (http_parser*);`
127
- Callbacks: on_message_begin, on_headers_complete, on_message_complete.
128
- * data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
129
- Callbacks: (requests only) on_path, on_query_string, on_uri, on_fragment,
130
- (common) on_header_field, on_header_value, on_body;
131
-
132
- Callbacks must return 0 on success. Returning a non-zero value indicates
133
- error to the parser, making it exit immediately.
134
-
135
- In case you parse HTTP message in chunks (i.e. `read()` request line
136
- from socket, parse, read half headers, parse, etc) your data callbacks
137
- may be called more than once. Http-parser guarantees that data pointer is only
138
- valid for the lifetime of callback. You can also `read()` into a heap allocated
139
- buffer to avoid copying memory around if this fits your application.
140
-
141
- Reading headers may be a tricky task if you read/parse headers partially.
142
- Basically, you need to remember whether last header callback was field or value
143
- and apply following logic:
144
-
145
- (on_header_field and on_header_value shortened to on_h_*)
146
- ------------------------ ------------ --------------------------------------------
147
- | State (prev. callback) | Callback | Description/action |
148
- ------------------------ ------------ --------------------------------------------
149
- | nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
150
- | | | into it |
151
- ------------------------ ------------ --------------------------------------------
152
- | value | on_h_field | New header started. |
153
- | | | Copy current name,value buffers to headers |
154
- | | | list and allocate new buffer for new name |
155
- ------------------------ ------------ --------------------------------------------
156
- | field | on_h_field | Previous name continues. Reallocate name |
157
- | | | buffer and append callback data to it |
158
- ------------------------ ------------ --------------------------------------------
159
- | field | on_h_value | Value for current header started. Allocate |
160
- | | | new buffer and copy callback data to it |
161
- ------------------------ ------------ --------------------------------------------
162
- | value | on_h_value | Value continues. Reallocate value buffer |
163
- | | | and append callback data to it |
164
- ------------------------ ------------ --------------------------------------------
38
+ TODO: in the present form, usage of the Java version of the parser
39
+ shouldn't be too difficult to figure out for someone familiar with the
40
+ C version.
165
41
 
42
+ More documentation will follow shortly, in case you're looking for an
43
+ easy to use http library, this lib is probably not what you are
44
+ looking for anyway ...
166
45
 
167
- See examples of reading in headers:
46
+ All text after this paragraph (and most of the text above it) are from
47
+ the original C version of the README and are currently only here for
48
+ reference. In case you encounter any difficulties, find bugs, need
49
+ help or have suggestions, feel free to contact me at
50
+ (tim.becker@kuriositaet.de).
168
51
 
169
- * [partial example](http://gist.github.com/155877) in C
170
- * [from http-parser tests](http://github.com/ry/http-parser/blob/37a0ff8928fb0d83cec0d0d8909c5a4abcd221af/test.c#L403) in C
171
- * [from Node library](http://github.com/ry/node/blob/842eaf446d2fdcb33b296c67c911c32a0dabc747/src/http.js#L284) in Javascript