class Rack::Handler::FTW

FTW cannot fully respect the Rack 1.1 specification due to technical limitations in the Rack design, specifically:

FTW::Connection does not implement rewind. Need it? File a ticket.

To support HTTP Upgrade, CONNECT, and protocol-switching features, this server handler will set “ftw.connection” to the FTW::Connection related to this request.

The above data is based on the response to this ticket:

https://github.com/rack/rack/issues/347

Constants

FTW_DOT_CONNECTION

A string constant value (used to avoid typos).

PATH_INFO

A string constant value (used to avoid typos).

QUERY_STRING

A string constant value (used to avoid typos).

RACK_DOT_ERRORS

A string constant value (used to avoid typos).

RACK_DOT_INPUT

A string constant value (used to avoid typos).

RACK_DOT_LOGGER

A string constant value (used to avoid typos).

RACK_DOT_MULTIPROCESS

A string constant value (used to avoid typos).

RACK_DOT_MULTITHREAD

A string constant value (used to avoid typos).

RACK_DOT_RUN_ONCE

A string constant value (used to avoid typos).

RACK_DOT_URL_SCHEME

A string constant value (used to avoid typos).

RACK_DOT_VERSION

A string constant value (used to avoid typos).

RACK_VERSION

The version of the rack specification supported by this handler.

REQUEST_METHOD

A string constant value (used to avoid typos).

SCRIPT_NAME

A string constant value (used to avoid typos).

SERVER_NAME

A string constant value (used to avoid typos).

SERVER_PORT

A string constant value (used to avoid typos).

Public Class Methods

new(app, config) click to toggle source

setup a new rack server

# File lib/rack/handler/ftw.rb, line 72
def initialize(app, config)
  @app = app
  @config = config
  @threads = []
end
run(app, config) click to toggle source

This method is invoked when rack starts this as the server.

# File lib/rack/handler/ftw.rb, line 63
def self.run(app, config)
  #@logger.subscribe(STDOUT)
  server = self.new(app, config)
  server.run
end

Public Instance Methods

run() click to toggle source

Run the server.

Connections are farmed out to threads.

# File lib/rack/handler/ftw.rb, line 81
def run
  # {:environment=>"development", :pid=>nil, :Port=>9292, :Host=>"0.0.0.0",
  #  :AccessLog=>[], :config=>"/home/jls/projects/ruby-ftw/examples/test.ru",
  #  :server=>"FTW"}
  #
  # listen, pass connections off
  #
  #
  # """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."""
  #
  logger.info("Starting server", :config => @config)
  @server = FTW::Server.new([@config[:Host], @config[:Port]].join(":"))
  @server.each_connection do |connection|
    # The rack specification insists that 'rack.input' objects support
    # #rewind. Bleh. Just lie about it and monkeypatch it in.
    # This is required for Sinatra to accept 'post' requests, otherwise
    # it barfs.
    class << connection
      def rewind(*args)
        # lolrack, nothing to do here.
      end
    end

    @threads << Thread.new do
      handle_connection(connection)
    end
  end
end
stop() click to toggle source
# File lib/rack/handler/ftw.rb, line 112
def stop
  @server.stop unless @server.nil?
  @threads.each(&:join)
end

Private Instance Methods

handle_connection(connection) click to toggle source

Handle a new connection.

This method parses http requests and passes them on to handle_request

@param connection The FTW::Connection being handled.

# File lib/rack/handler/ftw.rb, line 122
def handle_connection(connection)
  while true
    begin
      request = read_http_message(connection)
    rescue IOError, 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
    rescue ArgumentError
      # Invalid http request sent
      break
    end

    begin
      handle_request(request, connection)
    rescue => e
      puts e.inspect
      puts e.backtrace
      raise e
    end
  end
ensure
  connection.disconnect("Closing...")
end
handle_request(request, connection) click to toggle source

Handle a request. This will set up the rack 'env' and invoke the application associated with this handler.

# File lib/rack/handler/ftw.rb, line 149
def handle_request(request, connection)
  path, query = request.path.split("?", 2)
  env = {
    # CGI-like environment as required by the Rack SPEC version 1.1
    REQUEST_METHOD => request.method,
    SCRIPT_NAME => "/", # TODO(sissel): not totally sure what this really should be
    PATH_INFO => path,
    QUERY_STRING => query.nil? ? "" : query,
    SERVER_NAME => "hahaha, no", # TODO(sissel): Set this
    SERVER_PORT => "", # TODO(sissel): Set this

    # Rack-specific environment, also required by Rack SPEC version 1.1
    RACK_DOT_VERSION => RACK_VERSION,
    RACK_DOT_URL_SCHEME =>  "http", # TODO(sissel): support https
    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
    #     absence of these variables should correspond with the presence or
    #     absence of the appropriate HTTP header in the request. """
    #
    # It doesn't specify how to translate the header names into this hash syntax.
    # I looked at what Thin does, and it capitalizes and replaces dashes with
    # underscores, so I'll just copy that behavior. The specific code that implements
    # this in thin is here:
    # https://github.com/macournoyer/thin/blob/2e9db13e414ae7425/ext/thin_parser/thin.c#L89-L95
    #
    # The Rack spec also doesn't describe what should be done for headers
    # 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)
  end
  response.body = body

  begin
    connection.write(response.to_s + CRLF)
    write_http_body(body, connection, response["Transfer-Encoding"] == "chunked")
  rescue => e
    @logger.error(e)
    connection.disconnect(e.inspect)
  end
end
logger() click to toggle source

Get the logger.

# File lib/rack/handler/ftw.rb, line 219
def logger
  if @logger.nil?
    @logger = Cabin::Channel.get
  end
  return @logger
end