lib/rack/handler/ftw.rb in ftw-0.0.8 vs lib/rack/handler/ftw.rb in ftw-0.0.9
- old
+ new
@@ -1,10 +1,11 @@
require "rack"
require "ftw"
require "ftw/protocol"
require "ftw/crlf"
require "socket"
+require "cabin"
# FTW cannot fully respect the Rack 1.1 specification due to technical
# limitations in the Rack design, specifically:
#
# * rack.input must be buffered, to support IO#rewind, for the duration of each
@@ -52,24 +53,28 @@
# A string constant value (used to avoid typos).
RACK_DOT_MULTIPROCESS = "rack.multiprocess".freeze
# A string constant value (used to avoid typos).
RACK_DOT_RUN_ONCE = "rack.run_once".freeze
# A string constant value (used to avoid typos).
+ RACK_DOT_LOGGER = "rack.logger".freeze
+ # A string constant value (used to avoid typos).
FTW_DOT_CONNECTION = "ftw.connection".freeze
# This method is invoked when rack starts this as the server.
def self.run(app, config)
+ #@logger.subscribe(STDOUT)
server = self.new(app, config)
server.run
end # def self.run
private
# setup a new rack server
def initialize(app, config)
@app = app
@config = config
+ @threads = []
end # def initialize
# Run the server.
#
# Connections are farmed out to threads.
@@ -83,27 +88,40 @@
#
# """A Rack application is an Ruby object (not a class) that responds to
# call. It takes exactly one argument, the environment and returns an
# Array of exactly three values: The status, the headers, and the body."""
#
- server = FTW::Server.new([@config[:Host], @config[:Port]].join(":"))
- server.each_connection do |connection|
- Thread.new do
+ logger.info("Starting server", :config => @config)
+ @server = FTW::Server.new([@config[:Host], @config[:Port]].join(":"))
+ @server.each_connection do |connection|
+ @threads << Thread.new do
handle_connection(connection)
end
end
end # def run
+ def stop
+ @server.stop unless @server.nil?
+ @threads.each(&:join)
+ end # def stop
+
# Handle a new connection.
#
# This method parses http requests and passes them on to #handle_request
#
# @param connection The FTW::Connection being handled.
def handle_connection(connection)
while true
begin
request = read_http_message(connection)
+ rescue EOFError, Errno::EPIPE, Errno::ECONNRESET, HTTP::Parser::Error
+ # Connection EOF'd or errored before we finished reading a full HTTP
+ # message, shut it down.
+ break
+ end
+
+ begin
handle_request(request, connection)
rescue => e
puts e.inspect
puts e.backtrace
raise e
@@ -131,18 +149,19 @@
RACK_DOT_INPUT => connection,
RACK_DOT_ERRORS => STDERR,
RACK_DOT_MULTITHREAD => true,
RACK_DOT_MULTIPROCESS => false,
RACK_DOT_RUN_ONCE => false,
+ RACK_DOT_LOGGER => logger,
# Extensions, not in Rack v1.1.
# ftw.connection lets you access the connection involved in this request.
# It should be used when you need to hijack the connection for use
# in proxying, HTTP CONNECT, websockets, SPDY(maybe?), etc.
FTW_DOT_CONNECTION => connection
- }
+ } # env
request.headers.each do |name, value|
# The Rack spec says:
# """ Variables corresponding to the client-supplied HTTP request headers
# (i.e., variables whose names begin with HTTP_). The presence or
@@ -159,12 +178,14 @@
# with multiple values.
#
env["HTTP_#{name.upcase.gsub("-", "_")}"] = value
end # request.headers.each
+ # Invoke the application in this rack app
status, headers, body = @app.call(env)
+ # The application is done handling this request, respond to the client.
response = FTW::Response.new
response.status = status.to_i
response.version = request.version
headers.each do |name, value|
response.headers.add(name, value)
@@ -175,7 +196,15 @@
body.each do |chunk|
connection.write(chunk)
end
end # def handle_request
- public(:run, :initialize)
+ # Get the logger.
+ def logger
+ if @logger.nil?
+ @logger = Cabin::Channel.get
+ end
+ return @logger
+ end # def logger
+
+ public(:run, :initialize, :stop)
end