rack 2.0.9.3 → 2.2.8

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +740 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +151 -147
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +44 -15
  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 +33 -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 +219 -184
  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 +5 -3
  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 +55 -56
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +4 -2
  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 +222 -63
  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 +34 -21
  66. data/lib/rack/session/cookie.rb +12 -12
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +5 -3
  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 +13 -7
  74. data/lib/rack/utils.rb +105 -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 -179
  79. data/HISTORY.md +0 -520
  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 -107
  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 -520
  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 -721
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1423
  166. data/test/spec_response.rb +0 -528
  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 -357
  175. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  176. data/test/spec_session_pool.rb +0 -247
  177. data/test/spec_show_exceptions.rb +0 -93
  178. data/test/spec_show_status.rb +0 -104
  179. data/test/spec_static.rb +0 -184
  180. data/test/spec_tempfile_reaper.rb +0 -64
  181. data/test/spec_thin.rb +0 -96
  182. data/test/spec_urlmap.rb +0 -237
  183. data/test/spec_utils.rb +0 -742
  184. data/test/spec_version.rb +0 -11
  185. data/test/spec_webrick.rb +0 -206
  186. data/test/static/another/index.html +0 -1
  187. data/test/static/foo.html +0 -1
  188. data/test/static/index.html +0 -1
  189. data/test/testrequest.rb +0 -78
  190. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  191. 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_total_part_limit
@@ -93,21 +98,20 @@ module Rack
93
98
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
94
99
  end
95
100
  else
101
+ # :nocov:
96
102
  def clock_time
97
103
  Time.now.to_f
98
104
  end
105
+ # :nocov:
99
106
  end
100
- module_function :clock_time
101
107
 
102
108
  def parse_query(qs, d = nil, &unescaper)
103
109
  Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
104
110
  end
105
- module_function :parse_query
106
111
 
107
112
  def parse_nested_query(qs, d = nil)
108
113
  Rack::Utils.default_query_parser.parse_nested_query(qs, d)
109
114
  end
110
- module_function :parse_nested_query
111
115
 
112
116
  def build_query(params)
113
117
  params.map { |k, v|
@@ -118,7 +122,6 @@ module Rack
118
122
  end
119
123
  }.join("&")
120
124
  end
121
- module_function :build_query
122
125
 
123
126
  def build_nested_query(value, prefix = nil)
124
127
  case value
@@ -129,7 +132,7 @@ module Rack
129
132
  when Hash
130
133
  value.map { |k, v|
131
134
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
132
- }.reject(&:empty?).join('&')
135
+ }.delete_if(&:empty?).join('&')
133
136
  when nil
134
137
  prefix
135
138
  else
@@ -137,20 +140,22 @@ module Rack
137
140
  "#{prefix}=#{escape(value)}"
138
141
  end
139
142
  end
140
- module_function :build_nested_query
141
143
 
142
144
  def q_values(q_value_header)
143
145
  q_value_header.to_s.split(/\s*,\s*/).map do |part|
144
146
  value, parameters = part.split(/\s*;\s*/, 2)
145
147
  quality = 1.0
146
- if md = /\Aq=([\d.]+)/.match(parameters)
148
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
147
149
  quality = md[1].to_f
148
150
  end
149
151
  [value, quality]
150
152
  end
151
153
  end
152
- module_function :q_values
153
154
 
155
+ # Return best accept value to use, based on the algorithm
156
+ # in RFC 2616 Section 14. If there are multiple best
157
+ # matches (same specificity and quality), the value returned
158
+ # is arbitrary.
154
159
  def best_q_match(q_value_header, available_mimes)
155
160
  values = q_values(q_value_header)
156
161
 
@@ -163,7 +168,6 @@ module Rack
163
168
  end.last
164
169
  matches && matches.first
165
170
  end
166
- module_function :best_q_match
167
171
 
168
172
  ESCAPE_HTML = {
169
173
  "&" => "&amp;",
@@ -180,51 +184,55 @@ module Rack
180
184
  def escape_html(string)
181
185
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
182
186
  end
183
- module_function :escape_html
184
187
 
185
188
  def select_best_encoding(available_encodings, accept_encoding)
186
189
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
187
190
 
188
- expanded_accept_encoding =
189
- accept_encoding.map { |m, q|
190
- if m == "*"
191
- (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
192
- else
193
- [[m, q]]
191
+ expanded_accept_encoding = []
192
+
193
+ accept_encoding.each do |m, q|
194
+ preference = available_encodings.index(m) || available_encodings.size
195
+
196
+ if m == "*"
197
+ (available_encodings - accept_encoding.map(&:first)).each do |m2|
198
+ expanded_accept_encoding << [m2, q, preference]
194
199
  end
195
- }.inject([]) { |mem, list|
196
- mem + list
197
- }
200
+ else
201
+ expanded_accept_encoding << [m, q, preference]
202
+ end
203
+ end
198
204
 
199
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
205
+ encoding_candidates = expanded_accept_encoding
206
+ .sort_by { |_, q, p| [-q, p] }
207
+ .map!(&:first)
200
208
 
201
209
  unless encoding_candidates.include?("identity")
202
210
  encoding_candidates.push("identity")
203
211
  end
204
212
 
205
- expanded_accept_encoding.each { |m, q|
213
+ expanded_accept_encoding.each do |m, q|
206
214
  encoding_candidates.delete(m) if q == 0.0
207
- }
215
+ end
208
216
 
209
- return (encoding_candidates & available_encodings)[0]
217
+ (encoding_candidates & available_encodings)[0]
210
218
  end
211
- module_function :select_best_encoding
212
219
 
213
220
  def parse_cookies(env)
214
221
  parse_cookies_header env[HTTP_COOKIE]
215
222
  end
216
- module_function :parse_cookies
217
223
 
218
224
  def parse_cookies_header(header)
219
- # According to RFC 2109:
220
- # If multiple cookies satisfy the criteria above, they are ordered in
221
- # the Cookie header such that those with more specific Path attributes
222
- # precede those with less specific. Ordering with respect to other
223
- # attributes (e.g., Domain) is unspecified.
224
- cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
225
- cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
225
+ # According to RFC 6265:
226
+ # The syntax for cookie headers only supports semicolons
227
+ # User Agent -> Server ==
228
+ # Cookie: SID=31d4d96e407aad42; lang=en-US
229
+ return {} unless header
230
+ header.split(/[;] */n).each_with_object({}) do |cookie, cookies|
231
+ next if cookie.empty?
232
+ key, value = cookie.split('=', 2)
233
+ cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
234
+ end
226
235
  end
227
- module_function :parse_cookies_header
228
236
 
229
237
  def add_cookie_to_header(header, key, value)
230
238
  case value
@@ -232,31 +240,7 @@ module Rack
232
240
  domain = "; domain=#{value[:domain]}" if value[:domain]
233
241
  path = "; path=#{value[:path]}" if value[:path]
234
242
  max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
235
- # There is an RFC mess in the area of date formatting for Cookies. Not
236
- # only are there contradicting RFCs and examples within RFC text, but
237
- # there are also numerous conflicting names of fields and partially
238
- # cross-applicable specifications.
239
- #
240
- # These are best described in RFC 2616 3.3.1. This RFC text also
241
- # specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
242
- # fixed length format with space-date delimited fields.
243
- #
244
- # See also RFC 1123 section 5.2.14.
245
- #
246
- # RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
247
- # in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
248
- # the space delimited format. These formats are compliant with RFC 2822.
249
- #
250
- # For reference, all involved RFCs are:
251
- # RFC 822
252
- # RFC 1123
253
- # RFC 2109
254
- # RFC 2616
255
- # RFC 2822
256
- # RFC 2965
257
- # RFC 6265
258
- expires = "; expires=" +
259
- rfc2822(value[:expires].clone.gmtime) if value[:expires]
243
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
260
244
  secure = "; secure" if value[:secure]
261
245
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
262
246
  same_site =
@@ -264,11 +248,11 @@ module Rack
264
248
  when false, nil
265
249
  nil
266
250
  when :none, 'None', :None
267
- '; SameSite=None'.freeze
251
+ '; SameSite=None'
268
252
  when :lax, 'Lax', :Lax
269
- '; SameSite=Lax'.freeze
253
+ '; SameSite=Lax'
270
254
  when true, :strict, 'Strict', :Strict
271
- '; SameSite=Strict'.freeze
255
+ '; SameSite=Strict'
272
256
  else
273
257
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
274
258
  end
@@ -290,13 +274,11 @@ module Rack
290
274
  raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
291
275
  end
292
276
  end
293
- module_function :add_cookie_to_header
294
277
 
295
278
  def set_cookie_header!(header, key, value)
296
279
  header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
297
280
  nil
298
281
  end
299
- module_function :set_cookie_header!
300
282
 
301
283
  def make_delete_cookie_header(header, key, value)
302
284
  case header
@@ -308,25 +290,30 @@ module Rack
308
290
  cookies = header
309
291
  end
310
292
 
311
- cookies.reject! { |cookie|
312
- if value[:domain]
313
- cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
314
- elsif value[:path]
315
- cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
316
- else
317
- cookie =~ /\A#{escape(key)}=/
318
- end
319
- }
293
+ key = escape(key)
294
+ domain = value[:domain]
295
+ path = value[:path]
296
+ regexp = if domain
297
+ if path
298
+ /\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
299
+ else
300
+ /\A#{key}=.*domain=#{domain}(?:;|$)/
301
+ end
302
+ elsif path
303
+ /\A#{key}=.*path=#{path}(?:;|$)/
304
+ else
305
+ /\A#{key}=/
306
+ end
307
+
308
+ cookies.reject! { |cookie| regexp.match? cookie }
320
309
 
321
310
  cookies.join("\n")
322
311
  end
323
- module_function :make_delete_cookie_header
324
312
 
325
313
  def delete_cookie_header!(header, key, value = {})
326
314
  header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
327
315
  nil
328
316
  end
329
- module_function :delete_cookie_header!
330
317
 
331
318
  # Adds a cookie that will *remove* a cookie from the client. Hence the
332
319
  # strange method name.
@@ -334,17 +321,15 @@ module Rack
334
321
  new_header = make_delete_cookie_header(header, key, value)
335
322
 
336
323
  add_cookie_to_header(new_header, key,
337
- {:value => '', :path => nil, :domain => nil,
338
- :max_age => '0',
339
- :expires => Time.at(0) }.merge(value))
324
+ { value: '', path: nil, domain: nil,
325
+ max_age: '0',
326
+ expires: Time.at(0) }.merge(value))
340
327
 
341
328
  end
342
- module_function :add_remove_cookie_to_header
343
329
 
344
330
  def rfc2822(time)
345
331
  time.rfc2822
346
332
  end
347
- module_function :rfc2822
348
333
 
349
334
  # Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
350
335
  # of '% %b %Y'.
@@ -356,11 +341,10 @@ module Rack
356
341
  # weekday and month.
357
342
  #
358
343
  def rfc2109(time)
359
- wday = Time::RFC2822_DAY_NAME[time.wday]
360
- mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
344
+ wday = RFC2822_DAY_NAME[time.wday]
345
+ mon = RFC2822_MONTH_NAME[time.mon - 1]
361
346
  time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
362
347
  end
363
- module_function :rfc2109
364
348
 
365
349
  # Parses the "Range:" header, if present, into an array of Range objects.
366
350
  # Returns nil if the header is missing or syntactically invalid.
@@ -369,7 +353,6 @@ module Rack
369
353
  warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
370
354
  get_byte_ranges env['HTTP_RANGE'], size
371
355
  end
372
- module_function :byte_ranges
373
356
 
374
357
  def get_byte_ranges(http_range, size)
375
358
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
@@ -392,14 +375,13 @@ module Rack
392
375
  else
393
376
  r1 = r1.to_i
394
377
  return nil if r1 < r0 # backwards range is syntactically invalid
395
- r1 = size-1 if r1 >= size
378
+ r1 = size - 1 if r1 >= size
396
379
  end
397
380
  end
398
381
  ranges << (r0..r1) if r0 <= r1
399
382
  end
400
383
  ranges
401
384
  end
402
- module_function :get_byte_ranges
403
385
 
404
386
  # Constant time string comparison.
405
387
  #
@@ -413,10 +395,9 @@ module Rack
413
395
  l = a.unpack("C*")
414
396
 
415
397
  r, i = 0, -1
416
- b.each_byte { |v| r |= v ^ l[i+=1] }
398
+ b.each_byte { |v| r |= v ^ l[i += 1] }
417
399
  r == 0
418
400
  end
419
- module_function :secure_compare
420
401
 
421
402
  # Context allows the use of a compatible middleware at different points
422
403
  # in a request handling stack. A compatible middleware must define
@@ -439,19 +420,25 @@ module Rack
439
420
  self.class.new(@for, app)
440
421
  end
441
422
 
442
- def context(env, app=@app)
423
+ def context(env, app = @app)
443
424
  recontext(app).call(env)
444
425
  end
445
426
  end
446
427
 
447
428
  # A case-insensitive Hash that preserves the original case of a
448
429
  # header when set.
449
- class HeaderHash < Hash
450
- def self.new(hash={})
451
- HeaderHash === hash ? hash : super(hash)
430
+ #
431
+ # @api private
432
+ class HeaderHash < Hash # :nodoc:
433
+ def self.[](headers)
434
+ if headers.is_a?(HeaderHash) && !headers.frozen?
435
+ return headers
436
+ else
437
+ return self.new(headers)
438
+ end
452
439
  end
453
440
 
454
- def initialize(hash={})
441
+ def initialize(hash = {})
455
442
  super()
456
443
  @names = {}
457
444
  hash.each { |k, v| self[k] = v }
@@ -463,6 +450,12 @@ module Rack
463
450
  @names = other.names.dup
464
451
  end
465
452
 
453
+ # on clear, we need to clear @names hash
454
+ def clear
455
+ super
456
+ @names.clear
457
+ end
458
+
466
459
  def each
467
460
  super do |k, v|
468
461
  yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
@@ -471,7 +464,7 @@ module Rack
471
464
 
472
465
  def to_hash
473
466
  hash = {}
474
- each { |k,v| hash[k] = v }
467
+ each { |k, v| hash[k] = v }
475
468
  hash
476
469
  end
477
470
 
@@ -524,13 +517,14 @@ module Rack
524
517
 
525
518
  # Every standard HTTP code mapped to the appropriate message.
526
519
  # Generated with:
527
- # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
528
- # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
529
- # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
520
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
521
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
522
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
530
523
  HTTP_STATUS_CODES = {
531
524
  100 => 'Continue',
532
525
  101 => 'Switching Protocols',
533
526
  102 => 'Processing',
527
+ 103 => 'Early Hints',
534
528
  200 => 'OK',
535
529
  201 => 'Created',
536
530
  202 => 'Accepted',
@@ -547,6 +541,7 @@ module Rack
547
541
  303 => 'See Other',
548
542
  304 => 'Not Modified',
549
543
  305 => 'Use Proxy',
544
+ 306 => '(Unused)',
550
545
  307 => 'Temporary Redirect',
551
546
  308 => 'Permanent Redirect',
552
547
  400 => 'Bad Request',
@@ -571,6 +566,7 @@ module Rack
571
566
  422 => 'Unprocessable Entity',
572
567
  423 => 'Locked',
573
568
  424 => 'Failed Dependency',
569
+ 425 => 'Too Early',
574
570
  426 => 'Upgrade Required',
575
571
  428 => 'Precondition Required',
576
572
  429 => 'Too Many Requests',
@@ -585,12 +581,13 @@ module Rack
585
581
  506 => 'Variant Also Negotiates',
586
582
  507 => 'Insufficient Storage',
587
583
  508 => 'Loop Detected',
584
+ 509 => 'Bandwidth Limit Exceeded',
588
585
  510 => 'Not Extended',
589
586
  511 => 'Network Authentication Required'
590
587
  }
591
588
 
592
589
  # Responses with HTTP status codes that should not have an entity body
593
- STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
590
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
594
591
 
595
592
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
596
593
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -598,12 +595,11 @@ module Rack
598
595
 
599
596
  def status_code(status)
600
597
  if status.is_a?(Symbol)
601
- SYMBOL_TO_STATUS_CODE[status] || 500
598
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
602
599
  else
603
600
  status.to_i
604
601
  end
605
602
  end
606
- module_function :status_code
607
603
 
608
604
  PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
609
605
 
@@ -617,18 +613,16 @@ module Rack
617
613
  part == '..' ? clean.pop : clean << part
618
614
  end
619
615
 
620
- clean.unshift '/' if parts.empty? || parts.first.empty?
621
-
622
- ::File.join(*clean)
616
+ clean_path = clean.join(::File::SEPARATOR)
617
+ clean_path.prepend("/") if parts.empty? || parts.first.empty?
618
+ clean_path
623
619
  end
624
- module_function :clean_path_info
625
620
 
626
- NULL_BYTE = "\0".freeze
621
+ NULL_BYTE = "\0"
627
622
 
628
623
  def valid_path?(path)
629
624
  path.valid_encoding? && !path.include?(NULL_BYTE)
630
625
  end
631
- module_function :valid_path?
632
626
 
633
627
  end
634
628
  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.8"
24
+
25
+ # Return the Rack release as a dotted string.
26
+ def self.release
27
+ RELEASE
28
+ end
29
+ end