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

Sign up to get free protection for your applications and to get access to all the features.
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