class Thin::Connection

Connection between the server and client. This class is instanciated by EventMachine on each new connection that is opened.

Constants

AsyncResponse

This is a template async response. N.B. Can't use string for body on 1.9

Attributes

app[RW]

Rack application (adapter) served by this connection.

backend[RW]

Backend to the server

request[RW]

Current request served by the connection

response[RW]

Next response sent through the connection

threaded[W]

Calling the application in a threaded allowing concurrent processing of requests.

Public Instance Methods

can_persist!() click to toggle source

Allows this connection to be persistent.

# File lib/thin/connection.rb, line 172
def can_persist!
  @can_persist = true
end
can_persist?() click to toggle source

Return true if this connection is allowed to stay open and be persistent.

# File lib/thin/connection.rb, line 177
def can_persist?
  @can_persist
end
close_request_response() click to toggle source
# File lib/thin/connection.rb, line 139
def close_request_response
  @request.async_close.succeed if @request.async_close
  @request.close  rescue nil
  @response.close rescue nil
end
idle?() click to toggle source

Return true if the connection is open but is not processing any user requests

# File lib/thin/connection.rb, line 189
def idle?
  @idle
end
persistent?() click to toggle source

Return true if the connection must be left open and ready to be reused for another request.

# File lib/thin/connection.rb, line 183
def persistent?
  @can_persist && @response.persistent?
end
post_init() click to toggle source

Get the connection ready to process a request.

# File lib/thin/connection.rb, line 30
def post_init
  @request  = Request.new
  @response = Response.new
end
post_process(result) click to toggle source
# File lib/thin/connection.rb, line 95
def post_process(result)
  return unless result
  result = result.to_a

  # Status code -1 indicates that we're going to respond later (async).
  return if result.first == AsyncResponse.first

  @response.status, @response.headers, @response.body = *result

  log_error("Rack application returned nil body. " \
            "Probably you wanted it to be an empty string?") if @response.body.nil?

  # HEAD requests should not return a body.
  @response.skip_body! if @request.head?

  # Make the response persistent if requested by the client
  @response.persistent! if @request.persistent?

  # Send the response
  @response.each do |chunk|
    trace chunk
    send_data chunk
  end

rescue Exception => e
  unexpected_error(e)
  # Close connection since we can't handle response gracefully
  close_connection
ensure
  # If the body is being deferred, then terminate afterward.
  if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback)
    @response.body.callback { terminate_request }
    @response.body.errback  { terminate_request }
  else
    # Don't terminate the response if we're going async.
    terminate_request unless result && result.first == AsyncResponse.first
  end
end
pre_process() click to toggle source
# File lib/thin/connection.rb, line 63
def pre_process
  # Add client info to the request env
  @request.remote_address = remote_address

  # Connection may be closed unless the App#call response was a [-1, ...]
  # It should be noted that connection objects will linger until this
  # callback is no longer referenced, so be tidy!
  @request.async_callback = method(:post_process)

  if @backend.ssl?
    @request.env["rack.url_scheme"] = "https"

    if cert = get_peer_cert
      @request.env['rack.peer_cert'] = cert
    end
  end

  # When we're under a non-async framework like rails, we can still spawn
  # off async responses using the callback info, so there's little point
  # in removing this.
  response = AsyncResponse
  catch(:async) do
    # Process the request calling the Rack adapter
    response = @app.call(@request.env)
  end
  response
rescue Exception => e
  unexpected_error(e)
  # Pass through error response
  can_persist? && @request.persistent? ? Response::PERSISTENT_ERROR : Response::ERROR
end
process() click to toggle source

Called when all data was received and the request is ready to be processed.

# File lib/thin/connection.rb, line 47
def process
  if threaded?
    @request.threaded = true
    EventMachine.defer { post_process(pre_process) }
  else
    @request.threaded = false
    post_process(pre_process)
  end
end
receive_data(data) click to toggle source

Called when data is received from the client.

# File lib/thin/connection.rb, line 36
def receive_data(data)
  @idle = false
  trace data
  process if @request.parse(data)
rescue InvalidRequest => e
  log_error("Invalid request", e)
  post_process Response::BAD_REQUEST
end
remote_address() click to toggle source

IP Address of the remote client.

# File lib/thin/connection.rb, line 201
def remote_address
  socket_address
rescue Exception => e
  log_error('Could not infer remote address', e)
  nil
end
ssl_verify_peer(cert) click to toggle source
# File lib/thin/connection.rb, line 57
def ssl_verify_peer(cert)
  # In order to make the cert available later we have to have made at least
  # a show of verifying it.
  true
end
terminate_request() click to toggle source

Does request and response cleanup (closes open IO streams and deletes created temporary files). Re-initializes response and request if client supports persistent connection.

# File lib/thin/connection.rb, line 149
def terminate_request
  unless persistent?
    close_connection_after_writing rescue nil
    close_request_response
  else
    close_request_response
    # Connection become idle but it's still open
    @idle = true
    # Prepare the connection for another request if the client
    # supports HTTP pipelining (persistent connection).
    post_init
  end
end
threaded?() click to toggle source

true if app.call will be called inside a thread. You can set all requests as threaded setting Connection#threaded=true or on a per-request case returning true in app.deferred?.

# File lib/thin/connection.rb, line 196
def threaded?
  @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env))
end
unbind() click to toggle source

Called when the connection is unbinded from the socket and can no longer be used to process requests.

# File lib/thin/connection.rb, line 165
def unbind
  @request.async_close.succeed if @request.async_close
  @response.body.fail if @response.body.respond_to?(:fail)
  @backend.connection_finished(self)
end
unexpected_error(e) click to toggle source

Logs information about an unexpected exceptional condition

# File lib/thin/connection.rb, line 135
def unexpected_error(e)
  log_error("Unexpected error while processing request", e)
end

Protected Instance Methods

socket_address() click to toggle source

Returns IP address of peer as a string.

# File lib/thin/connection.rb, line 210
def socket_address
  peer = get_peername
  Socket.unpack_sockaddr_in(peer)[1] if peer
end