puma 5.0.0.beta1 → 5.0.0.beta2
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/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 +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b265505e1f4f00fe8c7de1c1d66103664bdd5920c2aed54b66e3303ded01c881
|
4
|
+
data.tar.gz: 3c921f609fd679b3b8cfbbc807678c16d09b03f43a8104913f0e0dd54e7e93f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74dce09aab280163c048a0c3ae3a81cd16b2ac32c7641fd43640d65797a61edf6e25a11f5111324111cd0068a98fc2ec13fcb3ac44154cef39d4bb3eb839a392
|
7
|
+
data.tar.gz: c6cf2c5d6d29867130be2fa572d5aea12a45a50052841a2de26e0a4600004f6fffae80922a9c641e8a155c8967ea8349d759b62a7ae9c2f6cbc84433fd092d13
|
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
|