unicorn 4.8.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +0 -1
- data/.gitattributes +5 -0
- data/.gitignore +2 -2
- data/.manifest +17 -21
- data/.olddoc.yml +25 -0
- data/Application_Timeouts +7 -7
- data/CONTRIBUTORS +6 -2
- data/DESIGN +2 -4
- data/Documentation/.gitignore +1 -3
- data/Documentation/unicorn.1 +222 -0
- data/Documentation/unicorn_rails.1 +207 -0
- data/FAQ +23 -6
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +138 -92
- data/HACKING +10 -32
- data/ISSUES +80 -19
- data/KNOWN_ISSUES +18 -18
- data/LATEST +23 -27
- data/LICENSE +2 -2
- data/Links +24 -22
- data/NEWS +823 -0
- data/PHILOSOPHY +0 -6
- data/README +49 -48
- data/Rakefile +0 -44
- data/SIGNALS +2 -2
- data/Sandbox +11 -10
- data/TODO +0 -2
- data/TUNING +30 -9
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +4 -2
- data/bin/unicorn_rails +3 -3
- data/examples/big_app_gc.rb +1 -1
- data/examples/init.sh +36 -8
- data/examples/logrotate.conf +17 -2
- data/examples/nginx.conf +14 -14
- data/examples/unicorn.conf.minimal.rb +2 -2
- data/examples/unicorn.conf.rb +14 -6
- data/examples/unicorn.socket +11 -0
- data/examples/unicorn@.service +40 -0
- data/ext/unicorn_http/c_util.h +5 -13
- data/ext/unicorn_http/common_field_optimization.h +22 -5
- data/ext/unicorn_http/epollexclusive.h +124 -0
- data/ext/unicorn_http/ext_help.h +0 -44
- data/ext/unicorn_http/extconf.rb +32 -5
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +3 -2
- data/ext/unicorn_http/unicorn_http.c +926 -638
- data/ext/unicorn_http/unicorn_http.rl +159 -170
- data/ext/unicorn_http/unicorn_http_common.rl +1 -1
- data/lib/unicorn/configurator.rb +110 -46
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +110 -31
- data/lib/unicorn/http_response.rb +17 -31
- data/lib/unicorn/http_server.rb +272 -180
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/oob_gc.rb +16 -6
- data/lib/unicorn/select_waiter.rb +6 -0
- data/lib/unicorn/socket_helper.rb +58 -78
- data/lib/unicorn/stream_input.rb +8 -7
- data/lib/unicorn/tee_input.rb +16 -11
- data/lib/unicorn/tmpio.rb +10 -6
- data/lib/unicorn/util.rb +5 -4
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +36 -23
- data/lib/unicorn.rb +64 -44
- data/man/man1/unicorn.1 +124 -122
- data/man/man1/unicorn_rails.1 +113 -127
- data/t/.gitignore +0 -1
- data/t/GNUmakefile +3 -80
- data/t/README +4 -4
- data/t/t0002-parser-error.sh +3 -3
- data/t/t0011-active-unix-socket.sh +1 -1
- data/t/t0012-reload-empty-config.sh +2 -1
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/t/test-lib.sh +4 -3
- data/test/benchmark/README +14 -4
- data/test/benchmark/ddstream.ru +50 -0
- data/test/benchmark/readinput.ru +40 -0
- data/test/benchmark/uconnect.perl +66 -0
- data/test/exec/test_exec.rb +74 -20
- data/test/test_helper.rb +42 -33
- data/test/unit/test_ccc.rb +91 -0
- data/test/unit/test_droplet.rb +1 -1
- data/test/unit/test_http_parser.rb +49 -19
- data/test/unit/test_http_parser_ng.rb +98 -115
- data/test/unit/test_request.rb +11 -11
- data/test/unit/test_response.rb +31 -19
- data/test/unit/test_server.rb +89 -15
- data/test/unit/test_signals.rb +9 -9
- data/test/unit/test_socket_helper.rb +16 -12
- data/test/unit/test_tee_input.rb +10 -0
- data/test/unit/test_upload.rb +10 -15
- data/test/unit/test_util.rb +32 -6
- data/test/unit/test_waiter.rb +34 -0
- data/unicorn.gemspec +28 -23
- data/unicorn_1 +1 -0
- data/unicorn_rails_1 +1 -0
- metadata +68 -134
- data/.wrongdoc.yml +0 -10
- data/ChangeLog +0 -4861
- data/Documentation/GNUmakefile +0 -30
- data/Documentation/unicorn.1.txt +0 -178
- data/Documentation/unicorn_rails.1.txt +0 -175
- data/examples/git.ru +0 -13
- data/lib/unicorn/app/exec_cgi.rb +0 -154
- data/lib/unicorn/app/inetd.rb +0 -109
- data/lib/unicorn/ssl_client.rb +0 -11
- data/lib/unicorn/ssl_configurator.rb +0 -104
- data/lib/unicorn/ssl_server.rb +0 -42
- data/local.mk.sample +0 -59
- data/script/isolate_for_tests +0 -32
- data/t/hijack.ru +0 -42
- data/t/sslgen.sh +0 -71
- data/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/t/t0200-rack-hijack.sh +0 -27
- data/t/t0600-https-server-basic.sh +0 -48
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
data/lib/unicorn/configurator.rb
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
# -*- encoding: binary -*-
|
|
2
2
|
require 'logger'
|
|
3
|
-
require 'unicorn/ssl_configurator'
|
|
4
3
|
|
|
5
|
-
# Implements a simple DSL for configuring a
|
|
4
|
+
# Implements a simple DSL for configuring a unicorn server.
|
|
6
5
|
#
|
|
7
|
-
# See
|
|
8
|
-
#
|
|
6
|
+
# See https://yhbt.net/unicorn/examples/unicorn.conf.rb and
|
|
7
|
+
# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
|
|
9
8
|
# example configuration files. An example config file for use with
|
|
10
9
|
# nginx is also available at
|
|
11
|
-
#
|
|
10
|
+
# https://yhbt.net/unicorn/examples/nginx.conf
|
|
12
11
|
#
|
|
13
12
|
# See the link:/TUNING.html document for more information on tuning unicorn.
|
|
14
13
|
class Unicorn::Configurator
|
|
15
14
|
include Unicorn
|
|
16
|
-
include Unicorn::SSLConfigurator
|
|
17
15
|
|
|
18
16
|
# :stopdoc:
|
|
19
17
|
attr_accessor :set, :config_file, :after_reload
|
|
@@ -43,12 +41,24 @@ class Unicorn::Configurator
|
|
|
43
41
|
:before_exec => lambda { |server|
|
|
44
42
|
server.logger.info("forked child re-executing...")
|
|
45
43
|
},
|
|
44
|
+
:after_worker_exit => lambda { |server, worker, status|
|
|
45
|
+
m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
|
|
46
|
+
if status.success?
|
|
47
|
+
server.logger.info(m)
|
|
48
|
+
else
|
|
49
|
+
server.logger.error(m)
|
|
50
|
+
end
|
|
51
|
+
},
|
|
52
|
+
:after_worker_ready => lambda { |server, worker|
|
|
53
|
+
server.logger.info("worker=#{worker.nr} ready")
|
|
54
|
+
},
|
|
46
55
|
:pid => nil,
|
|
56
|
+
:early_hints => false,
|
|
57
|
+
:worker_exec => false,
|
|
47
58
|
:preload_app => false,
|
|
48
59
|
:check_client_connection => false,
|
|
49
|
-
:rewindable_input => true,
|
|
60
|
+
:rewindable_input => true,
|
|
50
61
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
|
51
|
-
:trust_x_forwarded => true,
|
|
52
62
|
}
|
|
53
63
|
#:startdoc:
|
|
54
64
|
|
|
@@ -79,6 +89,9 @@ class Unicorn::Configurator
|
|
|
79
89
|
RACKUP[:set_listener] and
|
|
80
90
|
set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
|
|
81
91
|
|
|
92
|
+
RACKUP[:no_default_middleware] and
|
|
93
|
+
set[:default_middleware] = false
|
|
94
|
+
|
|
82
95
|
# unicorn_rails creates dirs here after working_directory is bound
|
|
83
96
|
after_reload.call if after_reload
|
|
84
97
|
|
|
@@ -154,6 +167,38 @@ class Unicorn::Configurator
|
|
|
154
167
|
set_hook(:after_fork, block_given? ? block : args[0])
|
|
155
168
|
end
|
|
156
169
|
|
|
170
|
+
# sets after_worker_exit hook to a given block. This block will be called
|
|
171
|
+
# by the master process after a worker exits:
|
|
172
|
+
#
|
|
173
|
+
# after_worker_exit do |server,worker,status|
|
|
174
|
+
# # status is a Process::Status instance for the exited worker process
|
|
175
|
+
# unless status.success?
|
|
176
|
+
# server.logger.error("worker process failure: #{status.inspect}")
|
|
177
|
+
# end
|
|
178
|
+
# end
|
|
179
|
+
#
|
|
180
|
+
# after_worker_exit is only available in unicorn 5.3.0+
|
|
181
|
+
def after_worker_exit(*args, &block)
|
|
182
|
+
set_hook(:after_worker_exit, block_given? ? block : args[0], 3)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# sets after_worker_ready hook to a given block. This block will be called
|
|
186
|
+
# by a worker process after it has been fully loaded, directly before it
|
|
187
|
+
# starts responding to requests:
|
|
188
|
+
#
|
|
189
|
+
# after_worker_ready do |server,worker|
|
|
190
|
+
# server.logger.info("worker #{worker.nr} ready, dropping privileges")
|
|
191
|
+
# worker.user('username', 'groupname')
|
|
192
|
+
# end
|
|
193
|
+
#
|
|
194
|
+
# Do not use Configurator#user if you rely on changing users in the
|
|
195
|
+
# after_worker_ready hook.
|
|
196
|
+
#
|
|
197
|
+
# after_worker_ready is only available in unicorn 5.3.0+
|
|
198
|
+
def after_worker_ready(*args, &block)
|
|
199
|
+
set_hook(:after_worker_ready, block_given? ? block : args[0])
|
|
200
|
+
end
|
|
201
|
+
|
|
157
202
|
# sets before_fork got be a given Proc object. This Proc
|
|
158
203
|
# object will be called by the master process before forking
|
|
159
204
|
# each worker.
|
|
@@ -184,8 +229,6 @@ class Unicorn::Configurator
|
|
|
184
229
|
# to have nginx always retry backends that may have had workers
|
|
185
230
|
# SIGKILL-ed due to timeouts.
|
|
186
231
|
#
|
|
187
|
-
# # See http://wiki.nginx.org/NginxHttpUpstreamModule for more details
|
|
188
|
-
# # on nginx upstream configuration:
|
|
189
232
|
# upstream unicorn_backend {
|
|
190
233
|
# # for UNIX domain socket setups:
|
|
191
234
|
# server unix:/path/to/.unicorn.sock fail_timeout=0;
|
|
@@ -195,6 +238,9 @@ class Unicorn::Configurator
|
|
|
195
238
|
# server 192.168.0.8:8080 fail_timeout=0;
|
|
196
239
|
# server 192.168.0.9:8080 fail_timeout=0;
|
|
197
240
|
# }
|
|
241
|
+
#
|
|
242
|
+
# See https://nginx.org/en/docs/http/ngx_http_upstream_module.html
|
|
243
|
+
# for more details on nginx upstream configuration.
|
|
198
244
|
def timeout(seconds)
|
|
199
245
|
set_int(:timeout, seconds, 3)
|
|
200
246
|
# POSIX says 31 days is the smallest allowed maximum timeout for select()
|
|
@@ -202,6 +248,17 @@ class Unicorn::Configurator
|
|
|
202
248
|
set[:timeout] = seconds > max ? max : seconds
|
|
203
249
|
end
|
|
204
250
|
|
|
251
|
+
# Whether to exec in each worker process after forking. This changes the
|
|
252
|
+
# memory layout of each worker process, which is a security feature designed
|
|
253
|
+
# to defeat possible address space discovery attacks. Note that using
|
|
254
|
+
# worker_exec only makes sense if you are not preloading the application,
|
|
255
|
+
# and will result in higher memory usage.
|
|
256
|
+
#
|
|
257
|
+
# worker_exec is only available in unicorn 5.3.0+
|
|
258
|
+
def worker_exec(bool)
|
|
259
|
+
set_bool(:worker_exec, bool)
|
|
260
|
+
end
|
|
261
|
+
|
|
205
262
|
# sets the current number of worker_processes to +nr+. Each worker
|
|
206
263
|
# process will serve exactly one client at a time. You can
|
|
207
264
|
# increment or decrement this value at runtime by sending SIGTTIN
|
|
@@ -212,6 +269,23 @@ class Unicorn::Configurator
|
|
|
212
269
|
set_int(:worker_processes, nr, 1)
|
|
213
270
|
end
|
|
214
271
|
|
|
272
|
+
# sets whether to add default middleware in the development and
|
|
273
|
+
# deployment RACK_ENVs.
|
|
274
|
+
#
|
|
275
|
+
# default_middleware is only available in unicorn 5.5.0+
|
|
276
|
+
def default_middleware(bool)
|
|
277
|
+
set_bool(:default_middleware, bool)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# sets whether to enable the proposed early hints Rack API.
|
|
281
|
+
# If enabled, Rails 5.2+ will automatically send a 103 Early Hint
|
|
282
|
+
# for all the `javascript_include_tag` and `stylesheet_link_tag`
|
|
283
|
+
# in your response. See: https://api.rubyonrails.org/v5.2/classes/ActionDispatch/Request.html#method-i-send_early_hints
|
|
284
|
+
# See also https://tools.ietf.org/html/rfc8297
|
|
285
|
+
def early_hints(bool)
|
|
286
|
+
set_bool(:early_hints, bool)
|
|
287
|
+
end
|
|
288
|
+
|
|
215
289
|
# sets listeners to the given +addresses+, replacing or augmenting the
|
|
216
290
|
# current set. This is for the global listener pool shared by all
|
|
217
291
|
# worker processes. For per-worker listeners, see the after_fork example
|
|
@@ -257,6 +331,11 @@ class Unicorn::Configurator
|
|
|
257
331
|
#
|
|
258
332
|
# Default: 1024
|
|
259
333
|
#
|
|
334
|
+
# Note: with the Linux kernel, the net.core.somaxconn sysctl defaults
|
|
335
|
+
# to 128, capping this value to 128. Raising the sysctl allows a
|
|
336
|
+
# larger backlog (which may not be desirable with multiple,
|
|
337
|
+
# load-balanced machines).
|
|
338
|
+
#
|
|
260
339
|
# [:rcvbuf => bytes, :sndbuf => bytes]
|
|
261
340
|
#
|
|
262
341
|
# Maximum receive and send buffer sizes (in bytes) of sockets.
|
|
@@ -280,20 +359,19 @@ class Unicorn::Configurator
|
|
|
280
359
|
# Setting this to +true+ can make streaming responses in Rails 3.1
|
|
281
360
|
# appear more quickly at the cost of slightly higher bandwidth usage.
|
|
282
361
|
# The effect of this option is most visible if nginx is not used,
|
|
283
|
-
# but nginx remains highly recommended with
|
|
362
|
+
# but nginx remains highly recommended with unicorn.
|
|
284
363
|
#
|
|
285
364
|
# This has no effect on UNIX sockets.
|
|
286
365
|
#
|
|
287
|
-
# Default: +true+ (Nagle's algorithm disabled) in
|
|
288
|
-
#
|
|
289
|
-
# 3.x
|
|
366
|
+
# Default: +true+ (Nagle's algorithm disabled) in unicorn
|
|
367
|
+
# This defaulted to +false+ in unicorn 3.x
|
|
290
368
|
#
|
|
291
369
|
# [:tcp_nopush => true or false]
|
|
292
370
|
#
|
|
293
371
|
# Enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
|
|
294
372
|
#
|
|
295
373
|
# This prevents partial TCP frames from being sent out and reduces
|
|
296
|
-
# wakeups in nginx if it is on a different machine. Since
|
|
374
|
+
# wakeups in nginx if it is on a different machine. Since unicorn
|
|
297
375
|
# is only designed for applications that send the response body
|
|
298
376
|
# quickly without keepalive, sockets will always be flushed on close
|
|
299
377
|
# to prevent delays.
|
|
@@ -301,7 +379,7 @@ class Unicorn::Configurator
|
|
|
301
379
|
# This has no effect on UNIX sockets.
|
|
302
380
|
#
|
|
303
381
|
# Default: +false+
|
|
304
|
-
# This defaulted to +true+ in
|
|
382
|
+
# This defaulted to +true+ in unicorn 3.4 - 3.7
|
|
305
383
|
#
|
|
306
384
|
# [:ipv6only => true or false]
|
|
307
385
|
#
|
|
@@ -309,8 +387,6 @@ class Unicorn::Configurator
|
|
|
309
387
|
# to receive IPv4 queries on dual-stack systems. A separate IPv4-only
|
|
310
388
|
# listener is required if this is true.
|
|
311
389
|
#
|
|
312
|
-
# This option is only available for Ruby 1.9.2 and later.
|
|
313
|
-
#
|
|
314
390
|
# Enabling this option for the IPv6-only listener and having a
|
|
315
391
|
# separate IPv4 listener is recommended if you wish to support IPv6
|
|
316
392
|
# on the same TCP port. Otherwise, the value of \env[\"REMOTE_ADDR\"]
|
|
@@ -387,12 +463,10 @@ class Unicorn::Configurator
|
|
|
387
463
|
# and +false+ or +nil+ is synonymous for a value of zero.
|
|
388
464
|
#
|
|
389
465
|
# A value of +1+ is a good optimization for local networks
|
|
390
|
-
# and trusted clients.
|
|
391
|
-
#
|
|
392
|
-
# denial-of-service attacks. There is no good reason to ever
|
|
393
|
-
# disable this with a +zero+ value when serving HTTP.
|
|
466
|
+
# and trusted clients. There is no good reason to ever
|
|
467
|
+
# disable this with a +zero+ value with unicorn.
|
|
394
468
|
#
|
|
395
|
-
# Default: 1
|
|
469
|
+
# Default: 1
|
|
396
470
|
#
|
|
397
471
|
# [:accept_filter => String]
|
|
398
472
|
#
|
|
@@ -401,8 +475,7 @@ class Unicorn::Configurator
|
|
|
401
475
|
# This enables either the "dataready" or (default) "httpready"
|
|
402
476
|
# accept() filter under FreeBSD. This is intended as an
|
|
403
477
|
# optimization to reduce context switches with common GET/HEAD
|
|
404
|
-
# requests.
|
|
405
|
-
# some protection against certain denial-of-service attacks, too.
|
|
478
|
+
# requests.
|
|
406
479
|
#
|
|
407
480
|
# There is no good reason to change from the default.
|
|
408
481
|
#
|
|
@@ -469,13 +542,12 @@ class Unicorn::Configurator
|
|
|
469
542
|
# Disabling rewindability can improve performance by lowering
|
|
470
543
|
# I/O and memory usage for applications that accept uploads.
|
|
471
544
|
# Keep in mind that the Rack 1.x spec requires
|
|
472
|
-
# \env[\"rack.input\"] to be rewindable,
|
|
473
|
-
#
|
|
545
|
+
# \env[\"rack.input\"] to be rewindable,
|
|
546
|
+
# but the Rack 2.x spec does not.
|
|
474
547
|
#
|
|
475
|
-
# +rewindable_input+ defaults to +true+
|
|
476
|
-
#
|
|
477
|
-
#
|
|
478
|
-
# (less demanding) spec.
|
|
548
|
+
# +rewindable_input+ defaults to +true+ for compatibility.
|
|
549
|
+
# Setting it to +false+ may be safe for applications and
|
|
550
|
+
# frameworks developed for Rack 2.x and later.
|
|
479
551
|
def rewindable_input(bool)
|
|
480
552
|
set_bool(:rewindable_input, bool)
|
|
481
553
|
end
|
|
@@ -536,7 +608,7 @@ class Unicorn::Configurator
|
|
|
536
608
|
# just let chdir raise errors
|
|
537
609
|
path = File.expand_path(path)
|
|
538
610
|
if config_file &&
|
|
539
|
-
config_file
|
|
611
|
+
! config_file.start_with?('/') &&
|
|
540
612
|
! File.readable?("#{path}/#{config_file}")
|
|
541
613
|
raise ArgumentError,
|
|
542
614
|
"config_file=#{config_file} would not be accessible in" \
|
|
@@ -551,6 +623,10 @@ class Unicorn::Configurator
|
|
|
551
623
|
# This switch will occur after calling the after_fork hook, and only
|
|
552
624
|
# if the Worker#user method is not called in the after_fork hook
|
|
553
625
|
# +group+ is optional and will not change if unspecified.
|
|
626
|
+
#
|
|
627
|
+
# Do not use Configurator#user if you rely on changing users in the
|
|
628
|
+
# after_worker_ready hook. Instead, you need to call Worker#user
|
|
629
|
+
# directly in after_worker_ready.
|
|
554
630
|
def user(user, group = nil)
|
|
555
631
|
# raises ArgumentError on invalid user/group
|
|
556
632
|
Etc.getpwnam(user)
|
|
@@ -558,18 +634,6 @@ class Unicorn::Configurator
|
|
|
558
634
|
set[:user] = [ user, group ]
|
|
559
635
|
end
|
|
560
636
|
|
|
561
|
-
# Sets whether or not the parser will trust X-Forwarded-Proto and
|
|
562
|
-
# X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
|
|
563
|
-
# Rainbows!/Zbatery installations facing untrusted clients directly
|
|
564
|
-
# should set this to +false+. This is +true+ by default as Unicorn
|
|
565
|
-
# is designed to only sit behind trusted nginx proxies.
|
|
566
|
-
#
|
|
567
|
-
# This has never been publically documented and is subject to removal
|
|
568
|
-
# in future releases.
|
|
569
|
-
def trust_x_forwarded(bool) # :nodoc:
|
|
570
|
-
set_bool(:trust_x_forwarded, bool)
|
|
571
|
-
end
|
|
572
|
-
|
|
573
637
|
# expands "unix:path/to/foo" to a socket relative to the current path
|
|
574
638
|
# expands pathnames of sockets if relative to "~" or "~username"
|
|
575
639
|
# expands "*:port and ":port" to "0.0.0.0:port"
|
|
@@ -603,7 +667,7 @@ private
|
|
|
603
667
|
def canonicalize_tcp(addr, port)
|
|
604
668
|
packed = Socket.pack_sockaddr_in(port, addr)
|
|
605
669
|
port, addr = Socket.unpack_sockaddr_in(packed)
|
|
606
|
-
|
|
670
|
+
addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
|
607
671
|
end
|
|
608
672
|
|
|
609
673
|
def set_path(var, path) #:nodoc:
|
|
@@ -659,7 +723,7 @@ private
|
|
|
659
723
|
raise ArgumentError, "rackup file (#{ru}) not readable"
|
|
660
724
|
|
|
661
725
|
# it could be a .rb file, too, we don't parse those manually
|
|
662
|
-
ru
|
|
726
|
+
ru.end_with?('.ru') or return
|
|
663
727
|
|
|
664
728
|
/^#\\(.*)/ =~ File.read(ru) or return
|
|
665
729
|
RACKUP[:optparse].parse!($1.split(/\s+/))
|
data/lib/unicorn/const.rb
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# -*- encoding: binary -*-
|
|
2
2
|
|
|
3
|
-
# :
|
|
4
|
-
# Frequently used constants when constructing requests or responses.
|
|
5
|
-
# Many times the constant just refers to a string with the same
|
|
6
|
-
# contents. Using these constants gave about a 3% to 10% performance
|
|
7
|
-
# improvement over using the strings directly. Symbols did not really
|
|
8
|
-
# improve things much compared to constants.
|
|
9
|
-
module Unicorn::Const
|
|
3
|
+
module Unicorn::Const # :nodoc:
|
|
10
4
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
|
11
5
|
DEFAULT_HOST = "0.0.0.0"
|
|
12
6
|
|
|
@@ -23,22 +17,5 @@ module Unicorn::Const
|
|
|
23
17
|
# temporary file for reading (112 kilobytes). This is the default
|
|
24
18
|
# value of client_body_buffer_size.
|
|
25
19
|
MAX_BODY = 1024 * 112
|
|
26
|
-
|
|
27
|
-
# :stopdoc:
|
|
28
|
-
# common errors we'll send back
|
|
29
|
-
# (N.B. these are not used by unicorn, but we won't drop them until
|
|
30
|
-
# unicorn 5.x to avoid breaking Rainbows!).
|
|
31
|
-
ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
|
|
32
|
-
ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
|
|
33
|
-
ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
|
|
34
|
-
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
|
35
|
-
|
|
36
|
-
EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
|
|
37
|
-
EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "
|
|
38
|
-
|
|
39
|
-
HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
|
|
40
|
-
HTTP_EXPECT = "HTTP_EXPECT"
|
|
41
|
-
|
|
42
|
-
# :startdoc:
|
|
43
20
|
end
|
|
44
|
-
|
|
21
|
+
require_relative 'version'
|
data/lib/unicorn/http_request.rb
CHANGED
|
@@ -13,7 +13,8 @@ class Unicorn::HttpParser
|
|
|
13
13
|
"rack.multiprocess" => true,
|
|
14
14
|
"rack.multithread" => false,
|
|
15
15
|
"rack.run_once" => false,
|
|
16
|
-
"rack.version" => [1,
|
|
16
|
+
"rack.version" => [1, 2],
|
|
17
|
+
"rack.hijack?" => true,
|
|
17
18
|
"SCRIPT_NAME" => "",
|
|
18
19
|
|
|
19
20
|
# this is not in the Rack spec, but some apps may rely on it
|
|
@@ -22,14 +23,12 @@ class Unicorn::HttpParser
|
|
|
22
23
|
|
|
23
24
|
NULL_IO = StringIO.new("")
|
|
24
25
|
|
|
25
|
-
attr_accessor :response_start_sent
|
|
26
|
-
|
|
27
26
|
# :stopdoc:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
RACK_INPUT = 'rack.input'.freeze
|
|
27
|
+
HTTP_RESPONSE_START = [ 'HTTP'.freeze, '/1.1 '.freeze ]
|
|
28
|
+
EMPTY_ARRAY = [].freeze
|
|
31
29
|
@@input_class = Unicorn::TeeInput
|
|
32
30
|
@@check_client_connection = false
|
|
31
|
+
@@tcpi_inspect_ok = Socket.const_defined?(:TCP_INFO)
|
|
33
32
|
|
|
34
33
|
def self.input_class
|
|
35
34
|
@@input_class
|
|
@@ -63,17 +62,16 @@ class Unicorn::HttpParser
|
|
|
63
62
|
# This does minimal exception trapping and it is up to the caller
|
|
64
63
|
# to handle any socket errors (e.g. user aborted upload).
|
|
65
64
|
def read(socket)
|
|
66
|
-
clear
|
|
67
65
|
e = env
|
|
68
66
|
|
|
69
|
-
# From
|
|
67
|
+
# From https://www.ietf.org/rfc/rfc3875:
|
|
70
68
|
# "Script authors should be aware that the REMOTE_ADDR and
|
|
71
69
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
|
72
70
|
# may not identify the ultimate source of the request. They
|
|
73
71
|
# identify the client for the immediate request to the server;
|
|
74
72
|
# that client may be a proxy, gateway, or other intermediary
|
|
75
73
|
# acting on behalf of the actual source client."
|
|
76
|
-
e[REMOTE_ADDR] = socket.kgio_addr
|
|
74
|
+
e['REMOTE_ADDR'] = socket.kgio_addr
|
|
77
75
|
|
|
78
76
|
# short circuit the common case with small GET requests first
|
|
79
77
|
socket.kgio_read!(16384, buf)
|
|
@@ -83,40 +81,121 @@ class Unicorn::HttpParser
|
|
|
83
81
|
false until add_parse(socket.kgio_read!(16384))
|
|
84
82
|
end
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
check_client_connection(socket) if @@check_client_connection
|
|
85
|
+
|
|
86
|
+
e['rack.input'] = 0 == content_length ?
|
|
87
|
+
NULL_IO : @@input_class.new(socket, self)
|
|
88
|
+
|
|
89
|
+
# for Rack hijacking in Rack 1.5 and later
|
|
90
|
+
e['unicorn.socket'] = socket
|
|
91
|
+
e['rack.hijack'] = self
|
|
91
92
|
|
|
92
|
-
e[RACK_INPUT] = 0 == content_length ?
|
|
93
|
-
NULL_IO : @@input_class.new(socket, self)
|
|
94
|
-
hijack_setup(e, socket)
|
|
95
93
|
e.merge!(DEFAULTS)
|
|
96
94
|
end
|
|
97
95
|
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
# for rack.hijack, we respond to this method so no extra allocation
|
|
97
|
+
# of a proc object
|
|
98
|
+
def call
|
|
99
|
+
hijacked!
|
|
100
|
+
env['rack.hijack_io'] = env['unicorn.socket']
|
|
101
|
+
end
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
def hijacked?
|
|
104
|
+
env.include?('rack.hijack_io'.freeze)
|
|
105
|
+
end
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
if Raindrops.const_defined?(:TCP_Info)
|
|
108
|
+
TCPI = Raindrops::TCP_Info.allocate
|
|
109
|
+
|
|
110
|
+
def check_client_connection(socket) # :nodoc:
|
|
111
|
+
if Unicorn::TCPClient === socket
|
|
112
|
+
# Raindrops::TCP_Info#get!, #state (reads struct tcp_info#tcpi_state)
|
|
113
|
+
raise Errno::EPIPE, "client closed connection".freeze,
|
|
114
|
+
EMPTY_ARRAY if closed_state?(TCPI.get!(socket).state)
|
|
115
|
+
else
|
|
116
|
+
write_http_header(socket)
|
|
117
|
+
end
|
|
108
118
|
end
|
|
109
119
|
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
if Raindrops.const_defined?(:TCP)
|
|
121
|
+
# raindrops 0.18.0+ supports FreeBSD + Linux using the same names
|
|
122
|
+
# Evaluate these hash lookups at load time so we can
|
|
123
|
+
# generate an opt_case_dispatch instruction
|
|
124
|
+
eval <<-EOS
|
|
125
|
+
def closed_state?(state) # :nodoc:
|
|
126
|
+
case state
|
|
127
|
+
when #{Raindrops::TCP[:ESTABLISHED]}
|
|
128
|
+
false
|
|
129
|
+
when #{Raindrops::TCP.values_at(
|
|
130
|
+
:CLOSE_WAIT, :TIME_WAIT, :CLOSE, :LAST_ACK, :CLOSING).join(',')}
|
|
131
|
+
true
|
|
132
|
+
else
|
|
133
|
+
false
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
EOS
|
|
137
|
+
else
|
|
138
|
+
# raindrops before 0.18 only supported TCP_INFO under Linux
|
|
139
|
+
def closed_state?(state) # :nodoc:
|
|
140
|
+
case state
|
|
141
|
+
when 1 # ESTABLISHED
|
|
142
|
+
false
|
|
143
|
+
when 8, 6, 7, 9, 11 # CLOSE_WAIT, TIME_WAIT, CLOSE, LAST_ACK, CLOSING
|
|
144
|
+
true
|
|
145
|
+
else
|
|
146
|
+
false
|
|
147
|
+
end
|
|
148
|
+
end
|
|
112
149
|
end
|
|
113
150
|
else
|
|
114
|
-
|
|
115
|
-
|
|
151
|
+
|
|
152
|
+
# Ruby 2.2+ can show struct tcp_info as a string Socket::Option#inspect.
|
|
153
|
+
# Not that efficient, but probably still better than doing unnecessary
|
|
154
|
+
# work after a client gives up.
|
|
155
|
+
def check_client_connection(socket) # :nodoc:
|
|
156
|
+
if Unicorn::TCPClient === socket && @@tcpi_inspect_ok
|
|
157
|
+
opt = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO).inspect
|
|
158
|
+
if opt =~ /\bstate=(\S+)/
|
|
159
|
+
raise Errno::EPIPE, "client closed connection".freeze,
|
|
160
|
+
EMPTY_ARRAY if closed_state_str?($1)
|
|
161
|
+
else
|
|
162
|
+
@@tcpi_inspect_ok = false
|
|
163
|
+
write_http_header(socket)
|
|
164
|
+
end
|
|
165
|
+
opt.clear
|
|
166
|
+
else
|
|
167
|
+
write_http_header(socket)
|
|
168
|
+
end
|
|
116
169
|
end
|
|
117
170
|
|
|
118
|
-
def
|
|
119
|
-
|
|
171
|
+
def closed_state_str?(state)
|
|
172
|
+
case state
|
|
173
|
+
when 'ESTABLISHED'
|
|
174
|
+
false
|
|
175
|
+
# not a typo, ruby maps TCP_CLOSE (no 'D') to state=CLOSED (w/ 'D')
|
|
176
|
+
when 'CLOSE_WAIT', 'TIME_WAIT', 'CLOSED', 'LAST_ACK', 'CLOSING'
|
|
177
|
+
true
|
|
178
|
+
else
|
|
179
|
+
false
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def write_http_header(socket) # :nodoc:
|
|
185
|
+
if headers?
|
|
186
|
+
self.response_start_sent = true
|
|
187
|
+
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# called by ext/unicorn_http/unicorn_http.rl via rb_funcall
|
|
192
|
+
def self.is_chunked?(v) # :nodoc:
|
|
193
|
+
vals = v.split(/[ \t]*,[ \t]*/).map!(&:downcase)
|
|
194
|
+
if vals.pop == 'chunked'.freeze
|
|
195
|
+
return true unless vals.include?('chunked'.freeze)
|
|
196
|
+
raise Unicorn::HttpParserError, 'double chunked', []
|
|
120
197
|
end
|
|
198
|
+
return false unless vals.include?('chunked'.freeze)
|
|
199
|
+
raise Unicorn::HttpParserError, 'chunked not last', []
|
|
121
200
|
end
|
|
122
201
|
end
|
|
@@ -10,66 +10,52 @@
|
|
|
10
10
|
# is the job of Rack, with the exception of the "Date" and "Status" header.
|
|
11
11
|
module Unicorn::HttpResponse
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
hash[code] = "#{code} #{msg}"
|
|
16
|
-
hash
|
|
17
|
-
}
|
|
18
|
-
CRLF = "\r\n"
|
|
13
|
+
STATUS_CODES = defined?(Rack::Utils::HTTP_STATUS_CODES) ?
|
|
14
|
+
Rack::Utils::HTTP_STATUS_CODES : {}
|
|
19
15
|
|
|
16
|
+
# internal API, code will always be common-enough-for-even-old-Rack
|
|
20
17
|
def err_response(code, response_start_sent)
|
|
21
|
-
"#{response_start_sent ? '' : 'HTTP/1.1 '}
|
|
18
|
+
"#{response_start_sent ? '' : 'HTTP/1.1 '}" \
|
|
19
|
+
"#{code} #{STATUS_CODES[code]}\r\n\r\n"
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
# writes the rack_response to socket as an HTTP response
|
|
25
23
|
def http_response_write(socket, status, headers, body,
|
|
26
|
-
|
|
27
|
-
status = CODES[status.to_i] || status
|
|
24
|
+
req = Unicorn::HttpRequest.new)
|
|
28
25
|
hijack = nil
|
|
29
26
|
|
|
30
|
-
http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
|
|
31
27
|
if headers
|
|
32
|
-
|
|
28
|
+
code = status.to_i
|
|
29
|
+
msg = STATUS_CODES[code]
|
|
30
|
+
start = req.response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
|
|
31
|
+
buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
|
|
33
32
|
"Date: #{httpdate}\r\n" \
|
|
34
|
-
"Status: #{status}\r\n" \
|
|
35
33
|
"Connection: close\r\n"
|
|
36
34
|
headers.each do |key, value|
|
|
37
35
|
case key
|
|
38
|
-
when %r{\A(?:Date
|
|
36
|
+
when %r{\A(?:Date|Connection)\z}i
|
|
39
37
|
next
|
|
40
38
|
when "rack.hijack"
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
hijack =
|
|
39
|
+
# This should only be hit under Rack >= 1.5, as this was an illegal
|
|
40
|
+
# key in Rack < 1.5
|
|
41
|
+
hijack = value
|
|
44
42
|
else
|
|
45
43
|
if value =~ /\n/
|
|
46
44
|
# avoiding blank, key-only cookies with /\n+/
|
|
47
|
-
|
|
45
|
+
value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
|
|
48
46
|
else
|
|
49
47
|
buf << "#{key}: #{value}\r\n"
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
|
-
socket.write(buf <<
|
|
51
|
+
socket.write(buf << "\r\n".freeze)
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
if hijack
|
|
57
|
-
|
|
55
|
+
req.hijacked!
|
|
58
56
|
hijack.call(socket)
|
|
59
57
|
else
|
|
60
58
|
body.each { |chunk| socket.write(chunk) }
|
|
61
59
|
end
|
|
62
|
-
ensure
|
|
63
|
-
body.respond_to?(:close) and body.close
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Rack 1.5.0 (protocol version 1.2) adds response hijacking support
|
|
67
|
-
if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
|
|
68
|
-
def hijack_prepare(value)
|
|
69
|
-
value
|
|
70
|
-
end
|
|
71
|
-
else
|
|
72
|
-
def hijack_prepare(_)
|
|
73
|
-
end
|
|
74
60
|
end
|
|
75
61
|
end
|