about summary refs log tree commit homepage
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/test_configurator.rb8
-rw-r--r--test/unit/test_http_parser.rb446
-rw-r--r--test/unit/test_http_parser_ng.rb463
-rw-r--r--test/unit/test_http_parser_xftrust.rb38
-rw-r--r--test/unit/test_request.rb18
-rw-r--r--test/unit/test_response.rb55
-rw-r--r--test/unit/test_server.rb11
-rw-r--r--test/unit/test_signals.rb2
-rw-r--r--test/unit/test_stream_input.rb204
-rw-r--r--test/unit/test_tee_input.rb117
-rw-r--r--test/unit/test_upload.rb12
11 files changed, 1044 insertions, 330 deletions
diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb
index ac1efa8..c19c427 100644
--- a/test/unit/test_configurator.rb
+++ b/test/unit/test_configurator.rb
@@ -33,6 +33,14 @@ class TestConfigurator < Test::Unit::TestCase
     assert_equal "0.0.0.0:2007", meth.call('2007')
     assert_equal "0.0.0.0:2007", meth.call(2007)
 
+    %w([::1]:2007 [::]:2007).each do |addr|
+      assert_equal addr, meth.call(addr.dup)
+    end
+
+    # for Rainbows! users only
+    assert_equal "[::]:80", meth.call("[::]")
+    assert_equal "127.6.6.6:80", meth.call("127.6.6.6")
+
     # the next two aren't portable, consider them unsupported for now
     # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('1:2007')
     # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('2:2007')
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index 222c227..dc1aab7 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -14,9 +14,10 @@ class HttpParserTest < Test::Unit::TestCase
 
   def test_parse_simple
     parser = HttpParser.new
-    req = {}
-    http = "GET / HTTP/1.1\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    req = parser.env
+    http = parser.buf
+    http << "GET / HTTP/1.1\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal '', http
 
     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
@@ -28,17 +29,17 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal '', req['QUERY_STRING']
 
     assert parser.keepalive?
-    parser.reset
+    parser.clear
     req.clear
 
-    http = "G"
-    assert_nil parser.headers(req, http)
+    http << "G"
+    assert_nil parser.parse
     assert_equal "G", http
     assert req.empty?
 
     # try parsing again to ensure we were reset correctly
-    http = "GET /hello-world HTTP/1.1\r\n\r\n"
-    assert parser.headers(req, http)
+    http << "ET /hello-world HTTP/1.1\r\n\r\n"
+    assert parser.parse
 
     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
     assert_equal '/hello-world', req['REQUEST_PATH']
@@ -53,96 +54,161 @@ class HttpParserTest < Test::Unit::TestCase
 
   def test_tab_lws
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.1\r\nHost:\tfoo.bar\r\n\r\n"
-    assert_equal req.object_id, parser.headers(req, tmp).object_id
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost:\tfoo.bar\r\n\r\n"
+    assert_equal req.object_id, parser.parse.object_id
     assert_equal "foo.bar", req['HTTP_HOST']
   end
 
   def test_connection_close_no_ka
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
-    assert_equal req.object_id, parser.headers(req, tmp).object_id
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
+    assert_equal req.object_id, parser.parse.object_id
     assert_equal "GET", req['REQUEST_METHOD']
     assert ! parser.keepalive?
   end
 
   def test_connection_keep_alive_ka
     parser = HttpParser.new
-    req = {}
-    tmp = "HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
-    assert_equal req.object_id, parser.headers(req, tmp).object_id
+    req = parser.env
+    parser.buf << "HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
+    assert_equal req.object_id, parser.parse.object_id
     assert parser.keepalive?
   end
 
-  def test_connection_keep_alive_ka_bad_method
+  def test_connection_keep_alive_no_body
     parser = HttpParser.new
-    req = {}
-    tmp = "POST / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
-    assert_equal req.object_id, parser.headers(req, tmp).object_id
-    assert ! parser.keepalive?
+    req = parser.env
+    parser.buf << "POST / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
+    assert_equal req.object_id, parser.parse.object_id
+    assert parser.keepalive?
+  end
+
+  def test_connection_keep_alive_no_body_empty
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "POST / HTTP/1.1\r\n" \
+                  "Content-Length: 0\r\n" \
+                  "Connection: keep-alive\r\n\r\n"
+    assert_equal req.object_id, parser.parse.object_id
+    assert parser.keepalive?
   end
 
   def test_connection_keep_alive_ka_bad_version
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"
-    assert_equal req.object_id, parser.headers(req, tmp).object_id
+    req = parser.env
+    parser.buf << "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"
+    assert_equal req.object_id, parser.parse.object_id
     assert parser.keepalive?
   end
 
   def test_parse_server_host_default_port
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"
-    assert_equal req, parser.headers(req, tmp)
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal 'foo', req['SERVER_NAME']
     assert_equal '80', req['SERVER_PORT']
-    assert_equal '', tmp
+    assert_equal '', parser.buf
     assert parser.keepalive?
   end
 
   def test_parse_server_host_alt_port
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.1\r\nHost: foo:999\r\n\r\n"
-    assert_equal req, parser.headers(req, tmp)
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost: foo:999\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal 'foo', req['SERVER_NAME']
     assert_equal '999', req['SERVER_PORT']
-    assert_equal '', tmp
+    assert_equal '', parser.buf
     assert parser.keepalive?
   end
 
   def test_parse_server_host_empty_port
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n\r\n"
-    assert_equal req, parser.headers(req, tmp)
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal 'foo', req['SERVER_NAME']
     assert_equal '80', req['SERVER_PORT']
-    assert_equal '', tmp
+    assert_equal '', parser.buf
     assert parser.keepalive?
   end
 
   def test_parse_server_host_xfp_https
     parser = HttpParser.new
-    req = {}
-    tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n" \
-          "X-Forwarded-Proto: https\r\n\r\n"
-    assert_equal req, parser.headers(req, tmp)
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \
+                  "X-Forwarded-Proto: https\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal 'foo', req['SERVER_NAME']
     assert_equal '443', req['SERVER_PORT']
-    assert_equal '', tmp
+    assert_equal '', parser.buf
     assert parser.keepalive?
   end
 
+  def test_parse_xfp_https_chained
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.0\r\n" \
+                  "X-Forwarded-Proto: https,http\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal '443', req['SERVER_PORT'], req.inspect
+    assert_equal 'https', req['rack.url_scheme'], req.inspect
+    assert_equal '', parser.buf
+  end
+
+  def test_parse_xfp_https_chained_backwards
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.0\r\n" \
+          "X-Forwarded-Proto: http,https\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal '80', req['SERVER_PORT'], req.inspect
+    assert_equal 'http', req['rack.url_scheme'], req.inspect
+    assert_equal '', parser.buf
+  end
+
+  def test_parse_xfp_gopher_is_ignored
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.0\r\n" \
+                  "X-Forwarded-Proto: gopher\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal '80', req['SERVER_PORT'], req.inspect
+    assert_equal 'http', req['rack.url_scheme'], req.inspect
+    assert_equal '', parser.buf
+  end
+
+  def test_parse_x_forwarded_ssl_on
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.0\r\n" \
+                  "X-Forwarded-Ssl: on\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal '443', req['SERVER_PORT'], req.inspect
+    assert_equal 'https', req['rack.url_scheme'], req.inspect
+    assert_equal '', parser.buf
+  end
+
+  def test_parse_x_forwarded_ssl_off
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.0\r\nX-Forwarded-Ssl: off\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal '80', req['SERVER_PORT'], req.inspect
+    assert_equal 'http', req['rack.url_scheme'], req.inspect
+    assert_equal '', parser.buf
+  end
+
   def test_parse_strange_headers
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
-    assert_equal req, parser.headers(req, should_be_good)
-    assert_equal '', should_be_good
+    parser.buf << should_be_good
+    assert_equal req, parser.parse
+    assert_equal '', parser.buf
     assert parser.keepalive?
   end
 
@@ -152,14 +218,14 @@ class HttpParserTest < Test::Unit::TestCase
   def test_nasty_pound_header
     parser = HttpParser.new
     nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
-    req = {}
-    buf = nasty_pound_header.dup
+    req = parser.env
+    parser.buf << nasty_pound_header.dup
 
     assert nasty_pound_header =~ /(-----BEGIN .*--END CERTIFICATE-----)/m
     expect = $1.dup
     expect.gsub!(/\r\n\t/, ' ')
-    assert_equal req, parser.headers(req, buf)
-    assert_equal '', buf
+    assert_equal req, parser.parse
+    assert_equal '', parser.buf
     assert_equal expect, req['HTTP_X_SSL_BULLSHIT']
   end
 
@@ -170,9 +236,10 @@ class HttpParserTest < Test::Unit::TestCase
              "\t\r\n" \
              "    \r\n" \
              "  ASDF\r\n\r\n"
-    req = {}
-    assert_equal req, parser.headers(req, header)
-    assert_equal '', header
+    parser.buf << header
+    req = parser.env
+    assert_equal req, parser.parse
+    assert_equal '', parser.buf
     assert_equal 'ASDF', req['HTTP_X_ASDF']
   end
 
@@ -184,9 +251,10 @@ class HttpParserTest < Test::Unit::TestCase
              "\t\r\n" \
              "       x\r\n" \
              "  ASDF\r\n\r\n"
-    req = {}
-    assert_equal req, parser.headers(req, header)
-    assert_equal '', header
+    req = parser.env
+    parser.buf << header
+    assert_equal req, parser.parse
+    assert_equal '', parser.buf
     assert_equal 'hi y x ASDF', req['HTTP_X_ASDF']
   end
 
@@ -196,8 +264,9 @@ class HttpParserTest < Test::Unit::TestCase
              "Host: \r\n" \
              "    YHBT.net\r\n" \
              "\r\n"
-    req = {}
-    assert_equal req, parser.headers(req, header)
+    parser.buf << header
+    req = parser.env
+    assert_equal req, parser.parse
     assert_equal 'example.com', req['HTTP_HOST']
   end
 
@@ -206,21 +275,22 @@ class HttpParserTest < Test::Unit::TestCase
   # in case we ever go multithreaded/evented...
   def test_resumable_continuations
     nr = 1000
-    req = {}
     header = "GET / HTTP/1.1\r\n" \
              "X-ASDF:      \r\n" \
              "  hello\r\n"
     tmp = []
     nr.times { |i|
       parser = HttpParser.new
-      assert parser.headers(req, "#{header} #{i}\r\n").nil?
+      req = parser.env
+      parser.buf << "#{header} #{i}\r\n"
+      assert parser.parse.nil?
       asdf = req['HTTP_X_ASDF']
       assert_equal "hello #{i}", asdf
       tmp << [ parser, asdf ]
-      req.clear
     }
     tmp.each_with_index { |(parser, asdf), i|
-      assert_equal req, parser.headers(req, "#{header} #{i}\r\n .\r\n\r\n")
+      parser.buf << " .\r\n\r\n"
+      assert parser.parse
       assert_equal "hello #{i} .", asdf
     }
   end
@@ -231,8 +301,8 @@ class HttpParserTest < Test::Unit::TestCase
              "    y\r\n" \
              "Host: hello\r\n" \
              "\r\n"
-    req = {}
-    assert_raises(HttpParserError) { parser.headers(req, header) }
+    parser.buf << header
+    assert_raises(HttpParserError) { parser.parse }
   end
 
   def test_parse_ie6_urls
@@ -244,7 +314,7 @@ class HttpParserTest < Test::Unit::TestCase
        /mal"formed"?
     ).each do |path|
       parser = HttpParser.new
-      req = {}
+      req = parser.env
       sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
       assert_equal req, parser.headers(req, sorta_safe)
       assert_equal path, req['REQUEST_URI']
@@ -255,13 +325,13 @@ class HttpParserTest < Test::Unit::TestCase
   
   def test_parse_error
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     bad_http = "GET / SsUTF/1.1"
 
     assert_raises(HttpParserError) { parser.headers(req, bad_http) }
 
     # make sure we can recover
-    parser.reset
+    parser.clear
     req.clear
     assert_equal req, parser.headers(req, "GET / HTTP/1.0\r\n\r\n")
     assert ! parser.keepalive?
@@ -269,7 +339,7 @@ class HttpParserTest < Test::Unit::TestCase
 
   def test_piecemeal
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     http = "GET"
     assert_nil parser.headers(req, http)
     assert_nil parser.headers(req, http)
@@ -291,9 +361,10 @@ class HttpParserTest < Test::Unit::TestCase
   # not common, but underscores do appear in practice
   def test_absolute_uri_underscores
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     http = "GET http://under_score.example.com/foo?q=bar HTTP/1.0\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    parser.buf << http
+    assert_equal req, parser.parse
     assert_equal 'http', req['rack.url_scheme']
     assert_equal '/foo?q=bar', req['REQUEST_URI']
     assert_equal '/foo', req['REQUEST_PATH']
@@ -302,16 +373,17 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal 'under_score.example.com', req['HTTP_HOST']
     assert_equal 'under_score.example.com', req['SERVER_NAME']
     assert_equal '80', req['SERVER_PORT']
-    assert_equal "", http
+    assert_equal "", parser.buf
     assert ! parser.keepalive?
   end
 
   # some dumb clients add users because they're stupid
   def test_absolute_uri_w_user
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     http = "GET http://user%20space@example.com/foo?q=bar HTTP/1.0\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    parser.buf << http
+    assert_equal req, parser.parse
     assert_equal 'http', req['rack.url_scheme']
     assert_equal '/foo?q=bar', req['REQUEST_URI']
     assert_equal '/foo', req['REQUEST_PATH']
@@ -320,7 +392,7 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal 'example.com', req['HTTP_HOST']
     assert_equal 'example.com', req['SERVER_NAME']
     assert_equal '80', req['SERVER_PORT']
-    assert_equal "", http
+    assert_equal "", parser.buf
     assert ! parser.keepalive?
   end
 
@@ -329,7 +401,7 @@ class HttpParserTest < Test::Unit::TestCase
   def test_absolute_uri_uri_parse
     "#{URI::REGEXP::PATTERN::UNRESERVED};:&=+$,".split(//).each do |char|
       parser = HttpParser.new
-      req = {}
+      req = parser.env
       http = "GET http://#{char}@example.com/ HTTP/1.0\r\n\r\n"
       assert_equal req, parser.headers(req, http)
       assert_equal 'http', req['rack.url_scheme']
@@ -347,9 +419,9 @@ class HttpParserTest < Test::Unit::TestCase
 
   def test_absolute_uri
     parser = HttpParser.new
-    req = {}
-    http = "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    req = parser.env
+    parser.buf << "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal 'http', req['rack.url_scheme']
     assert_equal '/foo?q=bar', req['REQUEST_URI']
     assert_equal '/foo', req['REQUEST_PATH']
@@ -358,17 +430,18 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal 'example.com', req['HTTP_HOST']
     assert_equal 'example.com', req['SERVER_NAME']
     assert_equal '80', req['SERVER_PORT']
-    assert_equal "", http
+    assert_equal "", parser.buf
     assert ! parser.keepalive?
   end
 
   # X-Forwarded-Proto is not in rfc2616, absolute URIs are, however...
   def test_absolute_uri_https
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     http = "GET https://example.com/foo?q=bar HTTP/1.1\r\n" \
            "X-Forwarded-Proto: http\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    parser.buf << http
+    assert_equal req, parser.parse
     assert_equal 'https', req['rack.url_scheme']
     assert_equal '/foo?q=bar', req['REQUEST_URI']
     assert_equal '/foo', req['REQUEST_PATH']
@@ -377,17 +450,17 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal 'example.com', req['HTTP_HOST']
     assert_equal 'example.com', req['SERVER_NAME']
     assert_equal '443', req['SERVER_PORT']
-    assert_equal "", http
+    assert_equal "", parser.buf
     assert parser.keepalive?
   end
 
   # Host: header should be ignored for absolute URIs
   def test_absolute_uri_with_port
     parser = HttpParser.new
-    req = {}
-    http = "GET http://example.com:8080/foo?q=bar HTTP/1.2\r\n" \
+    req = parser.env
+    parser.buf << "GET http://example.com:8080/foo?q=bar HTTP/1.2\r\n" \
            "Host: bad.example.com\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    assert_equal req, parser.parse
     assert_equal 'http', req['rack.url_scheme']
     assert_equal '/foo?q=bar', req['REQUEST_URI']
     assert_equal '/foo', req['REQUEST_PATH']
@@ -396,16 +469,16 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal 'example.com:8080', req['HTTP_HOST']
     assert_equal 'example.com', req['SERVER_NAME']
     assert_equal '8080', req['SERVER_PORT']
-    assert_equal "", http
+    assert_equal "", parser.buf
     assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
   end
 
   def test_absolute_uri_with_empty_port
     parser = HttpParser.new
-    req = {}
-    http = "GET https://example.com:/foo?q=bar HTTP/1.1\r\n" \
+    req = parser.env
+    parser.buf << "GET https://example.com:/foo?q=bar HTTP/1.1\r\n" \
            "Host: bad.example.com\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    assert_equal req, parser.parse
     assert_equal 'https', req['rack.url_scheme']
     assert_equal '/foo?q=bar', req['REQUEST_URI']
     assert_equal '/foo', req['REQUEST_PATH']
@@ -414,42 +487,192 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal 'example.com:', req['HTTP_HOST']
     assert_equal 'example.com', req['SERVER_NAME']
     assert_equal '443', req['SERVER_PORT']
+    assert_equal "", parser.buf
+    assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
+  end
+
+  def test_absolute_ipv6_uri
+    parser = HttpParser.new
+    req = parser.env
+    url = "http://[::1]/foo?q=bar"
+    http = "GET #{url} HTTP/1.1\r\n" \
+           "Host: bad.example.com\r\n\r\n"
+    assert_equal req, parser.headers(req, http)
+    assert_equal 'http', req['rack.url_scheme']
+    assert_equal '/foo?q=bar', req['REQUEST_URI']
+    assert_equal '/foo', req['REQUEST_PATH']
+    assert_equal 'q=bar', req['QUERY_STRING']
+
+    uri = URI.parse(url)
+    assert_equal "[::1]", uri.host,
+                 "URI.parse changed upstream for #{url}? host=#{uri.host}"
+    assert_equal "[::1]", req['HTTP_HOST']
+    assert_equal "[::1]", req['SERVER_NAME']
+    assert_equal '80', req['SERVER_PORT']
     assert_equal "", http
     assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
   end
 
-  def test_put_body_oneshot
+  def test_absolute_ipv6_uri_alpha
     parser = HttpParser.new
-    req = {}
-    http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
+    req = parser.env
+    url = "http://[::a]/"
+    http = "GET #{url} HTTP/1.1\r\n" \
+           "Host: bad.example.com\r\n\r\n"
     assert_equal req, parser.headers(req, http)
+    assert_equal 'http', req['rack.url_scheme']
+
+    uri = URI.parse(url)
+    assert_equal "[::a]", uri.host,
+                 "URI.parse changed upstream for #{url}? host=#{uri.host}"
+    assert_equal "[::a]", req['HTTP_HOST']
+    assert_equal "[::a]", req['SERVER_NAME']
+    assert_equal '80', req['SERVER_PORT']
+  end
+
+  def test_absolute_ipv6_uri_alpha_2
+    parser = HttpParser.new
+    req = parser.env
+    url = "http://[::B]/"
+    http = "GET #{url} HTTP/1.1\r\n" \
+           "Host: bad.example.com\r\n\r\n"
+    assert_equal req, parser.headers(req, http)
+    assert_equal 'http', req['rack.url_scheme']
+
+    uri = URI.parse(url)
+    assert_equal "[::B]", uri.host,
+                 "URI.parse changed upstream for #{url}? host=#{uri.host}"
+    assert_equal "[::B]", req['HTTP_HOST']
+    assert_equal "[::B]", req['SERVER_NAME']
+    assert_equal '80', req['SERVER_PORT']
+  end
+
+  def test_absolute_ipv6_uri_with_empty_port
+    parser = HttpParser.new
+    req = parser.env
+    url = "https://[::1]:/foo?q=bar"
+    http = "GET #{url} HTTP/1.1\r\n" \
+           "Host: bad.example.com\r\n\r\n"
+    assert_equal req, parser.headers(req, http)
+    assert_equal 'https', req['rack.url_scheme']
+    assert_equal '/foo?q=bar', req['REQUEST_URI']
+    assert_equal '/foo', req['REQUEST_PATH']
+    assert_equal 'q=bar', req['QUERY_STRING']
+
+    uri = URI.parse(url)
+    assert_equal "[::1]", uri.host,
+                 "URI.parse changed upstream for #{url}? host=#{uri.host}"
+    assert_equal "[::1]:", req['HTTP_HOST']
+    assert_equal "[::1]", req['SERVER_NAME']
+    assert_equal '443', req['SERVER_PORT']
+    assert_equal "", http
+    assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
+  end
+
+  def test_absolute_ipv6_uri_with_port
+    parser = HttpParser.new
+    req = parser.env
+    url = "https://[::1]:666/foo?q=bar"
+    http = "GET #{url} HTTP/1.1\r\n" \
+           "Host: bad.example.com\r\n\r\n"
+    assert_equal req, parser.headers(req, http)
+    assert_equal 'https', req['rack.url_scheme']
+    assert_equal '/foo?q=bar', req['REQUEST_URI']
+    assert_equal '/foo', req['REQUEST_PATH']
+    assert_equal 'q=bar', req['QUERY_STRING']
+
+    uri = URI.parse(url)
+    assert_equal "[::1]", uri.host,
+                 "URI.parse changed upstream for #{url}? host=#{uri.host}"
+    assert_equal "[::1]:666", req['HTTP_HOST']
+    assert_equal "[::1]", req['SERVER_NAME']
+    assert_equal '666', req['SERVER_PORT']
+    assert_equal "", http
+    assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
+  end
+
+  def test_ipv6_host_header
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\n" \
+                  "Host: [::1]\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal "[::1]", req['HTTP_HOST']
+    assert_equal "[::1]", req['SERVER_NAME']
+    assert_equal '80', req['SERVER_PORT']
+    assert_equal "", parser.buf
+    assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
+  end
+
+  def test_ipv6_host_header_with_port
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\n" \
+                  "Host: [::1]:666\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal "[::1]", req['SERVER_NAME']
+    assert_equal '666', req['SERVER_PORT']
+    assert_equal "[::1]:666", req['HTTP_HOST']
+    assert_equal "", parser.buf
+    assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
+  end
+
+  def test_ipv6_host_header_with_empty_port
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost: [::1]:\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal "[::1]", req['SERVER_NAME']
+    assert_equal '80', req['SERVER_PORT']
+    assert_equal "[::1]:", req['HTTP_HOST']
+    assert_equal "", parser.buf
+    assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
+  end
+
+  # XXX Highly unlikely..., just make sure we don't segfault or assert on it
+  def test_broken_ipv6_host_header
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "GET / HTTP/1.1\r\nHost: [::1:\r\n\r\n"
+    assert_equal req, parser.parse
+    assert_equal "[", req['SERVER_NAME']
+    assert_equal ':1:', req['SERVER_PORT']
+    assert_equal "[::1:", req['HTTP_HOST']
+    assert_equal "", parser.buf
+  end
+
+  def test_put_body_oneshot
+    parser = HttpParser.new
+    req = parser.env
+    parser.buf << "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
+    assert_equal req, parser.parse
     assert_equal '/', req['REQUEST_PATH']
     assert_equal '/', req['REQUEST_URI']
     assert_equal 'PUT', req['REQUEST_METHOD']
     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
     assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
-    assert_equal "abcde", http
+    assert_equal "abcde", parser.buf
     assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
   end
 
   def test_put_body_later
     parser = HttpParser.new
-    req = {}
-    http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
-    assert_equal req, parser.headers(req, http)
+    req = parser.env
+    parser.buf << "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
+    assert_equal req, parser.parse
     assert_equal '/l', req['REQUEST_PATH']
     assert_equal '/l', req['REQUEST_URI']
     assert_equal 'PUT', req['REQUEST_METHOD']
     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
     assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
-    assert_equal "", http
+    assert_equal "", parser.buf
     assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
   end
 
   def test_unknown_methods
     %w(GETT HEADR XGET XHEAD).each { |m|
       parser = HttpParser.new
-      req = {}
+      req = parser.env
       s = "#{m} /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
       ok = false
       assert_nothing_raised do
@@ -461,23 +684,24 @@ class HttpParserTest < Test::Unit::TestCase
       assert_equal 'page=1', req['QUERY_STRING']
       assert_equal "", s
       assert_equal m, req['REQUEST_METHOD']
-      assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
+      assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
     }
   end
 
   def test_fragment_in_uri
     parser = HttpParser.new
-    req = {}
+    req = parser.env
     get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
+    parser.buf << get
     ok = false
     assert_nothing_raised do
-      ok = parser.headers(req, get)
+      ok = parser.parse
     end
     assert ok
     assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
     assert_equal 'posts-17408', req['FRAGMENT']
     assert_equal 'page=1', req['QUERY_STRING']
-    assert_equal '', get
+    assert_equal '', parser.buf
     assert parser.keepalive?
   end
 
@@ -503,8 +727,9 @@ class HttpParserTest < Test::Unit::TestCase
     10.times do |c|
       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
       assert_raises Unicorn::HttpParserError do
-        parser.headers({}, get)
-        parser.reset
+        parser.buf << get
+        parser.parse
+        parser.clear
       end
     end
 
@@ -512,25 +737,28 @@ class HttpParserTest < Test::Unit::TestCase
     10.times do |c|
       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
       assert_raises Unicorn::HttpParserError do
-        parser.headers({}, get)
-        parser.reset
+        parser.buf << get
+        parser.parse
+        parser.clear
       end
     end
 
     # then large headers are rejected too
     get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
     get << "X-Test: test\r\n" * (80 * 1024)
+    parser.buf << get
     assert_raises Unicorn::HttpParserError do
-      parser.headers({}, get)
-      parser.reset
+      parser.parse
     end
+    parser.clear
 
     # finally just that random garbage gets blocked all the time
     10.times do |c|
       get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
       assert_raises Unicorn::HttpParserError do
-        parser.headers({}, get)
-        parser.reset
+        parser.buf << get
+        parser.parse
+        parser.clear
       end
     end
 
diff --git a/test/unit/test_http_parser_ng.rb b/test/unit/test_http_parser_ng.rb
index cb30f32..e57428c 100644
--- a/test/unit/test_http_parser_ng.rb
+++ b/test/unit/test_http_parser_ng.rb
@@ -8,57 +8,164 @@ include Unicorn
 class HttpParserNgTest < Test::Unit::TestCase
 
   def setup
+    HttpParser.keepalive_requests = HttpParser::KEEPALIVE_REQUESTS_DEFAULT
     @parser = HttpParser.new
   end
 
+  def test_keepalive_requests_default_constant
+    assert_kind_of Integer, HttpParser::KEEPALIVE_REQUESTS_DEFAULT
+    assert HttpParser::KEEPALIVE_REQUESTS_DEFAULT >= 0
+  end
+
+  def test_keepalive_requests_setting
+    HttpParser.keepalive_requests = 0
+    assert_equal 0, HttpParser.keepalive_requests
+    HttpParser.keepalive_requests = nil
+    assert HttpParser.keepalive_requests >= 0xffffffff
+    HttpParser.keepalive_requests = 1
+    assert_equal 1, HttpParser.keepalive_requests
+    HttpParser.keepalive_requests = 666
+    assert_equal 666, HttpParser.keepalive_requests
+
+    assert_raises(TypeError) { HttpParser.keepalive_requests = "666" }
+    assert_raises(TypeError) { HttpParser.keepalive_requests = [] }
+  end
+
+  def test_keepalive_requests_with_next?
+    req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
+    expect = {
+      "SERVER_NAME" => "example.com",
+      "HTTP_HOST" => "example.com",
+      "rack.url_scheme" => "http",
+      "REQUEST_PATH" => "/",
+      "SERVER_PROTOCOL" => "HTTP/1.1",
+      "PATH_INFO" => "/",
+      "HTTP_VERSION" => "HTTP/1.1",
+      "REQUEST_URI" => "/",
+      "SERVER_PORT" => "80",
+      "REQUEST_METHOD" => "GET",
+      "QUERY_STRING" => ""
+    }.freeze
+    HttpParser::KEEPALIVE_REQUESTS_DEFAULT.times do |nr|
+      @parser.buf << req
+      assert_equal expect, @parser.parse
+      assert @parser.next?
+    end
+    @parser.buf << req
+    assert_equal expect, @parser.parse
+    assert ! @parser.next?
+  end
+
+  def test_fewer_keepalive_requests_with_next?
+    HttpParser.keepalive_requests = 5
+    @parser = HttpParser.new
+    req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
+    expect = {
+      "SERVER_NAME" => "example.com",
+      "HTTP_HOST" => "example.com",
+      "rack.url_scheme" => "http",
+      "REQUEST_PATH" => "/",
+      "SERVER_PROTOCOL" => "HTTP/1.1",
+      "PATH_INFO" => "/",
+      "HTTP_VERSION" => "HTTP/1.1",
+      "REQUEST_URI" => "/",
+      "SERVER_PORT" => "80",
+      "REQUEST_METHOD" => "GET",
+      "QUERY_STRING" => ""
+    }.freeze
+    5.times do |nr|
+      @parser.buf << req
+      assert_equal expect, @parser.parse
+      assert @parser.next?
+    end
+    @parser.buf << req
+    assert_equal expect, @parser.parse
+    assert ! @parser.next?
+  end
+
+  def test_default_keepalive_is_off
+    assert ! @parser.keepalive?
+    assert ! @parser.next?
+    assert_nothing_raised do
+      @parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
+      @parser.parse
+    end
+    assert @parser.keepalive?
+    @parser.clear
+    assert ! @parser.keepalive?
+    assert ! @parser.next?
+  end
+
   def test_identity_byte_headers
-    req = {}
+    req = @parser.env
     str = "PUT / HTTP/1.1\r\n"
     str << "Content-Length: 123\r\n"
     str << "\r"
-    hdr = ""
+    hdr = @parser.buf
     str.each_byte { |byte|
-      assert_nil @parser.headers(req, hdr << byte.chr)
+      hdr << byte.chr
+      assert_nil @parser.parse
     }
     hdr << "\n"
-    assert_equal req.object_id, @parser.headers(req, hdr).object_id
+    assert_equal req.object_id, @parser.parse.object_id
     assert_equal '123', req['CONTENT_LENGTH']
     assert_equal 0, hdr.size
     assert ! @parser.keepalive?
     assert @parser.headers?
     assert_equal 123, @parser.content_length
+    dst = ""
+    buf = '.' * 123
+    @parser.filter_body(dst, buf)
+    assert_equal '.' * 123, dst
+    assert_equal "", buf
+    assert @parser.keepalive?
   end
 
   def test_identity_step_headers
-    req = {}
-    str = "PUT / HTTP/1.1\r\n"
-    assert ! @parser.headers(req, str)
+    req = @parser.env
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\n"
+    assert ! @parser.parse
     str << "Content-Length: 123\r\n"
-    assert ! @parser.headers(req, str)
+    assert ! @parser.parse
     str << "\r\n"
-    assert_equal req.object_id, @parser.headers(req, str).object_id
+    assert_equal req.object_id, @parser.parse.object_id
     assert_equal '123', req['CONTENT_LENGTH']
     assert_equal 0, str.size
     assert ! @parser.keepalive?
     assert @parser.headers?
+    dst = ""
+    buf = '.' * 123
+    @parser.filter_body(dst, buf)
+    assert_equal '.' * 123, dst
+    assert_equal "", buf
+    assert @parser.keepalive?
   end
 
   def test_identity_oneshot_header
-    req = {}
-    str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
-    assert_equal req.object_id, @parser.headers(req, str).object_id
+    req = @parser.env
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
+    assert_equal req.object_id, @parser.parse.object_id
     assert_equal '123', req['CONTENT_LENGTH']
     assert_equal 0, str.size
     assert ! @parser.keepalive?
+    assert @parser.headers?
+    dst = ""
+    buf = '.' * 123
+    @parser.filter_body(dst, buf)
+    assert_equal '.' * 123, dst
+    assert_equal "", buf
   end
 
   def test_identity_oneshot_header_with_body
     body = ('a' * 123).freeze
-    req = {}
-    str = "PUT / HTTP/1.1\r\n" \
-          "Content-Length: #{body.length}\r\n" \
-          "\r\n#{body}"
-    assert_equal req.object_id, @parser.headers(req, str).object_id
+    req = @parser.env
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\n" \
+           "Content-Length: #{body.length}\r\n" \
+           "\r\n#{body}"
+    assert_equal req.object_id, @parser.parse.object_id
     assert_equal '123', req['CONTENT_LENGTH']
     assert_equal 123, str.size
     assert_equal body, str
@@ -67,12 +174,13 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert_equal 0, str.size
     assert_equal tmp, body
     assert_equal "", @parser.filter_body(tmp, str)
-    assert ! @parser.keepalive?
+    assert @parser.keepalive?
   end
 
   def test_identity_oneshot_header_with_body_partial
-    str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
-    assert_equal Hash, @parser.headers({}, str).class
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
+    assert_equal Hash, @parser.parse.class
     assert_equal 1, str.size
     assert_equal 'a', str
     tmp = ''
@@ -85,12 +193,13 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert_nil rv
     assert_equal "", str
     assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
-    assert ! @parser.keepalive?
+    assert @parser.keepalive?
   end
 
   def test_identity_oneshot_header_with_body_slop
-    str = "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
-    assert_equal Hash, @parser.headers({}, str).class
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
+    assert_equal Hash, @parser.parse.class
     assert_equal 2, str.size
     assert_equal 'aG', str
     tmp = ''
@@ -99,92 +208,100 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert_equal "G", @parser.filter_body(tmp, str)
     assert_equal 1, tmp.size
     assert_equal "a", tmp
-    assert ! @parser.keepalive?
+    assert @parser.keepalive?
   end
 
   def test_chunked
-    str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    req = @parser.env
+    str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
+    assert_equal req, @parser.parse, "msg=#{str}"
     assert_equal 0, str.size
     tmp = ""
-    assert_nil @parser.filter_body(tmp, "6")
+    assert_nil @parser.filter_body(tmp, str << "6")
     assert_equal 0, tmp.size
-    assert_nil @parser.filter_body(tmp, rv = "\r\n")
-    assert_equal 0, rv.size
+    assert_nil @parser.filter_body(tmp, str << "\r\n")
+    assert_equal 0, str.size
     assert_equal 0, tmp.size
     tmp = ""
-    assert_nil @parser.filter_body(tmp, "..")
+    assert_nil @parser.filter_body(tmp, str << "..")
     assert_equal "..", tmp
-    assert_nil @parser.filter_body(tmp, "abcd\r\n0\r\n")
+    assert_nil @parser.filter_body(tmp, str << "abcd\r\n0\r\n")
     assert_equal "abcd", tmp
-    rv = "PUT"
-    assert_equal rv.object_id, @parser.filter_body(tmp, rv).object_id
-    assert_equal "PUT", rv
+    assert_equal str.object_id, @parser.filter_body(tmp, str << "PUT").object_id
+    assert_equal "PUT", str
     assert ! @parser.keepalive?
+    str << "TY: FOO\r\n\r\n"
+    assert_equal req, @parser.parse
+    assert_equal "FOO", req["HTTP_PUTTY"]
+    assert @parser.keepalive?
   end
 
   def test_two_chunks
-    str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
+    req = @parser.env
+    assert_equal req, @parser.parse
     assert_equal 0, str.size
     tmp = ""
-    assert_nil @parser.filter_body(tmp, "6")
+    assert_nil @parser.filter_body(tmp, str << "6")
     assert_equal 0, tmp.size
-    assert_nil @parser.filter_body(tmp, rv = "\r\n")
-    assert_equal "", rv
+    assert_nil @parser.filter_body(tmp, str << "\r\n")
+    assert_equal "", str
     assert_equal 0, tmp.size
     tmp = ""
-    assert_nil @parser.filter_body(tmp, "..")
+    assert_nil @parser.filter_body(tmp, str << "..")
     assert_equal 2, tmp.size
     assert_equal "..", tmp
-    assert_nil @parser.filter_body(tmp, "abcd\r\n1")
+    assert_nil @parser.filter_body(tmp, str << "abcd\r\n1")
     assert_equal "abcd", tmp
-    assert_nil @parser.filter_body(tmp, "\r")
+    assert_nil @parser.filter_body(tmp, str << "\r")
     assert_equal "", tmp
-    assert_nil @parser.filter_body(tmp, "\n")
+    assert_nil @parser.filter_body(tmp, str << "\n")
     assert_equal "", tmp
-    assert_nil @parser.filter_body(tmp, "z")
+    assert_nil @parser.filter_body(tmp, str << "z")
     assert_equal "z", tmp
-    assert_nil @parser.filter_body(tmp, "\r\n")
-    assert_nil @parser.filter_body(tmp, "0")
-    assert_nil @parser.filter_body(tmp, "\r")
-    rv = @parser.filter_body(tmp, buf = "\nGET")
+    assert_nil @parser.filter_body(tmp, str << "\r\n")
+    assert_nil @parser.filter_body(tmp, str << "0")
+    assert_nil @parser.filter_body(tmp, str << "\r")
+    rv = @parser.filter_body(tmp, str << "\nGET")
     assert_equal "GET", rv
-    assert_equal buf.object_id, rv.object_id
+    assert_equal str.object_id, rv.object_id
     assert ! @parser.keepalive?
   end
 
   def test_big_chunk
-    str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
-          "4000\r\nabcd"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
+           "4000\r\nabcd"
+    req = @parser.env
+    assert_equal req, @parser.parse
     tmp = ''
     assert_nil @parser.filter_body(tmp, str)
     assert_equal '', str
-    str = ' ' * 16300
+    str << ' ' * 16300
     assert_nil @parser.filter_body(tmp, str)
     assert_equal '', str
-    str = ' ' * 80
+    str << ' ' * 80
     assert_nil @parser.filter_body(tmp, str)
     assert_equal '', str
     assert ! @parser.body_eof?
-    assert_equal "", @parser.filter_body(tmp, "\r\n0\r\n")
+    assert_equal "", @parser.filter_body(tmp, str << "\r\n0\r\n")
     assert_equal "", tmp
     assert @parser.body_eof?
-    assert_equal req, @parser.trailers(req, moo = "\r\n")
-    assert_equal "", moo
+    str << "\r\n"
+    assert_equal req, @parser.parse
+    assert_equal "", str
     assert @parser.body_eof?
-    assert ! @parser.keepalive?
+    assert @parser.keepalive?
   end
 
   def test_two_chunks_oneshot
-    str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
-          "1\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    req = @parser.env
+    str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
+           "1\r\na\r\n2\r\n..\r\n0\r\n"
+    assert_equal req, @parser.parse
     tmp = ''
     assert_nil @parser.filter_body(tmp, str)
     assert_equal 'a..', tmp
@@ -195,31 +312,33 @@ class HttpParserNgTest < Test::Unit::TestCase
 
   def test_chunks_bytewise
     chunked = "10\r\nabcdefghijklmnop\r\n11\r\n0123456789abcdefg\r\n0\r\n"
-    str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n#{chunked}"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
-    assert_equal chunked, str
+    str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
+    buf = @parser.buf
+    buf << str
+    req = @parser.env
+    assert_equal req, @parser.parse
+    assert_equal "", buf
     tmp = ''
-    buf = ''
     body = ''
-    str = str[0..-2]
+    str = chunked[0..-2]
     str.each_byte { |byte|
       assert_nil @parser.filter_body(tmp, buf << byte.chr)
       body << tmp
     }
     assert_equal 'abcdefghijklmnop0123456789abcdefg', body
-    rv = @parser.filter_body(tmp, buf << "\n")
+    rv = @parser.filter_body(tmp, buf<< "\n")
     assert_equal rv.object_id, buf.object_id
     assert ! @parser.keepalive?
   end
 
   def test_trailers
-    str = "PUT / HTTP/1.1\r\n" \
-          "Trailer: Content-MD5\r\n" \
-          "transfer-Encoding: chunked\r\n\r\n" \
-          "1\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    req = @parser.env
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\n" \
+           "Trailer: Content-MD5\r\n" \
+           "transfer-Encoding: chunked\r\n\r\n" \
+           "1\r\na\r\n2\r\n..\r\n0\r\n"
+    assert_equal req, @parser.parse
     assert_equal 'Content-MD5', req['HTTP_TRAILER']
     assert_nil req['HTTP_CONTENT_MD5']
     tmp = ''
@@ -234,19 +353,22 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert_nil @parser.trailers(req, str)
     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
-    assert_nil @parser.trailers(req, str << "\r")
-    assert_equal req, @parser.trailers(req, str << "\nGET / ")
+    str << "\r"
+    assert_nil @parser.parse
+    str << "\nGET / "
+    assert_equal req, @parser.parse
     assert_equal "GET / ", str
-    assert ! @parser.keepalive?
+    assert @parser.keepalive?
   end
 
   def test_trailers_slowly
-    str = "PUT / HTTP/1.1\r\n" \
-          "Trailer: Content-MD5\r\n" \
-          "transfer-Encoding: chunked\r\n\r\n" \
-          "1\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\n" \
+           "Trailer: Content-MD5\r\n" \
+           "transfer-Encoding: chunked\r\n\r\n" \
+           "1\r\na\r\n2\r\n..\r\n0\r\n"
+    req = @parser.env
+    assert_equal req, @parser.parse
     assert_equal 'Content-MD5', req['HTTP_TRAILER']
     assert_nil req['HTTP_CONTENT_MD5']
     tmp = ''
@@ -264,16 +386,19 @@ class HttpParserNgTest < Test::Unit::TestCase
     }
     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
-    assert_nil @parser.trailers(req, str << "\r")
-    assert_equal req, @parser.trailers(req, str << "\n")
+    str << "\r"
+    assert_nil @parser.parse
+    str << "\n"
+    assert_equal req, @parser.parse
   end
 
   def test_max_chunk
-    str = "PUT / HTTP/1.1\r\n" \
-          "transfer-Encoding: chunked\r\n\r\n" \
-          "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    str << "PUT / HTTP/1.1\r\n" \
+           "transfer-Encoding: chunked\r\n\r\n" \
+           "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
+    req = @parser.env
+    assert_equal req, @parser.parse
     assert_nil @parser.content_length
     assert_nothing_raised { @parser.filter_body('', str) }
     assert ! @parser.keepalive?
@@ -281,64 +406,61 @@ class HttpParserNgTest < Test::Unit::TestCase
 
   def test_max_body
     n = HttpParser::LENGTH_MAX
-    str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
-    req = {}
-    assert_nothing_raised { @parser.headers(req, str) }
+    @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
+    req = @parser.env
+    assert_nothing_raised { @parser.headers(req, @parser.buf) }
     assert_equal n, req['CONTENT_LENGTH'].to_i
     assert ! @parser.keepalive?
   end
 
   def test_overflow_chunk
     n = HttpParser::CHUNK_MAX + 1
-    str = "PUT / HTTP/1.1\r\n" \
-          "transfer-Encoding: chunked\r\n\r\n" \
-          "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    req = @parser.env
+    str << "PUT / HTTP/1.1\r\n" \
+           "transfer-Encoding: chunked\r\n\r\n" \
+           "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
+    assert_equal req, @parser.parse
     assert_nil @parser.content_length
     assert_raise(HttpParserError) { @parser.filter_body('', str) }
-    assert ! @parser.keepalive?
   end
 
   def test_overflow_content_length
     n = HttpParser::LENGTH_MAX + 1
-    str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
-    assert_raise(HttpParserError) { @parser.headers({}, str) }
-    assert ! @parser.keepalive?
+    @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
+    assert_raise(HttpParserError) { @parser.parse }
   end
 
   def test_bad_chunk
-    str = "PUT / HTTP/1.1\r\n" \
-          "transfer-Encoding: chunked\r\n\r\n" \
-          "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    @parser.buf << "PUT / HTTP/1.1\r\n" \
+                   "transfer-Encoding: chunked\r\n\r\n" \
+                   "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
+    req = @parser.env
+    assert_equal req, @parser.parse
     assert_nil @parser.content_length
-    assert_raise(HttpParserError) { @parser.filter_body('', str) }
-    assert ! @parser.keepalive?
+    assert_raise(HttpParserError) { @parser.filter_body("", @parser.buf) }
   end
 
   def test_bad_content_length
-    str = "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
-    assert_raise(HttpParserError) { @parser.headers({}, str) }
-    assert ! @parser.keepalive?
+    @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
+    assert_raise(HttpParserError) { @parser.parse }
   end
 
   def test_bad_trailers
-    str = "PUT / HTTP/1.1\r\n" \
-          "Trailer: Transfer-Encoding\r\n" \
-          "transfer-Encoding: chunked\r\n\r\n" \
-          "1\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    str = @parser.buf
+    req = @parser.env
+    str << "PUT / HTTP/1.1\r\n" \
+           "Trailer: Transfer-Encoding\r\n" \
+           "transfer-Encoding: chunked\r\n\r\n" \
+           "1\r\na\r\n2\r\n..\r\n0\r\n"
+    assert_equal req, @parser.parse
     assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
     tmp = ''
     assert_nil @parser.filter_body(tmp, str)
     assert_equal 'a..', tmp
     assert_equal '', str
     str << "Transfer-Encoding: identity\r\n\r\n"
-    assert_raise(HttpParserError) { @parser.trailers(req, str) }
-    assert ! @parser.keepalive?
+    assert_raise(HttpParserError) { @parser.parse }
   end
 
   def test_repeat_headers
@@ -347,18 +469,19 @@ class HttpParserNgTest < Test::Unit::TestCase
           "Trailer: Content-SHA1\r\n" \
           "transfer-Encoding: chunked\r\n\r\n" \
           "1\r\na\r\n2\r\n..\r\n0\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, str)
+    req = @parser.env
+    @parser.buf << str
+    assert_equal req, @parser.parse
     assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
     assert ! @parser.keepalive?
   end
 
   def test_parse_simple_request
     parser = HttpParser.new
-    req = {}
-    http = "GET /read-rfc1945-if-you-dont-believe-me\r\n"
-    assert_equal req, parser.headers(req, http)
-    assert_equal '', http
+    req = parser.env
+    parser.buf << "GET /read-rfc1945-if-you-dont-believe-me\r\n"
+    assert_equal req, parser.parse
+    assert_equal '', parser.buf
     expect = {
       "SERVER_NAME"=>"localhost",
       "rack.url_scheme"=>"http",
@@ -388,7 +511,8 @@ class HttpParserNgTest < Test::Unit::TestCase
       "*" => { qs => "", pi => "" },
     }.each do |uri,expect|
       assert_equal req, @parser.headers(req.clear, str % [ uri ])
-      @parser.reset
+      req = req.dup
+      @parser.clear
       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
       assert_equal expect[qs], req[qs], "#{qs} mismatch"
       assert_equal expect[pi], req[pi], "#{pi} mismatch"
@@ -412,7 +536,8 @@ class HttpParserNgTest < Test::Unit::TestCase
       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
     }.each do |uri,expect|
       assert_equal req, @parser.headers(req.clear, str % [ uri ])
-      @parser.reset
+      req = req.dup
+      @parser.clear
       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
       assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
       assert_equal expect[qs], req[qs], "#{qs} mismatch"
@@ -440,11 +565,22 @@ class HttpParserNgTest < Test::Unit::TestCase
     end
   end
 
+  def test_backtrace_is_empty
+    begin
+      @parser.headers({}, "AAADFSFDSFD\r\n\r\n")
+      assert false, "should never get here line:#{__LINE__}"
+    rescue HttpParserError => e
+      assert_equal [], e.backtrace
+      return
+    end
+    assert false, "should never get here line:#{__LINE__}"
+  end
+
   def test_ignore_version_header
-    http = "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n"
-    req = {}
-    assert_equal req, @parser.headers(req, http)
-    assert_equal '', http
+    @parser.buf << "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n"
+    req = @parser.env
+    assert_equal req, @parser.parse
+    assert_equal '', @parser.buf
     expect = {
       "SERVER_NAME" => "localhost",
       "rack.url_scheme" => "http",
@@ -460,4 +596,59 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert_equal expect, req
   end
 
+  def test_pipelined_requests
+    host = "example.com"
+    expect = {
+      "HTTP_HOST" => host,
+      "SERVER_NAME" => host,
+      "REQUEST_PATH" => "/",
+      "rack.url_scheme" => "http",
+      "SERVER_PROTOCOL" => "HTTP/1.1",
+      "PATH_INFO" => "/",
+      "HTTP_VERSION" => "HTTP/1.1",
+      "REQUEST_URI" => "/",
+      "SERVER_PORT" => "80",
+      "REQUEST_METHOD" => "GET",
+      "QUERY_STRING" => ""
+    }
+    req1 = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
+    req2 = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
+    @parser.buf << (req1 + req2)
+    env1 = @parser.parse.dup
+    assert_equal expect, env1
+    assert_equal req2, @parser.buf
+    assert ! @parser.env.empty?
+    assert @parser.next?
+    assert @parser.keepalive?
+    assert @parser.headers?
+    assert_equal expect, @parser.env
+    env2 = @parser.parse.dup
+    host.replace "www.example.com"
+    assert_equal "www.example.com", expect["HTTP_HOST"]
+    assert_equal "www.example.com", expect["SERVER_NAME"]
+    assert_equal expect, env2
+    assert_equal "", @parser.buf
+  end
+
+  def test_keepalive_requests_disabled
+    req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
+    expect = {
+      "SERVER_NAME" => "example.com",
+      "HTTP_HOST" => "example.com",
+      "rack.url_scheme" => "http",
+      "REQUEST_PATH" => "/",
+      "SERVER_PROTOCOL" => "HTTP/1.1",
+      "PATH_INFO" => "/",
+      "HTTP_VERSION" => "HTTP/1.1",
+      "REQUEST_URI" => "/",
+      "SERVER_PORT" => "80",
+      "REQUEST_METHOD" => "GET",
+      "QUERY_STRING" => ""
+    }.freeze
+    HttpParser.keepalive_requests = 0
+    @parser = HttpParser.new
+    @parser.buf << req
+    assert_equal expect, @parser.parse
+    assert ! @parser.next?
+  end
 end
diff --git a/test/unit/test_http_parser_xftrust.rb b/test/unit/test_http_parser_xftrust.rb
new file mode 100644
index 0000000..db8cfa9
--- /dev/null
+++ b/test/unit/test_http_parser_xftrust.rb
@@ -0,0 +1,38 @@
+# -*- encoding: binary -*-
+require 'test/test_helper'
+
+include Unicorn
+
+class HttpParserXFTrustTest < Test::Unit::TestCase
+  def setup
+    assert HttpParser.trust_x_forwarded?
+  end
+
+  def test_xf_trust_false_xfp
+    HttpParser.trust_x_forwarded = false
+    parser = HttpParser.new
+    parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \
+                  "X-Forwarded-Proto: https\r\n\r\n"
+    env = parser.parse
+    assert_kind_of Hash, env
+    assert_equal 'foo', env['SERVER_NAME']
+    assert_equal '80', env['SERVER_PORT']
+    assert_equal 'http', env['rack.url_scheme']
+  end
+
+  def test_xf_trust_false_xfs
+    HttpParser.trust_x_forwarded = false
+    parser = HttpParser.new
+    parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \
+                  "X-Forwarded-SSL: on\r\n\r\n"
+    env = parser.parse
+    assert_kind_of Hash, env
+    assert_equal 'foo', env['SERVER_NAME']
+    assert_equal '80', env['SERVER_PORT']
+    assert_equal 'http', env['rack.url_scheme']
+  end
+
+  def teardown
+    HttpParser.trust_x_forwarded = true
+  end
+end
diff --git a/test/unit/test_request.rb b/test/unit/test_request.rb
index 1896300..bd452a5 100644
--- a/test/unit/test_request.rb
+++ b/test/unit/test_request.rb
@@ -11,7 +11,11 @@ class RequestTest < Test::Unit::TestCase
 
   class MockRequest < StringIO
     alias_method :readpartial, :sysread
+    alias_method :kgio_read!, :sysread
     alias_method :read_nonblock, :sysread
+    def kgio_addr
+      '127.0.0.1'
+    end
   end
 
   def setup
@@ -117,7 +121,7 @@ class RequestTest < Test::Unit::TestCase
 
   def test_no_content_stringio
     client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
-    res = env = nil
+    env = nil
     assert_nothing_raised { env = @request.read(client) }
     assert_equal StringIO, env['rack.input'].class
   end
@@ -126,7 +130,7 @@ class RequestTest < Test::Unit::TestCase
     client = MockRequest.new("PUT / HTTP/1.1\r\n" \
                              "Content-Length: 0\r\n" \
                              "Host: foo\r\n\r\n")
-    res = env = nil
+    env = nil
     assert_nothing_raised { env = @request.read(client) }
     assert_equal StringIO, env['rack.input'].class
   end
@@ -135,7 +139,7 @@ class RequestTest < Test::Unit::TestCase
     client = MockRequest.new("PUT / HTTP/1.1\r\n" \
                              "Content-Length: 1\r\n" \
                              "Host: foo\r\n\r\n")
-    res = env = nil
+    env = nil
     assert_nothing_raised { env = @request.read(client) }
     assert_equal Unicorn::TeeInput, env['rack.input'].class
   end
@@ -159,6 +163,14 @@ class RequestTest < Test::Unit::TestCase
     buf = (' ' * bs).freeze
     length = bs * count
     client = Tempfile.new('big_put')
+    def client.kgio_addr; '127.0.0.1'; end
+    def client.kgio_read(*args)
+      readpartial(*args)
+    rescue EOFError
+    end
+    def client.kgio_read!(*args)
+      readpartial(*args)
+    end
     client.syswrite(
       "PUT / HTTP/1.1\r\n" \
       "Host: foo\r\n" \
diff --git a/test/unit/test_response.rb b/test/unit/test_response.rb
index b3bc3a2..fb6edef 100644
--- a/test/unit/test_response.rb
+++ b/test/unit/test_response.rb
@@ -7,41 +7,42 @@
 # for more information.
 
 require 'test/test_helper'
+require 'time'
 
 include Unicorn
 
 class ResponseTest < Test::Unit::TestCase
-  
+  include Unicorn::HttpResponse
+
+  def test_httpdate
+    before = Time.now.to_i
+    str = httpdate
+    assert_kind_of(String, str)
+    middle = Time.parse(str).to_i
+    after = Time.now.to_i
+    assert before <= middle
+    assert middle <= after
+  end
+
   def test_response_headers
     out = StringIO.new
-    HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, ["cool"]])
+    http_response_write(out, 200, {"X-Whatever" => "stuff"}, ["cool"])
     assert ! out.closed?
+
     assert out.length > 0, "output didn't have data"
   end
 
   def test_response_string_status
     out = StringIO.new
-    HttpResponse.write(out,['200', {}, []])
+    http_response_write(out,'200', {}, [])
     assert ! out.closed?
     assert out.length > 0, "output didn't have data"
     assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
   end
 
-  def test_response_OFS_set
-    old_ofs = $,
-    $, = "\f\v"
-    out = StringIO.new
-    HttpResponse.write(out,[200, {"X-k" => "cd","X-y" => "z"}, ["cool"]])
-    assert ! out.closed?
-    resp = out.string
-    assert ! resp.include?("\f\v"), "output didn't use $, ($OFS)"
-    ensure
-      $, = old_ofs
-  end
-
   def test_response_200
     io = StringIO.new
-    HttpResponse.write(io, [200, {}, []])
+    http_response_write(io, 200, {}, [])
     assert ! io.closed?
     assert io.length > 0, "output didn't have data"
   end
@@ -49,7 +50,7 @@ class ResponseTest < Test::Unit::TestCase
   def test_response_with_default_reason
     code = 400
     io = StringIO.new
-    HttpResponse.write(io, [code, {}, []])
+    http_response_write(io, code, {}, [])
     assert ! io.closed?
     lines = io.string.split(/\r\n/)
     assert_match(/.* Bad Request$/, lines.first,
@@ -58,7 +59,7 @@ class ResponseTest < Test::Unit::TestCase
 
   def test_rack_multivalue_headers
     out = StringIO.new
-    HttpResponse.write(out,[200, {"X-Whatever" => "stuff\nbleh"}, []])
+    http_response_write(out,200, {"X-Whatever" => "stuff\nbleh"}, [])
     assert ! out.closed?
     assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
   end
@@ -67,21 +68,9 @@ class ResponseTest < Test::Unit::TestCase
   # some broken clients still rely on it
   def test_status_header_added
     out = StringIO.new
-    HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, []])
-    assert ! out.closed?
-    assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
-  end
-
-  # we always favor the code returned by the application, since "Status"
-  # in the header hash is not allowed by Rack (but not every app is
-  # fully Rack-compliant).
-  def test_status_header_ignores_app_hash
-    out = StringIO.new
-    header_hash = {"X-Whatever" => "stuff", 'StaTus' => "666" }
-    HttpResponse.write(out,[200, header_hash, []])
+    http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
     assert ! out.closed?
     assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
-    assert_equal 1, out.string.split(/\r\n/).grep(/^Status:/i).size
   end
 
   def test_body_closed
@@ -89,7 +78,7 @@ class ResponseTest < Test::Unit::TestCase
     body = StringIO.new(expect_body)
     body.rewind
     out = StringIO.new
-    HttpResponse.write(out,[200, {}, body])
+    http_response_write(out,200, {}, body)
     assert ! out.closed?
     assert body.closed?
     assert_match(expect_body, out.string.split(/\r\n/).last)
@@ -97,7 +86,7 @@ class ResponseTest < Test::Unit::TestCase
 
   def test_unknown_status_pass_through
     out = StringIO.new
-    HttpResponse.write(out,["666 I AM THE BEAST", {}, [] ])
+    http_response_write(out,"666 I AM THE BEAST", {}, [] )
     assert ! out.closed?
     headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
     assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb
index 41d3e02..88d7aba 100644
--- a/test/unit/test_server.rb
+++ b/test/unit/test_server.rb
@@ -10,7 +10,7 @@ require 'test/test_helper'
 
 include Unicorn
 
-class TestHandler
+class TestHandler
 
   def call(env)
     while env['rack.input'].read(4096)
@@ -19,7 +19,7 @@ class TestHandler
     rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
       $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
       raise e
-   end
+  end
 end
 
 
@@ -169,7 +169,6 @@ class WebServerTest < Test::Unit::TestCase
 
   def test_client_malformed_body
     sock = nil
-    buf = nil
     bs = 15653984
     assert_nothing_raised do
       sock = TCPSocket.new('127.0.0.1', @port)
@@ -271,7 +270,7 @@ class WebServerTest < Test::Unit::TestCase
   def test_file_streamed_request
     body = "a" * (Unicorn::Const::MAX_BODY * 2)
     long = "PUT /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body
-    do_test(long, Unicorn::Const::CHUNK_SIZE * 2 -400)
+    do_test(long, Unicorn::Const::CHUNK_SIZE * 2 - 400)
   end
 
   def test_file_streamed_request_bad_body
@@ -279,13 +278,11 @@ class WebServerTest < Test::Unit::TestCase
     long = "GET /test HTTP/1.1\r\nContent-ength: #{body.length}\r\n\r\n" + body
     assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
                   Errno::EBADF) {
-      do_test(long, Unicorn::Const::CHUNK_SIZE * 2 -400)
+      do_test(long, Unicorn::Const::CHUNK_SIZE * 2 - 400)
     }
   end
 
   def test_listener_names
     assert_equal [ "127.0.0.1:#@port" ], Unicorn.listener_names
   end
-
 end
-
diff --git a/test/unit/test_signals.rb b/test/unit/test_signals.rb
index 7c78b44..71cf8f4 100644
--- a/test/unit/test_signals.rb
+++ b/test/unit/test_signals.rb
@@ -166,7 +166,7 @@ class SignalsTest < Test::Unit::TestCase
     expect = @bs * @count
     assert_equal(expect, got, "expect=#{expect} got=#{got}")
     assert_nothing_raised { sock.close }
-  end unless ENV['RBX_SKIP']
+  end
 
   def test_request_read
     app = lambda { |env|
diff --git a/test/unit/test_stream_input.rb b/test/unit/test_stream_input.rb
new file mode 100644
index 0000000..f59157a
--- /dev/null
+++ b/test/unit/test_stream_input.rb
@@ -0,0 +1,204 @@
+# -*- encoding: binary -*-
+
+require 'test/unit'
+require 'digest/sha1'
+require 'unicorn'
+
+class TestStreamInput < Test::Unit::TestCase
+  def setup
+    @rs = $/
+    @env = {}
+    @rd, @wr = Kgio::UNIXSocket.pair
+    @rd.sync = @wr.sync = true
+    @start_pid = $$
+  end
+
+  def teardown
+    return if $$ != @start_pid
+    $/ = @rs
+    @rd.close rescue nil
+    @wr.close rescue nil
+    Process.waitall
+  end
+
+  def test_read_negative
+    r = init_request('hello')
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_raises(ArgumentError) { si.read(-1) }
+    assert_equal 'hello', si.read
+  end
+
+  def test_read_small
+    r = init_request('hello')
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal 'hello', si.read
+    assert_equal '', si.read
+    assert_nil si.read(5)
+    assert_nil si.gets
+  end
+
+  def test_gets_oneliner
+    r = init_request('hello')
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal 'hello', si.gets
+    assert_nil si.gets
+  end
+
+  def test_gets_multiline
+    r = init_request("a\nb\n\n")
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal "a\n", si.gets
+    assert_equal "b\n", si.gets
+    assert_equal "\n", si.gets
+    assert_nil si.gets
+  end
+
+  def test_gets_empty_rs
+    $/ = nil
+    r = init_request("a\nb\n\n")
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal "a\nb\n\n", si.gets
+    assert_nil si.gets
+  end
+
+  def test_read_with_equal_len
+    r = init_request("abcde")
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal "abcde", si.read(5)
+    assert_nil si.read(5)
+  end
+
+  def test_big_body_multi
+    r = init_request('.', Unicorn::Const::MAX_BODY + 1)
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
+    assert ! @parser.body_eof?
+    nr = Unicorn::Const::MAX_BODY / 4
+    pid = fork {
+      @rd.close
+      nr.times { @wr.write('....') }
+      @wr.close
+    }
+    @wr.close
+    assert_equal '.', si.read(1)
+    nr.times { |x|
+      assert_equal '....', si.read(4), "nr=#{x}"
+    }
+    assert_nil si.read(1)
+    status = nil
+    assert_nothing_raised { pid, status = Process.waitpid2(pid) }
+    assert status.success?
+  end
+
+  def test_gets_long
+    r = init_request("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
+    si = Unicorn::StreamInput.new(@rd, r)
+    status = line = nil
+    pid = fork {
+      @rd.close
+      3.times { @wr.write("ffff" * 4096) }
+      @wr.write "#$/foo#$/"
+      @wr.close
+    }
+    @wr.close
+    assert_nothing_raised { line = si.gets }
+    assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
+    assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
+    assert_nothing_raised { line = si.gets }
+    assert_equal "foo#$/", line
+    assert_nil si.gets
+    assert_nothing_raised { pid, status = Process.waitpid2(pid) }
+    assert status.success?
+  end
+
+  def test_read_with_buffer
+    r = init_request('hello')
+    si = Unicorn::StreamInput.new(@rd, r)
+    buf = ''
+    rv = si.read(4, buf)
+    assert_equal 'hell', rv
+    assert_equal 'hell', buf
+    assert_equal rv.object_id, buf.object_id
+    assert_equal 'o', si.read
+    assert_equal nil, si.read(5, buf)
+  end
+
+  def test_read_with_buffer_clobbers
+    r = init_request('hello')
+    si = Unicorn::StreamInput.new(@rd, r)
+    buf = 'foo'
+    assert_equal 'hello', si.read(nil, buf)
+    assert_equal 'hello', buf
+    assert_equal '', si.read(nil, buf)
+    assert_equal '', buf
+    buf = 'asdf'
+    assert_nil si.read(5, buf)
+    assert_equal '', buf
+  end
+
+  def test_read_zero
+    r = init_request('hello')
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal '', si.read(0)
+    buf = 'asdf'
+    rv = si.read(0, buf)
+    assert_equal rv.object_id, buf.object_id
+    assert_equal '', buf
+    assert_equal 'hello', si.read
+    assert_nil si.read(5)
+    assert_equal '', si.read(0)
+    buf = 'hello'
+    rv = si.read(0, buf)
+    assert_equal rv.object_id, buf.object_id
+    assert_equal '', rv
+  end
+
+  def test_gets_read_mix
+    r = init_request("hello\nasdfasdf")
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal "hello\n", si.gets
+    assert_equal "asdfasdf", si.read(9)
+    assert_nil si.read(9)
+  end
+
+  def test_gets_read_mix_chunked
+    r = @parser = Unicorn::HttpParser.new
+    body = "6\r\nhello"
+    @buf = "POST / HTTP/1.1\r\n" \
+           "Host: localhost\r\n" \
+           "Transfer-Encoding: chunked\r\n" \
+           "\r\n#{body}"
+    assert_equal @env, @parser.headers(@env, @buf)
+    assert_equal body, @buf
+    si = Unicorn::StreamInput.new(@rd, r)
+    @wr.syswrite "\n\r\n"
+    assert_equal "hello\n", si.gets
+    @wr.syswrite "8\r\nasdfasdf\r\n"
+    assert_equal"asdfasdf", si.read(9) + si.read(9)
+    @wr.syswrite "0\r\n\r\n"
+    assert_nil si.read(9)
+  end
+
+  def test_gets_read_mix_big
+    r = init_request("hello\n#{'.' * 65536}")
+    si = Unicorn::StreamInput.new(@rd, r)
+    assert_equal "hello\n", si.gets
+    assert_equal '.' * 16384, si.read(16384)
+    assert_equal '.' * 16383, si.read(16383)
+    assert_equal '.' * 16384, si.read(16384)
+    assert_equal '.' * 16385, si.read(16385)
+    assert_nil si.gets
+  end
+
+  def init_request(body, size = nil)
+    @parser = Unicorn::HttpParser.new
+    body = body.to_s.freeze
+    @buf = "POST / HTTP/1.1\r\n" \
+           "Host: localhost\r\n" \
+           "Content-Length: #{size || body.size}\r\n" \
+           "\r\n#{body}"
+    assert_equal @env, @parser.headers(@env, @buf)
+    assert_equal body, @buf
+    @parser
+  end
+end
diff --git a/test/unit/test_tee_input.rb b/test/unit/test_tee_input.rb
index a127882..96eb268 100644
--- a/test/unit/test_tee_input.rb
+++ b/test/unit/test_tee_input.rb
@@ -4,12 +4,15 @@ require 'test/unit'
 require 'digest/sha1'
 require 'unicorn'
 
+class TeeInput < Unicorn::TeeInput
+  attr_accessor :tmp, :len
+end
+
 class TestTeeInput < Test::Unit::TestCase
 
   def setup
     @rs = $/
-    @env = {}
-    @rd, @wr = IO.pipe
+    @rd, @wr = Kgio::UNIXSocket.pair
     @rd.sync = @wr.sync = true
     @start_pid = $$
   end
@@ -27,8 +30,8 @@ class TestTeeInput < Test::Unit::TestCase
   end
 
   def test_gets_long
-    init_parser("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    r = init_request("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
+    ti = TeeInput.new(@rd, r)
     status = line = nil
     pid = fork {
       @rd.close
@@ -48,8 +51,8 @@ class TestTeeInput < Test::Unit::TestCase
   end
 
   def test_gets_short
-    init_parser("hello", 5 + "#$/foo".size)
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    r = init_request("hello", 5 + "#$/foo".size)
+    ti = TeeInput.new(@rd, r)
     status = line = nil
     pid = fork {
       @rd.close
@@ -67,8 +70,8 @@ class TestTeeInput < Test::Unit::TestCase
   end
 
   def test_small_body
-    init_parser('hello')
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    r = init_request('hello')
+    ti = TeeInput.new(@rd, r)
     assert_equal 0, @parser.content_length
     assert @parser.body_eof?
     assert_equal StringIO, ti.tmp.class
@@ -77,11 +80,12 @@ class TestTeeInput < Test::Unit::TestCase
     assert_equal 'hello', ti.read
     assert_equal '', ti.read
     assert_nil ti.read(4096)
+    assert_equal 5, ti.size
   end
 
   def test_read_with_buffer
-    init_parser('hello')
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    r = init_request('hello')
+    ti = TeeInput.new(@rd, r)
     buf = ''
     rv = ti.read(4, buf)
     assert_equal 'hell', rv
@@ -95,8 +99,8 @@ class TestTeeInput < Test::Unit::TestCase
   end
 
   def test_big_body
-    init_parser('.' * Unicorn::Const::MAX_BODY << 'a')
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    r = init_request('.' * Unicorn::Const::MAX_BODY << 'a')
+    ti = TeeInput.new(@rd, r)
     assert_equal 0, @parser.content_length
     assert @parser.body_eof?
     assert_kind_of File, ti.tmp
@@ -106,9 +110,9 @@ class TestTeeInput < Test::Unit::TestCase
 
   def test_read_in_full_if_content_length
     a, b = 300, 3
-    init_parser('.' * b, 300)
+    r = init_request('.' * b, 300)
     assert_equal 300, @parser.content_length
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    ti = TeeInput.new(@rd, r)
     pid = fork {
       @wr.write('.' * 197)
       sleep 1 # still a *potential* race here that would make the test moot...
@@ -121,13 +125,12 @@ class TestTeeInput < Test::Unit::TestCase
   end
 
   def test_big_body_multi
-    init_parser('.', Unicorn::Const::MAX_BODY + 1)
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    r = init_request('.', Unicorn::Const::MAX_BODY + 1)
+    ti = TeeInput.new(@rd, r)
     assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
     assert ! @parser.body_eof?
     assert_kind_of File, ti.tmp
     assert_equal 0, ti.tmp.pos
-    assert_equal 1, ti.tmp.size
     assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
     nr = Unicorn::Const::MAX_BODY / 4
     pid = fork {
@@ -138,8 +141,8 @@ class TestTeeInput < Test::Unit::TestCase
     @wr.close
     assert_equal '.', ti.read(1)
     assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
-    nr.times {
-      assert_equal '....', ti.read(4)
+    nr.times { |x|
+      assert_equal '....', ti.read(4), "nr=#{x}"
       assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
     }
     assert_nil ti.read(1)
@@ -150,12 +153,12 @@ class TestTeeInput < Test::Unit::TestCase
 
   def test_chunked
     @parser = Unicorn::HttpParser.new
-    @buf = "POST / HTTP/1.1\r\n" \
-           "Host: localhost\r\n" \
-           "Transfer-Encoding: chunked\r\n" \
-           "\r\n"
-    assert_equal @env, @parser.headers(@env, @buf)
-    assert_equal "", @buf
+    @parser.buf << "POST / HTTP/1.1\r\n" \
+                   "Host: localhost\r\n" \
+                   "Transfer-Encoding: chunked\r\n" \
+                   "\r\n"
+    assert @parser.parse
+    assert_equal "", @parser.buf
 
     pid = fork {
       @rd.close
@@ -163,7 +166,7 @@ class TestTeeInput < Test::Unit::TestCase
       @wr.write("0\r\n\r\n")
     }
     @wr.close
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    ti = TeeInput.new(@rd, @parser)
     assert_nil @parser.content_length
     assert_nil ti.len
     assert ! @parser.body_eof?
@@ -185,12 +188,13 @@ class TestTeeInput < Test::Unit::TestCase
 
   def test_chunked_ping_pong
     @parser = Unicorn::HttpParser.new
-    @buf = "POST / HTTP/1.1\r\n" \
+    buf = @parser.buf
+    buf << "POST / HTTP/1.1\r\n" \
            "Host: localhost\r\n" \
            "Transfer-Encoding: chunked\r\n" \
            "\r\n"
-    assert_equal @env, @parser.headers(@env, @buf)
-    assert_equal "", @buf
+    assert @parser.parse
+    assert_equal "", buf
     chunks = %w(aa bbb cccc dddd eeee)
     rd, wr = IO.pipe
 
@@ -201,7 +205,7 @@ class TestTeeInput < Test::Unit::TestCase
       end
       @wr.write("0\r\n\r\n")
     }
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    ti = TeeInput.new(@rd, @parser)
     assert_nil @parser.content_length
     assert_nil ti.len
     assert ! @parser.body_eof?
@@ -215,13 +219,14 @@ class TestTeeInput < Test::Unit::TestCase
 
   def test_chunked_with_trailer
     @parser = Unicorn::HttpParser.new
-    @buf = "POST / HTTP/1.1\r\n" \
+    buf = @parser.buf
+    buf << "POST / HTTP/1.1\r\n" \
            "Host: localhost\r\n" \
            "Trailer: Hello\r\n" \
            "Transfer-Encoding: chunked\r\n" \
            "\r\n"
-    assert_equal @env, @parser.headers(@env, @buf)
-    assert_equal "", @buf
+    assert @parser.parse
+    assert_equal "", buf
 
     pid = fork {
       @rd.close
@@ -230,28 +235,62 @@ class TestTeeInput < Test::Unit::TestCase
       @wr.write("Hello: World\r\n\r\n")
     }
     @wr.close
-    ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
+    ti = TeeInput.new(@rd, @parser)
     assert_nil @parser.content_length
     assert_nil ti.len
     assert ! @parser.body_eof?
     assert_equal 25, ti.size
-    assert_equal "World", @env['HTTP_HELLO']
+    assert_equal "World", @parser.env['HTTP_HELLO']
     status = nil
     assert_nothing_raised { pid, status = Process.waitpid2(pid) }
     assert status.success?
   end
 
+  def test_chunked_and_size_slow
+    @parser = Unicorn::HttpParser.new
+    buf = @parser.buf
+    buf << "POST / HTTP/1.1\r\n" \
+           "Host: localhost\r\n" \
+           "Trailer: Hello\r\n" \
+           "Transfer-Encoding: chunked\r\n" \
+           "\r\n"
+    assert @parser.parse
+    assert_equal "", buf
+
+    @wr.write("9\r\nabcde")
+    ti = TeeInput.new(@rd, @parser)
+    assert_nil @parser.content_length
+    assert_equal "abcde", ti.read(9)
+    assert ! @parser.body_eof?
+    @wr.write("fghi\r\n0\r\nHello: World\r\n\r\n")
+    assert_equal 9, ti.size
+    assert_equal "fghi", ti.read(9)
+    assert_equal nil, ti.read(9)
+    assert_equal "World", @parser.env['HTTP_HELLO']
+  end
+
+  def test_gets_read_mix
+    r = init_request("hello\nasdfasdf")
+    ti = Unicorn::TeeInput.new(@rd, r)
+    assert_equal "hello\n", ti.gets
+    assert_equal "asdfasdf", ti.read(9)
+    assert_nil ti.read(9)
+  end
+
 private
 
-  def init_parser(body, size = nil)
+  def init_request(body, size = nil)
     @parser = Unicorn::HttpParser.new
     body = body.to_s.freeze
-    @buf = "POST / HTTP/1.1\r\n" \
+    buf = @parser.buf
+    buf << "POST / HTTP/1.1\r\n" \
            "Host: localhost\r\n" \
            "Content-Length: #{size || body.size}\r\n" \
            "\r\n#{body}"
-    assert_equal @env, @parser.headers(@env, @buf)
-    assert_equal body, @buf
+    assert @parser.parse
+    assert_equal body, buf
+    @buf = buf
+    @parser
   end
 
 end
diff --git a/test/unit/test_upload.rb b/test/unit/test_upload.rb
index dc0eb40..e2c103a 100644
--- a/test/unit/test_upload.rb
+++ b/test/unit/test_upload.rb
@@ -145,8 +145,14 @@ class UploadTest < Test::Unit::TestCase
   end
 
   def test_put_excessive_overwrite_closed
+    tmp = Tempfile.new('overwrite_check')
+    tmp.sync = true
     start_server(lambda { |env|
-      while env['rack.input'].read(65536); end
+      nr = 0
+      while buf = env['rack.input'].read(65536)
+        nr += buf.size
+      end
+      tmp.write(nr.to_s)
       [ 200, @hdr, [] ]
     })
     sock = TCPSocket.new(@addr, @port)
@@ -157,7 +163,9 @@ class UploadTest < Test::Unit::TestCase
     assert_raise(Errno::ECONNRESET, Errno::EPIPE) do
       ::Unicorn::Const::CHUNK_SIZE.times { sock.syswrite(buf) }
     end
-    assert_equal "HTTP/1.1 200 OK\r\n", sock.gets
+    assert_nothing_raised { sock.gets }
+    tmp.rewind
+    assert_equal length, tmp.read.to_i
   end
 
   # Despite reading numerous articles and inspecting the 1.9.1-p0 C