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.
- data/Gemfile.lock +16 -16
- data/LICENSE-MIT +20 -0
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +68 -11
- data/ext/ruby_http_parser/ruby_http_parser.c +74 -6
- data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +26 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/README.md +23 -143
- data/ext/ruby_http_parser/vendor/http-parser-java/TODO +3 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +115 -61
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +19 -3
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +64 -52
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +27 -28
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +259 -85
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +324 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +51 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +15 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +47 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +183 -447
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +61 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +26 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +165 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +58 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +232 -29
- data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +154 -7
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +52 -10
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +3 -1
- data/ext/ruby_http_parser/vendor/http-parser/test.c +89 -3
- data/http_parser.rb.gemspec +8 -2
- data/lib/http_parser.rb +17 -0
- data/spec/parser_spec.rb +97 -6
- data/tasks/compile.rake +3 -1
- metadata +83 -20
- 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.
|
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.
|
11
|
-
rake (0.
|
12
|
-
rake-compiler (0.7.
|
13
|
-
rake
|
14
|
-
rspec (2.0
|
15
|
-
rspec-core (~> 2.0
|
16
|
-
rspec-expectations (~> 2.0
|
17
|
-
rspec-mocks (~> 2.0
|
18
|
-
rspec-core (2.0
|
19
|
-
rspec-expectations (2.0
|
20
|
-
diff-lcs (
|
21
|
-
rspec-mocks (2.0
|
22
|
-
|
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.
|
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
|
-
|
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
|
-
|
142
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|