Connection between the server and client. This class is instanciated by EventMachine on each new connection that is opened.
This is a template async response. N.B. Can't use string for body on 1.9
Rack application (adapter) served by this connection.
Backend to the server
Current request served by the connection
Next response sent through the connection
Calling the application in a threaded allowing concurrent processing of requests.
Allows this connection to be persistent.
# File lib/thin/connection.rb, line 172 def can_persist! @can_persist = true end
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
# 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
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
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
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
# 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
# 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
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(method(:pre_process), method(:post_process)) else @request.threaded = false post_process(pre_process) end end
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
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
# 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
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
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
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
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
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