From 70bbef66cf7a78e3d08ef3a4c2b0699596730674 Mon Sep 17 00:00:00 2001 From: Evan Phoenix Date: Thu, 9 Aug 2012 16:54:55 -0700 Subject: [PATCH] Work around JRuby buffering the request inside #accept --- lib/puma/client.rb | 50 +++++++++++++++++++++++++++++++++++++++++++++ lib/puma/const.rb | 4 ++++ lib/puma/reactor.rb | 20 ++++++++++++++++-- lib/puma/server.rb | 6 ++++-- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/lib/puma/client.rb b/lib/puma/client.rb index 01a6d2af..b1565051 100644 --- a/lib/puma/client.rb +++ b/lib/puma/client.rb @@ -1,3 +1,8 @@ +class IO + module WaitReadable + end +end + module Puma class Client include Puma::Const @@ -17,10 +22,16 @@ module Puma @buffer = nil @timeout_at = nil + + @requests_served = 0 end attr_reader :env, :to_io, :body, :io, :timeout_at, :ready + def inspect + "#" + end + def set_timeout(val) @timeout_at = Time.now + val end @@ -65,6 +76,7 @@ module Puma unless cl @buffer = body.empty? ? nil : body @body = EmptyBody + @requests_served += 1 @ready = true return true end @@ -74,6 +86,7 @@ module Puma if remain <= 0 @body = StringIO.new(body) @buffer = nil + @requests_served += 1 @ready = true return true end @@ -119,8 +132,43 @@ module Puma false end + def jruby_start_try_to_finish + return read_body unless @read_header + + begin + data = @io.sysread_nonblock(CHUNK_SIZE) + rescue OpenSSL::SSL::SSLError => e + return false if e.kind_of? IO::WaitReadable + raise e + end + + if @buffer + @buffer << data + else + @buffer = data + end + + @parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes) + + if @parser.finished? + return setup_body + elsif @parsed_bytes >= MAX_HEADER + raise HttpParserError, + "HEADER is longer than allowed, aborting client early." + end + + false + end + def eagerly_finish return true if @ready + + if defined?(JRUBY_VERSION) and + defined?(OpenSSL::SSL::SSLSocket) and + @io.kind_of? OpenSSL::SSL::SSLSocket + return true if jruby_start_try_to_finish + end + return false unless IO.select([@to_io], nil, nil, 0) try_to_finish end @@ -142,6 +190,7 @@ module Puma unless chunk @body.close @buffer = nil + @requests_served += 1 @ready = true raise EOFError end @@ -151,6 +200,7 @@ module Puma if remain <= 0 @body.rewind @buffer = nil + @requests_served += 1 @ready = true return true end diff --git a/lib/puma/const.rb b/lib/puma/const.rb index 26d81af2..0ff153ac 100644 --- a/lib/puma/const.rb +++ b/lib/puma/const.rb @@ -31,6 +31,10 @@ module Puma # session. PERSISTENT_TIMEOUT = 20 + # The default number of seconds to wait until we get the first data + # for the request + FIRST_DATA_TIMEOUT = 30 + DATE = "Date".freeze SCRIPT_NAME = "SCRIPT_NAME".freeze diff --git a/lib/puma/reactor.rb b/lib/puma/reactor.rb index 75087d56..72e9d87f 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -46,11 +46,19 @@ module Puma c.close sockets.delete c + if c.timeout_at + @timeouts.delete c + end + @events.parse_error @server, c.env, e - rescue EOFError + rescue EOFError => e c.close sockets.delete c + + if c.timeout_at + @timeouts.delete c + end end end end @@ -73,7 +81,15 @@ module Puma end def run_in_thread - @thread = Thread.new { run } + @thread = Thread.new { + begin + run + rescue Exception => e + puts "MAJOR ERROR DETECTED" + p e + puts e.backtrace + end + } end def calculate_sleep diff --git a/lib/puma/server.rb b/lib/puma/server.rb index 544abe8a..1d22034b 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -56,6 +56,8 @@ module Puma @persistent_timeout = PERSISTENT_TIMEOUT @persistent_check, @persistent_wakeup = IO.pipe + @first_data_timeout = FIRST_DATA_TIMEOUT + @unix_paths = [] @proto_env = { @@ -199,7 +201,6 @@ module Puma @status = :run @thread_pool = ThreadPool.new(@min_threads, @max_threads) do |client| - process_now = false begin @@ -213,6 +214,7 @@ module Puma if process_now process_client client else + client.set_timeout @first_data_timeout @reactor.add client end end @@ -315,7 +317,7 @@ module Puma end # The client disconnected while we were reading data - rescue EOFError, SystemCallError + rescue EOFError, SystemCallError => e # Swallow them. The ensure tries to close +client+ down # The client doesn't know HTTP well