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.



159
160
161
# File 'lib/thin/connection.rb', line 159

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)


164
165
166
# File 'lib/thin/connection.rb', line 164

def can_persist?
  @can_persist
end

#close_request_responseObject



128
129
130
131
132
# File 'lib/thin/connection.rb', line 128

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

#handle_errorObject

Logs catched exception and closes the connection.



122
123
124
125
126
# File 'lib/thin/connection.rb', line 122

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)


170
171
172
# File 'lib/thin/connection.rb', line 170

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



85
86
87
88
89
90
91
92
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
# File 'lib/thin/connection.rb', line 85

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
# 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)
  
  # 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.



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

def remote_address
  @request.forwarded_for || 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.



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/thin/connection.rb', line 138

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)


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

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.



152
153
154
155
156
# File 'lib/thin/connection.rb', line 152

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