jun-puma 1.0.1-java → 1.0.2-java
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/lib/puma/puma_http11.jar +0 -0
- metadata +3 -81
- data/bin/puma-wild +0 -25
- data/docs/architecture.md +0 -74
- data/docs/compile_options.md +0 -55
- data/docs/deployment.md +0 -102
- data/docs/fork_worker.md +0 -31
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +0 -9
- data/docs/jungle/rc.d/README.md +0 -74
- data/docs/jungle/rc.d/puma +0 -61
- data/docs/jungle/rc.d/puma.conf +0 -10
- data/docs/kubernetes.md +0 -78
- data/docs/nginx.md +0 -80
- data/docs/plugins.md +0 -38
- data/docs/rails_dev_mode.md +0 -28
- data/docs/restart.md +0 -64
- data/docs/signals.md +0 -98
- data/docs/stats.md +0 -142
- data/docs/systemd.md +0 -244
- data/docs/testing_benchmarks_local_files.md +0 -150
- data/docs/testing_test_rackup_ci_files.md +0 -36
- data/ext/puma_http11/PumaHttp11Service.java +0 -17
- data/ext/puma_http11/ext_help.h +0 -15
- data/ext/puma_http11/http11_parser.c +0 -1057
- data/ext/puma_http11/http11_parser.h +0 -65
- data/ext/puma_http11/http11_parser.java.rl +0 -145
- data/ext/puma_http11/http11_parser.rl +0 -149
- data/ext/puma_http11/http11_parser_common.rl +0 -54
- data/ext/puma_http11/mini_ssl.c +0 -832
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -15
- data/ext/puma_http11/org/jruby/puma/Http11.java +0 -226
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +0 -455
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -508
- data/ext/puma_http11/puma_http11.c +0 -492
- data/lib/puma/app/status.rb +0 -96
- data/lib/puma/binder.rb +0 -501
- data/lib/puma/cli.rb +0 -243
- data/lib/puma/client.rb +0 -632
- data/lib/puma/cluster/worker.rb +0 -182
- data/lib/puma/cluster/worker_handle.rb +0 -97
- data/lib/puma/cluster.rb +0 -562
- data/lib/puma/commonlogger.rb +0 -115
- data/lib/puma/configuration.rb +0 -391
- data/lib/puma/const.rb +0 -289
- data/lib/puma/control_cli.rb +0 -316
- data/lib/puma/detect.rb +0 -45
- data/lib/puma/dsl.rb +0 -1204
- data/lib/puma/error_logger.rb +0 -113
- data/lib/puma/events.rb +0 -57
- data/lib/puma/io_buffer.rb +0 -46
- data/lib/puma/jruby_restart.rb +0 -27
- data/lib/puma/json_serialization.rb +0 -96
- data/lib/puma/launcher/bundle_pruner.rb +0 -104
- data/lib/puma/launcher.rb +0 -484
- data/lib/puma/log_writer.rb +0 -147
- data/lib/puma/minissl/context_builder.rb +0 -95
- data/lib/puma/minissl.rb +0 -458
- data/lib/puma/null_io.rb +0 -61
- data/lib/puma/plugin/systemd.rb +0 -90
- data/lib/puma/plugin/tmp_restart.rb +0 -36
- data/lib/puma/plugin.rb +0 -111
- data/lib/puma/rack/builder.rb +0 -297
- data/lib/puma/rack/urlmap.rb +0 -93
- data/lib/puma/rack_default.rb +0 -24
- data/lib/puma/reactor.rb +0 -125
- data/lib/puma/request.rb +0 -671
- data/lib/puma/runner.rb +0 -213
- data/lib/puma/sd_notify.rb +0 -149
- data/lib/puma/server.rb +0 -664
- data/lib/puma/single.rb +0 -69
- data/lib/puma/state_file.rb +0 -68
- data/lib/puma/thread_pool.rb +0 -434
- data/lib/puma/util.rb +0 -141
- data/lib/puma.rb +0 -78
- data/lib/rack/handler/puma.rb +0 -141
- data/tools/Dockerfile +0 -16
- data/tools/trickletest.rb +0 -44
data/lib/puma/server.rb
DELETED
@@ -1,664 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'stringio'
|
4
|
-
|
5
|
-
require_relative 'thread_pool'
|
6
|
-
require_relative 'const'
|
7
|
-
require_relative 'log_writer'
|
8
|
-
require_relative 'events'
|
9
|
-
require_relative 'null_io'
|
10
|
-
require_relative 'reactor'
|
11
|
-
require_relative 'client'
|
12
|
-
require_relative 'binder'
|
13
|
-
require_relative 'util'
|
14
|
-
require_relative 'request'
|
15
|
-
|
16
|
-
require 'socket'
|
17
|
-
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
18
|
-
|
19
|
-
module Puma
|
20
|
-
|
21
|
-
# The HTTP Server itself. Serves out a single Rack app.
|
22
|
-
#
|
23
|
-
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
24
|
-
# to generate one or more `Puma::Server` instances capable of handling requests.
|
25
|
-
# Each Puma process will contain one `Puma::Server` instance.
|
26
|
-
#
|
27
|
-
# The `Puma::Server` instance pulls requests from the socket, adds them to a
|
28
|
-
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
|
29
|
-
#
|
30
|
-
# Each `Puma::Server` will have one reactor and one thread pool.
|
31
|
-
class Server
|
32
|
-
include Puma::Const
|
33
|
-
include Request
|
34
|
-
|
35
|
-
attr_reader :thread
|
36
|
-
attr_reader :log_writer
|
37
|
-
attr_reader :events
|
38
|
-
attr_reader :min_threads, :max_threads # for #stats
|
39
|
-
attr_reader :requests_count # @version 5.0.0
|
40
|
-
attr_reader :idle_timeout_reached
|
41
|
-
|
42
|
-
# @todo the following may be deprecated in the future
|
43
|
-
attr_reader :auto_trim_time, :early_hints, :first_data_timeout,
|
44
|
-
:leak_stack_on_error,
|
45
|
-
:persistent_timeout, :reaping_time
|
46
|
-
|
47
|
-
attr_accessor :app
|
48
|
-
attr_accessor :binder
|
49
|
-
|
50
|
-
THREAD_LOCAL_KEY = :puma_server
|
51
|
-
|
52
|
-
# Create a server for the rack app +app+.
|
53
|
-
#
|
54
|
-
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
55
|
-
#
|
56
|
-
# +events+ is a Puma::Events object used to notify application status events.
|
57
|
-
#
|
58
|
-
# Server#run returns a thread that you can join on to wait for the server
|
59
|
-
# to do its work.
|
60
|
-
#
|
61
|
-
# @note Several instance variables exist so they are available for testing,
|
62
|
-
# and have default values set via +fetch+. Normally the values are set via
|
63
|
-
# `::Puma::Configuration.puma_default_options`.
|
64
|
-
#
|
65
|
-
# @note The `events` parameter is set to nil, and set to `Events.new` in code.
|
66
|
-
# Often `options` needs to be passed, but `events` does not. Using nil allows
|
67
|
-
# calling code to not require events.rb.
|
68
|
-
#
|
69
|
-
def initialize(app, events = nil, options = {})
|
70
|
-
@app = app
|
71
|
-
@events = events || Events.new
|
72
|
-
|
73
|
-
@check, @notify = nil
|
74
|
-
@status = :stop
|
75
|
-
|
76
|
-
@thread = nil
|
77
|
-
@thread_pool = nil
|
78
|
-
|
79
|
-
@options = if options.is_a?(UserFileDefaultOptions)
|
80
|
-
options
|
81
|
-
else
|
82
|
-
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
83
|
-
end
|
84
|
-
|
85
|
-
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
86
|
-
@early_hints = @options[:early_hints]
|
87
|
-
@first_data_timeout = @options[:first_data_timeout]
|
88
|
-
@persistent_timeout = @options[:persistent_timeout]
|
89
|
-
@idle_timeout = @options[:idle_timeout]
|
90
|
-
@min_threads = @options[:min_threads]
|
91
|
-
@max_threads = @options[:max_threads]
|
92
|
-
@queue_requests = @options[:queue_requests]
|
93
|
-
@max_fast_inline = @options[:max_fast_inline]
|
94
|
-
@io_selector_backend = @options[:io_selector_backend]
|
95
|
-
@http_content_length_limit = @options[:http_content_length_limit]
|
96
|
-
|
97
|
-
# make this a hash, since we prefer `key?` over `include?`
|
98
|
-
@supported_http_methods =
|
99
|
-
if @options[:supported_http_methods] == :any
|
100
|
-
:any
|
101
|
-
else
|
102
|
-
if (ary = @options[:supported_http_methods])
|
103
|
-
ary
|
104
|
-
else
|
105
|
-
SUPPORTED_HTTP_METHODS
|
106
|
-
end.sort.product([nil]).to_h.freeze
|
107
|
-
end
|
108
|
-
|
109
|
-
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
110
|
-
@leak_stack_on_error = @options[:environment] ? temp : true
|
111
|
-
|
112
|
-
@binder = Binder.new(log_writer)
|
113
|
-
|
114
|
-
ENV['RACK_ENV'] ||= "development"
|
115
|
-
|
116
|
-
@mode = :http
|
117
|
-
|
118
|
-
@precheck_closing = true
|
119
|
-
|
120
|
-
@requests_count = 0
|
121
|
-
|
122
|
-
@idle_timeout_reached = false
|
123
|
-
end
|
124
|
-
|
125
|
-
def inherit_binder(bind)
|
126
|
-
@binder = bind
|
127
|
-
end
|
128
|
-
|
129
|
-
class << self
|
130
|
-
# @!attribute [r] current
|
131
|
-
def current
|
132
|
-
Thread.current[THREAD_LOCAL_KEY]
|
133
|
-
end
|
134
|
-
|
135
|
-
# :nodoc:
|
136
|
-
# @version 5.0.0
|
137
|
-
def tcp_cork_supported?
|
138
|
-
Socket.const_defined?(:TCP_CORK) && Socket.const_defined?(:IPPROTO_TCP)
|
139
|
-
end
|
140
|
-
|
141
|
-
# :nodoc:
|
142
|
-
# @version 5.0.0
|
143
|
-
def closed_socket_supported?
|
144
|
-
Socket.const_defined?(:TCP_INFO) && Socket.const_defined?(:IPPROTO_TCP)
|
145
|
-
end
|
146
|
-
private :tcp_cork_supported?
|
147
|
-
private :closed_socket_supported?
|
148
|
-
end
|
149
|
-
|
150
|
-
# On Linux, use TCP_CORK to better control how the TCP stack
|
151
|
-
# packetizes our stream. This improves both latency and throughput.
|
152
|
-
# socket parameter may be an MiniSSL::Socket, so use to_io
|
153
|
-
#
|
154
|
-
if tcp_cork_supported?
|
155
|
-
# 6 == Socket::IPPROTO_TCP
|
156
|
-
# 3 == TCP_CORK
|
157
|
-
# 1/0 == turn on/off
|
158
|
-
def cork_socket(socket)
|
159
|
-
skt = socket.to_io
|
160
|
-
begin
|
161
|
-
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
|
162
|
-
rescue IOError, SystemCallError
|
163
|
-
Puma::Util.purge_interrupt_queue
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def uncork_socket(socket)
|
168
|
-
skt = socket.to_io
|
169
|
-
begin
|
170
|
-
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
|
171
|
-
rescue IOError, SystemCallError
|
172
|
-
Puma::Util.purge_interrupt_queue
|
173
|
-
end
|
174
|
-
end
|
175
|
-
else
|
176
|
-
def cork_socket(socket)
|
177
|
-
end
|
178
|
-
|
179
|
-
def uncork_socket(socket)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
if closed_socket_supported?
|
184
|
-
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
185
|
-
|
186
|
-
def closed_socket?(socket)
|
187
|
-
skt = socket.to_io
|
188
|
-
return false unless skt.kind_of?(TCPSocket) && @precheck_closing
|
189
|
-
|
190
|
-
begin
|
191
|
-
tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
192
|
-
rescue IOError, SystemCallError
|
193
|
-
Puma::Util.purge_interrupt_queue
|
194
|
-
@precheck_closing = false
|
195
|
-
false
|
196
|
-
else
|
197
|
-
state = tcp_info.unpack(UNPACK_TCP_STATE_FROM_TCP_INFO)[0]
|
198
|
-
# TIME_WAIT: 6, CLOSE: 7, CLOSE_WAIT: 8, LAST_ACK: 9, CLOSING: 11
|
199
|
-
(state >= 6 && state <= 9) || state == 11
|
200
|
-
end
|
201
|
-
end
|
202
|
-
else
|
203
|
-
def closed_socket?(socket)
|
204
|
-
false
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
# @!attribute [r] backlog
|
209
|
-
def backlog
|
210
|
-
@thread_pool&.backlog
|
211
|
-
end
|
212
|
-
|
213
|
-
# @!attribute [r] running
|
214
|
-
def running
|
215
|
-
@thread_pool&.spawned
|
216
|
-
end
|
217
|
-
|
218
|
-
|
219
|
-
# This number represents the number of requests that
|
220
|
-
# the server is capable of taking right now.
|
221
|
-
#
|
222
|
-
# For example if the number is 5 then it means
|
223
|
-
# there are 5 threads sitting idle ready to take
|
224
|
-
# a request. If one request comes in, then the
|
225
|
-
# value would be 4 until it finishes processing.
|
226
|
-
# @!attribute [r] pool_capacity
|
227
|
-
def pool_capacity
|
228
|
-
@thread_pool&.pool_capacity
|
229
|
-
end
|
230
|
-
|
231
|
-
# Runs the server.
|
232
|
-
#
|
233
|
-
# If +background+ is true (the default) then a thread is spun
|
234
|
-
# up in the background to handle requests. Otherwise requests
|
235
|
-
# are handled synchronously.
|
236
|
-
#
|
237
|
-
def run(background=true, thread_name: 'srv')
|
238
|
-
BasicSocket.do_not_reverse_lookup = true
|
239
|
-
|
240
|
-
@events.fire :state, :booting
|
241
|
-
|
242
|
-
@status = :run
|
243
|
-
|
244
|
-
@thread_pool = ThreadPool.new(thread_name, @options) { |client| process_client client }
|
245
|
-
|
246
|
-
if @queue_requests
|
247
|
-
@reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
|
248
|
-
@reactor.run
|
249
|
-
end
|
250
|
-
|
251
|
-
|
252
|
-
@thread_pool.auto_reap! if @options[:reaping_time]
|
253
|
-
@thread_pool.auto_trim! if @options[:auto_trim_time]
|
254
|
-
|
255
|
-
@check, @notify = Puma::Util.pipe unless @notify
|
256
|
-
|
257
|
-
@events.fire :state, :running
|
258
|
-
|
259
|
-
if background
|
260
|
-
@thread = Thread.new do
|
261
|
-
Puma.set_thread_name thread_name
|
262
|
-
handle_servers
|
263
|
-
end
|
264
|
-
return @thread
|
265
|
-
else
|
266
|
-
handle_servers
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
# This method is called from the Reactor thread when a queued Client receives data,
|
271
|
-
# times out, or when the Reactor is shutting down.
|
272
|
-
#
|
273
|
-
# It is responsible for ensuring that a request has been completely received
|
274
|
-
# before it starts to be processed by the ThreadPool. This may be known as read buffering.
|
275
|
-
# If read buffering is not done, and no other read buffering is performed (such as by an application server
|
276
|
-
# such as nginx) then the application would be subject to a slow client attack.
|
277
|
-
#
|
278
|
-
# For a graphical representation of how the request buffer works see [architecture.md](https://github.com/puma/puma/blob/master/docs/architecture.md#connection-pipeline).
|
279
|
-
#
|
280
|
-
# The method checks to see if it has the full header and body with
|
281
|
-
# the `Puma::Client#try_to_finish` method. If the full request has been sent,
|
282
|
-
# then the request is passed to the ThreadPool (`@thread_pool << client`)
|
283
|
-
# so that a "worker thread" can pick up the request and begin to execute application logic.
|
284
|
-
# The Client is then removed from the reactor (return `true`).
|
285
|
-
#
|
286
|
-
# If a client object times out, a 408 response is written, its connection is closed,
|
287
|
-
# and the object is removed from the reactor (return `true`).
|
288
|
-
#
|
289
|
-
# If the Reactor is shutting down, all Clients are either timed out or passed to the
|
290
|
-
# ThreadPool, depending on their current state (#can_close?).
|
291
|
-
#
|
292
|
-
# Otherwise, if the full request is not ready then the client will remain in the reactor
|
293
|
-
# (return `false`). When the client sends more data to the socket the `Puma::Client` object
|
294
|
-
# will wake up and again be checked to see if it's ready to be passed to the thread pool.
|
295
|
-
def reactor_wakeup(client)
|
296
|
-
shutdown = !@queue_requests
|
297
|
-
if client.try_to_finish || (shutdown && !client.can_close?)
|
298
|
-
@thread_pool << client
|
299
|
-
elsif shutdown || client.timeout == 0
|
300
|
-
client.timeout!
|
301
|
-
else
|
302
|
-
client.set_timeout(@first_data_timeout)
|
303
|
-
false
|
304
|
-
end
|
305
|
-
rescue StandardError => e
|
306
|
-
client_error(e, client)
|
307
|
-
client.close
|
308
|
-
true
|
309
|
-
end
|
310
|
-
|
311
|
-
def handle_servers
|
312
|
-
begin
|
313
|
-
check = @check
|
314
|
-
sockets = [check] + @binder.ios
|
315
|
-
pool = @thread_pool
|
316
|
-
queue_requests = @queue_requests
|
317
|
-
drain = @options[:drain_on_shutdown] ? 0 : nil
|
318
|
-
|
319
|
-
addr_send_name, addr_value = case @options[:remote_address]
|
320
|
-
when :value
|
321
|
-
[:peerip=, @options[:remote_address_value]]
|
322
|
-
when :header
|
323
|
-
[:remote_addr_header=, @options[:remote_address_header]]
|
324
|
-
when :proxy_protocol
|
325
|
-
[:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
|
326
|
-
else
|
327
|
-
[nil, nil]
|
328
|
-
end
|
329
|
-
|
330
|
-
while @status == :run || (drain && shutting_down?)
|
331
|
-
begin
|
332
|
-
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : @idle_timeout)
|
333
|
-
unless ios
|
334
|
-
unless shutting_down?
|
335
|
-
@idle_timeout_reached = true
|
336
|
-
@status = :stop
|
337
|
-
end
|
338
|
-
|
339
|
-
break
|
340
|
-
end
|
341
|
-
|
342
|
-
ios.first.each do |sock|
|
343
|
-
if sock == check
|
344
|
-
break if handle_check
|
345
|
-
else
|
346
|
-
pool.wait_until_not_full
|
347
|
-
pool.wait_for_less_busy_worker(@options[:wait_for_less_busy_worker])
|
348
|
-
|
349
|
-
io = begin
|
350
|
-
sock.accept_nonblock
|
351
|
-
rescue IO::WaitReadable
|
352
|
-
next
|
353
|
-
end
|
354
|
-
drain += 1 if shutting_down?
|
355
|
-
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
356
|
-
c.listener = sock
|
357
|
-
c.http_content_length_limit = @http_content_length_limit
|
358
|
-
c.send(addr_send_name, addr_value) if addr_value
|
359
|
-
}
|
360
|
-
end
|
361
|
-
end
|
362
|
-
rescue IOError, Errno::EBADF
|
363
|
-
# In the case that any of the sockets are unexpectedly close.
|
364
|
-
raise
|
365
|
-
rescue StandardError => e
|
366
|
-
@log_writer.unknown_error e, nil, "Listen loop"
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
@log_writer.debug "Drained #{drain} additional connections." if drain
|
371
|
-
@events.fire :state, @status
|
372
|
-
|
373
|
-
if queue_requests
|
374
|
-
@queue_requests = false
|
375
|
-
@reactor.shutdown
|
376
|
-
end
|
377
|
-
|
378
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
379
|
-
rescue Exception => e
|
380
|
-
@log_writer.unknown_error e, nil, "Exception handling servers"
|
381
|
-
ensure
|
382
|
-
# Errno::EBADF is infrequently raised
|
383
|
-
[@check, @notify].each do |io|
|
384
|
-
begin
|
385
|
-
io.close unless io.closed?
|
386
|
-
rescue Errno::EBADF
|
387
|
-
end
|
388
|
-
end
|
389
|
-
@notify = nil
|
390
|
-
@check = nil
|
391
|
-
end
|
392
|
-
|
393
|
-
@events.fire :state, :done
|
394
|
-
end
|
395
|
-
|
396
|
-
# :nodoc:
|
397
|
-
def handle_check
|
398
|
-
cmd = @check.read(1)
|
399
|
-
|
400
|
-
case cmd
|
401
|
-
when STOP_COMMAND
|
402
|
-
@status = :stop
|
403
|
-
return true
|
404
|
-
when HALT_COMMAND
|
405
|
-
@status = :halt
|
406
|
-
return true
|
407
|
-
when RESTART_COMMAND
|
408
|
-
@status = :restart
|
409
|
-
return true
|
410
|
-
end
|
411
|
-
|
412
|
-
false
|
413
|
-
end
|
414
|
-
|
415
|
-
# Given a connection on +client+, handle the incoming requests,
|
416
|
-
# or queue the connection in the Reactor if no request is available.
|
417
|
-
#
|
418
|
-
# This method is called from a ThreadPool worker thread.
|
419
|
-
#
|
420
|
-
# This method supports HTTP Keep-Alive so it may, depending on if the client
|
421
|
-
# indicates that it supports keep alive, wait for another request before
|
422
|
-
# returning.
|
423
|
-
#
|
424
|
-
# Return true if one or more requests were processed.
|
425
|
-
def process_client(client)
|
426
|
-
# Advertise this server into the thread
|
427
|
-
Thread.current[THREAD_LOCAL_KEY] = self
|
428
|
-
|
429
|
-
clean_thread_locals = @options[:clean_thread_locals]
|
430
|
-
close_socket = true
|
431
|
-
|
432
|
-
requests = 0
|
433
|
-
|
434
|
-
begin
|
435
|
-
if @queue_requests &&
|
436
|
-
!client.eagerly_finish
|
437
|
-
|
438
|
-
client.set_timeout(@first_data_timeout)
|
439
|
-
if @reactor.add client
|
440
|
-
close_socket = false
|
441
|
-
return false
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
with_force_shutdown(client) do
|
446
|
-
client.finish(@first_data_timeout)
|
447
|
-
end
|
448
|
-
|
449
|
-
while true
|
450
|
-
@requests_count += 1
|
451
|
-
case handle_request(client, requests + 1)
|
452
|
-
when false
|
453
|
-
break
|
454
|
-
when :async
|
455
|
-
close_socket = false
|
456
|
-
break
|
457
|
-
when true
|
458
|
-
ThreadPool.clean_thread_locals if clean_thread_locals
|
459
|
-
|
460
|
-
requests += 1
|
461
|
-
|
462
|
-
# As an optimization, try to read the next request from the
|
463
|
-
# socket for a short time before returning to the reactor.
|
464
|
-
fast_check = @status == :run
|
465
|
-
|
466
|
-
# Always pass the client back to the reactor after a reasonable
|
467
|
-
# number of inline requests if there are other requests pending.
|
468
|
-
fast_check = false if requests >= @max_fast_inline &&
|
469
|
-
@thread_pool.backlog > 0
|
470
|
-
|
471
|
-
next_request_ready = with_force_shutdown(client) do
|
472
|
-
client.reset(fast_check)
|
473
|
-
end
|
474
|
-
|
475
|
-
unless next_request_ready
|
476
|
-
break unless @queue_requests
|
477
|
-
client.set_timeout @persistent_timeout
|
478
|
-
if @reactor.add client
|
479
|
-
close_socket = false
|
480
|
-
break
|
481
|
-
end
|
482
|
-
end
|
483
|
-
end
|
484
|
-
end
|
485
|
-
true
|
486
|
-
rescue StandardError => e
|
487
|
-
client_error(e, client, requests)
|
488
|
-
# The ensure tries to close +client+ down
|
489
|
-
requests > 0
|
490
|
-
ensure
|
491
|
-
client.io_buffer.reset
|
492
|
-
|
493
|
-
begin
|
494
|
-
client.close if close_socket
|
495
|
-
rescue IOError, SystemCallError
|
496
|
-
Puma::Util.purge_interrupt_queue
|
497
|
-
# Already closed
|
498
|
-
rescue StandardError => e
|
499
|
-
@log_writer.unknown_error e, nil, "Client"
|
500
|
-
end
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
# Triggers a client timeout if the thread-pool shuts down
|
505
|
-
# during execution of the provided block.
|
506
|
-
def with_force_shutdown(client, &block)
|
507
|
-
@thread_pool.with_force_shutdown(&block)
|
508
|
-
rescue ThreadPool::ForceShutdown
|
509
|
-
client.timeout!
|
510
|
-
end
|
511
|
-
|
512
|
-
# :nocov:
|
513
|
-
|
514
|
-
# Handle various error types thrown by Client I/O operations.
|
515
|
-
def client_error(e, client, requests = 1)
|
516
|
-
# Swallow, do not log
|
517
|
-
return if [ConnectionError, EOFError].include?(e.class)
|
518
|
-
|
519
|
-
case e
|
520
|
-
when MiniSSL::SSLError
|
521
|
-
lowlevel_error(e, client.env)
|
522
|
-
@log_writer.ssl_error e, client.io
|
523
|
-
when HttpParserError
|
524
|
-
response_to_error(client, requests, e, 400)
|
525
|
-
@log_writer.parse_error e, client
|
526
|
-
when HttpParserError501
|
527
|
-
response_to_error(client, requests, e, 501)
|
528
|
-
@log_writer.parse_error e, client
|
529
|
-
else
|
530
|
-
response_to_error(client, requests, e, 500)
|
531
|
-
@log_writer.unknown_error e, nil, "Read"
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
|
-
# A fallback rack response if +@app+ raises as exception.
|
536
|
-
#
|
537
|
-
def lowlevel_error(e, env, status=500)
|
538
|
-
if handler = @options[:lowlevel_error_handler]
|
539
|
-
if handler.arity == 1
|
540
|
-
return handler.call(e)
|
541
|
-
elsif handler.arity == 2
|
542
|
-
return handler.call(e, env)
|
543
|
-
else
|
544
|
-
return handler.call(e, env, status)
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
if @leak_stack_on_error
|
549
|
-
backtrace = e.backtrace.nil? ? '<no backtrace available>' : e.backtrace.join("\n")
|
550
|
-
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{backtrace}"]]
|
551
|
-
else
|
552
|
-
[status, {}, [""]]
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
|
-
def response_to_error(client, requests, err, status_code)
|
557
|
-
status, headers, res_body = lowlevel_error(err, client.env, status_code)
|
558
|
-
prepare_response(status, headers, res_body, requests, client)
|
559
|
-
client.write_error(status_code)
|
560
|
-
end
|
561
|
-
private :response_to_error
|
562
|
-
|
563
|
-
# Wait for all outstanding requests to finish.
|
564
|
-
#
|
565
|
-
def graceful_shutdown
|
566
|
-
if @options[:shutdown_debug]
|
567
|
-
threads = Thread.list
|
568
|
-
total = threads.size
|
569
|
-
|
570
|
-
pid = Process.pid
|
571
|
-
|
572
|
-
$stdout.syswrite "#{pid}: === Begin thread backtrace dump ===\n"
|
573
|
-
|
574
|
-
threads.each_with_index do |t,i|
|
575
|
-
$stdout.syswrite "#{pid}: Thread #{i+1}/#{total}: #{t.inspect}\n"
|
576
|
-
$stdout.syswrite "#{pid}: #{t.backtrace.join("\n#{pid}: ")}\n\n"
|
577
|
-
end
|
578
|
-
$stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
|
579
|
-
end
|
580
|
-
|
581
|
-
if @status != :restart
|
582
|
-
@binder.close
|
583
|
-
end
|
584
|
-
|
585
|
-
if @thread_pool
|
586
|
-
if timeout = @options[:force_shutdown_after]
|
587
|
-
@thread_pool.shutdown timeout.to_f
|
588
|
-
else
|
589
|
-
@thread_pool.shutdown
|
590
|
-
end
|
591
|
-
end
|
592
|
-
end
|
593
|
-
|
594
|
-
def notify_safely(message)
|
595
|
-
@notify << message
|
596
|
-
rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
|
597
|
-
# The server, in another thread, is shutting down
|
598
|
-
Puma::Util.purge_interrupt_queue
|
599
|
-
rescue RuntimeError => e
|
600
|
-
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
601
|
-
if e.message.include?('IOError')
|
602
|
-
Puma::Util.purge_interrupt_queue
|
603
|
-
else
|
604
|
-
raise e
|
605
|
-
end
|
606
|
-
end
|
607
|
-
private :notify_safely
|
608
|
-
|
609
|
-
# Stops the acceptor thread and then causes the worker threads to finish
|
610
|
-
# off the request queue before finally exiting.
|
611
|
-
|
612
|
-
def stop(sync=false)
|
613
|
-
notify_safely(STOP_COMMAND)
|
614
|
-
@thread.join if @thread && sync
|
615
|
-
end
|
616
|
-
|
617
|
-
def halt(sync=false)
|
618
|
-
notify_safely(HALT_COMMAND)
|
619
|
-
@thread.join if @thread && sync
|
620
|
-
end
|
621
|
-
|
622
|
-
def begin_restart(sync=false)
|
623
|
-
notify_safely(RESTART_COMMAND)
|
624
|
-
@thread.join if @thread && sync
|
625
|
-
end
|
626
|
-
|
627
|
-
def shutting_down?
|
628
|
-
@status == :stop || @status == :restart
|
629
|
-
end
|
630
|
-
|
631
|
-
# List of methods invoked by #stats.
|
632
|
-
# @version 5.0.0
|
633
|
-
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
|
634
|
-
|
635
|
-
# Returns a hash of stats about the running server for reporting purposes.
|
636
|
-
# @version 5.0.0
|
637
|
-
# @!attribute [r] stats
|
638
|
-
def stats
|
639
|
-
STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
|
640
|
-
end
|
641
|
-
|
642
|
-
# below are 'delegations' to binder
|
643
|
-
# remove in Puma 7?
|
644
|
-
|
645
|
-
|
646
|
-
def add_tcp_listener(host, port, optimize_for_latency = true, backlog = 1024)
|
647
|
-
@binder.add_tcp_listener host, port, optimize_for_latency, backlog
|
648
|
-
end
|
649
|
-
|
650
|
-
def add_ssl_listener(host, port, ctx, optimize_for_latency = true,
|
651
|
-
backlog = 1024)
|
652
|
-
@binder.add_ssl_listener host, port, ctx, optimize_for_latency, backlog
|
653
|
-
end
|
654
|
-
|
655
|
-
def add_unix_listener(path, umask = nil, mode = nil, backlog = 1024)
|
656
|
-
@binder.add_unix_listener path, umask, mode, backlog
|
657
|
-
end
|
658
|
-
|
659
|
-
# @!attribute [r] connected_ports
|
660
|
-
def connected_ports
|
661
|
-
@binder.connected_ports
|
662
|
-
end
|
663
|
-
end
|
664
|
-
end
|