puma 5.0.0.beta1-java → 5.0.0.beta2-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 +23 -1
- data/README.md +3 -3
- data/docs/architecture.md +3 -3
- data/docs/deployment.md +6 -2
- data/docs/signals.md +4 -4
- 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 +12 -2
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +37 -6
- data/ext/puma_http11/puma_http11.c +1 -1
- data/lib/puma.rb +1 -0
- data/lib/puma/app/status.rb +4 -2
- data/lib/puma/binder.rb +5 -4
- data/lib/puma/client.rb +36 -9
- data/lib/puma/cluster.rb +8 -4
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/const.rb +1 -1
- data/lib/puma/dsl.rb +3 -3
- data/lib/puma/error_logger.rb +96 -0
- data/lib/puma/events.rb +33 -31
- data/lib/puma/launcher.rb +5 -2
- data/lib/puma/minissl.rb +34 -2
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/reactor.rb +2 -2
- data/lib/puma/runner.rb +1 -1
- data/lib/puma/server.rb +84 -44
- data/lib/puma/state_file.rb +1 -1
- data/lib/puma/thread_pool.rb +5 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35a07b62e45c13573cba434caa78d336c981104d65448bf2cba63cc7fe87ebe5
|
4
|
+
data.tar.gz: d05d3e5a89a5dec3f71a133024d596975bb5fb0a8b4e2a8c0342479f061132fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18498ab4e516ea2b386a8515c5ced3f2f5572e72f6281c87433f24f69b0ad94b55db2466a8f77df24b611c054353641ecce50b397b9cd698370bf81e3ba66369
|
7
|
+
data.tar.gz: 82efab2a81aebbd2eb50de04f906bb5f607f83121fe484f31feacd09cd481433d3f008278e8dcaf844a9b8ed3915e5985fa1a11fcf6d4f556eef24c87e9c6b3d
|
data/History.md
CHANGED
@@ -6,11 +6,12 @@
|
|
6
6
|
* EXPERIMENTAL: Added `nakayoshi_fork` option. Reduce memory usage in preloaded cluster-mode apps by GCing before fork and compacting, where available. (#2093, #2256)
|
7
7
|
* Added pumactl `thread-backtraces` command to print thread backtraces (#2054)
|
8
8
|
* Added incrementing `requests_count` to `Puma.stats`. (#2106)
|
9
|
-
* Increased maximum URI path length from 2048 to
|
9
|
+
* Increased maximum URI path length from 2048 to 8192 bytes (#2167, #2344)
|
10
10
|
* `lowlevel_error_handler` is now called during a forced threadpool shutdown, and if a callable with 3 arguments is set, we now also pass the status code (#2203)
|
11
11
|
* Faster phased restart and worker timeout (#2220)
|
12
12
|
* Added `state_permission` to config DSL to set state file permissions (#2238)
|
13
13
|
* Added `Puma.stats_hash`, which returns a stats in Hash instead of a JSON string (#2086, #2253)
|
14
|
+
* `rack.multithread` and `rack.multiprocess` now dynamically resolved by `max_thread` and `workers` respectively (#2288)
|
14
15
|
|
15
16
|
* Deprecations, Removals and Breaking API Changes
|
16
17
|
* `--control` has been removed. Use `--control-url` (#1487)
|
@@ -24,8 +25,12 @@
|
|
24
25
|
* Daemonization has been removed without replacement. (#2170)
|
25
26
|
* Changed #connected_port to #connected_ports (#2076)
|
26
27
|
* Configuration: `environment` is read from `RAILS_ENV`, if `RACK_ENV` can't be found (#2022)
|
28
|
+
* Log binding on http:// for TCP bindings to make it clickable
|
27
29
|
|
28
30
|
* Bugfixes
|
31
|
+
* Fix JSON loading issues on phased-restarts (#2269)
|
32
|
+
* Improve shutdown reliability (#2312, #2338)
|
33
|
+
* Close client http connections made to an ssl server with TLSv1.3 (#2116)
|
29
34
|
* Do not set user_config to quiet by default to allow for file config (#2074)
|
30
35
|
* Always close SSL connection in Puma::ControlCLI (#2211)
|
31
36
|
* Windows update extconf.rb for use with ssp and varied Ruby/MSYS2 combinations (#2069)
|
@@ -44,6 +49,15 @@
|
|
44
49
|
* Fix `UserFileDefaultOptions#fetch` to properly use `default` (#2233)
|
45
50
|
* Improvements to `out_of_band` hook (#2234)
|
46
51
|
* Prefer the rackup file specified by the CLI (#2225)
|
52
|
+
* Fix for spawning subprocesses with fork_worker option (#2267)
|
53
|
+
* Set `CONTENT_LENGTH` for chunked requests (#2287)
|
54
|
+
* JRuby - Add Puma::MiniSSL::Engine#init? and #teardown methods, run all SSL tests (#2317)
|
55
|
+
* Improve shutdown reliability (#2312)
|
56
|
+
* Resolve issue with threadpool waiting counter decrement when thread is killed
|
57
|
+
* Constrain rake-compiler version to 0.9.4 to fix `ClassNotFound` exception when using MiniSSL with Java8.
|
58
|
+
* Fix recursive `prune_bundler` (#2319).
|
59
|
+
* Ensure that TCP_CORK is usable
|
60
|
+
* Fix corner case when request body is chunked (#2326)
|
47
61
|
|
48
62
|
* Refactor
|
49
63
|
* Remove unused loader argument from Plugin initializer (#2095)
|
@@ -54,6 +68,14 @@
|
|
54
68
|
* ThreadPool concurrency refactoring (#2220)
|
55
69
|
* JSON parse cluster worker stats instead of regex (#2124)
|
56
70
|
* Support parallel tests in verbose progress reporting (#2223)
|
71
|
+
* Refactor error handling in server accept loop (#2239)
|
72
|
+
|
73
|
+
## 4.3.4/4.3.5 and 3.12.5/3.12.6 / 2020-05-22
|
74
|
+
|
75
|
+
Each patchlevel release contains a separate security fix. We recommend simply upgrading to 4.3.5/3.12.6.
|
76
|
+
|
77
|
+
* Security
|
78
|
+
* Fix: Fixed two separate HTTP smuggling vulnerabilities that used the Transfer-Encoding header. CVE-2020-11076 and CVE-2020-11077.
|
57
79
|
|
58
80
|
## 4.3.3 and 3.12.4 / 2020-02-28
|
59
81
|
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
[![Code Climate](https://codeclimate.com/github/puma/puma.svg)](https://codeclimate.com/github/puma/puma)
|
10
10
|
[![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=puma&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=puma&package-manager=bundler&version-scheme=semver)
|
11
|
-
[![StackOverflow](
|
11
|
+
[![StackOverflow](https://img.shields.io/badge/stackoverflow-Puma-blue.svg)]( https://stackoverflow.com/questions/tagged/puma )
|
12
12
|
|
13
13
|
Puma is a **simple, fast, multi-threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications**.
|
14
14
|
|
@@ -27,7 +27,7 @@ $ gem install puma
|
|
27
27
|
$ puma
|
28
28
|
```
|
29
29
|
|
30
|
-
Without arguments, puma will look for a rackup (.ru) file in
|
30
|
+
Without arguments, puma will look for a rackup (.ru) file in
|
31
31
|
working directory called `config.ru`.
|
32
32
|
|
33
33
|
## Frameworks
|
@@ -135,7 +135,7 @@ Preloading can’t be used with phased restart, since phased restart kills and r
|
|
135
135
|
If puma encounters an error outside of the context of your application, it will respond with a 500 and a simple
|
136
136
|
textual error message (see `lowlevel_error` in [this file](https://github.com/puma/puma/blob/master/lib/puma/server.rb)).
|
137
137
|
You can specify custom behavior for this scenario. For example, you can report the error to your third-party
|
138
|
-
error-tracking service (in this example, [rollbar](
|
138
|
+
error-tracking service (in this example, [rollbar](https://rollbar.com)):
|
139
139
|
|
140
140
|
```ruby
|
141
141
|
lowlevel_error_handler do |e|
|
data/docs/architecture.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
## Overview
|
4
4
|
|
5
|
-
![
|
5
|
+
![https://bit.ly/2iJuFky](images/puma-general-arch.png)
|
6
6
|
|
7
7
|
Puma is a threaded web server, processing requests across a TCP or UNIX socket.
|
8
8
|
|
@@ -12,7 +12,7 @@ Clustered mode is shown/discussed here. Single mode is analogous to having a sin
|
|
12
12
|
|
13
13
|
## Connection pipeline
|
14
14
|
|
15
|
-
![
|
15
|
+
![https://bit.ly/2zwzhEK](images/puma-connection-flow.png)
|
16
16
|
|
17
17
|
* Upon startup, Puma listens on a TCP or UNIX socket.
|
18
18
|
* The backlog of this socket is configured (with a default of 1024), determining how many established but unaccepted connections can exist concurrently.
|
@@ -29,7 +29,7 @@ Clustered mode is shown/discussed here. Single mode is analogous to having a sin
|
|
29
29
|
|
30
30
|
### Disabling `queue_requests`
|
31
31
|
|
32
|
-
![
|
32
|
+
![https://bit.ly/2zxCJ1Z](images/puma-connection-flow-no-reactor.png)
|
33
33
|
|
34
34
|
The `queue_requests` option is `true` by default, enabling the separate thread used to buffer requests as described above.
|
35
35
|
|
data/docs/deployment.md
CHANGED
@@ -20,7 +20,10 @@ Welcome back!
|
|
20
20
|
Puma was originally conceived as a thread-only webserver, but grew the ability to
|
21
21
|
also use processes in version 2.
|
22
22
|
|
23
|
-
|
23
|
+
To run puma in single mode (e.g. for a development environment) you will need to
|
24
|
+
set the number of workers to 0, anything above will run in cluster mode.
|
25
|
+
|
26
|
+
Here are some rules of thumb for cluster mode:
|
24
27
|
|
25
28
|
### MRI
|
26
29
|
|
@@ -66,7 +69,8 @@ thread to become available.
|
|
66
69
|
|
67
70
|
* Have your upstream proxy set a header with the time it received the request:
|
68
71
|
* nginx: `proxy_set_header X-Request-Start "${msec}";`
|
69
|
-
* haproxy: `http-request set-header X-Request-Start
|
72
|
+
* haproxy >= 1.9: `http-request set-header X-Request-Start t=%[date()]%[date_us()]`
|
73
|
+
* haproxy < 1.9: `http-request set-header X-Request-Start t=%[date()]`
|
70
74
|
* In your Rack middleware, determine the amount of time elapsed since `X-Request-Start`.
|
71
75
|
* To improve accuracy, you will want to subtract time spent waiting for slow clients:
|
72
76
|
* `env['puma.request_body_wait']` contains the number of milliseconds Puma spent
|
data/docs/signals.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
The [unix signal](
|
1
|
+
The [unix signal](https://en.wikipedia.org/wiki/Unix_signal) is a method of sending messages between [processes](https://en.wikipedia.org/wiki/Process_(computing)). When a signal is sent, the operating system interrupts the target process's normal flow of execution. There are standard signals that are used to stop a process but there are also custom signals that can be used for other purposes. This document is an attempt to list all supported signals that Puma will respond to. In general, signals need only be sent to the master process of a cluster.
|
2
2
|
|
3
3
|
## Sending Signals
|
4
4
|
|
5
|
-
If you are new to signals it can be useful to see how they can be used. When a process is created in a *nix like operating system it will have a [PID - or process identifier](
|
5
|
+
If you are new to signals it can be useful to see how they can be used. When a process is created in a *nix like operating system it will have a [PID - or process identifier](https://en.wikipedia.org/wiki/Process_identifier) that can be used to send signals to the process. For demonstration we will create an infinitely running process by tailing a file:
|
6
6
|
|
7
7
|
```sh
|
8
8
|
$ echo "foo" >> my.log
|
@@ -17,13 +17,13 @@ $ ps aux | grep tail
|
|
17
17
|
schneems 87152 0.0 0.0 2432772 492 s032 S+ 12:46PM 0:00.00 tail -f my.log
|
18
18
|
```
|
19
19
|
|
20
|
-
You can send a signal in Ruby using the [Process module](
|
20
|
+
You can send a signal in Ruby using the [Process module](https://www.ruby-doc.org/core-2.1.1/Process.html#kill-method):
|
21
21
|
|
22
22
|
```
|
23
23
|
$ irb
|
24
24
|
> puts pid
|
25
25
|
=> 87152
|
26
|
-
Process.detach(pid) #
|
26
|
+
Process.detach(pid) # https://ruby-doc.org/core-2.1.1/Process.html#method-c-detach
|
27
27
|
Process.kill("TERM", pid)
|
28
28
|
```
|
29
29
|
|
@@ -14,12 +14,14 @@
|
|
14
14
|
|
15
15
|
/*
|
16
16
|
* capitalizes all lower-case ASCII characters,
|
17
|
-
* converts dashes to underscores.
|
17
|
+
* converts dashes to underscores, and underscores to commas.
|
18
18
|
*/
|
19
19
|
static void snake_upcase_char(char *c)
|
20
20
|
{
|
21
21
|
if (*c >= 'a' && *c <= 'z')
|
22
22
|
*c &= ~0x20;
|
23
|
+
else if (*c == '_')
|
24
|
+
*c = ',';
|
23
25
|
else if (*c == '-')
|
24
26
|
*c = '_';
|
25
27
|
}
|
@@ -12,12 +12,14 @@
|
|
12
12
|
|
13
13
|
/*
|
14
14
|
* capitalizes all lower-case ASCII characters,
|
15
|
-
* converts dashes to underscores.
|
15
|
+
* converts dashes to underscores, and underscores to commas.
|
16
16
|
*/
|
17
17
|
static void snake_upcase_char(char *c)
|
18
18
|
{
|
19
19
|
if (*c >= 'a' && *c <= 'z')
|
20
20
|
*c &= ~0x20;
|
21
|
+
else if (*c == '_')
|
22
|
+
*c = ',';
|
21
23
|
else if (*c == '-')
|
22
24
|
*c = '_';
|
23
25
|
}
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -301,6 +301,7 @@ void raise_error(SSL* ssl, int result) {
|
|
301
301
|
char msg[512];
|
302
302
|
const char* err_str;
|
303
303
|
int err = errno;
|
304
|
+
int mask = 4095;
|
304
305
|
int ssl_err = SSL_get_error(ssl, result);
|
305
306
|
int verify_err = (int) SSL_get_verify_result(ssl);
|
306
307
|
|
@@ -317,8 +318,8 @@ void raise_error(SSL* ssl, int result) {
|
|
317
318
|
} else {
|
318
319
|
err = (int) ERR_get_error();
|
319
320
|
ERR_error_string_n(err, buf, sizeof(buf));
|
320
|
-
|
321
|
-
|
321
|
+
int errexp = err & mask;
|
322
|
+
snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, errexp);
|
322
323
|
}
|
323
324
|
} else {
|
324
325
|
snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
|
@@ -462,6 +463,13 @@ VALUE engine_peercert(VALUE self) {
|
|
462
463
|
return rb_cert_buf;
|
463
464
|
}
|
464
465
|
|
466
|
+
static VALUE
|
467
|
+
engine_ssl_vers_st(VALUE self) {
|
468
|
+
ms_conn* conn;
|
469
|
+
Data_Get_Struct(self, ms_conn, conn);
|
470
|
+
return rb_ary_new3(2, rb_str_new2(SSL_get_version(conn->ssl)), rb_str_new2(SSL_state_string(conn->ssl)));
|
471
|
+
}
|
472
|
+
|
465
473
|
VALUE noop(VALUE self) {
|
466
474
|
return Qnil;
|
467
475
|
}
|
@@ -533,6 +541,8 @@ void Init_mini_ssl(VALUE puma) {
|
|
533
541
|
rb_define_method(eng, "init?", engine_init, 0);
|
534
542
|
|
535
543
|
rb_define_method(eng, "peercert", engine_peercert, 0);
|
544
|
+
|
545
|
+
rb_define_method(eng, "ssl_vers_st", engine_ssl_vers_st, 0);
|
536
546
|
}
|
537
547
|
|
538
548
|
#else
|
@@ -120,6 +120,8 @@ public class MiniSSL extends RubyObject {
|
|
120
120
|
}
|
121
121
|
|
122
122
|
private SSLEngine engine;
|
123
|
+
private boolean closed;
|
124
|
+
private boolean handshake;
|
123
125
|
private MiniSSLBuffer inboundNetData;
|
124
126
|
private MiniSSLBuffer outboundAppData;
|
125
127
|
private MiniSSLBuffer outboundNetData;
|
@@ -157,6 +159,8 @@ public class MiniSSL extends RubyObject {
|
|
157
159
|
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
158
160
|
|
159
161
|
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
162
|
+
closed = false;
|
163
|
+
handshake = false;
|
160
164
|
engine = sslCtx.createSSLEngine();
|
161
165
|
|
162
166
|
String[] protocols;
|
@@ -173,7 +177,7 @@ public class MiniSSL extends RubyObject {
|
|
173
177
|
engine.setEnabledProtocols(protocols);
|
174
178
|
engine.setUseClientMode(false);
|
175
179
|
|
176
|
-
long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
|
180
|
+
long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger("to_i").getLongValue();
|
177
181
|
if ((verify_mode & 0x1) != 0) { // 'peer'
|
178
182
|
engine.setWantClientAuth(true);
|
179
183
|
}
|
@@ -240,14 +244,21 @@ public class MiniSSL extends RubyObject {
|
|
240
244
|
// need to wait for more data to come in before we retry
|
241
245
|
retryOp = false;
|
242
246
|
break;
|
247
|
+
case CLOSED:
|
248
|
+
closed = true;
|
249
|
+
retryOp = false;
|
250
|
+
break;
|
243
251
|
default:
|
244
|
-
// other
|
252
|
+
// other case is OK. We're done here.
|
245
253
|
retryOp = false;
|
246
254
|
}
|
255
|
+
if (res.getHandshakeStatus() == HandshakeStatus.FINISHED) {
|
256
|
+
handshake = true;
|
257
|
+
}
|
247
258
|
}
|
248
259
|
|
249
260
|
// after each op, run any delegated tasks if needed
|
250
|
-
if(
|
261
|
+
if(res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
|
251
262
|
Runnable runnable;
|
252
263
|
while ((runnable = engine.getDelegatedTask()) != null) {
|
253
264
|
runnable.run();
|
@@ -271,13 +282,14 @@ public class MiniSSL extends RubyObject {
|
|
271
282
|
|
272
283
|
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
273
284
|
boolean done = false;
|
285
|
+
SSLEngineResult res = null;
|
274
286
|
while (!done) {
|
275
287
|
switch (handshakeStatus) {
|
276
288
|
case NEED_WRAP:
|
277
|
-
doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
289
|
+
res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
278
290
|
break;
|
279
291
|
case NEED_UNWRAP:
|
280
|
-
|
292
|
+
res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
281
293
|
if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
|
282
294
|
// need more data before we can shake more hands
|
283
295
|
done = true;
|
@@ -286,7 +298,9 @@ public class MiniSSL extends RubyObject {
|
|
286
298
|
default:
|
287
299
|
done = true;
|
288
300
|
}
|
289
|
-
|
301
|
+
if (!done) {
|
302
|
+
handshakeStatus = res.getHandshakeStatus();
|
303
|
+
}
|
290
304
|
}
|
291
305
|
|
292
306
|
if (inboundNetData.hasRemaining()) {
|
@@ -360,4 +374,21 @@ public class MiniSSL extends RubyObject {
|
|
360
374
|
return getRuntime().getNil();
|
361
375
|
}
|
362
376
|
}
|
377
|
+
|
378
|
+
@JRubyMethod(name = "init?")
|
379
|
+
public IRubyObject isInit(ThreadContext context) {
|
380
|
+
return handshake ? getRuntime().getFalse() : getRuntime().getTrue();
|
381
|
+
}
|
382
|
+
|
383
|
+
@JRubyMethod
|
384
|
+
public IRubyObject shutdown() {
|
385
|
+
if (closed || engine.isInboundDone() && engine.isOutboundDone()) {
|
386
|
+
if (engine.isOutboundDone()) {
|
387
|
+
engine.closeOutbound();
|
388
|
+
}
|
389
|
+
return getRuntime().getTrue();
|
390
|
+
} else {
|
391
|
+
return getRuntime().getFalse();
|
392
|
+
}
|
393
|
+
}
|
363
394
|
}
|
@@ -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, 8192);
|
58
58
|
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
59
59
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
60
60
|
|
data/lib/puma.rb
CHANGED
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
|
data/lib/puma/binder.rb
CHANGED
@@ -6,6 +6,7 @@ require 'socket'
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
8
|
require 'puma/minissl/context_builder'
|
9
|
+
require 'puma/configuration'
|
9
10
|
|
10
11
|
module Puma
|
11
12
|
class Binder
|
@@ -13,7 +14,7 @@ module Puma
|
|
13
14
|
|
14
15
|
RACK_VERSION = [1,6].freeze
|
15
16
|
|
16
|
-
def initialize(events)
|
17
|
+
def initialize(events, conf = Configuration.new)
|
17
18
|
@events = events
|
18
19
|
@listeners = []
|
19
20
|
@inherited_fds = {}
|
@@ -23,8 +24,8 @@ module Puma
|
|
23
24
|
@proto_env = {
|
24
25
|
"rack.version".freeze => RACK_VERSION,
|
25
26
|
"rack.errors".freeze => events.stderr,
|
26
|
-
"rack.multithread".freeze =>
|
27
|
-
"rack.multiprocess".freeze =>
|
27
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
28
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
28
29
|
"rack.run_once".freeze => false,
|
29
30
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
30
31
|
|
@@ -113,7 +114,7 @@ module Puma
|
|
113
114
|
i.local_address.ip_unpack.join(':')
|
114
115
|
end
|
115
116
|
|
116
|
-
logger.log "* #{log_msg} on
|
117
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
data/lib/puma/client.rb
CHANGED
@@ -308,8 +308,16 @@ module Puma
|
|
308
308
|
|
309
309
|
te = @env[TRANSFER_ENCODING2]
|
310
310
|
|
311
|
-
if te
|
312
|
-
|
311
|
+
if te
|
312
|
+
if te.include?(",")
|
313
|
+
te.split(",").each do |part|
|
314
|
+
if CHUNKED.casecmp(part.strip) == 0
|
315
|
+
return setup_chunked_body(body)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
elsif CHUNKED.casecmp(te) == 0
|
319
|
+
return setup_chunked_body(body)
|
320
|
+
end
|
313
321
|
end
|
314
322
|
|
315
323
|
@chunked_body = false
|
@@ -412,7 +420,10 @@ module Puma
|
|
412
420
|
raise EOFError
|
413
421
|
end
|
414
422
|
|
415
|
-
|
423
|
+
if decode_chunk(chunk)
|
424
|
+
@env[CONTENT_LENGTH] = @chunked_content_length
|
425
|
+
return true
|
426
|
+
end
|
416
427
|
end
|
417
428
|
end
|
418
429
|
|
@@ -424,20 +435,36 @@ module Puma
|
|
424
435
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
425
436
|
@body.binmode
|
426
437
|
@tempfile = @body
|
438
|
+
@chunked_content_length = 0
|
439
|
+
|
440
|
+
if decode_chunk(body)
|
441
|
+
@env[CONTENT_LENGTH] = @chunked_content_length
|
442
|
+
return true
|
443
|
+
end
|
444
|
+
end
|
427
445
|
|
428
|
-
|
446
|
+
def write_chunk(str)
|
447
|
+
@chunked_content_length += @body.write(str)
|
429
448
|
end
|
430
449
|
|
431
450
|
def decode_chunk(chunk)
|
432
451
|
if @partial_part_left > 0
|
433
452
|
if @partial_part_left <= chunk.size
|
434
453
|
if @partial_part_left > 2
|
435
|
-
|
454
|
+
write_chunk(chunk[0..(@partial_part_left-3)]) # skip the \r\n
|
436
455
|
end
|
437
456
|
chunk = chunk[@partial_part_left..-1]
|
438
457
|
@partial_part_left = 0
|
439
458
|
else
|
440
|
-
|
459
|
+
if @partial_part_left > 2
|
460
|
+
if @partial_part_left == chunk.size + 1
|
461
|
+
# Don't include the last \r
|
462
|
+
write_chunk(chunk[0..(@partial_part_left-3)])
|
463
|
+
else
|
464
|
+
# don't include the last \r\n
|
465
|
+
write_chunk(chunk)
|
466
|
+
end
|
467
|
+
end
|
441
468
|
@partial_part_left -= chunk.size
|
442
469
|
return false
|
443
470
|
end
|
@@ -484,12 +511,12 @@ module Puma
|
|
484
511
|
|
485
512
|
case
|
486
513
|
when got == len
|
487
|
-
|
514
|
+
write_chunk(part[0..-3]) # to skip the ending \r\n
|
488
515
|
when got <= len - 2
|
489
|
-
|
516
|
+
write_chunk(part)
|
490
517
|
@partial_part_left = len - part.size
|
491
518
|
when got == len - 1 # edge where we get just \r but not \n
|
492
|
-
|
519
|
+
write_chunk(part[0..-2])
|
493
520
|
@partial_part_left = len - part.size
|
494
521
|
end
|
495
522
|
else
|
data/lib/puma/cluster.rb
CHANGED
@@ -5,7 +5,6 @@ require 'puma/util'
|
|
5
5
|
require 'puma/plugin'
|
6
6
|
|
7
7
|
require 'time'
|
8
|
-
require 'json'
|
9
8
|
|
10
9
|
module Puma
|
11
10
|
# This class is instantiated by the `Puma::Launcher` and used
|
@@ -95,6 +94,7 @@ module Puma
|
|
95
94
|
|
96
95
|
def ping!(status)
|
97
96
|
@last_checkin = Time.now
|
97
|
+
require 'json'
|
98
98
|
@last_status = JSON.parse(status, symbolize_names: true)
|
99
99
|
end
|
100
100
|
|
@@ -248,6 +248,7 @@ module Puma
|
|
248
248
|
$0 = title
|
249
249
|
|
250
250
|
Signal.trap "SIGINT", "IGNORE"
|
251
|
+
Signal.trap "SIGCHLD", "DEFAULT"
|
251
252
|
|
252
253
|
fork_worker = @options[:fork_worker] && index == 0
|
253
254
|
|
@@ -284,9 +285,11 @@ module Puma
|
|
284
285
|
|
285
286
|
if fork_worker
|
286
287
|
restart_server.clear
|
288
|
+
worker_pids = []
|
287
289
|
Signal.trap "SIGCHLD" do
|
288
|
-
|
289
|
-
|
290
|
+
wakeup! if worker_pids.reject! do |p|
|
291
|
+
Process.wait(p, Process::WNOHANG) rescue true
|
292
|
+
end
|
290
293
|
end
|
291
294
|
|
292
295
|
Thread.new do
|
@@ -303,7 +306,7 @@ module Puma
|
|
303
306
|
elsif idx == 0 # restart server
|
304
307
|
restart_server << true << false
|
305
308
|
else # fork worker
|
306
|
-
pid = spawn_worker(idx, master)
|
309
|
+
worker_pids << pid = spawn_worker(idx, master)
|
307
310
|
@worker_write << "f#{pid}:#{idx}\n" rescue nil
|
308
311
|
end
|
309
312
|
end
|
@@ -330,6 +333,7 @@ module Puma
|
|
330
333
|
while true
|
331
334
|
sleep Const::WORKER_CHECK_INTERVAL
|
332
335
|
begin
|
336
|
+
require 'json'
|
333
337
|
io << "p#{Process.pid}#{server.stats.to_json}\n"
|
334
338
|
rescue IOError
|
335
339
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
data/lib/puma/commonlogger.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Puma
|
4
4
|
# Rack::CommonLogger forwards every request to the given +app+, and
|
5
5
|
# logs a line in the
|
6
|
-
# {Apache common log format}[
|
6
|
+
# {Apache common log format}[https://httpd.apache.org/docs/1.3/logs.html#common]
|
7
7
|
# to the +logger+.
|
8
8
|
#
|
9
9
|
# If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
|
@@ -16,7 +16,7 @@ module Puma
|
|
16
16
|
# (which is called without arguments in order to make the error appear for
|
17
17
|
# sure)
|
18
18
|
class CommonLogger
|
19
|
-
# Common Log Format:
|
19
|
+
# Common Log Format: https://httpd.apache.org/docs/1.3/logs.html#common
|
20
20
|
#
|
21
21
|
# lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
|
22
22
|
#
|
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 = "5.0.0.
|
103
|
+
PUMA_VERSION = VERSION = "5.0.0.beta2".freeze
|
104
104
|
CODE_NAME = "Spoony Bard".freeze
|
105
105
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
106
|
|
data/lib/puma/dsl.rb
CHANGED
@@ -443,8 +443,8 @@ module Puma
|
|
443
443
|
#
|
444
444
|
# @note Cluster mode only.
|
445
445
|
# @example
|
446
|
-
#
|
447
|
-
# puts 'Before worker
|
446
|
+
# on_worker_boot do
|
447
|
+
# puts 'Before worker boot...'
|
448
448
|
# end
|
449
449
|
def on_worker_boot(&block)
|
450
450
|
@options[:before_worker_boot] ||= []
|
@@ -769,7 +769,7 @@ module Puma
|
|
769
769
|
# also increase time to boot and fork. See your logs for details on how much
|
770
770
|
# time this adds to your boot process. For most apps, it will be less than one
|
771
771
|
# second.
|
772
|
-
def nakayoshi_fork(enabled=
|
772
|
+
def nakayoshi_fork(enabled=true)
|
773
773
|
@options[:nakayoshi_fork] = enabled
|
774
774
|
end
|
775
775
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'puma/const'
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
# The implementation of a detailed error logging.
|
7
|
+
#
|
8
|
+
class ErrorLogger
|
9
|
+
include Const
|
10
|
+
|
11
|
+
attr_reader :ioerr
|
12
|
+
|
13
|
+
REQUEST_FORMAT = %{"%s %s%s" - (%s)}
|
14
|
+
|
15
|
+
def initialize(ioerr)
|
16
|
+
@ioerr = ioerr
|
17
|
+
@ioerr.sync = true
|
18
|
+
|
19
|
+
@debug = ENV.key? 'PUMA_DEBUG'
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.stdio
|
23
|
+
new $stderr
|
24
|
+
end
|
25
|
+
|
26
|
+
# Print occured error details.
|
27
|
+
# +options+ hash with additional options:
|
28
|
+
# - +error+ is an exception object
|
29
|
+
# - +req+ the http request
|
30
|
+
# - +text+ (default nil) custom string to print in title
|
31
|
+
# and before all remaining info.
|
32
|
+
#
|
33
|
+
def info(options={})
|
34
|
+
ioerr.puts title(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Print occured error details only if
|
38
|
+
# environment variable PUMA_DEBUG is defined.
|
39
|
+
# +options+ hash with additional options:
|
40
|
+
# - +error+ is an exception object
|
41
|
+
# - +req+ the http request
|
42
|
+
# - +text+ (default nil) custom string to print in title
|
43
|
+
# and before all remaining info.
|
44
|
+
#
|
45
|
+
def debug(options={})
|
46
|
+
return unless @debug
|
47
|
+
|
48
|
+
error = options[:error]
|
49
|
+
req = options[:req]
|
50
|
+
|
51
|
+
string_block = []
|
52
|
+
string_block << title(options)
|
53
|
+
string_block << request_dump(req) if req
|
54
|
+
string_block << error_backtrace(options) if error
|
55
|
+
|
56
|
+
ioerr.puts string_block.join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
def title(options={})
|
60
|
+
text = options[:text]
|
61
|
+
req = options[:req]
|
62
|
+
error = options[:error]
|
63
|
+
|
64
|
+
string_block = ["#{Time.now}"]
|
65
|
+
string_block << " #{text}" if text
|
66
|
+
string_block << " (#{request_title(req)})" if request_parsed?(req)
|
67
|
+
string_block << ": #{error.inspect}" if error
|
68
|
+
string_block.join('')
|
69
|
+
end
|
70
|
+
|
71
|
+
def request_dump(req)
|
72
|
+
"Headers: #{request_headers(req)}\n" \
|
73
|
+
"Body: #{req.body}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def request_title(req)
|
77
|
+
env = req.env
|
78
|
+
|
79
|
+
REQUEST_FORMAT % [
|
80
|
+
env[REQUEST_METHOD],
|
81
|
+
env[REQUEST_PATH] || env[PATH_INFO],
|
82
|
+
env[QUERY_STRING] || "",
|
83
|
+
env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-"
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
def request_headers(req)
|
88
|
+
headers = req.env.select { |key, _| key.start_with?('HTTP_') }
|
89
|
+
headers.map { |key, value| [key[5..-1], value] }.to_h.inspect
|
90
|
+
end
|
91
|
+
|
92
|
+
def request_parsed?(req)
|
93
|
+
req && req.env[REQUEST_METHOD]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/puma/events.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'puma/const'
|
4
3
|
require "puma/null_io"
|
4
|
+
require 'puma/error_logger'
|
5
5
|
require 'stringio'
|
6
6
|
|
7
7
|
module Puma
|
@@ -23,8 +23,6 @@ module Puma
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
include Const
|
27
|
-
|
28
26
|
# Create an Events object that prints to +stdout+ and +stderr+.
|
29
27
|
#
|
30
28
|
def initialize(stdout, stderr)
|
@@ -36,6 +34,7 @@ module Puma
|
|
36
34
|
@stderr.sync = true
|
37
35
|
|
38
36
|
@debug = ENV.key? 'PUMA_DEBUG'
|
37
|
+
@error_logger = ErrorLogger.new(@stderr)
|
39
38
|
|
40
39
|
@hooks = Hash.new { |h,k| h[k] = [] }
|
41
40
|
end
|
@@ -66,7 +65,8 @@ module Puma
|
|
66
65
|
# Write +str+ to +@stdout+
|
67
66
|
#
|
68
67
|
def log(str)
|
69
|
-
@stdout.puts format(str)
|
68
|
+
@stdout.puts format(str) if @stdout.respond_to? :puts
|
69
|
+
rescue Errno::EPIPE
|
70
70
|
end
|
71
71
|
|
72
72
|
def write(str)
|
@@ -80,7 +80,7 @@ module Puma
|
|
80
80
|
# Write +str+ to +@stderr+
|
81
81
|
#
|
82
82
|
def error(str)
|
83
|
-
@
|
83
|
+
@error_logger.info(text: format("ERROR: #{str}"))
|
84
84
|
exit 1
|
85
85
|
end
|
86
86
|
|
@@ -88,43 +88,45 @@ module Puma
|
|
88
88
|
formatter.call(str)
|
89
89
|
end
|
90
90
|
|
91
|
+
# An HTTP connection error has occurred.
|
92
|
+
# +error+ a connection exception, +req+ the request,
|
93
|
+
# and +text+ additional info
|
94
|
+
#
|
95
|
+
def connection_error(error, req, text="HTTP connection error")
|
96
|
+
@error_logger.info(error: error, req: req, text: text)
|
97
|
+
end
|
98
|
+
|
91
99
|
# An HTTP parse error has occurred.
|
92
|
-
# +
|
93
|
-
#
|
100
|
+
# +error+ a parsing exception,
|
101
|
+
# and +req+ the request.
|
94
102
|
#
|
95
|
-
def parse_error(
|
96
|
-
@
|
97
|
-
"(#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}#{env[REQUEST_PATH]}): " \
|
98
|
-
"#{error.inspect}" \
|
99
|
-
"\n---\n"
|
103
|
+
def parse_error(error, req)
|
104
|
+
@error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
|
100
105
|
end
|
101
106
|
|
102
107
|
# An SSL error has occurred.
|
103
|
-
# +
|
104
|
-
# any peer certificate (if present)
|
108
|
+
# +error+ an exception object, +peeraddr+ peer address,
|
109
|
+
# and +peercert+ any peer certificate (if present).
|
105
110
|
#
|
106
|
-
def ssl_error(
|
111
|
+
def ssl_error(error, peeraddr, peercert)
|
107
112
|
subject = peercert ? peercert.subject : nil
|
108
|
-
@
|
113
|
+
@error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
|
109
114
|
end
|
110
115
|
|
111
116
|
# An unknown error has occurred.
|
112
|
-
# +
|
113
|
-
# +
|
117
|
+
# +error+ an exception object, +req+ the request,
|
118
|
+
# and +text+ additional info
|
114
119
|
#
|
115
|
-
def unknown_error(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
string_block << error.backtrace
|
126
|
-
@stderr.puts string_block.join("\n")
|
127
|
-
end
|
120
|
+
def unknown_error(error, req=nil, text="Unknown error")
|
121
|
+
@error_logger.info(error: error, req: req, text: text)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Log occurred error debug dump.
|
125
|
+
# +error+ an exception object, +req+ the request,
|
126
|
+
# and +text+ additional info
|
127
|
+
#
|
128
|
+
def debug_error(error, req=nil, text="")
|
129
|
+
@error_logger.debug(error: error, req: req, text: text)
|
128
130
|
end
|
129
131
|
|
130
132
|
def on_booted(&block)
|
data/lib/puma/launcher.rb
CHANGED
@@ -47,7 +47,7 @@ module Puma
|
|
47
47
|
@original_argv = @argv.dup
|
48
48
|
@config = conf
|
49
49
|
|
50
|
-
@binder = Binder.new(@events)
|
50
|
+
@binder = Binder.new(@events, conf)
|
51
51
|
@binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
|
52
52
|
@binder.create_activated_fds(ENV).each { |k| ENV.delete k }
|
53
53
|
|
@@ -111,6 +111,7 @@ module Puma
|
|
111
111
|
sf.pid = Process.pid
|
112
112
|
sf.control_url = @options[:control_url]
|
113
113
|
sf.control_auth_token = @options[:control_auth_token]
|
114
|
+
sf.running_from = File.expand_path('.')
|
114
115
|
|
115
116
|
sf.save path, permission
|
116
117
|
end
|
@@ -172,12 +173,13 @@ module Puma
|
|
172
173
|
case @status
|
173
174
|
when :halt
|
174
175
|
log "* Stopping immediately!"
|
176
|
+
@runner.stop_control
|
175
177
|
when :run, :stop
|
176
178
|
graceful_stop
|
177
179
|
when :restart
|
178
180
|
log "* Restarting..."
|
179
181
|
ENV.replace(previous_env)
|
180
|
-
@runner.
|
182
|
+
@runner.stop_control
|
181
183
|
restart!
|
182
184
|
when :exit
|
183
185
|
# nothing
|
@@ -286,6 +288,7 @@ module Puma
|
|
286
288
|
end
|
287
289
|
|
288
290
|
def prune_bundler
|
291
|
+
return if ENV['PUMA_BUNDLER_PRUNED']
|
289
292
|
return unless defined?(Bundler)
|
290
293
|
require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
|
291
294
|
unless puma_wild_location
|
data/lib/puma/minissl.rb
CHANGED
@@ -5,8 +5,18 @@ begin
|
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
|
+
# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
|
9
|
+
require 'puma/puma_http11'
|
10
|
+
|
8
11
|
module Puma
|
9
12
|
module MiniSSL
|
13
|
+
|
14
|
+
# define constant at runtime, as it's easy to determine at built time,
|
15
|
+
# but Puma could (it shouldn't) be loaded with an older OpenSSL version
|
16
|
+
HAS_TLS1_3 = !IS_JRUBY &&
|
17
|
+
(OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
|
18
|
+
(OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1
|
19
|
+
|
10
20
|
class Socket
|
11
21
|
def initialize(socket, engine)
|
12
22
|
@socket = socket
|
@@ -22,6 +32,24 @@ module Puma
|
|
22
32
|
@socket.closed?
|
23
33
|
end
|
24
34
|
|
35
|
+
# returns a two element array
|
36
|
+
# first is protocol version (SSL_get_version)
|
37
|
+
# second is 'handshake' state (SSL_state_string)
|
38
|
+
#
|
39
|
+
# used for dropping tcp connections to ssl
|
40
|
+
# see OpenSSL ssl/ssl_stat.c SSL_state_string for info
|
41
|
+
#
|
42
|
+
def ssl_version_state
|
43
|
+
IS_JRUBY ? [nil, nil] : @engine.ssl_vers_st
|
44
|
+
end
|
45
|
+
|
46
|
+
# used to check the handshake status, in particular when a TCP connection
|
47
|
+
# is made with TLSv1.3 as an available protocol
|
48
|
+
def bad_tlsv1_3?
|
49
|
+
HAS_TLS1_3 && @engine.ssl_vers_st == ['TLSv1.3', 'SSLERR']
|
50
|
+
end
|
51
|
+
private :bad_tlsv1_3?
|
52
|
+
|
25
53
|
def readpartial(size)
|
26
54
|
while true
|
27
55
|
output = @engine.read
|
@@ -41,6 +69,7 @@ module Puma
|
|
41
69
|
|
42
70
|
def engine_read_all
|
43
71
|
output = @engine.read
|
72
|
+
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
44
73
|
while output and additional_output = @engine.read
|
45
74
|
output << additional_output
|
46
75
|
end
|
@@ -167,7 +196,10 @@ module Puma
|
|
167
196
|
end
|
168
197
|
end
|
169
198
|
|
170
|
-
if
|
199
|
+
if IS_JRUBY
|
200
|
+
OPENSSL_NO_SSL3 = false
|
201
|
+
OPENSSL_NO_TLS1 = false
|
202
|
+
|
171
203
|
class SSLError < StandardError
|
172
204
|
# Define this for jruby even though it isn't used.
|
173
205
|
end
|
@@ -184,7 +216,7 @@ module Puma
|
|
184
216
|
@no_tlsv1_1 = false
|
185
217
|
end
|
186
218
|
|
187
|
-
if
|
219
|
+
if IS_JRUBY
|
188
220
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
189
221
|
attr_reader :keystore
|
190
222
|
attr_accessor :keystore_pass
|
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/reactor.rb
CHANGED
@@ -252,7 +252,7 @@ module Puma
|
|
252
252
|
c.close
|
253
253
|
clear_monitor mon
|
254
254
|
|
255
|
-
@events.ssl_error
|
255
|
+
@events.ssl_error e, addr, cert
|
256
256
|
|
257
257
|
# The client doesn't know HTTP well
|
258
258
|
rescue HttpParserError => e
|
@@ -263,7 +263,7 @@ module Puma
|
|
263
263
|
|
264
264
|
clear_monitor mon
|
265
265
|
|
266
|
-
@events.parse_error
|
266
|
+
@events.parse_error e, c
|
267
267
|
rescue StandardError => e
|
268
268
|
@server.lowlevel_error(e, c.env)
|
269
269
|
|
data/lib/puma/runner.rb
CHANGED
data/lib/puma/server.rb
CHANGED
@@ -98,10 +98,22 @@ module Puma
|
|
98
98
|
@binder = bind
|
99
99
|
end
|
100
100
|
|
101
|
+
class << self
|
102
|
+
# :nodoc:
|
103
|
+
def tcp_cork_supported?
|
104
|
+
RbConfig::CONFIG['host_os'] =~ /linux/ &&
|
105
|
+
Socket.const_defined?(:IPPROTO_TCP) &&
|
106
|
+
Socket.const_defined?(:TCP_CORK) &&
|
107
|
+
Socket.const_defined?(:SOL_TCP) &&
|
108
|
+
Socket.const_defined?(:TCP_INFO)
|
109
|
+
end
|
110
|
+
private :tcp_cork_supported?
|
111
|
+
end
|
112
|
+
|
101
113
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
102
114
|
# packetizes our stream. This improves both latency and throughput.
|
103
115
|
#
|
104
|
-
if
|
116
|
+
if tcp_cork_supported?
|
105
117
|
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
106
118
|
|
107
119
|
# 6 == Socket::IPPROTO_TCP
|
@@ -109,7 +121,7 @@ module Puma
|
|
109
121
|
# 1/0 == turn on/off
|
110
122
|
def cork_socket(socket)
|
111
123
|
begin
|
112
|
-
socket.setsockopt(
|
124
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if socket.kind_of? TCPSocket
|
113
125
|
rescue IOError, SystemCallError
|
114
126
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
115
127
|
end
|
@@ -117,7 +129,7 @@ module Puma
|
|
117
129
|
|
118
130
|
def uncork_socket(socket)
|
119
131
|
begin
|
120
|
-
socket.setsockopt(
|
132
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if socket.kind_of? TCPSocket
|
121
133
|
rescue IOError, SystemCallError
|
122
134
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
123
135
|
end
|
@@ -207,14 +219,16 @@ module Puma
|
|
207
219
|
|
208
220
|
client.close
|
209
221
|
|
210
|
-
@events.ssl_error
|
222
|
+
@events.ssl_error e, addr, cert
|
211
223
|
rescue HttpParserError => e
|
212
224
|
client.write_error(400)
|
213
225
|
client.close
|
214
226
|
|
215
|
-
@events.parse_error
|
216
|
-
rescue ConnectionError, EOFError
|
227
|
+
@events.parse_error e, client
|
228
|
+
rescue ConnectionError, EOFError => e
|
217
229
|
client.close
|
230
|
+
|
231
|
+
@events.connection_error e, client
|
218
232
|
else
|
219
233
|
if process_now
|
220
234
|
process_client client, buffer
|
@@ -281,35 +295,26 @@ module Puma
|
|
281
295
|
if sock == check
|
282
296
|
break if handle_check
|
283
297
|
else
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
end
|
299
|
-
rescue SystemCallError
|
300
|
-
# nothing
|
301
|
-
rescue Errno::ECONNABORTED
|
302
|
-
# client closed the socket even before accept
|
303
|
-
begin
|
304
|
-
io.close
|
305
|
-
rescue
|
306
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
307
|
-
end
|
298
|
+
pool.wait_until_not_full
|
299
|
+
pool.wait_for_less_busy_worker(
|
300
|
+
@options[:wait_for_less_busy_worker].to_f)
|
301
|
+
|
302
|
+
io = begin
|
303
|
+
sock.accept_nonblock
|
304
|
+
rescue IO::WaitReadable
|
305
|
+
next
|
306
|
+
end
|
307
|
+
client = Client.new io, @binder.env(sock)
|
308
|
+
if remote_addr_value
|
309
|
+
client.peerip = remote_addr_value
|
310
|
+
elsif remote_addr_header
|
311
|
+
client.remote_addr_header = remote_addr_header
|
308
312
|
end
|
313
|
+
pool << client
|
309
314
|
end
|
310
315
|
end
|
311
316
|
rescue Object => e
|
312
|
-
@events.unknown_error
|
317
|
+
@events.unknown_error e, nil, "Listen loop"
|
313
318
|
end
|
314
319
|
end
|
315
320
|
|
@@ -322,10 +327,14 @@ module Puma
|
|
322
327
|
end
|
323
328
|
graceful_shutdown if @status == :stop || @status == :restart
|
324
329
|
rescue Exception => e
|
325
|
-
|
326
|
-
STDERR.puts e.backtrace
|
330
|
+
@events.unknown_error e, nil, "Exception handling servers"
|
327
331
|
ensure
|
328
|
-
|
332
|
+
begin
|
333
|
+
@check.close unless @check.closed?
|
334
|
+
rescue Errno::EBADF, RuntimeError
|
335
|
+
# RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
|
336
|
+
# Errno::EBADF is infrequently raised
|
337
|
+
end
|
329
338
|
@notify.close
|
330
339
|
@notify = nil
|
331
340
|
@check = nil
|
@@ -415,7 +424,7 @@ module Puma
|
|
415
424
|
|
416
425
|
close_socket = true
|
417
426
|
|
418
|
-
@events.ssl_error
|
427
|
+
@events.ssl_error e, addr, cert
|
419
428
|
|
420
429
|
# The client doesn't know HTTP well
|
421
430
|
rescue HttpParserError => e
|
@@ -423,7 +432,7 @@ module Puma
|
|
423
432
|
|
424
433
|
client.write_error(400)
|
425
434
|
|
426
|
-
@events.parse_error
|
435
|
+
@events.parse_error e, client
|
427
436
|
|
428
437
|
# Server error
|
429
438
|
rescue StandardError => e
|
@@ -431,8 +440,7 @@ module Puma
|
|
431
440
|
|
432
441
|
client.write_error(500)
|
433
442
|
|
434
|
-
@events.unknown_error
|
435
|
-
|
443
|
+
@events.unknown_error e, nil, "Read"
|
436
444
|
ensure
|
437
445
|
buffer.reset
|
438
446
|
|
@@ -442,7 +450,7 @@ module Puma
|
|
442
450
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
443
451
|
# Already closed
|
444
452
|
rescue StandardError => e
|
445
|
-
@events.unknown_error
|
453
|
+
@events.unknown_error e, nil, "Client"
|
446
454
|
end
|
447
455
|
end
|
448
456
|
end
|
@@ -478,7 +486,7 @@ module Puma
|
|
478
486
|
|
479
487
|
env[PATH_INFO] = env[REQUEST_PATH]
|
480
488
|
|
481
|
-
# From
|
489
|
+
# From https://www.ietf.org/rfc/rfc3875 :
|
482
490
|
# "Script authors should be aware that the REMOTE_ADDR and
|
483
491
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
484
492
|
# may not identify the ultimate source of the request.
|
@@ -567,12 +575,44 @@ module Puma
|
|
567
575
|
end
|
568
576
|
|
569
577
|
fast_write client, "\r\n".freeze
|
570
|
-
rescue ConnectionError
|
578
|
+
rescue ConnectionError => e
|
579
|
+
@events.debug_error e
|
571
580
|
# noop, if we lost the socket we just won't send the early hints
|
572
581
|
end
|
573
582
|
}
|
574
583
|
end
|
575
584
|
|
585
|
+
# Fixup any headers with , in the name to have _ now. We emit
|
586
|
+
# headers with , in them during the parse phase to avoid ambiguity
|
587
|
+
# with the - to _ conversion for critical headers. But here for
|
588
|
+
# compatibility, we'll convert them back. This code is written to
|
589
|
+
# avoid allocation in the common case (ie there are no headers
|
590
|
+
# with , in their names), that's why it has the extra conditionals.
|
591
|
+
|
592
|
+
to_delete = nil
|
593
|
+
to_add = nil
|
594
|
+
|
595
|
+
env.each do |k,v|
|
596
|
+
if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
|
597
|
+
if to_delete
|
598
|
+
to_delete << k
|
599
|
+
else
|
600
|
+
to_delete = [k]
|
601
|
+
end
|
602
|
+
|
603
|
+
unless to_add
|
604
|
+
to_add = {}
|
605
|
+
end
|
606
|
+
|
607
|
+
to_add[k.tr(",", "_")] = v
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
if to_delete
|
612
|
+
to_delete.each { |k| env.delete(k) }
|
613
|
+
env.merge! to_add
|
614
|
+
end
|
615
|
+
|
576
616
|
# A rack extension. If the app writes #call'ables to this
|
577
617
|
# array, we will invoke them when the request is done.
|
578
618
|
#
|
@@ -594,12 +634,12 @@ module Puma
|
|
594
634
|
return :async
|
595
635
|
end
|
596
636
|
rescue ThreadPool::ForceShutdown => e
|
597
|
-
@events.unknown_error
|
637
|
+
@events.unknown_error e, req, "Rack app"
|
598
638
|
@events.log "Detected force shutdown of a thread"
|
599
639
|
|
600
640
|
status, headers, res_body = lowlevel_error(e, env, 503)
|
601
641
|
rescue Exception => e
|
602
|
-
@events.unknown_error
|
642
|
+
@events.unknown_error e, req, "Rack app"
|
603
643
|
|
604
644
|
status, headers, res_body = lowlevel_error(e, env, 500)
|
605
645
|
end
|
@@ -889,7 +929,7 @@ module Puma
|
|
889
929
|
@check, @notify = Puma::Util.pipe unless @notify
|
890
930
|
begin
|
891
931
|
@notify << message
|
892
|
-
rescue IOError
|
932
|
+
rescue IOError, NoMethodError, Errno::EPIPE
|
893
933
|
# The server, in another thread, is shutting down
|
894
934
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
895
935
|
rescue RuntimeError => e
|
data/lib/puma/state_file.rb
CHANGED
data/lib/puma/thread_pool.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.0.
|
4
|
+
version: 5.0.0.beta2
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05
|
11
|
+
date: 2020-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- lib/puma/control_cli.rb
|
87
87
|
- lib/puma/detect.rb
|
88
88
|
- lib/puma/dsl.rb
|
89
|
+
- lib/puma/error_logger.rb
|
89
90
|
- lib/puma/events.rb
|
90
91
|
- lib/puma/io_buffer.rb
|
91
92
|
- lib/puma/jruby_restart.rb
|
@@ -109,13 +110,13 @@ files:
|
|
109
110
|
- lib/rack/handler/puma.rb
|
110
111
|
- tools/Dockerfile
|
111
112
|
- tools/trickletest.rb
|
112
|
-
homepage:
|
113
|
+
homepage: https://puma.io
|
113
114
|
licenses:
|
114
115
|
- BSD-3-Clause
|
115
116
|
metadata:
|
116
117
|
bug_tracker_uri: https://github.com/puma/puma/issues
|
117
118
|
changelog_uri: https://github.com/puma/puma/blob/master/History.md
|
118
|
-
homepage_uri:
|
119
|
+
homepage_uri: https://puma.io
|
119
120
|
source_code_uri: https://github.com/puma/puma
|
120
121
|
post_install_message:
|
121
122
|
rdoc_options: []
|