From 5b14ec54da4b3dcd52c8755b6a036e5e94a565d1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 3 Oct 2009 16:14:57 -0700 Subject: common Base class for all concurrency models They're similar enough (especially as far as the constants go) and allows a :Base to be used which basically acts like plain Unicorn but with HTTP keepalive + pipelining support --- lib/rainbows.rb | 2 +- lib/rainbows/base.rb | 69 +++++++++++++++++++++++++++++++++++++++++++++ lib/rainbows/revactor.rb | 4 +-- lib/rainbows/thread_base.rb | 61 --------------------------------------- lib/rainbows/thread_pool.rb | 4 +-- 5 files changed, 72 insertions(+), 68 deletions(-) create mode 100644 lib/rainbows/base.rb delete mode 100644 lib/rainbows/thread_base.rb (limited to 'lib') diff --git a/lib/rainbows.rb b/lib/rainbows.rb index f79c1e8..611c0ec 100644 --- a/lib/rainbows.rb +++ b/lib/rainbows.rb @@ -11,9 +11,9 @@ module Rainbows require 'rainbows/const' require 'rainbows/http_server' require 'rainbows/http_response' + require 'rainbows/base' autoload :Revactor, 'rainbows/revactor' - autoload :ThreadBase, 'rainbows/thread_base' autoload :ThreadPool, 'rainbows/thread_pool' class << self diff --git a/lib/rainbows/base.rb b/lib/rainbows/base.rb new file mode 100644 index 0000000..0e5843d --- /dev/null +++ b/lib/rainbows/base.rb @@ -0,0 +1,69 @@ +# -*- encoding: binary -*- + +module Rainbows + + # base class for Rainbows concurrency models + module Base + + include Unicorn + include Rainbows::Const + + # write a response without caring if it went out or not for error + # messages. + # TODO: merge into Unicorn::HttpServer + def emergency_response(client, response_str) + client.write_nonblock(response_str) rescue nil + client.close rescue nil + end + + # once a client is accepted, it is processed in its entirety here + # in 3 easy steps: read request, call app, write app response + def process_client(client) + buf = client.readpartial(CHUNK_SIZE) + hp = HttpParser.new + env = {} + remote_addr = TCPSocket === client ? client.peeraddr.last : LOCALHOST + + begin + while ! hp.headers(env, buf) + buf << client.readpartial(CHUNK_SIZE) + end + + env[RACK_INPUT] = 0 == hp.content_length ? + HttpRequest::NULL_IO : + Unicorn::TeeInput.new(client, env, hp, buf) + env[REMOTE_ADDR] = remote_addr + response = app.call(env.update(RACK_DEFAULTS)) + + if 100 == response.first.to_i + client.write(EXPECT_100_RESPONSE) + env.delete(HTTP_EXPECT) + response = app.call(env) + end + + out = [ hp.keepalive? ? CONN_ALIVE : CONN_CLOSE ] if hp.headers? + HttpResponse.write(client, response, out) + end while hp.keepalive? and hp.reset.nil? and env.clear + client.close + # if we get any error, try to write something back to the client + # assuming we haven't closed the socket, but don't get hung up + # if the socket is already closed or broken. We'll always ensure + # the socket is closed at the end of this function + rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF + emergency_response(client, ERROR_500_RESPONSE) + rescue HttpParserError # try to tell the client they're bad + buf.empty? or emergency_response(client, ERROR_400_RESPONSE) + rescue Object => e + emergency_response(client, ERROR_500_RESPONSE) + logger.error "Read error: #{e.inspect}" + logger.error e.backtrace.join("\n") + end + + def self.included(klass) + HttpServer.constants.each do |x| + klass.const_set(x, HttpServer.const_get(x)) + end + end + + end +end diff --git a/lib/rainbows/revactor.rb b/lib/rainbows/revactor.rb index d2e28ff..f602183 100644 --- a/lib/rainbows/revactor.rb +++ b/lib/rainbows/revactor.rb @@ -10,9 +10,7 @@ module Rainbows module Revactor require 'rainbows/revactor/tee_input' - include Unicorn - include Rainbows::Const - HttpServer.constants.each { |x| const_set(x, HttpServer.const_get(x)) } + include Base # once a client is accepted, it is processed in its entirety here # in 3 easy steps: read request, call app, write app response diff --git a/lib/rainbows/thread_base.rb b/lib/rainbows/thread_base.rb deleted file mode 100644 index 52abac3..0000000 --- a/lib/rainbows/thread_base.rb +++ /dev/null @@ -1,61 +0,0 @@ -# -*- encoding: binary -*- - -module Rainbows - - module ThreadBase - - include Unicorn - include Rainbows::Const - - # write a response without caring if it went out or not - # This is in the case of untrappable errors - def emergency_response(client, response_str) - client.write_nonblock(response_str) rescue nil - client.close rescue nil - end - - # once a client is accepted, it is processed in its entirety here - # in 3 easy steps: read request, call app, write app response - def process_client(client) - buf = client.readpartial(CHUNK_SIZE) - hp = HttpParser.new - env = {} - remote_addr = TCPSocket === client ? client.peeraddr.last : LOCALHOST - - begin - while ! hp.headers(env, buf) - buf << client.readpartial(CHUNK_SIZE) - end - - env[RACK_INPUT] = 0 == hp.content_length ? - HttpRequest::NULL_IO : - Unicorn::TeeInput.new(client, env, hp, buf) - env[REMOTE_ADDR] = remote_addr - response = app.call(env.update(RACK_DEFAULTS)) - - if 100 == response.first.to_i - client.write(EXPECT_100_RESPONSE) - env.delete(HTTP_EXPECT) - response = app.call(env) - end - - out = [ hp.keepalive? ? CONN_ALIVE : CONN_CLOSE ] if hp.headers? - HttpResponse.write(client, response, out) - end while hp.keepalive? and hp.reset.nil? and env.clear - client.close - # if we get any error, try to write something back to the client - # assuming we haven't closed the socket, but don't get hung up - # if the socket is already closed or broken. We'll always ensure - # the socket is closed at the end of this function - rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF - emergency_response(client, ERROR_500_RESPONSE) - rescue HttpParserError # try to tell the client they're bad - buf.empty? or emergency_response(client, ERROR_400_RESPONSE) - rescue Object => e - emergency_response(client, ERROR_500_RESPONSE) - logger.error "Read error: #{e.inspect}" - logger.error e.backtrace.join("\n") - end - end -end - diff --git a/lib/rainbows/thread_pool.rb b/lib/rainbows/thread_pool.rb index 104bd38..e12559a 100644 --- a/lib/rainbows/thread_pool.rb +++ b/lib/rainbows/thread_pool.rb @@ -4,9 +4,7 @@ module Rainbows module ThreadPool - include ThreadBase - - HttpServer.constants.each { |x| const_set(x, HttpServer.const_get(x)) } + include Base def worker_loop(worker) init_worker_process(worker) -- cgit v1.2.3-24-ge0c7