unicorn 4.9.0 → 5.0.0.pre1
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 +4 -4
- data/FAQ +17 -8
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +2 -1
- data/TUNING +6 -3
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.rl +88 -155
- data/lib/unicorn.rb +7 -15
- data/lib/unicorn/configurator.rb +2 -17
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +22 -28
- data/lib/unicorn/http_response.rb +5 -20
- data/lib/unicorn/http_server.rb +112 -117
- data/lib/unicorn/socket_helper.rb +33 -67
- data/lib/unicorn/tmpio.rb +0 -5
- data/lib/unicorn/util.rb +1 -0
- data/lib/unicorn/worker.rb +1 -13
- data/t/hijack.ru +2 -1
- data/t/t0200-rack-hijack.sh +5 -2
- data/test/test_helper.rb +3 -2
- data/test/unit/test_http_parser_ng.rb +16 -114
- data/test/unit/test_response.rb +0 -17
- data/test/unit/test_socket_helper.rb +1 -1
- metadata +4 -16
- 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/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
data/lib/unicorn.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require 'fcntl'
|
3
2
|
require 'etc'
|
4
3
|
require 'stringio'
|
5
4
|
require 'rack'
|
@@ -22,8 +21,7 @@ module Unicorn
|
|
22
21
|
# since there is nothing in the application stack that is responsible
|
23
22
|
# for client shutdowns/disconnects. This exception is visible to Rack
|
24
23
|
# applications unless PrereadInput middleware is loaded.
|
25
|
-
|
26
|
-
end
|
24
|
+
ClientShutdown = Class.new(EOFError)
|
27
25
|
|
28
26
|
# :stopdoc:
|
29
27
|
|
@@ -102,19 +100,13 @@ module Unicorn
|
|
102
100
|
|
103
101
|
# remove this when we only support Ruby >= 2.0
|
104
102
|
def self.pipe # :nodoc:
|
105
|
-
Kgio::Pipe.new.each { |io| io.
|
103
|
+
Kgio::Pipe.new.each { |io| io.close_on_exec = true }
|
106
104
|
end
|
107
105
|
# :startdoc:
|
108
106
|
end
|
109
107
|
# :enddoc:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
require 'unicorn/configurator'
|
116
|
-
require 'unicorn/tmpio'
|
117
|
-
require 'unicorn/util'
|
118
|
-
require 'unicorn/http_response'
|
119
|
-
require 'unicorn/worker'
|
120
|
-
require 'unicorn/http_server'
|
108
|
+
|
109
|
+
%w(const socket_helper stream_input tee_input http_request configurator
|
110
|
+
tmpio util http_response worker http_server).each do |s|
|
111
|
+
require_relative "unicorn/#{s}"
|
112
|
+
end
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'logger'
|
3
|
-
require 'unicorn/ssl_configurator'
|
4
3
|
|
5
4
|
# Implements a simple DSL for configuring a \Unicorn server.
|
6
5
|
#
|
@@ -13,7 +12,6 @@ require 'unicorn/ssl_configurator'
|
|
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
|
@@ -48,7 +46,6 @@ class Unicorn::Configurator
|
|
48
46
|
:check_client_connection => false,
|
49
47
|
:rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
|
50
48
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
51
|
-
:trust_x_forwarded => true,
|
52
49
|
}
|
53
50
|
#:startdoc:
|
54
51
|
|
@@ -556,18 +553,6 @@ class Unicorn::Configurator
|
|
556
553
|
set[:user] = [ user, group ]
|
557
554
|
end
|
558
555
|
|
559
|
-
# Sets whether or not the parser will trust X-Forwarded-Proto and
|
560
|
-
# X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
|
561
|
-
# Rainbows!/Zbatery installations facing untrusted clients directly
|
562
|
-
# should set this to +false+. This is +true+ by default as Unicorn
|
563
|
-
# is designed to only sit behind trusted nginx proxies.
|
564
|
-
#
|
565
|
-
# This has never been publically documented and is subject to removal
|
566
|
-
# in future releases.
|
567
|
-
def trust_x_forwarded(bool) # :nodoc:
|
568
|
-
set_bool(:trust_x_forwarded, bool)
|
569
|
-
end
|
570
|
-
|
571
556
|
# expands "unix:path/to/foo" to a socket relative to the current path
|
572
557
|
# expands pathnames of sockets if relative to "~" or "~username"
|
573
558
|
# expands "*:port and ":port" to "0.0.0.0:port"
|
@@ -601,7 +586,7 @@ private
|
|
601
586
|
def canonicalize_tcp(addr, port)
|
602
587
|
packed = Socket.pack_sockaddr_in(port, addr)
|
603
588
|
port, addr = Socket.unpack_sockaddr_in(packed)
|
604
|
-
|
589
|
+
addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
605
590
|
end
|
606
591
|
|
607
592
|
def set_path(var, path) #:nodoc:
|
@@ -657,7 +642,7 @@ private
|
|
657
642
|
raise ArgumentError, "rackup file (#{ru}) not readable"
|
658
643
|
|
659
644
|
# it could be a .rb file, too, we don't parse those manually
|
660
|
-
ru
|
645
|
+
ru.end_with?('.ru') or return
|
661
646
|
|
662
647
|
/^#\\(.*)/ =~ File.read(ru) or return
|
663
648
|
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,21 +13,26 @@ 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
|
20
21
|
"SERVER_SOFTWARE" => "Unicorn #{Unicorn::Const::UNICORN_VERSION}"
|
21
22
|
}
|
22
23
|
|
24
|
+
RACK_HIJACK = "rack.hijack".freeze
|
25
|
+
RACK_HIJACK_IO = "rack.hijack_io".freeze
|
23
26
|
NULL_IO = StringIO.new("")
|
24
27
|
|
25
|
-
attr_accessor :response_start_sent
|
26
|
-
|
27
28
|
# :stopdoc:
|
28
29
|
# A frozen format for this is about 15% faster
|
30
|
+
# Drop these frozen strings when Ruby 2.2 becomes more prevalent,
|
31
|
+
# 2.2+ optimizes hash assignments when used with literal string keys
|
29
32
|
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
30
33
|
RACK_INPUT = 'rack.input'.freeze
|
34
|
+
UNICORN_SOCKET = 'unicorn.socket'.freeze
|
35
|
+
HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
|
31
36
|
@@input_class = Unicorn::TeeInput
|
32
37
|
@@check_client_connection = false
|
33
38
|
|
@@ -85,38 +90,27 @@ class Unicorn::HttpParser
|
|
85
90
|
|
86
91
|
# detect if the socket is valid by writing a partial response:
|
87
92
|
if @@check_client_connection && headers?
|
88
|
-
|
89
|
-
|
93
|
+
self.response_start_sent = true
|
94
|
+
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
90
95
|
end
|
91
96
|
|
92
97
|
e[RACK_INPUT] = 0 == content_length ?
|
93
98
|
NULL_IO : @@input_class.new(socket, self)
|
94
|
-
hijack_setup(e, socket)
|
95
|
-
e.merge!(DEFAULTS)
|
96
|
-
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
DEFAULTS["rack.version"] = [1, 2]
|
100
|
+
# for Rack hijacking in Rack 1.5 and later
|
101
|
+
e[UNICORN_SOCKET] = socket
|
102
|
+
e[RACK_HIJACK] = self
|
102
103
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
def hijacked?
|
107
|
-
env.include?(RACK_HIJACK_IO)
|
108
|
-
end
|
104
|
+
e.merge!(DEFAULTS)
|
105
|
+
end
|
109
106
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def hijack_setup(e, _)
|
116
|
-
end
|
107
|
+
# for rack.hijack, we respond to this method so no extra allocation
|
108
|
+
# of a proc object
|
109
|
+
def call
|
110
|
+
env[RACK_HIJACK_IO] = env[UNICORN_SOCKET]
|
111
|
+
end
|
117
112
|
|
118
|
-
|
119
|
-
|
120
|
-
end
|
113
|
+
def hijacked?
|
114
|
+
env.include?(RACK_HIJACK_IO)
|
121
115
|
end
|
122
116
|
end
|
@@ -24,23 +24,21 @@ module Unicorn::HttpResponse
|
|
24
24
|
# writes the rack_response to socket as an HTTP response
|
25
25
|
def http_response_write(socket, status, headers, body,
|
26
26
|
response_start_sent=false)
|
27
|
-
status = CODES[status.to_i] || status
|
28
27
|
hijack = nil
|
29
28
|
|
30
29
|
http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
|
31
30
|
if headers
|
32
|
-
buf = "#{http_response_start}#{status}\r\n" \
|
31
|
+
buf = "#{http_response_start}#{CODES[status.to_i] || 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+/
|
@@ -54,22 +52,9 @@ module Unicorn::HttpResponse
|
|
54
52
|
end
|
55
53
|
|
56
54
|
if hijack
|
57
|
-
body = nil # ensure we do not close body
|
58
55
|
hijack.call(socket)
|
59
56
|
else
|
60
57
|
body.each { |chunk| socket.write(chunk) }
|
61
58
|
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
59
|
end
|
75
60
|
end
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require "unicorn/ssl_server"
|
3
2
|
|
4
3
|
# This is the process manager of Unicorn. This manages worker
|
5
4
|
# processes which in turn handle the I/O and application process.
|
@@ -12,46 +11,23 @@ require "unicorn/ssl_server"
|
|
12
11
|
# See Unicorn::Configurator for information on how to configure \Unicorn.
|
13
12
|
class Unicorn::HttpServer
|
14
13
|
# :stopdoc:
|
15
|
-
attr_accessor :app, :
|
14
|
+
attr_accessor :app, :timeout, :worker_processes,
|
16
15
|
:before_fork, :after_fork, :before_exec,
|
17
16
|
:listener_opts, :preload_app,
|
18
|
-
:
|
19
|
-
:master_pid, :config, :ready_pipe, :user
|
17
|
+
:orig_app, :config, :ready_pipe, :user
|
20
18
|
|
21
19
|
attr_reader :pid, :logger
|
22
20
|
include Unicorn::SocketHelper
|
23
21
|
include Unicorn::HttpResponse
|
24
|
-
include Unicorn::SSLServer
|
25
|
-
|
26
|
-
# backwards compatibility with 1.x
|
27
|
-
Worker = Unicorn::Worker
|
28
22
|
|
29
23
|
# all bound listener sockets
|
24
|
+
# note: this is public used by raindrops, but not recommended for use
|
25
|
+
# in new projects
|
30
26
|
LISTENERS = []
|
31
27
|
|
32
28
|
# listeners we have yet to bind
|
33
29
|
NEW_LISTENERS = []
|
34
30
|
|
35
|
-
# This hash maps PIDs to Workers
|
36
|
-
WORKERS = {}
|
37
|
-
|
38
|
-
# We use SELF_PIPE differently in the master and worker processes:
|
39
|
-
#
|
40
|
-
# * The master process never closes or reinitializes this once
|
41
|
-
# initialized. Signal handlers in the master process will write to
|
42
|
-
# it to wake up the master from IO.select in exactly the same manner
|
43
|
-
# djb describes in http://cr.yp.to/docs/selfpipe.html
|
44
|
-
#
|
45
|
-
# * The workers immediately close the pipe they inherit. See the
|
46
|
-
# Unicorn::Worker class for the pipe workers use.
|
47
|
-
SELF_PIPE = []
|
48
|
-
|
49
|
-
# signal queue used for self-piping
|
50
|
-
SIG_QUEUE = []
|
51
|
-
|
52
|
-
# list of signals we care about and trap in master.
|
53
|
-
QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
|
54
|
-
|
55
31
|
# :startdoc:
|
56
32
|
# We populate this at startup so we can figure out how to reexecute
|
57
33
|
# and upgrade the currently running instance of Unicorn
|
@@ -71,7 +47,7 @@ class Unicorn::HttpServer
|
|
71
47
|
#
|
72
48
|
# Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.2.0/bin/unicorn"
|
73
49
|
START_CTX = {
|
74
|
-
:argv => ARGV.map
|
50
|
+
:argv => ARGV.map(&:dup),
|
75
51
|
0 => $0.dup,
|
76
52
|
}
|
77
53
|
# We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
|
@@ -92,7 +68,7 @@ class Unicorn::HttpServer
|
|
92
68
|
def initialize(app, options = {})
|
93
69
|
@app = app
|
94
70
|
@request = Unicorn::HttpRequest.new
|
95
|
-
|
71
|
+
@reexec_pid = 0
|
96
72
|
options = options.dup
|
97
73
|
@ready_pipe = options.delete(:ready_pipe)
|
98
74
|
@init_listeners = options[:listeners] ? options[:listeners].dup : []
|
@@ -100,6 +76,19 @@ class Unicorn::HttpServer
|
|
100
76
|
self.config = Unicorn::Configurator.new(options)
|
101
77
|
self.listener_opts = {}
|
102
78
|
|
79
|
+
# We use @self_pipe differently in the master and worker processes:
|
80
|
+
#
|
81
|
+
# * The master process never closes or reinitializes this once
|
82
|
+
# initialized. Signal handlers in the master process will write to
|
83
|
+
# it to wake up the master from IO.select in exactly the same manner
|
84
|
+
# djb describes in http://cr.yp.to/docs/selfpipe.html
|
85
|
+
#
|
86
|
+
# * The workers immediately close the pipe they inherit. See the
|
87
|
+
# Unicorn::Worker class for the pipe workers use.
|
88
|
+
@self_pipe = []
|
89
|
+
@workers = {} # hash maps PIDs to Workers
|
90
|
+
@sig_queue = [] # signal queue used for self-piping
|
91
|
+
|
103
92
|
# we try inheriting listeners first, so we bind them later.
|
104
93
|
# we don't write the pid file until we've bound listeners in case
|
105
94
|
# unicorn was started twice by mistake. Even though our #pid= method
|
@@ -111,7 +100,10 @@ class Unicorn::HttpServer
|
|
111
100
|
# monitoring tools may also rely on pid files existing before we
|
112
101
|
# attempt to connect to the listener(s)
|
113
102
|
config.commit!(self, :skip => [:listeners, :pid])
|
114
|
-
|
103
|
+
@orig_app = app
|
104
|
+
# list of signals we care about and trap in master.
|
105
|
+
@queue_sigs = [
|
106
|
+
:WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
|
115
107
|
end
|
116
108
|
|
117
109
|
# Runs the thing. Returns self so you can run join on it
|
@@ -119,13 +111,13 @@ class Unicorn::HttpServer
|
|
119
111
|
inherit_listeners!
|
120
112
|
# this pipe is used to wake us up from select(2) in #join when signals
|
121
113
|
# are trapped. See trap_deferred.
|
122
|
-
|
114
|
+
@self_pipe.replace(Unicorn.pipe)
|
123
115
|
@master_pid = $$
|
124
116
|
|
125
117
|
# setup signal handlers before writing pid file in case people get
|
126
118
|
# trigger happy and send signals as soon as the pid file exists.
|
127
119
|
# Note that signals don't actually get handled until the #join method
|
128
|
-
|
120
|
+
@queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
|
129
121
|
trap(:CHLD) { awaken_master }
|
130
122
|
|
131
123
|
# write pid early for Mongrel compatibility if we're not inheriting sockets
|
@@ -158,9 +150,6 @@ class Unicorn::HttpServer
|
|
158
150
|
|
159
151
|
LISTENERS.delete_if do |io|
|
160
152
|
if dead_names.include?(sock_name(io))
|
161
|
-
IO_PURGATORY.delete_if do |pio|
|
162
|
-
pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
|
163
|
-
end
|
164
153
|
(io.close rescue nil).nil? # true
|
165
154
|
else
|
166
155
|
set_server_sockopt(io, listener_opts[sock_name(io)])
|
@@ -198,7 +187,7 @@ class Unicorn::HttpServer
|
|
198
187
|
if path
|
199
188
|
if x = valid_pid?(path)
|
200
189
|
return path if pid && path == pid && x == $$
|
201
|
-
if x == reexec_pid && pid
|
190
|
+
if x == @reexec_pid && pid.end_with?('.oldbin')
|
202
191
|
logger.warn("will not set pid=#{path} while reexec-ed "\
|
203
192
|
"child is running PID:#{x}")
|
204
193
|
return
|
@@ -241,7 +230,7 @@ class Unicorn::HttpServer
|
|
241
230
|
begin
|
242
231
|
io = bind_listen(address, opt)
|
243
232
|
unless Kgio::TCPServer === io || Kgio::UNIXServer === io
|
244
|
-
|
233
|
+
io.autoclose = false
|
245
234
|
io = server_cast(io)
|
246
235
|
end
|
247
236
|
logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
|
@@ -267,7 +256,7 @@ class Unicorn::HttpServer
|
|
267
256
|
# is signalling us too often.
|
268
257
|
def join
|
269
258
|
respawn = true
|
270
|
-
last_check =
|
259
|
+
last_check = time_now
|
271
260
|
|
272
261
|
proc_name 'master'
|
273
262
|
logger.info "master process ready" # test_exec.rb relies on this message
|
@@ -281,11 +270,11 @@ class Unicorn::HttpServer
|
|
281
270
|
end
|
282
271
|
begin
|
283
272
|
reap_all_workers
|
284
|
-
case
|
273
|
+
case @sig_queue.shift
|
285
274
|
when nil
|
286
275
|
# avoid murdering workers after our master process (or the
|
287
276
|
# machine) comes out of suspend/hibernation
|
288
|
-
if (last_check + @timeout) >= (last_check =
|
277
|
+
if (last_check + @timeout) >= (last_check = time_now)
|
289
278
|
sleep_time = murder_lazy_workers
|
290
279
|
else
|
291
280
|
sleep_time = @timeout/2.0 + 1
|
@@ -306,13 +295,13 @@ class Unicorn::HttpServer
|
|
306
295
|
when :USR2 # exec binary, stay alive in case something went wrong
|
307
296
|
reexec
|
308
297
|
when :WINCH
|
309
|
-
if
|
298
|
+
if $stdin.tty?
|
299
|
+
logger.info "SIGWINCH ignored because we're not daemonized"
|
300
|
+
else
|
310
301
|
respawn = false
|
311
302
|
logger.info "gracefully stopping all workers"
|
312
303
|
soft_kill_each_worker(:QUIT)
|
313
304
|
self.worker_processes = 0
|
314
|
-
else
|
315
|
-
logger.info "SIGWINCH ignored because we're not daemonized"
|
316
305
|
end
|
317
306
|
when :TTIN
|
318
307
|
respawn = true
|
@@ -339,8 +328,8 @@ class Unicorn::HttpServer
|
|
339
328
|
# Terminates all workers, but does not exit master process
|
340
329
|
def stop(graceful = true)
|
341
330
|
self.listeners = []
|
342
|
-
limit =
|
343
|
-
until
|
331
|
+
limit = time_now + timeout
|
332
|
+
until @workers.empty? || time_now > limit
|
344
333
|
if graceful
|
345
334
|
soft_kill_each_worker(:QUIT)
|
346
335
|
else
|
@@ -369,14 +358,6 @@ class Unicorn::HttpServer
|
|
369
358
|
Unicorn::TeeInput.client_body_buffer_size = bytes
|
370
359
|
end
|
371
360
|
|
372
|
-
def trust_x_forwarded
|
373
|
-
Unicorn::HttpParser.trust_x_forwarded?
|
374
|
-
end
|
375
|
-
|
376
|
-
def trust_x_forwarded=(bool)
|
377
|
-
Unicorn::HttpParser.trust_x_forwarded = bool
|
378
|
-
end
|
379
|
-
|
380
361
|
def check_client_connection
|
381
362
|
Unicorn::HttpRequest.check_client_connection
|
382
363
|
end
|
@@ -389,17 +370,17 @@ class Unicorn::HttpServer
|
|
389
370
|
|
390
371
|
# wait for a signal hander to wake us up and then consume the pipe
|
391
372
|
def master_sleep(sec)
|
373
|
+
@self_pipe[0].kgio_wait_readable(sec) or return
|
392
374
|
# 11 bytes is the maximum string length which can be embedded within
|
393
375
|
# the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
|
394
376
|
# Most reads are only one byte here and uncommon, so it's not worth a
|
395
377
|
# persistent buffer, either:
|
396
|
-
|
397
|
-
SELF_PIPE[0].kgio_tryread(11)
|
378
|
+
@self_pipe[0].kgio_tryread(11)
|
398
379
|
end
|
399
380
|
|
400
381
|
def awaken_master
|
401
382
|
return if $$ != @master_pid
|
402
|
-
|
383
|
+
@self_pipe[1].kgio_trywrite('.') # wakeup master process from select
|
403
384
|
end
|
404
385
|
|
405
386
|
# reaps all unreaped workers
|
@@ -407,13 +388,13 @@ class Unicorn::HttpServer
|
|
407
388
|
begin
|
408
389
|
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
409
390
|
wpid or return
|
410
|
-
if reexec_pid == wpid
|
391
|
+
if @reexec_pid == wpid
|
411
392
|
logger.error "reaped #{status.inspect} exec()-ed"
|
412
|
-
|
393
|
+
@reexec_pid = 0
|
413
394
|
self.pid = pid.chomp('.oldbin') if pid
|
414
395
|
proc_name 'master'
|
415
396
|
else
|
416
|
-
worker =
|
397
|
+
worker = @workers.delete(wpid) and worker.close rescue nil
|
417
398
|
m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
|
418
399
|
status.success? ? logger.info(m) : logger.error(m)
|
419
400
|
end
|
@@ -424,13 +405,13 @@ class Unicorn::HttpServer
|
|
424
405
|
|
425
406
|
# reexecutes the START_CTX with a new binary
|
426
407
|
def reexec
|
427
|
-
if reexec_pid > 0
|
408
|
+
if @reexec_pid > 0
|
428
409
|
begin
|
429
|
-
Process.kill(0, reexec_pid)
|
430
|
-
logger.error "reexec-ed child already running PID
|
410
|
+
Process.kill(0, @reexec_pid)
|
411
|
+
logger.error "reexec-ed child already running PID:#@reexec_pid"
|
431
412
|
return
|
432
413
|
rescue Errno::ESRCH
|
433
|
-
|
414
|
+
@reexec_pid = 0
|
434
415
|
end
|
435
416
|
end
|
436
417
|
|
@@ -448,13 +429,10 @@ class Unicorn::HttpServer
|
|
448
429
|
end
|
449
430
|
end
|
450
431
|
|
451
|
-
|
432
|
+
@reexec_pid = fork do
|
452
433
|
listener_fds = {}
|
453
434
|
LISTENERS.each do |sock|
|
454
|
-
|
455
|
-
# Ruby that sets FD_CLOEXEC by default on new file descriptors
|
456
|
-
# ref: http://redmine.ruby-lang.org/issues/5041
|
457
|
-
sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
|
435
|
+
sock.close_on_exec = false
|
458
436
|
listener_fds[sock.fileno] = sock
|
459
437
|
end
|
460
438
|
ENV['UNICORN_FD'] = listener_fds.keys.join(',')
|
@@ -467,13 +445,13 @@ class Unicorn::HttpServer
|
|
467
445
|
(3..1024).each do |io|
|
468
446
|
next if listener_fds.include?(io)
|
469
447
|
io = IO.for_fd(io) rescue next
|
470
|
-
|
471
|
-
io.
|
448
|
+
io.autoclose = false
|
449
|
+
io.close_on_exec = true
|
472
450
|
end
|
473
451
|
|
474
452
|
# exec(command, hash) works in at least 1.9.1+, but will only be
|
475
453
|
# required in 1.9.4/2.0.0 at earliest.
|
476
|
-
cmd << listener_fds
|
454
|
+
cmd << listener_fds
|
477
455
|
logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
|
478
456
|
before_exec.call(self)
|
479
457
|
exec(*cmd)
|
@@ -484,8 +462,8 @@ class Unicorn::HttpServer
|
|
484
462
|
# forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
|
485
463
|
def murder_lazy_workers
|
486
464
|
next_sleep = @timeout - 1
|
487
|
-
now =
|
488
|
-
|
465
|
+
now = time_now.to_i
|
466
|
+
@workers.dup.each_pair do |wpid, worker|
|
489
467
|
tick = worker.tick
|
490
468
|
0 == tick and next # skip workers that haven't processed any clients
|
491
469
|
diff = now - tick
|
@@ -503,7 +481,7 @@ class Unicorn::HttpServer
|
|
503
481
|
end
|
504
482
|
|
505
483
|
def after_fork_internal
|
506
|
-
|
484
|
+
@self_pipe.each(&:close).clear # this is master-only, now
|
507
485
|
@ready_pipe.close if @ready_pipe
|
508
486
|
Unicorn::Configurator::RACKUP.clear
|
509
487
|
@ready_pipe = @init_listeners = @before_exec = @before_fork = nil
|
@@ -518,11 +496,11 @@ class Unicorn::HttpServer
|
|
518
496
|
def spawn_missing_workers
|
519
497
|
worker_nr = -1
|
520
498
|
until (worker_nr += 1) == @worker_processes
|
521
|
-
|
522
|
-
worker = Worker.new(worker_nr)
|
499
|
+
@workers.value?(worker_nr) and next
|
500
|
+
worker = Unicorn::Worker.new(worker_nr)
|
523
501
|
before_fork.call(self, worker)
|
524
502
|
if pid = fork
|
525
|
-
|
503
|
+
@workers[pid] = worker
|
526
504
|
worker.atfork_parent
|
527
505
|
else
|
528
506
|
after_fork_internal
|
@@ -536,9 +514,9 @@ class Unicorn::HttpServer
|
|
536
514
|
end
|
537
515
|
|
538
516
|
def maintain_worker_count
|
539
|
-
(off =
|
517
|
+
(off = @workers.size - worker_processes) == 0 and return
|
540
518
|
off < 0 and return spawn_missing_workers
|
541
|
-
|
519
|
+
@workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
|
542
520
|
end
|
543
521
|
|
544
522
|
# if we get any error, try to write something back to the client
|
@@ -566,29 +544,37 @@ class Unicorn::HttpServer
|
|
566
544
|
rescue
|
567
545
|
end
|
568
546
|
|
569
|
-
def
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
547
|
+
def e100_response_write(client, env)
|
548
|
+
# We use String#freeze to avoid allocations under Ruby 2.1+
|
549
|
+
# Not many users hit this code path, so it's better to reduce the
|
550
|
+
# constant table sizes even for 1.9.3-2.0 users who'll hit extra
|
551
|
+
# allocations here.
|
552
|
+
client.write(@request.response_start_sent ?
|
553
|
+
"100 Continue\r\n\r\nHTTP/1.1 ".freeze :
|
554
|
+
"HTTP/1.1 100 Continue\r\n\r\n".freeze)
|
555
|
+
env.delete('HTTP_EXPECT'.freeze)
|
575
556
|
end
|
576
557
|
|
577
558
|
# once a client is accepted, it is processed in its entirety here
|
578
559
|
# in 3 easy steps: read request, call app, write app response
|
579
560
|
def process_client(client)
|
580
561
|
status, headers, body = @app.call(env = @request.read(client))
|
581
|
-
return if @request.hijacked?
|
582
562
|
|
583
|
-
|
584
|
-
client.write(expect_100_response)
|
585
|
-
env.delete(Unicorn::Const::HTTP_EXPECT)
|
586
|
-
status, headers, body = @app.call(env)
|
563
|
+
begin
|
587
564
|
return if @request.hijacked?
|
565
|
+
|
566
|
+
if 100 == status.to_i
|
567
|
+
e100_response_write(client, env)
|
568
|
+
status, headers, body = @app.call(env)
|
569
|
+
return if @request.hijacked?
|
570
|
+
end
|
571
|
+
@request.headers? or headers = nil
|
572
|
+
http_response_write(client, status, headers, body,
|
573
|
+
@request.response_start_sent)
|
574
|
+
ensure
|
575
|
+
body.respond_to?(:close) and body.close
|
588
576
|
end
|
589
|
-
|
590
|
-
http_response_write(client, status, headers, body,
|
591
|
-
@request.response_start_sent)
|
577
|
+
|
592
578
|
unless client.closed? # rack.hijack may've close this for us
|
593
579
|
client.shutdown # in case of fork() in Rack app
|
594
580
|
client.close # flush and uncork socket immediately, no keepalive
|
@@ -597,9 +583,6 @@ class Unicorn::HttpServer
|
|
597
583
|
handle_error(client, e)
|
598
584
|
end
|
599
585
|
|
600
|
-
EXIT_SIGS = [ :QUIT, :TERM, :INT ]
|
601
|
-
WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
|
602
|
-
|
603
586
|
def nuke_listeners!(readers)
|
604
587
|
# only called from the worker, ordering is important here
|
605
588
|
tmp = readers.dup
|
@@ -614,23 +597,23 @@ class Unicorn::HttpServer
|
|
614
597
|
def init_worker_process(worker)
|
615
598
|
worker.atfork_child
|
616
599
|
# we'll re-trap :QUIT later for graceful shutdown iff we accept clients
|
617
|
-
|
618
|
-
exit!(0)
|
619
|
-
|
600
|
+
exit_sigs = [ :QUIT, :TERM, :INT ]
|
601
|
+
exit_sigs.each { |sig| trap(sig) { exit!(0) } }
|
602
|
+
exit!(0) if (@sig_queue & exit_sigs)[0]
|
603
|
+
(@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
|
620
604
|
trap(:CHLD, 'DEFAULT')
|
621
|
-
|
605
|
+
@sig_queue.clear
|
622
606
|
proc_name "worker[#{worker.nr}]"
|
623
607
|
START_CTX.clear
|
624
|
-
|
608
|
+
@workers.clear
|
625
609
|
|
626
610
|
after_fork.call(self, worker) # can drop perms and create listeners
|
627
|
-
LISTENERS.each { |sock| sock.
|
611
|
+
LISTENERS.each { |sock| sock.close_on_exec = true }
|
628
612
|
|
629
613
|
worker.user(*user) if user.kind_of?(Array) && ! worker.switched
|
630
614
|
self.timeout /= 2.0 # halve it for select()
|
631
615
|
@config = nil
|
632
616
|
build_app! unless preload_app
|
633
|
-
ssl_enable!
|
634
617
|
@after_fork = @listener_opts = @orig_app = nil
|
635
618
|
readers = LISTENERS.dup
|
636
619
|
readers << worker
|
@@ -651,7 +634,7 @@ class Unicorn::HttpServer
|
|
651
634
|
# for connections and doesn't die until the parent dies (or is
|
652
635
|
# given a INT, QUIT, or TERM signal)
|
653
636
|
def worker_loop(worker)
|
654
|
-
ppid = master_pid
|
637
|
+
ppid = @master_pid
|
655
638
|
readers = init_worker_process(worker)
|
656
639
|
nr = 0 # this becomes negative if we need to reopen logs
|
657
640
|
|
@@ -665,7 +648,7 @@ class Unicorn::HttpServer
|
|
665
648
|
begin
|
666
649
|
nr < 0 and reopen_worker_logs(worker.nr)
|
667
650
|
nr = 0
|
668
|
-
worker.tick =
|
651
|
+
worker.tick = time_now.to_i
|
669
652
|
tmp = ready.dup
|
670
653
|
while sock = tmp.shift
|
671
654
|
# Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
|
@@ -673,7 +656,7 @@ class Unicorn::HttpServer
|
|
673
656
|
if client = sock.kgio_tryaccept
|
674
657
|
process_client(client)
|
675
658
|
nr += 1
|
676
|
-
worker.tick =
|
659
|
+
worker.tick = time_now.to_i
|
677
660
|
end
|
678
661
|
break if nr < 0
|
679
662
|
end
|
@@ -690,7 +673,7 @@ class Unicorn::HttpServer
|
|
690
673
|
ppid == Process.ppid or return
|
691
674
|
|
692
675
|
# timeout used so we can detect parent death:
|
693
|
-
worker.tick =
|
676
|
+
worker.tick = time_now.to_i
|
694
677
|
ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
|
695
678
|
rescue => e
|
696
679
|
redo if nr < 0 && readers[0]
|
@@ -702,17 +685,17 @@ class Unicorn::HttpServer
|
|
702
685
|
# is no longer running.
|
703
686
|
def kill_worker(signal, wpid)
|
704
687
|
Process.kill(signal, wpid)
|
705
|
-
|
706
|
-
|
688
|
+
rescue Errno::ESRCH
|
689
|
+
worker = @workers.delete(wpid) and worker.close rescue nil
|
707
690
|
end
|
708
691
|
|
709
692
|
# delivers a signal to each worker
|
710
693
|
def kill_each_worker(signal)
|
711
|
-
|
694
|
+
@workers.keys.each { |wpid| kill_worker(signal, wpid) }
|
712
695
|
end
|
713
696
|
|
714
697
|
def soft_kill_each_worker(signal)
|
715
|
-
|
698
|
+
@workers.each_value { |worker| worker.soft_kill(signal) }
|
716
699
|
end
|
717
700
|
|
718
701
|
# unlinks a PID file at given +path+ if it contains the current PID
|
@@ -745,7 +728,7 @@ class Unicorn::HttpServer
|
|
745
728
|
config.commit!(self)
|
746
729
|
soft_kill_each_worker(:QUIT)
|
747
730
|
Unicorn::Util.reopen_logs
|
748
|
-
self.app = orig_app
|
731
|
+
self.app = @orig_app
|
749
732
|
build_app! if preload_app
|
750
733
|
logger.info "done reloading config_file=#{config.config_file}"
|
751
734
|
rescue StandardError, LoadError, SyntaxError => e
|
@@ -782,10 +765,10 @@ class Unicorn::HttpServer
|
|
782
765
|
def inherit_listeners!
|
783
766
|
# inherit sockets from parents, they need to be plain Socket objects
|
784
767
|
# before they become Kgio::UNIXServer or Kgio::TCPServer
|
785
|
-
inherited = ENV['UNICORN_FD'].to_s.split(
|
768
|
+
inherited = ENV['UNICORN_FD'].to_s.split(',').map do |fd|
|
786
769
|
io = Socket.for_fd(fd.to_i)
|
787
770
|
set_server_sockopt(io, listener_opts[sock_name(io)])
|
788
|
-
|
771
|
+
io.autoclose = false
|
789
772
|
logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
|
790
773
|
server_cast(io)
|
791
774
|
end
|
@@ -810,8 +793,20 @@ class Unicorn::HttpServer
|
|
810
793
|
# call only after calling inherit_listeners!
|
811
794
|
# This binds any listeners we did NOT inherit from the parent
|
812
795
|
def bind_new_listeners!
|
813
|
-
NEW_LISTENERS.each { |addr| listen(addr) }
|
796
|
+
NEW_LISTENERS.each { |addr| listen(addr) }.clear
|
814
797
|
raise ArgumentError, "no listeners" if LISTENERS.empty?
|
815
|
-
|
798
|
+
end
|
799
|
+
|
800
|
+
# try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
|
801
|
+
# offset adjustments and generates less garbage (Float vs Time object)
|
802
|
+
begin
|
803
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
804
|
+
def time_now
|
805
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
806
|
+
end
|
807
|
+
rescue NameError, NoMethodError
|
808
|
+
def time_now # Ruby <= 2.0
|
809
|
+
Time.now
|
810
|
+
end
|
816
811
|
end
|
817
812
|
end
|