puma 6.4.1 → 7.2.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 +407 -8
- data/README.md +109 -49
- data/docs/deployment.md +58 -23
- data/docs/fork_worker.md +11 -1
- data/docs/java_options.md +54 -0
- data/docs/jungle/README.md +1 -1
- data/docs/kubernetes.md +11 -16
- data/docs/plugins.md +6 -2
- data/docs/restart.md +2 -2
- data/docs/signals.md +21 -21
- data/docs/stats.md +11 -5
- data/docs/systemd.md +14 -5
- data/ext/puma_http11/extconf.rb +20 -32
- data/ext/puma_http11/mini_ssl.c +29 -9
- data/ext/puma_http11/org/jruby/puma/Http11.java +40 -9
- data/ext/puma_http11/puma_http11.c +125 -118
- data/lib/puma/app/status.rb +11 -3
- data/lib/puma/binder.rb +21 -11
- data/lib/puma/cli.rb +10 -8
- data/lib/puma/client.rb +183 -83
- data/lib/puma/cluster/worker.rb +24 -21
- data/lib/puma/cluster/worker_handle.rb +38 -8
- data/lib/puma/cluster.rb +73 -47
- data/lib/puma/cluster_accept_loop_delay.rb +91 -0
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +131 -60
- data/lib/puma/const.rb +31 -12
- data/lib/puma/control_cli.rb +10 -6
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +411 -121
- data/lib/puma/error_logger.rb +7 -5
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/jruby_restart.rb +0 -16
- data/lib/puma/launcher/bundle_pruner.rb +1 -1
- data/lib/puma/launcher.rb +73 -55
- data/lib/puma/log_writer.rb +9 -9
- data/lib/puma/minissl/context_builder.rb +1 -0
- data/lib/puma/minissl.rb +1 -1
- data/lib/puma/null_io.rb +26 -0
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -13
- data/lib/puma/request.rb +71 -39
- data/lib/puma/runner.rb +15 -17
- data/lib/puma/sd_notify.rb +1 -4
- data/lib/puma/server.rb +134 -73
- data/lib/puma/single.rb +7 -4
- data/lib/puma/state_file.rb +3 -2
- data/lib/puma/thread_pool.rb +57 -80
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +10 -7
- data/tools/Dockerfile +15 -5
- metadata +14 -15
- data/ext/puma_http11/ext_help.h +0 -15
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
|
|
|
@@ -33,16 +33,16 @@ Now you will see via `ps` that there is no more `tail` process. Sometimes when r
|
|
|
33
33
|
|
|
34
34
|
Puma cluster responds to these signals:
|
|
35
35
|
|
|
36
|
-
- `TTIN
|
|
37
|
-
- `TTOU
|
|
38
|
-
- `TERM
|
|
39
|
-
- `USR2
|
|
40
|
-
- `USR1
|
|
41
|
-
- `HUP
|
|
42
|
-
- `INT
|
|
43
|
-
- `CHLD`
|
|
44
|
-
- `URG
|
|
45
|
-
- `INFO
|
|
36
|
+
- `TTIN`: Increment the worker count by 1.
|
|
37
|
+
- `TTOU`: Decrement the worker count by 1.
|
|
38
|
+
- `TERM`: Send `TERM` to worker. The worker will attempt to finish then exit.
|
|
39
|
+
- `USR2`: Restart workers. This also reloads the Puma configuration file, if there is one.
|
|
40
|
+
- `USR1`: Restart workers in phases, a rolling restart. This will not reload the configuration file.
|
|
41
|
+
- `HUP`: Reopen log files defined in `stdout_redirect` configuration parameter. If there is no `stdout_redirect` option provided, it will behave like `INT`.
|
|
42
|
+
- `INT`: Equivalent of sending Ctrl-C to cluster. Puma will attempt to finish then exit.
|
|
43
|
+
- `CHLD`: Reap zombie child processes and wake event loop in `fork_worker` mode.
|
|
44
|
+
- `URG`: Refork workers in phases from worker 0 if `fork_worker` option is enabled.
|
|
45
|
+
- `INFO`: Print backtraces of all Puma threads.
|
|
46
46
|
|
|
47
47
|
## Callbacks order in case of different signals
|
|
48
48
|
|
|
@@ -54,12 +54,12 @@ puma configuration file reloaded, if there is one
|
|
|
54
54
|
puma configuration file reloaded, if there is one
|
|
55
55
|
|
|
56
56
|
before_fork
|
|
57
|
-
|
|
57
|
+
before_worker_fork
|
|
58
58
|
after_worker_fork
|
|
59
59
|
|
|
60
60
|
Gemfile in context
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
before_worker_boot
|
|
63
63
|
|
|
64
64
|
Code of the app is loaded and running
|
|
65
65
|
```
|
|
@@ -67,18 +67,18 @@ Code of the app is loaded and running
|
|
|
67
67
|
### Send USR2
|
|
68
68
|
|
|
69
69
|
```
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
before_worker_shutdown
|
|
71
|
+
before_restart
|
|
72
72
|
|
|
73
73
|
puma configuration file reloaded, if there is one
|
|
74
74
|
|
|
75
75
|
before_fork
|
|
76
|
-
|
|
76
|
+
before_worker_fork
|
|
77
77
|
after_worker_fork
|
|
78
78
|
|
|
79
79
|
Gemfile in context
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
before_worker_boot
|
|
82
82
|
|
|
83
83
|
Code of the app is loaded and running
|
|
84
84
|
```
|
|
@@ -86,13 +86,13 @@ Code of the app is loaded and running
|
|
|
86
86
|
### Send USR1
|
|
87
87
|
|
|
88
88
|
```
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
before_worker_shutdown
|
|
90
|
+
before_worker_fork
|
|
91
91
|
after_worker_fork
|
|
92
92
|
|
|
93
93
|
Gemfile in context
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
before_worker_boot
|
|
96
96
|
|
|
97
97
|
Code of the app is loaded and running
|
|
98
98
|
```
|
data/docs/stats.md
CHANGED
|
@@ -55,16 +55,22 @@ 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_threads`, 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
|
+
* reactor_max: the maximum observed number of requests held in Puma's "reactor" which is used for asyncronously buffering request bodies. This stat is reset on every call, so it's the maximum value observed since the last stat call.
|
|
69
|
+
* backlog_max: the maximum number of requests that have been fully buffered by the reactor and placed in a ready queue, but have not yet been picked up by a server thread. This stat is reset on every call, so it's the maximum value observed since the last stat call.
|
|
64
70
|
|
|
65
71
|
### cluster mode
|
|
66
72
|
|
|
67
|
-
* phase: which phase of restart the process is in, during [phased restart](https://github.com/puma/puma/blob/
|
|
73
|
+
* phase: which phase of restart the process is in, during [phased restart](https://github.com/puma/puma/blob/main/docs/restart.md)
|
|
68
74
|
* workers: ??
|
|
69
75
|
* booted_workers: how many workers currently running?
|
|
70
76
|
* old_workers: ??
|
data/docs/systemd.md
CHANGED
|
@@ -72,7 +72,7 @@ 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
|
|
@@ -99,9 +99,11 @@ ListenStream=0.0.0.0:9293
|
|
|
99
99
|
# ListenStream=/run/puma.sock
|
|
100
100
|
|
|
101
101
|
# Socket options matching Puma defaults
|
|
102
|
-
NoDelay=true
|
|
103
102
|
ReusePort=true
|
|
104
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
|
|
105
107
|
|
|
106
108
|
[Install]
|
|
107
109
|
WantedBy=sockets.target
|
|
@@ -117,8 +119,8 @@ or cluster mode.
|
|
|
117
119
|
### Sockets and symlinks
|
|
118
120
|
|
|
119
121
|
When using releases folders, you should set the socket path using the shared
|
|
120
|
-
folder path (ex. `/srv/
|
|
121
|
-
path (`/srv/
|
|
122
|
+
folder path (ex. `/srv/project/shared/tmp/puma.sock`), not the release folder
|
|
123
|
+
path (`/srv/project/releases/1234/tmp/puma.sock`).
|
|
122
124
|
|
|
123
125
|
Puma will detect the release path socket as different than the one provided by
|
|
124
126
|
systemd and attempt to bind it again, resulting in the exception `There is
|
|
@@ -137,7 +139,7 @@ automatically for any activated socket. When systemd socket activation is not
|
|
|
137
139
|
enabled, this option does nothing.
|
|
138
140
|
|
|
139
141
|
This also accepts an optional argument `only` (DSL: `'only'`) to discard any
|
|
140
|
-
binds that
|
|
142
|
+
binds that are not socket activated.
|
|
141
143
|
|
|
142
144
|
## Usage
|
|
143
145
|
|
|
@@ -239,6 +241,13 @@ cap $stage puma:start --dry-run
|
|
|
239
241
|
cap $stage puma:stop --dry-run
|
|
240
242
|
~~~~
|
|
241
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
|
+
|
|
242
251
|
[Restart]: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=
|
|
243
252
|
[#1367]: https://github.com/puma/puma/issues/1367
|
|
244
253
|
[#1499]: https://github.com/puma/puma/issues/1499
|
data/ext/puma_http11/extconf.rb
CHANGED
|
@@ -10,15 +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
|
-
# also looks within the Ruby build for directory info
|
|
14
13
|
has_openssl_dir = dir_config('openssl').any? ||
|
|
15
|
-
RbConfig::CONFIG['configure_args']&.include?('openssl')
|
|
16
|
-
Dir.exist?("#{RbConfig::TOPDIR}/src/main/c/openssl") # TruffleRuby
|
|
14
|
+
RbConfig::CONFIG['configure_args']&.include?('openssl')
|
|
17
15
|
|
|
18
16
|
found_pkg_config = !has_openssl_dir && pkg_config('openssl')
|
|
19
17
|
|
|
20
18
|
found_ssl = if !$mingw && found_pkg_config
|
|
21
|
-
puts '
|
|
19
|
+
puts '──── Using OpenSSL pkgconfig (openssl.pc) ────'
|
|
22
20
|
true
|
|
23
21
|
elsif have_library('libcrypto', 'BIO_read') && have_library('libssl', 'SSL_CTX_new')
|
|
24
22
|
true
|
|
@@ -33,45 +31,35 @@ unless ENV["PUMA_DISABLE_SSL"]
|
|
|
33
31
|
if found_ssl
|
|
34
32
|
have_header "openssl/bio.h"
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
have_func "DTLS_method" , "openssl/ssl.h"
|
|
38
|
-
have_func "SSL_CTX_set_session_cache_mode(NULL, 0)", "openssl/ssl.h"
|
|
34
|
+
ssl_h = "openssl/ssl.h".freeze
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
have_func "
|
|
42
|
-
have_func "
|
|
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
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
have_func "
|
|
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
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
have_func "
|
|
48
|
+
puts "\n──── Below is yes for 1.1.1 & later ────"
|
|
49
|
+
have_func "SSL_CTX_set_ciphersuites(NULL, \"\")" , ssl_h
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else
|
|
58
|
-
puts "checking for Random.bytes... no"
|
|
59
|
-
end
|
|
51
|
+
puts "\n──── Below is yes for 3.0.0 & later ────"
|
|
52
|
+
have_func "SSL_get1_peer_certificate" , ssl_h
|
|
53
|
+
|
|
54
|
+
puts ''
|
|
60
55
|
end
|
|
61
56
|
end
|
|
62
57
|
|
|
63
58
|
if ENV["PUMA_MAKE_WARNINGS_INTO_ERRORS"]
|
|
64
59
|
# Make all warnings into errors
|
|
65
60
|
# Except `implicit-fallthrough` since most failures comes from ragel state machine generated code
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
append_cflags '-Wno-implicit-fallthrough'
|
|
69
|
-
else
|
|
70
|
-
# flag may not exist on some platforms, -Werror may not be defined on some platforms, but
|
|
71
|
-
# works with all in current CI
|
|
72
|
-
$CFLAGS << " #{config_string('WERRORFLAG') || '-Werror'}"
|
|
73
|
-
$CFLAGS << ' -Wno-implicit-fallthrough'
|
|
74
|
-
end
|
|
61
|
+
append_cflags(config_string('WERRORFLAG') || '-Werror')
|
|
62
|
+
append_cflags '-Wno-implicit-fallthrough'
|
|
75
63
|
end
|
|
76
64
|
|
|
77
65
|
create_makefile("puma/puma_http11")
|
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);
|
|
@@ -461,13 +471,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
|
461
471
|
SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
|
|
462
472
|
}
|
|
463
473
|
|
|
464
|
-
// Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
|
|
465
474
|
session_id_bytes = rb_funcall(
|
|
466
|
-
#ifdef HAVE_RANDOM_BYTES
|
|
467
475
|
rb_cRandom,
|
|
468
|
-
#else
|
|
469
|
-
rb_const_get(rb_cRandom, rb_intern_const("DEFAULT")),
|
|
470
|
-
#endif
|
|
471
476
|
rb_intern_const("bytes"),
|
|
472
477
|
1, ULL2NUM(SSL_MAX_SSL_SESSION_ID_LENGTH));
|
|
473
478
|
|
|
@@ -655,14 +660,29 @@ VALUE engine_shutdown(VALUE self) {
|
|
|
655
660
|
|
|
656
661
|
TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
|
|
657
662
|
|
|
663
|
+
if (SSL_in_init(conn->ssl)) {
|
|
664
|
+
// Avoid "shutdown while in init" error
|
|
665
|
+
// See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2827-L2828
|
|
666
|
+
return Qtrue;
|
|
667
|
+
}
|
|
668
|
+
|
|
658
669
|
ERR_clear_error();
|
|
659
670
|
|
|
660
671
|
ok = SSL_shutdown(conn->ssl);
|
|
661
|
-
|
|
662
|
-
|
|
672
|
+
// See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2792-L2797
|
|
673
|
+
// for description of SSL_shutdown return values.
|
|
674
|
+
switch (ok) {
|
|
675
|
+
case 0:
|
|
676
|
+
// "close notify" alert is sent by us.
|
|
677
|
+
return Qfalse;
|
|
678
|
+
case 1:
|
|
679
|
+
// "close notify" alert was received from peer.
|
|
680
|
+
return Qtrue;
|
|
681
|
+
default:
|
|
682
|
+
raise_error(conn->ssl, ok);
|
|
663
683
|
}
|
|
664
684
|
|
|
665
|
-
return
|
|
685
|
+
return Qnil;
|
|
666
686
|
}
|
|
667
687
|
|
|
668
688
|
VALUE engine_init(VALUE self) {
|
|
@@ -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);
|
|
@@ -88,6 +109,10 @@ public class Http11 extends RubyObject {
|
|
|
88
109
|
return (RubyClass)runtime.getModule("Puma").getConstant("HttpParserError");
|
|
89
110
|
}
|
|
90
111
|
|
|
112
|
+
private static boolean is_ows(int c) {
|
|
113
|
+
return c == ' ' || c == '\t';
|
|
114
|
+
}
|
|
115
|
+
|
|
91
116
|
public static void http_field(Ruby runtime, RubyHash req, ByteList buffer, int field, int flen, int value, int vlen) {
|
|
92
117
|
RubyString f;
|
|
93
118
|
IRubyObject v;
|
|
@@ -99,12 +124,18 @@ public class Http11 extends RubyObject {
|
|
|
99
124
|
int bite = b.get(i) & 0xFF;
|
|
100
125
|
if(bite == '-') {
|
|
101
126
|
b.set(i, (byte)'_');
|
|
127
|
+
} else if(bite == '_') {
|
|
128
|
+
b.set(i, (byte)',');
|
|
102
129
|
} else {
|
|
103
130
|
b.set(i, (byte)Character.toUpperCase(bite));
|
|
104
131
|
}
|
|
105
132
|
}
|
|
106
133
|
|
|
107
|
-
while (vlen > 0 &&
|
|
134
|
+
while (vlen > 0 && is_ows(buffer.get(value + vlen - 1))) vlen--;
|
|
135
|
+
while (vlen > 0 && is_ows(buffer.get(value))) {
|
|
136
|
+
vlen--;
|
|
137
|
+
value++;
|
|
138
|
+
}
|
|
108
139
|
|
|
109
140
|
if (b.equals(CONTENT_LENGTH_BYTELIST) || b.equals(CONTENT_TYPE_BYTELIST)) {
|
|
110
141
|
f = RubyString.newString(runtime, b);
|