puma 6.4.0 → 6.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +235 -18
- data/README.md +87 -26
- data/docs/fork_worker.md +11 -1
- data/docs/java_options.md +54 -0
- data/docs/kubernetes.md +1 -1
- data/docs/plugins.md +4 -0
- data/docs/restart.md +1 -0
- data/docs/signals.md +2 -2
- data/docs/stats.md +8 -3
- data/docs/systemd.md +13 -6
- data/ext/puma_http11/extconf.rb +21 -14
- data/ext/puma_http11/mini_ssl.c +16 -2
- data/ext/puma_http11/org/jruby/puma/Http11.java +31 -8
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +2 -1
- data/ext/puma_http11/puma_http11.c +4 -1
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +12 -4
- data/lib/puma/cli.rb +9 -5
- data/lib/puma/client.rb +104 -16
- data/lib/puma/cluster/worker.rb +9 -6
- data/lib/puma/cluster/worker_handle.rb +4 -5
- data/lib/puma/cluster.rb +112 -27
- data/lib/puma/configuration.rb +37 -21
- data/lib/puma/const.rb +22 -3
- data/lib/puma/control_cli.rb +4 -4
- data/lib/puma/detect.rb +3 -4
- data/lib/puma/dsl.rb +303 -58
- data/lib/puma/error_logger.rb +4 -4
- data/lib/puma/jruby_restart.rb +0 -16
- data/lib/puma/launcher.rb +21 -8
- data/lib/puma/log_writer.rb +9 -9
- data/lib/puma/minissl/context_builder.rb +3 -0
- data/lib/puma/minissl.rb +6 -0
- data/lib/puma/null_io.rb +42 -2
- data/lib/puma/request.rb +35 -14
- data/lib/puma/runner.rb +10 -3
- data/lib/puma/sd_notify.rb +1 -4
- data/lib/puma/server.rb +53 -21
- data/lib/puma/single.rb +1 -1
- data/lib/puma/state_file.rb +2 -2
- data/lib/puma/thread_pool.rb +14 -2
- data/lib/puma/util.rb +1 -1
- data/lib/rack/handler/puma.rb +8 -5
- data/tools/Dockerfile +5 -3
- metadata +10 -12
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Java Options
|
|
2
|
+
|
|
3
|
+
`System Properties` or `Environment Variables` can be used to change Puma's
|
|
4
|
+
default configuration for its Java extension. The provided values are evaluated
|
|
5
|
+
during initialization, and changes while running the app have no effect.
|
|
6
|
+
Moreover, default values may be used in case of invalid inputs.
|
|
7
|
+
|
|
8
|
+
## Supported Options
|
|
9
|
+
|
|
10
|
+
| ENV Name | Default Value | Validation |
|
|
11
|
+
|------------------------------|:-------------:|:------------------------:|
|
|
12
|
+
| PUMA_QUERY_STRING_MAX_LENGTH | 1024 * 10 | Positive natural number |
|
|
13
|
+
| PUMA_REQUEST_PATH_MAX_LENGTH | 8192 | Positive natural number |
|
|
14
|
+
| PUMA_REQUEST_URI_MAX_LENGTH | 1024 * 12 | Positive natural number |
|
|
15
|
+
| PUMA_SKIP_SIGUSR2 | nil | n/a |
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
### Invalid inputs
|
|
20
|
+
|
|
21
|
+
An empty string will be handled as missing, and the default value will be used instead.
|
|
22
|
+
Puma will print an error message for other invalid values.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
foo@bar:~/puma$ PUMA_QUERY_STRING_MAX_LENGTH=abc PUMA_REQUEST_PATH_MAX_LENGTH='' PUMA_REQUEST_URI_MAX_LENGTH=0 bundle exec bin/puma test/rackup/hello.ru
|
|
26
|
+
|
|
27
|
+
The value 0 for PUMA_REQUEST_URI_MAX_LENGTH is invalid. Using default value 12288 instead.
|
|
28
|
+
The value abc for PUMA_QUERY_STRING_MAX_LENGTH is invalid. Using default value 10240 instead.
|
|
29
|
+
Puma starting in single mode...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Valid inputs
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
foo@bar:~/puma$ PUMA_REQUEST_PATH_MAX_LENGTH=9 bundle exec bin/puma test/rackup/hello.ru
|
|
36
|
+
|
|
37
|
+
Puma starting in single mode...
|
|
38
|
+
```
|
|
39
|
+
```
|
|
40
|
+
foo@bar:~ export path=/123456789 # 10 chars
|
|
41
|
+
foo@bar:~ curl "http://localhost:9292${path}"
|
|
42
|
+
|
|
43
|
+
Puma caught this error: HTTP element REQUEST_PATH is longer than the 9 allowed length. (Puma::HttpParserError)
|
|
44
|
+
|
|
45
|
+
foo@bar:~ export path=/12345678 # 9 chars
|
|
46
|
+
foo@bar:~ curl "http://localhost:9292${path}"
|
|
47
|
+
Hello World
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Java Flight Recorder Compatibility
|
|
51
|
+
|
|
52
|
+
Unfortunately Java Flight Recorder uses `SIGUSR2` internally. If you wish to
|
|
53
|
+
use JFR, turn off Puma's trapping of `SIGUSR2` by setting the environment variable
|
|
54
|
+
`PUMA_SKIP_SIGUSR2` to any value.
|
data/docs/kubernetes.md
CHANGED
|
@@ -69,7 +69,7 @@ More discussions and links to relevant articles can be found in https://github.c
|
|
|
69
69
|
|
|
70
70
|
With containerization, you will have to make a decision about how "big" to make each pod. Should you run 2 pods with 50 workers each? 25 pods, each with 4 workers? 100 pods, with each Puma running in single mode? Each scenario represents the same total amount of capacity (100 Puma processes that can respond to requests), but there are tradeoffs to make.
|
|
71
71
|
|
|
72
|
-
* Worker counts should be somewhere between 4 and 32 in most cases. You want more than 4 in order to minimize time spent in request queueing for a free Puma worker, but probably less than ~32 because otherwise autoscaling is working in too large of an increment or they probably won't fit very well into your nodes.
|
|
72
|
+
* Worker counts should be somewhere between 4 and 32 in most cases. You want more than 4 in order to minimize time spent in request queueing for a free Puma worker, but probably less than ~32 because otherwise autoscaling is working in too large of an increment or they probably won't fit very well into your nodes. In any queueing system, queue time is proportional to 1/n, where n is the number of things pulling from the queue. Each pod will have its own request queue (i.e., the socket backlog). If you have 4 pods with 1 worker each (4 request queues), wait times are, proportionally, about 4 times higher than if you had 1 pod with 4 workers (1 request queue).
|
|
73
73
|
* Unless you have a very I/O-heavy application (50%+ time spent waiting on IO), use the default thread count (5 for MRI). Using higher numbers of threads with low I/O wait (<50%) will lead to additional request queueing time (latency!) and additional memory usage.
|
|
74
74
|
* More processes per pod reduces memory usage per process, because of copy-on-write memory and because the cost of the single master process is "amortized" over more child processes.
|
|
75
75
|
* Don't run less than 4 processes per pod if you can. Low numbers of processes per pod will lead to high request queueing, which means you will have to run more pods.
|
data/docs/plugins.md
CHANGED
|
@@ -36,3 +36,7 @@ object that is useful for additional configuration.
|
|
|
36
36
|
|
|
37
37
|
Public methods in [`Puma::Plugin`](../lib/puma/plugin.rb) are treated as a
|
|
38
38
|
public API for plugins.
|
|
39
|
+
|
|
40
|
+
## Binder hooks
|
|
41
|
+
|
|
42
|
+
There's `Puma::Binder#before_parse` method that allows to add proc to run before the body of `Puma::Binder#parse`. Example of usage can be found in [that repository](https://github.com/anchordotdev/puma-acme/blob/v0.1.3/lib/puma/acme/plugin.rb#L97-L118) (`before_parse_hook` could be renamed `before_parse`, making monkey patching of [binder.rb](https://github.com/anchordotdev/puma-acme/blob/v0.1.3/lib/puma/acme/binder.rb) is unnecessary).
|
data/docs/restart.md
CHANGED
|
@@ -27,6 +27,7 @@ Any of the following will cause a Puma server to perform a hot restart:
|
|
|
27
27
|
|
|
28
28
|
### Additional notes
|
|
29
29
|
|
|
30
|
+
* The newly started Puma process changes its current working directory to the directory specified by the `directory` option. If `directory` is set to symlink, this is automatically re-evaluated, so this mechanism can be used to upgrade the application.
|
|
30
31
|
* Only one version of the application is running at a time.
|
|
31
32
|
* `on_restart` is invoked just before the server shuts down. This can be used to clean up resources (like long-lived database connections) gracefully. Since Ruby 2.0, it is not typically necessary to explicitly close file descriptors on restart. This is because any file descriptor opened by Ruby will have the `FD_CLOEXEC` flag set, meaning that file descriptors are closed on `exec`. `on_restart` is useful, though, if your application needs to perform any more graceful protocol-specific shutdown procedures before closing connections.
|
|
32
33
|
|
data/docs/signals.md
CHANGED
|
@@ -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](https://
|
|
20
|
+
You can send a signal in Ruby using the [Process module](https://docs.ruby-lang.org/en/master/Process.html#method-c-kill):
|
|
21
21
|
|
|
22
22
|
```
|
|
23
23
|
$ irb
|
|
24
24
|
> puts pid
|
|
25
25
|
=> 87152
|
|
26
|
-
Process.detach(pid) # https://ruby-
|
|
26
|
+
Process.detach(pid) # https://docs.ruby-lang.org/en/master/Process.html#method-c-detach
|
|
27
27
|
Process.kill("TERM", pid)
|
|
28
28
|
```
|
|
29
29
|
|
data/docs/stats.md
CHANGED
|
@@ -55,9 +55,14 @@ end
|
|
|
55
55
|
|
|
56
56
|
When Puma runs in single mode, these stats are available at the top level. When Puma runs in cluster mode, these stats are available within the `worker_status` array in a hash labeled `last_status`, in an array of hashes where one hash represents each worker.
|
|
57
57
|
|
|
58
|
-
* backlog: requests that are waiting for an available thread to be available. if this is above 0, you need more capacity
|
|
59
|
-
* running: how many threads are
|
|
60
|
-
|
|
58
|
+
* backlog: requests that are waiting for an available thread to be available. if this is frequently above 0, you need more capacity.
|
|
59
|
+
* running: how many threads are spawned. A spawned thread may be busy processing a request or waiting for a new request. If `min_threads` and `max_threads` are set to the same number,
|
|
60
|
+
this will be a never-changing number (other than rare cases when a thread dies, etc).
|
|
61
|
+
* busy_threads: `running` - `how many threads are waiting to receive work` + `how many requests are waiting for a thread to pick them up`.
|
|
62
|
+
this is a "wholistic" stat reflecting the overall current state of work to be done and the capacity to do it.
|
|
63
|
+
* pool_capacity: `how many threads are waiting to receive work` + `max_threads` - `running`. In a typical configuration where `min_threads`
|
|
64
|
+
and `max_threads` are configured to the same number, this is simply `how many threads are waiting to receive work`. This number exists only as a stat
|
|
65
|
+
and is not used for any internal decisions, unlike `busy_theads`, which is usually a more useful stat.
|
|
61
66
|
* max_threads: the maximum number of threads Puma is configured to spool per worker
|
|
62
67
|
* requests_count: the number of requests this worker has served since starting
|
|
63
68
|
|
data/docs/systemd.md
CHANGED
|
@@ -51,7 +51,7 @@ ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/puma.rb
|
|
|
51
51
|
# Variant: Rails start.
|
|
52
52
|
# ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/config/puma.rb ../config.ru
|
|
53
53
|
|
|
54
|
-
# Variant: Use `bundle exec
|
|
54
|
+
# Variant: Use `bundle exec puma` instead of binstub
|
|
55
55
|
# Variant: Specify directives inline.
|
|
56
56
|
# ExecStart=/<FULLPATH>/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
|
|
57
57
|
|
|
@@ -72,13 +72,11 @@ systemd and Puma also support socket activation, where systemd opens the
|
|
|
72
72
|
listening socket(s) in advance and provides them to the Puma master process on
|
|
73
73
|
startup. Among other advantages, this keeps listening sockets open across puma
|
|
74
74
|
restarts and achieves graceful restarts, including when upgraded Puma, and is
|
|
75
|
-
compatible with both
|
|
75
|
+
compatible with both cluster mode and application preload.
|
|
76
76
|
|
|
77
77
|
**Note:** Any wrapper scripts which `exec`, or other indirections in `ExecStart`
|
|
78
78
|
may result in activated socket file descriptors being closed before reaching the
|
|
79
|
-
puma master process.
|
|
80
|
-
`--keep-file-descriptors` flag. `bundle exec` can be avoided by using a `puma`
|
|
81
|
-
executable generated by `bundle binstubs puma`. This is tracked in [#1499].
|
|
79
|
+
puma master process.
|
|
82
80
|
|
|
83
81
|
**Note:** Socket activation doesn't currently work on JRuby. This is tracked in
|
|
84
82
|
[#1367].
|
|
@@ -101,9 +99,11 @@ ListenStream=0.0.0.0:9293
|
|
|
101
99
|
# ListenStream=/run/puma.sock
|
|
102
100
|
|
|
103
101
|
# Socket options matching Puma defaults
|
|
104
|
-
NoDelay=true
|
|
105
102
|
ReusePort=true
|
|
106
103
|
Backlog=1024
|
|
104
|
+
# Enable this if you're using Puma with the "low_latency" option, read more in Puma DSL docs and systemd docs:
|
|
105
|
+
# https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html#NoDelay=
|
|
106
|
+
# NoDelay=true
|
|
107
107
|
|
|
108
108
|
[Install]
|
|
109
109
|
WantedBy=sockets.target
|
|
@@ -241,6 +241,13 @@ cap $stage puma:start --dry-run
|
|
|
241
241
|
cap $stage puma:stop --dry-run
|
|
242
242
|
~~~~
|
|
243
243
|
|
|
244
|
+
### Disabling Puma Systemd Integration
|
|
245
|
+
|
|
246
|
+
If you would like to disable Puma's systemd integration, for example if you handle it elsewhere
|
|
247
|
+
in your code yourself, simply set the the environment variable `PUMA_SKIP_SYSTEMD` to any value.
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
244
251
|
[Restart]: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=
|
|
245
252
|
[#1367]: https://github.com/puma/puma/issues/1367
|
|
246
253
|
[#1499]: https://github.com/puma/puma/issues/1499
|
data/ext/puma_http11/extconf.rb
CHANGED
|
@@ -10,11 +10,13 @@ end
|
|
|
10
10
|
|
|
11
11
|
unless ENV["PUMA_DISABLE_SSL"]
|
|
12
12
|
# don't use pkg_config('openssl') if '--with-openssl-dir' is used
|
|
13
|
-
has_openssl_dir = dir_config('openssl').any?
|
|
13
|
+
has_openssl_dir = dir_config('openssl').any? ||
|
|
14
|
+
RbConfig::CONFIG['configure_args']&.include?('openssl')
|
|
15
|
+
|
|
14
16
|
found_pkg_config = !has_openssl_dir && pkg_config('openssl')
|
|
15
17
|
|
|
16
18
|
found_ssl = if !$mingw && found_pkg_config
|
|
17
|
-
puts '
|
|
19
|
+
puts '──── Using OpenSSL pkgconfig (openssl.pc) ────'
|
|
18
20
|
true
|
|
19
21
|
elsif have_library('libcrypto', 'BIO_read') && have_library('libssl', 'SSL_CTX_new')
|
|
20
22
|
true
|
|
@@ -29,22 +31,27 @@ unless ENV["PUMA_DISABLE_SSL"]
|
|
|
29
31
|
if found_ssl
|
|
30
32
|
have_header "openssl/bio.h"
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
ssl_h = "openssl/ssl.h".freeze
|
|
35
|
+
|
|
36
|
+
puts "\n──── Below are yes for 1.0.2 & later ────"
|
|
37
|
+
have_func "DTLS_method" , ssl_h
|
|
38
|
+
have_func "SSL_CTX_set_session_cache_mode(NULL, 0)", ssl_h
|
|
39
|
+
|
|
40
|
+
puts "\n──── Below are yes for 1.1.0 & later ────"
|
|
41
|
+
have_func "TLS_server_method" , ssl_h
|
|
42
|
+
have_func "SSL_CTX_set_min_proto_version(NULL, 0)" , ssl_h
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
have_func "
|
|
44
|
+
puts "\n──── Below is yes for 1.1.0 and later, but isn't documented until 3.0.0 ────"
|
|
45
|
+
# https://github.com/openssl/openssl/blob/OpenSSL_1_1_0/include/openssl/ssl.h#L1159
|
|
46
|
+
have_func "SSL_CTX_set_dh_auto(NULL, 0)" , ssl_h
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
have_func "
|
|
48
|
+
puts "\n──── Below is yes for 1.1.1 & later ────"
|
|
49
|
+
have_func "SSL_CTX_set_ciphersuites(NULL, \"\")" , ssl_h
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
have_func "
|
|
51
|
+
puts "\n──── Below is yes for 3.0.0 & later ────"
|
|
52
|
+
have_func "SSL_get1_peer_certificate" , ssl_h
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
have_func "SSL_get1_peer_certificate" , "openssl/ssl.h"
|
|
54
|
+
puts ''
|
|
48
55
|
|
|
49
56
|
# Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
|
|
50
57
|
if Random.respond_to?(:bytes)
|
data/ext/puma_http11/mini_ssl.c
CHANGED
|
@@ -229,7 +229,7 @@ VALUE
|
|
|
229
229
|
sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
230
230
|
SSL_CTX* ctx;
|
|
231
231
|
int ssl_options;
|
|
232
|
-
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
|
|
232
|
+
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, ssl_ciphersuites, no_tlsv1, no_tlsv1_1,
|
|
233
233
|
verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
|
|
234
234
|
BIO *bio;
|
|
235
235
|
X509 *x509 = NULL;
|
|
@@ -269,6 +269,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
|
269
269
|
|
|
270
270
|
ssl_cipher_filter = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0);
|
|
271
271
|
|
|
272
|
+
ssl_ciphersuites = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_ciphersuites"), 0);
|
|
273
|
+
|
|
272
274
|
no_tlsv1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1"), 0);
|
|
273
275
|
|
|
274
276
|
no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
|
|
@@ -444,6 +446,14 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
|
444
446
|
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
|
|
445
447
|
}
|
|
446
448
|
|
|
449
|
+
#if HAVE_SSL_CTX_SET_CIPHERSUITES
|
|
450
|
+
// Only override OpenSSL default ciphersuites if config option is supplied.
|
|
451
|
+
if (!NIL_P(ssl_ciphersuites)) {
|
|
452
|
+
StringValue(ssl_ciphersuites);
|
|
453
|
+
SSL_CTX_set_ciphersuites(ctx, RSTRING_PTR(ssl_ciphersuites));
|
|
454
|
+
}
|
|
455
|
+
#endif
|
|
456
|
+
|
|
447
457
|
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
|
448
458
|
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no longer needed.
|
|
449
459
|
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
|
@@ -546,7 +556,7 @@ NORETURN(void raise_error(SSL* ssl, int result));
|
|
|
546
556
|
|
|
547
557
|
void raise_error(SSL* ssl, int result) {
|
|
548
558
|
char buf[512];
|
|
549
|
-
char msg[
|
|
559
|
+
char msg[768];
|
|
550
560
|
const char* err_str;
|
|
551
561
|
int err = errno;
|
|
552
562
|
int mask = 4095;
|
|
@@ -804,6 +814,10 @@ void Init_mini_ssl(VALUE puma) {
|
|
|
804
814
|
|
|
805
815
|
rb_define_method(eng, "init?", engine_init, 0);
|
|
806
816
|
|
|
817
|
+
/* @!attribute [r] peercert
|
|
818
|
+
* Returns `nil` when `MiniSSL::Context#verify_mode` is set to `VERIFY_NONE`.
|
|
819
|
+
* @return [String, nil] DER encoded cert
|
|
820
|
+
*/
|
|
807
821
|
rb_define_method(eng, "peercert", engine_peercert, 0);
|
|
808
822
|
|
|
809
823
|
rb_define_method(eng, "ssl_vers_st", engine_ssl_vers_st, 0);
|
|
@@ -26,14 +26,14 @@ public class Http11 extends RubyObject {
|
|
|
26
26
|
public final static String MAX_FIELD_NAME_LENGTH_ERR = "HTTP element FIELD_NAME is longer than the 256 allowed length.";
|
|
27
27
|
public final static int MAX_FIELD_VALUE_LENGTH = 80 * 1024;
|
|
28
28
|
public final static String MAX_FIELD_VALUE_LENGTH_ERR = "HTTP element FIELD_VALUE is longer than the 81920 allowed length.";
|
|
29
|
-
public final static int MAX_REQUEST_URI_LENGTH = 1024 * 12;
|
|
30
|
-
public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the
|
|
29
|
+
public final static int MAX_REQUEST_URI_LENGTH = getConstLength("PUMA_REQUEST_URI_MAX_LENGTH", 1024 * 12);
|
|
30
|
+
public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the " + MAX_REQUEST_URI_LENGTH + " allowed length.";
|
|
31
31
|
public final static int MAX_FRAGMENT_LENGTH = 1024;
|
|
32
|
-
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element
|
|
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
|
|
35
|
-
public final static int MAX_QUERY_STRING_LENGTH =
|
|
36
|
-
public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the
|
|
32
|
+
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element FRAGMENT is longer than the 1024 allowed length.";
|
|
33
|
+
public final static int MAX_REQUEST_PATH_LENGTH = getConstLength("PUMA_REQUEST_PATH_MAX_LENGTH", 8192);
|
|
34
|
+
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the " + MAX_REQUEST_PATH_LENGTH + " allowed length.";
|
|
35
|
+
public final static int MAX_QUERY_STRING_LENGTH = getConstLength("PUMA_QUERY_STRING_MAX_LENGTH", 10 * 1024);
|
|
36
|
+
public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the " + MAX_QUERY_STRING_LENGTH +" allowed length.";
|
|
37
37
|
public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
|
|
38
38
|
public final static String MAX_HEADER_LENGTH_ERR = "HTTP element HEADER is longer than the 114688 allowed length.";
|
|
39
39
|
|
|
@@ -48,6 +48,27 @@ public class Http11 extends RubyObject {
|
|
|
48
48
|
public static final ByteList QUERY_STRING_BYTELIST = new ByteList(ByteList.plain("QUERY_STRING"));
|
|
49
49
|
public static final ByteList SERVER_PROTOCOL_BYTELIST = new ByteList(ByteList.plain("SERVER_PROTOCOL"));
|
|
50
50
|
|
|
51
|
+
public static String getEnvOrProperty(String name) {
|
|
52
|
+
String envValue = System.getenv(name);
|
|
53
|
+
return (envValue != null) ? envValue : System.getProperty(name);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public static int getConstLength(String name, Integer defaultValue) {
|
|
57
|
+
String stringValue = getEnvOrProperty(name);
|
|
58
|
+
if (stringValue == null || stringValue.isEmpty()) return defaultValue;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
int value = Integer.parseUnsignedInt(stringValue);
|
|
62
|
+
if (value <= 0) {
|
|
63
|
+
throw new NumberFormatException("The number is not positive.");
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
} catch (NumberFormatException e) {
|
|
67
|
+
System.err.println(String.format("The value %s for %s is invalid. Using default value %d instead.", stringValue, name, defaultValue));
|
|
68
|
+
return defaultValue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
51
72
|
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
|
52
73
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
|
53
74
|
return new Http11(runtime, klass);
|
|
@@ -56,7 +77,7 @@ public class Http11 extends RubyObject {
|
|
|
56
77
|
|
|
57
78
|
public static void createHttp11(Ruby runtime) {
|
|
58
79
|
RubyModule mPuma = runtime.defineModule("Puma");
|
|
59
|
-
mPuma.defineClassUnder("HttpParserError",runtime.getClass("
|
|
80
|
+
mPuma.defineClassUnder("HttpParserError",runtime.getClass("StandardError"),runtime.getClass("StandardError").getAllocator());
|
|
60
81
|
|
|
61
82
|
RubyClass cHttpParser = mPuma.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR);
|
|
62
83
|
cHttpParser.defineAnnotatedMethods(Http11.class);
|
|
@@ -99,6 +120,8 @@ public class Http11 extends RubyObject {
|
|
|
99
120
|
int bite = b.get(i) & 0xFF;
|
|
100
121
|
if(bite == '-') {
|
|
101
122
|
b.set(i, (byte)'_');
|
|
123
|
+
} else if(bite == '_') {
|
|
124
|
+
b.set(i, (byte)',');
|
|
102
125
|
} else {
|
|
103
126
|
b.set(i, (byte)Character.toUpperCase(bite));
|
|
104
127
|
}
|
|
@@ -47,6 +47,7 @@ import static javax.net.ssl.SSLEngineResult.Status;
|
|
|
47
47
|
import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
|
48
48
|
|
|
49
49
|
public class MiniSSL extends RubyObject { // MiniSSL::Engine
|
|
50
|
+
private static final long serialVersionUID = -6903439483039141234L;
|
|
50
51
|
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
|
51
52
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
|
52
53
|
return new MiniSSL(runtime, klass);
|
|
@@ -500,7 +501,7 @@ public class MiniSSL extends RubyObject { // MiniSSL::Engine
|
|
|
500
501
|
}
|
|
501
502
|
|
|
502
503
|
private static RaiseException newError(Ruby runtime, RubyClass errorClass, String message, Throwable cause) {
|
|
503
|
-
RaiseException ex =
|
|
504
|
+
RaiseException ex = RaiseException.from(runtime, errorClass, message);
|
|
504
505
|
ex.initCause(cause);
|
|
505
506
|
return ex;
|
|
506
507
|
}
|
|
@@ -461,6 +461,9 @@ void Init_mini_ssl(VALUE mod);
|
|
|
461
461
|
|
|
462
462
|
void Init_puma_http11(void)
|
|
463
463
|
{
|
|
464
|
+
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
465
|
+
rb_ext_ractor_safe(true);
|
|
466
|
+
#endif
|
|
464
467
|
|
|
465
468
|
VALUE mPuma = rb_define_module("Puma");
|
|
466
469
|
VALUE cHttpParser = rb_define_class_under(mPuma, "HttpParser", rb_cObject);
|
|
@@ -472,7 +475,7 @@ void Init_puma_http11(void)
|
|
|
472
475
|
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
|
473
476
|
DEF_GLOBAL(request_path, "REQUEST_PATH");
|
|
474
477
|
|
|
475
|
-
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError",
|
|
478
|
+
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eStandardError);
|
|
476
479
|
rb_global_variable(&eHttpParserError);
|
|
477
480
|
|
|
478
481
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
data/lib/puma/app/status.rb
CHANGED
|
@@ -80,7 +80,7 @@ module Puma
|
|
|
80
80
|
|
|
81
81
|
def authenticate(env)
|
|
82
82
|
return true unless @auth_token
|
|
83
|
-
env['QUERY_STRING'].to_s.split(
|
|
83
|
+
env['QUERY_STRING'].to_s.split(/[&;]/).include? "token=#{@auth_token}"
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def rack_response(status, body, content_type='application/json')
|
data/lib/puma/binder.rb
CHANGED
|
@@ -19,13 +19,14 @@ module Puma
|
|
|
19
19
|
|
|
20
20
|
RACK_VERSION = [1,6].freeze
|
|
21
21
|
|
|
22
|
-
def initialize(log_writer, conf = Configuration.new)
|
|
22
|
+
def initialize(log_writer, conf = Configuration.new, env: ENV)
|
|
23
23
|
@log_writer = log_writer
|
|
24
24
|
@conf = conf
|
|
25
25
|
@listeners = []
|
|
26
26
|
@inherited_fds = {}
|
|
27
27
|
@activated_sockets = {}
|
|
28
28
|
@unix_paths = []
|
|
29
|
+
@env = env
|
|
29
30
|
|
|
30
31
|
@proto_env = {
|
|
31
32
|
"rack.version".freeze => RACK_VERSION,
|
|
@@ -34,7 +35,7 @@ module Puma
|
|
|
34
35
|
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
|
35
36
|
"rack.run_once".freeze => false,
|
|
36
37
|
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
|
37
|
-
"SCRIPT_NAME".freeze =>
|
|
38
|
+
"SCRIPT_NAME".freeze => env['SCRIPT_NAME'] || "",
|
|
38
39
|
|
|
39
40
|
# I'd like to set a default CONTENT_TYPE here but some things
|
|
40
41
|
# depend on their not being a default set and inferring
|
|
@@ -87,7 +88,7 @@ module Puma
|
|
|
87
88
|
# @version 5.0.0
|
|
88
89
|
#
|
|
89
90
|
def create_activated_fds(env_hash)
|
|
90
|
-
@log_writer.debug "ENV['LISTEN_FDS'] #{
|
|
91
|
+
@log_writer.debug "ENV['LISTEN_FDS'] #{@env['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
|
91
92
|
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
|
92
93
|
env_hash['LISTEN_FDS'].to_i.times do |index|
|
|
93
94
|
sock = TCPServer.for_fd(socket_activation_fd(index))
|
|
@@ -141,7 +142,14 @@ module Puma
|
|
|
141
142
|
end
|
|
142
143
|
end
|
|
143
144
|
|
|
145
|
+
def before_parse(&block)
|
|
146
|
+
@before_parse ||= []
|
|
147
|
+
@before_parse << block if block
|
|
148
|
+
@before_parse
|
|
149
|
+
end
|
|
150
|
+
|
|
144
151
|
def parse(binds, log_writer = nil, log_msg = 'Listening')
|
|
152
|
+
before_parse.each(&:call)
|
|
145
153
|
log_writer ||= @log_writer
|
|
146
154
|
binds.each do |str|
|
|
147
155
|
uri = URI.parse str
|
|
@@ -183,7 +191,7 @@ module Puma
|
|
|
183
191
|
io = inherit_unix_listener path, fd
|
|
184
192
|
log_writer.log "* Inherited #{str}"
|
|
185
193
|
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
|
186
|
-
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
|
194
|
+
!abstract && @activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
|
187
195
|
@unix_paths << path unless abstract || File.exist?(path)
|
|
188
196
|
io = inherit_unix_listener path, sock
|
|
189
197
|
log_writer.log "* Activated #{str}"
|
data/lib/puma/cli.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Puma
|
|
|
24
24
|
# Create a new CLI object using +argv+ as the command line
|
|
25
25
|
# arguments.
|
|
26
26
|
#
|
|
27
|
-
def initialize(argv, log_writer = LogWriter.stdio, events = Events.new)
|
|
27
|
+
def initialize(argv, log_writer = LogWriter.stdio, events = Events.new, env: ENV)
|
|
28
28
|
@debug = false
|
|
29
29
|
@argv = argv.dup
|
|
30
30
|
@log_writer = log_writer
|
|
@@ -39,7 +39,7 @@ module Puma
|
|
|
39
39
|
@control_url = nil
|
|
40
40
|
@control_options = {}
|
|
41
41
|
|
|
42
|
-
setup_options
|
|
42
|
+
setup_options env
|
|
43
43
|
|
|
44
44
|
begin
|
|
45
45
|
@parser.parse! @argv
|
|
@@ -63,7 +63,7 @@ module Puma
|
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
@launcher = Puma::Launcher.new(@conf, :log_writer
|
|
66
|
+
@launcher = Puma::Launcher.new(@conf, env: ENV, log_writer: @log_writer, events: @events, argv: argv)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
attr_reader :launcher
|
|
@@ -92,8 +92,8 @@ module Puma
|
|
|
92
92
|
# Build the OptionParser object to handle the available options.
|
|
93
93
|
#
|
|
94
94
|
|
|
95
|
-
def setup_options
|
|
96
|
-
@conf = Configuration.new({}, {events: @events}) do |user_config, file_config|
|
|
95
|
+
def setup_options(env = ENV)
|
|
96
|
+
@conf = Configuration.new({}, {events: @events}, env) do |user_config, file_config|
|
|
97
97
|
@parser = OptionParser.new do |o|
|
|
98
98
|
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
|
99
99
|
user_config.bind arg
|
|
@@ -157,6 +157,10 @@ module Puma
|
|
|
157
157
|
user_config.pidfile arg
|
|
158
158
|
end
|
|
159
159
|
|
|
160
|
+
o.on "--plugin PLUGIN", "Load the given PLUGIN. Can be used multiple times to load multiple plugins." do |arg|
|
|
161
|
+
user_config.plugin arg
|
|
162
|
+
end
|
|
163
|
+
|
|
160
164
|
o.on "--preload", "Preload the app. Cluster mode only" do
|
|
161
165
|
user_config.preload_app!
|
|
162
166
|
end
|