puma 6.5.0-java → 6.6.1-java
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 +63 -6
- data/README.md +17 -9
- data/docs/fork_worker.md +7 -1
- data/docs/java_options.md +6 -0
- data/docs/plugins.md +4 -0
- data/docs/signals.md +2 -2
- data/docs/stats.md +6 -2
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/org/jruby/puma/Http11.java +2 -2
- data/ext/puma_http11/puma_http11.c +1 -1
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +7 -0
- data/lib/puma/client.rb +43 -5
- data/lib/puma/cluster/worker.rb +9 -6
- data/lib/puma/cluster/worker_handle.rb +1 -1
- data/lib/puma/cluster.rb +36 -18
- data/lib/puma/configuration.rb +2 -0
- data/lib/puma/const.rb +10 -9
- data/lib/puma/dsl.rb +36 -23
- data/lib/puma/launcher.rb +14 -5
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/request.rb +12 -8
- data/lib/puma/runner.rb +4 -1
- data/lib/puma/sd_notify.rb +1 -4
- data/lib/puma/server.rb +9 -5
- data/lib/puma/single.rb +1 -1
- data/lib/puma/thread_pool.rb +4 -3
- data/tools/Dockerfile +3 -1
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77572e7c8734be26c763ff4e53aa5cd9e37ed091fdc6cfeeb81ca1421ac6da02
|
4
|
+
data.tar.gz: 0b77cde4be14a0541131e8ef17e2980ab014e94d76c4e83e2a84bf7557d95929
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6d3710d65cfb68d777bc0388b42bcdfe420cfa6b20c9b64ab0419c248f2919480d9aa07ba65cc4abe3e0dae40722e3aa544fadbe2706965c21d601329319b6c
|
7
|
+
data.tar.gz: 174671b99f41f99ee43e0567d3ef3599fe2064a08f6ea647b00c054498163876709a92867868116a1a53588ed08a7a61553c0848116341e0d46df7ad648da43f
|
data/History.md
CHANGED
@@ -1,3 +1,38 @@
|
|
1
|
+
## 6.6.1 / 2025-07-30
|
2
|
+
|
3
|
+
* Bugfixes
|
4
|
+
* Accept `to_path` to be `nil` on request bodies ([#3635])
|
5
|
+
* Fix single runner stats before the server start ([#3572])
|
6
|
+
* Fix incomplete worker boot state on refork ([#3601])
|
7
|
+
* Improve HttpParserError messages for better debugging ([#3586])
|
8
|
+
* Fix refork logs to distinguish from phased restarts ([#3598])
|
9
|
+
* Fix `rack.after_reply` so it doesn't interrupt chain on error ([#3680])
|
10
|
+
|
11
|
+
## 6.6.0 / 2025-01-29
|
12
|
+
|
13
|
+
* Features
|
14
|
+
* Option to turn off SIGUSR2 trapping ([#3570], [#3567])
|
15
|
+
* Shorten `ThreadPool` trimmer and reaper thread names ([#3383])
|
16
|
+
* Add after_refork hook ([#3386])
|
17
|
+
* Add busy threads stat ([#3517])
|
18
|
+
* Add a debug log before running each type of hook ([#3375])
|
19
|
+
* Allow alternative schemes in Binder ([#3348], [#3302])
|
20
|
+
* Avoid spawning `Threadpool#trim` thread if pool size is fixed ([#3384])
|
21
|
+
|
22
|
+
* Bugfixes
|
23
|
+
* Change `HttpParserError` to be subclass of `StandardError` ([#3590], [#3552])
|
24
|
+
* added test cases
|
25
|
+
* fix update phased restart symlink folder
|
26
|
+
|
27
|
+
* Performance
|
28
|
+
* Only ping worker 0 during phased restart if using fork worker ([#3568])
|
29
|
+
|
30
|
+
* Refactor
|
31
|
+
* Fix multi-delimiter split to get status app token ([#3505])
|
32
|
+
* Change ping to use const ([#3595])
|
33
|
+
* Fixup use of Puma::Const::PipeRequest constants ([#3565])
|
34
|
+
* Update DSL hook processing logic to be consistent ([#3376])
|
35
|
+
|
1
36
|
## 6.5.0 / 2024-11-23
|
2
37
|
|
3
38
|
* Features
|
@@ -138,7 +173,7 @@
|
|
138
173
|
|
139
174
|
* Features
|
140
175
|
* Ability to supply a custom logger ([#2770], [#2511])
|
141
|
-
* Warn when
|
176
|
+
* Warn when cluster mode-only hooks are defined in single mode ([#3089])
|
142
177
|
* Adds the on_booted event ([#2709])
|
143
178
|
|
144
179
|
* Bugfixes
|
@@ -733,7 +768,7 @@ Each patchlevel release contains a separate security fix. We recommend simply up
|
|
733
768
|
* Fix Java 8 support ([#1773])
|
734
769
|
* Fix error `uninitialized constant Puma::Cluster` ([#1731])
|
735
770
|
* Fix `not_token` being able to be set to true ([#1803])
|
736
|
-
* Fix "Hang on SIGTERM with ruby 2.6 in
|
771
|
+
* Fix "Hang on SIGTERM with ruby 2.6 in cluster mode" (PR [#1741], [#1674], [#1720], [#1730], [#1755])
|
737
772
|
|
738
773
|
## 3.12.1 / 2019-03-19
|
739
774
|
|
@@ -1144,7 +1179,7 @@ Each patchlevel release contains a separate security fix. We recommend simply up
|
|
1144
1179
|
* 4 minor features:
|
1145
1180
|
|
1146
1181
|
* Listen to unix socket with provided backlog if any
|
1147
|
-
* Improves the
|
1182
|
+
* Improves the cluster mode stats to report worker stats
|
1148
1183
|
* Pass the env to the lowlevel_error handler. Fixes [#854]
|
1149
1184
|
* Treat path-like hosts as unix sockets. Fixes [#824]
|
1150
1185
|
|
@@ -1871,7 +1906,7 @@ The "clearly I don't have enough tests for the config" release.
|
|
1871
1906
|
|
1872
1907
|
* 3 doc changes:
|
1873
1908
|
* Add note about on_worker_boot hook
|
1874
|
-
* Add some documentation for
|
1909
|
+
* Add some documentation for Cluster mode
|
1875
1910
|
* Added quotes to /etc/puma.conf
|
1876
1911
|
|
1877
1912
|
## 2.0.1 / 2013-04-30
|
@@ -2125,6 +2160,28 @@ be added back in a future date when a java Puma::MiniSSL is added.
|
|
2125
2160
|
* Bugfixes
|
2126
2161
|
* Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
|
2127
2162
|
|
2163
|
+
[#3680]:https://github.com/puma/puma/pull/3680 "PR by @byroot, merged 2025-07-31"
|
2164
|
+
[#3572]:https://github.com/puma/puma/pull/3572 "PR by @barthez, merged 2025-02-06"
|
2165
|
+
[#3586]:https://github.com/puma/puma/pull/3586 "PR by @MSP-Greg, merged 2025-02-03"
|
2166
|
+
[#3598]:https://github.com/puma/puma/pull/3598 "PR by @joshuay03, merged 2025-01-31"
|
2167
|
+
[#3601]:https://github.com/puma/puma/pull/3601 "PR by @joshuay03, merged 2025-01-31"
|
2168
|
+
[#3635]:https://github.com/puma/puma/pull/3635 "PR by @LevitatingBusinessMan, merged 2025-05-08"
|
2169
|
+
[#3570]:https://github.com/puma/puma/pull/3570 "PR by @mohamedhafez, merged 2024-12-30"
|
2170
|
+
[#3567]:https://github.com/puma/puma/issues/3567 "Issue by @mohamedhafez, closed 2024-12-30"
|
2171
|
+
[#3383]:https://github.com/puma/puma/pull/3383 "PR by @joshuay03, merged 2024-11-29"
|
2172
|
+
[#3386]:https://github.com/puma/puma/pull/3386 "PR by @Drakula2k, merged 2024-11-27"
|
2173
|
+
[#3517]:https://github.com/puma/puma/pull/3517 "PR by @jjb, merged 2024-11-26"
|
2174
|
+
[#3375]:https://github.com/puma/puma/pull/3375 "PR by @joshuay03, merged 2024-11-23"
|
2175
|
+
[#3348]:https://github.com/puma/puma/pull/3348 "PR by @tomurb, merged 2024-11-23"
|
2176
|
+
[#3302]:https://github.com/puma/puma/issues/3302 "Issue by @benburkert, closed 2024-11-23"
|
2177
|
+
[#3384]:https://github.com/puma/puma/pull/3384 "PR by @joshuay03, merged 2024-11-23"
|
2178
|
+
[#3590]:https://github.com/puma/puma/pull/3590 "PR by @MSP-Greg, merged 2025-01-01"
|
2179
|
+
[#3552]:https://github.com/puma/puma/issues/3552 "Issue by @utay, closed 2025-01-01"
|
2180
|
+
[#3568]:https://github.com/puma/puma/pull/3568 "PR by @joshuay03, merged 2024-12-11"
|
2181
|
+
[#3505]:https://github.com/puma/puma/pull/3505 "PR by @AnthonyClark, merged 2025-01-27"
|
2182
|
+
[#3595]:https://github.com/puma/puma/pull/3595 "PR by @nateberkopec, merged 2025-01-07"
|
2183
|
+
[#3565]:https://github.com/puma/puma/pull/3565 "PR by @MSP-Greg, merged 2024-11-28"
|
2184
|
+
[#3376]:https://github.com/puma/puma/pull/3376 "PR by @joshuay03, merged 2024-11-23"
|
2128
2185
|
[#3407]:https://github.com/puma/puma/pull/3407 "PR by @JacobEvelyn, merged 2024-11-05"
|
2129
2186
|
[#3439]:https://github.com/puma/puma/pull/3439 "PR by @codergeek121, merged 2024-11-04"
|
2130
2187
|
[#3437]:https://github.com/puma/puma/issues/3437 "Issue by @rafaelfranca, closed 2024-11-04"
|
@@ -2709,14 +2766,14 @@ be added back in a future date when a java Puma::MiniSSL is added.
|
|
2709
2766
|
[#782]:https://github.com/puma/puma/issues/782 "Issue by @Tonkpils, closed 2016-07-19"
|
2710
2767
|
[#1010]:https://github.com/puma/puma/issues/1010 "Issue by @mneumark, closed 2016-07-19"
|
2711
2768
|
[#959]:https://github.com/puma/puma/issues/959 "Issue by @mwpastore, closed 2016-04-22"
|
2712
|
-
[#840]:https://github.com/puma/puma/issues/840 "Issue by @
|
2769
|
+
[#840]:https://github.com/puma/puma/issues/840 "Issue by @marisawallace, closed 2016-04-07"
|
2713
2770
|
[#1007]:https://github.com/puma/puma/pull/1007 "PR by @willnet, merged 2016-06-24"
|
2714
2771
|
[#1014]:https://github.com/puma/puma/pull/1014 "PR by @szymon-jez, merged 2016-07-11"
|
2715
2772
|
[#1015]:https://github.com/puma/puma/pull/1015 "PR by @bf4, merged 2016-07-19"
|
2716
2773
|
[#1017]:https://github.com/puma/puma/pull/1017 "PR by @jorihardman, merged 2016-07-19"
|
2717
2774
|
[#954]:https://github.com/puma/puma/pull/954 "PR by @jf, merged 2016-04-12"
|
2718
2775
|
[#955]:https://github.com/puma/puma/pull/955 "PR by @jf, merged 2016-04-22"
|
2719
|
-
[#956]:https://github.com/puma/puma/pull/956 "PR by @
|
2776
|
+
[#956]:https://github.com/puma/puma/pull/956 "PR by @marisawallace, merged 2016-04-12"
|
2720
2777
|
[#960]:https://github.com/puma/puma/pull/960 "PR by @kmayer, merged 2016-04-15"
|
2721
2778
|
[#969]:https://github.com/puma/puma/pull/969 "PR by @frankwong15, merged 2016-05-10"
|
2722
2779
|
[#970]:https://github.com/puma/puma/pull/970 "PR by @willnet, merged 2016-04-26"
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
# Puma: A Ruby Web Server Built For Parallelism
|
6
6
|
|
7
|
-
[](https://github.com/puma/puma/actions/workflows/tests.yml?query=branch%3Amaster)
|
8
8
|
[](https://codeclimate.com/github/puma/puma)
|
9
9
|
[]( https://stackoverflow.com/questions/tagged/puma )
|
10
10
|
|
@@ -102,9 +102,9 @@ Puma will automatically scale the number of threads, from the minimum until it c
|
|
102
102
|
|
103
103
|
Be aware that additionally Puma creates threads on its own for internal purposes (e.g. handling slow clients). So, even if you specify -t 1:1, expect around 7 threads created in your application.
|
104
104
|
|
105
|
-
###
|
105
|
+
### Cluster mode
|
106
106
|
|
107
|
-
Puma also offers "
|
107
|
+
Puma also offers "cluster mode". Cluster mode `fork`s workers from a master process. Each child process still has its own thread pool. You can tune the number of workers with the `-w` (or `--workers`) flag:
|
108
108
|
|
109
109
|
```
|
110
110
|
$ puma -t 8:32 -w 3
|
@@ -116,13 +116,13 @@ Or with the `WEB_CONCURRENCY` environment variable:
|
|
116
116
|
$ WEB_CONCURRENCY=3 puma -t 8:32
|
117
117
|
```
|
118
118
|
|
119
|
-
Note that threads are still used in
|
119
|
+
Note that threads are still used in cluster mode, and the `-t` thread flag setting is per worker, so `-w 2 -t 16:16` will spawn 32 threads in total, with 16 in each worker process.
|
120
120
|
|
121
121
|
If the `WEB_CONCURRENCY` environment variable is set to `"auto"` and the `concurrent-ruby` gem is available in your application, Puma will set the worker process count to the result of [available processors](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent.html#available_processor_count-class_method).
|
122
122
|
|
123
123
|
For an in-depth discussion of the tradeoffs of thread and process count settings, [see our docs](https://github.com/puma/puma/blob/9282a8efa5a0c48e39c60d22ca70051a25df9f55/docs/kubernetes.md#workers-per-pod-and-other-config-issues).
|
124
124
|
|
125
|
-
In
|
125
|
+
In cluster mode, Puma can "preload" your application. This loads all the application code *prior* to forking. Preloading reduces total memory usage of your application via an operating system feature called [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write).
|
126
126
|
|
127
127
|
If the `WEB_CONCURRENCY` environment variable is set to a value > 1 (and `--prune-bundler` has not been specified), preloading will be enabled by default. Otherwise, you can use the `--preload` flag from the command line:
|
128
128
|
|
@@ -140,9 +140,9 @@ preload_app!
|
|
140
140
|
|
141
141
|
Preloading can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preloading copies the code of master into the workers.
|
142
142
|
|
143
|
-
####
|
143
|
+
#### Cluster mode hooks
|
144
144
|
|
145
|
-
When using
|
145
|
+
When using cluster mode, Puma's configuration DSL provides `before_fork` and `on_worker_boot`
|
146
146
|
hooks to run code when the master process forks and child workers are booted respectively.
|
147
147
|
|
148
148
|
It is recommended to use these hooks with `preload_app!`, otherwise constants loaded by your
|
@@ -159,7 +159,7 @@ on_worker_boot do
|
|
159
159
|
end
|
160
160
|
```
|
161
161
|
|
162
|
-
In addition, there is an `on_refork`
|
162
|
+
In addition, there is an `on_refork` and `after_refork` hooks which are used only in [`fork_worker` mode](docs/fork_worker.md),
|
163
163
|
when the worker 0 child process forks a grandchild worker:
|
164
164
|
|
165
165
|
```ruby
|
@@ -169,7 +169,14 @@ on_refork do
|
|
169
169
|
end
|
170
170
|
```
|
171
171
|
|
172
|
-
|
172
|
+
```ruby
|
173
|
+
after_refork do
|
174
|
+
# Used only when fork_worker mode is enabled. Add code to run inside the Puma worker 0
|
175
|
+
# child process after it forks a grandchild worker.
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
Importantly, note the following considerations when Ruby forks a child process:
|
173
180
|
|
174
181
|
1. File descriptors such as network sockets **are** copied from the parent to the forked
|
175
182
|
child process. Dual-use of the same sockets by parent and child will result in I/O conflicts
|
@@ -186,6 +193,7 @@ Therefore, we recommend the following:
|
|
186
193
|
2. If (1) is not possible, use `before_fork` and `on_refork` to disconnect the parent's socket
|
187
194
|
connections when forking, so that they are not accidentally copied to the child process.
|
188
195
|
3. Use `on_worker_boot` to restart any background threads on the forked child.
|
196
|
+
4. Use `after_refork` to restart any background threads on the parent.
|
189
197
|
|
190
198
|
#### Master process lifecycle hooks
|
191
199
|
|
data/docs/fork_worker.md
CHANGED
@@ -24,7 +24,13 @@ The `fork_worker` option allows your application to be initialized only once for
|
|
24
24
|
|
25
25
|
### Usage Considerations
|
26
26
|
|
27
|
-
- `fork_worker` introduces
|
27
|
+
- `fork_worker` introduces new `on_refork` and `after_refork` configuration hooks. Note the following:
|
28
|
+
- When initially forking the parent process to the worker 0 child, `before_fork` will trigger on the parent process and `on_worker_boot` will trigger on the worker 0 child as normal.
|
29
|
+
- When forking the worker 0 child to grandchild workers, `on_refork` and `after_refork` will trigger on the worker 0 child, and `on_worker_boot` will trigger on each grandchild worker.
|
30
|
+
- For clarity, `before_fork` does not trigger on worker 0, and `after_refork` does not trigger on the grandchild.
|
31
|
+
- As a general migration guide:
|
32
|
+
- Copy any logic within your existing `before_fork` hook to the `on_refork` hook.
|
33
|
+
- Consider to copy logic from your `on_worker_boot` hook to the `after_refork` hook, if it is needed to reset the state of worker 0 after it forks.
|
28
34
|
|
29
35
|
### Limitations
|
30
36
|
|
data/docs/java_options.md
CHANGED
@@ -12,6 +12,7 @@ Moreover, default values may be used in case of invalid inputs.
|
|
12
12
|
| PUMA_QUERY_STRING_MAX_LENGTH | 1024 * 10 | Positive natural number |
|
13
13
|
| PUMA_REQUEST_PATH_MAX_LENGTH | 8192 | Positive natural number |
|
14
14
|
| PUMA_REQUEST_URI_MAX_LENGTH | 1024 * 12 | Positive natural number |
|
15
|
+
| PUMA_SKIP_SIGUSR2 | nil | n/a |
|
15
16
|
|
16
17
|
## Examples
|
17
18
|
|
@@ -46,3 +47,8 @@ foo@bar:~ curl "http://localhost:9292${path}"
|
|
46
47
|
Hello World
|
47
48
|
```
|
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/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/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://ruby-
|
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,10 +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
|
58
|
+
* backlog: requests that are waiting for an available thread to be available. if this is frequently above 0, you need more capacity.
|
59
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
60
|
this will be a never-changing number (other than rare cases when a thread dies, etc).
|
61
|
-
*
|
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.
|
62
66
|
* max_threads: the maximum number of threads Puma is configured to spool per worker
|
63
67
|
* requests_count: the number of requests this worker has served since starting
|
64
68
|
|
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
|
@@ -29,7 +29,7 @@ public class Http11 extends RubyObject {
|
|
29
29
|
public final static int MAX_REQUEST_URI_LENGTH = getConstLength("PUMA_REQUEST_URI_MAX_LENGTH", 1024 * 12);
|
30
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
|
32
|
+
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element FRAGMENT is longer than the 1024 allowed length.";
|
33
33
|
public final static int MAX_REQUEST_PATH_LENGTH = getConstLength("PUMA_REQUEST_PATH_MAX_LENGTH", 8192);
|
34
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
35
|
public final static int MAX_QUERY_STRING_LENGTH = getConstLength("PUMA_QUERY_STRING_MAX_LENGTH", 10 * 1024);
|
@@ -77,7 +77,7 @@ public class Http11 extends RubyObject {
|
|
77
77
|
|
78
78
|
public static void createHttp11(Ruby runtime) {
|
79
79
|
RubyModule mPuma = runtime.defineModule("Puma");
|
80
|
-
mPuma.defineClassUnder("HttpParserError",runtime.getClass("
|
80
|
+
mPuma.defineClassUnder("HttpParserError",runtime.getClass("StandardError"),runtime.getClass("StandardError").getAllocator());
|
81
81
|
|
82
82
|
RubyClass cHttpParser = mPuma.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR);
|
83
83
|
cHttpParser.defineAnnotatedMethods(Http11.class);
|
@@ -475,7 +475,7 @@ void Init_puma_http11(void)
|
|
475
475
|
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
476
476
|
DEF_GLOBAL(request_path, "REQUEST_PATH");
|
477
477
|
|
478
|
-
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError",
|
478
|
+
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eStandardError);
|
479
479
|
rb_global_variable(&eHttpParserError);
|
480
480
|
|
481
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
@@ -142,7 +142,14 @@ module Puma
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
+
def before_parse(&block)
|
146
|
+
@before_parse ||= []
|
147
|
+
@before_parse << block if block
|
148
|
+
@before_parse
|
149
|
+
end
|
150
|
+
|
145
151
|
def parse(binds, log_writer = nil, log_msg = 'Listening')
|
152
|
+
before_parse.each(&:call)
|
146
153
|
log_writer ||= @log_writer
|
147
154
|
binds.each do |str|
|
148
155
|
uri = URI.parse str
|
data/lib/puma/client.rb
CHANGED
@@ -170,7 +170,7 @@ module Puma
|
|
170
170
|
if @buffer
|
171
171
|
return false unless try_to_parse_proxy_protocol
|
172
172
|
|
173
|
-
@parsed_bytes =
|
173
|
+
@parsed_bytes = parser_execute
|
174
174
|
|
175
175
|
if @parser.finished?
|
176
176
|
return setup_body
|
@@ -273,20 +273,20 @@ module Puma
|
|
273
273
|
|
274
274
|
return false unless try_to_parse_proxy_protocol
|
275
275
|
|
276
|
-
@parsed_bytes =
|
276
|
+
@parsed_bytes = parser_execute
|
277
277
|
|
278
278
|
if @parser.finished? && above_http_content_limit(@parser.body.bytesize)
|
279
279
|
@http_content_length_limit_exceeded = true
|
280
280
|
end
|
281
281
|
|
282
282
|
if @parser.finished?
|
283
|
-
|
283
|
+
setup_body
|
284
284
|
elsif @parsed_bytes >= MAX_HEADER
|
285
285
|
raise HttpParserError,
|
286
286
|
"HEADER is longer than allowed, aborting client early."
|
287
|
+
else
|
288
|
+
false
|
287
289
|
end
|
288
|
-
|
289
|
-
false
|
290
290
|
end
|
291
291
|
|
292
292
|
def eagerly_finish
|
@@ -300,6 +300,44 @@ module Puma
|
|
300
300
|
@to_io.wait_readable(timeout) || timeout! until try_to_finish
|
301
301
|
end
|
302
302
|
|
303
|
+
# Wraps `@parser.execute` and adds meaningful error messages
|
304
|
+
# @return [Integer] bytes of buffer read by parser
|
305
|
+
#
|
306
|
+
def parser_execute
|
307
|
+
@parser.execute(@env, @buffer, @parsed_bytes)
|
308
|
+
rescue => e
|
309
|
+
@env[HTTP_CONNECTION] = 'close'
|
310
|
+
raise e unless HttpParserError === e && e.message.include?('non-SSL')
|
311
|
+
|
312
|
+
req, _ = @buffer.split "\r\n\r\n"
|
313
|
+
request_line, headers = req.split "\r\n", 2
|
314
|
+
|
315
|
+
# below checks for request issues and changes error message accordingly
|
316
|
+
if !@env.key? REQUEST_METHOD
|
317
|
+
if request_line.count(' ') != 2
|
318
|
+
# maybe this is an SSL connection ?
|
319
|
+
raise e
|
320
|
+
else
|
321
|
+
method = request_line[/\A[^ ]+/]
|
322
|
+
raise e, "Invalid HTTP format, parsing fails. Bad method #{method}"
|
323
|
+
end
|
324
|
+
elsif !@env.key? REQUEST_PATH
|
325
|
+
path = request_line[/\A[^ ]+ +([^ ?\r\n]+)/, 1]
|
326
|
+
raise e, "Invalid HTTP format, parsing fails. Bad path #{path}"
|
327
|
+
elsif request_line.match?(/\A[^ ]+ +[^ ?\r\n]+\?/) && !@env.key?(QUERY_STRING)
|
328
|
+
query = request_line[/\A[^ ]+ +[^? ]+\?([^ ]+)/, 1]
|
329
|
+
raise e, "Invalid HTTP format, parsing fails. Bad query #{query}"
|
330
|
+
elsif !@env.key? SERVER_PROTOCOL
|
331
|
+
# protocol is bad
|
332
|
+
text = request_line[/[^ ]*\z/]
|
333
|
+
raise HttpParserError, "Invalid HTTP format, parsing fails. Bad protocol #{text}"
|
334
|
+
elsif !headers.empty?
|
335
|
+
# headers are bad
|
336
|
+
hdrs = headers.split("\r\n").map { |h| h.gsub "\n", '\n'}.join "\n"
|
337
|
+
raise HttpParserError, "Invalid HTTP format, parsing fails. Bad headers\n#{hdrs}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
303
341
|
def timeout!
|
304
342
|
write_error(408) if in_data_phase
|
305
343
|
raise ConnectionError
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -88,25 +88,27 @@ module Puma
|
|
88
88
|
server.begin_restart(true)
|
89
89
|
@config.run_hooks(:before_refork, nil, @log_writer, @hook_data)
|
90
90
|
end
|
91
|
+
elsif idx == -2 # refork cycle is done
|
92
|
+
@config.run_hooks(:after_refork, nil, @log_writer, @hook_data)
|
91
93
|
elsif idx == 0 # restart server
|
92
94
|
restart_server << true << false
|
93
95
|
else # fork worker
|
94
96
|
worker_pids << pid = spawn_worker(idx)
|
95
|
-
@worker_write << "#{
|
97
|
+
@worker_write << "#{PIPE_FORK}#{pid}:#{idx}\n" rescue nil
|
96
98
|
end
|
97
99
|
end
|
98
100
|
end
|
99
101
|
end
|
100
102
|
|
101
103
|
Signal.trap "SIGTERM" do
|
102
|
-
@worker_write << "#{
|
104
|
+
@worker_write << "#{PIPE_EXTERNAL_TERM}#{Process.pid}\n" rescue nil
|
103
105
|
restart_server.clear
|
104
106
|
server.stop
|
105
107
|
restart_server << false
|
106
108
|
end
|
107
109
|
|
108
110
|
begin
|
109
|
-
@worker_write << "#{
|
111
|
+
@worker_write << "#{PIPE_BOOT}#{Process.pid}:#{index}\n"
|
110
112
|
rescue SystemCallError, IOError
|
111
113
|
Puma::Util.purge_interrupt_queue
|
112
114
|
STDERR.puts "Master seems to have exited, exiting."
|
@@ -122,7 +124,7 @@ module Puma
|
|
122
124
|
|
123
125
|
stat_thread ||= Thread.new(@worker_write) do |io|
|
124
126
|
Puma.set_thread_name "stat pld"
|
125
|
-
base_payload = "
|
127
|
+
base_payload = "#{PIPE_PING}#{Process.pid}"
|
126
128
|
|
127
129
|
while true
|
128
130
|
begin
|
@@ -131,7 +133,8 @@ module Puma
|
|
131
133
|
t = server.pool_capacity || 0
|
132
134
|
m = server.max_threads || 0
|
133
135
|
rc = server.requests_count || 0
|
134
|
-
|
136
|
+
bt = server.busy_threads || 0
|
137
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads":#{m}, "requests_count":#{rc}, "busy_threads":#{bt} }\n!
|
135
138
|
io << payload
|
136
139
|
rescue IOError
|
137
140
|
Puma::Util.purge_interrupt_queue
|
@@ -147,7 +150,7 @@ module Puma
|
|
147
150
|
# exiting until any background operations are completed
|
148
151
|
@config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
|
149
152
|
ensure
|
150
|
-
@worker_write << "#{
|
153
|
+
@worker_write << "#{PIPE_TERM}#{Process.pid}\n" rescue nil
|
151
154
|
@worker_write.close
|
152
155
|
end
|
153
156
|
|
@@ -51,7 +51,7 @@ module Puma
|
|
51
51
|
@term
|
52
52
|
end
|
53
53
|
|
54
|
-
STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*) }/
|
54
|
+
STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*), "busy_threads":(?<busy_threads>\d*) }/
|
55
55
|
private_constant :STATUS_PATTERN
|
56
56
|
|
57
57
|
def ping!(status)
|
data/lib/puma/cluster.rb
CHANGED
@@ -44,10 +44,15 @@ module Puma
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def start_phased_restart
|
47
|
+
def start_phased_restart(refork = false)
|
48
48
|
@events.fire_on_restart!
|
49
|
+
|
49
50
|
@phase += 1
|
50
|
-
|
51
|
+
if refork
|
52
|
+
log "- Starting worker refork, phase: #{@phase}"
|
53
|
+
else
|
54
|
+
log "- Starting phased worker restart, phase: #{@phase}"
|
55
|
+
end
|
51
56
|
|
52
57
|
# Be sure to change the directory again before loading
|
53
58
|
# the app. This way we can pick up new code.
|
@@ -87,6 +92,10 @@ module Puma
|
|
87
92
|
|
88
93
|
if @options[:fork_worker] && all_workers_in_phase?
|
89
94
|
@fork_writer << "0\n"
|
95
|
+
|
96
|
+
if worker_at(0).phase > 0
|
97
|
+
@fork_writer << "-2\n"
|
98
|
+
end
|
90
99
|
end
|
91
100
|
end
|
92
101
|
|
@@ -162,7 +171,7 @@ module Puma
|
|
162
171
|
(@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
|
163
172
|
end
|
164
173
|
|
165
|
-
def check_workers
|
174
|
+
def check_workers(refork = false)
|
166
175
|
return if @next_check >= Time.now
|
167
176
|
|
168
177
|
@next_check = Time.now + @options[:worker_check_interval]
|
@@ -180,7 +189,12 @@ module Puma
|
|
180
189
|
w = @workers.find { |x| x.phase != @phase }
|
181
190
|
|
182
191
|
if w
|
183
|
-
|
192
|
+
if refork
|
193
|
+
log "- Stopping #{w.pid} for refork..."
|
194
|
+
else
|
195
|
+
log "- Stopping #{w.pid} for phased upgrade..."
|
196
|
+
end
|
197
|
+
|
184
198
|
unless w.term?
|
185
199
|
w.term
|
186
200
|
log "- #{w.signal} sent to #{w.pid}..."
|
@@ -224,7 +238,7 @@ module Puma
|
|
224
238
|
def phased_restart(refork = false)
|
225
239
|
return false if @options[:preload_app] && !refork
|
226
240
|
|
227
|
-
@phased_restart = true
|
241
|
+
@phased_restart = refork ? :refork : true
|
228
242
|
wakeup!
|
229
243
|
|
230
244
|
true
|
@@ -364,7 +378,7 @@ module Puma
|
|
364
378
|
|
365
379
|
before = Thread.list.reject(&fork_safe)
|
366
380
|
|
367
|
-
log "* Restarts: (\u2714) hot (\u2716) phased"
|
381
|
+
log "* Restarts: (\u2714) hot (\u2716) phased (#{@options[:fork_worker] ? "\u2714" : "\u2716"}) refork"
|
368
382
|
log "* Preloading application"
|
369
383
|
load_and_bind
|
370
384
|
|
@@ -382,7 +396,7 @@ module Puma
|
|
382
396
|
end
|
383
397
|
end
|
384
398
|
else
|
385
|
-
log "* Restarts: (\u2714) hot (\u2714) phased"
|
399
|
+
log "* Restarts: (\u2714) hot (\u2714) phased (#{@options[:fork_worker] ? "\u2714" : "\u2716"}) refork"
|
386
400
|
|
387
401
|
unless @config.app_configured?
|
388
402
|
error "No application configured, nothing to run"
|
@@ -444,19 +458,23 @@ module Puma
|
|
444
458
|
end
|
445
459
|
|
446
460
|
if @phased_restart
|
447
|
-
start_phased_restart
|
461
|
+
start_phased_restart(@phased_restart == :refork)
|
462
|
+
|
463
|
+
in_phased_restart = @phased_restart
|
448
464
|
@phased_restart = false
|
449
|
-
|
465
|
+
|
450
466
|
workers_not_booted = @options[:workers]
|
467
|
+
# worker 0 is not restarted on refork
|
468
|
+
workers_not_booted -= 1 if in_phased_restart == :refork
|
451
469
|
end
|
452
470
|
|
453
|
-
check_workers
|
471
|
+
check_workers(in_phased_restart == :refork)
|
454
472
|
|
455
473
|
if read.wait_readable([0, @next_check - Time.now].max)
|
456
474
|
req = read.read_nonblock(1)
|
457
475
|
next unless req
|
458
476
|
|
459
|
-
if req ==
|
477
|
+
if req == PIPE_WAKEUP
|
460
478
|
@next_check = Time.now
|
461
479
|
next
|
462
480
|
end
|
@@ -464,7 +482,7 @@ module Puma
|
|
464
482
|
result = read.gets
|
465
483
|
pid = result.to_i
|
466
484
|
|
467
|
-
if req ==
|
485
|
+
if req == PIPE_BOOT || req == PIPE_FORK
|
468
486
|
pid, idx = result.split(':').map(&:to_i)
|
469
487
|
w = worker_at idx
|
470
488
|
w.pid = pid if w.pid.nil?
|
@@ -472,22 +490,22 @@ module Puma
|
|
472
490
|
|
473
491
|
if w = @workers.find { |x| x.pid == pid }
|
474
492
|
case req
|
475
|
-
when
|
493
|
+
when PIPE_BOOT
|
476
494
|
w.boot!
|
477
495
|
log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
|
478
496
|
@next_check = Time.now
|
479
497
|
workers_not_booted -= 1
|
480
|
-
when
|
498
|
+
when PIPE_EXTERNAL_TERM
|
481
499
|
# external term, see worker method, Signal.trap "SIGTERM"
|
482
500
|
w.term!
|
483
|
-
when
|
501
|
+
when PIPE_TERM
|
484
502
|
w.term unless w.term?
|
485
|
-
when
|
503
|
+
when PIPE_PING
|
486
504
|
status = result.sub(/^\d+/,'').chomp
|
487
505
|
w.ping!(status)
|
488
506
|
@events.fire(:ping!, w)
|
489
507
|
|
490
|
-
if in_phased_restart && workers_not_booted.positive? && w0 = worker_at(0)
|
508
|
+
if in_phased_restart && @options[:fork_worker] && workers_not_booted.positive? && w0 = worker_at(0)
|
491
509
|
w0.ping!(status)
|
492
510
|
@events.fire(:ping!, w0)
|
493
511
|
end
|
@@ -497,7 +515,7 @@ module Puma
|
|
497
515
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
498
516
|
booted = true
|
499
517
|
end
|
500
|
-
when
|
518
|
+
when PIPE_IDLE
|
501
519
|
if idle_workers[pid]
|
502
520
|
idle_workers.delete pid
|
503
521
|
else
|
data/lib/puma/configuration.rb
CHANGED
data/lib/puma/const.rb
CHANGED
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "6.
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "6.6.1"
|
104
|
+
CODE_NAME = "Return to Forever"
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
107
107
|
|
@@ -294,14 +294,15 @@ module Puma
|
|
294
294
|
|
295
295
|
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
296
296
|
|
297
|
+
# All constants are prefixed with `PIPE_` to avoid name collisions.
|
297
298
|
module PipeRequest
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
299
|
+
PIPE_WAKEUP = "!"
|
300
|
+
PIPE_BOOT = "b"
|
301
|
+
PIPE_FORK = "f"
|
302
|
+
PIPE_EXTERNAL_TERM = "e"
|
303
|
+
PIPE_TERM = "t"
|
304
|
+
PIPE_PING = "p"
|
305
|
+
PIPE_IDLE = "i"
|
305
306
|
end
|
306
307
|
end
|
307
308
|
end
|
data/lib/puma/dsl.rb
CHANGED
@@ -47,6 +47,7 @@ module Puma
|
|
47
47
|
# | on_worker_boot | :before_worker_boot | inside, before |
|
48
48
|
# | on_worker_shutdown | :before_worker_shutdown | inside, after |
|
49
49
|
# | on_refork | :before_refork | inside |
|
50
|
+
# | after_refork | :after_refork | inside |
|
50
51
|
#
|
51
52
|
class DSL
|
52
53
|
ON_WORKER_KEY = [String, Symbol].freeze
|
@@ -438,8 +439,7 @@ module Puma
|
|
438
439
|
# end
|
439
440
|
#
|
440
441
|
def on_restart(&block)
|
441
|
-
|
442
|
-
@options[:on_restart] << block
|
442
|
+
process_hook :on_restart, nil, block, 'on_restart'
|
443
443
|
end
|
444
444
|
|
445
445
|
# Command to use to restart Puma. This should be just how to
|
@@ -654,8 +654,6 @@ module Puma
|
|
654
654
|
# @example
|
655
655
|
# state_permission 0600
|
656
656
|
#
|
657
|
-
# @version 5.0.0
|
658
|
-
#
|
659
657
|
def state_permission(permission)
|
660
658
|
@options[:state_permission] = permission
|
661
659
|
end
|
@@ -731,8 +729,7 @@ module Puma
|
|
731
729
|
def before_fork(&block)
|
732
730
|
warn_if_in_single_mode('before_fork')
|
733
731
|
|
734
|
-
|
735
|
-
@options[:before_fork] << block
|
732
|
+
process_hook :before_fork, nil, block, 'before_fork'
|
736
733
|
end
|
737
734
|
|
738
735
|
# Code to run in a worker when it boots to setup
|
@@ -812,7 +809,7 @@ module Puma
|
|
812
809
|
|
813
810
|
alias_method :after_worker_boot, :after_worker_fork
|
814
811
|
|
815
|
-
# Code to run after puma is booted (works for both
|
812
|
+
# Code to run after puma is booted (works for both single and cluster modes).
|
816
813
|
#
|
817
814
|
# @example
|
818
815
|
# on_booted do
|
@@ -823,7 +820,7 @@ module Puma
|
|
823
820
|
@config.options[:events].on_booted(&block)
|
824
821
|
end
|
825
822
|
|
826
|
-
# Code to run after puma is stopped (works for both
|
823
|
+
# Code to run after puma is stopped (works for both single and cluster modes).
|
827
824
|
#
|
828
825
|
# @example
|
829
826
|
# on_stopped do
|
@@ -852,12 +849,33 @@ module Puma
|
|
852
849
|
# 3.times {GC.start}
|
853
850
|
# end
|
854
851
|
#
|
855
|
-
# @version 5.0.0
|
856
|
-
#
|
857
852
|
def on_refork(key = nil, &block)
|
853
|
+
warn_if_in_single_mode('on_refork')
|
854
|
+
|
858
855
|
process_hook :before_refork, key, block, 'on_refork'
|
859
856
|
end
|
860
857
|
|
858
|
+
# When `fork_worker` is enabled, code to run in Worker 0
|
859
|
+
# after all other workers are re-forked from this process,
|
860
|
+
# after the server has temporarily stopped serving requests
|
861
|
+
# (once per complete refork cycle).
|
862
|
+
#
|
863
|
+
# This can be used to re-open any connections to remote servers
|
864
|
+
# (database, Redis, ...) that were closed via on_refork.
|
865
|
+
#
|
866
|
+
# This can be called multiple times to add several hooks.
|
867
|
+
#
|
868
|
+
# @note Cluster mode with `fork_worker` enabled only.
|
869
|
+
#
|
870
|
+
# @example
|
871
|
+
# after_refork do
|
872
|
+
# puts 'After refork...'
|
873
|
+
# end
|
874
|
+
#
|
875
|
+
def after_refork(key = nil, &block)
|
876
|
+
process_hook :after_refork, key, block, 'after_refork'
|
877
|
+
end
|
878
|
+
|
861
879
|
# Provide a block to be executed just before a thread is added to the thread
|
862
880
|
# pool. Be careful: while the block executes, thread creation is delayed, and
|
863
881
|
# probably a request will have to wait too! The new thread will not be added to
|
@@ -876,8 +894,7 @@ module Puma
|
|
876
894
|
# end
|
877
895
|
#
|
878
896
|
def on_thread_start(&block)
|
879
|
-
|
880
|
-
@options[:before_thread_start] << block
|
897
|
+
process_hook :before_thread_start, nil, block, 'on_thread_start'
|
881
898
|
end
|
882
899
|
|
883
900
|
# Provide a block to be executed after a thread is trimmed from the thread
|
@@ -901,8 +918,7 @@ module Puma
|
|
901
918
|
# end
|
902
919
|
#
|
903
920
|
def on_thread_exit(&block)
|
904
|
-
|
905
|
-
@options[:before_thread_exit] << block
|
921
|
+
process_hook :before_thread_exit, nil, block, 'on_thread_exit'
|
906
922
|
end
|
907
923
|
|
908
924
|
# Code to run out-of-band when the worker is idle.
|
@@ -1091,7 +1107,7 @@ module Puma
|
|
1091
1107
|
|
1092
1108
|
# Set the timeout for worker shutdown.
|
1093
1109
|
#
|
1094
|
-
# The default is
|
1110
|
+
# The default is 30 seconds.
|
1095
1111
|
#
|
1096
1112
|
# @note Cluster mode only.
|
1097
1113
|
#
|
@@ -1123,10 +1139,10 @@ module Puma
|
|
1123
1139
|
# @see Puma::Cluster#cull_workers
|
1124
1140
|
#
|
1125
1141
|
def worker_culling_strategy(strategy)
|
1126
|
-
|
1142
|
+
strategy = strategy.to_sym
|
1127
1143
|
|
1128
1144
|
if ![:youngest, :oldest].include?(strategy)
|
1129
|
-
raise "Invalid value for worker_culling_strategy - #{
|
1145
|
+
raise "Invalid value for worker_culling_strategy - #{strategy}"
|
1130
1146
|
end
|
1131
1147
|
|
1132
1148
|
@options[:worker_culling_strategy] = strategy
|
@@ -1174,8 +1190,6 @@ module Puma
|
|
1174
1190
|
# @see Puma::Server#handle_servers
|
1175
1191
|
# @see Puma::ThreadPool#wait_for_less_busy_worker
|
1176
1192
|
#
|
1177
|
-
# @version 5.0.0
|
1178
|
-
#
|
1179
1193
|
def wait_for_less_busy_worker(val=0.005)
|
1180
1194
|
@options[:wait_for_less_busy_worker] = val.to_f
|
1181
1195
|
end
|
@@ -1249,10 +1263,9 @@ module Puma
|
|
1249
1263
|
# A refork will automatically trigger once after the specified number of requests
|
1250
1264
|
# (default 1000), or pass 0 to disable auto refork.
|
1251
1265
|
#
|
1266
|
+
# @note This is experimental.
|
1252
1267
|
# @note Cluster mode only.
|
1253
1268
|
#
|
1254
|
-
# @version 5.0.0
|
1255
|
-
#
|
1256
1269
|
def fork_worker(after_requests=1000)
|
1257
1270
|
@options[:fork_worker] = Integer(after_requests)
|
1258
1271
|
end
|
@@ -1415,8 +1428,8 @@ module Puma
|
|
1415
1428
|
if workers_val == 0
|
1416
1429
|
log_string =
|
1417
1430
|
"Warning: You specified code to run in a `#{hook_name}` block, " \
|
1418
|
-
"but Puma is not configured to run in cluster mode (worker count > 0
|
1419
|
-
"so your `#{hook_name}` block
|
1431
|
+
"but Puma is not configured to run in cluster mode (worker count > 0), " \
|
1432
|
+
"so your `#{hook_name}` block will not run."
|
1420
1433
|
|
1421
1434
|
LogWriter.stdio.log(log_string)
|
1422
1435
|
end
|
data/lib/puma/launcher.rb
CHANGED
@@ -167,6 +167,13 @@ module Puma
|
|
167
167
|
log "* phased-restart called but not available, restarting normally."
|
168
168
|
return restart
|
169
169
|
end
|
170
|
+
|
171
|
+
if @options.file_options[:tag].nil?
|
172
|
+
dir = File.realdirpath(@restart_dir)
|
173
|
+
@options[:tag] = File.basename(dir)
|
174
|
+
set_process_title
|
175
|
+
end
|
176
|
+
|
170
177
|
true
|
171
178
|
end
|
172
179
|
|
@@ -412,12 +419,14 @@ module Puma
|
|
412
419
|
end
|
413
420
|
|
414
421
|
def setup_signals
|
415
|
-
|
416
|
-
|
417
|
-
|
422
|
+
unless ENV["PUMA_SKIP_SIGUSR2"]
|
423
|
+
begin
|
424
|
+
Signal.trap "SIGUSR2" do
|
425
|
+
restart
|
426
|
+
end
|
427
|
+
rescue Exception
|
428
|
+
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
418
429
|
end
|
419
|
-
rescue Exception
|
420
|
-
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
421
430
|
end
|
422
431
|
|
423
432
|
unless Puma.jruby?
|
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/request.rb
CHANGED
@@ -135,12 +135,15 @@ module Puma
|
|
135
135
|
uncork_socket client.io
|
136
136
|
app_body.close if app_body.respond_to? :close
|
137
137
|
client&.tempfile_close
|
138
|
-
after_reply = env[RACK_AFTER_REPLY]
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
138
|
+
if after_reply = env[RACK_AFTER_REPLY]
|
139
|
+
after_reply.each do |o|
|
140
|
+
begin
|
141
|
+
o.call
|
142
|
+
rescue StandardError => e
|
143
|
+
@log_writer.debug_error e
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
144
147
|
end
|
145
148
|
|
146
149
|
# Assembles the headers and prepares the body for actually sending the
|
@@ -191,7 +194,8 @@ module Puma
|
|
191
194
|
elsif res_body.is_a?(File) && res_body.respond_to?(:size)
|
192
195
|
body = res_body
|
193
196
|
content_length = body.size
|
194
|
-
elsif res_body.respond_to?(:to_path) &&
|
197
|
+
elsif res_body.respond_to?(:to_path) && (fn = res_body.to_path) &&
|
198
|
+
File.readable?(fn)
|
195
199
|
body = File.open fn, 'rb'
|
196
200
|
content_length = body.size
|
197
201
|
close_body = true
|
@@ -199,7 +203,7 @@ module Puma
|
|
199
203
|
body = res_body
|
200
204
|
end
|
201
205
|
elsif !res_body.is_a?(::File) && res_body.respond_to?(:to_path) &&
|
202
|
-
File.readable?(fn = res_body.to_path)
|
206
|
+
(fn = res_body.to_path) && File.readable?(fn = res_body.to_path)
|
203
207
|
body = File.open fn, 'rb'
|
204
208
|
content_length = body.size
|
205
209
|
close_body = true
|
data/lib/puma/runner.rb
CHANGED
@@ -8,6 +8,9 @@ module Puma
|
|
8
8
|
# serve requests. This class spawns a new instance of `Puma::Server` via
|
9
9
|
# a call to `start_server`.
|
10
10
|
class Runner
|
11
|
+
|
12
|
+
include ::Puma::Const::PipeRequest
|
13
|
+
|
11
14
|
def initialize(launcher)
|
12
15
|
@launcher = launcher
|
13
16
|
@log_writer = launcher.log_writer
|
@@ -27,7 +30,7 @@ module Puma
|
|
27
30
|
def wakeup!
|
28
31
|
return unless @wakeup
|
29
32
|
|
30
|
-
@wakeup.write
|
33
|
+
@wakeup.write PIPE_WAKEUP unless @wakeup.closed?
|
31
34
|
|
32
35
|
rescue SystemCallError, IOError
|
33
36
|
Puma::Util.purge_interrupt_queue
|
data/lib/puma/sd_notify.rb
CHANGED
@@ -137,10 +137,7 @@ module Puma
|
|
137
137
|
ENV.delete("NOTIFY_SOCKET") if unset_env
|
138
138
|
|
139
139
|
begin
|
140
|
-
Addrinfo.unix(sock, :DGRAM).connect
|
141
|
-
s.close_on_exec = true
|
142
|
-
s.write(state)
|
143
|
-
end
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect { |s| s.write state }
|
144
141
|
rescue StandardError => e
|
145
142
|
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
146
143
|
end
|
data/lib/puma/server.rb
CHANGED
@@ -233,6 +233,11 @@ module Puma
|
|
233
233
|
@thread_pool&.pool_capacity
|
234
234
|
end
|
235
235
|
|
236
|
+
# @!attribute [r] busy_threads
|
237
|
+
def busy_threads
|
238
|
+
@thread_pool&.busy_threads
|
239
|
+
end
|
240
|
+
|
236
241
|
# Runs the server.
|
237
242
|
#
|
238
243
|
# If +background+ is true (the default) then a thread is spun
|
@@ -253,9 +258,8 @@ module Puma
|
|
253
258
|
@reactor.run
|
254
259
|
end
|
255
260
|
|
256
|
-
|
257
261
|
@thread_pool.auto_reap! if options[:reaping_time]
|
258
|
-
@thread_pool.auto_trim! if options[:auto_trim_time]
|
262
|
+
@thread_pool.auto_trim! if @min_threads != @max_threads && options[:auto_trim_time]
|
259
263
|
|
260
264
|
@check, @notify = Puma::Util.pipe unless @notify
|
261
265
|
|
@@ -340,7 +344,7 @@ module Puma
|
|
340
344
|
@idle_timeout_reached = true
|
341
345
|
|
342
346
|
if @clustered
|
343
|
-
@worker_write << "#{PipeRequest::
|
347
|
+
@worker_write << "#{PipeRequest::PIPE_IDLE}#{Process.pid}\n" rescue nil
|
344
348
|
next
|
345
349
|
else
|
346
350
|
@log_writer.log "- Idle timeout reached"
|
@@ -353,7 +357,7 @@ module Puma
|
|
353
357
|
|
354
358
|
if @idle_timeout_reached && @clustered
|
355
359
|
@idle_timeout_reached = false
|
356
|
-
@worker_write << "#{PipeRequest::
|
360
|
+
@worker_write << "#{PipeRequest::PIPE_IDLE}#{Process.pid}\n" rescue nil
|
357
361
|
end
|
358
362
|
|
359
363
|
ios.first.each do |sock|
|
@@ -646,7 +650,7 @@ module Puma
|
|
646
650
|
|
647
651
|
# List of methods invoked by #stats.
|
648
652
|
# @version 5.0.0
|
649
|
-
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
|
653
|
+
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count, :busy_threads].freeze
|
650
654
|
|
651
655
|
# Returns a hash of stats about the running server for reporting purposes.
|
652
656
|
# @version 5.0.0
|
data/lib/puma/single.rb
CHANGED
data/lib/puma/thread_pool.rb
CHANGED
@@ -91,7 +91,8 @@ module Puma
|
|
91
91
|
with_mutex do
|
92
92
|
{ backlog: @todo.size,
|
93
93
|
running: @spawned,
|
94
|
-
pool_capacity: @waiting + (@max - @spawned)
|
94
|
+
pool_capacity: @waiting + (@max - @spawned),
|
95
|
+
busy_threads: @spawned - @waiting + @todo.size
|
95
96
|
}
|
96
97
|
end
|
97
98
|
end
|
@@ -369,12 +370,12 @@ module Puma
|
|
369
370
|
end
|
370
371
|
|
371
372
|
def auto_trim!(timeout=@auto_trim_time)
|
372
|
-
@auto_trim = Automaton.new(self, timeout, "#{@name}
|
373
|
+
@auto_trim = Automaton.new(self, timeout, "#{@name} tp trim", :trim)
|
373
374
|
@auto_trim.start!
|
374
375
|
end
|
375
376
|
|
376
377
|
def auto_reap!(timeout=@reaping_time)
|
377
|
-
@reaper = Automaton.new(self, timeout, "#{@name}
|
378
|
+
@reaper = Automaton.new(self, timeout, "#{@name} tp reap", :reap)
|
378
379
|
@reaper.start!
|
379
380
|
end
|
380
381
|
|
data/tools/Dockerfile
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
FROM ruby:3.2
|
4
4
|
|
5
|
+
RUN apt-get update && apt-get install -y ragel
|
6
|
+
|
5
7
|
# throw errors if Gemfile has been modified since Gemfile.lock
|
6
8
|
RUN bundle config --global frozen 1
|
7
9
|
|
@@ -13,4 +15,4 @@ RUN bundle install
|
|
13
15
|
RUN bundle exec rake compile
|
14
16
|
|
15
17
|
EXPOSE 9292
|
16
|
-
CMD bundle exec bin/puma test/rackup/hello.ru
|
18
|
+
CMD ["bundle", "exec", "bin/puma", "test/rackup/hello.ru"]
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.6.1
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,7 +131,6 @@ metadata:
|
|
132
131
|
homepage_uri: https://puma.io
|
133
132
|
source_code_uri: https://github.com/puma/puma
|
134
133
|
rubygems_mfa_required: 'true'
|
135
|
-
post_install_message:
|
136
134
|
rdoc_options: []
|
137
135
|
require_paths:
|
138
136
|
- lib
|
@@ -147,8 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
145
|
- !ruby/object:Gem::Version
|
148
146
|
version: '0'
|
149
147
|
requirements: []
|
150
|
-
rubygems_version: 3.
|
151
|
-
signing_key:
|
148
|
+
rubygems_version: 3.6.9
|
152
149
|
specification_version: 4
|
153
150
|
summary: A Ruby/Rack web server built for parallelism.
|
154
151
|
test_files: []
|