http_parser.rb 0.5.1-x86-mingw32 → 0.5.2-x86-mingw32

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 (50) 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/LICENSE-MIT +1 -1
  6. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +52 -10
  7. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +3 -1
  8. data/ext/ruby_http_parser/vendor/http-parser/test.c +89 -3
  9. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +26 -1
  10. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +23 -143
  11. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +3 -0
  12. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/ext/primitives.jar +0 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +115 -61
  15. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +19 -3
  16. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +64 -52
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +27 -28
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +259 -85
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +324 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +51 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +15 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +47 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +183 -447
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +61 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +2 -1
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +26 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +165 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +58 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +232 -29
  38. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -1
  39. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -1
  40. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +1 -0
  41. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +154 -7
  42. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  43. data/http_parser.rb.gemspec +8 -2
  44. data/lib/1.8/ruby_http_parser.so +0 -0
  45. data/lib/1.9/ruby_http_parser.so +0 -0
  46. data/lib/http_parser.rb +17 -0
  47. data/spec/parser_spec.rb +97 -6
  48. data/tasks/compile.rake +3 -1
  49. metadata +83 -20
  50. 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,4 @@
1
- Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to
@@ -1,4 +1,4 @@
1
- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
2
  *
3
3
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  * of this software and associated documentation files (the "Software"), to
@@ -186,7 +186,28 @@ static const uint8_t normal_url_char[256] = {
186
186
  /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
187
187
  1, 1, 1, 1, 1, 1, 1, 1,
188
188
  /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
189
- 1, 1, 1, 1, 1, 1, 1, 0 };
189
+ 1, 1, 1, 1, 1, 1, 1, 0,
190
+
191
+ /* Remainder of non-ASCII range are accepted as-is to support implicitly UTF-8
192
+ encoded paths. This is out of spec, but clients generate this and most other
193
+ HTTP servers support it. We should, too. */
194
+
195
+ 1, 1, 1, 1, 1, 1, 1, 1,
196
+ 1, 1, 1, 1, 1, 1, 1, 1,
197
+ 1, 1, 1, 1, 1, 1, 1, 1,
198
+ 1, 1, 1, 1, 1, 1, 1, 1,
199
+ 1, 1, 1, 1, 1, 1, 1, 1,
200
+ 1, 1, 1, 1, 1, 1, 1, 1,
201
+ 1, 1, 1, 1, 1, 1, 1, 1,
202
+ 1, 1, 1, 1, 1, 1, 1, 1,
203
+ 1, 1, 1, 1, 1, 1, 1, 1,
204
+ 1, 1, 1, 1, 1, 1, 1, 1,
205
+ 1, 1, 1, 1, 1, 1, 1, 1,
206
+ 1, 1, 1, 1, 1, 1, 1, 1,
207
+ 1, 1, 1, 1, 1, 1, 1, 1,
208
+ 1, 1, 1, 1, 1, 1, 1, 1,
209
+ 1, 1, 1, 1, 1, 1, 1, 1,
210
+ 1, 1, 1, 1, 1, 1, 1, 1 };
190
211
 
191
212
 
192
213
  enum state
@@ -240,15 +261,17 @@ enum state
240
261
 
241
262
  , s_header_almost_done
242
263
 
264
+ , s_chunk_size_start
265
+ , s_chunk_size
266
+ , s_chunk_parameters
267
+ , s_chunk_size_almost_done
268
+
243
269
  , s_headers_almost_done
244
270
  /* Important: 's_headers_almost_done' must be the last 'header' state. All
245
271
  * states beyond this must be 'body' states. It is used for overflow
246
272
  * checking. See the PARSING_HEADER() macro.
247
273
  */
248
- , s_chunk_size_start
249
- , s_chunk_size
250
- , s_chunk_size_almost_done
251
- , s_chunk_parameters
274
+
252
275
  , s_chunk_data
253
276
  , s_chunk_data_almost_done
254
277
  , s_chunk_data_done
@@ -258,7 +281,7 @@ enum state
258
281
  };
259
282
 
260
283
 
261
- #define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
284
+ #define PARSING_HEADER(state) (state <= s_headers_almost_done)
262
285
 
263
286
 
264
287
  enum header_states
@@ -331,10 +354,20 @@ size_t http_parser_execute (http_parser *parser,
331
354
  uint64_t nread = parser->nread;
332
355
 
333
356
  if (len == 0) {
334
- if (state == s_body_identity_eof) {
335
- CALLBACK2(message_complete);
357
+ switch (state) {
358
+ case s_body_identity_eof:
359
+ CALLBACK2(message_complete);
360
+ return 0;
361
+
362
+ case s_dead:
363
+ case s_start_req_or_res:
364
+ case s_start_res:
365
+ case s_start_req:
366
+ return 0;
367
+
368
+ default:
369
+ return 1; // error
336
370
  }
337
- return 0;
338
371
  }
339
372
 
340
373
  /* technically we could combine all of these (except for url_mark) into one
@@ -707,6 +740,9 @@ size_t http_parser_execute (http_parser *parser,
707
740
  CALLBACK(url);
708
741
  state = s_req_http_start;
709
742
  break;
743
+ case '?':
744
+ state = s_req_query_string_start;
745
+ break;
710
746
  default:
711
747
  goto error;
712
748
  }
@@ -729,6 +765,9 @@ size_t http_parser_execute (http_parser *parser,
729
765
  CALLBACK(url);
730
766
  state = s_req_http_start;
731
767
  break;
768
+ case '?':
769
+ state = s_req_query_string_start;
770
+ break;
732
771
  default:
733
772
  goto error;
734
773
  }
@@ -1448,6 +1487,7 @@ size_t http_parser_execute (http_parser *parser,
1448
1487
 
1449
1488
  case s_chunk_size_start:
1450
1489
  {
1490
+ assert(nread == 1);
1451
1491
  assert(parser->flags & F_CHUNKED);
1452
1492
 
1453
1493
  c = unhex[(unsigned char)ch];
@@ -1497,6 +1537,8 @@ size_t http_parser_execute (http_parser *parser,
1497
1537
  assert(parser->flags & F_CHUNKED);
1498
1538
  STRICT_CHECK(ch != LF);
1499
1539
 
1540
+ nread = 0;
1541
+
1500
1542
  if (parser->content_length == 0) {
1501
1543
  parser->flags |= F_TRAILING;
1502
1544
  state = s_header_field_start;
@@ -1,4 +1,4 @@
1
- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
2
  *
3
3
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  * of this software and associated documentation files (the "Software"), to
@@ -24,6 +24,8 @@
24
24
  extern "C" {
25
25
  #endif
26
26
 
27
+ #define HTTP_PARSER_VERSION_MAJOR 1
28
+ #define HTTP_PARSER_VERSION_MINOR 0
27
29
 
28
30
  #include <sys/types.h>
29
31
  #if defined(_WIN32) && !defined(__MINGW32__)
@@ -1,4 +1,4 @@
1
- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
2
  *
3
3
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  * of this software and associated documentation files (the "Software"), to
@@ -557,7 +557,7 @@ const struct message requests[] =
557
557
  ,.body= ""
558
558
  }
559
559
 
560
- #define MSEARCH_REQ 19
560
+ #define MSEARCH_REQ 20
561
561
  , {.name= "m-search request"
562
562
  ,.type= HTTP_REQUEST
563
563
  ,.raw= "M-SEARCH * HTTP/1.1\r\n"
@@ -582,6 +582,84 @@ const struct message requests[] =
582
582
  ,.body= ""
583
583
  }
584
584
 
585
+ #define UTF8_PATH_REQ 21
586
+ , {.name= "utf-8 path request"
587
+ ,.type= HTTP_REQUEST
588
+ ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
589
+ "Host: github.com\r\n"
590
+ "\r\n"
591
+ ,.should_keep_alive= TRUE
592
+ ,.message_complete_on_eof= FALSE
593
+ ,.http_major= 1
594
+ ,.http_minor= 1
595
+ ,.method= HTTP_GET
596
+ ,.query_string= "q=1"
597
+ ,.fragment= "narf"
598
+ ,.request_path= "/δ¶/δt/pope"
599
+ ,.request_url= "/δ¶/δt/pope?q=1#narf"
600
+ ,.num_headers= 1
601
+ ,.headers= { {"Host", "github.com" }
602
+ }
603
+ ,.body= ""
604
+ }
605
+
606
+ #define QUERY_TERMINATED_HOST 22
607
+ , {.name= "host terminated by a query string"
608
+ ,.type= HTTP_REQUEST
609
+ ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
610
+ "\r\n"
611
+ ,.should_keep_alive= TRUE
612
+ ,.message_complete_on_eof= FALSE
613
+ ,.http_major= 1
614
+ ,.http_minor= 1
615
+ ,.method= HTTP_GET
616
+ ,.query_string= "hail=all"
617
+ ,.fragment= ""
618
+ ,.request_path= ""
619
+ ,.request_url= "http://hypnotoad.org?hail=all"
620
+ ,.num_headers= 0
621
+ ,.headers= { }
622
+ ,.body= ""
623
+ }
624
+
625
+ #define QUERY_TERMINATED_HOSTPORT 23
626
+ , {.name= "host:port terminated by a query string"
627
+ ,.type= HTTP_REQUEST
628
+ ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
629
+ "\r\n"
630
+ ,.should_keep_alive= TRUE
631
+ ,.message_complete_on_eof= FALSE
632
+ ,.http_major= 1
633
+ ,.http_minor= 1
634
+ ,.method= HTTP_GET
635
+ ,.query_string= "hail=all"
636
+ ,.fragment= ""
637
+ ,.request_path= ""
638
+ ,.request_url= "http://hypnotoad.org:1234?hail=all"
639
+ ,.num_headers= 0
640
+ ,.headers= { }
641
+ ,.body= ""
642
+ }
643
+
644
+ #define SPACE_TERMINATED_HOSTPORT 24
645
+ , {.name= "host:port terminated by a space"
646
+ ,.type= HTTP_REQUEST
647
+ ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
648
+ "\r\n"
649
+ ,.should_keep_alive= TRUE
650
+ ,.message_complete_on_eof= FALSE
651
+ ,.http_major= 1
652
+ ,.http_minor= 1
653
+ ,.method= HTTP_GET
654
+ ,.query_string= ""
655
+ ,.fragment= ""
656
+ ,.request_path= ""
657
+ ,.request_url= "http://hypnotoad.org:1234"
658
+ ,.num_headers= 0
659
+ ,.headers= { }
660
+ ,.body= ""
661
+ }
662
+
585
663
  , {.name= NULL } /* sentinel */
586
664
  };
587
665
 
@@ -1735,14 +1813,22 @@ main (void)
1735
1813
 
1736
1814
  /// REQUESTS
1737
1815
 
1738
-
1739
1816
  test_simple("hello world", 0);
1740
1817
  test_simple("GET / HTP/1.1\r\n\r\n", 0);
1741
1818
 
1819
+
1742
1820
  test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1743
1821
  test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1744
1822
  test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
1745
1823
 
1824
+ // Well-formed but incomplete
1825
+ test_simple("GET / HTTP/1.1\r\n"
1826
+ "Content-Type: text/plain\r\n"
1827
+ "Content-Length: 6\r\n"
1828
+ "\r\n"
1829
+ "fooba",
1830
+ 0);
1831
+
1746
1832
  static const char *all_methods[] = {
1747
1833
  "DELETE",
1748
1834
  "GET",
@@ -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