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