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, debug?, #log, log, #log_error, log_error, #silent, #silent=, silent?, #trace, trace, trace?

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.



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

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)


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

def can_persist?
  @can_persist
end

#close_request_responseObject



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

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.



125
126
127
128
129
130
131
# File 'lib/thin/connection.rb', line 125

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

  send_data "HTTP/1.0 400 Bad Request\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nBad Request!\r\n"
  close_connection_after_writing
end

#persistent?Boolean

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

Returns:

  • (Boolean)


175
176
177
# File 'lib/thin/connection.rb', line 175

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



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
120
121
122
# File 'lib/thin/connection.rb', line 91

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 "!! 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



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

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.



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

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.



36
37
38
39
40
41
42
43
44
45
# File 'lib/thin/connection.rb', line 36

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

  send_data "HTTP/1.0 400 Bad Request\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nBad Request!\r\n"
  close_connection_after_writing
end

#remote_addressObject

IP Address of the remote client.



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

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.



143
144
145
146
147
148
149
150
151
152
153
# File 'lib/thin/connection.rb', line 143

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)


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

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.



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

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