rack 3.0.0 → 3.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +592 -1
  3. data/CONTRIBUTING.md +11 -9
  4. data/README.md +120 -29
  5. data/SPEC.rdoc +206 -288
  6. data/lib/rack/auth/abstract/request.rb +2 -0
  7. data/lib/rack/auth/basic.rb +1 -4
  8. data/lib/rack/bad_request.rb +8 -0
  9. data/lib/rack/body_proxy.rb +18 -2
  10. data/lib/rack/builder.rb +61 -40
  11. data/lib/rack/cascade.rb +0 -3
  12. data/lib/rack/common_logger.rb +3 -2
  13. data/lib/rack/conditional_get.rb +4 -3
  14. data/lib/rack/constants.rb +6 -1
  15. data/lib/rack/directory.rb +6 -3
  16. data/lib/rack/etag.rb +3 -0
  17. data/lib/rack/events.rb +21 -6
  18. data/lib/rack/files.rb +1 -1
  19. data/lib/rack/head.rb +2 -3
  20. data/lib/rack/headers.rb +98 -14
  21. data/lib/rack/lint.rb +491 -428
  22. data/lib/rack/media_type.rb +18 -9
  23. data/lib/rack/method_override.rb +2 -2
  24. data/lib/rack/mime.rb +6 -5
  25. data/lib/rack/mock_request.rb +10 -15
  26. data/lib/rack/mock_response.rb +50 -20
  27. data/lib/rack/multipart/parser.rb +279 -80
  28. data/lib/rack/multipart/uploaded_file.rb +42 -5
  29. data/lib/rack/multipart.rb +34 -1
  30. data/lib/rack/query_parser.rb +88 -80
  31. data/lib/rack/request.rb +98 -65
  32. data/lib/rack/response.rb +35 -20
  33. data/lib/rack/rewindable_input.rb +4 -1
  34. data/lib/rack/sendfile.rb +53 -23
  35. data/lib/rack/show_exceptions.rb +10 -4
  36. data/lib/rack/show_status.rb +0 -2
  37. data/lib/rack/static.rb +9 -4
  38. data/lib/rack/urlmap.rb +1 -1
  39. data/lib/rack/utils.rb +205 -133
  40. data/lib/rack/version.rb +3 -20
  41. data/lib/rack.rb +11 -17
  42. metadata +6 -15
  43. data/lib/rack/auth/digest/md5.rb +0 -1
  44. data/lib/rack/auth/digest/nonce.rb +0 -1
  45. data/lib/rack/auth/digest/params.rb +0 -1
  46. data/lib/rack/auth/digest/request.rb +0 -1
  47. data/lib/rack/auth/digest.rb +0 -256
  48. data/lib/rack/chunked.rb +0 -120
  49. data/lib/rack/file.rb +0 -9
  50. data/lib/rack/logger.rb +0 -22
data/lib/rack/builder.rb CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require_relative 'urlmap'
4
4
 
5
+ module Rack; end
6
+ Rack::BUILDER_TOPLEVEL_BINDING = ->(builder){builder.instance_eval{binding}}
7
+
5
8
  module Rack
6
9
  # Rack::Builder provides a domain-specific language (DSL) to construct Rack
7
10
  # applications. It is primarily used to parse +config.ru+ files which
@@ -10,26 +13,23 @@ module Rack
10
13
  #
11
14
  # Example:
12
15
  #
13
- # require 'rack/lobster'
14
- # app = Rack::Builder.new do
15
- # use Rack::CommonLogger
16
- # use Rack::ShowExceptions
17
- # map "/lobster" do
18
- # use Rack::Lint
19
- # run Rack::Lobster.new
20
- # end
21
- # end
16
+ # app = Rack::Builder.new do
17
+ # use Rack::CommonLogger
18
+ # map "/ok" do
19
+ # run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
20
+ # end
21
+ # end
22
22
  #
23
- # run app
23
+ # run app
24
24
  #
25
25
  # Or
26
26
  #
27
- # app = Rack::Builder.app do
28
- # use Rack::CommonLogger
29
- # run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
30
- # end
27
+ # app = Rack::Builder.app do
28
+ # use Rack::CommonLogger
29
+ # run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
30
+ # end
31
31
  #
32
- # run app
32
+ # run app
33
33
  #
34
34
  # +use+ adds middleware to the stack, +run+ dispatches to an application.
35
35
  # You can use +map+ to construct a Rack::URLMap in a convenient way.
@@ -56,15 +56,15 @@ module Rack
56
56
  # Rack::Builder.parse_file('app.rb')
57
57
  # # requires app.rb, which can be anywhere in Ruby's
58
58
  # # load path. After requiring, assumes App constant
59
- # # contains Rack application
59
+ # # is a Rack application
60
60
  #
61
61
  # Rack::Builder.parse_file('./my_app.rb')
62
62
  # # requires ./my_app.rb, which should be in the
63
63
  # # process's current directory. After requiring,
64
- # # assumes MyApp constant contains Rack application
65
- def self.parse_file(path)
64
+ # # assumes MyApp constant is a Rack application
65
+ def self.parse_file(path, **options)
66
66
  if path.end_with?('.ru')
67
- return self.load_file(path)
67
+ return self.load_file(path, **options)
68
68
  else
69
69
  require path
70
70
  return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join(''))
@@ -84,7 +84,7 @@ module Rack
84
84
  # use Rack::ContentLength
85
85
  # require './app.rb'
86
86
  # run App
87
- def self.load_file(path)
87
+ def self.load_file(path, **options)
88
88
  config = ::File.read(path)
89
89
  config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8
90
90
 
@@ -94,16 +94,18 @@ module Rack
94
94
 
95
95
  config.sub!(/^__END__\n.*\Z/m, '')
96
96
 
97
- return new_from_string(config, path)
97
+ return new_from_string(config, path, **options)
98
98
  end
99
99
 
100
100
  # Evaluate the given +builder_script+ string in the context of
101
101
  # a Rack::Builder block, returning a Rack application.
102
- def self.new_from_string(builder_script, file = "(rackup)")
102
+ def self.new_from_string(builder_script, path = "(rackup)", **options)
103
+ builder = self.new(**options)
104
+
103
105
  # We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
104
106
  # We cannot use instance_eval(String) as that would resolve constants differently.
105
- binding, builder = TOPLEVEL_BINDING.eval('Rack::Builder.new.instance_eval { [binding, self] }')
106
- eval builder_script, binding, file
107
+ binding = BUILDER_TOPLEVEL_BINDING.call(builder)
108
+ eval(builder_script, binding, path)
107
109
 
108
110
  return builder.to_app
109
111
  end
@@ -111,16 +113,24 @@ module Rack
111
113
  # Initialize a new Rack::Builder instance. +default_app+ specifies the
112
114
  # default application if +run+ is not called later. If a block
113
115
  # is given, it is evaluated in the context of the instance.
114
- def initialize(default_app = nil, &block)
116
+ def initialize(default_app = nil, **options, &block)
115
117
  @use = []
116
118
  @map = nil
117
119
  @run = default_app
118
120
  @warmup = nil
119
121
  @freeze_app = false
122
+ @options = options
120
123
 
121
124
  instance_eval(&block) if block_given?
122
125
  end
123
126
 
127
+ # Any options provided to the Rack::Builder instance at initialization.
128
+ # These options can be server-specific. Some general options are:
129
+ #
130
+ # * +:isolation+: One of +process+, +thread+ or +fiber+. The execution
131
+ # isolation model to use.
132
+ attr :options
133
+
124
134
  # Create a new Rack::Builder instance and return the Rack application
125
135
  # generated from it.
126
136
  def self.app(default_app = nil, &block)
@@ -152,6 +162,8 @@ module Rack
152
162
  @use << proc { |app| generate_map(app, mapping) }
153
163
  end
154
164
  @use << proc { |app| middleware.new(app, *args, &block) }
165
+
166
+ nil
155
167
  end
156
168
  # :nocov:
157
169
  ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
@@ -180,19 +192,12 @@ module Rack
180
192
  #
181
193
  # run Heartbeat.new
182
194
  #
183
- # It could also be a module:
184
- #
185
- # module HelloWorld
186
- # def call(env)
187
- # [200, { "content-type" => "text/plain" }, ["Hello World"]]
188
- # end
189
- # end
190
- #
191
- # run HelloWorld
192
195
  def run(app = nil, &block)
193
196
  raise ArgumentError, "Both app and block given!" if app && block_given?
194
197
 
195
198
  @run = app || block
199
+
200
+ nil
196
201
  end
197
202
 
198
203
  # Takes a lambda or block that is used to warm-up the application. This block is called
@@ -213,21 +218,35 @@ module Rack
213
218
  # the Rack application specified by run inside the block. Other requests will be sent to the
214
219
  # default application specified by run outside the block.
215
220
  #
216
- # Rack::Builder.app do
221
+ # class App
222
+ # def call(env)
223
+ # [200, {'content-type' => 'text/plain'}, ["Hello World"]]
224
+ # end
225
+ # end
226
+ #
227
+ # class Heartbeat
228
+ # def call(env)
229
+ # [200, { "content-type" => "text/plain" }, ["OK"]]
230
+ # end
231
+ # end
232
+ #
233
+ # app = Rack::Builder.app do
217
234
  # map '/heartbeat' do
218
- # run Heartbeat
235
+ # run Heartbeat.new
219
236
  # end
220
- # run App
237
+ # run App.new
221
238
  # end
222
239
  #
240
+ # run app
241
+ #
223
242
  # The +use+ method can also be used inside the block to specify middleware to run under a specific path:
224
243
  #
225
- # Rack::Builder.app do
244
+ # app = Rack::Builder.app do
226
245
  # map '/heartbeat' do
227
246
  # use Middleware
228
- # run Heartbeat
247
+ # run Heartbeat.new
229
248
  # end
230
- # run App
249
+ # run App.new
231
250
  # end
232
251
  #
233
252
  # This example includes a piece of middleware which will run before +/heartbeat+ requests hit +Heartbeat+.
@@ -237,6 +256,8 @@ module Rack
237
256
  def map(path, &block)
238
257
  @map ||= {}
239
258
  @map[path] = block
259
+
260
+ nil
240
261
  end
241
262
 
242
263
  # Freeze the app (set using run) and all middleware instances when building the application
data/lib/rack/cascade.rb CHANGED
@@ -9,9 +9,6 @@ module Rack
9
9
  # status codes, return the last response.
10
10
 
11
11
  class Cascade
12
- # deprecated, no longer used
13
- NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
14
-
15
12
  # An array of applications to try in order.
16
13
  attr_reader :apps
17
14
 
@@ -20,7 +20,7 @@ module Rack
20
20
  # The actual format is slightly different than the above due to the
21
21
  # separation of SCRIPT_NAME and PATH_INFO, and because the elapsed
22
22
  # time in seconds is included at the end.
23
- FORMAT = %{%s - %s [%s] "%s %s%s%s %s" %d %s %0.4f\n}
23
+ FORMAT = %{%s - %s [%s] "%s %s%s%s %s" %d %s %0.4f }
24
24
 
25
25
  # +logger+ can be any object that supports the +write+ or +<<+ methods,
26
26
  # which includes the standard library Logger. These methods are called
@@ -66,7 +66,8 @@ module Rack
66
66
  length,
67
67
  Utils.clock_time - began_at)
68
68
 
69
- msg.gsub!(/[^[:print:]\n]/) { |c| sprintf("\\x%x", c.ord) }
69
+ msg.gsub!(/[^[:print:]]/) { |c| sprintf("\\x%x", c.ord) }
70
+ msg[-1] = "\n"
70
71
 
71
72
  logger = @logger || request.get_header(RACK_ERRORS)
72
73
  # Standard library logger doesn't support write but it supports << which actually
@@ -34,9 +34,10 @@ module Rack
34
34
  response[0] = 304
35
35
  headers.delete(CONTENT_TYPE)
36
36
  headers.delete(CONTENT_LENGTH)
37
- response[2] = Rack::BodyProxy.new([]) do
38
- body.close if body.respond_to?(:close)
39
- end
37
+
38
+ # We are done with the body:
39
+ body.close if body.respond_to?(:close)
40
+ response[2] = []
40
41
  end
41
42
  response
42
43
  else
@@ -14,7 +14,7 @@ module Rack
14
14
  SERVER_NAME = 'SERVER_NAME'
15
15
  SERVER_PORT = 'SERVER_PORT'
16
16
  HTTP_COOKIE = 'HTTP_COOKIE'
17
-
17
+
18
18
  # Response Header Keys
19
19
  CACHE_CONTROL = 'cache-control'
20
20
  CONTENT_LENGTH = 'content-length'
@@ -32,6 +32,7 @@ module Rack
32
32
  DELETE = 'DELETE'
33
33
  HEAD = 'HEAD'
34
34
  OPTIONS = 'OPTIONS'
35
+ CONNECT = 'CONNECT'
35
36
  LINK = 'LINK'
36
37
  UNLINK = 'UNLINK'
37
38
  TRACE = 'TRACE'
@@ -39,6 +40,7 @@ module Rack
39
40
  # Rack environment variables
40
41
  RACK_VERSION = 'rack.version'
41
42
  RACK_TEMPFILES = 'rack.tempfiles'
43
+ RACK_EARLY_HINTS = 'rack.early_hints'
42
44
  RACK_ERRORS = 'rack.errors'
43
45
  RACK_LOGGER = 'rack.logger'
44
46
  RACK_INPUT = 'rack.input'
@@ -52,9 +54,12 @@ module Rack
52
54
  RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
53
55
  RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
54
56
  RACK_RESPONSE_FINISHED = 'rack.response_finished'
57
+ RACK_PROTOCOL = 'rack.protocol'
55
58
  RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
56
59
  RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
60
+ RACK_REQUEST_FORM_PAIRS = 'rack.request.form_pairs'
57
61
  RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
62
+ RACK_REQUEST_FORM_ERROR = 'rack.request.form_error'
58
63
  RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
59
64
  RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
60
65
  RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
@@ -17,7 +17,7 @@ module Rack
17
17
  # If +app+ is not specified, a Rack::Files of the same +root+ will be used.
18
18
 
19
19
  class Directory
20
- DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>\n"
20
+ DIR_FILE = "<tr><td class='name'><a href='./%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>\n"
21
21
  DIR_PAGE_HEADER = <<-PAGE
22
22
  <html><head>
23
23
  <title>%s</title>
@@ -51,7 +51,7 @@ table { width:100%%; }
51
51
  class DirectoryBody < Struct.new(:root, :path, :files)
52
52
  # Yield strings for each part of the directory entry
53
53
  def each
54
- show_path = Utils.escape_html(path.sub(/^#{root}/, ''))
54
+ show_path = Utils.escape_html(path.sub(/\A#{Regexp.escape(root)}/, ''))
55
55
  yield(DIR_PAGE_HEADER % [ show_path, show_path ])
56
56
 
57
57
  unless path.chomp('/') == root
@@ -82,6 +82,7 @@ table { width:100%%; }
82
82
  # Set the root directory and application for serving files.
83
83
  def initialize(root, app = nil)
84
84
  @root = ::File.expand_path(root)
85
+ @root_with_separator = @root.end_with?(::File::SEPARATOR) ? @root : "#{@root}#{::File::SEPARATOR}"
85
86
  @app = app || Files.new(@root)
86
87
  @head = Head.new(method(:get))
87
88
  end
@@ -118,7 +119,9 @@ table { width:100%%; }
118
119
  # Rack response to use for requests with paths outside the root, or nil if path is inside the root.
119
120
  def check_forbidden(path_info)
120
121
  return unless path_info.include? ".."
121
- return if ::File.expand_path(::File.join(@root, path_info)).start_with?(@root)
122
+
123
+ expanded_path = ::File.expand_path(::File.join(@root, path_info))
124
+ return if expanded_path == @root || expanded_path.start_with?(@root_with_separator)
122
125
 
123
126
  body = "Forbidden\n"
124
127
  [403, { CONTENT_TYPE => "text/plain",
data/lib/rack/etag.rb CHANGED
@@ -32,6 +32,9 @@ module Rack
32
32
  body = body.to_ary
33
33
  digest = digest_body(body)
34
34
  headers[ETAG_STRING] = %(W/"#{digest}") if digest
35
+
36
+ # Body was modified, so we need to re-assign it:
37
+ response[2] = body
35
38
  end
36
39
 
37
40
  unless headers[CACHE_CONTROL]
data/lib/rack/events.rb CHANGED
@@ -29,12 +29,13 @@ module Rack
29
29
  #
30
30
  # * on_send(request, response)
31
31
  #
32
- # The webserver has started iterating over the response body and presumably
33
- # has started sending data over the wire. This method is always called with
34
- # a request object and the response object. The response object is
35
- # constructed from the rack triple that the application returned. Changes
36
- # SHOULD NOT be made to the response object as the webserver has already
37
- # started sending data. Any mutations will likely result in an exception.
32
+ # The webserver has started iterating over the response body, or has called
33
+ # the streaming body, and presumably has started sending data over the
34
+ # wire. This method is always called with a request object and the response
35
+ # object. The response object is constructed from the rack triple that the
36
+ # application returned. Changes SHOULD NOT be made to the response object
37
+ # as the webserver has already started sending data. Any mutations will
38
+ # likely result in an exception.
38
39
  #
39
40
  # * on_finish(request, response)
40
41
  #
@@ -90,6 +91,20 @@ module Rack
90
91
  @handlers.reverse_each { |handler| handler.on_send request, response }
91
92
  super
92
93
  end
94
+
95
+ def call(stream)
96
+ @handlers.reverse_each { |handler| handler.on_send request, response }
97
+ super
98
+ end
99
+
100
+ def respond_to?(method_name, include_all = false)
101
+ case method_name
102
+ when :each, :call
103
+ @body.respond_to?(method_name, include_all)
104
+ else
105
+ super
106
+ end
107
+ end
93
108
  end
94
109
 
95
110
  class BufferedResponse < Rack::Response::Raw # :nodoc:
data/lib/rack/files.rb CHANGED
@@ -194,7 +194,7 @@ EOF
194
194
  status,
195
195
  {
196
196
  CONTENT_TYPE => "text/plain",
197
- CONTENT_LENGTH => body.size.to_s,
197
+ CONTENT_LENGTH => body.bytesize.to_s,
198
198
  "x-cascade" => "pass"
199
199
  }.merge!(headers),
200
200
  [body]
data/lib/rack/head.rb CHANGED
@@ -15,9 +15,8 @@ module Rack
15
15
  _, _, body = response = @app.call(env)
16
16
 
17
17
  if env[REQUEST_METHOD] == HEAD
18
- response[2] = Rack::BodyProxy.new([]) do
19
- body.close if body.respond_to? :close
20
- end
18
+ body.close if body.respond_to?(:close)
19
+ response[2] = []
21
20
  end
22
21
 
23
22
  response
data/lib/rack/headers.rb CHANGED
@@ -1,9 +1,93 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Headers is a Hash subclass that downcases all keys. It's designed
3
5
  # to be used by rack applications that don't implement the Rack 3 SPEC
4
6
  # (by using non-lowercase response header keys), automatically handling
5
7
  # the downcasing of keys.
6
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
+
7
91
  def self.[](*items)
8
92
  if items.length % 2 != 0
9
93
  if items.length == 1 && items.first.is_a?(Hash)
@@ -28,10 +112,10 @@ module Rack
28
112
  end
29
113
 
30
114
  def []=(key, value)
31
- super(key.downcase.freeze, value)
115
+ super(KNOWN_HEADERS[key] || key.downcase.freeze, value)
32
116
  end
33
117
  alias store []=
34
-
118
+
35
119
  def assoc(key)
36
120
  super(downcase_key(key))
37
121
  end
@@ -43,7 +127,7 @@ module Rack
43
127
  def delete(key)
44
128
  super(downcase_key(key))
45
129
  end
46
-
130
+
47
131
  def dig(key, *a)
48
132
  super(downcase_key(key), *a)
49
133
  end
@@ -52,7 +136,7 @@ module Rack
52
136
  key = downcase_key(key)
53
137
  super
54
138
  end
55
-
139
+
56
140
  def fetch_values(*a)
57
141
  super(*a.map!{|key| downcase_key(key)})
58
142
  end
@@ -63,34 +147,34 @@ module Rack
63
147
  alias include? has_key?
64
148
  alias key? has_key?
65
149
  alias member? has_key?
66
-
150
+
67
151
  def invert
68
152
  hash = self.class.new
69
153
  each{|key, value| hash[value] = key}
70
154
  hash
71
155
  end
72
-
156
+
73
157
  def merge(hash, &block)
74
158
  dup.merge!(hash, &block)
75
159
  end
76
-
160
+
77
161
  def reject(&block)
78
162
  hash = dup
79
163
  hash.reject!(&block)
80
164
  hash
81
165
  end
82
-
166
+
83
167
  def replace(hash)
84
168
  clear
85
169
  update(hash)
86
170
  end
87
-
171
+
88
172
  def select(&block)
89
173
  hash = dup
90
174
  hash.select!(&block)
91
175
  hash
92
176
  end
93
-
177
+
94
178
  def to_proc
95
179
  lambda{|x| self[x]}
96
180
  end
@@ -100,10 +184,10 @@ module Rack
100
184
  end
101
185
 
102
186
  def update(hash, &block)
103
- hash.each do |key, value|
187
+ hash.each do |key, value|
104
188
  self[key] = if block_given? && include?(key)
105
189
  block.call(key, self[key], value)
106
- else
190
+ else
107
191
  value
108
192
  end
109
193
  end
@@ -114,7 +198,7 @@ module Rack
114
198
  def values_at(*keys)
115
199
  keys.map{|key| self[key]}
116
200
  end
117
-
201
+
118
202
  # :nocov:
119
203
  if RUBY_VERSION >= '2.5'
120
204
  # :nocov:
@@ -148,7 +232,7 @@ module Rack
148
232
  private
149
233
 
150
234
  def downcase_key(key)
151
- key.is_a?(String) ? key.downcase : key
235
+ key.is_a?(String) ? KNOWN_HEADERS[key] || key.downcase : key
152
236
  end
153
237
  end
154
238
  end