rack 2.2.7 → 3.1.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +291 -78
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +328 -0
- data/SPEC.rdoc +213 -136
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +1 -4
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +21 -3
- data/lib/rack/builder.rb +102 -69
- data/lib/rack/cascade.rb +2 -3
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +67 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +864 -681
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +3 -0
- data/lib/rack/media_type.rb +9 -4
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +171 -0
- data/lib/rack/mock_response.rb +124 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +218 -91
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +81 -102
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +248 -123
- data/lib/rack/response.rb +146 -66
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +21 -4
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +237 -235
- data/lib/rack/version.rb +1 -9
- data/lib/rack.rb +13 -89
- metadata +15 -41
- data/README.rdoc +0 -320
- data/Rakefile +0 -130
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/auth/digest/md5.rb +0 -131
- data/lib/rack/auth/digest/nonce.rb +0 -54
- data/lib/rack/auth/digest/params.rb +0 -54
- data/lib/rack/auth/digest/request.rb +0 -43
- data/lib/rack/chunked.rb +0 -117
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/file.rb +0 -7
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -203
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- data/rack.gemspec +0 -46
data/lib/rack/deflater.rb
CHANGED
@@ -3,6 +3,11 @@
|
|
3
3
|
require "zlib"
|
4
4
|
require "time" # for Time.httpdate
|
5
5
|
|
6
|
+
require_relative 'constants'
|
7
|
+
require_relative 'utils'
|
8
|
+
require_relative 'request'
|
9
|
+
require_relative 'body_proxy'
|
10
|
+
|
6
11
|
module Rack
|
7
12
|
# This middleware enables content encoding of http responses,
|
8
13
|
# usually for purposes of compression.
|
@@ -21,8 +26,6 @@ module Rack
|
|
21
26
|
# Note that despite the name, Deflater does not support the +deflate+
|
22
27
|
# encoding.
|
23
28
|
class Deflater
|
24
|
-
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
25
|
-
|
26
29
|
# Creates Rack::Deflater middleware. Options:
|
27
30
|
#
|
28
31
|
# :if :: a lambda enabling / disabling deflation based on returned boolean value
|
@@ -41,11 +44,10 @@ module Rack
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def call(env)
|
44
|
-
status, headers, body = @app.call(env)
|
45
|
-
headers = Utils::HeaderHash[headers]
|
47
|
+
status, headers, body = response = @app.call(env)
|
46
48
|
|
47
49
|
unless should_deflate?(env, status, headers, body)
|
48
|
-
return
|
50
|
+
return response
|
49
51
|
end
|
50
52
|
|
51
53
|
request = Request.new(env)
|
@@ -54,21 +56,23 @@ module Rack
|
|
54
56
|
request.accept_encoding)
|
55
57
|
|
56
58
|
# Set the Vary HTTP header.
|
57
|
-
vary = headers["
|
58
|
-
unless vary.include?("*") || vary.
|
59
|
-
headers["
|
59
|
+
vary = headers["vary"].to_s.split(",").map(&:strip)
|
60
|
+
unless vary.include?("*") || vary.any?{|v| v.downcase == 'accept-encoding'}
|
61
|
+
headers["vary"] = vary.push("Accept-Encoding").join(",")
|
60
62
|
end
|
61
63
|
|
62
64
|
case encoding
|
63
65
|
when "gzip"
|
64
|
-
headers['
|
66
|
+
headers['content-encoding'] = "gzip"
|
65
67
|
headers.delete(CONTENT_LENGTH)
|
66
|
-
mtime = headers["
|
68
|
+
mtime = headers["last-modified"]
|
67
69
|
mtime = Time.httpdate(mtime).to_i if mtime
|
68
|
-
[
|
70
|
+
response[2] = GzipStream.new(body, mtime, @sync)
|
71
|
+
response
|
69
72
|
when "identity"
|
70
|
-
|
71
|
-
when nil
|
73
|
+
response
|
74
|
+
else # when nil
|
75
|
+
# Only possible encoding values here are 'gzip', 'identity', and nil
|
72
76
|
message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
|
73
77
|
bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
|
74
78
|
[406, { CONTENT_TYPE => "text/plain", CONTENT_LENGTH => message.length.to_s }, bp]
|
@@ -77,6 +81,9 @@ module Rack
|
|
77
81
|
|
78
82
|
# Body class used for gzip encoded responses.
|
79
83
|
class GzipStream
|
84
|
+
|
85
|
+
BUFFER_LENGTH = 128 * 1_024
|
86
|
+
|
80
87
|
# Initialize the gzip stream. Arguments:
|
81
88
|
# body :: Response body to compress with gzip
|
82
89
|
# mtime :: The modification time of the body, used to set the
|
@@ -93,19 +100,26 @@ module Rack
|
|
93
100
|
@writer = block
|
94
101
|
gzip = ::Zlib::GzipWriter.new(self)
|
95
102
|
gzip.mtime = @mtime if @mtime
|
96
|
-
@body.each
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
103
|
+
# @body.each is equivalent to @body.gets (slow)
|
104
|
+
if @body.is_a? ::File # XXX: Should probably be ::IO
|
105
|
+
while part = @body.read(BUFFER_LENGTH)
|
106
|
+
gzip.write(part)
|
107
|
+
gzip.flush if @sync
|
108
|
+
end
|
109
|
+
else
|
110
|
+
@body.each { |part|
|
111
|
+
# Skip empty strings, as they would result in no output,
|
112
|
+
# and flushing empty parts would raise Zlib::BufError.
|
113
|
+
next if part.empty?
|
114
|
+
gzip.write(part)
|
115
|
+
gzip.flush if @sync
|
116
|
+
}
|
117
|
+
end
|
104
118
|
ensure
|
105
|
-
gzip.
|
119
|
+
gzip.finish
|
106
120
|
end
|
107
121
|
|
108
|
-
# Call the block passed to #each with the
|
122
|
+
# Call the block passed to #each with the gzipped data.
|
109
123
|
def write(data)
|
110
124
|
@writer.call(data)
|
111
125
|
end
|
@@ -123,13 +137,13 @@ module Rack
|
|
123
137
|
# Skip compressing empty entity body responses and responses with
|
124
138
|
# no-transform set.
|
125
139
|
if Utils::STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
|
126
|
-
/\bno-transform\b/.match?(headers[
|
127
|
-
headers['
|
140
|
+
/\bno-transform\b/.match?(headers[CACHE_CONTROL].to_s) ||
|
141
|
+
headers['content-encoding']&.!~(/\bidentity\b/)
|
128
142
|
return false
|
129
143
|
end
|
130
144
|
|
131
145
|
# Skip if @compressible_types are given and does not include request's content type
|
132
|
-
return false if @compressible_types && !(headers.has_key?(
|
146
|
+
return false if @compressible_types && !(headers.has_key?(CONTENT_TYPE) && @compressible_types.include?(headers[CONTENT_TYPE][/[^;]*/]))
|
133
147
|
|
134
148
|
# Skip if @condition lambda is given and evaluates to false
|
135
149
|
return false if @condition && !@condition.call(env, status, headers, body)
|
data/lib/rack/directory.rb
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'time'
|
4
4
|
|
5
|
+
require_relative 'constants'
|
6
|
+
require_relative 'utils'
|
7
|
+
require_relative 'head'
|
8
|
+
require_relative 'mime'
|
9
|
+
require_relative 'files'
|
10
|
+
|
5
11
|
module Rack
|
6
12
|
# Rack::Directory serves entries below the +root+ given, according to the
|
7
13
|
# path info of the Rack request. If a directory is found, the file's contents
|
@@ -106,7 +112,7 @@ table { width:100%%; }
|
|
106
112
|
body = "Bad Request\n"
|
107
113
|
[400, { CONTENT_TYPE => "text/plain",
|
108
114
|
CONTENT_LENGTH => body.bytesize.to_s,
|
109
|
-
"
|
115
|
+
"x-cascade" => "pass" }, [body]]
|
110
116
|
end
|
111
117
|
|
112
118
|
# Rack response to use for requests with paths outside the root, or nil if path is inside the root.
|
@@ -117,7 +123,7 @@ table { width:100%%; }
|
|
117
123
|
body = "Forbidden\n"
|
118
124
|
[403, { CONTENT_TYPE => "text/plain",
|
119
125
|
CONTENT_LENGTH => body.bytesize.to_s,
|
120
|
-
"
|
126
|
+
"x-cascade" => "pass" }, [body]]
|
121
127
|
end
|
122
128
|
|
123
129
|
# Rack response to use for directories under the root.
|
@@ -176,7 +182,7 @@ table { width:100%%; }
|
|
176
182
|
body = "Entity not found: #{path_info}\n"
|
177
183
|
[404, { CONTENT_TYPE => "text/plain",
|
178
184
|
CONTENT_LENGTH => body.bytesize.to_s,
|
179
|
-
"
|
185
|
+
"x-cascade" => "pass" }, [body]]
|
180
186
|
end
|
181
187
|
|
182
188
|
# Stolen from Ramaze
|
data/lib/rack/etag.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../rack'
|
4
3
|
require 'digest/sha2'
|
5
4
|
|
5
|
+
require_relative 'constants'
|
6
|
+
require_relative 'utils'
|
7
|
+
|
6
8
|
module Rack
|
7
|
-
# Automatically sets the
|
9
|
+
# Automatically sets the etag header on all String bodies.
|
8
10
|
#
|
9
|
-
# The
|
11
|
+
# The etag header is skipped if etag or last-modified headers are sent or if
|
10
12
|
# a sendfile body (body.responds_to :to_path) is given (since such cases
|
11
13
|
# should be handled by apache/nginx).
|
12
14
|
#
|
13
|
-
# On initialization, you can pass two parameters: a
|
14
|
-
# used when
|
15
|
+
# On initialization, you can pass two parameters: a cache-control directive
|
16
|
+
# used when etag is absent and a directive when it is present. The first
|
15
17
|
# defaults to nil, while the second defaults to "max-age=0, private, must-revalidate"
|
16
18
|
class ETag
|
17
19
|
ETAG_STRING = Rack::ETAG
|
@@ -24,16 +26,11 @@ module Rack
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def call(env)
|
27
|
-
status, headers, body = @app.call(env)
|
28
|
-
|
29
|
-
headers = Utils::HeaderHash[headers]
|
29
|
+
status, headers, body = response = @app.call(env)
|
30
30
|
|
31
|
-
if etag_status?(status) &&
|
32
|
-
|
33
|
-
digest
|
34
|
-
body = Rack::BodyProxy.new(new_body) do
|
35
|
-
original_body.close if original_body.respond_to?(:close)
|
36
|
-
end
|
31
|
+
if etag_status?(status) && body.respond_to?(:to_ary) && !skip_caching?(headers)
|
32
|
+
body = body.to_ary
|
33
|
+
digest = digest_body(body)
|
37
34
|
headers[ETAG_STRING] = %(W/"#{digest}") if digest
|
38
35
|
end
|
39
36
|
|
@@ -45,7 +42,7 @@ module Rack
|
|
45
42
|
end
|
46
43
|
end
|
47
44
|
|
48
|
-
|
45
|
+
response
|
49
46
|
end
|
50
47
|
|
51
48
|
private
|
@@ -54,24 +51,18 @@ module Rack
|
|
54
51
|
status == 200 || status == 201
|
55
52
|
end
|
56
53
|
|
57
|
-
def etag_body?(body)
|
58
|
-
!body.respond_to?(:to_path)
|
59
|
-
end
|
60
|
-
|
61
54
|
def skip_caching?(headers)
|
62
|
-
headers.key?(ETAG_STRING) || headers.key?('
|
55
|
+
headers.key?(ETAG_STRING) || headers.key?('last-modified')
|
63
56
|
end
|
64
57
|
|
65
58
|
def digest_body(body)
|
66
|
-
parts = []
|
67
59
|
digest = nil
|
68
60
|
|
69
61
|
body.each do |part|
|
70
|
-
parts << part
|
71
62
|
(digest ||= Digest::SHA256.new) << part unless part.empty?
|
72
63
|
end
|
73
64
|
|
74
|
-
|
65
|
+
digest && digest.hexdigest.byteslice(0,32)
|
75
66
|
end
|
76
67
|
end
|
77
68
|
end
|
data/lib/rack/events.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'body_proxy'
|
4
|
+
require_relative 'request'
|
5
|
+
require_relative 'response'
|
6
|
+
|
3
7
|
module Rack
|
4
8
|
### This middleware provides hooks to certain places in the request /
|
5
9
|
# response lifecycle. This is so that middleware that don't need to filter
|
data/lib/rack/files.rb
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'time'
|
4
4
|
|
5
|
+
require_relative 'constants'
|
6
|
+
require_relative 'head'
|
7
|
+
require_relative 'utils'
|
8
|
+
require_relative 'request'
|
9
|
+
require_relative 'mime'
|
10
|
+
|
5
11
|
module Rack
|
6
12
|
# Rack::Files serves files below the +root+ directory given, according to the
|
7
13
|
# path info of the Rack request.
|
@@ -16,14 +22,6 @@ module Rack
|
|
16
22
|
ALLOW_HEADER = ALLOWED_VERBS.join(', ')
|
17
23
|
MULTIPART_BOUNDARY = 'AaB03x'
|
18
24
|
|
19
|
-
# @todo remove in 3.0
|
20
|
-
def self.method_added(name)
|
21
|
-
if name == :response_body
|
22
|
-
raise "#{self.class}\#response_body is no longer supported."
|
23
|
-
end
|
24
|
-
super
|
25
|
-
end
|
26
|
-
|
27
25
|
attr_reader :root
|
28
26
|
|
29
27
|
def initialize(root, headers = {}, default_mime = 'text/plain')
|
@@ -41,7 +39,7 @@ module Rack
|
|
41
39
|
def get(env)
|
42
40
|
request = Rack::Request.new env
|
43
41
|
unless ALLOWED_VERBS.include? request.request_method
|
44
|
-
return fail(405, "Method Not Allowed", { '
|
42
|
+
return fail(405, "Method Not Allowed", { 'allow' => ALLOW_HEADER })
|
45
43
|
end
|
46
44
|
|
47
45
|
path_info = Utils.unescape_path request.path_info
|
@@ -69,12 +67,12 @@ module Rack
|
|
69
67
|
|
70
68
|
def serving(request, path)
|
71
69
|
if request.options?
|
72
|
-
return [200, { '
|
70
|
+
return [200, { 'allow' => ALLOW_HEADER, CONTENT_LENGTH => '0' }, []]
|
73
71
|
end
|
74
72
|
last_modified = ::File.mtime(path).httpdate
|
75
73
|
return [304, {}, []] if request.get_header('HTTP_IF_MODIFIED_SINCE') == last_modified
|
76
74
|
|
77
|
-
headers = { "
|
75
|
+
headers = { "last-modified" => last_modified }
|
78
76
|
mime_type = mime_type path, @default_mime
|
79
77
|
headers[CONTENT_TYPE] = mime_type if mime_type
|
80
78
|
|
@@ -91,15 +89,15 @@ module Rack
|
|
91
89
|
elsif ranges.empty?
|
92
90
|
# Unsatisfiable. Return error, and file size:
|
93
91
|
response = fail(416, "Byte range unsatisfiable")
|
94
|
-
response[1]["
|
92
|
+
response[1]["content-range"] = "bytes */#{size}"
|
95
93
|
return response
|
96
|
-
|
94
|
+
else
|
97
95
|
# Partial content
|
98
96
|
partial_content = true
|
99
97
|
|
100
98
|
if ranges.size == 1
|
101
99
|
range = ranges[0]
|
102
|
-
headers["
|
100
|
+
headers["content-range"] = "bytes #{range.begin}-#{range.end}/#{size}"
|
103
101
|
else
|
104
102
|
headers[CONTENT_TYPE] = "multipart/byteranges; boundary=#{MULTIPART_BOUNDARY}"
|
105
103
|
end
|
@@ -164,8 +162,8 @@ module Rack
|
|
164
162
|
<<-EOF
|
165
163
|
\r
|
166
164
|
--#{MULTIPART_BOUNDARY}\r
|
167
|
-
|
168
|
-
|
165
|
+
content-type: #{options[:mime_type]}\r
|
166
|
+
content-range: bytes #{range.begin}-#{range.end}/#{options[:size]}\r
|
169
167
|
\r
|
170
168
|
EOF
|
171
169
|
end
|
@@ -197,7 +195,7 @@ EOF
|
|
197
195
|
{
|
198
196
|
CONTENT_TYPE => "text/plain",
|
199
197
|
CONTENT_LENGTH => body.size.to_s,
|
200
|
-
"
|
198
|
+
"x-cascade" => "pass"
|
201
199
|
}.merge!(headers),
|
202
200
|
[body]
|
203
201
|
]
|
data/lib/rack/head.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'body_proxy'
|
5
|
+
|
3
6
|
module Rack
|
4
7
|
# Rack::Head returns an empty body for all HEAD requests. It leaves
|
5
8
|
# all other requests unchanged.
|
@@ -9,17 +12,15 @@ module Rack
|
|
9
12
|
end
|
10
13
|
|
11
14
|
def call(env)
|
12
|
-
|
15
|
+
_, _, body = response = @app.call(env)
|
13
16
|
|
14
17
|
if env[REQUEST_METHOD] == HEAD
|
15
|
-
[
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
]
|
20
|
-
else
|
21
|
-
[status, headers, body]
|
18
|
+
response[2] = Rack::BodyProxy.new([]) do
|
19
|
+
body.close if body.respond_to? :close
|
20
|
+
end
|
22
21
|
end
|
22
|
+
|
23
|
+
response
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
data/lib/rack/headers.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
# Rack::Headers is a Hash subclass that downcases all keys. It's designed
|
5
|
+
# to be used by rack applications that don't implement the Rack 3 SPEC
|
6
|
+
# (by using non-lowercase response header keys), automatically handling
|
7
|
+
# the downcasing of keys.
|
8
|
+
class Headers < Hash
|
9
|
+
KNOWN_HEADERS = {}
|
10
|
+
%w(
|
11
|
+
Accept-CH
|
12
|
+
Accept-Patch
|
13
|
+
Accept-Ranges
|
14
|
+
Access-Control-Allow-Credentials
|
15
|
+
Access-Control-Allow-Headers
|
16
|
+
Access-Control-Allow-Methods
|
17
|
+
Access-Control-Allow-Origin
|
18
|
+
Access-Control-Expose-Headers
|
19
|
+
Access-Control-Max-Age
|
20
|
+
Age
|
21
|
+
Allow
|
22
|
+
Alt-Svc
|
23
|
+
Cache-Control
|
24
|
+
Connection
|
25
|
+
Content-Disposition
|
26
|
+
Content-Encoding
|
27
|
+
Content-Language
|
28
|
+
Content-Length
|
29
|
+
Content-Location
|
30
|
+
Content-MD5
|
31
|
+
Content-Range
|
32
|
+
Content-Security-Policy
|
33
|
+
Content-Security-Policy-Report-Only
|
34
|
+
Content-Type
|
35
|
+
Date
|
36
|
+
Delta-Base
|
37
|
+
ETag
|
38
|
+
Expect-CT
|
39
|
+
Expires
|
40
|
+
Feature-Policy
|
41
|
+
IM
|
42
|
+
Last-Modified
|
43
|
+
Link
|
44
|
+
Location
|
45
|
+
NEL
|
46
|
+
P3P
|
47
|
+
Permissions-Policy
|
48
|
+
Pragma
|
49
|
+
Preference-Applied
|
50
|
+
Proxy-Authenticate
|
51
|
+
Public-Key-Pins
|
52
|
+
Referrer-Policy
|
53
|
+
Refresh
|
54
|
+
Report-To
|
55
|
+
Retry-After
|
56
|
+
Server
|
57
|
+
Set-Cookie
|
58
|
+
Status
|
59
|
+
Strict-Transport-Security
|
60
|
+
Timing-Allow-Origin
|
61
|
+
Tk
|
62
|
+
Trailer
|
63
|
+
Transfer-Encoding
|
64
|
+
Upgrade
|
65
|
+
Vary
|
66
|
+
Via
|
67
|
+
WWW-Authenticate
|
68
|
+
Warning
|
69
|
+
X-Cascade
|
70
|
+
X-Content-Duration
|
71
|
+
X-Content-Security-Policy
|
72
|
+
X-Content-Type-Options
|
73
|
+
X-Correlation-ID
|
74
|
+
X-Correlation-Id
|
75
|
+
X-Download-Options
|
76
|
+
X-Frame-Options
|
77
|
+
X-Permitted-Cross-Domain-Policies
|
78
|
+
X-Powered-By
|
79
|
+
X-Redirect-By
|
80
|
+
X-Request-ID
|
81
|
+
X-Request-Id
|
82
|
+
X-Runtime
|
83
|
+
X-UA-Compatible
|
84
|
+
X-WebKit-CS
|
85
|
+
X-XSS-Protection
|
86
|
+
).each do |str|
|
87
|
+
downcased = str.downcase.freeze
|
88
|
+
KNOWN_HEADERS[str] = KNOWN_HEADERS[downcased] = downcased
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.[](*items)
|
92
|
+
if items.length % 2 != 0
|
93
|
+
if items.length == 1 && items.first.is_a?(Hash)
|
94
|
+
new.merge!(items.first)
|
95
|
+
else
|
96
|
+
raise ArgumentError, "odd number of arguments for Rack::Headers"
|
97
|
+
end
|
98
|
+
else
|
99
|
+
hash = new
|
100
|
+
loop do
|
101
|
+
break if items.length == 0
|
102
|
+
key = items.shift
|
103
|
+
value = items.shift
|
104
|
+
hash[key] = value
|
105
|
+
end
|
106
|
+
hash
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def [](key)
|
111
|
+
super(downcase_key(key))
|
112
|
+
end
|
113
|
+
|
114
|
+
def []=(key, value)
|
115
|
+
super(KNOWN_HEADERS[key] || key.downcase.freeze, value)
|
116
|
+
end
|
117
|
+
alias store []=
|
118
|
+
|
119
|
+
def assoc(key)
|
120
|
+
super(downcase_key(key))
|
121
|
+
end
|
122
|
+
|
123
|
+
def compare_by_identity
|
124
|
+
raise TypeError, "Rack::Headers cannot compare by identity, use regular Hash"
|
125
|
+
end
|
126
|
+
|
127
|
+
def delete(key)
|
128
|
+
super(downcase_key(key))
|
129
|
+
end
|
130
|
+
|
131
|
+
def dig(key, *a)
|
132
|
+
super(downcase_key(key), *a)
|
133
|
+
end
|
134
|
+
|
135
|
+
def fetch(key, *default, &block)
|
136
|
+
key = downcase_key(key)
|
137
|
+
super
|
138
|
+
end
|
139
|
+
|
140
|
+
def fetch_values(*a)
|
141
|
+
super(*a.map!{|key| downcase_key(key)})
|
142
|
+
end
|
143
|
+
|
144
|
+
def has_key?(key)
|
145
|
+
super(downcase_key(key))
|
146
|
+
end
|
147
|
+
alias include? has_key?
|
148
|
+
alias key? has_key?
|
149
|
+
alias member? has_key?
|
150
|
+
|
151
|
+
def invert
|
152
|
+
hash = self.class.new
|
153
|
+
each{|key, value| hash[value] = key}
|
154
|
+
hash
|
155
|
+
end
|
156
|
+
|
157
|
+
def merge(hash, &block)
|
158
|
+
dup.merge!(hash, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
def reject(&block)
|
162
|
+
hash = dup
|
163
|
+
hash.reject!(&block)
|
164
|
+
hash
|
165
|
+
end
|
166
|
+
|
167
|
+
def replace(hash)
|
168
|
+
clear
|
169
|
+
update(hash)
|
170
|
+
end
|
171
|
+
|
172
|
+
def select(&block)
|
173
|
+
hash = dup
|
174
|
+
hash.select!(&block)
|
175
|
+
hash
|
176
|
+
end
|
177
|
+
|
178
|
+
def to_proc
|
179
|
+
lambda{|x| self[x]}
|
180
|
+
end
|
181
|
+
|
182
|
+
def transform_values(&block)
|
183
|
+
dup.transform_values!(&block)
|
184
|
+
end
|
185
|
+
|
186
|
+
def update(hash, &block)
|
187
|
+
hash.each do |key, value|
|
188
|
+
self[key] = if block_given? && include?(key)
|
189
|
+
block.call(key, self[key], value)
|
190
|
+
else
|
191
|
+
value
|
192
|
+
end
|
193
|
+
end
|
194
|
+
self
|
195
|
+
end
|
196
|
+
alias merge! update
|
197
|
+
|
198
|
+
def values_at(*keys)
|
199
|
+
keys.map{|key| self[key]}
|
200
|
+
end
|
201
|
+
|
202
|
+
# :nocov:
|
203
|
+
if RUBY_VERSION >= '2.5'
|
204
|
+
# :nocov:
|
205
|
+
def slice(*a)
|
206
|
+
h = self.class.new
|
207
|
+
a.each{|k| h[k] = self[k] if has_key?(k)}
|
208
|
+
h
|
209
|
+
end
|
210
|
+
|
211
|
+
def transform_keys(&block)
|
212
|
+
dup.transform_keys!(&block)
|
213
|
+
end
|
214
|
+
|
215
|
+
def transform_keys!
|
216
|
+
hash = self.class.new
|
217
|
+
each do |k, v|
|
218
|
+
hash[yield k] = v
|
219
|
+
end
|
220
|
+
replace(hash)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# :nocov:
|
225
|
+
if RUBY_VERSION >= '3.0'
|
226
|
+
# :nocov:
|
227
|
+
def except(*a)
|
228
|
+
super(*a.map!{|key| downcase_key(key)})
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def downcase_key(key)
|
235
|
+
key.is_a?(String) ? KNOWN_HEADERS[key] || key.downcase : key
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|