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

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 (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