puma 4.3.7-java → 5.0.0.beta1-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 +59 -17
- data/LICENSE +23 -20
- data/README.md +17 -11
- data/docs/deployment.md +3 -1
- 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 +1 -0
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +3 -2
- data/ext/puma_http11/http11_parser.c +1 -3
- data/ext/puma_http11/http11_parser.rl +1 -3
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/puma_http11.c +2 -38
- data/lib/puma.rb +4 -0
- data/lib/puma/app/status.rb +16 -5
- data/lib/puma/binder.rb +62 -60
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +35 -32
- data/lib/puma/cluster.rb +179 -74
- 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 +70 -34
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +41 -29
- data/lib/puma/minissl.rb +13 -8
- 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 +6 -1
- data/lib/puma/runner.rb +5 -34
- data/lib/puma/server.rb +62 -177
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +5 -2
- data/lib/puma/thread_pool.rb +85 -47
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +16 -20
- 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
@@ -30,8 +30,8 @@ public class Http11 extends RubyObject {
|
|
30
30
|
public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the 12288 allowed length.";
|
31
31
|
public final static int MAX_FRAGMENT_LENGTH = 1024;
|
32
32
|
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
|
33
|
-
public final static int MAX_REQUEST_PATH_LENGTH =
|
34
|
-
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the
|
33
|
+
public final static int MAX_REQUEST_PATH_LENGTH = 8192;
|
34
|
+
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 8192 allowed length.";
|
35
35
|
public final static int MAX_QUERY_STRING_LENGTH = 1024 * 10;
|
36
36
|
public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the 10240 allowed length.";
|
37
37
|
public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
|
@@ -197,7 +197,7 @@ public class Http11 extends RubyObject {
|
|
197
197
|
validateMaxLength(runtime, parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
|
198
198
|
|
199
199
|
if(hp.has_error()) {
|
200
|
-
throw newHTTPParserError(runtime, "Invalid HTTP format, parsing fails.");
|
200
|
+
throw newHTTPParserError(runtime, "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
|
201
201
|
} else {
|
202
202
|
return runtime.newFixnum(parser.nread);
|
203
203
|
}
|
@@ -54,7 +54,7 @@ DEF_MAX_LENGTH(FIELD_NAME, 256);
|
|
54
54
|
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
55
55
|
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
56
56
|
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
57
|
-
DEF_MAX_LENGTH(REQUEST_PATH,
|
57
|
+
DEF_MAX_LENGTH(REQUEST_PATH, 8196);
|
58
58
|
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
59
59
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
60
60
|
|
@@ -112,21 +112,6 @@ static struct common_field common_http_fields[] = {
|
|
112
112
|
# undef f
|
113
113
|
};
|
114
114
|
|
115
|
-
/*
|
116
|
-
* qsort(3) and bsearch(3) improve average performance slightly, but may
|
117
|
-
* not be worth it for lack of portability to certain platforms...
|
118
|
-
*/
|
119
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
120
|
-
/* sort by length, then by name if there's a tie */
|
121
|
-
static int common_field_cmp(const void *a, const void *b)
|
122
|
-
{
|
123
|
-
struct common_field *cfa = (struct common_field *)a;
|
124
|
-
struct common_field *cfb = (struct common_field *)b;
|
125
|
-
signed long diff = cfa->len - cfb->len;
|
126
|
-
return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
|
127
|
-
}
|
128
|
-
#endif /* HAVE_QSORT_BSEARCH */
|
129
|
-
|
130
115
|
static void init_common_fields(void)
|
131
116
|
{
|
132
117
|
unsigned i;
|
@@ -143,28 +128,10 @@ static void init_common_fields(void)
|
|
143
128
|
}
|
144
129
|
rb_global_variable(&cf->value);
|
145
130
|
}
|
146
|
-
|
147
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
148
|
-
qsort(common_http_fields,
|
149
|
-
ARRAY_SIZE(common_http_fields),
|
150
|
-
sizeof(struct common_field),
|
151
|
-
common_field_cmp);
|
152
|
-
#endif /* HAVE_QSORT_BSEARCH */
|
153
131
|
}
|
154
132
|
|
155
133
|
static VALUE find_common_field_value(const char *field, size_t flen)
|
156
134
|
{
|
157
|
-
#if defined(HAVE_QSORT_BSEARCH)
|
158
|
-
struct common_field key;
|
159
|
-
struct common_field *found;
|
160
|
-
key.name = field;
|
161
|
-
key.len = (signed long)flen;
|
162
|
-
found = (struct common_field *)bsearch(&key, common_http_fields,
|
163
|
-
ARRAY_SIZE(common_http_fields),
|
164
|
-
sizeof(struct common_field),
|
165
|
-
common_field_cmp);
|
166
|
-
return found ? found->value : Qnil;
|
167
|
-
#else /* !HAVE_QSORT_BSEARCH */
|
168
135
|
unsigned i;
|
169
136
|
struct common_field *cf = common_http_fields;
|
170
137
|
for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
|
@@ -172,7 +139,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
172
139
|
return cf->value;
|
173
140
|
}
|
174
141
|
return Qnil;
|
175
|
-
#endif /* !HAVE_QSORT_BSEARCH */
|
176
142
|
}
|
177
143
|
|
178
144
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
@@ -401,7 +367,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
401
367
|
VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
|
402
368
|
|
403
369
|
if(puma_parser_has_error(http)) {
|
404
|
-
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
|
370
|
+
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
|
405
371
|
} else {
|
406
372
|
return INT2FIX(puma_parser_nread(http));
|
407
373
|
}
|
@@ -468,7 +434,6 @@ VALUE HttpParser_body(VALUE self) {
|
|
468
434
|
return http->body;
|
469
435
|
}
|
470
436
|
|
471
|
-
void Init_io_buffer(VALUE puma);
|
472
437
|
void Init_mini_ssl(VALUE mod);
|
473
438
|
|
474
439
|
void Init_puma_http11()
|
@@ -498,6 +463,5 @@ void Init_puma_http11()
|
|
498
463
|
rb_define_method(cHttpParser, "body", HttpParser_body, 0);
|
499
464
|
init_common_fields();
|
500
465
|
|
501
|
-
Init_io_buffer(mPuma);
|
502
466
|
Init_mini_ssl(mPuma);
|
503
467
|
}
|
data/lib/puma.rb
CHANGED
data/lib/puma/app/status.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Puma
|
4
6
|
module App
|
5
7
|
# Check out {#call}'s source code to see what actions this web application
|
@@ -17,10 +19,6 @@ module Puma
|
|
17
19
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
18
20
|
end
|
19
21
|
|
20
|
-
if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
|
21
|
-
require 'json'
|
22
|
-
end
|
23
|
-
|
24
22
|
case env['PATH_INFO']
|
25
23
|
when /\/stop$/
|
26
24
|
@cli.stop
|
@@ -56,7 +54,20 @@ module Puma
|
|
56
54
|
rack_response(200, GC.stat.to_json)
|
57
55
|
|
58
56
|
when /\/stats$/
|
59
|
-
rack_response(200, @cli.stats)
|
57
|
+
rack_response(200, @cli.stats.to_json)
|
58
|
+
|
59
|
+
when /\/thread-backtraces$/
|
60
|
+
backtraces = []
|
61
|
+
@cli.thread_status do |name, backtrace|
|
62
|
+
backtraces << { name: name, backtrace: backtrace }
|
63
|
+
end
|
64
|
+
|
65
|
+
rack_response(200, backtraces.to_json)
|
66
|
+
|
67
|
+
when /\/refork$/
|
68
|
+
Process.kill "SIGURG", $$
|
69
|
+
rack_response(200, OK_STATUS)
|
70
|
+
|
60
71
|
else
|
61
72
|
rack_response 404, "Unsupported action", 'text/plain'
|
62
73
|
end
|
data/lib/puma/binder.rb
CHANGED
@@ -11,7 +11,7 @@ module Puma
|
|
11
11
|
class Binder
|
12
12
|
include Puma::Const
|
13
13
|
|
14
|
-
RACK_VERSION = [1,
|
14
|
+
RACK_VERSION = [1,6].freeze
|
15
15
|
|
16
16
|
def initialize(events)
|
17
17
|
@events = events
|
@@ -43,7 +43,8 @@ module Puma
|
|
43
43
|
@ios = []
|
44
44
|
end
|
45
45
|
|
46
|
-
attr_reader :ios
|
46
|
+
attr_reader :ios, :listeners, :unix_paths, :proto_env, :envs, :activated_sockets, :inherited_fds
|
47
|
+
attr_writer :ios, :listeners
|
47
48
|
|
48
49
|
def env(sock)
|
49
50
|
@envs.fetch(sock, @proto_env)
|
@@ -53,40 +54,39 @@ module Puma
|
|
53
54
|
@ios.each { |i| i.close }
|
54
55
|
end
|
55
56
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
57
|
+
def connected_ports
|
58
|
+
ios.map { |io| io.addr[1] }.uniq
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_inherited_fds(env_hash)
|
62
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
63
|
+
fd, url = v.split(":", 2)
|
64
|
+
@inherited_fds[url] = fd.to_i
|
65
|
+
end.keys # pass keys back for removal
|
66
|
+
end
|
83
67
|
|
84
|
-
|
85
|
-
|
68
|
+
# systemd socket activation.
|
69
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
70
|
+
# LISTEN_PID = PID of the service process, aka us
|
71
|
+
# see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
72
|
+
def create_activated_fds(env_hash)
|
73
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
74
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
75
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
76
|
+
key = begin # Try to parse as a path
|
77
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
78
|
+
rescue ArgumentError # Try to parse as a port/ip
|
79
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
80
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
81
|
+
[:tcp, addr, port]
|
82
|
+
end
|
83
|
+
@activated_sockets[key] = sock
|
84
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
86
85
|
end
|
86
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
87
87
|
end
|
88
88
|
|
89
|
-
def parse(binds, logger)
|
89
|
+
def parse(binds, logger, log_msg = 'Listening')
|
90
90
|
binds.each do |str|
|
91
91
|
uri = URI.parse str
|
92
92
|
case uri.scheme
|
@@ -113,7 +113,7 @@ module Puma
|
|
113
113
|
i.local_address.ip_unpack.join(':')
|
114
114
|
end
|
115
115
|
|
116
|
-
logger.log "*
|
116
|
+
logger.log "* #{log_msg} on tcp://#{addr}"
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
@@ -149,7 +149,7 @@ module Puma
|
|
149
149
|
end
|
150
150
|
|
151
151
|
io = add_unix_listener path, umask, mode, backlog
|
152
|
-
logger.log "*
|
152
|
+
logger.log "* #{log_msg} on #{str}"
|
153
153
|
end
|
154
154
|
|
155
155
|
@listeners << [str, io]
|
@@ -204,12 +204,6 @@ module Puma
|
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
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
207
|
# Tell the server to listen on host +host+, port +port+.
|
214
208
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
215
209
|
# will be optimized for latency over throughput.
|
@@ -226,20 +220,17 @@ module Puma
|
|
226
220
|
end
|
227
221
|
|
228
222
|
host = host[1..-2] if host and host[0..0] == '['
|
229
|
-
|
223
|
+
tcp_server = TCPServer.new(host, port)
|
230
224
|
if optimize_for_latency
|
231
|
-
|
225
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
232
226
|
end
|
233
|
-
|
234
|
-
|
235
|
-
@connected_port = s.addr[1]
|
227
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
228
|
+
tcp_server.listen backlog
|
236
229
|
|
237
|
-
@ios <<
|
238
|
-
|
230
|
+
@ios << tcp_server
|
231
|
+
tcp_server
|
239
232
|
end
|
240
233
|
|
241
|
-
attr_reader :connected_port
|
242
|
-
|
243
234
|
def inherit_tcp_listener(host, port, fd)
|
244
235
|
if fd.kind_of? TCPServer
|
245
236
|
s = fd
|
@@ -360,26 +351,37 @@ module Puma
|
|
360
351
|
end
|
361
352
|
|
362
353
|
def close_listeners
|
363
|
-
|
364
|
-
io.close
|
354
|
+
listeners.each do |l, io|
|
355
|
+
io.close unless io.closed? # Ruby 2.2 issue
|
365
356
|
uri = URI.parse(l)
|
366
357
|
next unless uri.scheme == 'unix'
|
367
358
|
unix_path = "#{uri.host}#{uri.path}"
|
368
|
-
File.unlink unix_path if
|
359
|
+
File.unlink unix_path if unix_paths.include? unix_path
|
369
360
|
end
|
370
361
|
end
|
371
362
|
|
372
|
-
def
|
373
|
-
|
363
|
+
def redirects_for_restart
|
364
|
+
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
365
|
+
redirects[:close_others] = true
|
366
|
+
redirects
|
374
367
|
end
|
375
368
|
|
376
|
-
def
|
377
|
-
|
378
|
-
|
379
|
-
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
|
380
|
-
redirects[io.to_i] = io.to_i
|
369
|
+
def redirects_for_restart_env
|
370
|
+
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
371
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
381
372
|
end
|
382
|
-
|
373
|
+
end
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
def loopback_addresses
|
378
|
+
Socket.ip_address_list.select do |addrinfo|
|
379
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
380
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
381
|
+
end
|
382
|
+
|
383
|
+
def socket_activation_fd(int)
|
384
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
383
385
|
end
|
384
386
|
end
|
385
387
|
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
@@ -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,18 @@ 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
|
+
def can_close?
|
284
|
+
# Allow connection to close if it's received at least one full request
|
285
|
+
# and hasn't received any data for a future request.
|
286
|
+
#
|
287
|
+
# From RFC 2616 section 8.1.4:
|
288
|
+
# Servers SHOULD always respond to at least one request per connection,
|
289
|
+
# if at all possible.
|
290
|
+
@requests_served > 0 && @parsed_bytes == 0
|
291
|
+
end
|
292
|
+
|
270
293
|
private
|
271
294
|
|
272
295
|
def setup_body
|
@@ -285,16 +308,8 @@ module Puma
|
|
285
308
|
|
286
309
|
te = @env[TRANSFER_ENCODING2]
|
287
310
|
|
288
|
-
if te
|
289
|
-
|
290
|
-
te.split(",").each do |part|
|
291
|
-
if CHUNKED.casecmp(part.strip) == 0
|
292
|
-
return setup_chunked_body(body)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
elsif CHUNKED.casecmp(te) == 0
|
296
|
-
return setup_chunked_body(body)
|
297
|
-
end
|
311
|
+
if te && CHUNKED.casecmp(te) == 0
|
312
|
+
return setup_chunked_body(body)
|
298
313
|
end
|
299
314
|
|
300
315
|
@chunked_body = false
|
@@ -397,10 +412,7 @@ module Puma
|
|
397
412
|
raise EOFError
|
398
413
|
end
|
399
414
|
|
400
|
-
if decode_chunk(chunk)
|
401
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
402
|
-
return true
|
403
|
-
end
|
415
|
+
return true if decode_chunk(chunk)
|
404
416
|
end
|
405
417
|
end
|
406
418
|
|
@@ -413,28 +425,19 @@ module Puma
|
|
413
425
|
@body.binmode
|
414
426
|
@tempfile = @body
|
415
427
|
|
416
|
-
|
417
|
-
|
418
|
-
if decode_chunk(body)
|
419
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
420
|
-
return true
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
def write_chunk(str)
|
425
|
-
@chunked_content_length += @body.write(str)
|
428
|
+
return decode_chunk(body)
|
426
429
|
end
|
427
430
|
|
428
431
|
def decode_chunk(chunk)
|
429
432
|
if @partial_part_left > 0
|
430
433
|
if @partial_part_left <= chunk.size
|
431
434
|
if @partial_part_left > 2
|
432
|
-
|
435
|
+
@body << chunk[0..(@partial_part_left-3)] # skip the \r\n
|
433
436
|
end
|
434
437
|
chunk = chunk[@partial_part_left..-1]
|
435
438
|
@partial_part_left = 0
|
436
439
|
else
|
437
|
-
|
440
|
+
@body << chunk if @partial_part_left > 2 # don't include the last \r\n
|
438
441
|
@partial_part_left -= chunk.size
|
439
442
|
return false
|
440
443
|
end
|
@@ -481,12 +484,12 @@ module Puma
|
|
481
484
|
|
482
485
|
case
|
483
486
|
when got == len
|
484
|
-
|
487
|
+
@body << part[0..-3] # to skip the ending \r\n
|
485
488
|
when got <= len - 2
|
486
|
-
|
489
|
+
@body << part
|
487
490
|
@partial_part_left = len - part.size
|
488
491
|
when got == len - 1 # edge where we get just \r but not \n
|
489
|
-
|
492
|
+
@body << part[0..-2]
|
490
493
|
@partial_part_left = len - part.size
|
491
494
|
end
|
492
495
|
else
|