rack 2.0.9.3 → 2.2.0

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 +675 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -162
  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 -28
  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 +5 -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 +17 -11
  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 +58 -73
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +7 -4
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +53 -28
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +210 -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 +33 -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 +17 -13
  70. data/lib/rack/show_status.rb +5 -5
  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 +105 -130
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -28
  78. metadata +39 -182
  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,16 +1,21 @@
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
@@ -24,53 +29,39 @@ module Rack
24
29
  # This helps prevent a rogue client from flooding a Request.
25
30
  self.default_query_parser = QueryParser.make_default(65536, 100)
26
31
 
32
+ module_function
33
+
27
34
  # URI escapes. (CGI style space to +)
28
35
  def escape(s)
29
36
  URI.encode_www_form_component(s)
30
37
  end
31
- module_function :escape
32
38
 
33
39
  # Like URI escaping, but with %20 instead of +. Strictly speaking this is
34
40
  # true URI escaping.
35
41
  def escape_path(s)
36
42
  ::URI::DEFAULT_PARSER.escape s
37
43
  end
38
- module_function :escape_path
39
44
 
40
45
  # Unescapes the **path** component of a URI. See Rack::Utils.unescape for
41
46
  # unescaping query parameters or form components.
42
47
  def unescape_path(s)
43
48
  ::URI::DEFAULT_PARSER.unescape s
44
49
  end
45
- module_function :unescape_path
46
-
47
50
 
48
51
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
49
52
  # target encoding of the string returned, and it defaults to UTF-8
50
53
  def unescape(s, encoding = Encoding::UTF_8)
51
54
  URI.decode_www_form_component(s, encoding)
52
55
  end
53
- module_function :unescape
54
56
 
55
57
  class << self
56
- attr_accessor :multipart_total_part_limit
57
-
58
- attr_accessor :multipart_file_limit
59
-
60
- # multipart_part_limit is the original name of multipart_file_limit, but
61
- # the limit only counts parts with filenames.
62
- alias multipart_part_limit multipart_file_limit
63
- alias multipart_part_limit= multipart_file_limit=
58
+ attr_accessor :multipart_part_limit
64
59
  end
65
60
 
66
- # The maximum number of file parts a request can contain. Accepting too
67
- # many parts can lead to the server running out of file handles.
61
+ # The maximum number of parts a request can contain. Accepting too many part
62
+ # can lead to the server running out of file handles.
68
63
  # Set to `0` for no limit.
69
- self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
70
-
71
- # The maximum total number of parts a request can contain. Accepting too
72
- # many can lead to excessive memory use and parsing time.
73
- self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
64
+ self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
74
65
 
75
66
  def self.param_depth_limit
76
67
  default_query_parser.param_depth_limit
@@ -93,21 +84,20 @@ module Rack
93
84
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
94
85
  end
95
86
  else
87
+ # :nocov:
96
88
  def clock_time
97
89
  Time.now.to_f
98
90
  end
91
+ # :nocov:
99
92
  end
100
- module_function :clock_time
101
93
 
102
94
  def parse_query(qs, d = nil, &unescaper)
103
95
  Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
104
96
  end
105
- module_function :parse_query
106
97
 
107
98
  def parse_nested_query(qs, d = nil)
108
99
  Rack::Utils.default_query_parser.parse_nested_query(qs, d)
109
100
  end
110
- module_function :parse_nested_query
111
101
 
112
102
  def build_query(params)
113
103
  params.map { |k, v|
@@ -118,7 +108,6 @@ module Rack
118
108
  end
119
109
  }.join("&")
120
110
  end
121
- module_function :build_query
122
111
 
123
112
  def build_nested_query(value, prefix = nil)
124
113
  case value
@@ -129,7 +118,7 @@ module Rack
129
118
  when Hash
130
119
  value.map { |k, v|
131
120
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
132
- }.reject(&:empty?).join('&')
121
+ }.delete_if(&:empty?).join('&')
133
122
  when nil
134
123
  prefix
135
124
  else
@@ -137,20 +126,22 @@ module Rack
137
126
  "#{prefix}=#{escape(value)}"
138
127
  end
139
128
  end
140
- module_function :build_nested_query
141
129
 
142
130
  def q_values(q_value_header)
143
131
  q_value_header.to_s.split(/\s*,\s*/).map do |part|
144
132
  value, parameters = part.split(/\s*;\s*/, 2)
145
133
  quality = 1.0
146
- if md = /\Aq=([\d.]+)/.match(parameters)
134
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
147
135
  quality = md[1].to_f
148
136
  end
149
137
  [value, quality]
150
138
  end
151
139
  end
152
- module_function :q_values
153
140
 
141
+ # Return best accept value to use, based on the algorithm
142
+ # in RFC 2616 Section 14. If there are multiple best
143
+ # matches (same specificity and quality), the value returned
144
+ # is arbitrary.
154
145
  def best_q_match(q_value_header, available_mimes)
155
146
  values = q_values(q_value_header)
156
147
 
@@ -163,7 +154,6 @@ module Rack
163
154
  end.last
164
155
  matches && matches.first
165
156
  end
166
- module_function :best_q_match
167
157
 
168
158
  ESCAPE_HTML = {
169
159
  "&" => "&amp;",
@@ -180,51 +170,51 @@ module Rack
180
170
  def escape_html(string)
181
171
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
182
172
  end
183
- module_function :escape_html
184
173
 
185
174
  def select_best_encoding(available_encodings, accept_encoding)
186
175
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
187
176
 
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]]
177
+ expanded_accept_encoding = []
178
+
179
+ accept_encoding.each do |m, q|
180
+ preference = available_encodings.index(m) || available_encodings.size
181
+
182
+ if m == "*"
183
+ (available_encodings - accept_encoding.map(&:first)).each do |m2|
184
+ expanded_accept_encoding << [m2, q, preference]
194
185
  end
195
- }.inject([]) { |mem, list|
196
- mem + list
197
- }
186
+ else
187
+ expanded_accept_encoding << [m, q, preference]
188
+ end
189
+ end
198
190
 
199
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
191
+ encoding_candidates = expanded_accept_encoding
192
+ .sort_by { |_, q, p| [-q, p] }
193
+ .map!(&:first)
200
194
 
201
195
  unless encoding_candidates.include?("identity")
202
196
  encoding_candidates.push("identity")
203
197
  end
204
198
 
205
- expanded_accept_encoding.each { |m, q|
199
+ expanded_accept_encoding.each do |m, q|
206
200
  encoding_candidates.delete(m) if q == 0.0
207
- }
201
+ end
208
202
 
209
- return (encoding_candidates & available_encodings)[0]
203
+ (encoding_candidates & available_encodings)[0]
210
204
  end
211
- module_function :select_best_encoding
212
205
 
213
206
  def parse_cookies(env)
214
207
  parse_cookies_header env[HTTP_COOKIE]
215
208
  end
216
- module_function :parse_cookies
217
209
 
218
210
  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 }
211
+ # According to RFC 6265:
212
+ # The syntax for cookie headers only supports semicolons
213
+ # User Agent -> Server ==
214
+ # Cookie: SID=31d4d96e407aad42; lang=en-US
215
+ cookies = parse_query(header, ';') { |s| unescape(s) rescue s }
216
+ cookies.each_with_object({}) { |(k, v), hash| hash[k] = Array === v ? v.first : v }
226
217
  end
227
- module_function :parse_cookies_header
228
218
 
229
219
  def add_cookie_to_header(header, key, value)
230
220
  case value
@@ -232,31 +222,7 @@ module Rack
232
222
  domain = "; domain=#{value[:domain]}" if value[:domain]
233
223
  path = "; path=#{value[:path]}" if value[:path]
234
224
  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]
225
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
260
226
  secure = "; secure" if value[:secure]
261
227
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
262
228
  same_site =
@@ -264,11 +230,11 @@ module Rack
264
230
  when false, nil
265
231
  nil
266
232
  when :none, 'None', :None
267
- '; SameSite=None'.freeze
233
+ '; SameSite=None'
268
234
  when :lax, 'Lax', :Lax
269
- '; SameSite=Lax'.freeze
235
+ '; SameSite=Lax'
270
236
  when true, :strict, 'Strict', :Strict
271
- '; SameSite=Strict'.freeze
237
+ '; SameSite=Strict'
272
238
  else
273
239
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
274
240
  end
@@ -290,13 +256,11 @@ module Rack
290
256
  raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
291
257
  end
292
258
  end
293
- module_function :add_cookie_to_header
294
259
 
295
260
  def set_cookie_header!(header, key, value)
296
261
  header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
297
262
  nil
298
263
  end
299
- module_function :set_cookie_header!
300
264
 
301
265
  def make_delete_cookie_header(header, key, value)
302
266
  case header
@@ -308,25 +272,30 @@ module Rack
308
272
  cookies = header
309
273
  end
310
274
 
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
- }
275
+ key = escape(key)
276
+ domain = value[:domain]
277
+ path = value[:path]
278
+ regexp = if domain
279
+ if path
280
+ /\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
281
+ else
282
+ /\A#{key}=.*domain=#{domain}(?:;|$)/
283
+ end
284
+ elsif path
285
+ /\A#{key}=.*path=#{path}(?:;|$)/
286
+ else
287
+ /\A#{key}=/
288
+ end
289
+
290
+ cookies.reject! { |cookie| regexp.match? cookie }
320
291
 
321
292
  cookies.join("\n")
322
293
  end
323
- module_function :make_delete_cookie_header
324
294
 
325
295
  def delete_cookie_header!(header, key, value = {})
326
296
  header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
327
297
  nil
328
298
  end
329
- module_function :delete_cookie_header!
330
299
 
331
300
  # Adds a cookie that will *remove* a cookie from the client. Hence the
332
301
  # strange method name.
@@ -334,17 +303,15 @@ module Rack
334
303
  new_header = make_delete_cookie_header(header, key, value)
335
304
 
336
305
  add_cookie_to_header(new_header, key,
337
- {:value => '', :path => nil, :domain => nil,
338
- :max_age => '0',
339
- :expires => Time.at(0) }.merge(value))
306
+ { value: '', path: nil, domain: nil,
307
+ max_age: '0',
308
+ expires: Time.at(0) }.merge(value))
340
309
 
341
310
  end
342
- module_function :add_remove_cookie_to_header
343
311
 
344
312
  def rfc2822(time)
345
313
  time.rfc2822
346
314
  end
347
- module_function :rfc2822
348
315
 
349
316
  # Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
350
317
  # of '% %b %Y'.
@@ -360,7 +327,6 @@ module Rack
360
327
  mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
361
328
  time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
362
329
  end
363
- module_function :rfc2109
364
330
 
365
331
  # Parses the "Range:" header, if present, into an array of Range objects.
366
332
  # Returns nil if the header is missing or syntactically invalid.
@@ -369,37 +335,34 @@ module Rack
369
335
  warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
370
336
  get_byte_ranges env['HTTP_RANGE'], size
371
337
  end
372
- module_function :byte_ranges
373
338
 
374
339
  def get_byte_ranges(http_range, size)
375
340
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
376
341
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
377
342
  ranges = []
378
343
  $1.split(/,\s*/).each do |range_spec|
379
- return nil unless range_spec.include?('-')
380
- range = range_spec.split('-')
381
- r0, r1 = range[0], range[1]
382
- if r0.nil? || r0.empty?
383
- return nil if r1.nil?
344
+ return nil unless range_spec =~ /(\d*)-(\d*)/
345
+ r0, r1 = $1, $2
346
+ if r0.empty?
347
+ return nil if r1.empty?
384
348
  # suffix-byte-range-spec, represents trailing suffix of file
385
349
  r0 = size - r1.to_i
386
350
  r0 = 0 if r0 < 0
387
351
  r1 = size - 1
388
352
  else
389
353
  r0 = r0.to_i
390
- if r1.nil?
354
+ if r1.empty?
391
355
  r1 = size - 1
392
356
  else
393
357
  r1 = r1.to_i
394
358
  return nil if r1 < r0 # backwards range is syntactically invalid
395
- r1 = size-1 if r1 >= size
359
+ r1 = size - 1 if r1 >= size
396
360
  end
397
361
  end
398
362
  ranges << (r0..r1) if r0 <= r1
399
363
  end
400
364
  ranges
401
365
  end
402
- module_function :get_byte_ranges
403
366
 
404
367
  # Constant time string comparison.
405
368
  #
@@ -413,10 +376,9 @@ module Rack
413
376
  l = a.unpack("C*")
414
377
 
415
378
  r, i = 0, -1
416
- b.each_byte { |v| r |= v ^ l[i+=1] }
379
+ b.each_byte { |v| r |= v ^ l[i += 1] }
417
380
  r == 0
418
381
  end
419
- module_function :secure_compare
420
382
 
421
383
  # Context allows the use of a compatible middleware at different points
422
384
  # in a request handling stack. A compatible middleware must define
@@ -439,19 +401,25 @@ module Rack
439
401
  self.class.new(@for, app)
440
402
  end
441
403
 
442
- def context(env, app=@app)
404
+ def context(env, app = @app)
443
405
  recontext(app).call(env)
444
406
  end
445
407
  end
446
408
 
447
409
  # A case-insensitive Hash that preserves the original case of a
448
410
  # header when set.
449
- class HeaderHash < Hash
450
- def self.new(hash={})
451
- HeaderHash === hash ? hash : super(hash)
411
+ #
412
+ # @api private
413
+ class HeaderHash < Hash # :nodoc:
414
+ def self.[](headers)
415
+ if headers.is_a?(HeaderHash) && !headers.frozen?
416
+ return headers
417
+ else
418
+ return self.new(headers)
419
+ end
452
420
  end
453
421
 
454
- def initialize(hash={})
422
+ def initialize(hash = {})
455
423
  super()
456
424
  @names = {}
457
425
  hash.each { |k, v| self[k] = v }
@@ -463,6 +431,12 @@ module Rack
463
431
  @names = other.names.dup
464
432
  end
465
433
 
434
+ # on clear, we need to clear @names hash
435
+ def clear
436
+ super
437
+ @names.clear
438
+ end
439
+
466
440
  def each
467
441
  super do |k, v|
468
442
  yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
@@ -471,7 +445,7 @@ module Rack
471
445
 
472
446
  def to_hash
473
447
  hash = {}
474
- each { |k,v| hash[k] = v }
448
+ each { |k, v| hash[k] = v }
475
449
  hash
476
450
  end
477
451
 
@@ -524,13 +498,14 @@ module Rack
524
498
 
525
499
  # Every standard HTTP code mapped to the appropriate message.
526
500
  # 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,"'
501
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
502
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
503
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
530
504
  HTTP_STATUS_CODES = {
531
505
  100 => 'Continue',
532
506
  101 => 'Switching Protocols',
533
507
  102 => 'Processing',
508
+ 103 => 'Early Hints',
534
509
  200 => 'OK',
535
510
  201 => 'Created',
536
511
  202 => 'Accepted',
@@ -547,6 +522,7 @@ module Rack
547
522
  303 => 'See Other',
548
523
  304 => 'Not Modified',
549
524
  305 => 'Use Proxy',
525
+ 306 => '(Unused)',
550
526
  307 => 'Temporary Redirect',
551
527
  308 => 'Permanent Redirect',
552
528
  400 => 'Bad Request',
@@ -571,6 +547,7 @@ module Rack
571
547
  422 => 'Unprocessable Entity',
572
548
  423 => 'Locked',
573
549
  424 => 'Failed Dependency',
550
+ 425 => 'Too Early',
574
551
  426 => 'Upgrade Required',
575
552
  428 => 'Precondition Required',
576
553
  429 => 'Too Many Requests',
@@ -585,12 +562,13 @@ module Rack
585
562
  506 => 'Variant Also Negotiates',
586
563
  507 => 'Insufficient Storage',
587
564
  508 => 'Loop Detected',
565
+ 509 => 'Bandwidth Limit Exceeded',
588
566
  510 => 'Not Extended',
589
567
  511 => 'Network Authentication Required'
590
568
  }
591
569
 
592
570
  # 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)
571
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
594
572
 
595
573
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
596
574
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -598,12 +576,11 @@ module Rack
598
576
 
599
577
  def status_code(status)
600
578
  if status.is_a?(Symbol)
601
- SYMBOL_TO_STATUS_CODE[status] || 500
579
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
602
580
  else
603
581
  status.to_i
604
582
  end
605
583
  end
606
- module_function :status_code
607
584
 
608
585
  PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
609
586
 
@@ -617,18 +594,16 @@ module Rack
617
594
  part == '..' ? clean.pop : clean << part
618
595
  end
619
596
 
620
- clean.unshift '/' if parts.empty? || parts.first.empty?
621
-
622
- ::File.join(*clean)
597
+ clean_path = clean.join(::File::SEPARATOR)
598
+ clean_path.prepend("/") if parts.empty? || parts.first.empty?
599
+ clean_path
623
600
  end
624
- module_function :clean_path_info
625
601
 
626
- NULL_BYTE = "\0".freeze
602
+ NULL_BYTE = "\0"
627
603
 
628
604
  def valid_path?(path)
629
605
  path.valid_encoding? && !path.include?(NULL_BYTE)
630
606
  end
631
- module_function :valid_path?
632
607
 
633
608
  end
634
609
  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.0"
24
+
25
+ # Return the Rack release as a dotted string.
26
+ def self.release
27
+ RELEASE
28
+ end
29
+ end