Class: Thin::ReactorConnection

Inherits:
Object
  • 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.

Constant Summary collapse

CONTENT_LENGTH =
'Content-Length'.freeze
TRANSFER_ENCODING =
'Transfer-Encoding'.freeze
CHUNKED_REGEXP =
/\bchunked\b/i.freeze
BUFFER_SIZE =

whenever we accumulate this buffer size we flush to the socket when flush is called the buffer is emptied (and the conn is blocked)

256*1024

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?

Constructor Details

#initialize(socket, reactor) ⇒ ReactorConnection

Returns a new instance of ReactorConnection.



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

def initialize(socket, reactor)
  @socket = socket
  @reactor = reactor
  @data = ""
  @sends = 0
  @t = Time.now
  @request  = Request.new
  @response = Response.new
end

Instance Attribute Details

#appObject

Rack application (adapter) served by this connection.



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

def app
  @app
end

#backendObject

Backend to the server



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

def backend
  @backend
end

#fibered=(value) ⇒ Object (writeonly)

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



37
38
39
# File 'lib/thin/connection.rb', line 37

def fibered=(value)
  @fibered = value
end

#reactor=(value) ⇒ Object (writeonly)

Sets the attribute reactor

Parameters:

  • value

    the value to set the attribute reactor to.



38
39
40
# File 'lib/thin/connection.rb', line 38

def reactor=(value)
  @reactor = value
end

#requestObject

Current request served by the connection



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

def request
  @request
end

#responseObject

Next response sent through the connection



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

def response
  @response
end

#socket=(value) ⇒ Object (writeonly)

Sets the attribute socket

Parameters:

  • value

    the value to set the attribute socket to.



39
40
41
# File 'lib/thin/connection.rb', line 39

def socket=(value)
  @socket = value
end

#threaded=(value) ⇒ Object (writeonly)

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



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

def threaded=(value)
  @threaded = value
end

Instance Method Details

#can_persist!Object

Allows this connection to be persistent.



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

def can_persist!
end

#can_persist?Boolean

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

Returns:

  • (Boolean)


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

def can_persist?
  false
end

#close_connection(after_writing = false) ⇒ Object



64
65
66
67
68
69
70
71
72
# File 'lib/thin/connection.rb', line 64

def close_connection(after_writing=false)
  unless after_writing
    @reactor.detach(:read, @socket)
  end
  if after_writing        
    @reactor.detach(:read, @socket) unless @reactor.attached?(:read, @socket)
    @socket.close unless @socket.closed?
  end
end

#close_connection_after_writingObject



74
75
76
# File 'lib/thin/connection.rb', line 74

def close_connection_after_writing
  close_connection(true)
end

#close_request_responseObject



144
145
146
147
# File 'lib/thin/connection.rb', line 144

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

#flush_dataObject



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/thin/connection.rb', line 83

def flush_data
  return if @data.blank?
  begin
    @socket.syswrite(@data)
  rescue Exception => e
    puts e
    close_connection
    return 
  end
  @data = ''      
end

#handle_errorObject

Logs catched exception and closes the connection.



138
139
140
141
142
# File 'lib/thin/connection.rb', line 138

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)


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

def persistent?
  false
end

#post_initObject



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

def post_init
  @request  = Request.new
  @response = Response.new
  @reactor.attach(:read, @socket) do |socket, reactor|
    begin          
      receive_data(@socket.read_nonblock(8*1024))
    rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
    rescue Exception => e
      close_connection
    end
  end
end

#post_process(result) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/thin/connection.rb', line 116

def post_process(result)
  begin
    return unless result
    result = result.to_a
    # 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?
    # Send the response
    @response.each do |chunk|
      trace { chunk }
      send_data chunk
    end
    flush_data
  rescue Exception
    handle_error
  ensure
    terminate_request
  end
end

#pre_processObject

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



108
109
110
111
112
113
114
# File 'lib/thin/connection.rb', line 108

def pre_process
  @app.call(@request.env)
rescue Exception
  handle_error
  terminate_request
  nil # Signal to post_process that the request could not be processed
end

#receive_data(data) ⇒ Object

Called when data is received from the client.



96
97
98
99
100
101
102
103
# File 'lib/thin/connection.rb', line 96

def receive_data(data)  
  trace { data }
  post_process(pre_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.



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

def remote_address
  @request.forwarded_for || socket_address
rescue Exception
  log_error
  nil
end

#send_data(data) ⇒ Object



78
79
80
81
# File 'lib/thin/connection.rb', line 78

def send_data(data)
  @data << data 
  flush_data if @data.length >= BUFFER_SIZE
end

#terminate_requestObject



149
150
151
152
# File 'lib/thin/connection.rb', line 149

def terminate_request
  close_connection_after_writing rescue nil
  close_request_response
end

#threaded?Boolean

Returns:

  • (Boolean)


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

def threaded?
  false
end