rack 2.0.7 → 2.2.4

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.

Files changed (190) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +708 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -148
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +35 -10
  8. data/bin/rackup +1 -0
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +3 -1
  11. data/example/protectedlobster.ru +2 -0
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +1 -1
  14. data/lib/rack/auth/basic.rb +7 -4
  15. data/lib/rack/auth/digest/md5.rb +13 -11
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +5 -3
  19. data/lib/rack/body_proxy.rb +15 -14
  20. data/lib/rack/builder.rb +116 -23
  21. data/lib/rack/cascade.rb +28 -12
  22. data/lib/rack/chunked.rb +68 -20
  23. data/lib/rack/common_logger.rb +36 -25
  24. data/lib/rack/conditional_get.rb +20 -16
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +8 -7
  27. data/lib/rack/content_type.rb +5 -4
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +59 -34
  30. data/lib/rack/directory.rb +84 -64
  31. data/lib/rack/etag.rb +7 -4
  32. data/lib/rack/events.rb +19 -20
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +218 -0
  35. data/lib/rack/handler/cgi.rb +2 -3
  36. data/lib/rack/handler/fastcgi.rb +4 -4
  37. data/lib/rack/handler/lsws.rb +3 -3
  38. data/lib/rack/handler/scgi.rb +9 -8
  39. data/lib/rack/handler/thin.rb +3 -3
  40. data/lib/rack/handler/webrick.rb +15 -6
  41. data/lib/rack/handler.rb +7 -2
  42. data/lib/rack/head.rb +1 -1
  43. data/lib/rack/lint.rb +72 -26
  44. data/lib/rack/lobster.rb +10 -10
  45. data/lib/rack/lock.rb +2 -1
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +4 -2
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +97 -20
  51. data/lib/rack/multipart/generator.rb +17 -13
  52. data/lib/rack/multipart/parser.rb +56 -57
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +5 -4
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +59 -30
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +220 -61
  60. data/lib/rack/response.rb +127 -44
  61. data/lib/rack/rewindable_input.rb +4 -3
  62. data/lib/rack/runtime.rb +6 -4
  63. data/lib/rack/sendfile.rb +13 -9
  64. data/lib/rack/server.rb +95 -24
  65. data/lib/rack/session/abstract/id.rb +100 -22
  66. data/lib/rack/session/cookie.rb +22 -14
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +18 -9
  69. data/lib/rack/show_exceptions.rb +21 -17
  70. data/lib/rack/show_status.rb +9 -9
  71. data/lib/rack/static.rb +23 -11
  72. data/lib/rack/tempfile_reaper.rb +1 -1
  73. data/lib/rack/urlmap.rb +12 -6
  74. data/lib/rack/utils.rb +107 -111
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -28
  78. metadata +36 -178
  79. data/HISTORY.md +0 -505
  80. data/test/builder/an_underscore_app.rb +0 -5
  81. data/test/builder/anything.rb +0 -5
  82. data/test/builder/comment.ru +0 -4
  83. data/test/builder/end.ru +0 -5
  84. data/test/builder/line.ru +0 -1
  85. data/test/builder/options.ru +0 -2
  86. data/test/cgi/assets/folder/test.js +0 -1
  87. data/test/cgi/assets/fonts/font.eot +0 -1
  88. data/test/cgi/assets/images/image.png +0 -1
  89. data/test/cgi/assets/index.html +0 -1
  90. data/test/cgi/assets/javascripts/app.js +0 -1
  91. data/test/cgi/assets/stylesheets/app.css +0 -1
  92. data/test/cgi/lighttpd.conf +0 -26
  93. data/test/cgi/rackup_stub.rb +0 -6
  94. data/test/cgi/sample_rackup.ru +0 -5
  95. data/test/cgi/test +0 -9
  96. data/test/cgi/test+directory/test+file +0 -1
  97. data/test/cgi/test.fcgi +0 -9
  98. data/test/cgi/test.gz +0 -0
  99. data/test/cgi/test.ru +0 -5
  100. data/test/gemloader.rb +0 -10
  101. data/test/helper.rb +0 -34
  102. data/test/multipart/bad_robots +0 -259
  103. data/test/multipart/binary +0 -0
  104. data/test/multipart/content_type_and_no_filename +0 -6
  105. data/test/multipart/empty +0 -10
  106. data/test/multipart/fail_16384_nofile +0 -814
  107. data/test/multipart/file1.txt +0 -1
  108. data/test/multipart/filename_and_modification_param +0 -7
  109. data/test/multipart/filename_and_no_name +0 -6
  110. data/test/multipart/filename_with_encoded_words +0 -7
  111. data/test/multipart/filename_with_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  113. data/test/multipart/filename_with_null_byte +0 -7
  114. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  115. data/test/multipart/filename_with_single_quote +0 -7
  116. data/test/multipart/filename_with_unescaped_percentages +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  119. data/test/multipart/filename_with_unescaped_quotes +0 -6
  120. data/test/multipart/ie +0 -6
  121. data/test/multipart/invalid_character +0 -6
  122. data/test/multipart/mixed_files +0 -21
  123. data/test/multipart/nested +0 -10
  124. data/test/multipart/none +0 -9
  125. data/test/multipart/quoted +0 -15
  126. data/test/multipart/rack-logo.png +0 -0
  127. data/test/multipart/semicolon +0 -6
  128. data/test/multipart/text +0 -15
  129. data/test/multipart/three_files_three_fields +0 -31
  130. data/test/multipart/unity3d_wwwform +0 -11
  131. data/test/multipart/webkit +0 -32
  132. data/test/rackup/config.ru +0 -31
  133. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  134. data/test/spec_auth_basic.rb +0 -89
  135. data/test/spec_auth_digest.rb +0 -260
  136. data/test/spec_body_proxy.rb +0 -85
  137. data/test/spec_builder.rb +0 -233
  138. data/test/spec_cascade.rb +0 -63
  139. data/test/spec_cgi.rb +0 -84
  140. data/test/spec_chunked.rb +0 -103
  141. data/test/spec_common_logger.rb +0 -95
  142. data/test/spec_conditional_get.rb +0 -103
  143. data/test/spec_config.rb +0 -23
  144. data/test/spec_content_length.rb +0 -86
  145. data/test/spec_content_type.rb +0 -46
  146. data/test/spec_deflater.rb +0 -375
  147. data/test/spec_directory.rb +0 -148
  148. data/test/spec_etag.rb +0 -108
  149. data/test/spec_events.rb +0 -133
  150. data/test/spec_fastcgi.rb +0 -85
  151. data/test/spec_file.rb +0 -264
  152. data/test/spec_handler.rb +0 -57
  153. data/test/spec_head.rb +0 -46
  154. data/test/spec_lint.rb +0 -515
  155. data/test/spec_lobster.rb +0 -59
  156. data/test/spec_lock.rb +0 -204
  157. data/test/spec_logger.rb +0 -24
  158. data/test/spec_media_type.rb +0 -42
  159. data/test/spec_method_override.rb +0 -110
  160. data/test/spec_mime.rb +0 -51
  161. data/test/spec_mock.rb +0 -359
  162. data/test/spec_multipart.rb +0 -722
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1407
  166. data/test/spec_response.rb +0 -510
  167. data/test/spec_rewindable_input.rb +0 -128
  168. data/test/spec_runtime.rb +0 -50
  169. data/test/spec_sendfile.rb +0 -125
  170. data/test/spec_server.rb +0 -193
  171. data/test/spec_session_abstract_id.rb +0 -31
  172. data/test/spec_session_abstract_session_hash.rb +0 -45
  173. data/test/spec_session_cookie.rb +0 -442
  174. data/test/spec_session_memcache.rb +0 -320
  175. data/test/spec_session_pool.rb +0 -210
  176. data/test/spec_show_exceptions.rb +0 -93
  177. data/test/spec_show_status.rb +0 -104
  178. data/test/spec_static.rb +0 -184
  179. data/test/spec_tempfile_reaper.rb +0 -64
  180. data/test/spec_thin.rb +0 -96
  181. data/test/spec_urlmap.rb +0 -237
  182. data/test/spec_utils.rb +0 -742
  183. data/test/spec_version.rb +0 -11
  184. data/test/spec_webrick.rb +0 -206
  185. data/test/static/another/index.html +0 -1
  186. data/test/static/foo.html +0 -1
  187. data/test/static/index.html +0 -1
  188. data/test/testrequest.rb +0 -78
  189. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  190. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/utils.rb CHANGED
@@ -1,22 +1,30 @@
1
1
  # -*- encoding: binary -*-
2
+ # frozen_string_literal: true
3
+
2
4
  require 'uri'
3
5
  require 'fileutils'
4
6
  require 'set'
5
7
  require 'tempfile'
6
- require 'rack/query_parser'
7
8
  require 'time'
8
9
 
10
+ require_relative 'query_parser'
11
+
9
12
  module Rack
10
13
  # Rack::Utils contains a grab-bag of useful methods for writing web
11
14
  # applications adopted from all kinds of Ruby libraries.
12
15
 
13
16
  module Utils
17
+ (require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
18
+
14
19
  ParameterTypeError = QueryParser::ParameterTypeError
15
20
  InvalidParameterError = QueryParser::InvalidParameterError
16
21
  DEFAULT_SEP = QueryParser::DEFAULT_SEP
17
22
  COMMON_SEP = QueryParser::COMMON_SEP
18
23
  KeySpaceConstrainedParams = QueryParser::Params
19
24
 
25
+ RFC2822_DAY_NAME = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
26
+ RFC2822_MONTH_NAME = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
27
+
20
28
  class << self
21
29
  attr_accessor :default_query_parser
22
30
  end
@@ -24,33 +32,30 @@ module Rack
24
32
  # This helps prevent a rogue client from flooding a Request.
25
33
  self.default_query_parser = QueryParser.make_default(65536, 100)
26
34
 
35
+ module_function
36
+
27
37
  # URI escapes. (CGI style space to +)
28
38
  def escape(s)
29
39
  URI.encode_www_form_component(s)
30
40
  end
31
- module_function :escape
32
41
 
33
42
  # Like URI escaping, but with %20 instead of +. Strictly speaking this is
34
43
  # true URI escaping.
35
44
  def escape_path(s)
36
45
  ::URI::DEFAULT_PARSER.escape s
37
46
  end
38
- module_function :escape_path
39
47
 
40
48
  # Unescapes the **path** component of a URI. See Rack::Utils.unescape for
41
49
  # unescaping query parameters or form components.
42
50
  def unescape_path(s)
43
51
  ::URI::DEFAULT_PARSER.unescape s
44
52
  end
45
- module_function :unescape_path
46
-
47
53
 
48
54
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
49
55
  # target encoding of the string returned, and it defaults to UTF-8
50
56
  def unescape(s, encoding = Encoding::UTF_8)
51
57
  URI.decode_www_form_component(s, encoding)
52
58
  end
53
- module_function :unescape
54
59
 
55
60
  class << self
56
61
  attr_accessor :multipart_part_limit
@@ -82,21 +87,20 @@ module Rack
82
87
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
83
88
  end
84
89
  else
90
+ # :nocov:
85
91
  def clock_time
86
92
  Time.now.to_f
87
93
  end
94
+ # :nocov:
88
95
  end
89
- module_function :clock_time
90
96
 
91
97
  def parse_query(qs, d = nil, &unescaper)
92
98
  Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
93
99
  end
94
- module_function :parse_query
95
100
 
96
101
  def parse_nested_query(qs, d = nil)
97
102
  Rack::Utils.default_query_parser.parse_nested_query(qs, d)
98
103
  end
99
- module_function :parse_nested_query
100
104
 
101
105
  def build_query(params)
102
106
  params.map { |k, v|
@@ -107,7 +111,6 @@ module Rack
107
111
  end
108
112
  }.join("&")
109
113
  end
110
- module_function :build_query
111
114
 
112
115
  def build_nested_query(value, prefix = nil)
113
116
  case value
@@ -118,7 +121,7 @@ module Rack
118
121
  when Hash
119
122
  value.map { |k, v|
120
123
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
121
- }.reject(&:empty?).join('&')
124
+ }.delete_if(&:empty?).join('&')
122
125
  when nil
123
126
  prefix
124
127
  else
@@ -126,20 +129,22 @@ module Rack
126
129
  "#{prefix}=#{escape(value)}"
127
130
  end
128
131
  end
129
- module_function :build_nested_query
130
132
 
131
133
  def q_values(q_value_header)
132
134
  q_value_header.to_s.split(/\s*,\s*/).map do |part|
133
135
  value, parameters = part.split(/\s*;\s*/, 2)
134
136
  quality = 1.0
135
- if md = /\Aq=([\d.]+)/.match(parameters)
137
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
136
138
  quality = md[1].to_f
137
139
  end
138
140
  [value, quality]
139
141
  end
140
142
  end
141
- module_function :q_values
142
143
 
144
+ # Return best accept value to use, based on the algorithm
145
+ # in RFC 2616 Section 14. If there are multiple best
146
+ # matches (same specificity and quality), the value returned
147
+ # is arbitrary.
143
148
  def best_q_match(q_value_header, available_mimes)
144
149
  values = q_values(q_value_header)
145
150
 
@@ -152,7 +157,6 @@ module Rack
152
157
  end.last
153
158
  matches && matches.first
154
159
  end
155
- module_function :best_q_match
156
160
 
157
161
  ESCAPE_HTML = {
158
162
  "&" => "&amp;",
@@ -169,51 +173,55 @@ module Rack
169
173
  def escape_html(string)
170
174
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
171
175
  end
172
- module_function :escape_html
173
176
 
174
177
  def select_best_encoding(available_encodings, accept_encoding)
175
178
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
176
179
 
177
- expanded_accept_encoding =
178
- accept_encoding.map { |m, q|
179
- if m == "*"
180
- (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
181
- else
182
- [[m, q]]
180
+ expanded_accept_encoding = []
181
+
182
+ accept_encoding.each do |m, q|
183
+ preference = available_encodings.index(m) || available_encodings.size
184
+
185
+ if m == "*"
186
+ (available_encodings - accept_encoding.map(&:first)).each do |m2|
187
+ expanded_accept_encoding << [m2, q, preference]
183
188
  end
184
- }.inject([]) { |mem, list|
185
- mem + list
186
- }
189
+ else
190
+ expanded_accept_encoding << [m, q, preference]
191
+ end
192
+ end
187
193
 
188
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
194
+ encoding_candidates = expanded_accept_encoding
195
+ .sort_by { |_, q, p| [-q, p] }
196
+ .map!(&:first)
189
197
 
190
198
  unless encoding_candidates.include?("identity")
191
199
  encoding_candidates.push("identity")
192
200
  end
193
201
 
194
- expanded_accept_encoding.each { |m, q|
202
+ expanded_accept_encoding.each do |m, q|
195
203
  encoding_candidates.delete(m) if q == 0.0
196
- }
204
+ end
197
205
 
198
- return (encoding_candidates & available_encodings)[0]
206
+ (encoding_candidates & available_encodings)[0]
199
207
  end
200
- module_function :select_best_encoding
201
208
 
202
209
  def parse_cookies(env)
203
210
  parse_cookies_header env[HTTP_COOKIE]
204
211
  end
205
- module_function :parse_cookies
206
212
 
207
213
  def parse_cookies_header(header)
208
- # According to RFC 2109:
209
- # If multiple cookies satisfy the criteria above, they are ordered in
210
- # the Cookie header such that those with more specific Path attributes
211
- # precede those with less specific. Ordering with respect to other
212
- # attributes (e.g., Domain) is unspecified.
213
- cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
214
- cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
214
+ # According to RFC 6265:
215
+ # The syntax for cookie headers only supports semicolons
216
+ # User Agent -> Server ==
217
+ # Cookie: SID=31d4d96e407aad42; lang=en-US
218
+ return {} unless header
219
+ header.split(/[;] */n).each_with_object({}) do |cookie, cookies|
220
+ next if cookie.empty?
221
+ key, value = cookie.split('=', 2)
222
+ cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
223
+ end
215
224
  end
216
- module_function :parse_cookies_header
217
225
 
218
226
  def add_cookie_to_header(header, key, value)
219
227
  case value
@@ -221,41 +229,19 @@ module Rack
221
229
  domain = "; domain=#{value[:domain]}" if value[:domain]
222
230
  path = "; path=#{value[:path]}" if value[:path]
223
231
  max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
224
- # There is an RFC mess in the area of date formatting for Cookies. Not
225
- # only are there contradicting RFCs and examples within RFC text, but
226
- # there are also numerous conflicting names of fields and partially
227
- # cross-applicable specifications.
228
- #
229
- # These are best described in RFC 2616 3.3.1. This RFC text also
230
- # specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
231
- # fixed length format with space-date delimited fields.
232
- #
233
- # See also RFC 1123 section 5.2.14.
234
- #
235
- # RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
236
- # in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
237
- # the space delimited format. These formats are compliant with RFC 2822.
238
- #
239
- # For reference, all involved RFCs are:
240
- # RFC 822
241
- # RFC 1123
242
- # RFC 2109
243
- # RFC 2616
244
- # RFC 2822
245
- # RFC 2965
246
- # RFC 6265
247
- expires = "; expires=" +
248
- rfc2822(value[:expires].clone.gmtime) if value[:expires]
232
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
249
233
  secure = "; secure" if value[:secure]
250
234
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
251
235
  same_site =
252
236
  case value[:same_site]
253
237
  when false, nil
254
238
  nil
239
+ when :none, 'None', :None
240
+ '; SameSite=None'
255
241
  when :lax, 'Lax', :Lax
256
- '; SameSite=Lax'.freeze
242
+ '; SameSite=Lax'
257
243
  when true, :strict, 'Strict', :Strict
258
- '; SameSite=Strict'.freeze
244
+ '; SameSite=Strict'
259
245
  else
260
246
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
261
247
  end
@@ -277,13 +263,11 @@ module Rack
277
263
  raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
278
264
  end
279
265
  end
280
- module_function :add_cookie_to_header
281
266
 
282
267
  def set_cookie_header!(header, key, value)
283
268
  header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
284
269
  nil
285
270
  end
286
- module_function :set_cookie_header!
287
271
 
288
272
  def make_delete_cookie_header(header, key, value)
289
273
  case header
@@ -295,25 +279,30 @@ module Rack
295
279
  cookies = header
296
280
  end
297
281
 
298
- cookies.reject! { |cookie|
299
- if value[:domain]
300
- cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
301
- elsif value[:path]
302
- cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
303
- else
304
- cookie =~ /\A#{escape(key)}=/
305
- end
306
- }
282
+ key = escape(key)
283
+ domain = value[:domain]
284
+ path = value[:path]
285
+ regexp = if domain
286
+ if path
287
+ /\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
288
+ else
289
+ /\A#{key}=.*domain=#{domain}(?:;|$)/
290
+ end
291
+ elsif path
292
+ /\A#{key}=.*path=#{path}(?:;|$)/
293
+ else
294
+ /\A#{key}=/
295
+ end
296
+
297
+ cookies.reject! { |cookie| regexp.match? cookie }
307
298
 
308
299
  cookies.join("\n")
309
300
  end
310
- module_function :make_delete_cookie_header
311
301
 
312
302
  def delete_cookie_header!(header, key, value = {})
313
303
  header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
314
304
  nil
315
305
  end
316
- module_function :delete_cookie_header!
317
306
 
318
307
  # Adds a cookie that will *remove* a cookie from the client. Hence the
319
308
  # strange method name.
@@ -321,17 +310,15 @@ module Rack
321
310
  new_header = make_delete_cookie_header(header, key, value)
322
311
 
323
312
  add_cookie_to_header(new_header, key,
324
- {:value => '', :path => nil, :domain => nil,
325
- :max_age => '0',
326
- :expires => Time.at(0) }.merge(value))
313
+ { value: '', path: nil, domain: nil,
314
+ max_age: '0',
315
+ expires: Time.at(0) }.merge(value))
327
316
 
328
317
  end
329
- module_function :add_remove_cookie_to_header
330
318
 
331
319
  def rfc2822(time)
332
320
  time.rfc2822
333
321
  end
334
- module_function :rfc2822
335
322
 
336
323
  # Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
337
324
  # of '% %b %Y'.
@@ -343,11 +330,10 @@ module Rack
343
330
  # weekday and month.
344
331
  #
345
332
  def rfc2109(time)
346
- wday = Time::RFC2822_DAY_NAME[time.wday]
347
- mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
333
+ wday = RFC2822_DAY_NAME[time.wday]
334
+ mon = RFC2822_MONTH_NAME[time.mon - 1]
348
335
  time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
349
336
  end
350
- module_function :rfc2109
351
337
 
352
338
  # Parses the "Range:" header, if present, into an array of Range objects.
353
339
  # Returns nil if the header is missing or syntactically invalid.
@@ -356,7 +342,6 @@ module Rack
356
342
  warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
357
343
  get_byte_ranges env['HTTP_RANGE'], size
358
344
  end
359
- module_function :byte_ranges
360
345
 
361
346
  def get_byte_ranges(http_range, size)
362
347
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
@@ -364,7 +349,7 @@ module Rack
364
349
  ranges = []
365
350
  $1.split(/,\s*/).each do |range_spec|
366
351
  return nil unless range_spec =~ /(\d*)-(\d*)/
367
- r0,r1 = $1, $2
352
+ r0, r1 = $1, $2
368
353
  if r0.empty?
369
354
  return nil if r1.empty?
370
355
  # suffix-byte-range-spec, represents trailing suffix of file
@@ -378,14 +363,13 @@ module Rack
378
363
  else
379
364
  r1 = r1.to_i
380
365
  return nil if r1 < r0 # backwards range is syntactically invalid
381
- r1 = size-1 if r1 >= size
366
+ r1 = size - 1 if r1 >= size
382
367
  end
383
368
  end
384
369
  ranges << (r0..r1) if r0 <= r1
385
370
  end
386
371
  ranges
387
372
  end
388
- module_function :get_byte_ranges
389
373
 
390
374
  # Constant time string comparison.
391
375
  #
@@ -399,10 +383,9 @@ module Rack
399
383
  l = a.unpack("C*")
400
384
 
401
385
  r, i = 0, -1
402
- b.each_byte { |v| r |= v ^ l[i+=1] }
386
+ b.each_byte { |v| r |= v ^ l[i += 1] }
403
387
  r == 0
404
388
  end
405
- module_function :secure_compare
406
389
 
407
390
  # Context allows the use of a compatible middleware at different points
408
391
  # in a request handling stack. A compatible middleware must define
@@ -425,19 +408,25 @@ module Rack
425
408
  self.class.new(@for, app)
426
409
  end
427
410
 
428
- def context(env, app=@app)
411
+ def context(env, app = @app)
429
412
  recontext(app).call(env)
430
413
  end
431
414
  end
432
415
 
433
416
  # A case-insensitive Hash that preserves the original case of a
434
417
  # header when set.
435
- class HeaderHash < Hash
436
- def self.new(hash={})
437
- HeaderHash === hash ? hash : super(hash)
418
+ #
419
+ # @api private
420
+ class HeaderHash < Hash # :nodoc:
421
+ def self.[](headers)
422
+ if headers.is_a?(HeaderHash) && !headers.frozen?
423
+ return headers
424
+ else
425
+ return self.new(headers)
426
+ end
438
427
  end
439
428
 
440
- def initialize(hash={})
429
+ def initialize(hash = {})
441
430
  super()
442
431
  @names = {}
443
432
  hash.each { |k, v| self[k] = v }
@@ -449,6 +438,12 @@ module Rack
449
438
  @names = other.names.dup
450
439
  end
451
440
 
441
+ # on clear, we need to clear @names hash
442
+ def clear
443
+ super
444
+ @names.clear
445
+ end
446
+
452
447
  def each
453
448
  super do |k, v|
454
449
  yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
@@ -457,7 +452,7 @@ module Rack
457
452
 
458
453
  def to_hash
459
454
  hash = {}
460
- each { |k,v| hash[k] = v }
455
+ each { |k, v| hash[k] = v }
461
456
  hash
462
457
  end
463
458
 
@@ -510,13 +505,14 @@ module Rack
510
505
 
511
506
  # Every standard HTTP code mapped to the appropriate message.
512
507
  # Generated with:
513
- # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
514
- # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
515
- # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
508
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
509
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
510
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
516
511
  HTTP_STATUS_CODES = {
517
512
  100 => 'Continue',
518
513
  101 => 'Switching Protocols',
519
514
  102 => 'Processing',
515
+ 103 => 'Early Hints',
520
516
  200 => 'OK',
521
517
  201 => 'Created',
522
518
  202 => 'Accepted',
@@ -533,6 +529,7 @@ module Rack
533
529
  303 => 'See Other',
534
530
  304 => 'Not Modified',
535
531
  305 => 'Use Proxy',
532
+ 306 => '(Unused)',
536
533
  307 => 'Temporary Redirect',
537
534
  308 => 'Permanent Redirect',
538
535
  400 => 'Bad Request',
@@ -557,6 +554,7 @@ module Rack
557
554
  422 => 'Unprocessable Entity',
558
555
  423 => 'Locked',
559
556
  424 => 'Failed Dependency',
557
+ 425 => 'Too Early',
560
558
  426 => 'Upgrade Required',
561
559
  428 => 'Precondition Required',
562
560
  429 => 'Too Many Requests',
@@ -571,12 +569,13 @@ module Rack
571
569
  506 => 'Variant Also Negotiates',
572
570
  507 => 'Insufficient Storage',
573
571
  508 => 'Loop Detected',
572
+ 509 => 'Bandwidth Limit Exceeded',
574
573
  510 => 'Not Extended',
575
574
  511 => 'Network Authentication Required'
576
575
  }
577
576
 
578
577
  # Responses with HTTP status codes that should not have an entity body
579
- STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
578
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
580
579
 
581
580
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
582
581
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -584,12 +583,11 @@ module Rack
584
583
 
585
584
  def status_code(status)
586
585
  if status.is_a?(Symbol)
587
- SYMBOL_TO_STATUS_CODE[status] || 500
586
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
588
587
  else
589
588
  status.to_i
590
589
  end
591
590
  end
592
- module_function :status_code
593
591
 
594
592
  PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
595
593
 
@@ -603,18 +601,16 @@ module Rack
603
601
  part == '..' ? clean.pop : clean << part
604
602
  end
605
603
 
606
- clean.unshift '/' if parts.empty? || parts.first.empty?
607
-
608
- ::File.join(*clean)
604
+ clean_path = clean.join(::File::SEPARATOR)
605
+ clean_path.prepend("/") if parts.empty? || parts.first.empty?
606
+ clean_path
609
607
  end
610
- module_function :clean_path_info
611
608
 
612
- NULL_BYTE = "\0".freeze
609
+ NULL_BYTE = "\0"
613
610
 
614
611
  def valid_path?(path)
615
612
  path.valid_encoding? && !path.include?(NULL_BYTE)
616
613
  end
617
- module_function :valid_path?
618
614
 
619
615
  end
620
616
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
4
+ #
5
+ # Rack is freely distributable under the terms of an MIT-style license.
6
+ # See MIT-LICENSE or https://opensource.org/licenses/MIT.
7
+
8
+ # The Rack main module, serving as a namespace for all core Rack
9
+ # modules and classes.
10
+ #
11
+ # All modules meant for use in your application are <tt>autoload</tt>ed here,
12
+ # so it should be enough just to <tt>require 'rack'</tt> in your code.
13
+
14
+ module Rack
15
+ # The Rack protocol version number implemented.
16
+ VERSION = [1, 3]
17
+
18
+ # Return the Rack protocol version as a dotted string.
19
+ def self.version
20
+ VERSION.join(".")
21
+ end
22
+
23
+ RELEASE = "2.2.4"
24
+
25
+ # Return the Rack release as a dotted string.
26
+ def self.release
27
+ RELEASE
28
+ end
29
+ end