puma 4.3.5 → 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.
- checksums.yaml +4 -4
- data/History.md +1639 -519
- data/LICENSE +23 -20
- data/README.md +130 -42
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +2 -2
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -128
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +56 -11
- data/ext/puma_http11/http11_parser.c +69 -58
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +322 -130
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +52 -52
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +47 -57
- data/lib/puma/app/status.rb +53 -37
- data/lib/puma/binder.rb +232 -119
- data/lib/puma/cli.rb +33 -33
- data/lib/puma/client.rb +197 -101
- data/lib/puma/cluster/worker.rb +175 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +224 -229
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +112 -87
- data/lib/puma/const.rb +30 -25
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +31 -2
- data/lib/puma/dsl.rb +426 -110
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +44 -2
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +170 -148
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +35 -19
- data/lib/puma/minissl.rb +213 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +5 -9
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +644 -0
- data/lib/puma/runner.rb +83 -77
- data/lib/puma/server.rb +303 -773
- data/lib/puma/single.rb +18 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/systemd.rb +47 -0
- data/lib/puma/thread_pool.rb +136 -68
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -5
- data/lib/rack/handler/puma.rb +11 -12
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +36 -28
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/configuration.rb
CHANGED
@@ -1,20 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
6
7
|
|
7
8
|
module Puma
|
8
|
-
|
9
|
-
module ConfigDefault
|
10
|
-
DefaultRackup = "config.ru"
|
11
|
-
|
12
|
-
DefaultTCPHost = "0.0.0.0"
|
13
|
-
DefaultTCPPort = 9292
|
14
|
-
DefaultWorkerTimeout = 60
|
15
|
-
DefaultWorkerShutdownTimeout = 30
|
16
|
-
end
|
17
|
-
|
18
9
|
# A class used for storing "leveled" configuration options.
|
19
10
|
#
|
20
11
|
# In this class any "user" specified options take precedence over any
|
@@ -54,9 +45,7 @@ module Puma
|
|
54
45
|
attr_reader :user_options, :file_options, :default_options
|
55
46
|
|
56
47
|
def [](key)
|
57
|
-
|
58
|
-
return file_options[key] if file_options.key?(key)
|
59
|
-
return default_options[key] if default_options.key?(key)
|
48
|
+
fetch(key)
|
60
49
|
end
|
61
50
|
|
62
51
|
def []=(key, value)
|
@@ -64,7 +53,11 @@ module Puma
|
|
64
53
|
end
|
65
54
|
|
66
55
|
def fetch(key, default_value = nil)
|
67
|
-
|
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
|
68
61
|
end
|
69
62
|
|
70
63
|
def all_of(key)
|
@@ -90,6 +83,12 @@ module Puma
|
|
90
83
|
end
|
91
84
|
end
|
92
85
|
end
|
86
|
+
|
87
|
+
def final_options
|
88
|
+
default_options
|
89
|
+
.merge(file_options)
|
90
|
+
.merge(user_options)
|
91
|
+
end
|
93
92
|
end
|
94
93
|
|
95
94
|
# The main configuration class of Puma.
|
@@ -106,16 +105,17 @@ module Puma
|
|
106
105
|
#
|
107
106
|
# It also handles loading plugins.
|
108
107
|
#
|
109
|
-
#
|
108
|
+
# [Note:]
|
109
|
+
# `:port` and `:host` are not valid keys. By the time they make it to the
|
110
110
|
# configuration options they are expected to be incorporated into a `:binds` key.
|
111
111
|
# Under the hood the DSL maps `port` and `host` calls to `:binds`
|
112
112
|
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
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
|
119
119
|
#
|
120
120
|
# It is expected that `load` is called on the configuration instance after setting
|
121
121
|
# config. This method expands any values in `config_file` and puts them into the
|
@@ -126,7 +126,48 @@ module Puma
|
|
126
126
|
# is done because an environment variable may have been modified while loading
|
127
127
|
# configuration files.
|
128
128
|
class Configuration
|
129
|
-
|
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
|
+
}
|
130
171
|
|
131
172
|
def initialize(user_options={}, default_options = {}, &block)
|
132
173
|
default_options = self.puma_default_options.merge(default_options)
|
@@ -137,6 +178,10 @@ module Puma
|
|
137
178
|
@file_dsl = DSL.new(@options.file_options, self)
|
138
179
|
@default_dsl = DSL.new(@options.default_options, self)
|
139
180
|
|
181
|
+
if !@options[:prune_bundler]
|
182
|
+
default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
|
183
|
+
end
|
184
|
+
|
140
185
|
if block
|
141
186
|
configure(&block)
|
142
187
|
end
|
@@ -168,26 +213,21 @@ module Puma
|
|
168
213
|
end
|
169
214
|
|
170
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
|
+
|
171
226
|
{
|
172
|
-
:
|
173
|
-
:
|
174
|
-
:
|
175
|
-
:
|
176
|
-
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
177
|
-
:workers => 0,
|
178
|
-
:daemon => false,
|
179
|
-
:mode => :http,
|
180
|
-
:worker_timeout => DefaultWorkerTimeout,
|
181
|
-
:worker_boot_timeout => DefaultWorkerTimeout,
|
182
|
-
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
183
|
-
:remote_address => :socket,
|
184
|
-
:tag => method(:infer_tag),
|
185
|
-
:environment => -> { ENV['RACK_ENV'] || "development" },
|
186
|
-
:rackup => DefaultRackup,
|
187
|
-
:logger => STDOUT,
|
188
|
-
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
189
|
-
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
190
|
-
:raise_exception_on_sigterm => true
|
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'],
|
191
231
|
}
|
192
232
|
end
|
193
233
|
|
@@ -203,7 +243,7 @@ module Puma
|
|
203
243
|
return [] if files == ['-']
|
204
244
|
return files if files.any?
|
205
245
|
|
206
|
-
first_default_file = %W(config/puma/#{
|
246
|
+
first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
|
207
247
|
File.exist?(f)
|
208
248
|
end
|
209
249
|
|
@@ -245,16 +285,8 @@ module Puma
|
|
245
285
|
def app
|
246
286
|
found = options[:app] || load_rackup
|
247
287
|
|
248
|
-
if @options[:mode] == :tcp
|
249
|
-
require 'puma/tcp_logger'
|
250
|
-
|
251
|
-
logger = @options[:logger]
|
252
|
-
quiet = !@options[:log_requests]
|
253
|
-
return TCPLogger.new(logger, found, quiet)
|
254
|
-
end
|
255
|
-
|
256
288
|
if @options[:log_requests]
|
257
|
-
|
289
|
+
require_relative 'commonlogger'
|
258
290
|
logger = @options[:logger]
|
259
291
|
found = CommonLogger.new(found, logger)
|
260
292
|
end
|
@@ -267,16 +299,31 @@ module Puma
|
|
267
299
|
@options[:environment]
|
268
300
|
end
|
269
301
|
|
270
|
-
def environment_str
|
271
|
-
environment.respond_to?(:call) ? environment.call : environment
|
272
|
-
end
|
273
|
-
|
274
302
|
def load_plugin(name)
|
275
303
|
@plugins.create name
|
276
304
|
end
|
277
305
|
|
278
|
-
|
279
|
-
|
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
|
280
327
|
end
|
281
328
|
|
282
329
|
def self.temp_path
|
@@ -288,10 +335,6 @@ module Puma
|
|
288
335
|
|
289
336
|
private
|
290
337
|
|
291
|
-
def infer_tag
|
292
|
-
File.basename(Dir.getwd)
|
293
|
-
end
|
294
|
-
|
295
338
|
# Load and use the normal Rack builder if we can, otherwise
|
296
339
|
# fallback to our minimal version.
|
297
340
|
def rack_builder
|
@@ -319,6 +362,8 @@ module Puma
|
|
319
362
|
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
320
363
|
|
321
364
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
365
|
+
rack_options = rack_options || {}
|
366
|
+
|
322
367
|
@options.file_options.merge!(rack_options)
|
323
368
|
|
324
369
|
config_ru_binds = []
|
@@ -332,31 +377,11 @@ module Puma
|
|
332
377
|
end
|
333
378
|
|
334
379
|
def self.random_token
|
335
|
-
|
336
|
-
require 'openssl'
|
337
|
-
rescue LoadError
|
338
|
-
end
|
339
|
-
|
340
|
-
count = 16
|
341
|
-
|
342
|
-
bytes = nil
|
343
|
-
|
344
|
-
if defined? OpenSSL::Random
|
345
|
-
bytes = OpenSSL::Random.random_bytes(count)
|
346
|
-
elsif File.exist?("/dev/urandom")
|
347
|
-
File.open('/dev/urandom') { |f| bytes = f.read(count) }
|
348
|
-
end
|
349
|
-
|
350
|
-
if bytes
|
351
|
-
token = "".dup
|
352
|
-
bytes.each_byte { |b| token << b.to_s(16) }
|
353
|
-
else
|
354
|
-
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
355
|
-
end
|
380
|
+
require 'securerandom' unless defined?(SecureRandom)
|
356
381
|
|
357
|
-
|
382
|
+
SecureRandom.hex(16)
|
358
383
|
end
|
359
384
|
end
|
360
385
|
end
|
361
386
|
|
362
|
-
|
387
|
+
require_relative 'dsl'
|
data/lib/puma/const.rb
CHANGED
@@ -76,7 +76,7 @@ module Puma
|
|
76
76
|
508 => 'Loop Detected',
|
77
77
|
510 => 'Not Extended',
|
78
78
|
511 => 'Network Authentication Required'
|
79
|
-
}
|
79
|
+
}.freeze
|
80
80
|
|
81
81
|
# For some HTTP status codes the client only expects headers.
|
82
82
|
#
|
@@ -85,7 +85,7 @@ module Puma
|
|
85
85
|
204 => true,
|
86
86
|
205 => true,
|
87
87
|
304 => true
|
88
|
-
}
|
88
|
+
}.freeze
|
89
89
|
|
90
90
|
# Frequently used constants when constructing requests or responses. Many times
|
91
91
|
# the constant just refers to a string with the same contents. Using these constants
|
@@ -100,31 +100,17 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "6.0.1".freeze
|
104
|
+
CODE_NAME = "Sunflower".freeze
|
105
|
+
|
105
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
107
|
|
107
108
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
108
109
|
|
109
|
-
# The default number of seconds for another request within a persistent
|
110
|
-
# session.
|
111
|
-
PERSISTENT_TIMEOUT = 20
|
112
|
-
|
113
|
-
# The default number of seconds to wait until we get the first data
|
114
|
-
# for the request
|
115
|
-
FIRST_DATA_TIMEOUT = 30
|
116
|
-
|
117
110
|
# How long to wait when getting some write blocking on the socket when
|
118
111
|
# sending data back
|
119
112
|
WRITE_TIMEOUT = 10
|
120
113
|
|
121
|
-
# How many requests to attempt inline before sending a client back to
|
122
|
-
# the reactor to be subject to normal ordering. The idea here is that
|
123
|
-
# we amortize the cost of going back to the reactor for a well behaved
|
124
|
-
# but very "greedy" client across 10 requests. This prevents a not
|
125
|
-
# well behaved client from monopolizing the thread forever.
|
126
|
-
MAX_FAST_INLINE = 10
|
127
|
-
|
128
114
|
# The original URI requested by the client.
|
129
115
|
REQUEST_URI= 'REQUEST_URI'.freeze
|
130
116
|
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
@@ -144,9 +130,11 @@ module Puma
|
|
144
130
|
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
145
131
|
# Indicate that there was an internal error, obviously.
|
146
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,
|
147
135
|
# A common header for indicating the server is too busy. Not used yet.
|
148
136
|
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
149
|
-
}
|
137
|
+
}.freeze
|
150
138
|
|
151
139
|
# The basic max request size we'll try to read.
|
152
140
|
CHUNK_SIZE = 16 * 1024
|
@@ -160,6 +148,14 @@ module Puma
|
|
160
148
|
|
161
149
|
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
162
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
|
163
159
|
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
|
164
160
|
LINE_END = "\r\n".freeze
|
165
161
|
REMOTE_ADDR = "REMOTE_ADDR".freeze
|
@@ -174,8 +170,10 @@ module Puma
|
|
174
170
|
PORT_80 = "80".freeze
|
175
171
|
PORT_443 = "443".freeze
|
176
172
|
LOCALHOST = "localhost".freeze
|
177
|
-
|
178
|
-
|
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
|
179
177
|
|
180
178
|
SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
|
181
179
|
HTTP_11 = "HTTP/1.1".freeze
|
@@ -228,7 +226,6 @@ module Puma
|
|
228
226
|
COLON = ": ".freeze
|
229
227
|
|
230
228
|
NEWLINE = "\n".freeze
|
231
|
-
HTTP_INJECTION_REGEX = /[\r\n]/.freeze
|
232
229
|
|
233
230
|
HIJACK_P = "rack.hijack?".freeze
|
234
231
|
HIJACK = "rack.hijack".freeze
|
@@ -236,8 +233,16 @@ module Puma
|
|
236
233
|
|
237
234
|
EARLY_HINTS = "rack.early_hints".freeze
|
238
235
|
|
239
|
-
#
|
240
|
-
|
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
|
241
245
|
|
246
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
242
247
|
end
|
243
248
|
end
|