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

CONTENT_LENGTH =
'Content-Length'.freeze
TRANSFER_ENCODING =
'Transfer-Encoding'.freeze
CHUNKED_REGEXP =
/\bchunked\b/i.freeze
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, debug?, #log, log, #log_error, log_error, #silent, #silent=, silent?, #trace, trace, trace?

Instance Attribute Details

#appObject

Rack application (adapter) served by this connection.



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

def app
  @app
end

#backendObject

Backend to the server



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

def backend
  @backend
end

#requestObject

Current request served by the connection



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

def request
  @request
end

#responseObject

Next response sent through the connection



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

def response
  @response
end

#threaded=(value) ⇒ Object (writeonly)

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



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

def threaded=(value)
  @threaded = value
end

Instance Method Details

#can_persist!Object

Allows this connection to be persistent.



167
168
169
# File 'lib/thin/connection.rb', line 167

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)


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

def can_persist?
  @can_persist
end

#close_request_responseObject



136
137
138
139
140
# File 'lib/thin/connection.rb', line 136

def close_request_response
  @request.async_close.succeed if @request.async_close
  @request.close  rescue nil
  @response.close rescue nil
end

#handle_errorObject

Logs catched exception and closes the connection.



130
131
132
133
134
# File 'lib/thin/connection.rb', line 130

def handle_error
  log "!! Unexpected error while processing request: #{$!.message}"
  log_error
  close_connection rescue nil
end

#persistent?Boolean

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

Returns:

  • (Boolean)


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

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

#post_initObject

Get the connection ready to process a request.



34
35
36
37
# File 'lib/thin/connection.rb', line 34

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

#post_process(result) ⇒ Object



93
94
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
# File 'lib/thin/connection.rb', line 93

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

  # Set the Content-Length header if possible
  set_content_length(result) if need_content_length?(result)
  
  @response.status, @response.headers, @response.body = *result

  log "!! Rack application returned nil body. Probably you wanted it to be an empty string?" if @response.body.nil?

  # 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
  handle_error
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



61
62
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
# File 'lib/thin/connection.rb', line 61

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
  handle_error
  terminate_request
  nil # Signal to post_process that the request could not be processed
end

#processObject

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



51
52
53
54
55
56
57
58
59
# File 'lib/thin/connection.rb', line 51

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

#receive_data(data) ⇒ Object

Called when data is received from the client.



40
41
42
43
44
45
46
47
# File 'lib/thin/connection.rb', line 40

def receive_data(data)
  trace { data }
  process if @request.parse(data)
rescue InvalidRequest => e
  log "!! Invalid request"
  log_error e
  close_connection
end

#remote_addressObject

IP Address of the remote client.



190
191
192
193
194
195
# File 'lib/thin/connection.rb', line 190

def remote_address
  socket_address
rescue Exception
  log_error
  nil
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.



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/thin/connection.rb', line 146

def terminate_request
  unless persistent?
    close_connection_after_writing rescue nil
    close_request_response
  else
    close_request_response
    # 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)


185
186
187
# File 'lib/thin/connection.rb', line 185

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.



160
161
162
163
164
# File 'lib/thin/connection.rb', line 160

def unbind
  @request.async_close.succeed if @request.async_close
  @response.body.fail if @response.body.respond_to?(:fail)
  @backend.connection_finished(self)
end