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/base.rb | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 lib/rainbows/base.rb (limited to 'lib/rainbows/base.rb') 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 -- cgit v1.2.3-24-ge0c7