unicorn 4.7.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +0 -1
- data/.gitattributes +5 -0
- data/.gitignore +2 -2
- data/.manifest +14 -21
- data/.olddoc.yml +22 -0
- data/Application_Timeouts +7 -7
- 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 +139 -92
- data/HACKING +13 -28
- data/ISSUES +82 -19
- data/KNOWN_ISSUES +18 -18
- data/LATEST +22 -44
- data/LICENSE +2 -2
- data/Links +24 -22
- data/NEWS +729 -0
- data/PHILOSOPHY +0 -6
- data/README +50 -48
- data/Rakefile +0 -44
- data/SIGNALS +12 -3
- 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/common_field_optimization.h +23 -5
- data/ext/unicorn_http/ext_help.h +0 -20
- data/ext/unicorn_http/extconf.rb +37 -1
- data/ext/unicorn_http/global_variables.h +1 -1
- data/ext/unicorn_http/httpdate.c +2 -2
- data/ext/unicorn_http/unicorn_http.c +940 -644
- data/ext/unicorn_http/unicorn_http.rl +167 -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 +292 -199
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/oob_gc.rb +16 -6
- data/lib/unicorn/socket_helper.rb +58 -78
- data/lib/unicorn/stream_input.rb +9 -11
- 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 +99 -22
- data/lib/unicorn.rb +69 -42
- 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/t0300-no-default-middleware.sh +6 -1
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/t/test-lib.sh +2 -2
- 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 +20 -14
- data/test/unit/test_tee_input.rb +10 -0
- data/test/unit/test_upload.rb +10 -15
- data/test/unit/test_util.rb +28 -3
- data/unicorn.gemspec +28 -23
- data/unicorn_1 +1 -0
- data/unicorn_rails_1 +1 -0
- metadata +64 -134
- data/.wrongdoc.yml +0 -10
- data/ChangeLog +0 -4694
- 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
|