puma 3.11.4 → 6.0.1

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.

Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1717 -432
  3. data/LICENSE +23 -20
  4. data/README.md +190 -64
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +69 -58
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  17. data/docs/kubernetes.md +66 -0
  18. data/docs/nginx.md +2 -2
  19. data/docs/plugins.md +22 -12
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +47 -22
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +95 -120
  25. data/docs/testing_benchmarks_local_files.md +150 -0
  26. data/docs/testing_test_rackup_ci_files.md +36 -0
  27. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  28. data/ext/puma_http11/ext_help.h +1 -1
  29. data/ext/puma_http11/extconf.rb +61 -3
  30. data/ext/puma_http11/http11_parser.c +106 -118
  31. data/ext/puma_http11/http11_parser.h +2 -2
  32. data/ext/puma_http11/http11_parser.java.rl +22 -38
  33. data/ext/puma_http11/http11_parser.rl +6 -4
  34. data/ext/puma_http11/http11_parser_common.rl +6 -6
  35. data/ext/puma_http11/mini_ssl.c +376 -93
  36. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  37. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  38. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  39. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +250 -88
  40. data/ext/puma_http11/puma_http11.c +49 -57
  41. data/lib/puma/app/status.rb +71 -49
  42. data/lib/puma/binder.rb +243 -148
  43. data/lib/puma/cli.rb +50 -36
  44. data/lib/puma/client.rb +373 -233
  45. data/lib/puma/cluster/worker.rb +175 -0
  46. data/lib/puma/cluster/worker_handle.rb +97 -0
  47. data/lib/puma/cluster.rb +268 -235
  48. data/lib/puma/commonlogger.rb +4 -2
  49. data/lib/puma/configuration.rb +116 -88
  50. data/lib/puma/const.rb +49 -30
  51. data/lib/puma/control_cli.rb +123 -76
  52. data/lib/puma/detect.rb +33 -2
  53. data/lib/puma/dsl.rb +685 -135
  54. data/lib/puma/error_logger.rb +112 -0
  55. data/lib/puma/events.rb +17 -111
  56. data/lib/puma/io_buffer.rb +44 -5
  57. data/lib/puma/jruby_restart.rb +4 -59
  58. data/lib/puma/json_serialization.rb +96 -0
  59. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  60. data/lib/puma/launcher.rb +196 -130
  61. data/lib/puma/log_writer.rb +137 -0
  62. data/lib/puma/minissl/context_builder.rb +92 -0
  63. data/lib/puma/minissl.rb +249 -69
  64. data/lib/puma/null_io.rb +20 -1
  65. data/lib/puma/plugin/tmp_restart.rb +3 -1
  66. data/lib/puma/plugin.rb +9 -13
  67. data/lib/puma/rack/builder.rb +8 -9
  68. data/lib/puma/rack/urlmap.rb +2 -0
  69. data/lib/puma/rack_default.rb +3 -1
  70. data/lib/puma/reactor.rb +90 -187
  71. data/lib/puma/request.rb +644 -0
  72. data/lib/puma/runner.rb +94 -71
  73. data/lib/puma/server.rb +337 -715
  74. data/lib/puma/single.rb +27 -72
  75. data/lib/puma/state_file.rb +46 -7
  76. data/lib/puma/systemd.rb +47 -0
  77. data/lib/puma/thread_pool.rb +184 -93
  78. data/lib/puma/util.rb +23 -10
  79. data/lib/puma.rb +60 -3
  80. data/lib/rack/handler/puma.rb +17 -15
  81. data/tools/Dockerfile +16 -0
  82. data/tools/trickletest.rb +0 -1
  83. metadata +53 -33
  84. data/ext/puma_http11/io_buffer.c +0 -155
  85. data/lib/puma/accept_nonblock.rb +0 -23
  86. data/lib/puma/compat.rb +0 -14
  87. data/lib/puma/convenient.rb +0 -23
  88. data/lib/puma/daemon_ext.rb +0 -31
  89. data/lib/puma/delegation.rb +0 -11
  90. data/lib/puma/java_io_buffer.rb +0 -45
  91. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  92. data/lib/puma/tcp_logger.rb +0 -39
  93. data/tools/jungle/README.md +0 -19
  94. data/tools/jungle/init.d/README.md +0 -61
  95. data/tools/jungle/init.d/puma +0 -421
  96. data/tools/jungle/init.d/run-puma +0 -18
  97. data/tools/jungle/upstart/README.md +0 -61
  98. data/tools/jungle/upstart/puma-manager.conf +0 -31
  99. data/tools/jungle/upstart/puma.conf +0 -69
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  # Rack::CommonLogger forwards every request to the given +app+, and
3
5
  # logs a line in the
4
- # {Apache common log format}[http://httpd.apache.org/docs/1.3/logs.html#common]
6
+ # {Apache common log format}[https://httpd.apache.org/docs/1.3/logs.html#common]
5
7
  # to the +logger+.
6
8
  #
7
9
  # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
@@ -14,7 +16,7 @@ module Puma
14
16
  # (which is called without arguments in order to make the error appear for
15
17
  # sure)
16
18
  class CommonLogger
17
- # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
19
+ # Common Log Format: https://httpd.apache.org/docs/1.3/logs.html#common
18
20
  #
19
21
  # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
20
22
  #
@@ -1,24 +1,17 @@
1
- require 'puma/rack/builder'
2
- require 'puma/plugin'
3
- require 'puma/const'
1
+ # frozen_string_literal: true
4
2
 
5
- module Puma
6
-
7
- module ConfigDefault
8
- DefaultRackup = "config.ru"
9
-
10
- DefaultTCPHost = "0.0.0.0"
11
- DefaultTCPPort = 9292
12
- DefaultWorkerTimeout = 60
13
- DefaultWorkerShutdownTimeout = 30
14
- end
3
+ require_relative 'rack/builder'
4
+ require_relative 'plugin'
5
+ require_relative 'const'
6
+ # note that dsl is loaded at end of file, requires ConfigDefault constants
15
7
 
8
+ module Puma
16
9
  # A class used for storing "leveled" configuration options.
17
10
  #
18
11
  # In this class any "user" specified options take precedence over any
19
12
  # "file" specified options, take precedence over any "default" options.
20
13
  #
21
- # User input is prefered over "defaults":
14
+ # User input is preferred over "defaults":
22
15
  # user_options = { foo: "bar" }
23
16
  # default_options = { foo: "zoo" }
24
17
  # options = UserFileDefaultOptions.new(user_options, default_options)
@@ -30,7 +23,7 @@ module Puma
30
23
  # puts options.all_of(:foo)
31
24
  # # => ["bar", "zoo"]
32
25
  #
33
- # A "file" option can be set. This config will be prefered over "default" options
26
+ # A "file" option can be set. This config will be preferred over "default" options
34
27
  # but will defer to any available "user" specified options.
35
28
  #
36
29
  # user_options = { foo: "bar" }
@@ -52,9 +45,7 @@ module Puma
52
45
  attr_reader :user_options, :file_options, :default_options
53
46
 
54
47
  def [](key)
55
- return user_options[key] if user_options.key?(key)
56
- return file_options[key] if file_options.key?(key)
57
- return default_options[key] if default_options.key?(key)
48
+ fetch(key)
58
49
  end
59
50
 
60
51
  def []=(key, value)
@@ -62,7 +53,11 @@ module Puma
62
53
  end
63
54
 
64
55
  def fetch(key, default_value = nil)
65
- self[key] || default_value
56
+ return user_options[key] if user_options.key?(key)
57
+ return file_options[key] if file_options.key?(key)
58
+ return default_options[key] if default_options.key?(key)
59
+
60
+ default_value
66
61
  end
67
62
 
68
63
  def all_of(key)
@@ -88,6 +83,12 @@ module Puma
88
83
  end
89
84
  end
90
85
  end
86
+
87
+ def final_options
88
+ default_options
89
+ .merge(file_options)
90
+ .merge(user_options)
91
+ end
91
92
  end
92
93
 
93
94
  # The main configuration class of Puma.
@@ -104,16 +105,17 @@ module Puma
104
105
  #
105
106
  # It also handles loading plugins.
106
107
  #
107
- # > Note: `:port` and `:host` are not valid keys. By they time they make it to the
108
+ # [Note:]
109
+ # `:port` and `:host` are not valid keys. By the time they make it to the
108
110
  # configuration options they are expected to be incorporated into a `:binds` key.
109
111
  # Under the hood the DSL maps `port` and `host` calls to `:binds`
110
112
  #
111
- # config = Configuration.new({}) do |user_config, file_config, default_config|
112
- # user_config.port 3003
113
- # end
114
- # config.load
115
- # puts config.options[:port]
116
- # # => 3003
113
+ # config = Configuration.new({}) do |user_config, file_config, default_config|
114
+ # user_config.port 3003
115
+ # end
116
+ # config.load
117
+ # puts config.options[:port]
118
+ # # => 3003
117
119
  #
118
120
  # It is expected that `load` is called on the configuration instance after setting
119
121
  # config. This method expands any values in `config_file` and puts them into the
@@ -124,7 +126,48 @@ module Puma
124
126
  # is done because an environment variable may have been modified while loading
125
127
  # configuration files.
126
128
  class Configuration
127
- include ConfigDefault
129
+ DEFAULTS = {
130
+ auto_trim_time: 30,
131
+ binds: ['tcp://0.0.0.0:9292'.freeze],
132
+ clean_thread_locals: false,
133
+ debug: false,
134
+ early_hints: nil,
135
+ environment: 'development'.freeze,
136
+ # Number of seconds to wait until we get the first data for the request
137
+ first_data_timeout: 30,
138
+ io_selector_backend: :auto,
139
+ log_requests: false,
140
+ logger: STDOUT,
141
+ # How many requests to attempt inline before sending a client back to
142
+ # the reactor to be subject to normal ordering. The idea here is that
143
+ # we amortize the cost of going back to the reactor for a well behaved
144
+ # but very "greedy" client across 10 requests. This prevents a not
145
+ # well behaved client from monopolizing the thread forever.
146
+ max_fast_inline: 10,
147
+ max_threads: Puma.mri? ? 5 : 16,
148
+ min_threads: 0,
149
+ mode: :http,
150
+ mutate_stdout_and_stderr_to_sync_on_write: true,
151
+ out_of_band: [],
152
+ # Number of seconds for another request within a persistent session.
153
+ persistent_timeout: 20,
154
+ queue_requests: true,
155
+ rackup: 'config.ru'.freeze,
156
+ raise_exception_on_sigterm: true,
157
+ reaping_time: 1,
158
+ remote_address: :socket,
159
+ silence_single_worker_warning: false,
160
+ tag: File.basename(Dir.getwd),
161
+ tcp_host: '0.0.0.0'.freeze,
162
+ tcp_port: 9292,
163
+ wait_for_less_busy_worker: 0.005,
164
+ worker_boot_timeout: 60,
165
+ worker_check_interval: 5,
166
+ worker_culling_strategy: :youngest,
167
+ worker_shutdown_timeout: 30,
168
+ worker_timeout: 60,
169
+ workers: 0,
170
+ }
128
171
 
129
172
  def initialize(user_options={}, default_options = {}, &block)
130
173
  default_options = self.puma_default_options.merge(default_options)
@@ -135,6 +178,10 @@ module Puma
135
178
  @file_dsl = DSL.new(@options.file_options, self)
136
179
  @default_dsl = DSL.new(@options.default_options, self)
137
180
 
181
+ if !@options[:prune_bundler]
182
+ default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
183
+ end
184
+
138
185
  if block
139
186
  configure(&block)
140
187
  end
@@ -166,25 +213,21 @@ module Puma
166
213
  end
167
214
 
168
215
  def puma_default_options
216
+ defaults = DEFAULTS.dup
217
+ puma_options_from_env.each { |k,v| defaults[k] = v if v }
218
+ defaults
219
+ end
220
+
221
+ def puma_options_from_env
222
+ min = ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS']
223
+ max = ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS']
224
+ workers = ENV['WEB_CONCURRENCY']
225
+
169
226
  {
170
- :min_threads => 0,
171
- :max_threads => 16,
172
- :log_requests => false,
173
- :debug => false,
174
- :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
175
- :workers => 0,
176
- :daemon => false,
177
- :mode => :http,
178
- :worker_timeout => DefaultWorkerTimeout,
179
- :worker_boot_timeout => DefaultWorkerTimeout,
180
- :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
181
- :remote_address => :socket,
182
- :tag => method(:infer_tag),
183
- :environment => -> { ENV['RACK_ENV'] || "development" },
184
- :rackup => DefaultRackup,
185
- :logger => STDOUT,
186
- :persistent_timeout => Const::PERSISTENT_TIMEOUT,
187
- :first_data_timeout => Const::FIRST_DATA_TIMEOUT
227
+ min_threads: min && Integer(min),
228
+ max_threads: max && Integer(max),
229
+ workers: workers && Integer(workers),
230
+ environment: ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'],
188
231
  }
189
232
  end
190
233
 
@@ -200,7 +243,7 @@ module Puma
200
243
  return [] if files == ['-']
201
244
  return files if files.any?
202
245
 
203
- first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
246
+ first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
204
247
  File.exist?(f)
205
248
  end
206
249
 
@@ -242,16 +285,8 @@ module Puma
242
285
  def app
243
286
  found = options[:app] || load_rackup
244
287
 
245
- if @options[:mode] == :tcp
246
- require 'puma/tcp_logger'
247
-
248
- logger = @options[:logger]
249
- quiet = !@options[:log_requests]
250
- return TCPLogger.new(logger, found, quiet)
251
- end
252
-
253
288
  if @options[:log_requests]
254
- require 'puma/commonlogger'
289
+ require_relative 'commonlogger'
255
290
  logger = @options[:logger]
256
291
  found = CommonLogger.new(found, logger)
257
292
  end
@@ -264,16 +299,31 @@ module Puma
264
299
  @options[:environment]
265
300
  end
266
301
 
267
- def environment_str
268
- environment.respond_to?(:call) ? environment.call : environment
269
- end
270
-
271
302
  def load_plugin(name)
272
303
  @plugins.create name
273
304
  end
274
305
 
275
- def run_hooks(key, arg)
276
- @options.all_of(key).each { |b| b.call arg }
306
+ # @param key [:Symbol] hook to run
307
+ # @param arg [Launcher, Int] `:on_restart` passes Launcher
308
+ #
309
+ def run_hooks(key, arg, log_writer, hook_data = nil)
310
+ @options.all_of(key).each do |b|
311
+ begin
312
+ if Array === b
313
+ hook_data[b[1]] ||= Hash.new
314
+ b[0].call arg, hook_data[b[1]]
315
+ else
316
+ b.call arg
317
+ end
318
+ rescue => e
319
+ log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
320
+ log_writer.debug e.backtrace.join("\n")
321
+ end
322
+ end
323
+ end
324
+
325
+ def final_options
326
+ @options.final_options
277
327
  end
278
328
 
279
329
  def self.temp_path
@@ -285,10 +335,6 @@ module Puma
285
335
 
286
336
  private
287
337
 
288
- def infer_tag
289
- File.basename(Dir.getwd)
290
- end
291
-
292
338
  # Load and use the normal Rack builder if we can, otherwise
293
339
  # fallback to our minimal version.
294
340
  def rack_builder
@@ -316,6 +362,8 @@ module Puma
316
362
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
317
363
 
318
364
  rack_app, rack_options = rack_builder.parse_file(rackup)
365
+ rack_options = rack_options || {}
366
+
319
367
  @options.file_options.merge!(rack_options)
320
368
 
321
369
  config_ru_binds = []
@@ -329,31 +377,11 @@ module Puma
329
377
  end
330
378
 
331
379
  def self.random_token
332
- begin
333
- require 'openssl'
334
- rescue LoadError
335
- end
336
-
337
- count = 16
338
-
339
- bytes = nil
340
-
341
- if defined? OpenSSL::Random
342
- bytes = OpenSSL::Random.random_bytes(count)
343
- elsif File.exist?("/dev/urandom")
344
- File.open('/dev/urandom') { |f| bytes = f.read(count) }
345
- end
346
-
347
- if bytes
348
- token = "".dup
349
- bytes.each_byte { |b| token << b.to_s(16) }
350
- else
351
- token = (0..count).to_a.map { rand(255).to_s(16) }.join
352
- end
380
+ require 'securerandom' unless defined?(SecureRandom)
353
381
 
354
- return token
382
+ SecureRandom.hex(16)
355
383
  end
356
384
  end
357
385
  end
358
386
 
359
- require 'puma/dsl'
387
+ require_relative 'dsl'
data/lib/puma/const.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #encoding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  module Puma
3
5
  class UnsupportedOption < RuntimeError
4
6
  end
@@ -74,7 +76,7 @@ module Puma
74
76
  508 => 'Loop Detected',
75
77
  510 => 'Not Extended',
76
78
  511 => 'Network Authentication Required'
77
- }
79
+ }.freeze
78
80
 
79
81
  # For some HTTP status codes the client only expects headers.
80
82
  #
@@ -83,7 +85,7 @@ module Puma
83
85
  204 => true,
84
86
  205 => true,
85
87
  304 => true
86
- }
88
+ }.freeze
87
89
 
88
90
  # Frequently used constants when constructing requests or responses. Many times
89
91
  # the constant just refers to a string with the same contents. Using these constants
@@ -98,20 +100,13 @@ module Puma
98
100
  # too taxing on performance.
99
101
  module Const
100
102
 
101
- PUMA_VERSION = VERSION = "3.11.4".freeze
102
- CODE_NAME = "Love Song".freeze
103
+ PUMA_VERSION = VERSION = "6.0.1".freeze
104
+ CODE_NAME = "Sunflower".freeze
105
+
103
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
104
107
 
105
108
  FAST_TRACK_KA_TIMEOUT = 0.2
106
109
 
107
- # The default number of seconds for another request within a persistent
108
- # session.
109
- PERSISTENT_TIMEOUT = 20
110
-
111
- # The default number of seconds to wait until we get the first data
112
- # for the request
113
- FIRST_DATA_TIMEOUT = 30
114
-
115
110
  # How long to wait when getting some write blocking on the socket when
116
111
  # sending data back
117
112
  WRITE_TIMEOUT = 10
@@ -120,27 +115,26 @@ module Puma
120
115
  REQUEST_URI= 'REQUEST_URI'.freeze
121
116
  REQUEST_PATH = 'REQUEST_PATH'.freeze
122
117
  QUERY_STRING = 'QUERY_STRING'.freeze
118
+ CONTENT_LENGTH = "CONTENT_LENGTH".freeze
123
119
 
124
120
  PATH_INFO = 'PATH_INFO'.freeze
125
121
 
126
122
  PUMA_TMP_BASE = "puma".freeze
127
123
 
128
- # Indicate that we couldn't parse the request
129
- ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n".freeze
130
-
131
- # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
132
- ERROR_404_RESPONSE = "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze
133
-
134
- # The standard empty 408 response for requests that timed out.
135
- ERROR_408_RESPONSE = "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze
136
-
137
- CONTENT_LENGTH = "CONTENT_LENGTH".freeze
138
-
139
- # Indicate that there was an internal error, obviously.
140
- ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze
141
-
142
- # A common header for indicating the server is too busy. Not used yet.
143
- ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
124
+ ERROR_RESPONSE = {
125
+ # Indicate that we couldn't parse the request
126
+ 400 => "HTTP/1.1 400 Bad Request\r\n\r\n".freeze,
127
+ # 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,
129
+ # 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,
131
+ # Indicate that there was an internal error, obviously.
132
+ 500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
133
+ # Incorrect or invalid header value
134
+ 501 => "HTTP/1.1 501 Not Implemented\r\n\r\n".freeze,
135
+ # 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
137
+ }.freeze
144
138
 
145
139
  # The basic max request size we'll try to read.
146
140
  CHUNK_SIZE = 16 * 1024
@@ -154,10 +148,21 @@ module Puma
154
148
 
155
149
  REQUEST_METHOD = "REQUEST_METHOD".freeze
156
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
157
159
  # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
158
160
  LINE_END = "\r\n".freeze
159
161
  REMOTE_ADDR = "REMOTE_ADDR".freeze
160
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
161
166
 
162
167
  SERVER_NAME = "SERVER_NAME".freeze
163
168
  SERVER_PORT = "SERVER_PORT".freeze
@@ -165,8 +170,10 @@ module Puma
165
170
  PORT_80 = "80".freeze
166
171
  PORT_443 = "443".freeze
167
172
  LOCALHOST = "localhost".freeze
168
- LOCALHOST_IP = "127.0.0.1".freeze
169
- LOCALHOST_ADDR = "127.0.0.1:0".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
170
177
 
171
178
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
172
179
  HTTP_11 = "HTTP/1.1".freeze
@@ -225,5 +232,17 @@ module Puma
225
232
  HIJACK_IO = "rack.hijack_io".freeze
226
233
 
227
234
  EARLY_HINTS = "rack.early_hints".freeze
235
+
236
+ # Illegal character in the key or value of response header
237
+ DQUOTE = "\"".freeze
238
+ HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
239
+ ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
240
+ # header values can contain HTAB?
241
+ ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
242
+
243
+ # Banned keys of response header
244
+ BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
245
+
246
+ PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
228
247
  end
229
248
  end