puma 6.6.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: 94e6b5d56525a92e2e279d59467572a254c7c03b9dc14a3e91ec5eba67b54620
4
- data.tar.gz: 55e78ab10a5d222cce09dc89a92fbbd15d8e8ae7abbec95ca309e2903f68bd97
3
+ metadata.gz: 574e9afd59afd9a6d5b86bd74cc7e524e73982b61ca294884540cb54324771bf
4
+ data.tar.gz: 4702dc966028a0cb7c7f912ba6cdc33446071e8cd13ea63f14070f247f5d4504
5
5
  SHA512:
6
- metadata.gz: 750f9ec059b9710eb5ae739dc04ad600fff8386a40dc3ef83500189caacf8b7413d01eb60d751befd1aa077f3565e60617d13ceba8b9a0c29912abf410b700ff
7
- data.tar.gz: 1a56b1696333ae86e643bd045ebe83f6a1700ac5df948113a443326e867ce17bf3f4b7e05e01cda6b99605130a2b77ccf49d2ae153ad2e879971c7238d652548
6
+ metadata.gz: 89dccb7f7cde9cc70904c28cf6026022239eac61fe8c903d924f10e50a59d17876d181feb4c80c7b4315c46d166fdcca7743dca16a3c52e27827f2e2d39a8ec3
7
+ data.tar.gz: c30aa7e48f3b6c191351ebe5c9b407fec7b2523c679bc915c21aefb854b0b43e7b9d776fcc3fdef574da70df71225ae4dbb78ed7045f4b178ae8a8578ae48efd
data/History.md CHANGED
@@ -1,3 +1,13 @@
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
+
1
11
  ## 6.6.0 / 2025-01-29
2
12
 
3
13
  * Features
@@ -163,7 +173,7 @@
163
173
 
164
174
  * Features
165
175
  * Ability to supply a custom logger ([#2770], [#2511])
166
- * Warn when clustered-only hooks are defined in single mode ([#3089])
176
+ * Warn when cluster mode-only hooks are defined in single mode ([#3089])
167
177
  * Adds the on_booted event ([#2709])
168
178
 
169
179
  * Bugfixes
@@ -758,7 +768,7 @@ Each patchlevel release contains a separate security fix. We recommend simply up
758
768
  * Fix Java 8 support ([#1773])
759
769
  * Fix error `uninitialized constant Puma::Cluster` ([#1731])
760
770
  * Fix `not_token` being able to be set to true ([#1803])
761
- * 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])
762
772
 
763
773
  ## 3.12.1 / 2019-03-19
764
774
 
@@ -1169,7 +1179,7 @@ Each patchlevel release contains a separate security fix. We recommend simply up
1169
1179
  * 4 minor features:
1170
1180
 
1171
1181
  * Listen to unix socket with provided backlog if any
1172
- * Improves the clustered stats to report worker stats
1182
+ * Improves the cluster mode stats to report worker stats
1173
1183
  * Pass the env to the lowlevel_error handler. Fixes [#854]
1174
1184
  * Treat path-like hosts as unix sockets. Fixes [#824]
1175
1185
 
@@ -1896,7 +1906,7 @@ The "clearly I don't have enough tests for the config" release.
1896
1906
 
1897
1907
  * 3 doc changes:
1898
1908
  * Add note about on_worker_boot hook
1899
- * Add some documentation for Clustered mode
1909
+ * Add some documentation for Cluster mode
1900
1910
  * Added quotes to /etc/puma.conf
1901
1911
 
1902
1912
  ## 2.0.1 / 2013-04-30
@@ -2150,6 +2160,12 @@ be added back in a future date when a java Puma::MiniSSL is added.
2150
2160
  * Bugfixes
2151
2161
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
2152
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"
2153
2169
  [#3570]:https://github.com/puma/puma/pull/3570 "PR by @mohamedhafez, merged 2024-12-30"
2154
2170
  [#3567]:https://github.com/puma/puma/issues/3567 "Issue by @mohamedhafez, closed 2024-12-30"
2155
2171
  [#3383]:https://github.com/puma/puma/pull/3383 "PR by @joshuay03, merged 2024-11-29"
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
@@ -176,7 +176,7 @@ after_refork do
176
176
  end
177
177
  ```
178
178
 
179
- Importantly, note the following considerations when Ruby forks a child process:
179
+ Importantly, note the following considerations when Ruby forks a child process:
180
180
 
181
181
  1. File descriptors such as network sockets **are** copied from the parent to the forked
182
182
  child process. Dual-use of the same sockets by parent and child will result in I/O conflicts
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/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);
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
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.
@@ -166,7 +171,7 @@ module Puma
166
171
  (@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
167
172
  end
168
173
 
169
- def check_workers
174
+ def check_workers(refork = false)
170
175
  return if @next_check >= Time.now
171
176
 
172
177
  @next_check = Time.now + @options[:worker_check_interval]
@@ -184,7 +189,12 @@ module Puma
184
189
  w = @workers.find { |x| x.phase != @phase }
185
190
 
186
191
  if w
187
- 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
+
188
198
  unless w.term?
189
199
  w.term
190
200
  log "- #{w.signal} sent to #{w.pid}..."
@@ -228,7 +238,7 @@ module Puma
228
238
  def phased_restart(refork = false)
229
239
  return false if @options[:preload_app] && !refork
230
240
 
231
- @phased_restart = true
241
+ @phased_restart = refork ? :refork : true
232
242
  wakeup!
233
243
 
234
244
  true
@@ -368,7 +378,7 @@ module Puma
368
378
 
369
379
  before = Thread.list.reject(&fork_safe)
370
380
 
371
- log "* Restarts: (\u2714) hot (\u2716) phased"
381
+ log "* Restarts: (\u2714) hot (\u2716) phased (#{@options[:fork_worker] ? "\u2714" : "\u2716"}) refork"
372
382
  log "* Preloading application"
373
383
  load_and_bind
374
384
 
@@ -386,7 +396,7 @@ module Puma
386
396
  end
387
397
  end
388
398
  else
389
- log "* Restarts: (\u2714) hot (\u2714) phased"
399
+ log "* Restarts: (\u2714) hot (\u2714) phased (#{@options[:fork_worker] ? "\u2714" : "\u2716"}) refork"
390
400
 
391
401
  unless @config.app_configured?
392
402
  error "No application configured, nothing to run"
@@ -448,13 +458,17 @@ module Puma
448
458
  end
449
459
 
450
460
  if @phased_restart
451
- start_phased_restart
461
+ start_phased_restart(@phased_restart == :refork)
462
+
463
+ in_phased_restart = @phased_restart
452
464
  @phased_restart = false
453
- in_phased_restart = true
465
+
454
466
  workers_not_booted = @options[:workers]
467
+ # worker 0 is not restarted on refork
468
+ workers_not_booted -= 1 if in_phased_restart == :refork
455
469
  end
456
470
 
457
- check_workers
471
+ check_workers(in_phased_restart == :refork)
458
472
 
459
473
  if read.wait_readable([0, @next_check - Time.now].max)
460
474
  req = read.read_nonblock(1)
data/lib/puma/const.rb CHANGED
@@ -100,7 +100,7 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "6.6.0"
103
+ PUMA_VERSION = VERSION = "6.6.1"
104
104
  CODE_NAME = "Return to Forever"
105
105
 
106
106
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
data/lib/puma/dsl.rb CHANGED
@@ -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
@@ -811,7 +809,7 @@ module Puma
811
809
 
812
810
  alias_method :after_worker_boot, :after_worker_fork
813
811
 
814
- # 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).
815
813
  #
816
814
  # @example
817
815
  # on_booted do
@@ -822,7 +820,7 @@ module Puma
822
820
  @config.options[:events].on_booted(&block)
823
821
  end
824
822
 
825
- # 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).
826
824
  #
827
825
  # @example
828
826
  # on_stopped do
@@ -851,8 +849,6 @@ module Puma
851
849
  # 3.times {GC.start}
852
850
  # end
853
851
  #
854
- # @version 5.0.0
855
- #
856
852
  def on_refork(key = nil, &block)
857
853
  warn_if_in_single_mode('on_refork')
858
854
 
@@ -1111,7 +1107,7 @@ module Puma
1111
1107
 
1112
1108
  # Set the timeout for worker shutdown.
1113
1109
  #
1114
- # The default is 60 seconds.
1110
+ # The default is 30 seconds.
1115
1111
  #
1116
1112
  # @note Cluster mode only.
1117
1113
  #
@@ -1143,10 +1139,10 @@ module Puma
1143
1139
  # @see Puma::Cluster#cull_workers
1144
1140
  #
1145
1141
  def worker_culling_strategy(strategy)
1146
- stategy = strategy.to_sym
1142
+ strategy = strategy.to_sym
1147
1143
 
1148
1144
  if ![:youngest, :oldest].include?(strategy)
1149
- raise "Invalid value for worker_culling_strategy - #{stategy}"
1145
+ raise "Invalid value for worker_culling_strategy - #{strategy}"
1150
1146
  end
1151
1147
 
1152
1148
  @options[:worker_culling_strategy] = strategy
@@ -1194,8 +1190,6 @@ module Puma
1194
1190
  # @see Puma::Server#handle_servers
1195
1191
  # @see Puma::ThreadPool#wait_for_less_busy_worker
1196
1192
  #
1197
- # @version 5.0.0
1198
- #
1199
1193
  def wait_for_less_busy_worker(val=0.005)
1200
1194
  @options[:wait_for_less_busy_worker] = val.to_f
1201
1195
  end
@@ -1269,10 +1263,9 @@ module Puma
1269
1263
  # A refork will automatically trigger once after the specified number of requests
1270
1264
  # (default 1000), or pass 0 to disable auto refork.
1271
1265
  #
1266
+ # @note This is experimental.
1272
1267
  # @note Cluster mode only.
1273
1268
  #
1274
- # @version 5.0.0
1275
- #
1276
1269
  def fork_worker(after_requests=1000)
1277
1270
  @options[:fork_worker] = Integer(after_requests)
1278
1271
  end
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/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
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,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.6.0
4
+ version: 6.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-28 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: nio4r
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
145
  - !ruby/object:Gem::Version
146
146
  version: '0'
147
147
  requirements: []
148
- rubygems_version: 3.6.3
148
+ rubygems_version: 3.6.9
149
149
  specification_version: 4
150
150
  summary: A Ruby/Rack web server built for parallelism.
151
151
  test_files: []