puma 4.3.1 → 5.0.0
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 +94 -3
- data/LICENSE +23 -20
- data/README.md +26 -13
- 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 +7 -6
- 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 +15 -2
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
- data/ext/puma_http11/puma_http11.c +7 -38
- data/lib/puma.rb +17 -0
- data/lib/puma/app/status.rb +18 -3
- data/lib/puma/binder.rb +88 -68
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +67 -14
- data/lib/puma/cluster.rb +191 -74
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +31 -42
- data/lib/puma/const.rb +4 -3
- data/lib/puma/control_cli.rb +29 -17
- data/lib/puma/detect.rb +17 -0
- data/lib/puma/dsl.rb +144 -70
- data/lib/puma/error_logger.rb +97 -0
- data/lib/puma/events.rb +35 -31
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +49 -31
- data/lib/puma/minissl.rb +60 -18
- data/lib/puma/minissl/context_builder.rb +0 -3
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +9 -4
- data/lib/puma/runner.rb +8 -36
- data/lib/puma/server.rb +149 -186
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +6 -3
- data/lib/puma/thread_pool.rb +94 -49
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +21 -23
- 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/app/status.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'json'
|
4
|
-
|
5
3
|
module Puma
|
6
4
|
module App
|
7
5
|
# Check out {#call}'s source code to see what actions this web application
|
@@ -19,6 +17,10 @@ module Puma
|
|
19
17
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
20
18
|
end
|
21
19
|
|
20
|
+
if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
|
21
|
+
require 'json'
|
22
|
+
end
|
23
|
+
|
22
24
|
case env['PATH_INFO']
|
23
25
|
when /\/stop$/
|
24
26
|
@cli.stop
|
@@ -54,7 +56,20 @@ module Puma
|
|
54
56
|
rack_response(200, GC.stat.to_json)
|
55
57
|
|
56
58
|
when /\/stats$/
|
57
|
-
rack_response(200, @cli.stats)
|
59
|
+
rack_response(200, @cli.stats.to_json)
|
60
|
+
|
61
|
+
when /\/thread-backtraces$/
|
62
|
+
backtraces = []
|
63
|
+
@cli.thread_status do |name, backtrace|
|
64
|
+
backtraces << { name: name, backtrace: backtrace }
|
65
|
+
end
|
66
|
+
|
67
|
+
rack_response(200, backtraces.to_json)
|
68
|
+
|
69
|
+
when /\/refork$/
|
70
|
+
Process.kill "SIGURG", $$
|
71
|
+
rack_response(200, OK_STATUS)
|
72
|
+
|
58
73
|
else
|
59
74
|
rack_response 404, "Unsupported action", 'text/plain'
|
60
75
|
end
|
data/lib/puma/binder.rb
CHANGED
@@ -5,15 +5,22 @@ require 'socket'
|
|
5
5
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
|
-
require 'puma/
|
8
|
+
require 'puma/configuration'
|
9
9
|
|
10
10
|
module Puma
|
11
|
+
|
12
|
+
if HAS_SSL
|
13
|
+
require 'puma/minissl'
|
14
|
+
require 'puma/minissl/context_builder'
|
15
|
+
require 'puma/accept_nonblock'
|
16
|
+
end
|
17
|
+
|
11
18
|
class Binder
|
12
19
|
include Puma::Const
|
13
20
|
|
14
|
-
RACK_VERSION = [1,
|
21
|
+
RACK_VERSION = [1,6].freeze
|
15
22
|
|
16
|
-
def initialize(events)
|
23
|
+
def initialize(events, conf = Configuration.new)
|
17
24
|
@events = events
|
18
25
|
@listeners = []
|
19
26
|
@inherited_fds = {}
|
@@ -23,8 +30,8 @@ module Puma
|
|
23
30
|
@proto_env = {
|
24
31
|
"rack.version".freeze => RACK_VERSION,
|
25
32
|
"rack.errors".freeze => events.stderr,
|
26
|
-
"rack.multithread".freeze =>
|
27
|
-
"rack.multiprocess".freeze =>
|
33
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
34
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
28
35
|
"rack.run_once".freeze => false,
|
29
36
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
30
37
|
|
@@ -45,6 +52,12 @@ module Puma
|
|
45
52
|
|
46
53
|
attr_reader :ios
|
47
54
|
|
55
|
+
# @version 5.0.0
|
56
|
+
attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
|
57
|
+
|
58
|
+
# @version 5.0.0
|
59
|
+
attr_writer :ios, :listeners
|
60
|
+
|
48
61
|
def env(sock)
|
49
62
|
@envs.fetch(sock, @proto_env)
|
50
63
|
end
|
@@ -53,40 +66,43 @@ module Puma
|
|
53
66
|
@ios.each { |i| i.close }
|
54
67
|
end
|
55
68
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
if k =~ /PUMA_INHERIT_\d+/
|
61
|
-
fd, url = v.split(":", 2)
|
62
|
-
@inherited_fds[url] = fd.to_i
|
63
|
-
remove << k
|
64
|
-
elsif k == 'LISTEN_FDS' && ENV['LISTEN_PID'].to_i == $$
|
65
|
-
v.to_i.times do |num|
|
66
|
-
fd = num + 3
|
67
|
-
sock = TCPServer.for_fd(fd)
|
68
|
-
begin
|
69
|
-
key = [ :unix, Socket.unpack_sockaddr_un(sock.getsockname) ]
|
70
|
-
rescue ArgumentError
|
71
|
-
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
72
|
-
if addr =~ /\:/
|
73
|
-
addr = "[#{addr}]"
|
74
|
-
end
|
75
|
-
key = [ :tcp, addr, port ]
|
76
|
-
end
|
77
|
-
@activated_sockets[key] = sock
|
78
|
-
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
79
|
-
end
|
80
|
-
remove << k << 'LISTEN_PID'
|
81
|
-
end
|
82
|
-
end
|
69
|
+
# @version 5.0.0
|
70
|
+
def connected_ports
|
71
|
+
ios.map { |io| io.addr[1] }.uniq
|
72
|
+
end
|
83
73
|
|
84
|
-
|
85
|
-
|
74
|
+
# @version 5.0.0
|
75
|
+
def create_inherited_fds(env_hash)
|
76
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
77
|
+
fd, url = v.split(":", 2)
|
78
|
+
@inherited_fds[url] = fd.to_i
|
79
|
+
end.keys # pass keys back for removal
|
80
|
+
end
|
81
|
+
|
82
|
+
# systemd socket activation.
|
83
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
84
|
+
# LISTEN_PID = PID of the service process, aka us
|
85
|
+
# @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
86
|
+
# @version 5.0.0
|
87
|
+
#
|
88
|
+
def create_activated_fds(env_hash)
|
89
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
90
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
91
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
92
|
+
key = begin # Try to parse as a path
|
93
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
94
|
+
rescue ArgumentError # Try to parse as a port/ip
|
95
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
96
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
97
|
+
[:tcp, addr, port]
|
98
|
+
end
|
99
|
+
@activated_sockets[key] = sock
|
100
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
86
101
|
end
|
102
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
87
103
|
end
|
88
104
|
|
89
|
-
def parse(binds, logger)
|
105
|
+
def parse(binds, logger, log_msg = 'Listening')
|
90
106
|
binds.each do |str|
|
91
107
|
uri = URI.parse str
|
92
108
|
case uri.scheme
|
@@ -113,7 +129,7 @@ module Puma
|
|
113
129
|
i.local_address.ip_unpack.join(':')
|
114
130
|
end
|
115
131
|
|
116
|
-
logger.log "*
|
132
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
117
133
|
end
|
118
134
|
end
|
119
135
|
|
@@ -149,11 +165,14 @@ module Puma
|
|
149
165
|
end
|
150
166
|
|
151
167
|
io = add_unix_listener path, umask, mode, backlog
|
152
|
-
logger.log "*
|
168
|
+
logger.log "* #{log_msg} on #{str}"
|
153
169
|
end
|
154
170
|
|
155
171
|
@listeners << [str, io]
|
156
172
|
when "ssl"
|
173
|
+
|
174
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
175
|
+
|
157
176
|
params = Util.parse_query uri.query
|
158
177
|
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
159
178
|
|
@@ -204,12 +223,6 @@ module Puma
|
|
204
223
|
end
|
205
224
|
end
|
206
225
|
|
207
|
-
def loopback_addresses
|
208
|
-
Socket.ip_address_list.select do |addrinfo|
|
209
|
-
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
210
|
-
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
211
|
-
end
|
212
|
-
|
213
226
|
# Tell the server to listen on host +host+, port +port+.
|
214
227
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
215
228
|
# will be optimized for latency over throughput.
|
@@ -226,20 +239,17 @@ module Puma
|
|
226
239
|
end
|
227
240
|
|
228
241
|
host = host[1..-2] if host and host[0..0] == '['
|
229
|
-
|
242
|
+
tcp_server = TCPServer.new(host, port)
|
230
243
|
if optimize_for_latency
|
231
|
-
|
244
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
232
245
|
end
|
233
|
-
|
234
|
-
|
235
|
-
@connected_port = s.addr[1]
|
246
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
247
|
+
tcp_server.listen backlog
|
236
248
|
|
237
|
-
@ios <<
|
238
|
-
|
249
|
+
@ios << tcp_server
|
250
|
+
tcp_server
|
239
251
|
end
|
240
252
|
|
241
|
-
attr_reader :connected_port
|
242
|
-
|
243
253
|
def inherit_tcp_listener(host, port, fd)
|
244
254
|
if fd.kind_of? TCPServer
|
245
255
|
s = fd
|
@@ -253,9 +263,8 @@ module Puma
|
|
253
263
|
|
254
264
|
def add_ssl_listener(host, port, ctx,
|
255
265
|
optimize_for_latency=true, backlog=1024)
|
256
|
-
require 'puma/minissl'
|
257
266
|
|
258
|
-
|
267
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
259
268
|
|
260
269
|
if host == "localhost"
|
261
270
|
loopback_addresses.each do |addr|
|
@@ -272,7 +281,6 @@ module Puma
|
|
272
281
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
273
282
|
s.listen backlog
|
274
283
|
|
275
|
-
|
276
284
|
ssl = MiniSSL::Server.new s, ctx
|
277
285
|
env = @proto_env.dup
|
278
286
|
env[HTTPS_KEY] = HTTPS
|
@@ -283,8 +291,7 @@ module Puma
|
|
283
291
|
end
|
284
292
|
|
285
293
|
def inherit_ssl_listener(fd, ctx)
|
286
|
-
|
287
|
-
MiniSSL.check
|
294
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
288
295
|
|
289
296
|
if fd.kind_of? TCPServer
|
290
297
|
s = fd
|
@@ -360,26 +367,39 @@ module Puma
|
|
360
367
|
end
|
361
368
|
|
362
369
|
def close_listeners
|
363
|
-
|
364
|
-
io.close
|
370
|
+
listeners.each do |l, io|
|
371
|
+
io.close unless io.closed? # Ruby 2.2 issue
|
365
372
|
uri = URI.parse(l)
|
366
373
|
next unless uri.scheme == 'unix'
|
367
374
|
unix_path = "#{uri.host}#{uri.path}"
|
368
|
-
File.unlink unix_path if
|
375
|
+
File.unlink unix_path if unix_paths.include? unix_path
|
369
376
|
end
|
370
377
|
end
|
371
378
|
|
372
|
-
def
|
373
|
-
|
379
|
+
def redirects_for_restart
|
380
|
+
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
381
|
+
redirects[:close_others] = true
|
382
|
+
redirects
|
374
383
|
end
|
375
384
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
redirects[io.to_i] = io.to_i
|
385
|
+
# @version 5.0.0
|
386
|
+
def redirects_for_restart_env
|
387
|
+
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
388
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
381
389
|
end
|
382
|
-
|
390
|
+
end
|
391
|
+
|
392
|
+
private
|
393
|
+
|
394
|
+
def loopback_addresses
|
395
|
+
Socket.ip_address_list.select do |addrinfo|
|
396
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
397
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
398
|
+
end
|
399
|
+
|
400
|
+
# @version 5.0.0
|
401
|
+
def socket_activation_fd(int)
|
402
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
383
403
|
end
|
384
404
|
end
|
385
405
|
end
|
data/lib/puma/cli.rb
CHANGED
@@ -80,7 +80,7 @@ module Puma
|
|
80
80
|
@launcher.run
|
81
81
|
end
|
82
82
|
|
83
|
-
|
83
|
+
private
|
84
84
|
def unsupported(str)
|
85
85
|
@events.error(str)
|
86
86
|
raise UnsupportedOption
|
@@ -112,21 +112,11 @@ module Puma
|
|
112
112
|
configure_control_url(arg)
|
113
113
|
end
|
114
114
|
|
115
|
-
# alias --control-url for backwards-compatibility
|
116
|
-
o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
|
117
|
-
configure_control_url(arg)
|
118
|
-
end
|
119
|
-
|
120
115
|
o.on "--control-token TOKEN",
|
121
116
|
"The token to use as authentication for the control server" do |arg|
|
122
117
|
@control_options[:auth_token] = arg
|
123
118
|
end
|
124
119
|
|
125
|
-
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
126
|
-
user_config.daemonize
|
127
|
-
user_config.quiet
|
128
|
-
end
|
129
|
-
|
130
120
|
o.on "--debug", "Log lowlevel debugging information" do
|
131
121
|
user_config.debug
|
132
122
|
end
|
@@ -140,6 +130,12 @@ module Puma
|
|
140
130
|
user_config.environment arg
|
141
131
|
end
|
142
132
|
|
133
|
+
o.on "-f", "--fork-worker=[REQUESTS]", OptionParser::DecimalInteger,
|
134
|
+
"Fork new workers from existing worker. Cluster mode only",
|
135
|
+
"Auto-refork after REQUESTS (default 1000)" do |*args|
|
136
|
+
user_config.fork_worker(*args.compact)
|
137
|
+
end
|
138
|
+
|
143
139
|
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
144
140
|
$LOAD_PATH.unshift(*arg.split(':'))
|
145
141
|
end
|
@@ -192,10 +188,6 @@ module Puma
|
|
192
188
|
end
|
193
189
|
end
|
194
190
|
|
195
|
-
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
196
|
-
user_config.tcp_mode!
|
197
|
-
end
|
198
|
-
|
199
191
|
o.on "--early-hints", "Enable early hints support" do
|
200
192
|
user_config.early_hints
|
201
193
|
end
|
data/lib/puma/client.rb
CHANGED
@@ -153,7 +153,7 @@ module Puma
|
|
153
153
|
|
154
154
|
begin
|
155
155
|
data = @io.read_nonblock(CHUNK_SIZE)
|
156
|
-
rescue
|
156
|
+
rescue IO::WaitReadable
|
157
157
|
return false
|
158
158
|
rescue SystemCallError, IOError, EOFError
|
159
159
|
raise ConnectionError, "Connection error detected during read"
|
@@ -238,12 +238,23 @@ module Puma
|
|
238
238
|
return false unless IO.select([@to_io], nil, nil, 0)
|
239
239
|
try_to_finish
|
240
240
|
end
|
241
|
+
|
242
|
+
# For documentation, see https://github.com/puma/puma/issues/1754
|
243
|
+
send(:alias_method, :jruby_eagerly_finish, :eagerly_finish)
|
241
244
|
end # IS_JRUBY
|
242
245
|
|
243
|
-
def finish
|
246
|
+
def finish(timeout)
|
244
247
|
return true if @ready
|
245
248
|
until try_to_finish
|
246
|
-
|
249
|
+
can_read = begin
|
250
|
+
IO.select([@to_io], nil, nil, timeout)
|
251
|
+
rescue ThreadPool::ForceShutdown
|
252
|
+
nil
|
253
|
+
end
|
254
|
+
unless can_read
|
255
|
+
write_error(408) if in_data_phase
|
256
|
+
raise ConnectionError
|
257
|
+
end
|
247
258
|
end
|
248
259
|
true
|
249
260
|
end
|
@@ -259,7 +270,7 @@ module Puma
|
|
259
270
|
return @peerip if @peerip
|
260
271
|
|
261
272
|
if @remote_addr_header
|
262
|
-
hdr = (@env[@remote_addr_header] ||
|
273
|
+
hdr = (@env[@remote_addr_header] || LOCALHOST_IP).split(/[\s,]/).first
|
263
274
|
@peerip = hdr
|
264
275
|
return hdr
|
265
276
|
end
|
@@ -267,6 +278,20 @@ module Puma
|
|
267
278
|
@peerip ||= @io.peeraddr.last
|
268
279
|
end
|
269
280
|
|
281
|
+
# Returns true if the persistent connection can be closed immediately
|
282
|
+
# without waiting for the configured idle/shutdown timeout.
|
283
|
+
# @version 5.0.0
|
284
|
+
#
|
285
|
+
def can_close?
|
286
|
+
# Allow connection to close if it's received at least one full request
|
287
|
+
# and hasn't received any data for a future request.
|
288
|
+
#
|
289
|
+
# From RFC 2616 section 8.1.4:
|
290
|
+
# Servers SHOULD always respond to at least one request per connection,
|
291
|
+
# if at all possible.
|
292
|
+
@requests_served > 0 && @parsed_bytes == 0
|
293
|
+
end
|
294
|
+
|
270
295
|
private
|
271
296
|
|
272
297
|
def setup_body
|
@@ -285,8 +310,16 @@ module Puma
|
|
285
310
|
|
286
311
|
te = @env[TRANSFER_ENCODING2]
|
287
312
|
|
288
|
-
if te
|
289
|
-
|
313
|
+
if te
|
314
|
+
if te.include?(",")
|
315
|
+
te.split(",").each do |part|
|
316
|
+
if CHUNKED.casecmp(part.strip) == 0
|
317
|
+
return setup_chunked_body(body)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
elsif CHUNKED.casecmp(te) == 0
|
321
|
+
return setup_chunked_body(body)
|
322
|
+
end
|
290
323
|
end
|
291
324
|
|
292
325
|
@chunked_body = false
|
@@ -343,7 +376,7 @@ module Puma
|
|
343
376
|
|
344
377
|
begin
|
345
378
|
chunk = @io.read_nonblock(want)
|
346
|
-
rescue
|
379
|
+
rescue IO::WaitReadable
|
347
380
|
return false
|
348
381
|
rescue SystemCallError, IOError
|
349
382
|
raise ConnectionError, "Connection error detected during read"
|
@@ -389,7 +422,10 @@ module Puma
|
|
389
422
|
raise EOFError
|
390
423
|
end
|
391
424
|
|
392
|
-
|
425
|
+
if decode_chunk(chunk)
|
426
|
+
@env[CONTENT_LENGTH] = @chunked_content_length
|
427
|
+
return true
|
428
|
+
end
|
393
429
|
end
|
394
430
|
end
|
395
431
|
|
@@ -401,20 +437,37 @@ module Puma
|
|
401
437
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
402
438
|
@body.binmode
|
403
439
|
@tempfile = @body
|
440
|
+
@chunked_content_length = 0
|
404
441
|
|
405
|
-
|
442
|
+
if decode_chunk(body)
|
443
|
+
@env[CONTENT_LENGTH] = @chunked_content_length
|
444
|
+
return true
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# @version 5.0.0
|
449
|
+
def write_chunk(str)
|
450
|
+
@chunked_content_length += @body.write(str)
|
406
451
|
end
|
407
452
|
|
408
453
|
def decode_chunk(chunk)
|
409
454
|
if @partial_part_left > 0
|
410
455
|
if @partial_part_left <= chunk.size
|
411
456
|
if @partial_part_left > 2
|
412
|
-
|
457
|
+
write_chunk(chunk[0..(@partial_part_left-3)]) # skip the \r\n
|
413
458
|
end
|
414
459
|
chunk = chunk[@partial_part_left..-1]
|
415
460
|
@partial_part_left = 0
|
416
461
|
else
|
417
|
-
|
462
|
+
if @partial_part_left > 2
|
463
|
+
if @partial_part_left == chunk.size + 1
|
464
|
+
# Don't include the last \r
|
465
|
+
write_chunk(chunk[0..(@partial_part_left-3)])
|
466
|
+
else
|
467
|
+
# don't include the last \r\n
|
468
|
+
write_chunk(chunk)
|
469
|
+
end
|
470
|
+
end
|
418
471
|
@partial_part_left -= chunk.size
|
419
472
|
return false
|
420
473
|
end
|
@@ -461,12 +514,12 @@ module Puma
|
|
461
514
|
|
462
515
|
case
|
463
516
|
when got == len
|
464
|
-
|
517
|
+
write_chunk(part[0..-3]) # to skip the ending \r\n
|
465
518
|
when got <= len - 2
|
466
|
-
|
519
|
+
write_chunk(part)
|
467
520
|
@partial_part_left = len - part.size
|
468
521
|
when got == len - 1 # edge where we get just \r but not \n
|
469
|
-
|
522
|
+
write_chunk(part[0..-2])
|
470
523
|
@partial_part_left = len - part.size
|
471
524
|
end
|
472
525
|
else
|