macournoyer / thin

A very fast & simple Ruby web server

This URL has Read+Write access

Sat Aug 02 06:14:36 -0700 2008
macournoyer (committer)
Sat Aug 02 07:43:14 -0700 2008
commit  2470ab0206716b445c23a1c81eb5f01dff06ad3a
tree    cd7d8ce9a4b975955a3b37f435890d06e4a9c822
parent  cb6e21d6519c8da088f94864dec56c9122f2b753
thin / lib / thin / connection.rb
f1e62946 » macournoyer 2007-12-20 Remove useless condition in... 1 require 'socket'
2
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 3 module Thin
5a0965b3 » macournoyer 2008-02-04 Add more doc. 4 # Connection between the server and client.
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 5 # This class is instanciated by EventMachine on each new connection
6 # that is opened.
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 7 class Connection < EventMachine::Connection
affccc93 » Dan Kubb 2008-07-18 Add Content-Length header t... 8 CONTENT_LENGTH = 'Content-Length'.freeze
9 TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
10 CHUNKED_REGEXP = /\bchunked\b/i.freeze
11
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 12 include Logging
13
0fd4de53 » macournoyer 2008-03-06 Doc love 14 # Rack application (adapter) served by this connection.
ef8bf4cb » macournoyer 2007-12-14 Make work w/ Rack and drop ... 15 attr_accessor :app
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 16
b686394e » macournoyer 2008-02-28 Rename Connector to Backend... 17 # Backend to the server
18 attr_accessor :backend
b6aa2a6e » macournoyer 2008-01-29 Server now let current conn... 19
434ca28b » macournoyer 2008-02-19 INT signal now force stop a... 20 # Current request served by the connection
21 attr_accessor :request
22
0fd4de53 » macournoyer 2008-03-06 Doc love 23 # Next response sent through the connection
434ca28b » macournoyer 2008-02-19 INT signal now force stop a... 24 attr_accessor :response
7c1472b6 » macournoyer 2008-02-12 Rescue errors in Connection... 25
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 26 # Calling the application in a threaded allowing
27 # concurrent processing of requests.
b02c717f » macournoyer 2008-04-08 Define deferred?(env) in yo... 28 attr_writer :threaded
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 29
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 30 # Get the connection ready to process a request.
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 31 def post_init
e10c7d54 » macournoyer 2007-12-17 Move more request processin... 32 @request = Request.new
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 33 @response = Response.new
34 end
35
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 36 # Called when data is received from the client.
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 37 def receive_data(data)
edaaa848 » macournoyer 2008-01-04 * Add -D option to thin scr... 38 trace { data }
b698fe8f » macournoyer 2008-01-20 Convert some tabs to spaces 39 process if @request.parse(data)
4fde8628 » macournoyer 2007-12-10 Make is work w/ mongrel parser 40 rescue InvalidRequest => e
0304be1f » macournoyer 2008-02-17 Warn when descriptors table... 41 log "!! Invalid request"
4fde8628 » macournoyer 2007-12-10 Make is work w/ mongrel parser 42 log_error e
43 close_connection
44 end
45
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 46 # Called when all data was received and the request
0fd4de53 » macournoyer 2008-03-06 Doc love 47 # is ready to be processed.
4fde8628 » macournoyer 2007-12-10 Make is work w/ mongrel parser 48 def process
b02c717f » macournoyer 2008-04-08 Define deferred?(env) in yo... 49 if threaded?
06439cf6 » macournoyer 2008-04-18 Make sure app.deferred?(env... 50 @request.threaded = true
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 51 EventMachine.defer(method(:pre_process), method(:post_process))
52 else
06439cf6 » macournoyer 2008-04-18 Make sure app.deferred?(env... 53 @request.threaded = false
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 54 post_process(pre_process)
55 end
56 end
57
58 def pre_process
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 59 # Add client info to the request env
534a6479 » macournoyer 2008-01-24 A couple fixes and add spec... 60 @request.remote_address = remote_address
b698fe8f » macournoyer 2008-01-20 Convert some tabs to spaces 61
0fd4de53 » macournoyer 2008-03-06 Doc love 62 # Process the request calling the Rack adapter
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 63 @app.call(@request.env)
b3537eef » michaelklishin 2008-07-21 rescue Object => rescue Exc... 64 rescue Exception
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 65 handle_error
66 terminate_request
67 nil # Signal to post_process that the request could not be processed
68 end
69
70 def post_process(result)
71 return unless result
72
affccc93 » Dan Kubb 2008-07-18 Add Content-Length header t... 73 # Set the Content-Length header if possible
74 set_content_length(result) if need_content_length?(result)
75
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 76 @response.status, @response.headers, @response.body = result
278a99ae » michaelklishin 2008-08-02 Log a warning if Rack appli... 77
78 log "!! Rack application returned nil body. Probably you wanted it to be an empty string?" if @response.body.nil?
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 79 # Make the response persistent if requested by the client
e442be1f » macournoyer 2008-01-24 Add persistent connection (... 80 @response.persistent! if @request.persistent?
86f030f6 » macournoyer 2008-01-24 Branching for keep-alive im... 81
f37bcad3 » macournoyer 2007-12-14 Add new files 82 # Send the response
782a71e8 » macournoyer 2008-01-18 Prototyping response body s... 83 @response.each do |chunk|
84 trace { chunk }
85 send_data chunk
86 end
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 87
86f030f6 » macournoyer 2008-01-24 Branching for keep-alive im... 88 # If no more request on that same connection, we close it.
503d6cda » macournoyer 2008-02-07 Create a swiftiply connecto... 89 close_connection_after_writing unless persistent?
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 90
a1257542 » michaelklishin 2008-07-21 rescue Exception, not Object 91 rescue Exception
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 92 handle_error
93 ensure
94 terminate_request
95 end
37153518 » michaelklishin 2008-08-02 Explicitly tell how errors ... 96
97 # Logs catched exception and closes the connection.
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 98 def handle_error
0304be1f » macournoyer 2008-02-17 Warn when descriptors table... 99 log "!! Unexpected error while processing request: #{$!.message}"
dd6b82a8 » macournoyer 2008-02-16 Optimize Connection#remote_... 100 log_error
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 101 close_connection rescue nil
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 102 end
cb6e21d6 » michaelklishin 2008-08-02 Document what request termi... 103
2470ab02 » michaelklishin 2008-08-02 More precise Connection#ter... 104 # Does request and response cleanup (closes open IO streams and
105 # deletes created temporary files).
cb6e21d6 » michaelklishin 2008-08-02 Document what request termi... 106 # Re-initializes response and request if client supports persistent
107 # connection.
4d3709fa » macournoyer 2008-03-28 * Add threaded option to ru... 108 def terminate_request
86f030f6 » macournoyer 2008-01-24 Branching for keep-alive im... 109 @request.close rescue nil
ef8bf4cb » macournoyer 2007-12-14 Make work w/ Rack and drop ... 110 @response.close rescue nil
86f030f6 » macournoyer 2008-01-24 Branching for keep-alive im... 111
e442be1f » macournoyer 2008-01-24 Add persistent connection (... 112 # Prepare the connection for another request if the client
113 # supports HTTP pipelining (persistent connection).
503d6cda » macournoyer 2008-02-07 Create a swiftiply connecto... 114 post_init if persistent?
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 115 end
a68f46d6 » macournoyer 2008-01-23 Add support for connection ... 116
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 117 # Called when the connection is unbinded from the socket
118 # and can no longer be used to process requests.
b6aa2a6e » macournoyer 2008-01-29 Server now let current conn... 119 def unbind
b686394e » macournoyer 2008-02-28 Rename Connector to Backend... 120 @backend.connection_finished(self)
b6aa2a6e » macournoyer 2008-01-29 Server now let current conn... 121 end
122
2b7d72da » macournoyer 2008-02-20 * Add --max-persistent-conn... 123 # Allows this connection to be persistent.
124 def can_persist!
125 @can_persist = true
126 end
127
128 # Return +true+ if this connection is allowed to stay open and be persistent.
129 def can_persist?
130 @can_persist
131 end
132
8d3f1db0 » macournoyer 2008-02-10 Add more doc to Connection 133 # Return +true+ if the connection must be left open
134 # and ready to be reused for another request.
503d6cda » macournoyer 2008-02-07 Create a swiftiply connecto... 135 def persistent?
2b7d72da » macournoyer 2008-02-20 * Add --max-persistent-conn... 136 @can_persist && @response.persistent?
b02c717f » macournoyer 2008-04-08 Define deferred?(env) in yo... 137 end
138
139 # +true+ if <tt>app.call</tt> will be called inside a thread.
140 # You can set all requests as threaded setting <tt>Connection#threaded=true</tt>
141 # or on a per-request case returning +true+ in <tt>app.deferred?</tt>.
142 def threaded?
143 @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env))
144 end
b6aa2a6e » macournoyer 2008-01-29 Server now let current conn... 145
dd6b82a8 » macournoyer 2008-02-16 Optimize Connection#remote_... 146 # IP Address of the remote client.
7c1472b6 » macournoyer 2008-02-12 Rescue errors in Connection... 147 def remote_address
dd6b82a8 » macournoyer 2008-02-16 Optimize Connection#remote_... 148 @request.forwarded_for || socket_address
044d58ed » michaelklishin 2008-07-21 Again, rescue Exception, no... 149 rescue Exception
dd6b82a8 » macournoyer 2008-02-16 Optimize Connection#remote_... 150 log_error
7c1472b6 » macournoyer 2008-02-12 Rescue errors in Connection... 151 nil
152 end
4d24f67d » macournoyer 2008-02-13 Refactor remote_address log... 153
154 protected
553202ab » michaelklishin 2008-07-21 Tiny doc addition to Thin::... 155
156 # Returns IP address of peer as a string.
4d24f67d » macournoyer 2008-02-13 Refactor remote_address log... 157 def socket_address
158 Socket.unpack_sockaddr_in(get_peername)[1]
159 end
affccc93 » Dan Kubb 2008-07-18 Add Content-Length header t... 160
161 private
162 def need_content_length?(result)
163 status, headers, body = result
164 return false if headers.has_key?(CONTENT_LENGTH)
165 return false if (100..199).include?(status) || status == 204 || status == 304
166 return false if headers.has_key?(TRANSFER_ENCODING) && headers[TRANSFER_ENCODING] =~ CHUNKED_REGEXP
167 return false unless body.kind_of?(String) || body.kind_of?(Array)
168 true
169 end
170
171 def set_content_length(result)
172 headers, body = result[1..2]
173 case body
174 when String
c9634028 » macournoyer 2008-07-19 Add changelog entry for las... 175 # See http://redmine.ruby-lang.org/issues/show/203
affccc93 » Dan Kubb 2008-07-18 Add Content-Length header t... 176 headers[CONTENT_LENGTH] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
177 when Array
178 bytes = 0
179 body.each do |p|
180 bytes += p.respond_to?(:bytesize) ? p.bytesize : p.size
181 end
182 headers[CONTENT_LENGTH] = bytes.to_s
183 end
184 end
f641f3f4 » macournoyer 2007-12-05 Forgot to add files 185 end
a1257542 » michaelklishin 2008-07-21 rescue Exception, not Object 186 end