unicorn 0.2.3 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.document +1 -1
  2. data/.gitignore +1 -0
  3. data/CHANGELOG +1 -0
  4. data/DESIGN +4 -0
  5. data/GNUmakefile +30 -6
  6. data/Manifest +62 -3
  7. data/README +52 -42
  8. data/SIGNALS +17 -17
  9. data/TODO +27 -5
  10. data/bin/unicorn +15 -13
  11. data/bin/unicorn_rails +59 -22
  12. data/ext/unicorn/http11/http11.c +25 -104
  13. data/ext/unicorn/http11/http11_parser.c +24 -23
  14. data/ext/unicorn/http11/http11_parser.h +1 -3
  15. data/ext/unicorn/http11/http11_parser.rl +2 -1
  16. data/lib/unicorn.rb +58 -44
  17. data/lib/unicorn/app/old_rails.rb +23 -0
  18. data/lib/unicorn/app/old_rails/static.rb +58 -0
  19. data/lib/unicorn/cgi_wrapper.rb +151 -0
  20. data/lib/unicorn/configurator.rb +71 -31
  21. data/lib/unicorn/const.rb +9 -34
  22. data/lib/unicorn/http_request.rb +63 -66
  23. data/lib/unicorn/http_response.rb +6 -1
  24. data/lib/unicorn/socket.rb +15 -2
  25. data/test/benchmark/README +55 -0
  26. data/test/benchmark/big_request.rb +35 -0
  27. data/test/benchmark/dd.ru +18 -0
  28. data/test/benchmark/request.rb +47 -0
  29. data/test/benchmark/response.rb +29 -0
  30. data/test/exec/test_exec.rb +41 -157
  31. data/test/rails/app-1.2.3/.gitignore +2 -0
  32. data/test/rails/app-1.2.3/app/controllers/application.rb +4 -0
  33. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +34 -0
  34. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
  35. data/test/rails/app-1.2.3/config/boot.rb +9 -0
  36. data/test/rails/app-1.2.3/config/database.yml +12 -0
  37. data/test/rails/app-1.2.3/config/environment.rb +10 -0
  38. data/test/rails/app-1.2.3/config/environments/development.rb +7 -0
  39. data/test/rails/app-1.2.3/config/environments/production.rb +3 -0
  40. data/test/rails/app-1.2.3/config/routes.rb +4 -0
  41. data/test/rails/app-1.2.3/db/.gitignore +0 -0
  42. data/test/rails/app-1.2.3/public/404.html +1 -0
  43. data/test/rails/app-1.2.3/public/500.html +1 -0
  44. data/test/rails/app-2.0.2/.gitignore +2 -0
  45. data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
  46. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +34 -0
  47. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
  48. data/test/rails/app-2.0.2/config/boot.rb +9 -0
  49. data/test/rails/app-2.0.2/config/database.yml +12 -0
  50. data/test/rails/app-2.0.2/config/environment.rb +14 -0
  51. data/test/rails/app-2.0.2/config/environments/development.rb +6 -0
  52. data/test/rails/app-2.0.2/config/environments/production.rb +3 -0
  53. data/test/rails/app-2.0.2/config/routes.rb +4 -0
  54. data/test/rails/app-2.0.2/db/.gitignore +0 -0
  55. data/test/rails/app-2.0.2/public/404.html +1 -0
  56. data/test/rails/app-2.0.2/public/500.html +1 -0
  57. data/test/rails/app-2.2.2/.gitignore +2 -0
  58. data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
  59. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +34 -0
  60. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
  61. data/test/rails/app-2.2.2/config/boot.rb +109 -0
  62. data/test/rails/app-2.2.2/config/database.yml +12 -0
  63. data/test/rails/app-2.2.2/config/environment.rb +14 -0
  64. data/test/rails/app-2.2.2/config/environments/development.rb +5 -0
  65. data/test/rails/app-2.2.2/config/environments/production.rb +3 -0
  66. data/test/rails/app-2.2.2/config/routes.rb +4 -0
  67. data/test/rails/app-2.2.2/db/.gitignore +0 -0
  68. data/test/rails/app-2.2.2/public/404.html +1 -0
  69. data/test/rails/app-2.2.2/public/500.html +1 -0
  70. data/test/rails/app-2.3.2.1/.gitignore +2 -0
  71. data/test/rails/app-2.3.2.1/app/controllers/application_controller.rb +3 -0
  72. data/test/rails/app-2.3.2.1/app/controllers/foo_controller.rb +34 -0
  73. data/test/rails/app-2.3.2.1/app/helpers/application_helper.rb +2 -0
  74. data/test/rails/app-2.3.2.1/config/boot.rb +107 -0
  75. data/test/rails/app-2.3.2.1/config/database.yml +12 -0
  76. data/test/rails/app-2.3.2.1/config/environment.rb +14 -0
  77. data/test/rails/app-2.3.2.1/config/environments/development.rb +5 -0
  78. data/test/rails/app-2.3.2.1/config/environments/production.rb +4 -0
  79. data/test/rails/app-2.3.2.1/config/routes.rb +4 -0
  80. data/test/rails/app-2.3.2.1/db/.gitignore +0 -0
  81. data/test/rails/app-2.3.2.1/public/404.html +1 -0
  82. data/test/rails/app-2.3.2.1/public/500.html +1 -0
  83. data/test/rails/test_rails.rb +243 -0
  84. data/test/test_helper.rb +149 -2
  85. data/test/unit/test_configurator.rb +46 -0
  86. data/test/unit/test_http_parser.rb +77 -36
  87. data/test/unit/test_request.rb +2 -0
  88. data/test/unit/test_response.rb +20 -4
  89. data/test/unit/test_server.rb +30 -1
  90. data/test/unit/test_socket_helper.rb +159 -0
  91. data/unicorn.gemspec +5 -5
  92. metadata +68 -5
  93. data/test/benchmark/previous.rb +0 -11
  94. data/test/benchmark/simple.rb +0 -11
  95. data/test/benchmark/utils.rb +0 -82
@@ -8,22 +8,21 @@ module Unicorn
8
8
  #
9
9
  # Example (when used with the unicorn config file):
10
10
  # worker_processes 4
11
- # listeners %w(0.0.0.0:9292 /tmp/my_app.sock)
11
+ # listen '/tmp/my_app.sock', :backlog => 1
12
+ # listen '0.0.0.0:9292'
12
13
  # timeout 10
13
14
  # pid "/tmp/my_app.pid"
14
15
  # after_fork do |server,worker_nr|
15
16
  # server.listen("127.0.0.1:#{9293 + worker_nr}") rescue nil
16
17
  # end
17
18
  class Configurator
18
- include ::Unicorn::SocketHelper
19
-
20
19
  # The default logger writes its output to $stderr
21
20
  DEFAULT_LOGGER = Logger.new($stderr) unless defined?(DEFAULT_LOGGER)
22
21
 
23
22
  # Default settings for Unicorn
24
23
  DEFAULTS = {
25
24
  :timeout => 60,
26
- :listeners => [ Const::DEFAULT_LISTEN ],
25
+ :listeners => [],
27
26
  :logger => DEFAULT_LOGGER,
28
27
  :worker_processes => 1,
29
28
  :after_fork => lambda { |server, worker_nr|
@@ -43,7 +42,6 @@ module Unicorn
43
42
  server.logger.info("forked child re-executing...")
44
43
  },
45
44
  :pid => nil,
46
- :backlog => 1024,
47
45
  :preload_app => false,
48
46
  :stderr_path => nil,
49
47
  :stdout_path => nil,
@@ -83,23 +81,6 @@ module Unicorn
83
81
  @set[key]
84
82
  end
85
83
 
86
- # Changes the listen() syscall backlog to +nr+ for yet-to-be-created
87
- # sockets. Due to limitations of the OS, this cannot affect
88
- # existing listener sockets in any way, sockets must be completely
89
- # closed and rebound (inherited sockets preserve their existing
90
- # backlog setting). Some operating systems allow negative values
91
- # here to specify the maximum allowable value. See the listen(2)
92
- # syscall documentation of your OS for the exact semantics of this.
93
- #
94
- # If you are running unicorn on multiple machines, lowering this number
95
- # can help your load balancer detect when a machine is overloaded
96
- # and give requests to a different machine.
97
- def backlog(nr)
98
- Integer === nr or raise ArgumentError,
99
- "not an integer: backlog=#{nr.inspect}"
100
- @set[:backlog] = nr
101
- end
102
-
103
84
  # sets object to the +new+ Logger-like object. The new logger-like
104
85
  # object must respond to the following methods:
105
86
  # +debug+, +info+, +warn+, +error+, +fatal+, +close+
@@ -171,16 +152,65 @@ module Unicorn
171
152
  # sets listeners to the given +addresses+, replacing or augmenting the
172
153
  # current set. This is for the global listener pool shared by all
173
154
  # worker processes. For per-worker listeners, see the after_fork example
174
- def listeners(addresses)
155
+ # This is for internal API use only, do not use it in your Unicorn
156
+ # config file. Use listen instead.
157
+ def listeners(addresses) # :nodoc:
175
158
  Array === addresses or addresses = Array(addresses)
176
159
  addresses.map! { |addr| expand_addr(addr) }
177
160
  @set[:listeners] = addresses
178
161
  end
179
162
 
180
- # adds an +address+ to the existing listener set
181
- def listen(address)
163
+ # adds an +address+ to the existing listener set.
164
+ #
165
+ # The following options may be specified (but are generally not needed):
166
+ #
167
+ # +backlog+: this is the backlog of the listen() syscall.
168
+ #
169
+ # Some operating systems allow negative values here to specify the
170
+ # maximum allowable value. In most cases, this number is only
171
+ # recommendation and there are other OS-specific tunables and
172
+ # variables that can affect this number. See the listen(2)
173
+ # syscall documentation of your OS for the exact semantics of
174
+ # this.
175
+ #
176
+ # If you are running unicorn on multiple machines, lowering this number
177
+ # can help your load balancer detect when a machine is overloaded
178
+ # and give requests to a different machine.
179
+ #
180
+ # Default: 1024
181
+ #
182
+ # +rcvbuf+, +sndbuf+: maximum send and receive buffer sizes of sockets
183
+ #
184
+ # These correspond to the SO_RCVBUF and SO_SNDBUF settings which
185
+ # can be set via the setsockopt(2) syscall. Some kernels
186
+ # (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
187
+ # there is no need (and it is sometimes detrimental) to specify them.
188
+ #
189
+ # See the socket API documentation of your operating system
190
+ # to determine the exact semantics of these settings and
191
+ # other operating system-specific knobs where they can be
192
+ # specified.
193
+ #
194
+ # Defaults: operating system defaults
195
+ #
196
+ # Due to limitations of the operating system, options specified here
197
+ # cannot affect existing listener sockets in any way, sockets must be
198
+ # completely closed and rebound.
199
+ def listen(address, opt = { :backlog => 1024 })
200
+ address = expand_addr(address)
201
+ if String === address
202
+ Hash === @set[:listener_opts] or
203
+ @set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
204
+ [ :backlog, :sndbuf, :rcvbuf ].each do |key|
205
+ value = opt[key] or next
206
+ Integer === value or
207
+ raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
208
+ end
209
+ @set[:listener_opts][address].merge!(opt)
210
+ end
211
+
182
212
  @set[:listeners] = [] unless Array === @set[:listeners]
183
- @set[:listeners] << expand_addr(address)
213
+ @set[:listeners] << address
184
214
  end
185
215
 
186
216
  # sets the +path+ for the PID file of the unicorn master process
@@ -254,16 +284,26 @@ module Unicorn
254
284
  @set[var] = my_proc
255
285
  end
256
286
 
287
+ # expands "unix:path/to/foo" to a socket relative to the current path
257
288
  # expands pathnames of sockets if relative to "~" or "~username"
258
289
  # expands "*:port and ":port" to "0.0.0.0:port"
259
290
  def expand_addr(address) #:nodoc
260
291
  return address unless String === address
261
- if address[0..0] == '~'
262
- return File.expand_path(address)
263
- elsif address =~ %r{\A\*?:(\d+)\z}
264
- return "0.0.0.0:#$1"
292
+
293
+ case address
294
+ when %r{\Aunix:(.*)\z}
295
+ File.expand_path($1)
296
+ when %r{\A~}
297
+ File.expand_path(address)
298
+ when %r{\A\*:(\d+)\z}
299
+ "0.0.0.0:#$1"
300
+ when %r{\A(.*):(\d+)\z}
301
+ # canonicalize the name
302
+ packed = Socket.pack_sockaddr_in($2.to_i, $1)
303
+ Socket.unpack_sockaddr_in(packed).reverse!.join(':')
304
+ else
305
+ address
265
306
  end
266
- address
267
307
  end
268
308
 
269
309
  end
@@ -48,27 +48,17 @@ module Unicorn
48
48
  # the constant just refers to a string with the same contents. Using these constants
49
49
  # gave about a 3% to 10% performance improvement over using the strings directly.
50
50
  # Symbols did not really improve things much compared to constants.
51
- #
52
- # While Unicorn does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
53
- # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
54
- # too taxing on performance.
55
51
  module Const
56
52
  DATE="Date".freeze
57
53
 
58
54
  # This is the part of the path after the SCRIPT_NAME.
59
55
  PATH_INFO="PATH_INFO".freeze
60
56
 
61
- # Request body
62
- HTTP_BODY="HTTP_BODY".freeze
63
-
64
- # This is the initial part that your handler is identified as by URIClassifier.
65
- SCRIPT_NAME="SCRIPT_NAME".freeze
66
-
67
- # The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
57
+ # The original URI requested by the client.
68
58
  REQUEST_URI='REQUEST_URI'.freeze
69
59
  REQUEST_PATH='REQUEST_PATH'.freeze
70
60
 
71
- UNICORN_VERSION="0.2.3".freeze
61
+ UNICORN_VERSION="0.4.1".freeze
72
62
 
73
63
  UNICORN_TMP_BASE="unicorn".freeze
74
64
 
@@ -76,14 +66,6 @@ module Unicorn
76
66
  DEFAULT_PORT = "8080".freeze # default TCP listen port
77
67
  DEFAULT_LISTEN = "#{DEFAULT_HOST}:#{DEFAULT_PORT}".freeze
78
68
 
79
- # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
80
- ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Unicorn #{UNICORN_VERSION}\r\n\r\nNOT FOUND".freeze
81
-
82
- CONTENT_LENGTH="CONTENT_LENGTH".freeze
83
-
84
- # A common header for indicating the server is too busy. Not used yet.
85
- ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
86
-
87
69
  # The basic max request size we'll try to read.
88
70
  CHUNK_SIZE=(16 * 1024)
89
71
 
@@ -94,23 +76,16 @@ module Unicorn
94
76
  # Maximum request body size before it is moved out of memory and into a tempfile for reading.
95
77
  MAX_BODY=MAX_HEADER
96
78
 
79
+ # common errors we'll send back
80
+ ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n".freeze
81
+ ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze
82
+
97
83
  # A frozen format for this is about 15% faster
98
- CONTENT_TYPE = "Content-Type".freeze
99
- LAST_MODIFIED = "Last-Modified".freeze
100
- ETAG = "ETag".freeze
101
- REQUEST_METHOD="REQUEST_METHOD".freeze
102
- GET="GET".freeze
103
- HEAD="HEAD".freeze
104
- # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
105
- ETAG_FORMAT="\"%x-%x-%x\"".freeze
106
- LINE_END="\r\n".freeze
84
+ CONTENT_LENGTH="CONTENT_LENGTH".freeze
107
85
  REMOTE_ADDR="REMOTE_ADDR".freeze
108
86
  HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze
109
- HTTP_IF_MODIFIED_SINCE="HTTP_IF_MODIFIED_SINCE".freeze
110
- HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze
111
- REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n".freeze
112
- HOST = "HOST".freeze
113
- CONNECTION = "Connection".freeze
87
+ QUERY_STRING="QUERY_STRING".freeze
88
+ RACK_INPUT="rack.input".freeze
114
89
  end
115
90
 
116
91
  end
@@ -13,6 +13,20 @@ module Unicorn
13
13
  #
14
14
  class HttpRequest
15
15
 
16
+ # default parameters we merge into the request env for Rack handlers
17
+ DEF_PARAMS = {
18
+ "rack.errors" => $stderr,
19
+ "rack.multiprocess" => true,
20
+ "rack.multithread" => false,
21
+ "rack.run_once" => false,
22
+ "rack.url_scheme" => "http",
23
+ "rack.version" => [0, 1],
24
+ "SCRIPT_NAME" => "",
25
+
26
+ # this is not in the Rack spec, but some apps may rely on it
27
+ "SERVER_SOFTWARE" => "Unicorn #{Const::UNICORN_VERSION}"
28
+ }.freeze
29
+
16
30
  def initialize(logger)
17
31
  @logger = logger
18
32
  @body = nil
@@ -29,68 +43,47 @@ module Unicorn
29
43
  @body = nil
30
44
  end
31
45
 
32
- #
33
46
  # Does the majority of the IO processing. It has been written in
34
- # Ruby using about 7 different IO processing strategies and no
35
- # matter how it's done the performance just does not improve. It is
36
- # currently carefully constructed to make sure that it gets the best
37
- # possible performance, but anyone who thinks they can make it
38
- # faster is more than welcome to take a crack at it.
47
+ # Ruby using about 8 different IO processing strategies.
48
+ #
49
+ # It is currently carefully constructed to make sure that it gets
50
+ # the best possible performance for the common case: GET requests
51
+ # that are fully complete after a single read(2)
52
+ #
53
+ # Anyone who thinks they can make it faster is more than welcome to
54
+ # take a crack at it.
39
55
  #
40
56
  # returns an environment hash suitable for Rack if successful
41
57
  # This does minimal exception trapping and it is up to the caller
42
58
  # to handle any socket errors (e.g. user aborted upload).
43
59
  def read(socket)
44
- data = String.new(read_socket(socket))
45
- nparsed = 0
46
-
47
- # Assumption: nparsed will always be less since data will get
48
- # filled with more after each parsing. If it doesn't get more
49
- # then there was a problem with the read operation on the client
50
- # socket. Effect is to stop processing when the socket can't
51
- # fill the buffer for further parsing.
52
- while nparsed < data.length
53
- nparsed = @parser.execute(@params, data, nparsed)
54
-
55
- if @parser.finished?
56
- # From http://www.ietf.org/rfc/rfc3875:
57
- # "Script authors should be aware that the REMOTE_ADDR and
58
- # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
59
- # may not identify the ultimate source of the request. They
60
- # identify the client for the immediate request to the server;
61
- # that client may be a proxy, gateway, or other intermediary
62
- # acting on behalf of the actual source client."
63
- @params[Const::REMOTE_ADDR] = socket.unicorn_peeraddr
64
-
65
- handle_body(socket) and return rack_env # success!
66
- return nil # fail
67
- else
68
- # Parser is not done, queue up more data to read and continue
69
- # parsing
70
- data << read_socket(socket)
71
- if data.length >= Const::MAX_HEADER
72
- raise HttpParserError.new("HEADER is longer than allowed, " \
73
- "aborting client early.")
74
- end
75
- end
60
+ # short circuit the common case with small GET requests first
61
+ @parser.execute(@params, read_socket(socket)) and
62
+ return handle_body(socket)
63
+
64
+ data = @buffer.dup # read_socket will clobber @buffer
65
+
66
+ # Parser is not done, queue up more data to read and continue parsing
67
+ # an Exception thrown from the @parser will throw us out of the loop
68
+ loop do
69
+ data << read_socket(socket)
70
+ @parser.execute(@params, data) and return handle_body(socket)
76
71
  end
77
- nil # XXX bug?
78
72
  rescue HttpParserError => e
79
73
  @logger.error "HTTP parse error, malformed request " \
80
74
  "(#{@params[Const::HTTP_X_FORWARDED_FOR] ||
81
75
  socket.unicorn_peeraddr}): #{e.inspect}"
82
76
  @logger.error "REQUEST DATA: #{data.inspect}\n---\n" \
83
77
  "PARAMS: #{@params.inspect}\n---\n"
84
- socket.closed? or socket.close rescue nil
85
- nil
78
+ raise e
86
79
  end
87
80
 
88
81
  private
89
82
 
90
83
  # Handles dealing with the rest of the request
91
- # returns true if successful, false if not
84
+ # returns a Rack environment if successful, raises an exception if not
92
85
  def handle_body(socket)
93
- http_body = @params[Const::HTTP_BODY]
86
+ http_body = @params.delete(:http_body)
94
87
  content_length = @params[Const::CONTENT_LENGTH].to_i
95
88
  remain = content_length - http_body.length
96
89
 
@@ -108,9 +101,7 @@ module Unicorn
108
101
  # Some clients (like FF1.0) report 0 for body and then send a body.
109
102
  # This will probably truncate them but at least the request goes through
110
103
  # usually.
111
- if remain > 0
112
- read_body(socket, remain) or return false # fail!
113
- end
104
+ read_body(socket, remain) if remain > 0
114
105
  @body.rewind
115
106
  @body.sysseek(0) if @body.respond_to?(:sysseek)
116
107
 
@@ -118,29 +109,37 @@ module Unicorn
118
109
  # another request, we'll truncate it. Again, we don't do pipelining
119
110
  # or keepalive
120
111
  @body.truncate(content_length)
121
- true
112
+ rack_env(socket)
122
113
  end
123
114
 
124
115
  # Returns an environment which is rackable:
125
116
  # http://rack.rubyforge.org/doc/files/SPEC.html
126
117
  # Based on Rack's old Mongrel handler.
127
- def rack_env
118
+ def rack_env(socket)
119
+ # I'm considering enabling "unicorn.client". It gives
120
+ # applications some rope to do some "interesting" things like
121
+ # replacing a worker with another process that has full control
122
+ # over the HTTP response.
123
+ # @params["unicorn.client"] = socket
124
+
125
+ # From http://www.ietf.org/rfc/rfc3875:
126
+ # "Script authors should be aware that the REMOTE_ADDR and
127
+ # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
128
+ # may not identify the ultimate source of the request. They
129
+ # identify the client for the immediate request to the server;
130
+ # that client may be a proxy, gateway, or other intermediary
131
+ # acting on behalf of the actual source client."
132
+ @params[Const::REMOTE_ADDR] = socket.unicorn_peeraddr
133
+
128
134
  # It might be a dumbass full host request header
129
- @params[Const::REQUEST_PATH] ||=
130
- URI.parse(@params[Const::REQUEST_URI]).path
131
- raise "No REQUEST PATH" unless @params[Const::REQUEST_PATH]
132
-
133
- @params["QUERY_STRING"] ||= ''
134
- @params.update({ "rack.version" => [0,1],
135
- "rack.input" => @body,
136
- "rack.errors" => $stderr,
137
- "rack.multithread" => false,
138
- "rack.multiprocess" => true,
139
- "rack.run_once" => false,
140
- "rack.url_scheme" => "http",
141
- Const::PATH_INFO => @params[Const::REQUEST_PATH],
142
- Const::SCRIPT_NAME => "",
143
- })
135
+ @params[Const::PATH_INFO] = (
136
+ @params[Const::REQUEST_PATH] ||=
137
+ URI.parse(@params[Const::REQUEST_URI]).path) or
138
+ raise "No REQUEST_PATH"
139
+
140
+ @params[Const::QUERY_STRING] ||= ''
141
+ @params[Const::RACK_INPUT] = @body
142
+ @params.update(DEF_PARAMS)
144
143
  end
145
144
 
146
145
  # Does the heavy lifting of properly reading the larger body requests in
@@ -152,16 +151,14 @@ module Unicorn
152
151
  # writes always write the requested amount on a POSIX filesystem
153
152
  remain -= @body.syswrite(read_socket(socket))
154
153
  end
155
- true # success!
156
154
  rescue Object => e
157
155
  @logger.error "Error reading HTTP body: #{e.inspect}"
158
- socket.closed? or socket.close rescue nil
159
156
 
160
157
  # Any errors means we should delete the file, including if the file
161
158
  # is dumped. Truncate it ASAP to help avoid page flushes to disk.
162
159
  @body.truncate(0) rescue nil
163
160
  reset
164
- false
161
+ raise e
165
162
  end
166
163
 
167
164
  # read(2) on "slow" devices like sockets can be interrupted by signals
@@ -35,7 +35,11 @@ module Unicorn
35
35
  # the time anyways so just hope our app knows what it's doing
36
36
  headers.each do |key, value|
37
37
  next if SKIP.include?(key.downcase)
38
- value.split(/\n/).each { |v| out << "#{key}: #{v}" }
38
+ if value =~ /\n/
39
+ value.split(/\n/).each { |v| out << "#{key}: #{v}" }
40
+ else
41
+ out << "#{key}: #{value}"
42
+ end
39
43
  end
40
44
 
41
45
  # Rack should enforce Content-Length or chunked transfer encoding,
@@ -45,6 +49,7 @@ module Unicorn
45
49
  "Connection: close\r\n" \
46
50
  "#{out.join("\r\n")}\r\n\r\n")
47
51
  body.each { |chunk| socket_write(socket, chunk) }
52
+ socket.close # uncorks the socket immediately
48
53
  ensure
49
54
  body.respond_to?(:close) and body.close rescue nil
50
55
  end
@@ -62,10 +62,17 @@ module Unicorn
62
62
  end
63
63
  end
64
64
 
65
+ def log_buffer_sizes(sock, pfx = '')
66
+ respond_to?(:logger) or return
67
+ rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
68
+ sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
69
+ logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
70
+ end
71
+
65
72
  # creates a new server, socket. address may be a HOST:PORT or
66
73
  # an absolute path to a UNIX socket. address can even be a Socket
67
74
  # object in which case it is immediately returned
68
- def bind_listen(address = '0.0.0.0:8080', backlog = 1024)
75
+ def bind_listen(address = '0.0.0.0:8080', opt = { :backlog => 1024 })
69
76
  return address unless String === address
70
77
 
71
78
  domain, bind_addr = if address[0..0] == "/"
@@ -95,7 +102,13 @@ module Unicorn
95
102
  sock.close rescue nil
96
103
  return nil
97
104
  end
98
- sock.listen(backlog)
105
+ if opt[:rcvbuf] || opt[:sndbuf]
106
+ log_buffer_sizes(sock, "before: ")
107
+ sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
108
+ sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
109
+ log_buffer_sizes(sock, " after: ")
110
+ end
111
+ sock.listen(opt[:backlog] || 1024)
99
112
  set_server_sockopt(sock) if domain == AF_INET
100
113
  sock
101
114
  end