Class: Thin::Connection

Inherits:
EventMachine::Connection
  • Object
show all
Includes:
Logging
Defined in:
lib/thin/connection.rb

Overview

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

Direct Known Subclasses

SwiftiplyConnection, UnixConnection

Constant Summary collapse

AsyncResponse =

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

[-1, {}, []].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

debug=, debug?, level, level=, #log, log_debug, #log_debug, log_error, #log_error, #log_info, log_info, log_msg, #silent, #silent=, silent=, silent?, #trace, trace, trace=, trace?, trace_msg

Instance Attribute Details

#appObject

Rack application (adapter) served by this connection.



14
15
16
# File 'lib/thin/connection.rb', line 14

def app
  @app
end

#backendObject

Backend to the server



17
18
19
# File 'lib/thin/connection.rb', line 17

def backend
  @backend
end

#requestObject

Current request served by the connection



20
21
22
# File 'lib/thin/connection.rb', line 20

def request
  @request
end

#responseObject

Next response sent through the connection



23
24
25
# File 'lib/thin/connection.rb', line 23

def response
  @response
end

#threaded=(value) ⇒ Object (writeonly)

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



27
28
29
# File 'lib/thin/connection.rb', line 27

def threaded=(value)
  @threaded = value
end

Instance Method Details

#can_persist!Object

Allows this connection to be persistent.



172
173
174
# File 'lib/thin/connection.rb', line 172

def can_persist!
  @can_persist = true
end

#can_persist?Boolean

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

Returns:

  • (Boolean)


177
178
179
# File 'lib/thin/connection.rb', line 177

def can_persist?
  @can_persist
end

#close_request_responseObject



139
140
141
142
143
# 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?Boolean

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

Returns:

  • (Boolean)


189
190
191
# File 'lib/thin/connection.rb', line 189

def idle?
  @idle
end

#persistent?Boolean

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

Returns:

  • (Boolean)


183
184
185
# File 'lib/thin/connection.rb', line 183

def persistent?
  @can_persist && @response.persistent?
end

#post_initObject

Get the connection ready to process a request.



30
31
32
33
# File 'lib/thin/connection.rb', line 30

def post_init
  @request  = Request.new
  @response = Response.new
end

#post_process(result) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 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_processObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 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

#processObject

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



47
48
49
50
51
52
53
54
55
# 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) ⇒ Object

Called when data is received from the client.



36
37
38
39
40
41
42
43
# 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_addressObject

IP Address of the remote client.



201
202
203
204
205
206
# 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) ⇒ Object



57
58
59
60
61
# 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_requestObject

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



149
150
151
152
153
154
155
156
157
158
159
160
161
# 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?Boolean

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?.

Returns:

  • (Boolean)


196
197
198
# File 'lib/thin/connection.rb', line 196

def threaded?
  @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env))
end

#unbindObject

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



165
166
167
168
169
# 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) ⇒ Object

Logs information about an unexpected exceptional condition



135
136
137
# File 'lib/thin/connection.rb', line 135

def unexpected_error(e)
  log_error("Unexpected error while processing request", e)
end