http_parser.rb 0.5.3-java → 0.6.0.beta.2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/.gitmodules +3 -3
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +14 -5
  4. data/README.md +50 -45
  5. data/bench/standalone.rb +23 -0
  6. data/bench/thin.rb +1 -0
  7. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +120 -85
  8. data/ext/ruby_http_parser/ruby_http_parser.c +10 -41
  9. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  10. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
  11. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
  12. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1202 -671
  14. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +172 -51
  16. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +8 -3
  19. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +35 -102
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +775 -682
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +8 -4
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +70 -20
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
  37. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1637 -280
  38. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
  39. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
  40. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
  41. data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
  42. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1029 -615
  43. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +145 -16
  45. data/ext/ruby_http_parser/vendor/http-parser/test.c +1065 -141
  46. data/http_parser.rb.gemspec +3 -1
  47. data/spec/parser_spec.rb +41 -17
  48. data/spec/support/requests.json +236 -24
  49. data/spec/support/responses.json +182 -36
  50. data/tasks/compile.rake +2 -2
  51. data/tasks/fixtures.rake +7 -1
  52. metadata +213 -162
  53. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  54. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  55. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
@@ -1,6 +1,6 @@
1
- [submodule "ext/ruby_http_parser/vendor/http-parser"]
1
+ [submodule "http-parser"]
2
2
  path = ext/ruby_http_parser/vendor/http-parser
3
3
  url = git://github.com/joyent/http-parser.git
4
- [submodule "ext/ruby_http_parser/vendor/http-parser-java"]
4
+ [submodule "http-parser-java"]
5
5
  path = ext/ruby_http_parser/vendor/http-parser-java
6
- url = git://github.com/a2800276/http-parser.java.git
6
+ url = git://github.com/tmm1/http-parser.java
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
@@ -1,13 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- http_parser.rb (0.5.2)
4
+ http_parser.rb (0.6.0.beta.2)
5
5
 
6
6
  GEM
7
- remote: http://rubygems.org/
7
+ remote: https://rubygems.org/
8
8
  specs:
9
+ benchmark_suite (0.8.0)
10
+ bouncy-castle-java (1.5.0147)
9
11
  diff-lcs (1.1.2)
10
- json (1.5.1)
12
+ ffi (1.0.11)
13
+ ffi (1.0.11-java)
14
+ jruby-openssl (0.8.8)
15
+ bouncy-castle-java (>= 1.5.0147)
16
+ json (1.8.0)
17
+ json (1.8.0-java)
11
18
  rake (0.9.2)
12
19
  rake-compiler (0.7.9)
13
20
  rake
@@ -19,14 +26,16 @@ GEM
19
26
  rspec-expectations (2.4.0)
20
27
  diff-lcs (~> 1.1.2)
21
28
  rspec-mocks (2.4.0)
22
- yajl-ruby (0.8.2)
23
29
 
24
30
  PLATFORMS
31
+ java
25
32
  ruby
26
33
 
27
34
  DEPENDENCIES
35
+ benchmark_suite
36
+ ffi
28
37
  http_parser.rb!
38
+ jruby-openssl
29
39
  json (>= 1.4.6)
30
40
  rake-compiler (>= 0.7.9)
31
41
  rspec (>= 2.0.1)
32
- yajl-ruby (>= 0.8.1)
data/README.md CHANGED
@@ -16,70 +16,75 @@ This gem aims to work on all major Ruby platforms, including:
16
16
 
17
17
  ## Usage
18
18
 
19
- require "http/parser"
19
+ ```ruby
20
+ require "http/parser"
20
21
 
21
- parser = Http::Parser.new
22
+ parser = Http::Parser.new
22
23
 
23
- parser.on_headers_complete = proc do
24
- p parser.http_version
24
+ parser.on_headers_complete = proc do
25
+ p parser.http_version
25
26
 
26
- p parser.http_method # for requests
27
- p parser.request_url
27
+ p parser.http_method # for requests
28
+ p parser.request_url
28
29
 
29
- p parser.status_code # for responses
30
+ p parser.status_code # for responses
30
31
 
31
- p parser.headers
32
- end
32
+ p parser.headers
33
+ end
33
34
 
34
- parser.on_body = proc do |chunk|
35
- # One chunk of the body
36
- p chunk
37
- end
35
+ parser.on_body = proc do |chunk|
36
+ # One chunk of the body
37
+ p chunk
38
+ end
38
39
 
39
- parser.on_message_complete = proc do |env|
40
- # Headers and body is all parsed
41
- puts "Done!"
42
- end
40
+ parser.on_message_complete = proc do |env|
41
+ # Headers and body is all parsed
42
+ puts "Done!"
43
+ end
44
+ ```
43
45
 
44
- # Feed raw data from the socket to the parser
45
- parser << raw_data
46
+ # Feed raw data from the socket to the parser
47
+ `parser << raw_data`
46
48
 
47
49
  ## Advanced Usage
48
50
 
49
51
  ### Accept callbacks on an object
50
52
 
51
- module MyHttpConnection
52
- def connection_completed
53
- @parser = Http::Parser.new(self)
54
- end
53
+ ```ruby
54
+ module MyHttpConnection
55
+ def connection_completed
56
+ @parser = Http::Parser.new(self)
57
+ end
55
58
 
56
- def receive_data(data)
57
- @parser << data
58
- end
59
+ def receive_data(data)
60
+ @parser << data
61
+ end
59
62
 
60
- def on_message_begin
61
- @headers = nil
62
- @body = ''
63
- end
63
+ def on_message_begin
64
+ @headers = nil
65
+ @body = ''
66
+ end
64
67
 
65
- def on_headers_complete
66
- @headers = @parser.headers
67
- end
68
+ def on_headers_complete(headers)
69
+ @headers = headers
70
+ end
68
71
 
69
- def on_body(chunk)
70
- @body << chunk
71
- end
72
+ def on_body(chunk)
73
+ @body << chunk
74
+ end
72
75
 
73
- def on_message_complete
74
- p [@headers, @body]
75
- end
76
- end
76
+ def on_message_complete
77
+ p [@headers, @body]
78
+ end
79
+ end
80
+ ```
77
81
 
78
82
  ### Stop parsing after headers
79
83
 
80
- parser = Http::Parser.new
81
- parser.on_headers_complete = proc{ :stop }
82
-
83
- offset = parser << request_data
84
- body = request_data[offset..-1]
84
+ ```ruby
85
+ parser = Http::Parser.new
86
+ parser.on_headers_complete = proc{ :stop }
85
87
 
88
+ offset = parser << request_data
89
+ body = request_data[offset..-1]
90
+ ```
@@ -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
@@ -3,6 +3,7 @@ require "rubygems"
3
3
  require "thin_parser"
4
4
  require "http_parser"
5
5
  require "benchmark"
6
+ require "stringio"
6
7
 
7
8
  data = "POST /postit HTTP/1.1\r\n" +
8
9
  "Host: localhost:3000\r\n" +
@@ -1,36 +1,45 @@
1
1
  package org.ruby_http_parser;
2
2
 
3
+ import http_parser.HTTPException;
4
+ import http_parser.HTTPMethod;
5
+ import http_parser.HTTPParser;
6
+ import http_parser.lolevel.HTTPCallback;
7
+ import http_parser.lolevel.HTTPDataCallback;
8
+ import http_parser.lolevel.ParserSettings;
9
+
10
+ import java.nio.ByteBuffer;
11
+
12
+ import org.jcodings.Encoding;
13
+ import org.jcodings.specific.UTF8Encoding;
3
14
  import org.jruby.Ruby;
4
15
  import org.jruby.RubyArray;
5
16
  import org.jruby.RubyClass;
6
17
  import org.jruby.RubyHash;
7
- import org.jruby.RubyModule;
8
18
  import org.jruby.RubyNumeric;
9
19
  import org.jruby.RubyObject;
10
20
  import org.jruby.RubyString;
11
-
21
+ import org.jruby.RubySymbol;
22
+ import org.jruby.anno.JRubyMethod;
23
+ import org.jruby.exceptions.RaiseException;
12
24
  import org.jruby.runtime.ObjectAllocator;
13
25
  import org.jruby.runtime.ThreadContext;
14
26
  import org.jruby.runtime.builtin.IRubyObject;
15
-
16
- import org.jruby.anno.JRubyMethod;
17
- import org.jruby.exceptions.RaiseException;
18
-
19
- import java.nio.ByteBuffer;
20
- import http_parser.*;
21
- import http_parser.lolevel.ParserSettings;
22
- import http_parser.lolevel.HTTPCallback;
23
- import http_parser.lolevel.HTTPDataCallback;
27
+ import org.jruby.util.ByteList;
24
28
 
25
29
  public class RubyHttpParser extends RubyObject {
26
30
 
31
+ @JRubyMethod(name = "strict?", module = true)
32
+ public static IRubyObject strict(IRubyObject recv) {
33
+ return recv.getRuntime().newBoolean(true);
34
+ }
35
+
27
36
  public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
28
37
  public IRubyObject allocate(Ruby runtime, RubyClass klass) {
29
38
  return new RubyHttpParser(runtime, klass);
30
39
  }
31
40
  };
32
41
 
33
- byte[] fetchBytes (ByteBuffer b, int pos, int len) {
42
+ byte[] fetchBytes(ByteBuffer b, int pos, int len) {
34
43
  byte[] by = new byte[len];
35
44
  int saved = b.position();
36
45
  b.position(pos);
@@ -65,14 +74,18 @@ public class RubyHttpParser extends RubyObject {
65
74
 
66
75
  private IRubyObject callback_object;
67
76
 
68
- private String _current_header;
69
- private String _last_header;
77
+ private boolean completed;
78
+
79
+ private byte[] _current_header;
80
+ private byte[] _last_header;
81
+
82
+ private static final Encoding UTF8 = UTF8Encoding.INSTANCE;
70
83
 
71
84
  public RubyHttpParser(final Ruby runtime, RubyClass clazz) {
72
- super(runtime,clazz);
85
+ super(runtime, clazz);
73
86
 
74
87
  this.runtime = runtime;
75
- this.eParserError = (RubyClass)runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
88
+ this.eParserError = (RubyClass) runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
76
89
 
77
90
  this.on_message_begin = null;
78
91
  this.on_headers_complete = null;
@@ -81,7 +94,10 @@ public class RubyHttpParser extends RubyObject {
81
94
 
82
95
  this.callback_object = null;
83
96
 
84
- this.header_value_type = runtime.getModule("HTTP").getClass("Parser").getInstanceVariable("@default_header_value_type");
97
+ this.completed = false;
98
+
99
+ this.header_value_type = runtime.getModule("HTTP").getClass("Parser")
100
+ .getInstanceVariable("@default_header_value_type");
85
101
 
86
102
  initSettings();
87
103
  init();
@@ -91,48 +107,39 @@ public class RubyHttpParser extends RubyObject {
91
107
  this.settings = new ParserSettings();
92
108
 
93
109
  this.settings.on_url = new HTTPDataCallback() {
94
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
110
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
95
111
  byte[] data = fetchBytes(buf, pos, len);
96
- ((RubyString)requestUrl).concat(runtime.newString(new String(data)));
97
- return 0;
98
- }
99
- };
100
- this.settings.on_path = new HTTPDataCallback() {
101
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
102
- byte[] data = fetchBytes(buf, pos, len);
103
- ((RubyString)requestPath).concat(runtime.newString(new String(data)));
104
- return 0;
105
- }
106
- };
107
- this.settings.on_query_string = new HTTPDataCallback() {
108
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
109
- byte[] data = fetchBytes(buf, pos, len);
110
- ((RubyString)queryString).concat(runtime.newString(new String(data)));
111
- return 0;
112
- }
113
- };
114
- this.settings.on_fragment = new HTTPDataCallback() {
115
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
116
- byte[] data = fetchBytes(buf, pos, len);
117
- ((RubyString)fragment).concat(runtime.newString(new String(data)));
112
+ if (runtime.is1_9() || runtime.is2_0()) {
113
+ ((RubyString) requestUrl).cat(data, 0, data.length, UTF8);
114
+ } else {
115
+ ((RubyString) requestUrl).cat(data);
116
+ }
118
117
  return 0;
119
118
  }
120
119
  };
121
120
 
122
121
  this.settings.on_header_field = new HTTPDataCallback() {
123
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
122
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
124
123
  byte[] data = fetchBytes(buf, pos, len);
125
124
 
126
125
  if (_current_header == null)
127
- _current_header = new String(data);
128
- else
129
- _current_header = _current_header.concat(new String(data));
126
+ _current_header = data;
127
+ else {
128
+ byte[] tmp = new byte[_current_header.length + data.length];
129
+ System.arraycopy(_current_header, 0, tmp, 0, _current_header.length);
130
+ System.arraycopy(data, 0, tmp, _current_header.length, data.length);
131
+ _current_header = tmp;
132
+ }
130
133
 
131
134
  return 0;
132
135
  }
133
136
  };
137
+ final RubySymbol arraysSym = runtime.newSymbol("arrays");
138
+ final RubySymbol mixedSym = runtime.newSymbol("mixed");
139
+ final RubySymbol stopSym = runtime.newSymbol("stop");
140
+ final RubySymbol resetSym = runtime.newSymbol("reset");
134
141
  this.settings.on_header_value = new HTTPDataCallback() {
135
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
142
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
136
143
  byte[] data = fetchBytes(buf, pos, len);
137
144
  ThreadContext context = headers.getRuntime().getCurrentContext();
138
145
  IRubyObject key, val;
@@ -144,57 +151,74 @@ public class RubyHttpParser extends RubyObject {
144
151
  _current_header = null;
145
152
  }
146
153
 
147
- key = (IRubyObject)runtime.newString(_last_header);
154
+ key = RubyString.newString(runtime, new ByteList(_last_header, UTF8, false));
148
155
  val = headers.op_aref(context, key);
149
156
 
150
157
  if (new_field == 1) {
151
158
  if (val.isNil()) {
152
- if (header_value_type == runtime.newSymbol("arrays")) {
153
- headers.op_aset(context, key, RubyArray.newArrayLight(runtime, runtime.newString("")));
159
+ if (header_value_type == arraysSym) {
160
+ headers.op_aset(context, key,
161
+ RubyArray.newArrayLight(runtime, RubyString.newStringLight(runtime, 10, UTF8)));
154
162
  } else {
155
- headers.op_aset(context, key, runtime.newString(""));
163
+ headers.op_aset(context, key, RubyString.newStringLight(runtime, 10, UTF8));
156
164
  }
157
165
  } else {
158
- if (header_value_type == runtime.newSymbol("mixed")) {
166
+ if (header_value_type == mixedSym) {
159
167
  if (val instanceof RubyString) {
160
- headers.op_aset(context, key, RubyArray.newArrayLight(runtime, val, runtime.newString("")));
168
+ headers.op_aset(context, key,
169
+ RubyArray.newArrayLight(runtime, val, RubyString.newStringLight(runtime, 10, UTF8)));
161
170
  } else {
162
- ((RubyArray)val).add(runtime.newString(""));
171
+ ((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
163
172
  }
164
- } else if (header_value_type == runtime.newSymbol("arrays")) {
165
- ((RubyArray)val).add(runtime.newString(""));
173
+ } else if (header_value_type == arraysSym) {
174
+ ((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
166
175
  } else {
167
- ((RubyString)val).cat(", ".getBytes());
176
+ if (runtime.is1_9() || runtime.is2_0()) {
177
+ ((RubyString) val).cat(',', UTF8).cat(' ', UTF8);
178
+ } else {
179
+ ((RubyString) val).cat(',').cat(' ');
180
+ }
168
181
  }
169
182
  }
170
183
  val = headers.op_aref(context, key);
171
184
  }
172
185
 
173
186
  if (val instanceof RubyArray) {
174
- val = ((RubyArray)val).entry(-1);
187
+ val = ((RubyArray) val).entry(-1);
175
188
  }
176
189
 
177
- ((RubyString)val).cat(data);
190
+ if (runtime.is1_9() || runtime.is2_0()) {
191
+ ((RubyString) val).cat(data, 0, data.length, UTF8);
192
+ } else {
193
+ ((RubyString) val).cat(data);
194
+ }
178
195
 
179
196
  return 0;
180
197
  }
181
198
  };
182
199
 
183
200
  this.settings.on_message_begin = new HTTPCallback() {
184
- public int cb (http_parser.lolevel.HTTPParser p) {
201
+ public int cb(http_parser.lolevel.HTTPParser p) {
185
202
  headers = new RubyHash(runtime);
186
203
 
187
- requestUrl = runtime.newString("");
188
- requestPath = runtime.newString("");
189
- queryString = runtime.newString("");
190
- fragment = runtime.newString("");
191
-
192
- upgradeData = runtime.newString("");
204
+ if (runtime.is1_9() || runtime.is2_0()) {
205
+ requestUrl = RubyString.newEmptyString(runtime, UTF8);
206
+ requestPath = RubyString.newEmptyString(runtime, UTF8);
207
+ queryString = RubyString.newEmptyString(runtime, UTF8);
208
+ fragment = RubyString.newEmptyString(runtime, UTF8);
209
+ upgradeData = RubyString.newEmptyString(runtime, UTF8);
210
+ } else {
211
+ requestUrl = RubyString.newEmptyString(runtime);
212
+ requestPath = RubyString.newEmptyString(runtime);
213
+ queryString = RubyString.newEmptyString(runtime);
214
+ fragment = RubyString.newEmptyString(runtime);
215
+ upgradeData = RubyString.newEmptyString(runtime);
216
+ }
193
217
 
194
218
  IRubyObject ret = runtime.getNil();
195
219
 
196
220
  if (callback_object != null) {
197
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_message_begin")).toJava(Boolean.class) == Boolean.TRUE) {
221
+ if (((RubyObject) callback_object).respondsTo("on_message_begin")) {
198
222
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
199
223
  ret = callback_object.callMethod(context, "on_message_begin");
200
224
  }
@@ -203,7 +227,7 @@ public class RubyHttpParser extends RubyObject {
203
227
  ret = on_message_begin.callMethod(context, "call");
204
228
  }
205
229
 
206
- if (ret == runtime.newSymbol("stop")) {
230
+ if (ret == stopSym) {
207
231
  throw new StopException();
208
232
  } else {
209
233
  return 0;
@@ -211,11 +235,13 @@ public class RubyHttpParser extends RubyObject {
211
235
  }
212
236
  };
213
237
  this.settings.on_message_complete = new HTTPCallback() {
214
- public int cb (http_parser.lolevel.HTTPParser p) {
238
+ public int cb(http_parser.lolevel.HTTPParser p) {
215
239
  IRubyObject ret = runtime.getNil();
216
240
 
241
+ completed = true;
242
+
217
243
  if (callback_object != null) {
218
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_message_complete")).toJava(Boolean.class) == Boolean.TRUE) {
244
+ if (((RubyObject) callback_object).respondsTo("on_message_complete")) {
219
245
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
220
246
  ret = callback_object.callMethod(context, "on_message_complete");
221
247
  }
@@ -224,7 +250,7 @@ public class RubyHttpParser extends RubyObject {
224
250
  ret = on_message_complete.callMethod(context, "call");
225
251
  }
226
252
 
227
- if (ret == runtime.newSymbol("stop")) {
253
+ if (ret == stopSym) {
228
254
  throw new StopException();
229
255
  } else {
230
256
  return 0;
@@ -232,11 +258,11 @@ public class RubyHttpParser extends RubyObject {
232
258
  }
233
259
  };
234
260
  this.settings.on_headers_complete = new HTTPCallback() {
235
- public int cb (http_parser.lolevel.HTTPParser p) {
261
+ public int cb(http_parser.lolevel.HTTPParser p) {
236
262
  IRubyObject ret = runtime.getNil();
237
263
 
238
264
  if (callback_object != null) {
239
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_headers_complete")).toJava(Boolean.class) == Boolean.TRUE) {
265
+ if (((RubyObject) callback_object).respondsTo("on_headers_complete")) {
240
266
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
241
267
  ret = callback_object.callMethod(context, "on_headers_complete", headers);
242
268
  }
@@ -245,29 +271,32 @@ public class RubyHttpParser extends RubyObject {
245
271
  ret = on_headers_complete.callMethod(context, "call", headers);
246
272
  }
247
273
 
248
- if (ret == runtime.newSymbol("stop")) {
274
+ if (ret == stopSym) {
249
275
  throw new StopException();
276
+ } else if (ret == resetSym) {
277
+ return 1;
250
278
  } else {
251
279
  return 0;
252
280
  }
253
281
  }
254
282
  };
255
283
  this.settings.on_body = new HTTPDataCallback() {
256
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
284
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
257
285
  IRubyObject ret = runtime.getNil();
258
286
  byte[] data = fetchBytes(buf, pos, len);
259
287
 
260
288
  if (callback_object != null) {
261
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_body")).toJava(Boolean.class) == Boolean.TRUE) {
289
+ if (((RubyObject) callback_object).respondsTo("on_body")) {
262
290
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
263
- ret = callback_object.callMethod(context, "on_body", callback_object.getRuntime().newString(new String(data)));
291
+ ret = callback_object.callMethod(context, "on_body",
292
+ RubyString.newString(runtime, new ByteList(data, UTF8, false)));
264
293
  }
265
294
  } else if (on_body != null) {
266
295
  ThreadContext context = on_body.getRuntime().getCurrentContext();
267
- ret = on_body.callMethod(context, "call", on_body.getRuntime().newString(new String(data)));
296
+ ret = on_body.callMethod(context, "call", RubyString.newString(runtime, new ByteList(data, UTF8, false)));
268
297
  }
269
298
 
270
- if (ret == runtime.newSymbol("stop")) {
299
+ if (ret == stopSym) {
271
300
  throw new StopException();
272
301
  } else {
273
302
  return 0;
@@ -278,6 +307,7 @@ public class RubyHttpParser extends RubyObject {
278
307
 
279
308
  private void init() {
280
309
  this.parser = new HTTPParser();
310
+ this.parser.HTTP_PARSER_STRICT = true;
281
311
  this.headers = null;
282
312
 
283
313
  this.requestUrl = runtime.getNil();
@@ -331,8 +361,9 @@ public class RubyHttpParser extends RubyObject {
331
361
 
332
362
  @JRubyMethod(name = "<<")
333
363
  public IRubyObject execute(IRubyObject data) {
334
- RubyString str = (RubyString)data;
335
- ByteBuffer buf = ByteBuffer.wrap(str.getBytes());
364
+ RubyString str = (RubyString) data;
365
+ ByteList byteList = str.getByteList();
366
+ ByteBuffer buf = ByteBuffer.wrap(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
336
367
  boolean stopped = false;
337
368
 
338
369
  try {
@@ -345,9 +376,12 @@ public class RubyHttpParser extends RubyObject {
345
376
 
346
377
  if (parser.getUpgrade()) {
347
378
  byte[] upData = fetchBytes(buf, buf.position(), buf.limit() - buf.position());
348
- ((RubyString)upgradeData).concat(runtime.newString(new String(upData)));
349
-
350
- } else if (buf.hasRemaining()) {
379
+ if (runtime.is1_9() || runtime.is2_0()) {
380
+ ((RubyString) upgradeData).cat(upData, 0, upData.length, UTF8);
381
+ } else {
382
+ ((RubyString) upgradeData).cat(upData);
383
+ }
384
+ } else if (buf.hasRemaining() && !completed) {
351
385
  if (!stopped)
352
386
  throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
353
387
  }
@@ -357,12 +391,12 @@ public class RubyHttpParser extends RubyObject {
357
391
 
358
392
  @JRubyMethod(name = "keep_alive?")
359
393
  public IRubyObject shouldKeepAlive() {
360
- return parser.shouldKeepAlive() ? runtime.getTrue() : runtime.getFalse();
394
+ return runtime.newBoolean(parser.shouldKeepAlive());
361
395
  }
362
396
 
363
397
  @JRubyMethod(name = "upgrade?")
364
398
  public IRubyObject shouldUpgrade() {
365
- return parser.getUpgrade() ? runtime.getTrue() : runtime.getFalse();
399
+ return runtime.newBoolean(parser.getUpgrade());
366
400
  }
367
401
 
368
402
  @JRubyMethod(name = "http_major")
@@ -439,7 +473,8 @@ public class RubyHttpParser extends RubyObject {
439
473
 
440
474
  @JRubyMethod(name = "header_value_type=")
441
475
  public IRubyObject set_header_value_type(IRubyObject val) {
442
- if (val != runtime.newSymbol("mixed") && val != runtime.newSymbol("arrays") && val != runtime.newSymbol("strings")) {
476
+ String valString = val.toString();
477
+ if (valString != "mixed" && valString != "arrays" && valString != "strings") {
443
478
  throw runtime.newArgumentError("Invalid header value type");
444
479
  }
445
480
  header_value_type = val;