puma 6.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63fdc6ae2fa73c81ae2f7b46eab301f0fa4f3ad7363f6299736b349fcf3421f8
4
- data.tar.gz: 80573e037561faed9663644fcf281f717b7e60c1b8a41f3215bb530ac53a9320
3
+ metadata.gz: 574e9afd59afd9a6d5b86bd74cc7e524e73982b61ca294884540cb54324771bf
4
+ data.tar.gz: 4702dc966028a0cb7c7f912ba6cdc33446071e8cd13ea63f14070f247f5d4504
5
5
  SHA512:
6
- metadata.gz: 18fa6b3bb2dd0b2397cc92feead8728c6582fb94190d0a669147a9cd391eb0d61927427cc843f7d4385de46b281d1461d648edc818a01e495fc5341dbdda3d30
7
- data.tar.gz: 47877aca488f7c25549a0bcc07541e789a1de9d582c9e686949e311c258442128821d7187c478ffd7421acde1db8e88ed45ddb9f150b4396326f63404b78b6ee
6
+ metadata.gz: 89dccb7f7cde9cc70904c28cf6026022239eac61fe8c903d924f10e50a59d17876d181feb4c80c7b4315c46d166fdcca7743dca16a3c52e27827f2e2d39a8ec3
7
+ data.tar.gz: c30aa7e48f3b6c191351ebe5c9b407fec7b2523c679bc915c21aefb854b0b43e7b9d776fcc3fdef574da70df71225ae4dbb78ed7045f4b178ae8a8578ae48efd
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 clustered-only hooks are defined in single mode ([#3089])
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 clustered mode" (PR [#1741], [#1674], [#1720], [#1730], [#1755])
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 clustered stats to report worker stats
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 Clustered mode
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 @maxkwallace, closed 2016-04-07"
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 @maxkwallace, merged 2016-04-12"
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
- [![Actions](https://github.com/puma/puma/workflows/Tests/badge.svg?branch=master)](https://github.com/puma/puma/actions?query=workflow%3ATests)
7
+ [![Actions](https://github.com/puma/puma/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/puma/puma/actions/workflows/tests.yml?query=branch%3Amaster)
8
8
  [![Code Climate](https://codeclimate.com/github/puma/puma.svg)](https://codeclimate.com/github/puma/puma)
9
9
  [![StackOverflow](https://img.shields.io/badge/stackoverflow-Puma-blue.svg)]( 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
- ### Clustered mode
105
+ ### Cluster mode
106
106
 
107
- Puma also offers "clustered mode". Clustered 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:
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 clustered 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.
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 clustered 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).
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
- #### Clustered mode hooks
143
+ #### Cluster mode hooks
144
144
 
145
- When using clustered mode, Puma's configuration DSL provides `before_fork` and `on_worker_boot`
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` hook which is used only in [`fork_worker` mode](docs/fork_worker.md),
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
- Importantly, note the following considerations when Ruby forks a child process:
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 a new `on_refork` configuration hook. If you were using the `before_fork` hook previously, we generally recommend to copy its logic to `on_refork`. Note that `fork_worker` triggers the `before_fork` configuration hook *only* when initially forking the master process to worker 0, and triggers the `on_refork` hook on all subsequent forks from worker 0 to additional workers.
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-doc.org/3.2.2/Process.html#method-c-kill):
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-doc.org/3.2.2/Process.html#method-c-detach
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 [always true?]
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
- * pool_capacity: the number of requests that the server is capable of taking right now. For example, if the number is 5, then it means there are 5 threads sitting idle ready to take a request. If one request comes in, then the value would be 4 until it finishes processing. If the minimum threads allowed is zero, this number will still have a maximum value of the maximum threads allowed.
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 clustered mode and application preload.
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 REQUEST_PATH is longer than the 1024 allowed length.";
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("IOError"),runtime.getClass("IOError").getAllocator());
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", rb_eIOError);
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);
@@ -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('&;').include? "token=#{@auth_token}"
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 = @parser.execute(@env, @buffer, @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 = @parser.execute(@env, @buffer, @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
- return setup_body
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
@@ -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 << "#{Puma::Const::PipeRequest::FORK}#{pid}:#{idx}\n" rescue nil
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 << "#{Puma::Const::PipeRequest::EXTERNAL_TERM}#{Process.pid}\n" rescue nil
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 << "#{Puma::Const::PipeRequest::BOOT}#{Process.pid}:#{index}\n"
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 = "p#{Process.pid}"
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
- payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads":#{m}, "requests_count":#{rc} }\n!
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 << "#{Puma::Const::PipeRequest::TERM}#{Process.pid}\n" rescue nil
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
- log "- Starting phased worker restart, phase: #{@phase}"
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
- log "- Stopping #{w.pid} for phased upgrade..."
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
- in_phased_restart = true
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 == Puma::Const::PipeRequest::WAKEUP
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 == Puma::Const::PipeRequest::BOOT || req == Puma::Const::PipeRequest::FORK
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 Puma::Const::PipeRequest::BOOT
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 Puma::Const::PipeRequest::EXTERNAL_TERM
498
+ when PIPE_EXTERNAL_TERM
481
499
  # external term, see worker method, Signal.trap "SIGTERM"
482
500
  w.term!
483
- when Puma::Const::PipeRequest::TERM
501
+ when PIPE_TERM
484
502
  w.term unless w.term?
485
- when Puma::Const::PipeRequest::PING
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 Puma::Const::PipeRequest::IDLE
518
+ when PIPE_IDLE
501
519
  if idle_workers[pid]
502
520
  idle_workers.delete pid
503
521
  else
@@ -318,6 +318,8 @@ module Puma
318
318
  # @param arg [Launcher, Int] `:on_restart` passes Launcher
319
319
  #
320
320
  def run_hooks(key, arg, log_writer, hook_data = nil)
321
+ log_writer.debug "Running #{key} hooks"
322
+
321
323
  @options.all_of(key).each do |b|
322
324
  begin
323
325
  if Array === b
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.5.0"
104
- CODE_NAME = "Sky's Version"
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
- WAKEUP = "!"
299
- BOOT = "b"
300
- FORK = "f"
301
- EXTERNAL_TERM = "e"
302
- TERM = "t"
303
- PING = "p"
304
- IDLE = "i"
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
- @options[:on_restart] ||= []
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
- @options[:before_fork] ||= []
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: single and clustered)
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: single and clustered)
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
- @options[:before_thread_start] ||= []
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
- @options[:before_thread_exit] ||= []
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 60 seconds.
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
- stategy = strategy.to_sym
1142
+ strategy = strategy.to_sym
1127
1143
 
1128
1144
  if ![:youngest, :oldest].include?(strategy)
1129
- raise "Invalid value for worker_culling_strategy - #{stategy}"
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 did not run"
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
- begin
416
- Signal.trap "SIGUSR2" do
417
- restart
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/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
- begin
140
- after_reply.each { |o| o.call }
141
- rescue StandardError => e
142
- @log_writer.debug_error e
143
- end unless after_reply.empty?
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) && File.readable?(fn = res_body.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 Puma::Const::PipeRequest::WAKEUP unless @wakeup.closed?
33
+ @wakeup.write PIPE_WAKEUP unless @wakeup.closed?
31
34
 
32
35
  rescue SystemCallError, IOError
33
36
  Puma::Util.purge_interrupt_queue
@@ -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 do |s|
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::IDLE}#{Process.pid}\n" rescue nil
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::IDLE}#{Process.pid}\n" rescue nil
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
@@ -17,7 +17,7 @@ module Puma
17
17
  def stats
18
18
  {
19
19
  started_at: utc_iso8601(@started_at)
20
- }.merge(@server.stats).merge(super)
20
+ }.merge(@server&.stats || {}).merge(super)
21
21
  end
22
22
 
23
23
  def restart
@@ -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} threadpool trimmer", :trim)
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} threadpool reaper", :reap)
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.5.0
4
+ version: 6.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-22 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: nio4r
@@ -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.5.22
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: []