rack 2.0.6 → 2.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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +735 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +163 -145
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +29 -5
  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 +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 +74 -67
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +6 -5
  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 +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 +13 -7
  74. data/lib/rack/utils.rb +127 -119
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -28
  78. metadata +39 -181
  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 -1398
  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,42 +32,50 @@ 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
- attr_accessor :multipart_part_limit
61
+ attr_accessor :multipart_total_part_limit
62
+
63
+ attr_accessor :multipart_file_limit
64
+
65
+ # multipart_part_limit is the original name of multipart_file_limit, but
66
+ # the limit only counts parts with filenames.
67
+ alias multipart_part_limit multipart_file_limit
68
+ alias multipart_part_limit= multipart_file_limit=
57
69
  end
58
70
 
59
- # The maximum number of parts a request can contain. Accepting too many part
60
- # can lead to the server running out of file handles.
71
+ # The maximum number of file parts a request can contain. Accepting too
72
+ # many parts can lead to the server running out of file handles.
61
73
  # Set to `0` for no limit.
62
- self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
74
+ self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
75
+
76
+ # The maximum total number of parts a request can contain. Accepting too
77
+ # many can lead to excessive memory use and parsing time.
78
+ self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
63
79
 
64
80
  def self.param_depth_limit
65
81
  default_query_parser.param_depth_limit
@@ -82,21 +98,20 @@ module Rack
82
98
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
83
99
  end
84
100
  else
101
+ # :nocov:
85
102
  def clock_time
86
103
  Time.now.to_f
87
104
  end
105
+ # :nocov:
88
106
  end
89
- module_function :clock_time
90
107
 
91
108
  def parse_query(qs, d = nil, &unescaper)
92
109
  Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
93
110
  end
94
- module_function :parse_query
95
111
 
96
112
  def parse_nested_query(qs, d = nil)
97
113
  Rack::Utils.default_query_parser.parse_nested_query(qs, d)
98
114
  end
99
- module_function :parse_nested_query
100
115
 
101
116
  def build_query(params)
102
117
  params.map { |k, v|
@@ -107,7 +122,6 @@ module Rack
107
122
  end
108
123
  }.join("&")
109
124
  end
110
- module_function :build_query
111
125
 
112
126
  def build_nested_query(value, prefix = nil)
113
127
  case value
@@ -118,7 +132,7 @@ module Rack
118
132
  when Hash
119
133
  value.map { |k, v|
120
134
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
121
- }.reject(&:empty?).join('&')
135
+ }.delete_if(&:empty?).join('&')
122
136
  when nil
123
137
  prefix
124
138
  else
@@ -126,20 +140,22 @@ module Rack
126
140
  "#{prefix}=#{escape(value)}"
127
141
  end
128
142
  end
129
- module_function :build_nested_query
130
143
 
131
144
  def q_values(q_value_header)
132
145
  q_value_header.to_s.split(/\s*,\s*/).map do |part|
133
146
  value, parameters = part.split(/\s*;\s*/, 2)
134
147
  quality = 1.0
135
- if md = /\Aq=([\d.]+)/.match(parameters)
148
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
136
149
  quality = md[1].to_f
137
150
  end
138
151
  [value, quality]
139
152
  end
140
153
  end
141
- module_function :q_values
142
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.
143
159
  def best_q_match(q_value_header, available_mimes)
144
160
  values = q_values(q_value_header)
145
161
 
@@ -152,7 +168,6 @@ module Rack
152
168
  end.last
153
169
  matches && matches.first
154
170
  end
155
- module_function :best_q_match
156
171
 
157
172
  ESCAPE_HTML = {
158
173
  "&" => "&amp;",
@@ -169,51 +184,55 @@ module Rack
169
184
  def escape_html(string)
170
185
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
171
186
  end
172
- module_function :escape_html
173
187
 
174
188
  def select_best_encoding(available_encodings, accept_encoding)
175
189
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
176
190
 
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]]
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]
183
199
  end
184
- }.inject([]) { |mem, list|
185
- mem + list
186
- }
200
+ else
201
+ expanded_accept_encoding << [m, q, preference]
202
+ end
203
+ end
187
204
 
188
- 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)
189
208
 
190
209
  unless encoding_candidates.include?("identity")
191
210
  encoding_candidates.push("identity")
192
211
  end
193
212
 
194
- expanded_accept_encoding.each { |m, q|
213
+ expanded_accept_encoding.each do |m, q|
195
214
  encoding_candidates.delete(m) if q == 0.0
196
- }
215
+ end
197
216
 
198
- return (encoding_candidates & available_encodings)[0]
217
+ (encoding_candidates & available_encodings)[0]
199
218
  end
200
- module_function :select_best_encoding
201
219
 
202
220
  def parse_cookies(env)
203
221
  parse_cookies_header env[HTTP_COOKIE]
204
222
  end
205
- module_function :parse_cookies
206
223
 
207
224
  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 }
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
215
235
  end
216
- module_function :parse_cookies_header
217
236
 
218
237
  def add_cookie_to_header(header, key, value)
219
238
  case value
@@ -221,41 +240,19 @@ module Rack
221
240
  domain = "; domain=#{value[:domain]}" if value[:domain]
222
241
  path = "; path=#{value[:path]}" if value[:path]
223
242
  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]
243
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
249
244
  secure = "; secure" if value[:secure]
250
245
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
251
246
  same_site =
252
247
  case value[:same_site]
253
248
  when false, nil
254
249
  nil
250
+ when :none, 'None', :None
251
+ '; SameSite=None'
255
252
  when :lax, 'Lax', :Lax
256
- '; SameSite=Lax'.freeze
253
+ '; SameSite=Lax'
257
254
  when true, :strict, 'Strict', :Strict
258
- '; SameSite=Strict'.freeze
255
+ '; SameSite=Strict'
259
256
  else
260
257
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
261
258
  end
@@ -277,13 +274,11 @@ module Rack
277
274
  raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
278
275
  end
279
276
  end
280
- module_function :add_cookie_to_header
281
277
 
282
278
  def set_cookie_header!(header, key, value)
283
279
  header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
284
280
  nil
285
281
  end
286
- module_function :set_cookie_header!
287
282
 
288
283
  def make_delete_cookie_header(header, key, value)
289
284
  case header
@@ -295,25 +290,30 @@ module Rack
295
290
  cookies = header
296
291
  end
297
292
 
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
- }
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 }
307
309
 
308
310
  cookies.join("\n")
309
311
  end
310
- module_function :make_delete_cookie_header
311
312
 
312
313
  def delete_cookie_header!(header, key, value = {})
313
314
  header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
314
315
  nil
315
316
  end
316
- module_function :delete_cookie_header!
317
317
 
318
318
  # Adds a cookie that will *remove* a cookie from the client. Hence the
319
319
  # strange method name.
@@ -321,17 +321,15 @@ module Rack
321
321
  new_header = make_delete_cookie_header(header, key, value)
322
322
 
323
323
  add_cookie_to_header(new_header, key,
324
- {:value => '', :path => nil, :domain => nil,
325
- :max_age => '0',
326
- :expires => Time.at(0) }.merge(value))
324
+ { value: '', path: nil, domain: nil,
325
+ max_age: '0',
326
+ expires: Time.at(0) }.merge(value))
327
327
 
328
328
  end
329
- module_function :add_remove_cookie_to_header
330
329
 
331
330
  def rfc2822(time)
332
331
  time.rfc2822
333
332
  end
334
- module_function :rfc2822
335
333
 
336
334
  # Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
337
335
  # of '% %b %Y'.
@@ -343,11 +341,10 @@ module Rack
343
341
  # weekday and month.
344
342
  #
345
343
  def rfc2109(time)
346
- wday = Time::RFC2822_DAY_NAME[time.wday]
347
- mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
344
+ wday = RFC2822_DAY_NAME[time.wday]
345
+ mon = RFC2822_MONTH_NAME[time.mon - 1]
348
346
  time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
349
347
  end
350
- module_function :rfc2109
351
348
 
352
349
  # Parses the "Range:" header, if present, into an array of Range objects.
353
350
  # Returns nil if the header is missing or syntactically invalid.
@@ -356,36 +353,35 @@ module Rack
356
353
  warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
357
354
  get_byte_ranges env['HTTP_RANGE'], size
358
355
  end
359
- module_function :byte_ranges
360
356
 
361
357
  def get_byte_ranges(http_range, size)
362
358
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
363
359
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
364
360
  ranges = []
365
361
  $1.split(/,\s*/).each do |range_spec|
366
- return nil unless range_spec =~ /(\d*)-(\d*)/
367
- r0,r1 = $1, $2
368
- if r0.empty?
369
- return nil if r1.empty?
362
+ return nil unless range_spec.include?('-')
363
+ range = range_spec.split('-')
364
+ r0, r1 = range[0], range[1]
365
+ if r0.nil? || r0.empty?
366
+ return nil if r1.nil?
370
367
  # suffix-byte-range-spec, represents trailing suffix of file
371
368
  r0 = size - r1.to_i
372
369
  r0 = 0 if r0 < 0
373
370
  r1 = size - 1
374
371
  else
375
372
  r0 = r0.to_i
376
- if r1.empty?
373
+ if r1.nil?
377
374
  r1 = size - 1
378
375
  else
379
376
  r1 = r1.to_i
380
377
  return nil if r1 < r0 # backwards range is syntactically invalid
381
- r1 = size-1 if r1 >= size
378
+ r1 = size - 1 if r1 >= size
382
379
  end
383
380
  end
384
381
  ranges << (r0..r1) if r0 <= r1
385
382
  end
386
383
  ranges
387
384
  end
388
- module_function :get_byte_ranges
389
385
 
390
386
  # Constant time string comparison.
391
387
  #
@@ -399,10 +395,9 @@ module Rack
399
395
  l = a.unpack("C*")
400
396
 
401
397
  r, i = 0, -1
402
- b.each_byte { |v| r |= v ^ l[i+=1] }
398
+ b.each_byte { |v| r |= v ^ l[i += 1] }
403
399
  r == 0
404
400
  end
405
- module_function :secure_compare
406
401
 
407
402
  # Context allows the use of a compatible middleware at different points
408
403
  # in a request handling stack. A compatible middleware must define
@@ -425,19 +420,25 @@ module Rack
425
420
  self.class.new(@for, app)
426
421
  end
427
422
 
428
- def context(env, app=@app)
423
+ def context(env, app = @app)
429
424
  recontext(app).call(env)
430
425
  end
431
426
  end
432
427
 
433
428
  # A case-insensitive Hash that preserves the original case of a
434
429
  # header when set.
435
- class HeaderHash < Hash
436
- def self.new(hash={})
437
- 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
438
439
  end
439
440
 
440
- def initialize(hash={})
441
+ def initialize(hash = {})
441
442
  super()
442
443
  @names = {}
443
444
  hash.each { |k, v| self[k] = v }
@@ -449,6 +450,12 @@ module Rack
449
450
  @names = other.names.dup
450
451
  end
451
452
 
453
+ # on clear, we need to clear @names hash
454
+ def clear
455
+ super
456
+ @names.clear
457
+ end
458
+
452
459
  def each
453
460
  super do |k, v|
454
461
  yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
@@ -457,7 +464,7 @@ module Rack
457
464
 
458
465
  def to_hash
459
466
  hash = {}
460
- each { |k,v| hash[k] = v }
467
+ each { |k, v| hash[k] = v }
461
468
  hash
462
469
  end
463
470
 
@@ -510,13 +517,14 @@ module Rack
510
517
 
511
518
  # Every standard HTTP code mapped to the appropriate message.
512
519
  # 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,"'
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,"'
516
523
  HTTP_STATUS_CODES = {
517
524
  100 => 'Continue',
518
525
  101 => 'Switching Protocols',
519
526
  102 => 'Processing',
527
+ 103 => 'Early Hints',
520
528
  200 => 'OK',
521
529
  201 => 'Created',
522
530
  202 => 'Accepted',
@@ -533,6 +541,7 @@ module Rack
533
541
  303 => 'See Other',
534
542
  304 => 'Not Modified',
535
543
  305 => 'Use Proxy',
544
+ 306 => '(Unused)',
536
545
  307 => 'Temporary Redirect',
537
546
  308 => 'Permanent Redirect',
538
547
  400 => 'Bad Request',
@@ -557,6 +566,7 @@ module Rack
557
566
  422 => 'Unprocessable Entity',
558
567
  423 => 'Locked',
559
568
  424 => 'Failed Dependency',
569
+ 425 => 'Too Early',
560
570
  426 => 'Upgrade Required',
561
571
  428 => 'Precondition Required',
562
572
  429 => 'Too Many Requests',
@@ -571,12 +581,13 @@ module Rack
571
581
  506 => 'Variant Also Negotiates',
572
582
  507 => 'Insufficient Storage',
573
583
  508 => 'Loop Detected',
584
+ 509 => 'Bandwidth Limit Exceeded',
574
585
  510 => 'Not Extended',
575
586
  511 => 'Network Authentication Required'
576
587
  }
577
588
 
578
589
  # 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)
590
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
580
591
 
581
592
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
582
593
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -584,12 +595,11 @@ module Rack
584
595
 
585
596
  def status_code(status)
586
597
  if status.is_a?(Symbol)
587
- SYMBOL_TO_STATUS_CODE[status] || 500
598
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
588
599
  else
589
600
  status.to_i
590
601
  end
591
602
  end
592
- module_function :status_code
593
603
 
594
604
  PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
595
605
 
@@ -603,18 +613,16 @@ module Rack
603
613
  part == '..' ? clean.pop : clean << part
604
614
  end
605
615
 
606
- clean.unshift '/' if parts.empty? || parts.first.empty?
607
-
608
- ::File.join(*clean)
616
+ clean_path = clean.join(::File::SEPARATOR)
617
+ clean_path.prepend("/") if parts.empty? || parts.first.empty?
618
+ clean_path
609
619
  end
610
- module_function :clean_path_info
611
620
 
612
- NULL_BYTE = "\0".freeze
621
+ NULL_BYTE = "\0"
613
622
 
614
623
  def valid_path?(path)
615
624
  path.valid_encoding? && !path.include?(NULL_BYTE)
616
625
  end
617
- module_function :valid_path?
618
626
 
619
627
  end
620
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.7"
24
+
25
+ # Return the Rack release as a dotted string.
26
+ def self.release
27
+ RELEASE
28
+ end
29
+ end