http_parser.rb 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +11 -0
  2. data/.gitmodules +6 -0
  3. data/README.md +45 -0
  4. data/Rakefile +6 -0
  5. data/bench/thin.rb +57 -0
  6. data/ext/ruby_http_parser/.gitignore +1 -0
  7. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  8. data/ext/ruby_http_parser/ext_help.h +18 -0
  9. data/ext/ruby_http_parser/extconf.rb +16 -0
  10. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +403 -0
  11. data/ext/ruby_http_parser/ruby_http_parser.c +474 -0
  12. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +4 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +19 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +171 -0
  16. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +19 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/compile +1 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1590 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +167 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +7 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +90 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +31 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +1894 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +78 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/Util.java +112 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +487 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +115 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1865 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +539 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  40. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +4 -0
  41. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +19 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/README.md +171 -0
  43. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1590 -0
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +167 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/test.c +1755 -0
  46. data/http_parser.rb.gemspec +15 -0
  47. data/lib/http/parser.rb +1 -0
  48. data/lib/http_parser.rb +4 -0
  49. data/spec/parser_spec.rb +187 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/support/requests.json +381 -0
  52. data/spec/support/responses.json +186 -0
  53. data/tasks/compile.rake +39 -0
  54. data/tasks/spec.rake +5 -0
  55. data/tasks/submodules.rake +7 -0
  56. metadata +121 -0
@@ -0,0 +1,11 @@
1
+ tmp
2
+ *.bundle
3
+ *.gem
4
+ *.o
5
+ *.so
6
+ *.bundle
7
+ *.jar
8
+ *.swp
9
+ Makefile
10
+ tags
11
+ *.rbc
@@ -0,0 +1,6 @@
1
+ [submodule "ext/ruby_http_parser/vendor/http-parser"]
2
+ path = ext/ruby_http_parser/vendor/http-parser
3
+ url = git://github.com/ry/http-parser.git
4
+ [submodule "ext/ruby_http_parser/vendor/http-parser-java"]
5
+ path = ext/ruby_http_parser/vendor/http-parser-java
6
+ url = git://github.com/a2800276/http-parser.java.git
@@ -0,0 +1,45 @@
1
+ # http_parser.rb
2
+
3
+ A simple callback-based HTTP request/response parser for writing http
4
+ servers, clients and proxies.
5
+
6
+ This gem is built on top of [ry/http-parser](http://github.com/ry/http-parser) and its java port [a2800276/http-parser.java](http://github.com/a2800276/http-parser.java).
7
+
8
+ ## Supported Platforms
9
+
10
+ This gem aims to work on all major Ruby platforms, including:
11
+
12
+ - MRI 1.8 and 1.9
13
+ - Rubinius
14
+ - JRuby
15
+ - win32
16
+
17
+ ## Usage
18
+
19
+ require "http/parser"
20
+
21
+ parser = Http::Parser.new
22
+
23
+ parser.on_headers_complete = proc do |headers|
24
+ p parser.http_method
25
+ p parser.http_version
26
+
27
+ p parser.request_url # for requests
28
+ p parser.status_code # for responses
29
+
30
+ p headers
31
+ end
32
+
33
+ parser.on_body = proc do |chunk|
34
+ # One chunk of the body
35
+ p chunk
36
+ end
37
+
38
+ parser.on_message_complete = proc do |env|
39
+ # Headers and body is all parsed
40
+ puts "Done!"
41
+ end
42
+
43
+ # Feed raw data from the socket to the parser
44
+ parser << raw_data
45
+
@@ -0,0 +1,6 @@
1
+ # load tasks
2
+ Dir['tasks/*.rake'].sort.each { |f| load f }
3
+
4
+ # default task
5
+ task :compile => :submodules
6
+ task :default => [:compile, :spec]
@@ -0,0 +1,57 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+ require "rubygems"
3
+ require "thin_parser"
4
+ require "http_parser"
5
+ require "benchmark"
6
+
7
+ data = "POST /postit HTTP/1.1\r\n" +
8
+ "Host: localhost:3000\r\n" +
9
+ "User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9\r\n" +
10
+ "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n" +
11
+ "Accept-Language: en-us,en;q=0.5\r\n" +
12
+ "Accept-Encoding: gzip,deflate\r\n" +
13
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
14
+ "Keep-Alive: 300\r\n" +
15
+ "Connection: keep-alive\r\n" +
16
+ "Content-Type: text/html\r\n" +
17
+ "Content-Length: 37\r\n" +
18
+ "\r\n" +
19
+ "name=marc&email=macournoyer@gmail.com"
20
+
21
+ def thin(data)
22
+ env = {"rack.input" => StringIO.new}
23
+ Thin::HttpParser.new.execute(env, data, 0)
24
+ env
25
+ end
26
+
27
+ def http_parser(data)
28
+ body = StringIO.new
29
+ env = nil
30
+
31
+ parser = HTTP::RequestParser.new
32
+ parser.on_headers_complete = proc { |e| env = e }
33
+ parser.on_body = proc { |c| body << c }
34
+ parser << data
35
+
36
+ env["rack-input"] = body
37
+ env
38
+ end
39
+
40
+ # p thin(data)
41
+ # p http_parser(data)
42
+
43
+ TESTS = 30_000
44
+ Benchmark.bmbm do |results|
45
+ results.report("thin:") { TESTS.times { thin data } }
46
+ results.report("http-parser:") { TESTS.times { http_parser data } }
47
+ end
48
+
49
+ # On my MBP core duo 2.2Ghz
50
+ # Rehearsal ------------------------------------------------
51
+ # thin: 1.470000 0.000000 1.470000 ( 1.474737)
52
+ # http-parser: 1.270000 0.020000 1.290000 ( 1.292758)
53
+ # --------------------------------------- total: 2.760000sec
54
+ #
55
+ # user system total real
56
+ # thin: 1.150000 0.030000 1.180000 ( 1.173767)
57
+ # http-parser: 1.250000 0.010000 1.260000 ( 1.263796)
@@ -0,0 +1 @@
1
+ ryah_http_parser.*
@@ -0,0 +1,18 @@
1
+ import java.io.IOException;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyModule;
6
+ import org.jruby.runtime.load.BasicLibraryService;
7
+
8
+ import org.ruby_http_parser.*;
9
+
10
+ public class RubyHttpParserService implements BasicLibraryService {
11
+ public boolean basicLoad(final Ruby runtime) throws IOException {
12
+ RubyModule mHTTP = runtime.defineModule("HTTP");
13
+ RubyClass cParser = mHTTP.defineClassUnder("Parser", runtime.getObject(), RubyHttpParser.ALLOCATOR);
14
+ cParser.defineAnnotatedMethods(RubyHttpParser.class);
15
+ cParser.defineClassUnder("Error", runtime.getClass("IOError"),runtime.getClass("IOError").getAllocator());
16
+ return true;
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ #ifndef ext_help_h
2
+ #define ext_help_h
3
+
4
+ #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
5
+ #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
6
+ #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
7
+
8
+ /* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
9
+ #ifndef RSTRING_PTR
10
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
11
+ #endif
12
+
13
+ /* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
14
+ #ifndef RSTRING_LEN
15
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
16
+ #endif
17
+
18
+ #endif
@@ -0,0 +1,16 @@
1
+ require 'mkmf'
2
+
3
+ # mongrel and http-parser both define http_parser_(init|execute), so we
4
+ # rename functions in http-parser before using them.
5
+ vendor_dir = File.expand_path('../vendor/http-parser/', __FILE__)
6
+ src_dir = File.expand_path('../', __FILE__)
7
+ %w[ http_parser.c http_parser.h ].each do |file|
8
+ File.open(File.join(src_dir, "ryah_#{file}"), 'w'){ |f|
9
+ f.write File.read(File.join(vendor_dir, file)).gsub('http_parser', 'ryah_http_parser')
10
+ }
11
+ end
12
+
13
+ $CFLAGS << " -I#{src_dir}"
14
+
15
+ dir_config("ruby_http_parser")
16
+ create_makefile("ruby_http_parser")
@@ -0,0 +1,403 @@
1
+ package org.ruby_http_parser;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyHash;
6
+ import org.jruby.RubyModule;
7
+ import org.jruby.RubyNumeric;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.RubyString;
10
+
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+
15
+ import org.jruby.anno.JRubyMethod;
16
+ import org.jruby.exceptions.RaiseException;
17
+
18
+ import java.nio.ByteBuffer;
19
+ import http_parser.*;
20
+ import http_parser.lolevel.ParserSettings;
21
+ import http_parser.lolevel.HTTPCallback;
22
+ import http_parser.lolevel.HTTPDataCallback;
23
+
24
+ public class RubyHttpParser extends RubyObject {
25
+
26
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
27
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
28
+ return new RubyHttpParser(runtime, klass);
29
+ }
30
+ };
31
+
32
+ byte[] fetchBytes (ByteBuffer b, int pos, int len) {
33
+ byte[] by = new byte[len];
34
+ int saved = b.position();
35
+ b.position(pos);
36
+ b.get(by);
37
+ b.position(saved);
38
+ return by;
39
+ }
40
+
41
+ public class StopException extends RuntimeException {
42
+ }
43
+
44
+ private Ruby runtime;
45
+ private HTTPParser parser;
46
+ private ParserSettings settings;
47
+
48
+ private RubyClass eParserError;
49
+
50
+ private RubyHash headers;
51
+
52
+ private IRubyObject on_message_begin;
53
+ private IRubyObject on_headers_complete;
54
+ private IRubyObject on_body;
55
+ private IRubyObject on_message_complete;
56
+
57
+ private IRubyObject requestUrl;
58
+ private IRubyObject requestPath;
59
+ private IRubyObject queryString;
60
+ private IRubyObject fragment;
61
+
62
+ private IRubyObject callback_object;
63
+
64
+ private String _current_header;
65
+ private String _last_header;
66
+
67
+ public RubyHttpParser(final Ruby runtime, RubyClass clazz) {
68
+ super(runtime,clazz);
69
+
70
+ this.runtime = runtime;
71
+ this.eParserError = (RubyClass)runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
72
+
73
+ this.on_message_begin = null;
74
+ this.on_headers_complete = null;
75
+ this.on_body = null;
76
+ this.on_message_complete = null;
77
+
78
+ this.callback_object = null;
79
+
80
+ initSettings();
81
+ init();
82
+ }
83
+
84
+ private void initSettings() {
85
+ this.settings = new ParserSettings();
86
+
87
+ this.settings.on_url = new HTTPDataCallback() {
88
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
89
+ byte[] data = fetchBytes(buf, pos, len);
90
+ ((RubyString)requestUrl).concat(runtime.newString(new String(data)));
91
+ return 0;
92
+ }
93
+ };
94
+ this.settings.on_path = new HTTPDataCallback() {
95
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
96
+ byte[] data = fetchBytes(buf, pos, len);
97
+ ((RubyString)requestPath).concat(runtime.newString(new String(data)));
98
+ return 0;
99
+ }
100
+ };
101
+ this.settings.on_query_string = new HTTPDataCallback() {
102
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
103
+ byte[] data = fetchBytes(buf, pos, len);
104
+ ((RubyString)queryString).concat(runtime.newString(new String(data)));
105
+ return 0;
106
+ }
107
+ };
108
+ this.settings.on_fragment = new HTTPDataCallback() {
109
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
110
+ byte[] data = fetchBytes(buf, pos, len);
111
+ ((RubyString)fragment).concat(runtime.newString(new String(data)));
112
+ return 0;
113
+ }
114
+ };
115
+
116
+ this.settings.on_header_field = new HTTPDataCallback() {
117
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
118
+ byte[] data = fetchBytes(buf, pos, len);
119
+
120
+ if (_current_header == null)
121
+ _current_header = new String(data);
122
+ else
123
+ _current_header.concat(new String(data));
124
+
125
+ return 0;
126
+ }
127
+ };
128
+ this.settings.on_header_value = new HTTPDataCallback() {
129
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
130
+ byte[] data = fetchBytes(buf, pos, len);
131
+ ThreadContext context = headers.getRuntime().getCurrentContext();
132
+ IRubyObject key, val;
133
+
134
+ if (_current_header != null) {
135
+ _last_header = _current_header;
136
+ _current_header = null;
137
+
138
+ key = (IRubyObject)runtime.newString(_last_header);
139
+ val = headers.op_aref(context, key);
140
+
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);
146
+ }
147
+
148
+ if (val.isNil())
149
+ headers.op_aset(context, key, runtime.newString(new String(data)));
150
+ else
151
+ ((RubyString)val).cat(data);
152
+
153
+ return 0;
154
+ }
155
+ };
156
+
157
+ this.settings.on_message_begin = new HTTPCallback() {
158
+ public int cb (http_parser.lolevel.HTTPParser p) {
159
+ headers = new RubyHash(runtime);
160
+
161
+ requestUrl = runtime.newString("");
162
+ requestPath = runtime.newString("");
163
+ queryString = runtime.newString("");
164
+ fragment = runtime.newString("");
165
+
166
+ IRubyObject ret = runtime.getNil();
167
+
168
+ if (callback_object != null) {
169
+ if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_message_begin")).toJava(Boolean.class) == Boolean.TRUE) {
170
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
171
+ ret = callback_object.callMethod(context, "on_message_begin");
172
+ }
173
+ } else if (on_message_begin != null) {
174
+ ThreadContext context = on_message_begin.getRuntime().getCurrentContext();
175
+ ret = on_message_begin.callMethod(context, "call");
176
+ }
177
+
178
+ if (ret == runtime.newSymbol("stop")) {
179
+ throw new StopException();
180
+ } else {
181
+ return 0;
182
+ }
183
+ }
184
+ };
185
+ this.settings.on_message_complete = new HTTPCallback() {
186
+ public int cb (http_parser.lolevel.HTTPParser p) {
187
+ IRubyObject ret = runtime.getNil();
188
+
189
+ if (callback_object != null) {
190
+ if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_message_complete")).toJava(Boolean.class) == Boolean.TRUE) {
191
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
192
+ ret = callback_object.callMethod(context, "on_message_complete");
193
+ }
194
+ } else if (on_message_complete != null) {
195
+ ThreadContext context = on_message_complete.getRuntime().getCurrentContext();
196
+ ret = on_message_complete.callMethod(context, "call");
197
+ }
198
+
199
+ if (ret == runtime.newSymbol("stop")) {
200
+ throw new StopException();
201
+ } else {
202
+ return 0;
203
+ }
204
+ }
205
+ };
206
+ this.settings.on_headers_complete = new HTTPCallback() {
207
+ public int cb (http_parser.lolevel.HTTPParser p) {
208
+ IRubyObject ret = runtime.getNil();
209
+
210
+ if (callback_object != null) {
211
+ if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_headers_complete")).toJava(Boolean.class) == Boolean.TRUE) {
212
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
213
+ ret = callback_object.callMethod(context, "on_headers_complete", headers);
214
+ }
215
+ } else if (on_headers_complete != null) {
216
+ ThreadContext context = on_headers_complete.getRuntime().getCurrentContext();
217
+ ret = on_headers_complete.callMethod(context, "call", headers);
218
+ }
219
+
220
+ if (ret == runtime.newSymbol("stop")) {
221
+ throw new StopException();
222
+ } else {
223
+ return 0;
224
+ }
225
+ }
226
+ };
227
+ this.settings.on_body = new HTTPDataCallback() {
228
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
229
+ IRubyObject ret = runtime.getNil();
230
+ byte[] data = fetchBytes(buf, pos, len);
231
+
232
+ if (callback_object != null) {
233
+ if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_body")).toJava(Boolean.class) == Boolean.TRUE) {
234
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
235
+ ret = callback_object.callMethod(context, "on_body", callback_object.getRuntime().newString(new String(data)));
236
+ }
237
+ } else if (on_body != null) {
238
+ ThreadContext context = on_body.getRuntime().getCurrentContext();
239
+ ret = on_body.callMethod(context, "call", on_body.getRuntime().newString(new String(data)));
240
+ }
241
+
242
+ if (ret == runtime.newSymbol("stop")) {
243
+ throw new StopException();
244
+ } else {
245
+ return 0;
246
+ }
247
+ }
248
+ };
249
+ }
250
+
251
+ private void init() {
252
+ this.parser = new HTTPParser();
253
+ this.headers = null;
254
+
255
+ this.requestUrl = runtime.getNil();
256
+ this.requestPath = runtime.getNil();
257
+ this.queryString = runtime.getNil();
258
+ this.fragment = runtime.getNil();
259
+ }
260
+
261
+ @JRubyMethod(name = "initialize")
262
+ public IRubyObject initialize() {
263
+ return this;
264
+ }
265
+
266
+ @JRubyMethod(name = "initialize")
267
+ public IRubyObject initialize(IRubyObject arg) {
268
+ callback_object = arg;
269
+ return initialize();
270
+ }
271
+
272
+ @JRubyMethod(name = "on_message_begin=")
273
+ public IRubyObject set_on_message_begin(IRubyObject cb) {
274
+ on_message_begin = cb;
275
+ return cb;
276
+ }
277
+
278
+ @JRubyMethod(name = "on_headers_complete=")
279
+ public IRubyObject set_on_headers_complete(IRubyObject cb) {
280
+ on_headers_complete = cb;
281
+ return cb;
282
+ }
283
+
284
+ @JRubyMethod(name = "on_body=")
285
+ public IRubyObject set_on_body(IRubyObject cb) {
286
+ on_body = cb;
287
+ return cb;
288
+ }
289
+
290
+ @JRubyMethod(name = "on_message_complete=")
291
+ public IRubyObject set_on_message_complete(IRubyObject cb) {
292
+ on_message_complete = cb;
293
+ return cb;
294
+ }
295
+
296
+ @JRubyMethod(name = "<<")
297
+ public IRubyObject execute(IRubyObject data) {
298
+ RubyString str = (RubyString)data;
299
+ ByteBuffer buf = ByteBuffer.wrap(str.getBytes());
300
+ boolean stopped = false;
301
+
302
+ try {
303
+ this.parser.execute(this.settings, buf);
304
+ } catch (HTTPException e) {
305
+ throw new RaiseException(runtime, eParserError, e.getMessage(), true);
306
+ } catch (StopException e) {
307
+ stopped = true;
308
+ }
309
+
310
+ if (parser.getUpgrade()) {
311
+ // upgrade request
312
+ } else if (buf.hasRemaining()) {
313
+ if (!stopped)
314
+ throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
315
+ }
316
+
317
+ return RubyNumeric.int2fix(runtime, buf.position());
318
+ }
319
+
320
+ @JRubyMethod(name = "keep_alive?")
321
+ public IRubyObject shouldKeepAlive() {
322
+ return parser.shouldKeepAlive() ? runtime.getTrue() : runtime.getFalse();
323
+ }
324
+
325
+ @JRubyMethod(name = "upgrade?")
326
+ public IRubyObject shouldUpgrade() {
327
+ return parser.getUpgrade() ? runtime.getTrue() : runtime.getFalse();
328
+ }
329
+
330
+ @JRubyMethod(name = "http_major")
331
+ public IRubyObject httpMajor() {
332
+ if (parser.getMajor() == 0 && parser.getMinor() == 0)
333
+ return runtime.getNil();
334
+ else
335
+ return RubyNumeric.int2fix(runtime, parser.getMajor());
336
+ }
337
+
338
+ @JRubyMethod(name = "http_minor")
339
+ public IRubyObject httpMinor() {
340
+ if (parser.getMajor() == 0 && parser.getMinor() == 0)
341
+ return runtime.getNil();
342
+ else
343
+ return RubyNumeric.int2fix(runtime, parser.getMinor());
344
+ }
345
+
346
+ @JRubyMethod(name = "http_version")
347
+ public IRubyObject httpVersion() {
348
+ if (parser.getMajor() == 0 && parser.getMinor() == 0)
349
+ return runtime.getNil();
350
+ else
351
+ return runtime.newArray(httpMajor(), httpMinor());
352
+ }
353
+
354
+ @JRubyMethod(name = "http_method")
355
+ public IRubyObject httpMethod() {
356
+ HTTPMethod method = parser.getHTTPMethod();
357
+ if (method != null)
358
+ return runtime.newString(new String(method.bytes));
359
+ else
360
+ return runtime.getNil();
361
+ }
362
+
363
+ @JRubyMethod(name = "status_code")
364
+ public IRubyObject statusCode() {
365
+ int code = parser.getStatusCode();
366
+ if (code != 0)
367
+ return RubyNumeric.int2fix(runtime, code);
368
+ else
369
+ return runtime.getNil();
370
+ }
371
+
372
+ @JRubyMethod(name = "headers")
373
+ public IRubyObject getHeaders() {
374
+ return headers == null ? runtime.getNil() : headers;
375
+ }
376
+
377
+ @JRubyMethod(name = "request_url")
378
+ public IRubyObject getRequestUrl() {
379
+ return requestUrl == null ? runtime.getNil() : requestUrl;
380
+ }
381
+
382
+ @JRubyMethod(name = "request_path")
383
+ public IRubyObject getRequestPath() {
384
+ return requestPath == null ? runtime.getNil() : requestPath;
385
+ }
386
+
387
+ @JRubyMethod(name = "query_string")
388
+ public IRubyObject getQueryString() {
389
+ return queryString == null ? runtime.getNil() : queryString;
390
+ }
391
+
392
+ @JRubyMethod(name = "fragment")
393
+ public IRubyObject getFragment() {
394
+ return fragment == null ? runtime.getNil() : fragment;
395
+ }
396
+
397
+ @JRubyMethod(name = "reset!")
398
+ public IRubyObject reset() {
399
+ init();
400
+ return runtime.getTrue();
401
+ }
402
+
403
+ }