puma 6.0.0 → 6.1.0

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: c8a1a6014d9e1130e8ba06d5f74d472216448df3dbce4c15c4c24c91fbfe5e30
4
- data.tar.gz: f1be3485e37cd230f6c35854aeb959ffeab1dac0162bf482799a4755222bc498
3
+ metadata.gz: 1df130ccb43143b23de48c5dc2f0890165e078eba4f09432b0a73411b5b54a94
4
+ data.tar.gz: e562fc40625877488fd13719e9620f2401394a33a74bee46e5a4731becfab6ae
5
5
  SHA512:
6
- metadata.gz: bbdd201a97e5dffccbbb8d8f336693746b87763313422a94ac21f73641ca712b9d2461051cc0bd02771b51af606ce595c2dd09bff0c716146cf9ac56e7ae51f4
7
- data.tar.gz: e378848b22d1139559edd6736a9164a5cd98064c6232f0b2cc108122f79c93429ec33b156baa17e6ff82e9b3c77dbddbd22b2a0525a16f749129cf6f70c006da
6
+ metadata.gz: e436db46a9761b73dc370a38e1e59a0e2ac1e385ecf578fc631fc4671329b9cbcfd634e9bed73e17657c13eaf53387a55726d9c986739f6f9ccdb4223fea026e
7
+ data.tar.gz: db4526766e567440a95129831ef30cea038a98e9d613dc76308c96d0d81913d4e15fbb9c1c8d111ce395af0d908268316cb65c04511dbfbd8361fab31a62634a
data/History.md CHANGED
@@ -1,4 +1,35 @@
1
- ## 6.0.0 / 2022-10-XX
1
+ ## 6.1.0 / 2022-02-12
2
+
3
+ * Features
4
+ * WebSocket support via partial hijack ([#3058], [#3007])
5
+ * Add built-in systemd notify support ([#3011])
6
+ * Periodically send status to systemd ([#3006], [#2604])
7
+ * Introduce the ability to return 413: payload too large for requests ([#3040])
8
+ * Log loaded extensions when `PUMA_DEBUG` is set ([#3036], [#3020])
9
+
10
+ * Bugfixes
11
+ * Fix issue with rack 3 compatibility re: rackup ([#3061], [#3057])
12
+ * Allow setting TCP low_latency with SSL listener ([#3065])
13
+
14
+ * Performance
15
+ * Reduce memory usage for large file uploads ([#3062])
16
+
17
+ ## 6.0.2 / 2023-01-01
18
+
19
+ * Refactor
20
+ * Remove use of etc and time gems in Puma ([#3035], [#3033])
21
+ * Refactor const.rb - freeze ([#3016])
22
+
23
+ ## 6.0.1 / 2022-12-20
24
+
25
+ * Bugfixes
26
+ * Handle waking up a closed selector in Reactor#add ([#3005])
27
+ * Fixup response processing, enumerable bodies ([#3004], [#3000])
28
+ * Correctly close app body for all code paths ([#3002], [#2999])
29
+ * Refactor
30
+ * Add IOBuffer to Client, remove from ThreadPool thread instances ([#3013])
31
+
32
+ ## 6.0.0 / 2022-10-14
2
33
 
3
34
  * Breaking Changes
4
35
  * Dropping Ruby 2.2 and 2.3 support (now 2.4+) ([#2919])
@@ -9,6 +40,9 @@
9
40
  * Prefix all environment variables with `PUMA_` ([#2924], [#2853])
10
41
  * Removed some constants ([#2957], [#2958], [#2959], [#2960])
11
42
  * The following classes are now part of Puma's private API: `Client`, `Cluster::Worker`, `Cluster::Worker`, `HandleRequest`. ([#2988])
43
+ * Configuration constants like `DefaultRackup` removed ([#2928])
44
+ * Extracted `LogWriter` from `Events` ([#2798])
45
+ * Only accept the standard 8 HTTP methods, others rejected with 501. ([#2932])
12
46
 
13
47
  * Features
14
48
  * Increase throughput on large (100kb+) response bodies by 3-10x ([#2896], [#2892])
@@ -34,7 +68,6 @@
34
68
 
35
69
  * Refactor
36
70
  * log_writer.rb - add internal_write method ([#2888])
37
- * [WIP] Refactor: Split out LogWriter from Events (no logic change) ([#2798])
38
71
  * Extract prune_bundler code into it's own class. ([#2797])
39
72
  * Refactor Launcher#run to increase readability (no logic change) ([#2795])
40
73
  * Ruby 3.2 will have native IO#wait_* methods, don't require io/wait ([#2903])
@@ -1915,6 +1948,27 @@ be added back in a future date when a java Puma::MiniSSL is added.
1915
1948
  * Bugfixes
1916
1949
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
1917
1950
 
1951
+ [#3058]:https://github.com/puma/puma/pull/3058 "PR by @dentarg, merged 2023-01-29"
1952
+ [#3007]:https://github.com/puma/puma/issues/3007 "Issue by @MSP-Greg, closed 2023-01-29"
1953
+ [#3011]:https://github.com/puma/puma/pull/3011 "PR by @joaomarcos96, merged 2023-01-03"
1954
+ [#3006]:https://github.com/puma/puma/pull/3006 "PR by @QWYNG, merged 2023-02-09"
1955
+ [#2604]:https://github.com/puma/puma/issues/2604 "Issue by @dgoetz, closed 2023-02-09"
1956
+ [#3040]:https://github.com/puma/puma/pull/3040 "PR by @shayonj, merged 2023-01-02"
1957
+ [#3036]:https://github.com/puma/puma/pull/3036 "PR by @MSP-Greg, merged 2023-01-13"
1958
+ [#3020]:https://github.com/puma/puma/issues/3020 "Issue by @dentarg, closed 2023-01-13"
1959
+ [#3061]:https://github.com/puma/puma/pull/3061 "PR by @MSP-Greg, merged 2023-02-12"
1960
+ [#3057]:https://github.com/puma/puma/issues/3057 "Issue by @mmarvb8h, closed 2023-02-12"
1961
+ [#3065]:https://github.com/puma/puma/pull/3065 "PR by @MSP-Greg, merged 2023-02-11"
1962
+ [#3062]:https://github.com/puma/puma/pull/3062 "PR by @willkoehler, merged 2023-01-29"
1963
+ [#3035]:https://github.com/puma/puma/pull/3035 "PR by @MSP-Greg, merged 2022-12-24"
1964
+ [#3033]:https://github.com/puma/puma/issues/3033 "Issue by @jules-w2, closed 2022-12-24"
1965
+ [#3016]:https://github.com/puma/puma/pull/3016 "PR by @MSP-Greg, merged 2022-12-24"
1966
+ [#3005]:https://github.com/puma/puma/pull/3005 "PR by @JuanitoFatas, merged 2022-11-04"
1967
+ [#3004]:https://github.com/puma/puma/pull/3004 "PR by @MSP-Greg, merged 2022-11-24"
1968
+ [#3000]:https://github.com/puma/puma/issues/3000 "Issue by @dentarg, closed 2022-11-24"
1969
+ [#3002]:https://github.com/puma/puma/pull/3002 "PR by @MSP-Greg, merged 2022-11-03"
1970
+ [#2999]:https://github.com/puma/puma/issues/2999 "Issue by @aymeric-ledorze, closed 2022-11-03"
1971
+ [#3013]:https://github.com/puma/puma/pull/3013 "PR by @MSP-Greg, merged 2022-11-13"
1918
1972
  [#2919]:https://github.com/puma/puma/pull/2919 "PR by @MSP-Greg, merged 2022-08-30"
1919
1973
  [#2652]:https://github.com/puma/puma/issues/2652 "Issue by @Roguelazer, closed 2022-09-04"
1920
1974
  [#2653]:https://github.com/puma/puma/pull/2653 "PR by @Roguelazer, closed 2022-03-07"
@@ -1928,7 +1982,10 @@ be added back in a future date when a java Puma::MiniSSL is added.
1928
1982
  [#2958]:https://github.com/puma/puma/pull/2958 "PR by @JuanitoFatas, merged 2022-09-16"
1929
1983
  [#2959]:https://github.com/puma/puma/pull/2959 "PR by @JuanitoFatas, merged 2022-09-16"
1930
1984
  [#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"
1985
+ [#2988]:https://github.com/puma/puma/pull/2988 "PR by @MSP-Greg, merged 2022-10-12"
1986
+ [#2928]:https://github.com/puma/puma/pull/2928 "PR by @nateberkopec, merged 2022-09-10"
1987
+ [#2798]:https://github.com/puma/puma/pull/2798 "PR by @johnnyshields, merged 2022-02-05"
1988
+ [#2932]:https://github.com/puma/puma/pull/2932 "PR by @mrzasa, merged 2022-09-12"
1932
1989
  [#2896]:https://github.com/puma/puma/pull/2896 "PR by @MSP-Greg, merged 2022-09-13"
1933
1990
  [#2892]:https://github.com/puma/puma/pull/2892 "PR by @guilleiguaran, closed 2022-09-13"
1934
1991
  [#2923]:https://github.com/puma/puma/pull/2923 "PR by @nateberkopec, merged 2022-09-09"
@@ -1949,11 +2006,9 @@ be added back in a future date when a java Puma::MiniSSL is added.
1949
2006
  [#2904]:https://github.com/puma/puma/pull/2904 "PR by @kares, merged 2022-08-27"
1950
2007
  [#2884]:https://github.com/puma/puma/pull/2884 "PR by @kares, merged 2022-05-30"
1951
2008
  [#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
2009
  [#1441]:https://github.com/puma/puma/issues/1441 "Issue by @nirvdrum, closed 2022-09-12"
1954
2010
  [#2956]:https://github.com/puma/puma/pull/2956 "PR by @MSP-Greg, merged 2022-09-15"
1955
2011
  [#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
2012
  [#2797]:https://github.com/puma/puma/pull/2797 "PR by @johnnyshields, merged 2022-02-01"
1958
2013
  [#2795]:https://github.com/puma/puma/pull/2795 "PR by @johnnyshields, merged 2022-01-31"
1959
2014
  [#2903]:https://github.com/puma/puma/pull/2903 "PR by @MSP-Greg, merged 2022-08-27"
data/README.md CHANGED
@@ -365,6 +365,7 @@ Community guides:
365
365
  * [puma-metrics](https://github.com/harmjanblok/puma-metrics) — export Puma metrics to Prometheus
366
366
  * [puma-plugin-statsd](https://github.com/yob/puma-plugin-statsd) — send Puma metrics to statsd
367
367
  * [puma-plugin-systemd](https://github.com/sj26/puma-plugin-systemd) — deeper integration with systemd for notify, status and watchdog
368
+ * [puma-plugin-telemetry](https://github.com/babbel/puma-plugin-telemetry) - telemetry plugin for Puma offering various targets to publish
368
369
 
369
370
  ### Monitoring
370
371
 
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/docs/systemd.md CHANGED
@@ -24,8 +24,7 @@ After=network.target
24
24
 
25
25
  [Service]
26
26
  # Puma supports systemd's `Type=notify` and watchdog service
27
- # monitoring, if the [sd_notify](https://github.com/agis/ruby-sdnotify) gem is installed,
28
- # as of Puma 5.1 or later.
27
+ # monitoring, as of Puma 5.1 or later.
29
28
  # On earlier versions of Puma or JRuby, change this to `Type=simple` and remove
30
29
  # the `WatchdogSec` line.
31
30
  Type=notify
data/lib/puma/binder.rb CHANGED
@@ -158,10 +158,10 @@ module Puma
158
158
  ios_len = @ios.length
159
159
  params = Util.parse_query uri.query
160
160
 
161
- opt = params.key?('low_latency') && params['low_latency'] != 'false'
161
+ low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
162
162
  backlog = params.fetch('backlog', 1024).to_i
163
163
 
164
- io = add_tcp_listener uri.host, uri.port, opt, backlog
164
+ io = add_tcp_listener uri.host, uri.port, low_latency, backlog
165
165
 
166
166
  @ios[ios_len..-1].each do |i|
167
167
  addr = loc_addr_str i
@@ -251,7 +251,8 @@ module Puma
251
251
  else
252
252
  ios_len = @ios.length
253
253
  backlog = params.fetch('backlog', 1024).to_i
254
- io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
254
+ low_latency = params['low_latency'] != 'false'
255
+ io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
255
256
 
256
257
  @ios[ios_len..-1].each do |i|
257
258
  addr = loc_addr_str i
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
 
@@ -84,6 +86,9 @@ module Puma
84
86
  @requests_served = 0
85
87
  @hijacked = false
86
88
 
89
+ @http_content_length_limit = nil
90
+ @http_content_length_limit_exceeded = false
91
+
87
92
  @peerip = nil
88
93
  @peer_family = nil
89
94
  @listener = nil
@@ -93,12 +98,14 @@ module Puma
93
98
  @body_remain = 0
94
99
 
95
100
  @in_last_chunk = false
101
+
102
+ @read_buffer = +""
96
103
  end
97
104
 
98
105
  attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
99
- :tempfile
106
+ :tempfile, :io_buffer, :http_content_length_limit_exceeded
100
107
 
101
- attr_writer :peerip
108
+ attr_writer :peerip, :http_content_length_limit
102
109
 
103
110
  attr_accessor :remote_addr_header, :listener
104
111
 
@@ -138,6 +145,7 @@ module Puma
138
145
 
139
146
  def reset(fast_check=true)
140
147
  @parser.reset
148
+ @io_buffer.reset
141
149
  @read_header = true
142
150
  @read_proxy = !!@expect_proxy_proto
143
151
  @env = @proto_env.dup
@@ -148,6 +156,7 @@ module Puma
148
156
  @body_remain = 0
149
157
  @peerip = nil if @remote_addr_header
150
158
  @in_last_chunk = false
159
+ @http_content_length_limit_exceeded = false
151
160
 
152
161
  if @buffer
153
162
  return false unless try_to_parse_proxy_protocol
@@ -207,6 +216,17 @@ module Puma
207
216
  end
208
217
 
209
218
  def try_to_finish
219
+ if env[CONTENT_LENGTH] && above_http_content_limit(env[CONTENT_LENGTH].to_i)
220
+ @http_content_length_limit_exceeded = true
221
+ end
222
+
223
+ if @http_content_length_limit_exceeded
224
+ @buffer = nil
225
+ @body = EmptyBody
226
+ set_ready
227
+ return true
228
+ end
229
+
210
230
  return read_body if in_data_phase
211
231
 
212
232
  begin
@@ -236,6 +256,10 @@ module Puma
236
256
 
237
257
  @parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
238
258
 
259
+ if @parser.finished? && above_http_content_limit(@parser.body.bytesize)
260
+ @http_content_length_limit_exceeded = true
261
+ end
262
+
239
263
  if @parser.finished?
240
264
  return setup_body
241
265
  elsif @parsed_bytes >= MAX_HEADER
@@ -411,7 +435,7 @@ module Puma
411
435
  end
412
436
 
413
437
  begin
414
- chunk = @io.read_nonblock(want)
438
+ chunk = @io.read_nonblock(want, @read_buffer)
415
439
  rescue IO::WaitReadable
416
440
  return false
417
441
  rescue SystemCallError, IOError
@@ -443,7 +467,7 @@ module Puma
443
467
  def read_chunked_body
444
468
  while true
445
469
  begin
446
- chunk = @io.read_nonblock(4096)
470
+ chunk = @io.read_nonblock(4096, @read_buffer)
447
471
  rescue IO::WaitReadable
448
472
  return false
449
473
  rescue SystemCallError, IOError
@@ -591,5 +615,9 @@ module Puma
591
615
  @requests_served += 1
592
616
  @ready = true
593
617
  end
618
+
619
+ def above_http_content_limit(value)
620
+ @http_content_length_limit&.< value
621
+ end
594
622
  end
595
623
  end
@@ -115,6 +115,11 @@ module Puma
115
115
 
116
116
  while restart_server.pop
117
117
  server_thread = server.run
118
+
119
+ if @log_writer.debug? && index == 0
120
+ debug_loaded_extensions "Loaded Extensions - worker 0:"
121
+ end
122
+
118
123
  stat_thread ||= Thread.new(@worker_write) do |io|
119
124
  Puma.set_thread_name "stat pld"
120
125
  base_payload = "p#{Process.pid}"
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] },
@@ -469,6 +467,7 @@ module Puma
469
467
  @events.fire(:ping!, w)
470
468
  if !booted && @workers.none? {|worker| worker.last_status.empty?}
471
469
  @events.fire_on_booted!
470
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
472
471
  booted = true
473
472
  end
474
473
  end
@@ -478,6 +477,7 @@ module Puma
478
477
  end
479
478
  if in_phased_restart && workers_not_booted.zero?
480
479
  @events.fire_on_booted!
480
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
481
481
  in_phased_restart = false
482
482
  end
483
483
 
@@ -167,6 +167,7 @@ module Puma
167
167
  worker_shutdown_timeout: 30,
168
168
  worker_timeout: 60,
169
169
  workers: 0,
170
+ http_content_length_limit: nil
170
171
  }
171
172
 
172
173
  def initialize(user_options={}, default_options = {}, &block)
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.1.0"
103
+ CODE_NAME = "The Way Up"
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
@@ -65,6 +65,7 @@ module Puma
65
65
 
66
66
  ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
67
67
 
68
+ low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
68
69
  backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
69
70
 
70
71
  if defined?(JRUBY_VERSION)
@@ -114,7 +115,7 @@ module Puma
114
115
  end
115
116
 
116
117
  "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
117
- "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
118
+ "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
118
119
  end
119
120
  end
120
121
 
@@ -250,6 +251,7 @@ module Puma
250
251
  #
251
252
  # * Set the socket backlog depth with +backlog+, default is 1024.
252
253
  # * Set up an SSL certificate with +key+ & +cert+.
254
+ # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
253
255
  # * Set whether to optimize for low latency instead of throughput with
254
256
  # +low_latency+, default is to not optimize for low latency. This is done
255
257
  # via +Socket::TCP_NODELAY+.
@@ -259,6 +261,8 @@ module Puma
259
261
  # bind 'unix:///var/run/puma.sock?backlog=512'
260
262
  # @example SSL cert
261
263
  # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
264
+ # @example SSL cert for mutual TLS (mTLS)
265
+ # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
262
266
  # @example Disable optimization for low latency
263
267
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
264
268
  # @example Socket permissions
@@ -1019,6 +1023,19 @@ module Puma
1019
1023
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
1020
1024
  end
1021
1025
 
1026
+ # Specify how big the request payload should be, in bytes.
1027
+ # This limit is compared against Content-Length HTTP header.
1028
+ # If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
1029
+ # HTTP 413 status code is returned.
1030
+ #
1031
+ # When no Content-Length http header is present, it is compared against the
1032
+ # size of the body of the request.
1033
+ #
1034
+ # The default value for http_content_length_limit is nil.
1035
+ def http_content_length_limit(limit)
1036
+ @options[:http_content_length_limit] = limit
1037
+ end
1038
+
1022
1039
  private
1023
1040
 
1024
1041
  # To avoid adding cert_pem and key_pem as URI params, we store them on the
@@ -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