puma 6.0.2 → 6.2.1
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 +68 -0
- data/README.md +15 -3
- data/docs/systemd.md +1 -2
- data/lib/puma/binder.rb +4 -3
- data/lib/puma/cli.rb +1 -1
- data/lib/puma/client.rb +29 -4
- data/lib/puma/cluster/worker.rb +5 -0
- data/lib/puma/cluster.rb +2 -0
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +2 -0
- data/lib/puma/const.rb +2 -2
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +59 -2
- data/lib/puma/error_logger.rb +2 -1
- data/lib/puma/launcher.rb +9 -22
- data/lib/puma/log_writer.rb +13 -3
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/rack_default.rb +18 -3
- data/lib/puma/request.rb +79 -58
- data/lib/puma/runner.rb +7 -0
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +2 -0
- data/lib/puma/single.rb +2 -0
- data/lib/rack/handler/puma.rb +117 -94
- metadata +4 -3
- data/lib/puma/systemd.rb +0 -47
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../plugin'
|
4
|
+
|
5
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
6
|
+
# 1. when it has successfully started
|
7
|
+
# 2. when it is starting shutdown
|
8
|
+
# 3. periodically for a liveness check with a watchdog thread
|
9
|
+
# 4. periodically set the status
|
10
|
+
Puma::Plugin.create do
|
11
|
+
def start(launcher)
|
12
|
+
require_relative '../sd_notify'
|
13
|
+
|
14
|
+
launcher.log_writer.log "* Enabling systemd notification integration"
|
15
|
+
|
16
|
+
# hook_events
|
17
|
+
launcher.events.on_booted { Puma::SdNotify.ready }
|
18
|
+
launcher.events.on_stopped { Puma::SdNotify.stopping }
|
19
|
+
launcher.events.on_restart { Puma::SdNotify.reloading }
|
20
|
+
|
21
|
+
# start watchdog
|
22
|
+
if Puma::SdNotify.watchdog?
|
23
|
+
ping_f = watchdog_sleep_time
|
24
|
+
|
25
|
+
in_background do
|
26
|
+
launcher.log_writer.log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
|
27
|
+
loop do
|
28
|
+
sleep ping_f
|
29
|
+
Puma::SdNotify.watchdog
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# start status loop
|
35
|
+
instance = self
|
36
|
+
sleep_time = 1.0
|
37
|
+
in_background do
|
38
|
+
launcher.log_writer.log "Sending status to systemd every #{sleep_time.round(1)} sec"
|
39
|
+
|
40
|
+
loop do
|
41
|
+
sleep sleep_time
|
42
|
+
# TODO: error handling?
|
43
|
+
Puma::SdNotify.status(instance.status)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def status
|
49
|
+
if clustered?
|
50
|
+
messages = stats[:worker_status].map do |worker|
|
51
|
+
common_message(worker[:last_status])
|
52
|
+
end.join(',')
|
53
|
+
|
54
|
+
"Puma #{Puma::Const::VERSION}: cluster: #{booted_workers}/#{workers}, worker_status: [#{messages}]"
|
55
|
+
else
|
56
|
+
"Puma #{Puma::Const::VERSION}: worker: #{common_message(stats)}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def watchdog_sleep_time
|
63
|
+
usec = Integer(ENV["WATCHDOG_USEC"])
|
64
|
+
|
65
|
+
sec_f = usec / 1_000_000.0
|
66
|
+
# "It is recommended that a daemon sends a keep-alive notification message
|
67
|
+
# to the service manager every half of the time returned here."
|
68
|
+
sec_f / 2
|
69
|
+
end
|
70
|
+
|
71
|
+
def stats
|
72
|
+
Puma.stats_hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def clustered?
|
76
|
+
stats.has_key?(:workers)
|
77
|
+
end
|
78
|
+
|
79
|
+
def workers
|
80
|
+
stats.fetch(:workers, 1)
|
81
|
+
end
|
82
|
+
|
83
|
+
def booted_workers
|
84
|
+
stats.fetch(:booted_workers, 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
def common_message(stats)
|
88
|
+
"{ #{stats[:running]}/#{stats[:max_threads]} threads, #{stats[:pool_capacity]} available, #{stats[:backlog]} backlog }"
|
89
|
+
end
|
90
|
+
end
|
data/lib/puma/rack_default.rb
CHANGED
@@ -2,8 +2,23 @@
|
|
2
2
|
|
3
3
|
require_relative '../rack/handler/puma'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# rackup was removed in Rack 3, it is now a separate gem
|
6
|
+
if Object.const_defined? :Rackup
|
7
|
+
module Rackup
|
8
|
+
module Handler
|
9
|
+
def self.default(options = {})
|
10
|
+
::Rackup::Handler::Puma
|
11
|
+
end
|
12
|
+
end
|
8
13
|
end
|
14
|
+
elsif Object.const_defined?(:Rack) && Rack::RELEASE < '3'
|
15
|
+
module Rack
|
16
|
+
module Handler
|
17
|
+
def self.default(options = {})
|
18
|
+
::Rack::Handler::Puma
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
raise "Rack 3 must be used with the Rackup gem"
|
9
24
|
end
|
data/lib/puma/request.rb
CHANGED
@@ -53,8 +53,13 @@ module Puma
|
|
53
53
|
socket = client.io # io may be a MiniSSL::Socket
|
54
54
|
app_body = nil
|
55
55
|
|
56
|
+
|
56
57
|
return false if closed_socket?(socket)
|
57
58
|
|
59
|
+
if client.http_content_length_limit_exceeded
|
60
|
+
return prepare_response(413, {}, ["Payload Too Large"], requests, client)
|
61
|
+
end
|
62
|
+
|
58
63
|
normalize_env env, client
|
59
64
|
|
60
65
|
env[PUMA_SOCKET] = socket
|
@@ -101,6 +106,7 @@ module Puma
|
|
101
106
|
# is called
|
102
107
|
res_body = app_body
|
103
108
|
|
109
|
+
# full hijack, app called env['rack.hijack']
|
104
110
|
return :async if client.hijacked
|
105
111
|
|
106
112
|
status = status.to_i
|
@@ -164,78 +170,87 @@ module Puma
|
|
164
170
|
resp_info = str_headers(env, status, headers, res_body, io_buffer, force_keep_alive)
|
165
171
|
|
166
172
|
close_body = false
|
173
|
+
response_hijack = nil
|
174
|
+
content_length = resp_info[:content_length]
|
175
|
+
keep_alive = resp_info[:keep_alive]
|
167
176
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
if
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
177
|
+
if res_body.respond_to?(:each) && !resp_info[:response_hijack]
|
178
|
+
# below converts app_body into body, dependent on app_body's characteristics, and
|
179
|
+
# content_length will be set if it can be determined
|
180
|
+
if !content_length && !resp_info[:transfer_encoding] && status != 204
|
181
|
+
if res_body.respond_to?(:to_ary) && (array_body = res_body.to_ary) &&
|
182
|
+
array_body.is_a?(Array)
|
183
|
+
body = array_body.compact
|
184
|
+
content_length = body.sum(&:bytesize)
|
185
|
+
elsif res_body.is_a?(File) && res_body.respond_to?(:size)
|
186
|
+
body = res_body
|
187
|
+
content_length = body.size
|
188
|
+
elsif res_body.respond_to?(:to_path) && File.readable?(fn = res_body.to_path)
|
189
|
+
body = File.open fn, 'rb'
|
190
|
+
content_length = body.size
|
191
|
+
close_body = true
|
192
|
+
else
|
193
|
+
body = res_body
|
194
|
+
end
|
195
|
+
elsif !res_body.is_a?(::File) && res_body.respond_to?(:to_path) &&
|
178
196
|
File.readable?(fn = res_body.to_path)
|
179
197
|
body = File.open fn, 'rb'
|
180
|
-
|
198
|
+
content_length = body.size
|
181
199
|
close_body = true
|
200
|
+
elsif !res_body.is_a?(::File) && res_body.respond_to?(:filename) &&
|
201
|
+
res_body.respond_to?(:bytesize) && File.readable?(fn = res_body.filename)
|
202
|
+
# Sprockets::Asset
|
203
|
+
content_length = res_body.bytesize unless content_length
|
204
|
+
if (body_str = res_body.to_hash[:source])
|
205
|
+
body = [body_str]
|
206
|
+
else # avoid each and use a File object
|
207
|
+
body = File.open fn, 'rb'
|
208
|
+
close_body = true
|
209
|
+
end
|
182
210
|
else
|
183
211
|
body = res_body
|
184
212
|
end
|
185
|
-
elsif !res_body.is_a?(::File) && res_body.respond_to?(:to_path) && res_body.respond_to?(:each) &&
|
186
|
-
File.readable?(fn = res_body.to_path)
|
187
|
-
body = File.open fn, 'rb'
|
188
|
-
resp_info[:content_length] = body.size
|
189
|
-
close_body = true
|
190
|
-
elsif !res_body.is_a?(::File) && res_body.respond_to?(:filename) && res_body.respond_to?(:each) &&
|
191
|
-
res_body.respond_to?(:bytesize) && File.readable?(fn = res_body.filename)
|
192
|
-
# Sprockets::Asset
|
193
|
-
resp_info[:content_length] = res_body.bytesize unless resp_info[:content_length]
|
194
|
-
if res_body.to_hash[:source] # use each to return @source
|
195
|
-
body = res_body
|
196
|
-
else # avoid each and use a File object
|
197
|
-
body = File.open fn, 'rb'
|
198
|
-
close_body = true
|
199
|
-
end
|
200
213
|
else
|
201
|
-
|
214
|
+
# partial hijack, from Rack spec:
|
215
|
+
# Servers must ignore the body part of the response tuple when the
|
216
|
+
# rack.hijack response header is present.
|
217
|
+
response_hijack = resp_info[:response_hijack] || res_body
|
202
218
|
end
|
203
219
|
|
204
220
|
line_ending = LINE_END
|
205
221
|
|
206
|
-
content_length = resp_info[:content_length]
|
207
|
-
keep_alive = resp_info[:keep_alive]
|
208
|
-
|
209
|
-
if res_body && !res_body.respond_to?(:each)
|
210
|
-
response_hijack = res_body
|
211
|
-
else
|
212
|
-
response_hijack = resp_info[:response_hijack]
|
213
|
-
end
|
214
|
-
|
215
222
|
cork_socket socket
|
216
223
|
|
217
224
|
if resp_info[:no_body]
|
218
|
-
|
225
|
+
# 101 (Switching Protocols) doesn't return here or have content_length,
|
226
|
+
# it should be using `response_hijack`
|
227
|
+
unless status == 101
|
228
|
+
if content_length && status != 204
|
229
|
+
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
230
|
+
end
|
231
|
+
|
232
|
+
io_buffer << LINE_END
|
233
|
+
fast_write_str socket, io_buffer.read_and_reset
|
234
|
+
socket.flush
|
235
|
+
return keep_alive
|
236
|
+
end
|
237
|
+
else
|
238
|
+
if content_length
|
219
239
|
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
240
|
+
chunked = false
|
241
|
+
elsif !response_hijack && resp_info[:allow_chunked]
|
242
|
+
io_buffer << TRANSFER_ENCODING_CHUNKED
|
243
|
+
chunked = true
|
220
244
|
end
|
221
|
-
|
222
|
-
io_buffer << LINE_END
|
223
|
-
fast_write_str socket, io_buffer.read_and_reset
|
224
|
-
socket.flush
|
225
|
-
return keep_alive
|
226
|
-
end
|
227
|
-
if content_length
|
228
|
-
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
229
|
-
chunked = false
|
230
|
-
elsif !response_hijack and resp_info[:allow_chunked]
|
231
|
-
io_buffer << TRANSFER_ENCODING_CHUNKED
|
232
|
-
chunked = true
|
233
245
|
end
|
234
246
|
|
235
247
|
io_buffer << line_ending
|
236
248
|
|
249
|
+
# partial hijack, we write headers, then hand the socket to the app via
|
250
|
+
# response_hijack.call
|
237
251
|
if response_hijack
|
238
252
|
fast_write_str socket, io_buffer.read_and_reset
|
253
|
+
uncork_socket socket
|
239
254
|
response_hijack.call socket
|
240
255
|
return :async
|
241
256
|
end
|
@@ -295,8 +310,8 @@ module Puma
|
|
295
310
|
def fast_write_response(socket, body, io_buffer, chunked, content_length)
|
296
311
|
if body.is_a?(::File) && body.respond_to?(:read)
|
297
312
|
if chunked # would this ever happen?
|
298
|
-
while
|
299
|
-
io_buffer.append
|
313
|
+
while chunk = body.read(BODY_LEN_MAX)
|
314
|
+
io_buffer.append chunk.bytesize.to_s(16), LINE_END, chunk, LINE_END
|
300
315
|
end
|
301
316
|
fast_write_str socket, CLOSE_CHUNKED
|
302
317
|
else
|
@@ -347,16 +362,22 @@ module Puma
|
|
347
362
|
fast_write_str(socket, io_buffer.read_and_reset) unless io_buffer.length.zero?
|
348
363
|
else
|
349
364
|
# for enum bodies
|
350
|
-
fast_write_str socket, io_buffer.read_and_reset
|
351
365
|
if chunked
|
366
|
+
empty_body = true
|
352
367
|
body.each do |part|
|
353
|
-
next if (byte_size = part.bytesize).zero?
|
354
|
-
|
355
|
-
|
356
|
-
|
368
|
+
next if part.nil? || (byte_size = part.bytesize).zero?
|
369
|
+
empty_body = false
|
370
|
+
io_buffer.append byte_size.to_s(16), LINE_END, part, LINE_END
|
371
|
+
fast_write_str socket, io_buffer.read_and_reset
|
372
|
+
end
|
373
|
+
if empty_body
|
374
|
+
io_buffer << CLOSE_CHUNKED
|
375
|
+
fast_write_str socket, io_buffer.read_and_reset
|
376
|
+
else
|
377
|
+
fast_write_str socket, CLOSE_CHUNKED
|
357
378
|
end
|
358
|
-
fast_write_str socket, CLOSE_CHUNKED
|
359
379
|
else
|
380
|
+
fast_write_str socket, io_buffer.read_and_reset
|
360
381
|
body.each do |part|
|
361
382
|
next if part.bytesize.zero?
|
362
383
|
fast_write_str socket, part
|
@@ -476,7 +497,7 @@ module Puma
|
|
476
497
|
to_add = nil
|
477
498
|
|
478
499
|
env.each do |k,v|
|
479
|
-
if k.start_with?("HTTP_")
|
500
|
+
if k.start_with?("HTTP_") && k.include?(",") && k != "HTTP_TRANSFER,ENCODING"
|
480
501
|
if to_delete
|
481
502
|
to_delete << k
|
482
503
|
else
|
data/lib/puma/runner.rb
CHANGED
@@ -198,5 +198,12 @@ module Puma
|
|
198
198
|
}
|
199
199
|
}
|
200
200
|
end
|
201
|
+
|
202
|
+
# this method call should always be guarded by `@log_writer.debug?`
|
203
|
+
def debug_loaded_extensions(str)
|
204
|
+
@log_writer.debug "────────────────────────────────── #{str}"
|
205
|
+
re_ext = /\.#{RbConfig::CONFIG['DLEXT']}\z/i
|
206
|
+
$LOADED_FEATURES.grep(re_ext).each { |f| @log_writer.debug(" #{f}") }
|
207
|
+
end
|
201
208
|
end
|
202
209
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
# The MIT License
|
7
|
+
#
|
8
|
+
# Copyright (c) 2017-2022 Agis Anastasopoulos
|
9
|
+
#
|
10
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
11
|
+
# this software and associated documentation files (the "Software"), to deal in
|
12
|
+
# the Software without restriction, including without limitation the rights to
|
13
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
14
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
15
|
+
# subject to the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be included in all
|
18
|
+
# copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
23
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
24
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
25
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#
|
27
|
+
# This is a copy of https://github.com/agis/ruby-sdnotify as of commit cca575c
|
28
|
+
# The only changes made was "rehoming" it within the Puma module to avoid
|
29
|
+
# namespace collisions and applying standard's code formatting style.
|
30
|
+
#
|
31
|
+
# SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
|
32
|
+
# notify systemd about state changes. Methods of this package are no-op on
|
33
|
+
# non-systemd systems (eg. Darwin).
|
34
|
+
#
|
35
|
+
# The API maps closely to the original implementation of sd_notify(3),
|
36
|
+
# therefore be sure to check the official man pages prior to using SdNotify.
|
37
|
+
#
|
38
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
39
|
+
module SdNotify
|
40
|
+
# Exception raised when there's an error writing to the notification socket
|
41
|
+
class NotifyError < RuntimeError; end
|
42
|
+
|
43
|
+
READY = "READY=1"
|
44
|
+
RELOADING = "RELOADING=1"
|
45
|
+
STOPPING = "STOPPING=1"
|
46
|
+
STATUS = "STATUS="
|
47
|
+
ERRNO = "ERRNO="
|
48
|
+
MAINPID = "MAINPID="
|
49
|
+
WATCHDOG = "WATCHDOG=1"
|
50
|
+
FDSTORE = "FDSTORE=1"
|
51
|
+
|
52
|
+
def self.ready(unset_env=false)
|
53
|
+
notify(READY, unset_env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.reloading(unset_env=false)
|
57
|
+
notify(RELOADING, unset_env)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.stopping(unset_env=false)
|
61
|
+
notify(STOPPING, unset_env)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param status [String] a custom status string that describes the current
|
65
|
+
# state of the service
|
66
|
+
def self.status(status, unset_env=false)
|
67
|
+
notify("#{STATUS}#{status}", unset_env)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param errno [Integer]
|
71
|
+
def self.errno(errno, unset_env=false)
|
72
|
+
notify("#{ERRNO}#{errno}", unset_env)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param pid [Integer]
|
76
|
+
def self.mainpid(pid, unset_env=false)
|
77
|
+
notify("#{MAINPID}#{pid}", unset_env)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.watchdog(unset_env=false)
|
81
|
+
notify(WATCHDOG, unset_env)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.fdstore(unset_env=false)
|
85
|
+
notify(FDSTORE, unset_env)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param [Boolean] true if the service manager expects watchdog keep-alive
|
89
|
+
# notification messages to be sent from this process.
|
90
|
+
#
|
91
|
+
# If the $WATCHDOG_USEC environment variable is set,
|
92
|
+
# and the $WATCHDOG_PID variable is unset or set to the PID of the current
|
93
|
+
# process
|
94
|
+
#
|
95
|
+
# @note Unlike sd_watchdog_enabled(3), this method does not mutate the
|
96
|
+
# environment.
|
97
|
+
def self.watchdog?
|
98
|
+
wd_usec = ENV["WATCHDOG_USEC"]
|
99
|
+
wd_pid = ENV["WATCHDOG_PID"]
|
100
|
+
|
101
|
+
return false if !wd_usec
|
102
|
+
|
103
|
+
begin
|
104
|
+
wd_usec = Integer(wd_usec)
|
105
|
+
rescue
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
return false if wd_usec <= 0
|
110
|
+
return true if !wd_pid || wd_pid == $$.to_s
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
# Notify systemd with the provided state, via the notification socket, if
|
116
|
+
# any.
|
117
|
+
#
|
118
|
+
# Generally this method will be used indirectly through the other methods
|
119
|
+
# of the library.
|
120
|
+
#
|
121
|
+
# @param state [String]
|
122
|
+
# @param unset_env [Boolean]
|
123
|
+
#
|
124
|
+
# @return [Fixnum, nil] the number of bytes written to the notification
|
125
|
+
# socket or nil if there was no socket to report to (eg. the program wasn't
|
126
|
+
# started by systemd)
|
127
|
+
#
|
128
|
+
# @raise [NotifyError] if there was an error communicating with the systemd
|
129
|
+
# socket
|
130
|
+
#
|
131
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
132
|
+
def self.notify(state, unset_env=false)
|
133
|
+
sock = ENV["NOTIFY_SOCKET"]
|
134
|
+
|
135
|
+
return nil if !sock
|
136
|
+
|
137
|
+
ENV.delete("NOTIFY_SOCKET") if unset_env
|
138
|
+
|
139
|
+
begin
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect do |s|
|
141
|
+
s.close_on_exec = true
|
142
|
+
s.write(state)
|
143
|
+
end
|
144
|
+
rescue StandardError => e
|
145
|
+
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/puma/server.rb
CHANGED
@@ -95,6 +95,7 @@ module Puma
|
|
95
95
|
@queue_requests = @options[:queue_requests]
|
96
96
|
@max_fast_inline = @options[:max_fast_inline]
|
97
97
|
@io_selector_backend = @options[:io_selector_backend]
|
98
|
+
@http_content_length_limit = @options[:http_content_length_limit]
|
98
99
|
|
99
100
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
100
101
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
@@ -334,6 +335,7 @@ module Puma
|
|
334
335
|
drain += 1 if shutting_down?
|
335
336
|
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
336
337
|
c.listener = sock
|
338
|
+
c.http_content_length_limit = @http_content_length_limit
|
337
339
|
c.send(addr_send_name, addr_value) if addr_value
|
338
340
|
}
|
339
341
|
end
|