puma 6.0.0-java → 6.0.2-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 207c639db6488ca310a34b3a36e3aad929e0092d51cae794c20b538e632387e0
4
- data.tar.gz: aec4cb5abd8bd5ca672c4b8a277b80f922fe40ddc68abee0163c95e1d7f77361
3
+ metadata.gz: 9eb8ef6f417ad39e4feed73ed51aa3d483ff4ddb3312ddfc5bebabe6902c7b8c
4
+ data.tar.gz: 280255be5dc225b430f442ee9c00e21c279a32a88f15caed4fae3cdc2c7c74e7
5
5
  SHA512:
6
- metadata.gz: 03d26c6cceb420d1b97b360d690c1d742c290c47b28e2f19bd121ffe8c3cc36fc7c081ea26a2c78c27e40243237b53358fd23ec829a4a3c669013ca4d8b2a49e
7
- data.tar.gz: 286b9ea43b67608f73f55959e2e1c82dd5d56639c07117aaa4481d067a60a3b63199cf7dd5b540bb1754d0fe5acb12183cb5b26361c040d653615e84c80127c2
6
+ metadata.gz: dfc4d0562d09c4998493896c07721c0e2dad3669f3d5862a49f01d8d99b6062070171d320990e200268cd7be6bb0e0845e0343a71f953d0e6cefc8d8653fead3
7
+ data.tar.gz: 76d9ba10c17cb25b7d5e9d5393cf019bb71917c94bdcc610bbcabc802947c0a55deed210f73fb0c39474e93d39cecddd8047edefc1aec7f878a7d3998f1d6be1
data/History.md CHANGED
@@ -1,4 +1,19 @@
1
- ## 6.0.0 / 2022-10-XX
1
+ ## 6.0.2 / 2023-01-01
2
+
3
+ * Refactor
4
+ * Remove use of etc and time gems in Puma ([#3035], [#3033])
5
+ * Refactor const.rb - freeze ([#3016])
6
+
7
+ ## 6.0.1 / 2022-12-20
8
+
9
+ * Bugfixes
10
+ * Handle waking up a closed selector in Reactor#add ([#3005])
11
+ * Fixup response processing, enumerable bodies ([#3004], [#3000])
12
+ * Correctly close app body for all code paths ([#3002], [#2999])
13
+ * Refactor
14
+ * Add IOBuffer to Client, remove from ThreadPool thread instances ([#3013])
15
+
16
+ ## 6.0.0 / 2022-10-14
2
17
 
3
18
  * Breaking Changes
4
19
  * Dropping Ruby 2.2 and 2.3 support (now 2.4+) ([#2919])
@@ -9,6 +24,9 @@
9
24
  * Prefix all environment variables with `PUMA_` ([#2924], [#2853])
10
25
  * Removed some constants ([#2957], [#2958], [#2959], [#2960])
11
26
  * The following classes are now part of Puma's private API: `Client`, `Cluster::Worker`, `Cluster::Worker`, `HandleRequest`. ([#2988])
27
+ * Configuration constants like `DefaultRackup` removed ([#2928])
28
+ * Extracted `LogWriter` from `Events` ([#2798])
29
+ * Only accept the standard 8 HTTP methods, others rejected with 501. ([#2932])
12
30
 
13
31
  * Features
14
32
  * Increase throughput on large (100kb+) response bodies by 3-10x ([#2896], [#2892])
@@ -34,7 +52,6 @@
34
52
 
35
53
  * Refactor
36
54
  * log_writer.rb - add internal_write method ([#2888])
37
- * [WIP] Refactor: Split out LogWriter from Events (no logic change) ([#2798])
38
55
  * Extract prune_bundler code into it's own class. ([#2797])
39
56
  * Refactor Launcher#run to increase readability (no logic change) ([#2795])
40
57
  * Ruby 3.2 will have native IO#wait_* methods, don't require io/wait ([#2903])
@@ -1915,6 +1932,15 @@ be added back in a future date when a java Puma::MiniSSL is added.
1915
1932
  * Bugfixes
1916
1933
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
1917
1934
 
1935
+ [#3035]:https://github.com/puma/puma/pull/3035 "PR by @MSP-Greg, merged 2022-12-24"
1936
+ [#3033]:https://github.com/puma/puma/issues/3033 "Issue by @jules-w2, closed 2022-12-24"
1937
+ [#3016]:https://github.com/puma/puma/pull/3016 "PR by @MSP-Greg, merged 2022-12-24"
1938
+ [#3005]:https://github.com/puma/puma/pull/3005 "PR by @JuanitoFatas, merged 2022-11-04"
1939
+ [#3004]:https://github.com/puma/puma/pull/3004 "PR by @MSP-Greg, merged 2022-11-24"
1940
+ [#3000]:https://github.com/puma/puma/issues/3000 "Issue by @dentarg, closed 2022-11-24"
1941
+ [#3002]:https://github.com/puma/puma/pull/3002 "PR by @MSP-Greg, merged 2022-11-03"
1942
+ [#2999]:https://github.com/puma/puma/issues/2999 "Issue by @aymeric-ledorze, closed 2022-11-03"
1943
+ [#3013]:https://github.com/puma/puma/pull/3013 "PR by @MSP-Greg, merged 2022-11-13"
1918
1944
  [#2919]:https://github.com/puma/puma/pull/2919 "PR by @MSP-Greg, merged 2022-08-30"
1919
1945
  [#2652]:https://github.com/puma/puma/issues/2652 "Issue by @Roguelazer, closed 2022-09-04"
1920
1946
  [#2653]:https://github.com/puma/puma/pull/2653 "PR by @Roguelazer, closed 2022-03-07"
@@ -1928,7 +1954,10 @@ be added back in a future date when a java Puma::MiniSSL is added.
1928
1954
  [#2958]:https://github.com/puma/puma/pull/2958 "PR by @JuanitoFatas, merged 2022-09-16"
1929
1955
  [#2959]:https://github.com/puma/puma/pull/2959 "PR by @JuanitoFatas, merged 2022-09-16"
1930
1956
  [#2960]:https://github.com/puma/puma/pull/2960 "PR by @JuanitoFatas, merged 2022-09-16"
1931
- [#2988]:https://github.com/puma/puma/issues/2988 "Issue by @MSP-Greg, merged 2022-10-12"
1957
+ [#2988]:https://github.com/puma/puma/pull/2988 "PR by @MSP-Greg, merged 2022-10-12"
1958
+ [#2928]:https://github.com/puma/puma/pull/2928 "PR by @nateberkopec, merged 2022-09-10"
1959
+ [#2798]:https://github.com/puma/puma/pull/2798 "PR by @johnnyshields, merged 2022-02-05"
1960
+ [#2932]:https://github.com/puma/puma/pull/2932 "PR by @mrzasa, merged 2022-09-12"
1932
1961
  [#2896]:https://github.com/puma/puma/pull/2896 "PR by @MSP-Greg, merged 2022-09-13"
1933
1962
  [#2892]:https://github.com/puma/puma/pull/2892 "PR by @guilleiguaran, closed 2022-09-13"
1934
1963
  [#2923]:https://github.com/puma/puma/pull/2923 "PR by @nateberkopec, merged 2022-09-09"
@@ -1949,11 +1978,9 @@ be added back in a future date when a java Puma::MiniSSL is added.
1949
1978
  [#2904]:https://github.com/puma/puma/pull/2904 "PR by @kares, merged 2022-08-27"
1950
1979
  [#2884]:https://github.com/puma/puma/pull/2884 "PR by @kares, merged 2022-05-30"
1951
1980
  [#2897]:https://github.com/puma/puma/pull/2897 "PR by @Edouard-chin, merged 2022-08-27"
1952
- [#2932]:https://github.com/puma/puma/pull/2932 "PR by @mrzasa, merged 2022-09-12"
1953
1981
  [#1441]:https://github.com/puma/puma/issues/1441 "Issue by @nirvdrum, closed 2022-09-12"
1954
1982
  [#2956]:https://github.com/puma/puma/pull/2956 "PR by @MSP-Greg, merged 2022-09-15"
1955
1983
  [#2888]:https://github.com/puma/puma/pull/2888 "PR by @MSP-Greg, merged 2022-06-01"
1956
- [#2798]:https://github.com/puma/puma/pull/2798 "PR by @johnnyshields, merged 2022-02-05"
1957
1984
  [#2797]:https://github.com/puma/puma/pull/2797 "PR by @johnnyshields, merged 2022-02-01"
1958
1985
  [#2795]:https://github.com/puma/puma/pull/2795 "PR by @johnnyshields, merged 2022-01-31"
1959
1986
  [#2903]:https://github.com/puma/puma/pull/2903 "PR by @MSP-Greg, merged 2022-08-27"
data/docs/nginx.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This is a very common setup using an upstream. It was adapted from some Capistrano recipe I found on the Internet a while ago.
4
4
 
5
- ```
5
+ ```nginx
6
6
  upstream myapp {
7
7
  server unix:///myapp/tmp/puma.sock;
8
8
  }
data/lib/puma/client.rb CHANGED
@@ -9,6 +9,7 @@ class IO
9
9
  end
10
10
 
11
11
  require_relative 'detect'
12
+ require_relative 'io_buffer'
12
13
  require 'tempfile'
13
14
  require 'forwardable'
14
15
 
@@ -65,6 +66,7 @@ module Puma
65
66
  def initialize(io, env=nil)
66
67
  @io = io
67
68
  @to_io = io.to_io
69
+ @io_buffer = IOBuffer.new
68
70
  @proto_env = env
69
71
  @env = env ? env.dup : nil
70
72
 
@@ -96,7 +98,7 @@ module Puma
96
98
  end
97
99
 
98
100
  attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
99
- :tempfile
101
+ :tempfile, :io_buffer
100
102
 
101
103
  attr_writer :peerip
102
104
 
@@ -138,6 +140,7 @@ module Puma
138
140
 
139
141
  def reset(fast_check=true)
140
142
  @parser.reset
143
+ @io_buffer.reset
141
144
  @read_header = true
142
145
  @read_proxy = !!@expect_proxy_proto
143
146
  @env = @proto_env.dup
data/lib/puma/cluster.rb CHANGED
@@ -6,8 +6,6 @@ require_relative 'plugin'
6
6
  require_relative 'cluster/worker_handle'
7
7
  require_relative 'cluster/worker'
8
8
 
9
- require 'time'
10
-
11
9
  module Puma
12
10
  # This class is instantiated by the `Puma::Launcher` and used
13
11
  # to boot and serve a Ruby application when puma "workers" are needed
@@ -252,18 +250,18 @@ module Puma
252
250
  old_worker_count = @workers.count { |w| w.phase != @phase }
253
251
  worker_status = @workers.map do |w|
254
252
  {
255
- started_at: w.started_at.utc.iso8601,
253
+ started_at: utc_iso8601(w.started_at),
256
254
  pid: w.pid,
257
255
  index: w.index,
258
256
  phase: w.phase,
259
257
  booted: w.booted?,
260
- last_checkin: w.last_checkin.utc.iso8601,
258
+ last_checkin: utc_iso8601(w.last_checkin),
261
259
  last_status: w.last_status,
262
260
  }
263
261
  end
264
262
 
265
263
  {
266
- started_at: @started_at.utc.iso8601,
264
+ started_at: utc_iso8601(@started_at),
267
265
  workers: @workers.size,
268
266
  phase: @phase,
269
267
  booted_workers: worker_status.count { |w| w[:booted] },
data/lib/puma/const.rb CHANGED
@@ -5,7 +5,6 @@ module Puma
5
5
  class UnsupportedOption < RuntimeError
6
6
  end
7
7
 
8
-
9
8
  # Every standard HTTP code mapped to the appropriate message. These are
10
9
  # used so frequently that they are placed directly in Puma for easy
11
10
  # access rather than Puma::Const itself.
@@ -100,10 +99,10 @@ module Puma
100
99
  # too taxing on performance.
101
100
  module Const
102
101
 
103
- PUMA_VERSION = VERSION = "6.0.0".freeze
104
- CODE_NAME = "Sunflower".freeze
102
+ PUMA_VERSION = VERSION = "6.0.2"
103
+ CODE_NAME = "Sunflower"
105
104
 
106
- PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
105
+ PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
107
106
 
108
107
  FAST_TRACK_KA_TIMEOUT = 0.2
109
108
 
@@ -112,28 +111,28 @@ module Puma
112
111
  WRITE_TIMEOUT = 10
113
112
 
114
113
  # The original URI requested by the client.
115
- REQUEST_URI= 'REQUEST_URI'.freeze
116
- REQUEST_PATH = 'REQUEST_PATH'.freeze
117
- QUERY_STRING = 'QUERY_STRING'.freeze
118
- CONTENT_LENGTH = "CONTENT_LENGTH".freeze
114
+ REQUEST_URI= "REQUEST_URI"
115
+ REQUEST_PATH = "REQUEST_PATH"
116
+ QUERY_STRING = "QUERY_STRING"
117
+ CONTENT_LENGTH = "CONTENT_LENGTH"
119
118
 
120
- PATH_INFO = 'PATH_INFO'.freeze
119
+ PATH_INFO = "PATH_INFO"
121
120
 
122
- PUMA_TMP_BASE = "puma".freeze
121
+ PUMA_TMP_BASE = "puma"
123
122
 
124
123
  ERROR_RESPONSE = {
125
124
  # Indicate that we couldn't parse the request
126
- 400 => "HTTP/1.1 400 Bad Request\r\n\r\n".freeze,
125
+ 400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
127
126
  # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
128
- 404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze,
127
+ 404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND",
129
128
  # The standard empty 408 response for requests that timed out.
130
- 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
129
+ 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n",
131
130
  # Indicate that there was an internal error, obviously.
132
- 500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
131
+ 500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
133
132
  # Incorrect or invalid header value
134
- 501 => "HTTP/1.1 501 Not Implemented\r\n\r\n".freeze,
133
+ 501 => "HTTP/1.1 501 Not Implemented\r\n\r\n",
135
134
  # A common header for indicating the server is too busy. Not used yet.
136
- 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
135
+ 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
137
136
  }.freeze
138
137
 
139
138
  # The basic max request size we'll try to read.
@@ -146,95 +145,88 @@ module Puma
146
145
  # Maximum request body size before it is moved out of memory and into a tempfile for reading.
147
146
  MAX_BODY = MAX_HEADER
148
147
 
149
- REQUEST_METHOD = "REQUEST_METHOD".freeze
150
- HEAD = "HEAD".freeze
151
- GET = "GET".freeze
152
- POST = "POST".freeze
153
- PUT = "PUT".freeze
154
- DELETE = "DELETE".freeze
155
- OPTIONS = "OPTIONS".freeze
156
- TRACE = "TRACE".freeze
157
- PATCH = "PATCH".freeze
158
- SUPPORTED_HTTP_METHODS = [HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE, PATCH].freeze
148
+ REQUEST_METHOD = "REQUEST_METHOD"
149
+ HEAD = "HEAD"
150
+ SUPPORTED_HTTP_METHODS = %w[HEAD GET POST PUT DELETE OPTIONS TRACE PATCH].freeze
159
151
  # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
160
- LINE_END = "\r\n".freeze
161
- REMOTE_ADDR = "REMOTE_ADDR".freeze
162
- HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
163
- HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL".freeze
164
- HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME".freeze
165
- HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO".freeze
152
+ LINE_END = "\r\n"
153
+ REMOTE_ADDR = "REMOTE_ADDR"
154
+ HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
155
+ HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL"
156
+ HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME"
157
+ HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO"
166
158
 
167
- SERVER_NAME = "SERVER_NAME".freeze
168
- SERVER_PORT = "SERVER_PORT".freeze
169
- HTTP_HOST = "HTTP_HOST".freeze
170
- PORT_80 = "80".freeze
171
- PORT_443 = "443".freeze
172
- LOCALHOST = "localhost".freeze
173
- LOCALHOST_IPV4 = "127.0.0.1".freeze
174
- LOCALHOST_IPV6 = "::1".freeze
175
- UNSPECIFIED_IPV4 = "0.0.0.0".freeze
176
- UNSPECIFIED_IPV6 = "::".freeze
159
+ SERVER_NAME = "SERVER_NAME"
160
+ SERVER_PORT = "SERVER_PORT"
161
+ HTTP_HOST = "HTTP_HOST"
162
+ PORT_80 = "80"
163
+ PORT_443 = "443"
164
+ LOCALHOST = "localhost"
165
+ LOCALHOST_IPV4 = "127.0.0.1"
166
+ LOCALHOST_IPV6 = "::1"
167
+ UNSPECIFIED_IPV4 = "0.0.0.0"
168
+ UNSPECIFIED_IPV6 = "::"
177
169
 
178
- SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
179
- HTTP_11 = "HTTP/1.1".freeze
170
+ SERVER_PROTOCOL = "SERVER_PROTOCOL"
171
+ HTTP_11 = "HTTP/1.1"
180
172
 
181
- SERVER_SOFTWARE = "SERVER_SOFTWARE".freeze
182
- GATEWAY_INTERFACE = "GATEWAY_INTERFACE".freeze
183
- CGI_VER = "CGI/1.2".freeze
173
+ SERVER_SOFTWARE = "SERVER_SOFTWARE"
174
+ GATEWAY_INTERFACE = "GATEWAY_INTERFACE"
175
+ CGI_VER = "CGI/1.2"
184
176
 
185
- STOP_COMMAND = "?".freeze
186
- HALT_COMMAND = "!".freeze
187
- RESTART_COMMAND = "R".freeze
177
+ STOP_COMMAND = "?"
178
+ HALT_COMMAND = "!"
179
+ RESTART_COMMAND = "R"
188
180
 
189
- RACK_INPUT = "rack.input".freeze
190
- RACK_URL_SCHEME = "rack.url_scheme".freeze
191
- RACK_AFTER_REPLY = "rack.after_reply".freeze
192
- PUMA_SOCKET = "puma.socket".freeze
193
- PUMA_CONFIG = "puma.config".freeze
194
- PUMA_PEERCERT = "puma.peercert".freeze
181
+ RACK_INPUT = "rack.input"
182
+ RACK_URL_SCHEME = "rack.url_scheme"
183
+ RACK_AFTER_REPLY = "rack.after_reply"
184
+ PUMA_SOCKET = "puma.socket"
185
+ PUMA_CONFIG = "puma.config"
186
+ PUMA_PEERCERT = "puma.peercert"
195
187
 
196
- HTTP = "http".freeze
197
- HTTPS = "https".freeze
188
+ HTTP = "http"
189
+ HTTPS = "https"
198
190
 
199
- HTTPS_KEY = "HTTPS".freeze
191
+ HTTPS_KEY = "HTTPS"
200
192
 
201
- HTTP_VERSION = "HTTP_VERSION".freeze
202
- HTTP_CONNECTION = "HTTP_CONNECTION".freeze
203
- HTTP_EXPECT = "HTTP_EXPECT".freeze
204
- CONTINUE = "100-continue".freeze
193
+ HTTP_VERSION = "HTTP_VERSION"
194
+ HTTP_CONNECTION = "HTTP_CONNECTION"
195
+ HTTP_EXPECT = "HTTP_EXPECT"
196
+ CONTINUE = "100-continue"
205
197
 
206
- HTTP_11_100 = "HTTP/1.1 100 Continue\r\n\r\n".freeze
207
- HTTP_11_200 = "HTTP/1.1 200 OK\r\n".freeze
208
- HTTP_10_200 = "HTTP/1.0 200 OK\r\n".freeze
198
+ HTTP_11_100 = "HTTP/1.1 100 Continue\r\n\r\n"
199
+ HTTP_11_200 = "HTTP/1.1 200 OK\r\n"
200
+ HTTP_10_200 = "HTTP/1.0 200 OK\r\n"
209
201
 
210
- CLOSE = "close".freeze
211
- KEEP_ALIVE = "keep-alive".freeze
202
+ CLOSE = "close"
203
+ KEEP_ALIVE = "keep-alive"
212
204
 
213
- CONTENT_LENGTH2 = "content-length".freeze
214
- CONTENT_LENGTH_S = "Content-Length: ".freeze
215
- TRANSFER_ENCODING = "transfer-encoding".freeze
216
- TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING".freeze
205
+ CONTENT_LENGTH2 = "content-length"
206
+ CONTENT_LENGTH_S = "Content-Length: "
207
+ TRANSFER_ENCODING = "transfer-encoding"
208
+ TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING"
217
209
 
218
- CONNECTION_CLOSE = "Connection: close\r\n".freeze
219
- CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n".freeze
210
+ CONNECTION_CLOSE = "Connection: close\r\n"
211
+ CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n"
220
212
 
221
- TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n".freeze
222
- CLOSE_CHUNKED = "0\r\n\r\n".freeze
213
+ TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n"
214
+ CLOSE_CHUNKED = "0\r\n\r\n"
223
215
 
224
- CHUNKED = "chunked".freeze
216
+ CHUNKED = "chunked"
225
217
 
226
- COLON = ": ".freeze
218
+ COLON = ": "
227
219
 
228
- NEWLINE = "\n".freeze
220
+ NEWLINE = "\n"
229
221
 
230
- HIJACK_P = "rack.hijack?".freeze
231
- HIJACK = "rack.hijack".freeze
232
- HIJACK_IO = "rack.hijack_io".freeze
222
+ HIJACK_P = "rack.hijack?"
223
+ HIJACK = "rack.hijack"
224
+ HIJACK_IO = "rack.hijack_io"
233
225
 
234
- EARLY_HINTS = "rack.early_hints".freeze
226
+ EARLY_HINTS = "rack.early_hints"
235
227
 
236
228
  # Illegal character in the key or value of response header
237
- DQUOTE = "\"".freeze
229
+ DQUOTE = "\""
238
230
  HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
239
231
  ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
240
232
  # header values can contain HTAB?
data/lib/puma/dsl.rb CHANGED
@@ -250,6 +250,7 @@ module Puma
250
250
  #
251
251
  # * Set the socket backlog depth with +backlog+, default is 1024.
252
252
  # * Set up an SSL certificate with +key+ & +cert+.
253
+ # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
253
254
  # * Set whether to optimize for low latency instead of throughput with
254
255
  # +low_latency+, default is to not optimize for low latency. This is done
255
256
  # via +Socket::TCP_NODELAY+.
@@ -259,6 +260,8 @@ module Puma
259
260
  # bind 'unix:///var/run/puma.sock?backlog=512'
260
261
  # @example SSL cert
261
262
  # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
263
+ # @example SSL cert for mutual TLS (mTLS)
264
+ # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
262
265
  # @example Disable optimization for low latency
263
266
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
264
267
  # @example Socket permissions
@@ -22,6 +22,16 @@ module Puma
22
22
  read
23
23
  end
24
24
 
25
+ # Read & Reset - returns contents and resets
26
+ # @return [String] StringIO contents
27
+ def read_and_reset
28
+ rewind
29
+ str = read
30
+ truncate 0
31
+ rewind
32
+ str
33
+ end
34
+
25
35
  alias_method :clear, :reset
26
36
 
27
37
  # before Ruby 2.5, `write` would only take one argument
data/lib/puma/reactor.rb CHANGED
@@ -50,7 +50,7 @@ module Puma
50
50
  @input << client
51
51
  @selector.wakeup
52
52
  true
53
- rescue ClosedQueueError
53
+ rescue ClosedQueueError, IOError # Ignore if selector is already closed
54
54
  false
55
55
  end
56
56
 
data/lib/puma/request.rb CHANGED
@@ -14,15 +14,18 @@ module Puma
14
14
  #
15
15
  module Request # :nodoc:
16
16
 
17
- # determines whether to write body to io_buffer first, or straight to socket
18
- # also fixes max size of chunked body read when bosy is an IO.
19
- BODY_LEN_MAX = 1_024 * 256
17
+ # Single element array body: smaller bodies are written to io_buffer first,
18
+ # then a single write from io_buffer. Larger sizes are written separately.
19
+ # Also fixes max size of chunked file body read.
20
+ BODY_LEN_MAX = 1_024 * 256
20
21
 
21
- # size divide for using copy_stream on body
22
+ # File body: smaller bodies are combined with io_buffer, then written to
23
+ # socket. Larger bodies are written separately using `copy_stream`
22
24
  IO_BODY_MAX = 1_024 * 64
23
25
 
24
- # max size for io_buffer, force write when exceeded
25
- IO_BUFFER_LEN_MAX = 1_024 * 1_024 * 4
26
+ # Array body: elements are collected in io_buffer. When io_buffer's size
27
+ # exceeds value, they are written to the socket.
28
+ IO_BUFFER_LEN_MAX = 1_024 * 512
26
29
 
27
30
  SOCKET_WRITE_ERR_MSG = "Socket timeout writing data"
28
31
 
@@ -41,13 +44,14 @@ module Puma
41
44
  #
42
45
  # Finally, it'll return +true+ on keep-alive connections.
43
46
  # @param client [Puma::Client]
44
- # @param io_buffer [Puma::IOBuffer]
45
47
  # @param requests [Integer]
46
48
  # @return [Boolean,:async]
47
49
  #
48
- def handle_request(client, io_buffer, requests)
50
+ def handle_request(client, requests)
49
51
  env = client.env
52
+ io_buffer = client.io_buffer
50
53
  socket = client.io # io may be a MiniSSL::Socket
54
+ app_body = nil
51
55
 
52
56
  return false if closed_socket?(socket)
53
57
 
@@ -85,14 +89,18 @@ module Puma
85
89
 
86
90
  begin
87
91
  if SUPPORTED_HTTP_METHODS.include?(env[REQUEST_METHOD])
88
- status, headers, res_body = @thread_pool.with_force_shutdown do
92
+ status, headers, app_body = @thread_pool.with_force_shutdown do
89
93
  @app.call(env)
90
94
  end
91
95
  else
92
96
  @log_writer.log "Unsupported HTTP method used: #{env[REQUEST_METHOD]}"
93
- status, headers, res_body = [501, {}, ["#{env[REQUEST_METHOD]} method is not supported"]]
97
+ status, headers, app_body = [501, {}, ["#{env[REQUEST_METHOD]} method is not supported"]]
94
98
  end
95
99
 
100
+ # app_body needs to always be closed, hold value in case lowlevel_error
101
+ # is called
102
+ res_body = app_body
103
+
96
104
  return :async if client.hijacked
97
105
 
98
106
  status = status.to_i
@@ -114,60 +122,83 @@ module Puma
114
122
 
115
123
  status, headers, res_body = lowlevel_error(e, env, 500)
116
124
  end
117
- prepare_response(status, headers, res_body, io_buffer, requests, client)
125
+ prepare_response(status, headers, res_body, requests, client)
126
+ ensure
127
+ io_buffer.reset
128
+ uncork_socket client.io
129
+ app_body.close if app_body.respond_to? :close
130
+ client.tempfile&.unlink
131
+ after_reply = env[RACK_AFTER_REPLY] || []
132
+ begin
133
+ after_reply.each { |o| o.call }
134
+ rescue StandardError => e
135
+ @log_writer.debug_error e
136
+ end unless after_reply.empty?
118
137
  end
119
138
 
120
139
  # Assembles the headers and prepares the body for actually sending the
121
- # response via #fast_write_response.
140
+ # response via `#fast_write_response`.
122
141
  #
123
142
  # @param status [Integer] the status returned by the Rack application
124
143
  # @param headers [Hash] the headers returned by the Rack application
125
- # @param app_body [Array] the body returned by the Rack application or
126
- # a call to `lowlevel_error`
127
- # @param io_buffer [Puma::IOBuffer] modified in place
144
+ # @param res_body [Array] the body returned by the Rack application or
145
+ # a call to `Server#lowlevel_error`
128
146
  # @param requests [Integer] number of inline requests handled
129
147
  # @param client [Puma::Client]
130
- # @return [Boolean,:async]
131
- def prepare_response(status, headers, app_body, io_buffer, requests, client)
148
+ # @return [Boolean,:async] keep-alive status or `:async`
149
+ def prepare_response(status, headers, res_body, requests, client)
132
150
  env = client.env
133
- socket = client.io
134
-
135
- after_reply = env[RACK_AFTER_REPLY] || []
151
+ socket = client.io
152
+ io_buffer = client.io_buffer
136
153
 
137
154
  return false if closed_socket?(socket)
138
155
 
139
- resp_info = str_headers(env, status, headers, app_body, io_buffer, requests, client)
156
+ # Close the connection after a reasonable number of inline requests
157
+ # if the server is at capacity and the listener has a new connection ready.
158
+ # This allows Puma to service connections fairly when the number
159
+ # of concurrent connections exceeds the size of the threadpool.
160
+ force_keep_alive = requests < @max_fast_inline ||
161
+ @thread_pool.busy_threads < @max_threads ||
162
+ !client.listener.to_io.wait_readable(0)
163
+
164
+ resp_info = str_headers(env, status, headers, res_body, io_buffer, force_keep_alive)
165
+
166
+ close_body = false
140
167
 
141
168
  # below converts app_body into body, dependent on app_body's characteristics, and
142
169
  # resp_info[:content_length] will be set if it can be determined
143
170
  if !resp_info[:content_length] && !resp_info[:transfer_encoding] && status != 204
144
- if app_body.respond_to?(:to_ary)
145
- length = 0
146
- if array_body = app_body.to_ary
147
- body = array_body.map { |part| length += part.bytesize; part }
148
- elsif app_body.is_a?(::File) && app_body.respond_to?(:size)
149
- length = app_body.size
150
- elsif app_body.respond_to?(:each)
151
- body = []
152
- app_body.each { |part| length += part.bytesize; body << part }
153
- end
154
- resp_info[:content_length] = length
155
- elsif app_body.is_a?(File) && app_body.respond_to?(:size)
156
- resp_info[:content_length] = app_body.size
157
- body = app_body
158
- elsif app_body.respond_to?(:to_path) && app_body.respond_to?(:each) &&
159
- File.readable?(fn = app_body.to_path)
171
+ if res_body.respond_to?(:to_ary) && (array_body = res_body.to_ary)
172
+ body = array_body
173
+ resp_info[:content_length] = body.sum(&:bytesize)
174
+ elsif res_body.is_a?(File) && res_body.respond_to?(:size)
175
+ body = res_body
176
+ resp_info[:content_length] = body.size
177
+ elsif res_body.respond_to?(:to_path) && res_body.respond_to?(:each) &&
178
+ File.readable?(fn = res_body.to_path)
160
179
  body = File.open fn, 'rb'
161
180
  resp_info[:content_length] = body.size
181
+ close_body = true
162
182
  else
163
- body = app_body
183
+ body = res_body
164
184
  end
165
- elsif !app_body.is_a?(::File) && app_body.respond_to?(:to_path) && app_body.respond_to?(:each) &&
166
- File.readable?(fn = app_body.to_path)
185
+ elsif !res_body.is_a?(::File) && res_body.respond_to?(:to_path) && res_body.respond_to?(:each) &&
186
+ File.readable?(fn = res_body.to_path)
167
187
  body = File.open fn, 'rb'
168
188
  resp_info[:content_length] = body.size
189
+ close_body = true
190
+ elsif !res_body.is_a?(::File) && res_body.respond_to?(:filename) && res_body.respond_to?(:each) &&
191
+ res_body.respond_to?(:bytesize) && File.readable?(fn = res_body.filename)
192
+ # Sprockets::Asset
193
+ resp_info[:content_length] = res_body.bytesize unless resp_info[:content_length]
194
+ if res_body.to_hash[:source] # use each to return @source
195
+ body = res_body
196
+ else # avoid each and use a File object
197
+ body = File.open fn, 'rb'
198
+ close_body = true
199
+ end
169
200
  else
170
- body = app_body
201
+ body = res_body
171
202
  end
172
203
 
173
204
  line_ending = LINE_END
@@ -175,8 +206,8 @@ module Puma
175
206
  content_length = resp_info[:content_length]
176
207
  keep_alive = resp_info[:keep_alive]
177
208
 
178
- if app_body && !app_body.respond_to?(:each)
179
- response_hijack = app_body
209
+ if res_body && !res_body.respond_to?(:each)
210
+ response_hijack = res_body
180
211
  else
181
212
  response_hijack = resp_info[:response_hijack]
182
213
  end
@@ -189,7 +220,8 @@ module Puma
189
220
  end
190
221
 
191
222
  io_buffer << LINE_END
192
- fast_write_str socket, io_buffer.to_s
223
+ fast_write_str socket, io_buffer.read_and_reset
224
+ socket.flush
193
225
  return keep_alive
194
226
  end
195
227
  if content_length
@@ -203,25 +235,14 @@ module Puma
203
235
  io_buffer << line_ending
204
236
 
205
237
  if response_hijack
206
- fast_write_str socket, io_buffer.to_s
238
+ fast_write_str socket, io_buffer.read_and_reset
207
239
  response_hijack.call socket
208
240
  return :async
209
241
  end
210
242
 
211
243
  fast_write_response socket, body, io_buffer, chunked, content_length.to_i
244
+ body.close if close_body
212
245
  keep_alive
213
- ensure
214
- io_buffer.reset
215
- resp_info = nil
216
- uncork_socket socket
217
- app_body.close if app_body.respond_to? :close
218
- client.tempfile&.unlink
219
-
220
- begin
221
- after_reply.each { |o| o.call }
222
- rescue StandardError => e
223
- @log_writer.debug_error e
224
- end unless after_reply.empty?
225
246
  end
226
247
 
227
248
  # @param env [Hash] see Puma::Client#env, from request
@@ -237,10 +258,10 @@ module Puma
237
258
 
238
259
  # Used to write 'early hints', 'no body' responses, 'hijacked' responses,
239
260
  # and body segments (called by `fast_write_response`).
240
- # Writes a string to an io (normally `Client#io`) using `write_nonblock`.
261
+ # Writes a string to a socket (normally `Client#io`) using `write_nonblock`.
241
262
  # Large strings may not be written in one pass, especially if `io` is a
242
263
  # `MiniSSL::Socket`.
243
- # @param io [#write_nonblock] the io to write to
264
+ # @param socket [#write_nonblock] the request/response socket
244
265
  # @param str [String] the string written to the io
245
266
  # @raise [ConnectionError]
246
267
  #
@@ -249,7 +270,7 @@ module Puma
249
270
  byte_size = str.bytesize
250
271
  while n < byte_size
251
272
  begin
252
- n += socket.syswrite(n.zero? ? str : str.byteslice(n..-1))
273
+ n += socket.write_nonblock(n.zero? ? str : str.byteslice(n..-1))
253
274
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
254
275
  unless socket.wait_writable WRITE_TIMEOUT
255
276
  raise ConnectionError, SOCKET_WRITE_ERR_MSG
@@ -267,39 +288,41 @@ module Puma
267
288
  # @param socket [#write] the response socket
268
289
  # @param body [Enumerable, File] the body object
269
290
  # @param io_buffer [Puma::IOBuffer] contains headers
270
- # @param chunk [Boolean]
291
+ # @param chunked [Boolean]
292
+ # @paramn content_length [Integer
271
293
  # @raise [ConnectionError]
272
294
  #
273
295
  def fast_write_response(socket, body, io_buffer, chunked, content_length)
274
- if body.is_a?(::File) || body.respond_to?(:read) || body.respond_to?(:readpartial)
296
+ if body.is_a?(::File) && body.respond_to?(:read)
275
297
  if chunked # would this ever happen?
276
298
  while part = body.read(BODY_LEN_MAX)
277
299
  io_buffer.append part.bytesize.to_s(16), LINE_END, part, LINE_END
278
300
  end
279
- io_buffer << CLOSE_CHUNKED
280
- fast_write_str socket, io_buffer.to_s
301
+ fast_write_str socket, CLOSE_CHUNKED
281
302
  else
282
303
  if content_length <= IO_BODY_MAX
283
- io_buffer.write body.sysread(content_length)
284
- fast_write_str socket, io_buffer.to_s
304
+ io_buffer.write body.read(content_length)
305
+ fast_write_str socket, io_buffer.read_and_reset
285
306
  else
286
- fast_write_str socket, io_buffer.to_s
307
+ fast_write_str socket, io_buffer.read_and_reset
287
308
  IO.copy_stream body, socket
288
309
  end
289
310
  end
290
- body.close
291
311
  elsif body.is_a?(::Array) && body.length == 1
292
- body_first = body.first
293
- if body_first.is_a?(::String) && body_first.bytesize >= BODY_LEN_MAX
312
+ body_first = nil
313
+ # using body_first = body.first causes issues?
314
+ body.each { |str| body_first ||= str }
315
+
316
+ if body_first.is_a?(::String) && body_first.bytesize < BODY_LEN_MAX
317
+ # smaller body, write to io_buffer first
318
+ io_buffer.write body_first
319
+ fast_write_str socket, io_buffer.read_and_reset
320
+ else
294
321
  # large body, write both header & body to socket
295
- fast_write_str socket, io_buffer.to_s
322
+ fast_write_str socket, io_buffer.read_and_reset
296
323
  fast_write_str socket, body_first
297
- else
298
- # smaller body, write to stream first
299
- io_buffer.write body_first
300
- fast_write_str socket, io_buffer.to_s
301
324
  end
302
- else
325
+ elsif body.is_a?(::Array)
303
326
  # for array bodies, flush io_buffer to socket when size is greater than
304
327
  # IO_BUFFER_LEN_MAX
305
328
  if chunked
@@ -307,8 +330,7 @@ module Puma
307
330
  next if (byte_size = part.bytesize).zero?
308
331
  io_buffer.append byte_size.to_s(16), LINE_END, part, LINE_END
309
332
  if io_buffer.length > IO_BUFFER_LEN_MAX
310
- fast_write_str socket, io_buffer.to_s
311
- io_buffer.reset
333
+ fast_write_str socket, io_buffer.read_and_reset
312
334
  end
313
335
  end
314
336
  io_buffer.write CLOSE_CHUNKED
@@ -317,13 +339,31 @@ module Puma
317
339
  next if part.bytesize.zero?
318
340
  io_buffer.write part
319
341
  if io_buffer.length > IO_BUFFER_LEN_MAX
320
- fast_write_str socket, io_buffer.to_s
321
- io_buffer.reset
342
+ fast_write_str socket, io_buffer.read_and_reset
322
343
  end
323
344
  end
324
345
  end
325
- fast_write_str(socket, io_buffer.to_s) unless io_buffer.length.zero?
346
+ # may write last body part for non-chunked, also headers if array is empty
347
+ fast_write_str(socket, io_buffer.read_and_reset) unless io_buffer.length.zero?
348
+ else
349
+ # for enum bodies
350
+ fast_write_str socket, io_buffer.read_and_reset
351
+ if chunked
352
+ body.each do |part|
353
+ next if (byte_size = part.bytesize).zero?
354
+ fast_write_str socket, (byte_size.to_s(16) << LINE_END)
355
+ fast_write_str socket, part
356
+ fast_write_str socket, LINE_END
357
+ end
358
+ fast_write_str socket, CLOSE_CHUNKED
359
+ else
360
+ body.each do |part|
361
+ next if part.bytesize.zero?
362
+ fast_write_str socket, part
363
+ end
364
+ end
326
365
  end
366
+ socket.flush
327
367
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
328
368
  raise ConnectionError, SOCKET_WRITE_ERR_MSG
329
369
  rescue Errno::EPIPE, SystemCallError, IOError
@@ -496,12 +536,13 @@ module Puma
496
536
  # @param content_length [Integer,nil] content length if it can be determined from the
497
537
  # response body
498
538
  # @param io_buffer [Puma::IOBuffer] modified inn place
499
- # @param requests [Integer] number of inline requests handled
500
- # @param client [Puma::Client]
539
+ # @param force_keep_alive [Boolean] 'anded' with keep_alive, based on system
540
+ # status and `@max_fast_inline`
501
541
  # @return [Hash] resp_info
502
542
  # @version 5.0.3
503
543
  #
504
- def str_headers(env, status, headers, res_body, io_buffer, requests, client)
544
+ def str_headers(env, status, headers, res_body, io_buffer, force_keep_alive)
545
+
505
546
  line_ending = LINE_END
506
547
  colon = COLON
507
548
 
@@ -544,13 +585,8 @@ module Puma
544
585
  # if running without request queueing
545
586
  resp_info[:keep_alive] &&= @queue_requests
546
587
 
547
- # Close the connection after a reasonable number of inline requests
548
- # if the server is at capacity and the listener has a new connection ready.
549
- # This allows Puma to service connections fairly when the number
550
- # of concurrent connections exceeds the size of the threadpool.
551
- resp_info[:keep_alive] &&= requests < @max_fast_inline ||
552
- @thread_pool.busy_threads < @max_threads ||
553
- !client.listener.to_io.wait_readable(0)
588
+ # see prepare_response
589
+ resp_info[:keep_alive] &&= force_keep_alive
554
590
 
555
591
  resp_info[:response_hijack] = nil
556
592
 
@@ -560,7 +596,8 @@ module Puma
560
596
  case k.downcase
561
597
  when CONTENT_LENGTH2
562
598
  next if illegal_header_value?(vs)
563
- resp_info[:content_length] = vs
599
+ # nil.to_i is 0, nil&.to_i is nil
600
+ resp_info[:content_length] = vs&.to_i
564
601
  next
565
602
  when TRANSFER_ENCODING
566
603
  resp_info[:allow_chunked] = false
data/lib/puma/runner.rb CHANGED
@@ -182,6 +182,10 @@ module Puma
182
182
  end
183
183
  end
184
184
 
185
+ def utc_iso8601(val)
186
+ "#{val.utc.strftime '%FT%T'}Z"
187
+ end
188
+
185
189
  def stats
186
190
  {
187
191
  versions: {
data/lib/puma/server.rb CHANGED
@@ -11,7 +11,6 @@ require_relative 'reactor'
11
11
  require_relative 'client'
12
12
  require_relative 'binder'
13
13
  require_relative 'util'
14
- require_relative 'io_buffer'
15
14
  require_relative 'request'
16
15
 
17
16
  require 'socket'
@@ -230,7 +229,7 @@ module Puma
230
229
 
231
230
  @status = :run
232
231
 
233
- @thread_pool = ThreadPool.new(thread_name, @options) { |a, b| process_client a, b }
232
+ @thread_pool = ThreadPool.new(thread_name, @options) { |client| process_client client }
234
233
 
235
234
  if @queue_requests
236
235
  @reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
@@ -401,7 +400,7 @@ module Puma
401
400
  # returning.
402
401
  #
403
402
  # Return true if one or more requests were processed.
404
- def process_client(client, buffer)
403
+ def process_client(client)
405
404
  # Advertise this server into the thread
406
405
  Thread.current[ThreadLocalKey] = self
407
406
 
@@ -427,15 +426,13 @@ module Puma
427
426
 
428
427
  while true
429
428
  @requests_count += 1
430
- case handle_request(client, buffer, requests + 1)
429
+ case handle_request(client, requests + 1)
431
430
  when false
432
431
  break
433
432
  when :async
434
433
  close_socket = false
435
434
  break
436
435
  when true
437
- buffer.reset
438
-
439
436
  ThreadPool.clean_thread_locals if clean_thread_locals
440
437
 
441
438
  requests += 1
@@ -469,7 +466,7 @@ module Puma
469
466
  # The ensure tries to close +client+ down
470
467
  requests > 0
471
468
  ensure
472
- buffer.reset
469
+ client.io_buffer.reset
473
470
 
474
471
  begin
475
472
  client.close if close_socket
data/lib/puma/single.rb CHANGED
@@ -16,7 +16,7 @@ module Puma
16
16
  # @!attribute [r] stats
17
17
  def stats
18
18
  {
19
- started_at: @started_at.utc.iso8601
19
+ started_at: utc_iso8601(@started_at)
20
20
  }.merge(@server.stats).merge(super)
21
21
  end
22
22
 
@@ -45,7 +45,6 @@ module Puma
45
45
  @min = Integer(options[:min_threads])
46
46
  @max = Integer(options[:max_threads])
47
47
  @block = block
48
- @extra = [::Puma::IOBuffer]
49
48
  @out_of_band = options[:out_of_band]
50
49
  @clean_thread_locals = options[:clean_thread_locals]
51
50
  @reaping_time = options[:reaping_time]
@@ -112,8 +111,6 @@ module Puma
112
111
  not_empty = @not_empty
113
112
  not_full = @not_full
114
113
 
115
- extra = @extra.map { |i| i.new }
116
-
117
114
  while true
118
115
  work = nil
119
116
 
@@ -147,7 +144,7 @@ module Puma
147
144
  end
148
145
 
149
146
  begin
150
- @out_of_band_pending = true if block.call(work, *extra)
147
+ @out_of_band_pending = true if block.call(work)
151
148
  rescue Exception => e
152
149
  STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
153
150
  end
data/lib/puma.rb CHANGED
@@ -3,8 +3,6 @@
3
3
  # Standard libraries
4
4
  require 'socket'
5
5
  require 'tempfile'
6
- require 'time'
7
- require 'etc'
8
6
  require 'uri'
9
7
  require 'stringio'
10
8
 
@@ -28,7 +26,7 @@ module Puma
28
26
  # not in minissl.rb
29
27
  HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
30
28
 
31
- HAS_UNIX_SOCKET = Object.const_defined? :UNIXSocket
29
+ HAS_UNIX_SOCKET = Object.const_defined?(:UNIXSocket) && !IS_WINDOWS
32
30
 
33
31
  if HAS_SSL
34
32
  require_relative 'puma/minissl'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.0.2
5
5
  platform: java
6
6
  authors:
7
7
  - Evan Phoenix
@@ -144,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
144
  - !ruby/object:Gem::Version
145
145
  version: '0'
146
146
  requirements: []
147
- rubygems_version: 3.2.29
147
+ rubygems_version: 3.2.33
148
148
  signing_key:
149
149
  specification_version: 4
150
150
  summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for