puma 7.0.3 → 7.0.4
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/History.md +17 -1
- data/ext/puma_http11/mini_ssl.c +18 -3
- data/ext/puma_http11/org/jruby/puma/Http11.java +9 -1
- data/ext/puma_http11/puma_http11.c +23 -11
- data/lib/puma/cluster_accept_loop_delay.rb +92 -0
- data/lib/puma/const.rb +1 -1
- data/lib/puma/server.rb +44 -30
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13d2ebef5879f5ab45b75cadd20e4a72b643ff9f5e6af2cbcf69d655ec4475b8
|
4
|
+
data.tar.gz: 1ce847fac6f3e5f893814462b47792db32f1cb0960b9c8619d7ad26c4057bfec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e67841b0a72c35c6667745e0aad11b152dbd46dc952b51ec811100281f8d93e45bc70fd16861b848918790b2eb0b0b290fa493f82beb0e4ed818abdffcb2f9fc
|
7
|
+
data.tar.gz: cecf1704d003e0f0299811a6c16310bb79fd7ee5ff21ce991bb2b82430f2d31e348a65b4054e57656255af10ac62489e4c4df889367a6c0b967d540522717ea7
|
data/History.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 7.0.4 / 2025-09-23
|
2
|
+
|
3
|
+
* Bugfixes
|
4
|
+
* Fix SSL_shutdown error handling ([#3703])
|
5
|
+
* Strip whitespace from the beginnings of request header values. ([#3742])
|
6
|
+
|
7
|
+
* Performance
|
8
|
+
* puma_http11.c: Use interned UTF-8 strings for hash keys ([#3754])
|
9
|
+
* Move sleep cluster logic to its own class ([#3746], [#3740])
|
10
|
+
|
1
11
|
## 7.0.3 / 2025-09-13
|
2
12
|
|
3
13
|
* Performance
|
@@ -26,7 +36,8 @@
|
|
26
36
|
* Raise an ArgumentError if no block given to hooks ([#3377])
|
27
37
|
* Don't set env['HTTP_VERSION'] for Rack > 3.1 ([#3711], [#3576])
|
28
38
|
* Runner.rb - remove `ruby_engine` method, deprecated Nov-2024 ([#3701])
|
29
|
-
*
|
39
|
+
* Config `preload_app!` is now the default for clustered mode ([#3297])
|
40
|
+
* Config instance must be `clamp`-d before reading any values ([#3297])
|
30
41
|
* Response headers set to lowercase ([#3704])
|
31
42
|
* Update minimum Ruby version to 3.0 ([#3698])
|
32
43
|
* Rename callback hooks ([#3438])
|
@@ -2234,6 +2245,11 @@ be added back in a future date when a java Puma::MiniSSL is added.
|
|
2234
2245
|
* Bugfixes
|
2235
2246
|
* Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
|
2236
2247
|
|
2248
|
+
[#3703]:https://github.com/puma/puma/pull/3703 "PR by @marshall-lee, merged 2025-09-20"
|
2249
|
+
[#3742]:https://github.com/puma/puma/pull/3742 "PR by @kenballus, merged 2025-09-18"
|
2250
|
+
[#3754]:https://github.com/puma/puma/pull/3754 "PR by @byroot, merged 2025-09-18"
|
2251
|
+
[#3746]:https://github.com/puma/puma/pull/3746 "PR by @schneems, merged 2025-09-18"
|
2252
|
+
[#3740]:https://github.com/puma/puma/issues/3740 "Issue by @joshuay03, closed 2025-09-18"
|
2237
2253
|
[#3748]:https://github.com/puma/puma/pull/3748 "PR by @MSP-Greg, merged 2025-09-14"
|
2238
2254
|
[#3749]:https://github.com/puma/puma/pull/3749 "PR by @schneems, merged 2025-09-14"
|
2239
2255
|
[#3736]:https://github.com/puma/puma/pull/3736 "PR by @MSP-Greg, merged 2025-09-08"
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -660,14 +660,29 @@ VALUE engine_shutdown(VALUE self) {
|
|
660
660
|
|
661
661
|
TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
|
662
662
|
|
663
|
+
if (SSL_in_init(conn->ssl)) {
|
664
|
+
// Avoid "shutdown while in init" error
|
665
|
+
// See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2827-L2828
|
666
|
+
return Qtrue;
|
667
|
+
}
|
668
|
+
|
663
669
|
ERR_clear_error();
|
664
670
|
|
665
671
|
ok = SSL_shutdown(conn->ssl);
|
666
|
-
|
667
|
-
|
672
|
+
// See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2792-L2797
|
673
|
+
// for description of SSL_shutdown return values.
|
674
|
+
switch (ok) {
|
675
|
+
case 0:
|
676
|
+
// "close notify" alert is sent by us.
|
677
|
+
return Qfalse;
|
678
|
+
case 1:
|
679
|
+
// "close notify" alert was received from peer.
|
680
|
+
return Qtrue;
|
681
|
+
default:
|
682
|
+
raise_error(conn->ssl, ok);
|
668
683
|
}
|
669
684
|
|
670
|
-
return
|
685
|
+
return Qnil;
|
671
686
|
}
|
672
687
|
|
673
688
|
VALUE engine_init(VALUE self) {
|
@@ -109,6 +109,10 @@ public class Http11 extends RubyObject {
|
|
109
109
|
return (RubyClass)runtime.getModule("Puma").getConstant("HttpParserError");
|
110
110
|
}
|
111
111
|
|
112
|
+
private static boolean is_ows(int c) {
|
113
|
+
return c == ' ' || c == '\t';
|
114
|
+
}
|
115
|
+
|
112
116
|
public static void http_field(Ruby runtime, RubyHash req, ByteList buffer, int field, int flen, int value, int vlen) {
|
113
117
|
RubyString f;
|
114
118
|
IRubyObject v;
|
@@ -127,7 +131,11 @@ public class Http11 extends RubyObject {
|
|
127
131
|
}
|
128
132
|
}
|
129
133
|
|
130
|
-
while (vlen > 0 &&
|
134
|
+
while (vlen > 0 && is_ows(buffer.get(value + vlen - 1))) vlen--;
|
135
|
+
while (vlen > 0 && is_ows(buffer.get(value))) {
|
136
|
+
vlen--;
|
137
|
+
value++;
|
138
|
+
}
|
131
139
|
|
132
140
|
if (b.equals(CONTENT_LENGTH_BYTELIST) || b.equals(CONTENT_TYPE_BYTELIST)) {
|
133
141
|
f = RubyString.newString(runtime, b);
|
@@ -7,6 +7,7 @@
|
|
7
7
|
#define RSTRING_NOT_MODIFIED 1
|
8
8
|
|
9
9
|
#include "ruby.h"
|
10
|
+
#include "ruby/encoding.h"
|
10
11
|
#include "ext_help.h"
|
11
12
|
#include <assert.h>
|
12
13
|
#include <string.h>
|
@@ -48,8 +49,11 @@ static VALUE global_request_path;
|
|
48
49
|
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
|
49
50
|
|
50
51
|
/** Defines global strings in the init method. */
|
51
|
-
|
52
|
-
|
52
|
+
static inline void DEF_GLOBAL(VALUE *var, const char *cstr)
|
53
|
+
{
|
54
|
+
rb_global_variable(var);
|
55
|
+
*var = rb_enc_interned_str_cstr(cstr, rb_utf8_encoding());
|
56
|
+
}
|
53
57
|
|
54
58
|
/* Defines the maximum allowed lengths for various input elements.*/
|
55
59
|
#ifndef PUMA_REQUEST_URI_MAX_LENGTH
|
@@ -134,13 +138,13 @@ static void init_common_fields(void)
|
|
134
138
|
memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
|
135
139
|
|
136
140
|
for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) {
|
141
|
+
rb_global_variable(&cf->value);
|
137
142
|
if(cf->raw) {
|
138
143
|
cf->value = rb_str_new(cf->name, cf->len);
|
139
144
|
} else {
|
140
145
|
memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
|
141
146
|
cf->value = rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len);
|
142
147
|
}
|
143
|
-
rb_global_variable(&cf->value);
|
144
148
|
}
|
145
149
|
}
|
146
150
|
|
@@ -155,6 +159,10 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
155
159
|
return Qnil;
|
156
160
|
}
|
157
161
|
|
162
|
+
static int is_ows(const char c) {
|
163
|
+
return c == ' ' || c == '\t';
|
164
|
+
}
|
165
|
+
|
158
166
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
159
167
|
const char *value, size_t vlen)
|
160
168
|
{
|
@@ -181,7 +189,11 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
|
|
181
189
|
f = rb_str_new(hp->buf, new_size);
|
182
190
|
}
|
183
191
|
|
184
|
-
while (vlen > 0 &&
|
192
|
+
while (vlen > 0 && is_ows(value[vlen - 1])) vlen--;
|
193
|
+
while (vlen > 0 && is_ows(value[0])) {
|
194
|
+
vlen--;
|
195
|
+
value++;
|
196
|
+
}
|
185
197
|
|
186
198
|
/* check for duplicate header */
|
187
199
|
v = rb_hash_aref(hp->request, f);
|
@@ -468,15 +480,15 @@ void Init_puma_http11(void)
|
|
468
480
|
VALUE mPuma = rb_define_module("Puma");
|
469
481
|
VALUE cHttpParser = rb_define_class_under(mPuma, "HttpParser", rb_cObject);
|
470
482
|
|
471
|
-
DEF_GLOBAL(
|
472
|
-
DEF_GLOBAL(
|
473
|
-
DEF_GLOBAL(
|
474
|
-
DEF_GLOBAL(
|
475
|
-
DEF_GLOBAL(
|
476
|
-
DEF_GLOBAL(
|
483
|
+
DEF_GLOBAL(&global_request_method, "REQUEST_METHOD");
|
484
|
+
DEF_GLOBAL(&global_request_uri, "REQUEST_URI");
|
485
|
+
DEF_GLOBAL(&global_fragment, "FRAGMENT");
|
486
|
+
DEF_GLOBAL(&global_query_string, "QUERY_STRING");
|
487
|
+
DEF_GLOBAL(&global_server_protocol, "SERVER_PROTOCOL");
|
488
|
+
DEF_GLOBAL(&global_request_path, "REQUEST_PATH");
|
477
489
|
|
478
|
-
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eStandardError);
|
479
490
|
rb_global_variable(&eHttpParserError);
|
491
|
+
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eStandardError);
|
480
492
|
|
481
493
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
482
494
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Puma
|
4
|
+
# Calculate a delay value for sleeping when running in clustered mode
|
5
|
+
#
|
6
|
+
# The main reason this is a class is so it can be unit tested independently.
|
7
|
+
# This makes modification easier in the future if we can encode properties of the
|
8
|
+
# delay into a test instead of relying on end-to-end testing only.
|
9
|
+
#
|
10
|
+
# This is an imprecise mechanism to address specific goals:
|
11
|
+
#
|
12
|
+
# - Evenly distribute requests across all workers at start
|
13
|
+
# - Evenly distribute CPU resources across all workers
|
14
|
+
#
|
15
|
+
# ## Goal: Distribute requests across workers at start
|
16
|
+
#
|
17
|
+
# There was a perf bug in Puma where one worker would wake up slightly before the rest and accept
|
18
|
+
# all the requests on the socket even though it didn't have enough resources to process all of them.
|
19
|
+
# This was originally fixed by never calling accept when a worker had more requests than threads
|
20
|
+
# already https://github.com/puma/puma/pull/3678/files/2736ebddb3fc8528e5150b5913fba251c37a8bf7#diff-a95f46e7ce116caddc9b9a9aa81004246d5210d5da5f4df90a818c780630166bL251-L291
|
21
|
+
#
|
22
|
+
# With the introduction of true keepalive support, there are two ways a request can come in:
|
23
|
+
# - A new request from a new client comes into the socket and it must be "accept"-d
|
24
|
+
# - A keepalive request is served and the connection is retained. Another request is then accepted
|
25
|
+
#
|
26
|
+
# Ideally the server handles requests in the order they come in, and ideally it doesn't accept more requests than it can handle.
|
27
|
+
# These goals are contradictory, because when the server is at maximum capacity due to keepalive connections, it could mean we
|
28
|
+
# block all new requests, even if those came in before the new request on the older keepalive connection.
|
29
|
+
#
|
30
|
+
# ## Distribute CPU resources across all workers
|
31
|
+
#
|
32
|
+
# - This issue was opened https://github.com/puma/puma/issues/2078
|
33
|
+
#
|
34
|
+
# There are several entangled issues and it's not exactly clear the root cause, but the observable outcome
|
35
|
+
# was that performance was better with a small sleep, and that eventually became the default.
|
36
|
+
#
|
37
|
+
# An attempt to describe why this works is here: https://github.com/puma/puma/issues/2078#issuecomment-3287032470.
|
38
|
+
#
|
39
|
+
# Summarizing: The delay is for tuning the rate at which "accept" is called on the socket.
|
40
|
+
# Puma works by calling "accept" nonblock on the socket in a loop. When there are multiple workers,
|
41
|
+
# (processes) then they will "race" to accept a request at roughly the same rate. However if one
|
42
|
+
# worker has all threads busy processing requests, then accepting a new request might "steal" it from
|
43
|
+
# a less busy worker. If a worker has no work to do, it should loop as fast as possible.
|
44
|
+
#
|
45
|
+
# ## Solution(s): Distribute requests across workers at start
|
46
|
+
#
|
47
|
+
# For now, both goals are framed as "load balancing" across workers (processes) and achieved through
|
48
|
+
# the same mechanism of sleeping longer to delay busier workers. Rather than the prior Puma 6.x
|
49
|
+
# and earlier behavior of using a binary on/off sleep value, we increase it an amound proportional
|
50
|
+
# to the load the server is under. Capping the maximum delay to the scenario where all threads are busy
|
51
|
+
# and the todo list has reached a multiplier of the maximum number of threads.
|
52
|
+
#
|
53
|
+
# Private: API may change unexpectedly
|
54
|
+
class ClusterAcceptLoopDelay
|
55
|
+
attr_reader :max_threads, :max_delay
|
56
|
+
|
57
|
+
# Initialize happens once, `call` happens often. Push global calculations here
|
58
|
+
def initialize(
|
59
|
+
# Number of workers in the cluster
|
60
|
+
workers: ,
|
61
|
+
# Maximum delay in seconds i.e. 0.005 is 5 microseconds
|
62
|
+
max_delay: # In seconds i.e. 0.005 is 5 microseconds
|
63
|
+
|
64
|
+
)
|
65
|
+
@on = max_delay > 0 && workers >= 2
|
66
|
+
@max_delay = max_delay.to_f
|
67
|
+
|
68
|
+
# Reach maximum delay when `max_threads * overload_multiplier` is reached in the system
|
69
|
+
@overload_multiplier = 25.0
|
70
|
+
end
|
71
|
+
|
72
|
+
def on?
|
73
|
+
@on
|
74
|
+
end
|
75
|
+
|
76
|
+
# We want the extreme values of this delay to be known (minimum and maximum) as well as
|
77
|
+
# a predictable curve between the two. i.e. no step functions or hard cliffs.
|
78
|
+
#
|
79
|
+
# Return value is always numeric. Returns 0 if there should be no delay
|
80
|
+
def calculate(
|
81
|
+
# Number of threads working right now, plus number of requests in the todo list
|
82
|
+
busy_threads_plus_todo:,
|
83
|
+
# Maximum number of threads in the pool, note that the busy threads (alone) may go over this value at times
|
84
|
+
# if the pool needs to be reaped. The busy thread plus todo count may go over this value by a large amount
|
85
|
+
max_threads:
|
86
|
+
)
|
87
|
+
max_value = @overload_multiplier * max_threads
|
88
|
+
# Approaches max delay when `busy_threads_plus_todo` approaches `max_value`
|
89
|
+
return max_delay * busy_threads_plus_todo.clamp(0, max_value) / max_value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/puma/const.rb
CHANGED
@@ -100,7 +100,7 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "7.0.
|
103
|
+
PUMA_VERSION = VERSION = "7.0.4"
|
104
104
|
CODE_NAME = "Romantic Warrior"
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
data/lib/puma/server.rb
CHANGED
@@ -13,6 +13,7 @@ require_relative 'binder'
|
|
13
13
|
require_relative 'util'
|
14
14
|
require_relative 'request'
|
15
15
|
require_relative 'configuration'
|
16
|
+
require_relative 'cluster_accept_loop_delay'
|
16
17
|
|
17
18
|
require 'socket'
|
18
19
|
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
@@ -58,7 +59,6 @@ module Puma
|
|
58
59
|
attr_accessor :app
|
59
60
|
attr_accessor :binder
|
60
61
|
|
61
|
-
|
62
62
|
# Create a server for the rack app +app+.
|
63
63
|
#
|
64
64
|
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
@@ -110,6 +110,10 @@ module Puma
|
|
110
110
|
@enable_keep_alives &&= @queue_requests
|
111
111
|
@io_selector_backend = @options[:io_selector_backend]
|
112
112
|
@http_content_length_limit = @options[:http_content_length_limit]
|
113
|
+
@cluster_accept_loop_delay = ClusterAcceptLoopDelay.new(
|
114
|
+
workers: @options[:workers],
|
115
|
+
max_delay: @options[:wait_for_less_busy_worker] || 0 # Real default is in Configuration::DEFAULTS, this is for unit testing
|
116
|
+
)
|
113
117
|
|
114
118
|
if @options[:fiber_per_request]
|
115
119
|
singleton_class.prepend(FiberPerRequest)
|
@@ -245,11 +249,6 @@ module Puma
|
|
245
249
|
@thread_pool&.pool_capacity
|
246
250
|
end
|
247
251
|
|
248
|
-
# @!attribute [r] busy_threads
|
249
|
-
def busy_threads
|
250
|
-
@thread_pool&.busy_threads
|
251
|
-
end
|
252
|
-
|
253
252
|
# Runs the server.
|
254
253
|
#
|
255
254
|
# If +background+ is true (the default) then a thread is spun
|
@@ -266,7 +265,11 @@ module Puma
|
|
266
265
|
@thread_pool = ThreadPool.new(thread_name, options) { |client| process_client client }
|
267
266
|
|
268
267
|
if @queue_requests
|
269
|
-
@reactor = Reactor.new(@io_selector_backend) { |c|
|
268
|
+
@reactor = Reactor.new(@io_selector_backend) { |c|
|
269
|
+
# Inversion of control, the reactor is calling a method on the server when it
|
270
|
+
# is done buffering a request or receives a new request from a keepalive connection.
|
271
|
+
self.reactor_wakeup(c)
|
272
|
+
}
|
270
273
|
@reactor.run
|
271
274
|
end
|
272
275
|
|
@@ -291,6 +294,9 @@ module Puma
|
|
291
294
|
# This method is called from the Reactor thread when a queued Client receives data,
|
292
295
|
# times out, or when the Reactor is shutting down.
|
293
296
|
#
|
297
|
+
# While the code lives in the Server, the logic is executed on the reactor thread, independently
|
298
|
+
# from the server.
|
299
|
+
#
|
294
300
|
# It is responsible for ensuring that a request has been completely received
|
295
301
|
# before it starts to be processed by the ThreadPool. This may be known as read buffering.
|
296
302
|
# If read buffering is not done, and no other read buffering is performed (such as by an application server
|
@@ -325,7 +331,7 @@ module Puma
|
|
325
331
|
end
|
326
332
|
rescue StandardError => e
|
327
333
|
client_error(e, client)
|
328
|
-
client
|
334
|
+
close_client_safely(client)
|
329
335
|
true
|
330
336
|
end
|
331
337
|
|
@@ -339,7 +345,6 @@ module Puma
|
|
339
345
|
pool = @thread_pool
|
340
346
|
queue_requests = @queue_requests
|
341
347
|
drain = options[:drain_on_shutdown] ? 0 : nil
|
342
|
-
max_flt = @max_threads.to_f
|
343
348
|
|
344
349
|
addr_send_name, addr_value = case options[:remote_address]
|
345
350
|
when :value
|
@@ -384,15 +389,13 @@ module Puma
|
|
384
389
|
# clients until the code is finished.
|
385
390
|
pool.wait_while_out_of_band_running
|
386
391
|
|
387
|
-
#
|
388
|
-
if pool.busy_threads
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
sleep 0.0001
|
395
|
-
end
|
392
|
+
# A well rested herd (cluster) runs faster
|
393
|
+
if @cluster_accept_loop_delay.on? && (busy_threads_plus_todo = pool.busy_threads) > 0
|
394
|
+
delay = @cluster_accept_loop_delay.calculate(
|
395
|
+
max_threads: @max_threads,
|
396
|
+
busy_threads_plus_todo: busy_threads_plus_todo
|
397
|
+
)
|
398
|
+
sleep(delay)
|
396
399
|
end
|
397
400
|
|
398
401
|
io = begin
|
@@ -401,11 +404,9 @@ module Puma
|
|
401
404
|
next
|
402
405
|
end
|
403
406
|
drain += 1 if shutting_down?
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
c.send(addr_send_name, addr_value) if addr_value
|
408
|
-
}
|
407
|
+
client = new_client(io, sock)
|
408
|
+
client.send(addr_send_name, addr_value) if addr_value
|
409
|
+
pool << client
|
409
410
|
end
|
410
411
|
end
|
411
412
|
rescue IOError, Errno::EBADF
|
@@ -442,6 +443,14 @@ module Puma
|
|
442
443
|
@events.fire :state, :done
|
443
444
|
end
|
444
445
|
|
446
|
+
# :nodoc:
|
447
|
+
def new_client(io, sock)
|
448
|
+
client = Client.new(io, @binder.env(sock))
|
449
|
+
client.listener = sock
|
450
|
+
client.http_content_length_limit = @http_content_length_limit
|
451
|
+
client
|
452
|
+
end
|
453
|
+
|
445
454
|
# :nodoc:
|
446
455
|
def handle_check
|
447
456
|
cmd = @check.read(1)
|
@@ -529,16 +538,21 @@ module Puma
|
|
529
538
|
ensure
|
530
539
|
client.io_buffer.reset
|
531
540
|
|
532
|
-
|
533
|
-
client.close if close_socket
|
534
|
-
rescue IOError, SystemCallError
|
535
|
-
# Already closed
|
536
|
-
rescue StandardError => e
|
537
|
-
@log_writer.unknown_error e, nil, "Client"
|
538
|
-
end
|
541
|
+
close_client_safely(client) if close_socket
|
539
542
|
end
|
540
543
|
end
|
541
544
|
|
545
|
+
# :nodoc:
|
546
|
+
def close_client_safely(client)
|
547
|
+
client.close
|
548
|
+
rescue IOError, SystemCallError
|
549
|
+
# Already closed
|
550
|
+
rescue MiniSSL::SSLError => e
|
551
|
+
@log_writer.ssl_error e, client.io
|
552
|
+
rescue StandardError => e
|
553
|
+
@log_writer.unknown_error e, nil, "Client"
|
554
|
+
end
|
555
|
+
|
542
556
|
# Triggers a client timeout if the thread-pool shuts down
|
543
557
|
# during execution of the provided block.
|
544
558
|
def with_force_shutdown(client, &block)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/puma/cluster.rb
|
88
88
|
- lib/puma/cluster/worker.rb
|
89
89
|
- lib/puma/cluster/worker_handle.rb
|
90
|
+
- lib/puma/cluster_accept_loop_delay.rb
|
90
91
|
- lib/puma/commonlogger.rb
|
91
92
|
- lib/puma/configuration.rb
|
92
93
|
- lib/puma/const.rb
|