http_parser.rb 0.5.2 → 0.7.0

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/linux.yml +23 -0
  3. data/.github/workflows/windows.yml +23 -0
  4. data/.gitignore +5 -4
  5. data/.gitmodules +4 -4
  6. data/Gemfile +1 -1
  7. data/README.md +52 -47
  8. data/Rakefile +1 -0
  9. data/bench/standalone.rb +23 -0
  10. data/bench/thin.rb +1 -0
  11. data/ext/ruby_http_parser/extconf.rb +1 -1
  12. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +139 -83
  13. data/ext/ruby_http_parser/ruby_http_parser.c +40 -41
  14. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
  16. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
  17. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1202 -671
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +172 -51
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +8 -3
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +35 -102
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +775 -682
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +8 -4
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +70 -20
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
  42. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1637 -280
  43. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
  44. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +68 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
  46. data/ext/ruby_http_parser/vendor/http-parser/README.md +113 -38
  47. data/ext/ruby_http_parser/vendor/http-parser/bench.c +128 -0
  48. data/ext/ruby_http_parser/vendor/http-parser/contrib/parsertrace.c +157 -0
  49. data/ext/ruby_http_parser/vendor/http-parser/contrib/url_parser.c +47 -0
  50. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1576 -780
  51. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +111 -0
  52. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +308 -58
  53. data/ext/ruby_http_parser/vendor/http-parser/test.c +2964 -460
  54. data/http_parser.rb.gemspec +14 -7
  55. data/spec/parser_spec.rb +196 -102
  56. data/spec/support/requests.json +236 -24
  57. data/spec/support/responses.json +202 -36
  58. data/tasks/compile.rake +2 -2
  59. data/tasks/fixtures.rake +8 -2
  60. data/tasks/spec.rake +1 -1
  61. metadata +141 -134
  62. data/Gemfile.lock +0 -32
  63. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  64. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  65. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
  66. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
  67. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +0 -4
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: afdb28464ccb0f753a1d66be02b667683d6aadfe31fe03a88f889252b457761d
4
+ data.tar.gz: 6041b3dc3f212160ae00b72b193e4620c5facd59096a28b122c0ef5a901dc80b
5
+ SHA512:
6
+ metadata.gz: 41249c628f931cd9721e32bd378a44b8e49b25706b491ae3761d641e55bac1205afe9d3d02d88d5b4f3161adee4a65416c5eec2a51fcacad1b050d6f6d2637fc
7
+ data.tar.gz: ae5718390cce25d2ee7fcbaf923fac015189079b64ea2f37759c41ee2e7f2714dafaf6bf37ed6434f540e82e87c6a13723e226ac27000d1f0e2d7b2681643514
@@ -0,0 +1,23 @@
1
+ name: Testing on Ubuntu
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [ '2.6', '2.7', '3.0' ]
12
+ os: [ 'ubuntu-latest' ]
13
+ name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: unit testing
20
+ run: |
21
+ gem install bundler rake
22
+ bundle install --jobs 4 --retry 3
23
+ bundle exec rake
@@ -0,0 +1,23 @@
1
+ name: Testing on Windows
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [ '2.6', '2.7', '3.0' ]
12
+ os: [ 'windows-latest' ]
13
+ name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: unit testing
20
+ run: |
21
+ gem install bundler rake
22
+ bundle install --jobs 4 --retry 3
23
+ bundle exec rake
data/.gitignore CHANGED
@@ -1,11 +1,12 @@
1
- tmp
1
+ *.bundle
2
2
  *.bundle
3
3
  *.gem
4
+ *.jar
4
5
  *.o
6
+ *.rbc
5
7
  *.so
6
- *.bundle
7
- *.jar
8
8
  *.swp
9
+ Gemfile.lock
9
10
  Makefile
10
11
  tags
11
- *.rbc
12
+ tmp
data/.gitmodules CHANGED
@@ -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
- url = git://github.com/ry/http-parser.git
4
- [submodule "ext/ruby_http_parser/vendor/http-parser-java"]
3
+ url = https://github.com/nodejs/http-parser.git
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 = https://github.com/tmm1/http-parser.java
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
data/README.md CHANGED
@@ -3,83 +3,88 @@
3
3
  A simple callback-based HTTP request/response parser for writing http
4
4
  servers, clients and proxies.
5
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).
6
+ This gem is built on top of [joyent/http-parser](https://github.com/joyent/http-parser) and its java port [http-parser/http-parser.java](https://github.com/http-parser/http-parser.java).
7
7
 
8
8
  ## Supported Platforms
9
9
 
10
10
  This gem aims to work on all major Ruby platforms, including:
11
11
 
12
- - MRI 1.8 and 1.9
12
+ - MRI 1.8, 1.9 and 2.0; should work on MRI 2.4+
13
13
  - Rubinius
14
14
  - JRuby
15
15
  - win32
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
+ ```
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  # load tasks
2
+ require 'bundler/gem_tasks'
2
3
  Dir['tasks/*.rake'].sort.each { |f| load f }
3
4
 
4
5
  # default task
@@ -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
data/bench/thin.rb CHANGED
@@ -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" +
@@ -18,7 +18,7 @@ src_dir = File.expand_path('../', __FILE__)
18
18
  }
19
19
  end
20
20
 
21
- $CFLAGS << " -I#{src_dir}"
21
+ $CFLAGS << " -I\"#{src_dir}\""
22
22
 
23
23
  dir_config("ruby_http_parser")
24
24
  create_makefile("ruby_http_parser")
@@ -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);
@@ -55,6 +64,7 @@ public class RubyHttpParser extends RubyObject {
55
64
  private IRubyObject on_body;
56
65
  private IRubyObject on_message_complete;
57
66
 
67
+ private IRubyObject status;
58
68
  private IRubyObject requestUrl;
59
69
  private IRubyObject requestPath;
60
70
  private IRubyObject queryString;
@@ -65,14 +75,18 @@ public class RubyHttpParser extends RubyObject {
65
75
 
66
76
  private IRubyObject callback_object;
67
77
 
68
- private String _current_header;
69
- private String _last_header;
78
+ private boolean completed;
79
+
80
+ private byte[] _current_header;
81
+ private byte[] _last_header;
82
+
83
+ private static final Encoding UTF8 = UTF8Encoding.INSTANCE;
70
84
 
71
85
  public RubyHttpParser(final Ruby runtime, RubyClass clazz) {
72
- super(runtime,clazz);
86
+ super(runtime, clazz);
73
87
 
74
88
  this.runtime = runtime;
75
- this.eParserError = (RubyClass)runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
89
+ this.eParserError = (RubyClass) runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
76
90
 
77
91
  this.on_message_begin = null;
78
92
  this.on_headers_complete = null;
@@ -81,7 +95,10 @@ public class RubyHttpParser extends RubyObject {
81
95
 
82
96
  this.callback_object = null;
83
97
 
84
- this.header_value_type = runtime.getModule("HTTP").getClass("Parser").getInstanceVariable("@default_header_value_type");
98
+ this.completed = false;
99
+
100
+ this.header_value_type = runtime.getModule("HTTP").getClass("Parser")
101
+ .getInstanceVariable("@default_header_value_type");
85
102
 
86
103
  initSettings();
87
104
  init();
@@ -90,49 +107,52 @@ public class RubyHttpParser extends RubyObject {
90
107
  private void initSettings() {
91
108
  this.settings = new ParserSettings();
92
109
 
93
- this.settings.on_url = new HTTPDataCallback() {
94
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
110
+ this.settings.on_status = new HTTPDataCallback() {
111
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
95
112
  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)));
113
+ if (runtime.is1_9() || runtime.is2_0()) {
114
+ ((RubyString) status).cat(data, 0, data.length, UTF8);
115
+ } else {
116
+ ((RubyString) status).cat(data);
117
+ }
111
118
  return 0;
112
119
  }
113
120
  };
114
- this.settings.on_fragment = new HTTPDataCallback() {
115
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
121
+
122
+ this.settings.on_url = new HTTPDataCallback() {
123
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
116
124
  byte[] data = fetchBytes(buf, pos, len);
117
- ((RubyString)fragment).concat(runtime.newString(new String(data)));
125
+ if (runtime.is1_9() || runtime.is2_0()) {
126
+ ((RubyString) requestUrl).cat(data, 0, data.length, UTF8);
127
+ } else {
128
+ ((RubyString) requestUrl).cat(data);
129
+ }
118
130
  return 0;
119
131
  }
120
132
  };
121
133
 
122
134
  this.settings.on_header_field = new HTTPDataCallback() {
123
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
135
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
124
136
  byte[] data = fetchBytes(buf, pos, len);
125
137
 
126
138
  if (_current_header == null)
127
- _current_header = new String(data);
128
- else
129
- _current_header = _current_header.concat(new String(data));
139
+ _current_header = data;
140
+ else {
141
+ byte[] tmp = new byte[_current_header.length + data.length];
142
+ System.arraycopy(_current_header, 0, tmp, 0, _current_header.length);
143
+ System.arraycopy(data, 0, tmp, _current_header.length, data.length);
144
+ _current_header = tmp;
145
+ }
130
146
 
131
147
  return 0;
132
148
  }
133
149
  };
150
+ final RubySymbol arraysSym = runtime.newSymbol("arrays");
151
+ final RubySymbol mixedSym = runtime.newSymbol("mixed");
152
+ final RubySymbol stopSym = runtime.newSymbol("stop");
153
+ final RubySymbol resetSym = runtime.newSymbol("reset");
134
154
  this.settings.on_header_value = new HTTPDataCallback() {
135
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
155
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
136
156
  byte[] data = fetchBytes(buf, pos, len);
137
157
  ThreadContext context = headers.getRuntime().getCurrentContext();
138
158
  IRubyObject key, val;
@@ -144,57 +164,76 @@ public class RubyHttpParser extends RubyObject {
144
164
  _current_header = null;
145
165
  }
146
166
 
147
- key = (IRubyObject)runtime.newString(_last_header);
167
+ key = RubyString.newString(runtime, new ByteList(_last_header, UTF8, false));
148
168
  val = headers.op_aref(context, key);
149
169
 
150
170
  if (new_field == 1) {
151
171
  if (val.isNil()) {
152
- if (header_value_type == runtime.newSymbol("arrays")) {
153
- headers.op_aset(context, key, RubyArray.newArrayLight(runtime, runtime.newString("")));
172
+ if (header_value_type == arraysSym) {
173
+ headers.op_aset(context, key,
174
+ RubyArray.newArrayLight(runtime, RubyString.newStringLight(runtime, 10, UTF8)));
154
175
  } else {
155
- headers.op_aset(context, key, runtime.newString(""));
176
+ headers.op_aset(context, key, RubyString.newStringLight(runtime, 10, UTF8));
156
177
  }
157
178
  } else {
158
- if (header_value_type == runtime.newSymbol("mixed")) {
179
+ if (header_value_type == mixedSym) {
159
180
  if (val instanceof RubyString) {
160
- headers.op_aset(context, key, RubyArray.newArrayLight(runtime, val, runtime.newString("")));
181
+ headers.op_aset(context, key,
182
+ RubyArray.newArrayLight(runtime, val, RubyString.newStringLight(runtime, 10, UTF8)));
161
183
  } else {
162
- ((RubyArray)val).add(runtime.newString(""));
184
+ ((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
163
185
  }
164
- } else if (header_value_type == runtime.newSymbol("arrays")) {
165
- ((RubyArray)val).add(runtime.newString(""));
186
+ } else if (header_value_type == arraysSym) {
187
+ ((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
166
188
  } else {
167
- ((RubyString)val).cat(", ".getBytes());
189
+ if (runtime.is1_9() || runtime.is2_0()) {
190
+ ((RubyString) val).cat(',', UTF8).cat(' ', UTF8);
191
+ } else {
192
+ ((RubyString) val).cat(',').cat(' ');
193
+ }
168
194
  }
169
195
  }
170
196
  val = headers.op_aref(context, key);
171
197
  }
172
198
 
173
199
  if (val instanceof RubyArray) {
174
- val = ((RubyArray)val).entry(-1);
200
+ val = ((RubyArray) val).entry(-1);
175
201
  }
176
202
 
177
- ((RubyString)val).cat(data);
203
+ if (runtime.is1_9() || runtime.is2_0()) {
204
+ ((RubyString) val).cat(data, 0, data.length, UTF8);
205
+ } else {
206
+ ((RubyString) val).cat(data);
207
+ }
178
208
 
179
209
  return 0;
180
210
  }
181
211
  };
182
212
 
183
213
  this.settings.on_message_begin = new HTTPCallback() {
184
- public int cb (http_parser.lolevel.HTTPParser p) {
214
+ public int cb(http_parser.lolevel.HTTPParser p) {
185
215
  headers = new RubyHash(runtime);
186
216
 
187
- requestUrl = runtime.newString("");
188
- requestPath = runtime.newString("");
189
- queryString = runtime.newString("");
190
- fragment = runtime.newString("");
191
-
192
- upgradeData = runtime.newString("");
217
+ if (runtime.is1_9() || runtime.is2_0()) {
218
+ status = RubyString.newEmptyString(runtime, UTF8);
219
+ requestUrl = RubyString.newEmptyString(runtime, UTF8);
220
+ requestPath = RubyString.newEmptyString(runtime, UTF8);
221
+ queryString = RubyString.newEmptyString(runtime, UTF8);
222
+ fragment = RubyString.newEmptyString(runtime, UTF8);
223
+ upgradeData = RubyString.newEmptyString(runtime, UTF8);
224
+ } else {
225
+ status = RubyString.newEmptyString(runtime);
226
+ requestUrl = RubyString.newEmptyString(runtime);
227
+ requestPath = RubyString.newEmptyString(runtime);
228
+ queryString = RubyString.newEmptyString(runtime);
229
+ fragment = RubyString.newEmptyString(runtime);
230
+ upgradeData = RubyString.newEmptyString(runtime);
231
+ }
193
232
 
194
233
  IRubyObject ret = runtime.getNil();
195
234
 
196
235
  if (callback_object != null) {
197
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_message_begin")).toJava(Boolean.class) == Boolean.TRUE) {
236
+ if (((RubyObject) callback_object).respondsTo("on_message_begin")) {
198
237
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
199
238
  ret = callback_object.callMethod(context, "on_message_begin");
200
239
  }
@@ -203,7 +242,7 @@ public class RubyHttpParser extends RubyObject {
203
242
  ret = on_message_begin.callMethod(context, "call");
204
243
  }
205
244
 
206
- if (ret == runtime.newSymbol("stop")) {
245
+ if (ret == stopSym) {
207
246
  throw new StopException();
208
247
  } else {
209
248
  return 0;
@@ -211,11 +250,13 @@ public class RubyHttpParser extends RubyObject {
211
250
  }
212
251
  };
213
252
  this.settings.on_message_complete = new HTTPCallback() {
214
- public int cb (http_parser.lolevel.HTTPParser p) {
253
+ public int cb(http_parser.lolevel.HTTPParser p) {
215
254
  IRubyObject ret = runtime.getNil();
216
255
 
256
+ completed = true;
257
+
217
258
  if (callback_object != null) {
218
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_message_complete")).toJava(Boolean.class) == Boolean.TRUE) {
259
+ if (((RubyObject) callback_object).respondsTo("on_message_complete")) {
219
260
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
220
261
  ret = callback_object.callMethod(context, "on_message_complete");
221
262
  }
@@ -224,7 +265,7 @@ public class RubyHttpParser extends RubyObject {
224
265
  ret = on_message_complete.callMethod(context, "call");
225
266
  }
226
267
 
227
- if (ret == runtime.newSymbol("stop")) {
268
+ if (ret == stopSym) {
228
269
  throw new StopException();
229
270
  } else {
230
271
  return 0;
@@ -232,11 +273,11 @@ public class RubyHttpParser extends RubyObject {
232
273
  }
233
274
  };
234
275
  this.settings.on_headers_complete = new HTTPCallback() {
235
- public int cb (http_parser.lolevel.HTTPParser p) {
276
+ public int cb(http_parser.lolevel.HTTPParser p) {
236
277
  IRubyObject ret = runtime.getNil();
237
278
 
238
279
  if (callback_object != null) {
239
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_headers_complete")).toJava(Boolean.class) == Boolean.TRUE) {
280
+ if (((RubyObject) callback_object).respondsTo("on_headers_complete")) {
240
281
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
241
282
  ret = callback_object.callMethod(context, "on_headers_complete", headers);
242
283
  }
@@ -245,29 +286,32 @@ public class RubyHttpParser extends RubyObject {
245
286
  ret = on_headers_complete.callMethod(context, "call", headers);
246
287
  }
247
288
 
248
- if (ret == runtime.newSymbol("stop")) {
289
+ if (ret == stopSym) {
249
290
  throw new StopException();
291
+ } else if (ret == resetSym) {
292
+ return 1;
250
293
  } else {
251
294
  return 0;
252
295
  }
253
296
  }
254
297
  };
255
298
  this.settings.on_body = new HTTPDataCallback() {
256
- public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
299
+ public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
257
300
  IRubyObject ret = runtime.getNil();
258
301
  byte[] data = fetchBytes(buf, pos, len);
259
302
 
260
303
  if (callback_object != null) {
261
- if (((RubyObject)callback_object).respond_to_p(runtime.newSymbol("on_body")).toJava(Boolean.class) == Boolean.TRUE) {
304
+ if (((RubyObject) callback_object).respondsTo("on_body")) {
262
305
  ThreadContext context = callback_object.getRuntime().getCurrentContext();
263
- ret = callback_object.callMethod(context, "on_body", callback_object.getRuntime().newString(new String(data)));
306
+ ret = callback_object.callMethod(context, "on_body",
307
+ RubyString.newString(runtime, new ByteList(data, UTF8, false)));
264
308
  }
265
309
  } else if (on_body != null) {
266
310
  ThreadContext context = on_body.getRuntime().getCurrentContext();
267
- ret = on_body.callMethod(context, "call", on_body.getRuntime().newString(new String(data)));
311
+ ret = on_body.callMethod(context, "call", RubyString.newString(runtime, new ByteList(data, UTF8, false)));
268
312
  }
269
313
 
270
- if (ret == runtime.newSymbol("stop")) {
314
+ if (ret == stopSym) {
271
315
  throw new StopException();
272
316
  } else {
273
317
  return 0;
@@ -278,8 +322,10 @@ public class RubyHttpParser extends RubyObject {
278
322
 
279
323
  private void init() {
280
324
  this.parser = new HTTPParser();
325
+ this.parser.HTTP_PARSER_STRICT = true;
281
326
  this.headers = null;
282
-
327
+
328
+ this.status = runtime.getNil();
283
329
  this.requestUrl = runtime.getNil();
284
330
  this.requestPath = runtime.getNil();
285
331
  this.queryString = runtime.getNil();
@@ -331,8 +377,9 @@ public class RubyHttpParser extends RubyObject {
331
377
 
332
378
  @JRubyMethod(name = "<<")
333
379
  public IRubyObject execute(IRubyObject data) {
334
- RubyString str = (RubyString)data;
335
- ByteBuffer buf = ByteBuffer.wrap(str.getBytes());
380
+ RubyString str = (RubyString) data;
381
+ ByteList byteList = str.getByteList();
382
+ ByteBuffer buf = ByteBuffer.wrap(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
336
383
  boolean stopped = false;
337
384
 
338
385
  try {
@@ -345,9 +392,12 @@ public class RubyHttpParser extends RubyObject {
345
392
 
346
393
  if (parser.getUpgrade()) {
347
394
  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()) {
395
+ if (runtime.is1_9() || runtime.is2_0()) {
396
+ ((RubyString) upgradeData).cat(upData, 0, upData.length, UTF8);
397
+ } else {
398
+ ((RubyString) upgradeData).cat(upData);
399
+ }
400
+ } else if (buf.hasRemaining() && !completed) {
351
401
  if (!stopped)
352
402
  throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
353
403
  }
@@ -357,12 +407,12 @@ public class RubyHttpParser extends RubyObject {
357
407
 
358
408
  @JRubyMethod(name = "keep_alive?")
359
409
  public IRubyObject shouldKeepAlive() {
360
- return parser.shouldKeepAlive() ? runtime.getTrue() : runtime.getFalse();
410
+ return runtime.newBoolean(parser.shouldKeepAlive());
361
411
  }
362
412
 
363
413
  @JRubyMethod(name = "upgrade?")
364
414
  public IRubyObject shouldUpgrade() {
365
- return parser.getUpgrade() ? runtime.getTrue() : runtime.getFalse();
415
+ return runtime.newBoolean(parser.getUpgrade());
366
416
  }
367
417
 
368
418
  @JRubyMethod(name = "http_major")
@@ -412,6 +462,11 @@ public class RubyHttpParser extends RubyObject {
412
462
  return headers == null ? runtime.getNil() : headers;
413
463
  }
414
464
 
465
+ @JRubyMethod(name = "status")
466
+ public IRubyObject getStatus() {
467
+ return status == null ? runtime.getNil() : status;
468
+ }
469
+
415
470
  @JRubyMethod(name = "request_url")
416
471
  public IRubyObject getRequestUrl() {
417
472
  return requestUrl == null ? runtime.getNil() : requestUrl;
@@ -439,7 +494,8 @@ public class RubyHttpParser extends RubyObject {
439
494
 
440
495
  @JRubyMethod(name = "header_value_type=")
441
496
  public IRubyObject set_header_value_type(IRubyObject val) {
442
- if (val != runtime.newSymbol("mixed") && val != runtime.newSymbol("arrays") && val != runtime.newSymbol("strings")) {
497
+ String valString = val.toString();
498
+ if (valString != "mixed" && valString != "arrays" && valString != "strings") {
443
499
  throw runtime.newArgumentError("Invalid header value type");
444
500
  }
445
501
  header_value_type = val;