about summary refs log tree commit homepage
path: root/test
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-03-27 17:26:03 -0700
committerEric Wong <normalperson@yhbt.net>2009-03-27 17:26:03 -0700
commitc83b5a903a076fda67c7d062da1ad6ff9337fdd1 (patch)
treec543d22f6227be5f373274a09a05b3ce237fe869 /test
parent9509f414a88281c93f0a1cb28123b8ae7538ee7f (diff)
downloadunicorn-c83b5a903a076fda67c7d062da1ad6ff9337fdd1.tar.gz
This reworks error handling throughout the entire stack to be
more Ruby-ish.  Exceptions are raised instead of forcing the
us to check return values.

  If a client is sending us a bad request, we send a 400.

  If unicorn or app breaks in an unexpected way, we'll
  send a 500.

Both of these last-resort error responses are sent using
IO#write_nonblock to avoid tying Unicorn up longer than
necessary and all exceptions raised are ignored.

Sending a valid HTTP response back should reduce the chance of
us from being marked as down or broken by a load balancer.
Previously, some load balancers would mark us as down if we close
a socket without sending back a valid response; so make a best
effort to send one.  If for some reason we cannot write a valid
response, we're still susceptible to being marked as down.

A successful HttpResponse.write() call will now close the socket
immediately (instead of doing it higher up the stack).  This
ensures the errors will never get written to the socket on a
successful response.
Diffstat (limited to 'test')
-rw-r--r--test/unit/test_response.rb7
-rw-r--r--test/unit/test_server.rb29
2 files changed, 33 insertions, 3 deletions
diff --git a/test/unit/test_response.rb b/test/unit/test_response.rb
index 4c7423b..62cabdf 100644
--- a/test/unit/test_response.rb
+++ b/test/unit/test_response.rb
@@ -22,7 +22,7 @@ class ResponseTest < Test::Unit::TestCase
     $, = "\f\v"
     out = StringIO.new
     HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, ["cool"]])
-    resp = out.read
+    resp = out.string
     assert ! resp.include?("\f\v"), "output didn't use $, ($OFS)"
     ensure
       $, = old_ofs
@@ -38,8 +38,9 @@ class ResponseTest < Test::Unit::TestCase
     code = 400
     io = StringIO.new
     HttpResponse.write(io, [code, {}, []])
-    io.rewind
-    assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, io.readline.chomp, "wrong default reason phrase")
+    lines = io.string.split(/\r\n/)
+    assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, lines.first,
+                 "wrong default reason phrase")
   end
 
   def test_rack_multivalue_headers
diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb
index 356f946..eadeba4 100644
--- a/test/unit/test_server.rb
+++ b/test/unit/test_server.rb
@@ -35,6 +35,25 @@ class WebServerTest < Test::Unit::TestCase
     end
   end
 
+  def test_broken_app
+    teardown
+    port = unused_port
+    app = lambda { |env| raise RuntimeError, "hello" }
+    # [200, {}, []] }
+    redirect_test_io do
+      @server = HttpServer.new(app, :listeners => [ "127.0.0.1:#{port}"] )
+      @server.start
+    end
+    sock = nil
+    assert_nothing_raised do
+      sock = TCPSocket.new('127.0.0.1', port)
+      sock.syswrite("GET / HTTP/1.0\r\n\r\n")
+    end
+
+    assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
+    assert_nothing_raised { sock.close }
+  end
+
   def test_simple_server
     results = hit(["http://localhost:#{@port}/test"])
     assert_equal 'hello!\n', results[0], "Handler didn't really run"
@@ -77,6 +96,16 @@ class WebServerTest < Test::Unit::TestCase
     end
   end
 
+  def test_bad_client_400
+    sock = nil
+    assert_nothing_raised do
+      sock = TCPSocket.new('127.0.0.1', @port)
+      sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
+    end
+    assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
+    assert_nothing_raised { sock.close }
+  end
+
   def test_header_is_too_long
     redirect_test_io do
       long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n"