jls-http_parser.rb 0.5.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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/joyent/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
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ http_parser.rb (0.5.3)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ benchmark_suite (0.8.0)
10
+ diff-lcs (1.1.2)
11
+ ffi (1.0.11)
12
+ ffi (1.0.11-java)
13
+ json (1.5.1)
14
+ json (1.5.1-java)
15
+ rake (0.9.2)
16
+ rake-compiler (0.7.9)
17
+ rake
18
+ rspec (2.4.0)
19
+ rspec-core (~> 2.4.0)
20
+ rspec-expectations (~> 2.4.0)
21
+ rspec-mocks (~> 2.4.0)
22
+ rspec-core (2.4.0)
23
+ rspec-expectations (2.4.0)
24
+ diff-lcs (~> 1.1.2)
25
+ rspec-mocks (2.4.0)
26
+ yajl-ruby (1.1.0)
27
+
28
+ PLATFORMS
29
+ java
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ benchmark_suite
34
+ ffi
35
+ http_parser.rb!
36
+ json (>= 1.4.6)
37
+ rake-compiler (>= 0.7.9)
38
+ rspec (>= 2.0.1)
39
+ yajl-ruby (>= 0.8.1)
@@ -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.
@@ -0,0 +1,90 @@
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 [joyent/http-parser](http://github.com/joyent/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
+ ```ruby
20
+ require "http/parser"
21
+
22
+ parser = Http::Parser.new
23
+
24
+ parser.on_headers_complete = proc do
25
+ p parser.http_version
26
+
27
+ p parser.http_method # for requests
28
+ p parser.request_url
29
+
30
+ p parser.status_code # for responses
31
+
32
+ p parser.headers
33
+ end
34
+
35
+ parser.on_body = proc do |chunk|
36
+ # One chunk of the body
37
+ p chunk
38
+ end
39
+
40
+ parser.on_message_complete = proc do |env|
41
+ # Headers and body is all parsed
42
+ puts "Done!"
43
+ end
44
+ ```
45
+
46
+ # Feed raw data from the socket to the parser
47
+ `parser << raw_data`
48
+
49
+ ## Advanced Usage
50
+
51
+ ### Accept callbacks on an object
52
+
53
+ ```ruby
54
+ module MyHttpConnection
55
+ def connection_completed
56
+ @parser = Http::Parser.new(self)
57
+ end
58
+
59
+ def receive_data(data)
60
+ @parser << data
61
+ end
62
+
63
+ def on_message_begin
64
+ @headers = nil
65
+ @body = ''
66
+ end
67
+
68
+ def on_headers_complete(headers)
69
+ @headers = headers
70
+ end
71
+
72
+ def on_body(chunk)
73
+ @body << chunk
74
+ end
75
+
76
+ def on_message_complete
77
+ p [@headers, @body]
78
+ end
79
+ end
80
+ ```
81
+
82
+ ### Stop parsing after headers
83
+
84
+ ```ruby
85
+ parser = Http::Parser.new
86
+ parser.on_headers_complete = proc{ :stop }
87
+
88
+ offset = parser << request_data
89
+ body = request_data[offset..-1]
90
+ ```
@@ -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,23 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.dirname(__FILE__) + "/../lib"
3
+ require "rubygems"
4
+ require "http/parser"
5
+ require "benchmark/ips"
6
+
7
+ request = <<-REQUEST
8
+ GET / HTTP/1.1
9
+ Host: www.example.com
10
+ Connection: keep-alive
11
+ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 S
12
+ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
13
+ Accept-Encoding: gzip,deflate,sdch
14
+ Accept-Language: en-US,en;q=0.8
15
+ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
16
+
17
+ REQUEST
18
+ request.gsub!(/\n/m, "\r\n")
19
+
20
+ Benchmark.ips do |ips|
21
+ ips.report("instance") { Http::Parser.new }
22
+ ips.report("parsing") { Http::Parser.new << request }
23
+ end
@@ -0,0 +1,58 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+ require "rubygems"
3
+ require "thin_parser"
4
+ require "http_parser"
5
+ require "benchmark"
6
+ require "stringio"
7
+
8
+ data = "POST /postit HTTP/1.1\r\n" +
9
+ "Host: localhost:3000\r\n" +
10
+ "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" +
11
+ "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" +
12
+ "Accept-Language: en-us,en;q=0.5\r\n" +
13
+ "Accept-Encoding: gzip,deflate\r\n" +
14
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
15
+ "Keep-Alive: 300\r\n" +
16
+ "Connection: keep-alive\r\n" +
17
+ "Content-Type: text/html\r\n" +
18
+ "Content-Length: 37\r\n" +
19
+ "\r\n" +
20
+ "name=marc&email=macournoyer@gmail.com"
21
+
22
+ def thin(data)
23
+ env = {"rack.input" => StringIO.new}
24
+ Thin::HttpParser.new.execute(env, data, 0)
25
+ env
26
+ end
27
+
28
+ def http_parser(data)
29
+ body = StringIO.new
30
+ env = nil
31
+
32
+ parser = HTTP::RequestParser.new
33
+ parser.on_headers_complete = proc { |e| env = e }
34
+ parser.on_body = proc { |c| body << c }
35
+ parser << data
36
+
37
+ env["rack-input"] = body
38
+ env
39
+ end
40
+
41
+ # p thin(data)
42
+ # p http_parser(data)
43
+
44
+ TESTS = 30_000
45
+ Benchmark.bmbm do |results|
46
+ results.report("thin:") { TESTS.times { thin data } }
47
+ results.report("http-parser:") { TESTS.times { http_parser data } }
48
+ end
49
+
50
+ # On my MBP core duo 2.2Ghz
51
+ # Rehearsal ------------------------------------------------
52
+ # thin: 1.470000 0.000000 1.470000 ( 1.474737)
53
+ # http-parser: 1.270000 0.020000 1.290000 ( 1.292758)
54
+ # --------------------------------------- total: 2.760000sec
55
+ #
56
+ # user system total real
57
+ # thin: 1.150000 0.030000 1.180000 ( 1.173767)
58
+ # 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,24 @@
1
+ require 'mkmf'
2
+
3
+ # check out code if it hasn't been already
4
+ if Dir[File.expand_path('../vendor/http-parser/*', __FILE__)].empty?
5
+ Dir.chdir(File.expand_path('../../../', __FILE__)) do
6
+ xsystem 'git submodule init'
7
+ xsystem 'git submodule update'
8
+ end
9
+ end
10
+
11
+ # mongrel and http-parser both define http_parser_(init|execute), so we
12
+ # rename functions in http-parser before using them.
13
+ vendor_dir = File.expand_path('../vendor/http-parser/', __FILE__)
14
+ src_dir = File.expand_path('../', __FILE__)
15
+ %w[ http_parser.c http_parser.h ].each do |file|
16
+ File.open(File.join(src_dir, "ryah_#{file}"), 'w'){ |f|
17
+ f.write File.read(File.join(vendor_dir, file)).gsub('http_parser', 'ryah_http_parser')
18
+ }
19
+ end
20
+
21
+ $CFLAGS << " -I#{src_dir}"
22
+
23
+ dir_config("ruby_http_parser")
24
+ create_makefile("ruby_http_parser")
@@ -0,0 +1,474 @@
1
+ package org.ruby_http_parser;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyHash;
7
+ import org.jruby.RubyModule;
8
+ import org.jruby.RubyNumeric;
9
+ import org.jruby.RubyObject;
10
+ import org.jruby.RubyString;
11
+ import org.jruby.RubySymbol;
12
+
13
+ import org.jruby.runtime.ObjectAllocator;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+
17
+ import org.jruby.anno.JRubyMethod;
18
+ import org.jruby.exceptions.RaiseException;
19
+
20
+ import org.jruby.util.ByteList;
21
+
22
+ import org.jcodings.specific.UTF8Encoding;
23
+
24
+ import java.nio.ByteBuffer;
25
+ import http_parser.*;
26
+ import http_parser.lolevel.ParserSettings;
27
+ import http_parser.lolevel.HTTPCallback;
28
+ import http_parser.lolevel.HTTPDataCallback;
29
+
30
+ public class RubyHttpParser extends RubyObject {
31
+
32
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
33
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
34
+ return new RubyHttpParser(runtime, klass);
35
+ }
36
+ };
37
+
38
+ byte[] fetchBytes (ByteBuffer b, int pos, int len) {
39
+ byte[] by = new byte[len];
40
+ int saved = b.position();
41
+ b.position(pos);
42
+ b.get(by);
43
+ b.position(saved);
44
+ return by;
45
+ }
46
+
47
+ public class StopException extends RuntimeException {
48
+ }
49
+
50
+ private Ruby runtime;
51
+ private HTTPParser parser;
52
+ private ParserSettings settings;
53
+
54
+ private RubyClass eParserError;
55
+
56
+ private RubyHash headers;
57
+
58
+ private IRubyObject on_message_begin;
59
+ private IRubyObject on_headers_complete;
60
+ private IRubyObject on_body;
61
+ private IRubyObject on_message_complete;
62
+
63
+ private IRubyObject requestUrl;
64
+ private IRubyObject requestPath;
65
+ private IRubyObject queryString;
66
+ private IRubyObject fragment;
67
+
68
+ private IRubyObject header_value_type;
69
+ private IRubyObject upgradeData;
70
+
71
+ private IRubyObject callback_object;
72
+
73
+ private byte[] _current_header;
74
+ private byte[] _last_header;
75
+
76
+ public RubyHttpParser(final Ruby runtime, RubyClass clazz) {
77
+ super(runtime,clazz);
78
+
79
+ this.runtime = runtime;
80
+ this.eParserError = (RubyClass)runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
81
+
82
+ this.on_message_begin = null;
83
+ this.on_headers_complete = null;
84
+ this.on_body = null;
85
+ this.on_message_complete = null;
86
+
87
+ this.callback_object = null;
88
+
89
+ this.header_value_type = runtime.getModule("HTTP").getClass("Parser").getInstanceVariable("@default_header_value_type");
90
+
91
+ initSettings();
92
+ init();
93
+ }
94
+
95
+ private void initSettings() {
96
+ this.settings = new ParserSettings();
97
+
98
+ this.settings.on_url = new HTTPDataCallback() {
99
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
100
+ byte[] data = fetchBytes(buf, pos, len);
101
+ ((RubyString)requestUrl).cat(data);
102
+ return 0;
103
+ }
104
+ };
105
+ this.settings.on_path = new HTTPDataCallback() {
106
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
107
+ byte[] data = fetchBytes(buf, pos, len);
108
+ ((RubyString)requestPath).cat(data);
109
+ return 0;
110
+ }
111
+ };
112
+ this.settings.on_query_string = new HTTPDataCallback() {
113
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
114
+ byte[] data = fetchBytes(buf, pos, len);
115
+ ((RubyString)queryString).cat(data);
116
+ return 0;
117
+ }
118
+ };
119
+ this.settings.on_fragment = new HTTPDataCallback() {
120
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
121
+ byte[] data = fetchBytes(buf, pos, len);
122
+ ((RubyString)fragment).cat(data);
123
+ return 0;
124
+ }
125
+ };
126
+
127
+ this.settings.on_header_field = new HTTPDataCallback() {
128
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
129
+ byte[] data = fetchBytes(buf, pos, len);
130
+
131
+ if (_current_header == null)
132
+ _current_header = data;
133
+ else {
134
+ byte[] tmp = new byte[_current_header.length + data.length];
135
+ System.arraycopy(_current_header, 0, tmp, 0, _current_header.length);
136
+ System.arraycopy(data, 0, tmp, _current_header.length, data.length);
137
+ _current_header = tmp;
138
+ }
139
+
140
+ return 0;
141
+ }
142
+ };
143
+ final RubySymbol arraysSym = runtime.newSymbol("arrays");
144
+ final RubySymbol mixedSym = runtime.newSymbol("mixed");
145
+ final RubySymbol stopSym = runtime.newSymbol("stop");
146
+ this.settings.on_header_value = new HTTPDataCallback() {
147
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
148
+ byte[] data = fetchBytes(buf, pos, len);
149
+ ThreadContext context = headers.getRuntime().getCurrentContext();
150
+ IRubyObject key, val;
151
+ int new_field = 0;
152
+
153
+ if (_current_header != null) {
154
+ new_field = 1;
155
+ _last_header = _current_header;
156
+ _current_header = null;
157
+ }
158
+
159
+ key = RubyString.newString(runtime, new ByteList(_last_header, UTF8Encoding.INSTANCE, false));
160
+ val = headers.op_aref(context, key);
161
+
162
+ if (new_field == 1) {
163
+ if (val.isNil()) {
164
+ if (header_value_type == arraysSym) {
165
+ headers.op_aset(context, key, RubyArray.newArrayLight(runtime, RubyString.newStringLight(runtime, 10)));
166
+ } else {
167
+ headers.op_aset(context, key, RubyString.newStringLight(runtime, 10));
168
+ }
169
+ } else {
170
+ if (header_value_type == mixedSym) {
171
+ if (val instanceof RubyString) {
172
+ headers.op_aset(context, key, RubyArray.newArrayLight(runtime, val, RubyString.newStringLight(runtime, 10)));
173
+ } else {
174
+ ((RubyArray)val).add(RubyString.newStringLight(runtime, 10));
175
+ }
176
+ } else if (header_value_type == arraysSym) {
177
+ ((RubyArray)val).add(RubyString.newStringLight(runtime, 10));
178
+ } else {
179
+ ((RubyString)val).cat(',').cat(' ');
180
+ }
181
+ }
182
+ val = headers.op_aref(context, key);
183
+ }
184
+
185
+ if (val instanceof RubyArray) {
186
+ val = ((RubyArray)val).entry(-1);
187
+ }
188
+
189
+ ((RubyString)val).cat(data);
190
+
191
+ return 0;
192
+ }
193
+ };
194
+
195
+ this.settings.on_message_begin = new HTTPCallback() {
196
+ public int cb (http_parser.lolevel.HTTPParser p) {
197
+ headers = new RubyHash(runtime);
198
+
199
+ requestUrl = RubyString.newEmptyString(runtime);
200
+ requestPath = RubyString.newEmptyString(runtime);
201
+ queryString = RubyString.newEmptyString(runtime);
202
+ fragment = RubyString.newEmptyString(runtime);
203
+
204
+ upgradeData = RubyString.newEmptyString(runtime);
205
+
206
+ IRubyObject ret = runtime.getNil();
207
+
208
+ if (callback_object != null) {
209
+ if (((RubyObject)callback_object).respondsTo("on_message_begin")) {
210
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
211
+ ret = callback_object.callMethod(context, "on_message_begin");
212
+ }
213
+ } else if (on_message_begin != null) {
214
+ ThreadContext context = on_message_begin.getRuntime().getCurrentContext();
215
+ ret = on_message_begin.callMethod(context, "call");
216
+ }
217
+
218
+ if (ret == stopSym) {
219
+ throw new StopException();
220
+ } else {
221
+ return 0;
222
+ }
223
+ }
224
+ };
225
+ this.settings.on_message_complete = new HTTPCallback() {
226
+ public int cb (http_parser.lolevel.HTTPParser p) {
227
+ IRubyObject ret = runtime.getNil();
228
+
229
+ if (callback_object != null) {
230
+ if (((RubyObject)callback_object).respondsTo("on_message_complete")) {
231
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
232
+ ret = callback_object.callMethod(context, "on_message_complete");
233
+ }
234
+ } else if (on_message_complete != null) {
235
+ ThreadContext context = on_message_complete.getRuntime().getCurrentContext();
236
+ ret = on_message_complete.callMethod(context, "call");
237
+ }
238
+
239
+ if (ret == stopSym) {
240
+ throw new StopException();
241
+ } else {
242
+ return 0;
243
+ }
244
+ }
245
+ };
246
+ this.settings.on_headers_complete = new HTTPCallback() {
247
+ public int cb (http_parser.lolevel.HTTPParser p) {
248
+ IRubyObject ret = runtime.getNil();
249
+
250
+ if (callback_object != null) {
251
+ if (((RubyObject)callback_object).respondsTo("on_headers_complete")) {
252
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
253
+ ret = callback_object.callMethod(context, "on_headers_complete", headers);
254
+ }
255
+ } else if (on_headers_complete != null) {
256
+ ThreadContext context = on_headers_complete.getRuntime().getCurrentContext();
257
+ ret = on_headers_complete.callMethod(context, "call", headers);
258
+ }
259
+
260
+ if (ret == stopSym) {
261
+ throw new StopException();
262
+ } else {
263
+ return 0;
264
+ }
265
+ }
266
+ };
267
+ this.settings.on_body = new HTTPDataCallback() {
268
+ public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
269
+ IRubyObject ret = runtime.getNil();
270
+ byte[] data = fetchBytes(buf, pos, len);
271
+
272
+ if (callback_object != null) {
273
+ if (((RubyObject)callback_object).respondsTo("on_body")) {
274
+ ThreadContext context = callback_object.getRuntime().getCurrentContext();
275
+ ret = callback_object.callMethod(context, "on_body", RubyString.newString(runtime, new ByteList(data, UTF8Encoding.INSTANCE, false)));
276
+ }
277
+ } else if (on_body != null) {
278
+ ThreadContext context = on_body.getRuntime().getCurrentContext();
279
+ ret = on_body.callMethod(context, "call", RubyString.newString(runtime, new ByteList(data, UTF8Encoding.INSTANCE, false)));
280
+ }
281
+
282
+ if (ret == stopSym) {
283
+ throw new StopException();
284
+ } else {
285
+ return 0;
286
+ }
287
+ }
288
+ };
289
+ }
290
+
291
+ private void init() {
292
+ this.parser = new HTTPParser();
293
+ this.headers = null;
294
+
295
+ this.requestUrl = runtime.getNil();
296
+ this.requestPath = runtime.getNil();
297
+ this.queryString = runtime.getNil();
298
+ this.fragment = runtime.getNil();
299
+
300
+ this.upgradeData = runtime.getNil();
301
+ }
302
+
303
+ @JRubyMethod(name = "initialize")
304
+ public IRubyObject initialize() {
305
+ return this;
306
+ }
307
+
308
+ @JRubyMethod(name = "initialize")
309
+ public IRubyObject initialize(IRubyObject arg) {
310
+ callback_object = arg;
311
+ return initialize();
312
+ }
313
+
314
+ @JRubyMethod(name = "initialize")
315
+ public IRubyObject initialize(IRubyObject arg, IRubyObject arg2) {
316
+ header_value_type = arg2;
317
+ return initialize(arg);
318
+ }
319
+
320
+ @JRubyMethod(name = "on_message_begin=")
321
+ public IRubyObject set_on_message_begin(IRubyObject cb) {
322
+ on_message_begin = cb;
323
+ return cb;
324
+ }
325
+
326
+ @JRubyMethod(name = "on_headers_complete=")
327
+ public IRubyObject set_on_headers_complete(IRubyObject cb) {
328
+ on_headers_complete = cb;
329
+ return cb;
330
+ }
331
+
332
+ @JRubyMethod(name = "on_body=")
333
+ public IRubyObject set_on_body(IRubyObject cb) {
334
+ on_body = cb;
335
+ return cb;
336
+ }
337
+
338
+ @JRubyMethod(name = "on_message_complete=")
339
+ public IRubyObject set_on_message_complete(IRubyObject cb) {
340
+ on_message_complete = cb;
341
+ return cb;
342
+ }
343
+
344
+ @JRubyMethod(name = "<<")
345
+ public IRubyObject execute(IRubyObject data) {
346
+ RubyString str = (RubyString)data;
347
+ ByteList byteList = str.getByteList();
348
+ ByteBuffer buf = ByteBuffer.wrap(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
349
+ boolean stopped = false;
350
+
351
+ try {
352
+ this.parser.execute(this.settings, buf);
353
+ } catch (HTTPException e) {
354
+ throw new RaiseException(runtime, eParserError, e.getMessage(), true);
355
+ } catch (StopException e) {
356
+ stopped = true;
357
+ }
358
+
359
+ if (parser.getUpgrade()) {
360
+ byte[] upData = fetchBytes(buf, buf.position(), buf.limit() - buf.position());
361
+ ((RubyString)upgradeData).cat(upData);
362
+
363
+ } else if (buf.hasRemaining()) {
364
+ if (!stopped)
365
+ throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
366
+ }
367
+
368
+ return RubyNumeric.int2fix(runtime, buf.position());
369
+ }
370
+
371
+ @JRubyMethod(name = "keep_alive?")
372
+ public IRubyObject shouldKeepAlive() {
373
+ return runtime.newBoolean(parser.shouldKeepAlive());
374
+ }
375
+
376
+ @JRubyMethod(name = "upgrade?")
377
+ public IRubyObject shouldUpgrade() {
378
+ return runtime.newBoolean(parser.getUpgrade());
379
+ }
380
+
381
+ @JRubyMethod(name = "http_major")
382
+ public IRubyObject httpMajor() {
383
+ if (parser.getMajor() == 0 && parser.getMinor() == 0)
384
+ return runtime.getNil();
385
+ else
386
+ return RubyNumeric.int2fix(runtime, parser.getMajor());
387
+ }
388
+
389
+ @JRubyMethod(name = "http_minor")
390
+ public IRubyObject httpMinor() {
391
+ if (parser.getMajor() == 0 && parser.getMinor() == 0)
392
+ return runtime.getNil();
393
+ else
394
+ return RubyNumeric.int2fix(runtime, parser.getMinor());
395
+ }
396
+
397
+ @JRubyMethod(name = "http_version")
398
+ public IRubyObject httpVersion() {
399
+ if (parser.getMajor() == 0 && parser.getMinor() == 0)
400
+ return runtime.getNil();
401
+ else
402
+ return runtime.newArray(httpMajor(), httpMinor());
403
+ }
404
+
405
+ @JRubyMethod(name = "http_method")
406
+ public IRubyObject httpMethod() {
407
+ HTTPMethod method = parser.getHTTPMethod();
408
+ if (method != null)
409
+ return runtime.newString(new String(method.bytes));
410
+ else
411
+ return runtime.getNil();
412
+ }
413
+
414
+ @JRubyMethod(name = "status_code")
415
+ public IRubyObject statusCode() {
416
+ int code = parser.getStatusCode();
417
+ if (code != 0)
418
+ return RubyNumeric.int2fix(runtime, code);
419
+ else
420
+ return runtime.getNil();
421
+ }
422
+
423
+ @JRubyMethod(name = "headers")
424
+ public IRubyObject getHeaders() {
425
+ return headers == null ? runtime.getNil() : headers;
426
+ }
427
+
428
+ @JRubyMethod(name = "request_url")
429
+ public IRubyObject getRequestUrl() {
430
+ return requestUrl == null ? runtime.getNil() : requestUrl;
431
+ }
432
+
433
+ @JRubyMethod(name = "request_path")
434
+ public IRubyObject getRequestPath() {
435
+ return requestPath == null ? runtime.getNil() : requestPath;
436
+ }
437
+
438
+ @JRubyMethod(name = "query_string")
439
+ public IRubyObject getQueryString() {
440
+ return queryString == null ? runtime.getNil() : queryString;
441
+ }
442
+
443
+ @JRubyMethod(name = "fragment")
444
+ public IRubyObject getFragment() {
445
+ return fragment == null ? runtime.getNil() : fragment;
446
+ }
447
+
448
+ @JRubyMethod(name = "header_value_type")
449
+ public IRubyObject getHeaderValueType() {
450
+ return header_value_type == null ? runtime.getNil() : header_value_type;
451
+ }
452
+
453
+ @JRubyMethod(name = "header_value_type=")
454
+ public IRubyObject set_header_value_type(IRubyObject val) {
455
+ String valString = val.toString();
456
+ if (valString != "mixed" && valString != "arrays" && valString != "strings") {
457
+ throw runtime.newArgumentError("Invalid header value type");
458
+ }
459
+ header_value_type = val;
460
+ return val;
461
+ }
462
+
463
+ @JRubyMethod(name = "upgrade_data")
464
+ public IRubyObject upgradeData() {
465
+ return upgradeData == null ? runtime.getNil() : upgradeData;
466
+ }
467
+
468
+ @JRubyMethod(name = "reset!")
469
+ public IRubyObject reset() {
470
+ init();
471
+ return runtime.getTrue();
472
+ }
473
+
474
+ }