puma 4.3.3-java → 5.0.0.beta2-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +79 -8
- data/LICENSE +23 -20
- data/README.md +18 -12
- data/docs/architecture.md +3 -3
- data/docs/deployment.md +9 -3
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/signals.md +5 -4
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +4 -3
- data/ext/puma_http11/http11_parser.c +3 -1
- data/ext/puma_http11/http11_parser.rl +3 -1
- data/ext/puma_http11/mini_ssl.c +12 -2
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +37 -6
- data/ext/puma_http11/puma_http11.c +3 -38
- data/lib/puma.rb +5 -0
- data/lib/puma/app/status.rb +18 -3
- data/lib/puma/binder.rb +66 -63
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +64 -14
- data/lib/puma/cluster.rb +183 -74
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +30 -42
- data/lib/puma/const.rb +2 -3
- data/lib/puma/control_cli.rb +27 -17
- data/lib/puma/detect.rb +8 -0
- data/lib/puma/dsl.rb +72 -36
- data/lib/puma/error_logger.rb +96 -0
- data/lib/puma/events.rb +33 -31
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +46 -31
- data/lib/puma/minissl.rb +47 -10
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +8 -3
- data/lib/puma/runner.rb +6 -35
- data/lib/puma/server.rb +138 -182
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +6 -3
- data/lib/puma/thread_pool.rb +90 -49
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +18 -21
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
data/lib/puma/minissl.rb
CHANGED
@@ -5,8 +5,18 @@ begin
|
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
|
+
# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
|
9
|
+
require 'puma/puma_http11'
|
10
|
+
|
8
11
|
module Puma
|
9
12
|
module MiniSSL
|
13
|
+
|
14
|
+
# define constant at runtime, as it's easy to determine at built time,
|
15
|
+
# but Puma could (it shouldn't) be loaded with an older OpenSSL version
|
16
|
+
HAS_TLS1_3 = !IS_JRUBY &&
|
17
|
+
(OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
|
18
|
+
(OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1
|
19
|
+
|
10
20
|
class Socket
|
11
21
|
def initialize(socket, engine)
|
12
22
|
@socket = socket
|
@@ -22,6 +32,24 @@ module Puma
|
|
22
32
|
@socket.closed?
|
23
33
|
end
|
24
34
|
|
35
|
+
# returns a two element array
|
36
|
+
# first is protocol version (SSL_get_version)
|
37
|
+
# second is 'handshake' state (SSL_state_string)
|
38
|
+
#
|
39
|
+
# used for dropping tcp connections to ssl
|
40
|
+
# see OpenSSL ssl/ssl_stat.c SSL_state_string for info
|
41
|
+
#
|
42
|
+
def ssl_version_state
|
43
|
+
IS_JRUBY ? [nil, nil] : @engine.ssl_vers_st
|
44
|
+
end
|
45
|
+
|
46
|
+
# used to check the handshake status, in particular when a TCP connection
|
47
|
+
# is made with TLSv1.3 as an available protocol
|
48
|
+
def bad_tlsv1_3?
|
49
|
+
HAS_TLS1_3 && @engine.ssl_vers_st == ['TLSv1.3', 'SSLERR']
|
50
|
+
end
|
51
|
+
private :bad_tlsv1_3?
|
52
|
+
|
25
53
|
def readpartial(size)
|
26
54
|
while true
|
27
55
|
output = @engine.read
|
@@ -41,6 +69,7 @@ module Puma
|
|
41
69
|
|
42
70
|
def engine_read_all
|
43
71
|
output = @engine.read
|
72
|
+
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
44
73
|
while output and additional_output = @engine.read
|
45
74
|
output << additional_output
|
46
75
|
end
|
@@ -125,11 +154,14 @@ module Puma
|
|
125
154
|
|
126
155
|
def read_and_drop(timeout = 1)
|
127
156
|
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
157
|
+
case @socket.read_nonblock(1024, exception: false)
|
158
|
+
when nil
|
159
|
+
:eof
|
160
|
+
when :wait_readable
|
161
|
+
:eagain
|
162
|
+
else
|
163
|
+
:drop
|
164
|
+
end
|
133
165
|
end
|
134
166
|
|
135
167
|
def should_drop_bytes?
|
@@ -141,9 +173,7 @@ module Puma
|
|
141
173
|
# Read any drop any partially initialized sockets and any received bytes during shutdown.
|
142
174
|
# Don't let this socket hold this loop forever.
|
143
175
|
# If it can't send more packets within 1s, then give up.
|
144
|
-
while should_drop_bytes?
|
145
|
-
return if [:timeout, :eof].include?(read_and_drop(1))
|
146
|
-
end
|
176
|
+
return if [:timeout, :eof].include?(read_and_drop(1)) while should_drop_bytes?
|
147
177
|
rescue IOError, SystemCallError
|
148
178
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
149
179
|
# nothing
|
@@ -166,7 +196,10 @@ module Puma
|
|
166
196
|
end
|
167
197
|
end
|
168
198
|
|
169
|
-
if
|
199
|
+
if IS_JRUBY
|
200
|
+
OPENSSL_NO_SSL3 = false
|
201
|
+
OPENSSL_NO_TLS1 = false
|
202
|
+
|
170
203
|
class SSLError < StandardError
|
171
204
|
# Define this for jruby even though it isn't used.
|
172
205
|
end
|
@@ -183,7 +216,7 @@ module Puma
|
|
183
216
|
@no_tlsv1_1 = false
|
184
217
|
end
|
185
218
|
|
186
|
-
if
|
219
|
+
if IS_JRUBY
|
187
220
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
188
221
|
attr_reader :keystore
|
189
222
|
attr_accessor :keystore_pass
|
@@ -270,6 +303,10 @@ module Puma
|
|
270
303
|
Socket.new io, engine
|
271
304
|
end
|
272
305
|
|
306
|
+
def addr
|
307
|
+
@socket.addr
|
308
|
+
end
|
309
|
+
|
273
310
|
def close
|
274
311
|
@socket.close unless @socket.closed? # closed? call is for Windows
|
275
312
|
end
|
data/lib/puma/null_io.rb
CHANGED
data/lib/puma/plugin.rb
CHANGED
@@ -10,7 +10,7 @@ module Puma
|
|
10
10
|
|
11
11
|
def create(name)
|
12
12
|
if cls = Plugins.find(name)
|
13
|
-
plugin = cls.new
|
13
|
+
plugin = cls.new
|
14
14
|
@instances << plugin
|
15
15
|
return plugin
|
16
16
|
end
|
@@ -104,17 +104,8 @@ module Puma
|
|
104
104
|
Plugins.register name, cls
|
105
105
|
end
|
106
106
|
|
107
|
-
def initialize(loader)
|
108
|
-
@loader = loader
|
109
|
-
end
|
110
|
-
|
111
107
|
def in_background(&blk)
|
112
108
|
Plugins.add_background blk
|
113
109
|
end
|
114
|
-
|
115
|
-
def workers_supported?
|
116
|
-
return false if Puma.jruby? || Puma.windows?
|
117
|
-
true
|
118
|
-
end
|
119
110
|
end
|
120
111
|
end
|
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/rack/builder.rb
CHANGED
@@ -67,10 +67,6 @@ module Puma::Rack
|
|
67
67
|
options[:environment] = e
|
68
68
|
}
|
69
69
|
|
70
|
-
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
|
71
|
-
options[:daemonize] = d ? true : false
|
72
|
-
}
|
73
|
-
|
74
70
|
opts.on("-P", "--pid FILE", "file to store PID") { |f|
|
75
71
|
options[:pid] = ::File.expand_path(f)
|
76
72
|
}
|
data/lib/puma/reactor.rb
CHANGED
@@ -189,7 +189,12 @@ module Puma
|
|
189
189
|
if submon.value == @ready
|
190
190
|
false
|
191
191
|
else
|
192
|
-
submon.value.
|
192
|
+
if submon.value.can_close?
|
193
|
+
submon.value.close
|
194
|
+
else
|
195
|
+
# Pass remaining open client connections to the thread pool.
|
196
|
+
@app_pool << submon.value
|
197
|
+
end
|
193
198
|
begin
|
194
199
|
selector.deregister submon.value
|
195
200
|
rescue IOError
|
@@ -247,7 +252,7 @@ module Puma
|
|
247
252
|
c.close
|
248
253
|
clear_monitor mon
|
249
254
|
|
250
|
-
@events.ssl_error
|
255
|
+
@events.ssl_error e, addr, cert
|
251
256
|
|
252
257
|
# The client doesn't know HTTP well
|
253
258
|
rescue HttpParserError => e
|
@@ -258,7 +263,7 @@ module Puma
|
|
258
263
|
|
259
264
|
clear_monitor mon
|
260
265
|
|
261
|
-
@events.parse_error
|
266
|
+
@events.parse_error e, c
|
262
267
|
rescue StandardError => e
|
263
268
|
@server.lowlevel_error(e, c.env)
|
264
269
|
|
data/lib/puma/runner.rb
CHANGED
@@ -18,10 +18,6 @@ module Puma
|
|
18
18
|
@started_at = Time.now
|
19
19
|
end
|
20
20
|
|
21
|
-
def daemon?
|
22
|
-
@options[:daemon]
|
23
|
-
end
|
24
|
-
|
25
21
|
def development?
|
26
22
|
@options[:environment] == "development"
|
27
23
|
end
|
@@ -34,7 +30,7 @@ module Puma
|
|
34
30
|
@events.log str
|
35
31
|
end
|
36
32
|
|
37
|
-
def
|
33
|
+
def stop_control
|
38
34
|
@control.stop(true) if @control
|
39
35
|
end
|
40
36
|
|
@@ -52,8 +48,6 @@ module Puma
|
|
52
48
|
|
53
49
|
require 'puma/app/status'
|
54
50
|
|
55
|
-
uri = URI.parse str
|
56
|
-
|
57
51
|
if token = @options[:control_auth_token]
|
58
52
|
token = nil if token.empty? || token == 'none'
|
59
53
|
end
|
@@ -64,30 +58,16 @@ module Puma
|
|
64
58
|
control.min_threads = 0
|
65
59
|
control.max_threads = 1
|
66
60
|
|
67
|
-
|
68
|
-
when "ssl"
|
69
|
-
log "* Starting control server on #{str}"
|
70
|
-
params = Util.parse_query uri.query
|
71
|
-
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
72
|
-
|
73
|
-
control.add_ssl_listener uri.host, uri.port, ctx
|
74
|
-
when "tcp"
|
75
|
-
log "* Starting control server on #{str}"
|
76
|
-
control.add_tcp_listener uri.host, uri.port
|
77
|
-
when "unix"
|
78
|
-
log "* Starting control server on #{str}"
|
79
|
-
path = "#{uri.host}#{uri.path}"
|
80
|
-
mask = @options[:control_url_umask]
|
81
|
-
|
82
|
-
control.add_unix_listener path, mask
|
83
|
-
else
|
84
|
-
error "Invalid control URI: #{str}"
|
85
|
-
end
|
61
|
+
control.binder.parse [str], self, 'Starting control server'
|
86
62
|
|
87
63
|
control.run
|
88
64
|
@control = control
|
89
65
|
end
|
90
66
|
|
67
|
+
def close_control_listeners
|
68
|
+
@control.binder.close_listeners if @control
|
69
|
+
end
|
70
|
+
|
91
71
|
def ruby_engine
|
92
72
|
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
93
73
|
"ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
@@ -108,10 +88,6 @@ module Puma
|
|
108
88
|
log "* Version #{Puma::Const::PUMA_VERSION} (#{ruby_engine}), codename: #{Puma::Const::CODE_NAME}"
|
109
89
|
log "* Min threads: #{min_t}, max threads: #{max_t}"
|
110
90
|
log "* Environment: #{ENV['RACK_ENV']}"
|
111
|
-
|
112
|
-
if @options[:mode] == :tcp
|
113
|
-
log "* Mode: Lopez Express (tcp)"
|
114
|
-
end
|
115
91
|
end
|
116
92
|
|
117
93
|
def redirected_io?
|
@@ -150,7 +126,6 @@ module Puma
|
|
150
126
|
exit 1
|
151
127
|
end
|
152
128
|
|
153
|
-
# Load the app before we daemonize.
|
154
129
|
begin
|
155
130
|
@app = @launcher.config.app
|
156
131
|
rescue Exception => e
|
@@ -174,10 +149,6 @@ module Puma
|
|
174
149
|
server.max_threads = max_t
|
175
150
|
server.inherit_binder @launcher.binder
|
176
151
|
|
177
|
-
if @options[:mode] == :tcp
|
178
|
-
server.tcp_mode!
|
179
|
-
end
|
180
|
-
|
181
152
|
if @options[:early_hints]
|
182
153
|
server.early_hints = true
|
183
154
|
end
|
data/lib/puma/server.rb
CHANGED
@@ -11,6 +11,7 @@ require 'puma/client'
|
|
11
11
|
require 'puma/binder'
|
12
12
|
require 'puma/accept_nonblock'
|
13
13
|
require 'puma/util'
|
14
|
+
require 'puma/io_buffer'
|
14
15
|
|
15
16
|
require 'puma/puma_http11'
|
16
17
|
|
@@ -36,6 +37,7 @@ module Puma
|
|
36
37
|
|
37
38
|
attr_reader :thread
|
38
39
|
attr_reader :events
|
40
|
+
attr_reader :requests_count
|
39
41
|
attr_accessor :app
|
40
42
|
|
41
43
|
attr_accessor :min_threads
|
@@ -57,8 +59,7 @@ module Puma
|
|
57
59
|
@app = app
|
58
60
|
@events = events
|
59
61
|
|
60
|
-
@check, @notify =
|
61
|
-
|
62
|
+
@check, @notify = nil
|
62
63
|
@status = :stop
|
63
64
|
|
64
65
|
@min_threads = 0
|
@@ -85,24 +86,34 @@ module Puma
|
|
85
86
|
@mode = :http
|
86
87
|
|
87
88
|
@precheck_closing = true
|
89
|
+
|
90
|
+
@requests_count = 0
|
88
91
|
end
|
89
92
|
|
90
93
|
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
91
94
|
|
92
|
-
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :
|
95
|
+
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_ports
|
93
96
|
|
94
97
|
def inherit_binder(bind)
|
95
98
|
@binder = bind
|
96
99
|
end
|
97
100
|
|
98
|
-
|
99
|
-
|
101
|
+
class << self
|
102
|
+
# :nodoc:
|
103
|
+
def tcp_cork_supported?
|
104
|
+
RbConfig::CONFIG['host_os'] =~ /linux/ &&
|
105
|
+
Socket.const_defined?(:IPPROTO_TCP) &&
|
106
|
+
Socket.const_defined?(:TCP_CORK) &&
|
107
|
+
Socket.const_defined?(:SOL_TCP) &&
|
108
|
+
Socket.const_defined?(:TCP_INFO)
|
109
|
+
end
|
110
|
+
private :tcp_cork_supported?
|
100
111
|
end
|
101
112
|
|
102
113
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
103
114
|
# packetizes our stream. This improves both latency and throughput.
|
104
115
|
#
|
105
|
-
if
|
116
|
+
if tcp_cork_supported?
|
106
117
|
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
107
118
|
|
108
119
|
# 6 == Socket::IPPROTO_TCP
|
@@ -110,7 +121,7 @@ module Puma
|
|
110
121
|
# 1/0 == turn on/off
|
111
122
|
def cork_socket(socket)
|
112
123
|
begin
|
113
|
-
socket.setsockopt(
|
124
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if socket.kind_of? TCPSocket
|
114
125
|
rescue IOError, SystemCallError
|
115
126
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
116
127
|
end
|
@@ -118,7 +129,7 @@ module Puma
|
|
118
129
|
|
119
130
|
def uncork_socket(socket)
|
120
131
|
begin
|
121
|
-
socket.setsockopt(
|
132
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if socket.kind_of? TCPSocket
|
122
133
|
rescue IOError, SystemCallError
|
123
134
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
124
135
|
end
|
@@ -172,107 +183,6 @@ module Puma
|
|
172
183
|
@thread_pool and @thread_pool.pool_capacity
|
173
184
|
end
|
174
185
|
|
175
|
-
# Lopez Mode == raw tcp apps
|
176
|
-
|
177
|
-
def run_lopez_mode(background=true)
|
178
|
-
@thread_pool = ThreadPool.new(@min_threads,
|
179
|
-
@max_threads,
|
180
|
-
Hash) do |client, tl|
|
181
|
-
|
182
|
-
io = client.to_io
|
183
|
-
addr = io.peeraddr.last
|
184
|
-
|
185
|
-
if addr.empty?
|
186
|
-
# Set unix socket addrs to localhost
|
187
|
-
addr = "127.0.0.1:0"
|
188
|
-
else
|
189
|
-
addr = "#{addr}:#{io.peeraddr[1]}"
|
190
|
-
end
|
191
|
-
|
192
|
-
env = { 'thread' => tl, REMOTE_ADDR => addr }
|
193
|
-
|
194
|
-
begin
|
195
|
-
@app.call env, client.to_io
|
196
|
-
rescue Object => e
|
197
|
-
STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
|
198
|
-
STDERR.puts e.backtrace
|
199
|
-
end
|
200
|
-
|
201
|
-
client.close unless env['detach']
|
202
|
-
end
|
203
|
-
|
204
|
-
@events.fire :state, :running
|
205
|
-
|
206
|
-
if background
|
207
|
-
@thread = Thread.new do
|
208
|
-
Puma.set_thread_name "server"
|
209
|
-
handle_servers_lopez_mode
|
210
|
-
end
|
211
|
-
return @thread
|
212
|
-
else
|
213
|
-
handle_servers_lopez_mode
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def handle_servers_lopez_mode
|
218
|
-
begin
|
219
|
-
check = @check
|
220
|
-
sockets = [check] + @binder.ios
|
221
|
-
pool = @thread_pool
|
222
|
-
|
223
|
-
while @status == :run
|
224
|
-
begin
|
225
|
-
ios = IO.select sockets
|
226
|
-
ios.first.each do |sock|
|
227
|
-
if sock == check
|
228
|
-
break if handle_check
|
229
|
-
else
|
230
|
-
begin
|
231
|
-
if io = sock.accept_nonblock
|
232
|
-
client = Client.new io, nil
|
233
|
-
pool << client
|
234
|
-
end
|
235
|
-
rescue SystemCallError
|
236
|
-
# nothing
|
237
|
-
rescue Errno::ECONNABORTED
|
238
|
-
# client closed the socket even before accept
|
239
|
-
begin
|
240
|
-
io.close
|
241
|
-
rescue
|
242
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
rescue Object => e
|
248
|
-
@events.unknown_error self, e, "Listen loop"
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
@events.fire :state, @status
|
253
|
-
|
254
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
255
|
-
|
256
|
-
rescue Exception => e
|
257
|
-
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
258
|
-
STDERR.puts e.backtrace
|
259
|
-
ensure
|
260
|
-
begin
|
261
|
-
@check.close
|
262
|
-
rescue
|
263
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
264
|
-
end
|
265
|
-
|
266
|
-
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
-
begin
|
268
|
-
@notify.close
|
269
|
-
rescue IOError
|
270
|
-
# no biggy
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
@events.fire :state, :done
|
275
|
-
end
|
276
186
|
# Runs the server.
|
277
187
|
#
|
278
188
|
# If +background+ is true (the default) then a thread is spun
|
@@ -286,15 +196,9 @@ module Puma
|
|
286
196
|
|
287
197
|
@status = :run
|
288
198
|
|
289
|
-
if @mode == :tcp
|
290
|
-
return run_lopez_mode(background)
|
291
|
-
end
|
292
|
-
|
293
|
-
queue_requests = @queue_requests
|
294
|
-
|
295
199
|
@thread_pool = ThreadPool.new(@min_threads,
|
296
200
|
@max_threads,
|
297
|
-
IOBuffer) do |client, buffer|
|
201
|
+
::Puma::IOBuffer) do |client, buffer|
|
298
202
|
|
299
203
|
# Advertise this server into the thread
|
300
204
|
Thread.current[ThreadLocalKey] = self
|
@@ -302,10 +206,10 @@ module Puma
|
|
302
206
|
process_now = false
|
303
207
|
|
304
208
|
begin
|
305
|
-
if queue_requests
|
209
|
+
if @queue_requests
|
306
210
|
process_now = client.eagerly_finish
|
307
211
|
else
|
308
|
-
client.finish
|
212
|
+
client.finish(@first_data_timeout)
|
309
213
|
process_now = true
|
310
214
|
end
|
311
215
|
rescue MiniSSL::SSLError => e
|
@@ -315,14 +219,16 @@ module Puma
|
|
315
219
|
|
316
220
|
client.close
|
317
221
|
|
318
|
-
@events.ssl_error
|
222
|
+
@events.ssl_error e, addr, cert
|
319
223
|
rescue HttpParserError => e
|
320
224
|
client.write_error(400)
|
321
225
|
client.close
|
322
226
|
|
323
|
-
@events.parse_error
|
324
|
-
rescue ConnectionError, EOFError
|
227
|
+
@events.parse_error e, client
|
228
|
+
rescue ConnectionError, EOFError => e
|
325
229
|
client.close
|
230
|
+
|
231
|
+
@events.connection_error e, client
|
326
232
|
else
|
327
233
|
if process_now
|
328
234
|
process_client client, buffer
|
@@ -331,11 +237,14 @@ module Puma
|
|
331
237
|
@reactor.add client
|
332
238
|
end
|
333
239
|
end
|
240
|
+
|
241
|
+
process_now
|
334
242
|
end
|
335
243
|
|
244
|
+
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
336
245
|
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
337
246
|
|
338
|
-
if queue_requests
|
247
|
+
if @queue_requests
|
339
248
|
@reactor = Reactor.new self, @thread_pool
|
340
249
|
@reactor.run_in_thread
|
341
250
|
end
|
@@ -362,6 +271,7 @@ module Puma
|
|
362
271
|
end
|
363
272
|
|
364
273
|
def handle_servers
|
274
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
365
275
|
begin
|
366
276
|
check = @check
|
367
277
|
sockets = [check] + @binder.ios
|
@@ -385,51 +295,49 @@ module Puma
|
|
385
295
|
if sock == check
|
386
296
|
break if handle_check
|
387
297
|
else
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
pool << client
|
398
|
-
busy_threads = pool.wait_until_not_full
|
399
|
-
if busy_threads == 0
|
400
|
-
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
401
|
-
end
|
402
|
-
end
|
403
|
-
rescue SystemCallError
|
404
|
-
# nothing
|
405
|
-
rescue Errno::ECONNABORTED
|
406
|
-
# client closed the socket even before accept
|
407
|
-
begin
|
408
|
-
io.close
|
409
|
-
rescue
|
410
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
411
|
-
end
|
298
|
+
pool.wait_until_not_full
|
299
|
+
pool.wait_for_less_busy_worker(
|
300
|
+
@options[:wait_for_less_busy_worker].to_f)
|
301
|
+
|
302
|
+
io = begin
|
303
|
+
sock.accept_nonblock
|
304
|
+
rescue IO::WaitReadable
|
305
|
+
next
|
412
306
|
end
|
307
|
+
client = Client.new io, @binder.env(sock)
|
308
|
+
if remote_addr_value
|
309
|
+
client.peerip = remote_addr_value
|
310
|
+
elsif remote_addr_header
|
311
|
+
client.remote_addr_header = remote_addr_header
|
312
|
+
end
|
313
|
+
pool << client
|
413
314
|
end
|
414
315
|
end
|
415
316
|
rescue Object => e
|
416
|
-
@events.unknown_error
|
317
|
+
@events.unknown_error e, nil, "Listen loop"
|
417
318
|
end
|
418
319
|
end
|
419
320
|
|
420
321
|
@events.fire :state, @status
|
421
322
|
|
422
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
423
323
|
if queue_requests
|
324
|
+
@queue_requests = false
|
424
325
|
@reactor.clear!
|
425
326
|
@reactor.shutdown
|
426
327
|
end
|
328
|
+
graceful_shutdown if @status == :stop || @status == :restart
|
427
329
|
rescue Exception => e
|
428
|
-
|
429
|
-
STDERR.puts e.backtrace
|
330
|
+
@events.unknown_error e, nil, "Exception handling servers"
|
430
331
|
ensure
|
431
|
-
|
332
|
+
begin
|
333
|
+
@check.close unless @check.closed?
|
334
|
+
rescue Errno::EBADF, RuntimeError
|
335
|
+
# RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
|
336
|
+
# Errno::EBADF is infrequently raised
|
337
|
+
end
|
432
338
|
@notify.close
|
339
|
+
@notify = nil
|
340
|
+
@check = nil
|
433
341
|
end
|
434
342
|
|
435
343
|
@events.fire :state, :done
|
@@ -476,7 +384,6 @@ module Puma
|
|
476
384
|
close_socket = false
|
477
385
|
return
|
478
386
|
when true
|
479
|
-
return unless @queue_requests
|
480
387
|
buffer.reset
|
481
388
|
|
482
389
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
@@ -494,6 +401,7 @@ module Puma
|
|
494
401
|
end
|
495
402
|
|
496
403
|
unless client.reset(check_for_more_data)
|
404
|
+
return unless @queue_requests
|
497
405
|
close_socket = false
|
498
406
|
client.set_timeout @persistent_timeout
|
499
407
|
@reactor.add client
|
@@ -516,7 +424,7 @@ module Puma
|
|
516
424
|
|
517
425
|
close_socket = true
|
518
426
|
|
519
|
-
@events.ssl_error
|
427
|
+
@events.ssl_error e, addr, cert
|
520
428
|
|
521
429
|
# The client doesn't know HTTP well
|
522
430
|
rescue HttpParserError => e
|
@@ -524,7 +432,7 @@ module Puma
|
|
524
432
|
|
525
433
|
client.write_error(400)
|
526
434
|
|
527
|
-
@events.parse_error
|
435
|
+
@events.parse_error e, client
|
528
436
|
|
529
437
|
# Server error
|
530
438
|
rescue StandardError => e
|
@@ -532,8 +440,7 @@ module Puma
|
|
532
440
|
|
533
441
|
client.write_error(500)
|
534
442
|
|
535
|
-
@events.unknown_error
|
536
|
-
|
443
|
+
@events.unknown_error e, nil, "Read"
|
537
444
|
ensure
|
538
445
|
buffer.reset
|
539
446
|
|
@@ -543,7 +450,7 @@ module Puma
|
|
543
450
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
544
451
|
# Already closed
|
545
452
|
rescue StandardError => e
|
546
|
-
@events.unknown_error
|
453
|
+
@events.unknown_error e, nil, "Client"
|
547
454
|
end
|
548
455
|
end
|
549
456
|
end
|
@@ -579,7 +486,7 @@ module Puma
|
|
579
486
|
|
580
487
|
env[PATH_INFO] = env[REQUEST_PATH]
|
581
488
|
|
582
|
-
# From
|
489
|
+
# From https://www.ietf.org/rfc/rfc3875 :
|
583
490
|
# "Script authors should be aware that the REMOTE_ADDR and
|
584
491
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
585
492
|
# may not identify the ultimate source of the request.
|
@@ -626,6 +533,8 @@ module Puma
|
|
626
533
|
#
|
627
534
|
# Finally, it'll return +true+ on keep-alive connections.
|
628
535
|
def handle_request(req, lines)
|
536
|
+
@requests_count +=1
|
537
|
+
|
629
538
|
env = req.env
|
630
539
|
client = req.io
|
631
540
|
|
@@ -666,12 +575,44 @@ module Puma
|
|
666
575
|
end
|
667
576
|
|
668
577
|
fast_write client, "\r\n".freeze
|
669
|
-
rescue ConnectionError
|
578
|
+
rescue ConnectionError => e
|
579
|
+
@events.debug_error e
|
670
580
|
# noop, if we lost the socket we just won't send the early hints
|
671
581
|
end
|
672
582
|
}
|
673
583
|
end
|
674
584
|
|
585
|
+
# Fixup any headers with , in the name to have _ now. We emit
|
586
|
+
# headers with , in them during the parse phase to avoid ambiguity
|
587
|
+
# with the - to _ conversion for critical headers. But here for
|
588
|
+
# compatibility, we'll convert them back. This code is written to
|
589
|
+
# avoid allocation in the common case (ie there are no headers
|
590
|
+
# with , in their names), that's why it has the extra conditionals.
|
591
|
+
|
592
|
+
to_delete = nil
|
593
|
+
to_add = nil
|
594
|
+
|
595
|
+
env.each do |k,v|
|
596
|
+
if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
|
597
|
+
if to_delete
|
598
|
+
to_delete << k
|
599
|
+
else
|
600
|
+
to_delete = [k]
|
601
|
+
end
|
602
|
+
|
603
|
+
unless to_add
|
604
|
+
to_add = {}
|
605
|
+
end
|
606
|
+
|
607
|
+
to_add[k.tr(",", "_")] = v
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
if to_delete
|
612
|
+
to_delete.each { |k| env.delete(k) }
|
613
|
+
env.merge! to_add
|
614
|
+
end
|
615
|
+
|
675
616
|
# A rack extension. If the app writes #call'ables to this
|
676
617
|
# array, we will invoke them when the request is done.
|
677
618
|
#
|
@@ -693,17 +634,14 @@ module Puma
|
|
693
634
|
return :async
|
694
635
|
end
|
695
636
|
rescue ThreadPool::ForceShutdown => e
|
696
|
-
@events.
|
697
|
-
@events.
|
698
|
-
|
699
|
-
status = 503
|
700
|
-
headers = {}
|
701
|
-
res_body = ["Request was internally terminated early\n"]
|
637
|
+
@events.unknown_error e, req, "Rack app"
|
638
|
+
@events.log "Detected force shutdown of a thread"
|
702
639
|
|
640
|
+
status, headers, res_body = lowlevel_error(e, env, 503)
|
703
641
|
rescue Exception => e
|
704
|
-
@events.unknown_error
|
642
|
+
@events.unknown_error e, req, "Rack app"
|
705
643
|
|
706
|
-
status, headers, res_body = lowlevel_error(e, env)
|
644
|
+
status, headers, res_body = lowlevel_error(e, env, 500)
|
707
645
|
end
|
708
646
|
|
709
647
|
content_length = nil
|
@@ -718,10 +656,10 @@ module Puma
|
|
718
656
|
line_ending = LINE_END
|
719
657
|
colon = COLON
|
720
658
|
|
721
|
-
http_11 =
|
659
|
+
http_11 = env[HTTP_VERSION] == HTTP_11
|
660
|
+
if http_11
|
722
661
|
allow_chunked = true
|
723
662
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
|
724
|
-
include_keepalive_header = false
|
725
663
|
|
726
664
|
# An optimization. The most common response is 200, so we can
|
727
665
|
# reply with the proper 200 status without having to compute
|
@@ -735,11 +673,9 @@ module Puma
|
|
735
673
|
|
736
674
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
737
675
|
end
|
738
|
-
true
|
739
676
|
else
|
740
677
|
allow_chunked = false
|
741
678
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
|
742
|
-
include_keepalive_header = keep_alive
|
743
679
|
|
744
680
|
# Same optimization as above for HTTP/1.1
|
745
681
|
#
|
@@ -751,9 +687,12 @@ module Puma
|
|
751
687
|
|
752
688
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
753
689
|
end
|
754
|
-
false
|
755
690
|
end
|
756
691
|
|
692
|
+
# regardless of what the client wants, we always close the connection
|
693
|
+
# if running without request queueing
|
694
|
+
keep_alive &&= @queue_requests
|
695
|
+
|
757
696
|
response_hijack = nil
|
758
697
|
|
759
698
|
headers.each do |k, vs|
|
@@ -780,10 +719,15 @@ module Puma
|
|
780
719
|
end
|
781
720
|
end
|
782
721
|
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
722
|
+
# HTTP/1.1 & 1.0 assume different defaults:
|
723
|
+
# - HTTP 1.0 assumes the connection will be closed if not specified
|
724
|
+
# - HTTP 1.1 assumes the connection will be kept alive if not specified.
|
725
|
+
# Only set the header if we're doing something which is not the default
|
726
|
+
# for this protocol version
|
727
|
+
if http_11
|
728
|
+
lines << CONNECTION_CLOSE if !keep_alive
|
729
|
+
else
|
730
|
+
lines << CONNECTION_KEEP_ALIVE if keep_alive
|
787
731
|
end
|
788
732
|
|
789
733
|
if no_body
|
@@ -910,19 +854,21 @@ module Puma
|
|
910
854
|
|
911
855
|
# A fallback rack response if +@app+ raises as exception.
|
912
856
|
#
|
913
|
-
def lowlevel_error(e, env)
|
857
|
+
def lowlevel_error(e, env, status=500)
|
914
858
|
if handler = @options[:lowlevel_error_handler]
|
915
859
|
if handler.arity == 1
|
916
860
|
return handler.call(e)
|
917
|
-
|
861
|
+
elsif handler.arity == 2
|
918
862
|
return handler.call(e, env)
|
863
|
+
else
|
864
|
+
return handler.call(e, env, status)
|
919
865
|
end
|
920
866
|
end
|
921
867
|
|
922
868
|
if @leak_stack_on_error
|
923
|
-
[
|
869
|
+
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
|
924
870
|
else
|
925
|
-
[
|
871
|
+
[status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
|
926
872
|
end
|
927
873
|
end
|
928
874
|
|
@@ -980,9 +926,10 @@ module Puma
|
|
980
926
|
end
|
981
927
|
|
982
928
|
def notify_safely(message)
|
929
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
983
930
|
begin
|
984
931
|
@notify << message
|
985
|
-
rescue IOError
|
932
|
+
rescue IOError, NoMethodError, Errno::EPIPE
|
986
933
|
# The server, in another thread, is shutting down
|
987
934
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
988
935
|
rescue RuntimeError => e
|
@@ -1009,8 +956,9 @@ module Puma
|
|
1009
956
|
@thread.join if @thread && sync
|
1010
957
|
end
|
1011
958
|
|
1012
|
-
def begin_restart
|
959
|
+
def begin_restart(sync=false)
|
1013
960
|
notify_safely(RESTART_COMMAND)
|
961
|
+
@thread.join if @thread && sync
|
1014
962
|
end
|
1015
963
|
|
1016
964
|
def fast_write(io, str)
|
@@ -1048,5 +996,13 @@ module Puma
|
|
1048
996
|
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1049
997
|
end
|
1050
998
|
private :possible_header_injection?
|
999
|
+
|
1000
|
+
# List of methods invoked by #stats.
|
1001
|
+
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
|
1002
|
+
|
1003
|
+
# Returns a hash of stats about the running server for reporting purposes.
|
1004
|
+
def stats
|
1005
|
+
STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
|
1006
|
+
end
|
1051
1007
|
end
|
1052
1008
|
end
|