rack 2.0.1 → 2.2.17

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 (189) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +795 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +188 -145
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +46 -17
  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 +6 -4
  15. data/lib/rack/auth/digest/md5.rb +13 -11
  16. data/lib/rack/auth/digest/nonce.rb +5 -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 +37 -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 +60 -70
  30. data/lib/rack/directory.rb +84 -64
  31. data/lib/rack/etag.rb +8 -5
  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 +19 -10
  41. data/lib/rack/handler.rb +7 -2
  42. data/lib/rack/head.rb +1 -1
  43. data/lib/rack/lint.rb +221 -186
  44. data/lib/rack/lobster.rb +10 -10
  45. data/lib/rack/lock.rb +14 -4
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +23 -8
  48. data/lib/rack/method_override.rb +13 -4
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +135 -29
  51. data/lib/rack/multipart/generator.rb +17 -13
  52. data/lib/rack/multipart/parser.rb +85 -68
  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 +108 -36
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +232 -60
  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 +14 -10
  64. data/lib/rack/server.rb +97 -25
  65. data/lib/rack/session/abstract/id.rb +113 -25
  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 +24 -10
  69. data/lib/rack/show_exceptions.rb +22 -18
  70. data/lib/rack/show_status.rb +9 -9
  71. data/lib/rack/static.rb +25 -12
  72. data/lib/rack/tempfile_reaper.rb +1 -1
  73. data/lib/rack/urlmap.rb +13 -7
  74. data/lib/rack/utils.rb +135 -123
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -29
  78. metadata +25 -184
  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_percent_escaped_quotes +0 -6
  114. data/test/multipart/filename_with_single_quote +0 -7
  115. data/test/multipart/filename_with_unescaped_percentages +0 -6
  116. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  118. data/test/multipart/filename_with_unescaped_quotes +0 -6
  119. data/test/multipart/ie +0 -6
  120. data/test/multipart/invalid_character +0 -6
  121. data/test/multipart/mixed_files +0 -21
  122. data/test/multipart/nested +0 -10
  123. data/test/multipart/none +0 -9
  124. data/test/multipart/quoted +0 -15
  125. data/test/multipart/rack-logo.png +0 -0
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/unity3d_wwwform +0 -11
  130. data/test/multipart/webkit +0 -32
  131. data/test/rackup/config.ru +0 -31
  132. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  133. data/test/spec_auth_basic.rb +0 -89
  134. data/test/spec_auth_digest.rb +0 -260
  135. data/test/spec_body_proxy.rb +0 -85
  136. data/test/spec_builder.rb +0 -233
  137. data/test/spec_cascade.rb +0 -63
  138. data/test/spec_cgi.rb +0 -84
  139. data/test/spec_chunked.rb +0 -103
  140. data/test/spec_common_logger.rb +0 -95
  141. data/test/spec_conditional_get.rb +0 -103
  142. data/test/spec_config.rb +0 -23
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -46
  145. data/test/spec_deflater.rb +0 -365
  146. data/test/spec_directory.rb +0 -148
  147. data/test/spec_etag.rb +0 -108
  148. data/test/spec_events.rb +0 -133
  149. data/test/spec_fastcgi.rb +0 -85
  150. data/test/spec_file.rb +0 -251
  151. data/test/spec_handler.rb +0 -57
  152. data/test/spec_head.rb +0 -46
  153. data/test/spec_lint.rb +0 -515
  154. data/test/spec_lobster.rb +0 -59
  155. data/test/spec_lock.rb +0 -194
  156. data/test/spec_logger.rb +0 -24
  157. data/test/spec_media_type.rb +0 -42
  158. data/test/spec_method_override.rb +0 -83
  159. data/test/spec_mime.rb +0 -51
  160. data/test/spec_mock.rb +0 -342
  161. data/test/spec_multipart.rb +0 -716
  162. data/test/spec_null_logger.rb +0 -21
  163. data/test/spec_recursive.rb +0 -75
  164. data/test/spec_request.rb +0 -1393
  165. data/test/spec_response.rb +0 -510
  166. data/test/spec_rewindable_input.rb +0 -128
  167. data/test/spec_runtime.rb +0 -50
  168. data/test/spec_sendfile.rb +0 -125
  169. data/test/spec_server.rb +0 -193
  170. data/test/spec_session_abstract_id.rb +0 -31
  171. data/test/spec_session_abstract_session_hash.rb +0 -28
  172. data/test/spec_session_cookie.rb +0 -442
  173. data/test/spec_session_memcache.rb +0 -320
  174. data/test/spec_session_pool.rb +0 -210
  175. data/test/spec_show_exceptions.rb +0 -80
  176. data/test/spec_show_status.rb +0 -104
  177. data/test/spec_static.rb +0 -184
  178. data/test/spec_tempfile_reaper.rb +0 -64
  179. data/test/spec_thin.rb +0 -96
  180. data/test/spec_urlmap.rb +0 -237
  181. data/test/spec_utils.rb +0 -742
  182. data/test/spec_version.rb +0 -11
  183. data/test/spec_webrick.rb +0 -208
  184. data/test/static/another/index.html +0 -1
  185. data/test/static/foo.html +0 -1
  186. data/test/static/index.html +0 -1
  187. data/test/testrequest.rb +0 -78
  188. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  189. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/utils.rb CHANGED
@@ -1,22 +1,31 @@
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
+ RFC2396_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
28
+
20
29
  class << self
21
30
  attr_accessor :default_query_parser
22
31
  end
@@ -24,42 +33,50 @@ module Rack
24
33
  # This helps prevent a rogue client from flooding a Request.
25
34
  self.default_query_parser = QueryParser.make_default(65536, 100)
26
35
 
36
+ module_function
37
+
27
38
  # URI escapes. (CGI style space to +)
28
39
  def escape(s)
29
40
  URI.encode_www_form_component(s)
30
41
  end
31
- module_function :escape
32
42
 
33
43
  # Like URI escaping, but with %20 instead of +. Strictly speaking this is
34
44
  # true URI escaping.
35
45
  def escape_path(s)
36
- ::URI::DEFAULT_PARSER.escape s
46
+ RFC2396_PARSER.escape s
37
47
  end
38
- module_function :escape_path
39
48
 
40
49
  # Unescapes the **path** component of a URI. See Rack::Utils.unescape for
41
50
  # unescaping query parameters or form components.
42
51
  def unescape_path(s)
43
- ::URI::DEFAULT_PARSER.unescape s
52
+ RFC2396_PARSER.unescape s
44
53
  end
45
- module_function :unescape_path
46
-
47
54
 
48
55
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
49
56
  # target encoding of the string returned, and it defaults to UTF-8
50
57
  def unescape(s, encoding = Encoding::UTF_8)
51
58
  URI.decode_www_form_component(s, encoding)
52
59
  end
53
- module_function :unescape
54
60
 
55
61
  class << self
56
- attr_accessor :multipart_part_limit
62
+ attr_accessor :multipart_total_part_limit
63
+
64
+ attr_accessor :multipart_file_limit
65
+
66
+ # multipart_part_limit is the original name of multipart_file_limit, but
67
+ # the limit only counts parts with filenames.
68
+ alias multipart_part_limit multipart_file_limit
69
+ alias multipart_part_limit= multipart_file_limit=
57
70
  end
58
71
 
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.
72
+ # The maximum number of file parts a request can contain. Accepting too
73
+ # many parts can lead to the server running out of file handles.
61
74
  # Set to `0` for no limit.
62
- self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
75
+ self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
76
+
77
+ # The maximum total number of parts a request can contain. Accepting too
78
+ # many can lead to excessive memory use and parsing time.
79
+ self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
63
80
 
64
81
  def self.param_depth_limit
65
82
  default_query_parser.param_depth_limit
@@ -82,21 +99,20 @@ module Rack
82
99
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
83
100
  end
84
101
  else
102
+ # :nocov:
85
103
  def clock_time
86
104
  Time.now.to_f
87
105
  end
106
+ # :nocov:
88
107
  end
89
- module_function :clock_time
90
108
 
91
109
  def parse_query(qs, d = nil, &unescaper)
92
110
  Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
93
111
  end
94
- module_function :parse_query
95
112
 
96
113
  def parse_nested_query(qs, d = nil)
97
114
  Rack::Utils.default_query_parser.parse_nested_query(qs, d)
98
115
  end
99
- module_function :parse_nested_query
100
116
 
101
117
  def build_query(params)
102
118
  params.map { |k, v|
@@ -107,7 +123,6 @@ module Rack
107
123
  end
108
124
  }.join("&")
109
125
  end
110
- module_function :build_query
111
126
 
112
127
  def build_nested_query(value, prefix = nil)
113
128
  case value
@@ -118,7 +133,7 @@ module Rack
118
133
  when Hash
119
134
  value.map { |k, v|
120
135
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
121
- }.reject(&:empty?).join('&')
136
+ }.delete_if(&:empty?).join('&')
122
137
  when nil
123
138
  prefix
124
139
  else
@@ -126,20 +141,22 @@ module Rack
126
141
  "#{prefix}=#{escape(value)}"
127
142
  end
128
143
  end
129
- module_function :build_nested_query
130
144
 
131
145
  def q_values(q_value_header)
132
- q_value_header.to_s.split(/\s*,\s*/).map do |part|
133
- value, parameters = part.split(/\s*;\s*/, 2)
146
+ q_value_header.to_s.split(',').map do |part|
147
+ value, parameters = part.split(';', 2).map(&:strip)
134
148
  quality = 1.0
135
- if md = /\Aq=([\d.]+)/.match(parameters)
149
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
136
150
  quality = md[1].to_f
137
151
  end
138
152
  [value, quality]
139
153
  end
140
154
  end
141
- module_function :q_values
142
155
 
156
+ # Return best accept value to use, based on the algorithm
157
+ # in RFC 2616 Section 14. If there are multiple best
158
+ # matches (same specificity and quality), the value returned
159
+ # is arbitrary.
143
160
  def best_q_match(q_value_header, available_mimes)
144
161
  values = q_values(q_value_header)
145
162
 
@@ -152,7 +169,6 @@ module Rack
152
169
  end.last
153
170
  matches && matches.first
154
171
  end
155
- module_function :best_q_match
156
172
 
157
173
  ESCAPE_HTML = {
158
174
  "&" => "&amp;",
@@ -169,51 +185,55 @@ module Rack
169
185
  def escape_html(string)
170
186
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
171
187
  end
172
- module_function :escape_html
173
188
 
174
189
  def select_best_encoding(available_encodings, accept_encoding)
175
190
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
176
191
 
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]]
192
+ expanded_accept_encoding = []
193
+
194
+ accept_encoding.each do |m, q|
195
+ preference = available_encodings.index(m) || available_encodings.size
196
+
197
+ if m == "*"
198
+ (available_encodings - accept_encoding.map(&:first)).each do |m2|
199
+ expanded_accept_encoding << [m2, q, preference]
183
200
  end
184
- }.inject([]) { |mem, list|
185
- mem + list
186
- }
201
+ else
202
+ expanded_accept_encoding << [m, q, preference]
203
+ end
204
+ end
187
205
 
188
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
206
+ encoding_candidates = expanded_accept_encoding
207
+ .sort_by { |_, q, p| [-q, p] }
208
+ .map!(&:first)
189
209
 
190
210
  unless encoding_candidates.include?("identity")
191
211
  encoding_candidates.push("identity")
192
212
  end
193
213
 
194
- expanded_accept_encoding.each { |m, q|
214
+ expanded_accept_encoding.each do |m, q|
195
215
  encoding_candidates.delete(m) if q == 0.0
196
- }
216
+ end
197
217
 
198
- return (encoding_candidates & available_encodings)[0]
218
+ (encoding_candidates & available_encodings)[0]
199
219
  end
200
- module_function :select_best_encoding
201
220
 
202
221
  def parse_cookies(env)
203
222
  parse_cookies_header env[HTTP_COOKIE]
204
223
  end
205
- module_function :parse_cookies
206
224
 
207
225
  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 }
226
+ # According to RFC 6265:
227
+ # The syntax for cookie headers only supports semicolons
228
+ # User Agent -> Server ==
229
+ # Cookie: SID=31d4d96e407aad42; lang=en-US
230
+ return {} unless header
231
+ header.split(/[;] */n).each_with_object({}) do |cookie, cookies|
232
+ next if cookie.empty?
233
+ key, value = cookie.split('=', 2)
234
+ cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
235
+ end
215
236
  end
216
- module_function :parse_cookies_header
217
237
 
218
238
  def add_cookie_to_header(header, key, value)
219
239
  case value
@@ -221,41 +241,19 @@ module Rack
221
241
  domain = "; domain=#{value[:domain]}" if value[:domain]
222
242
  path = "; path=#{value[:path]}" if value[:path]
223
243
  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]
244
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
249
245
  secure = "; secure" if value[:secure]
250
246
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
251
247
  same_site =
252
248
  case value[:same_site]
253
249
  when false, nil
254
250
  nil
251
+ when :none, 'None', :None
252
+ '; SameSite=None'
255
253
  when :lax, 'Lax', :Lax
256
- '; SameSite=Lax'.freeze
254
+ '; SameSite=Lax'
257
255
  when true, :strict, 'Strict', :Strict
258
- '; SameSite=Strict'.freeze
256
+ '; SameSite=Strict'
259
257
  else
260
258
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
261
259
  end
@@ -277,13 +275,11 @@ module Rack
277
275
  raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
278
276
  end
279
277
  end
280
- module_function :add_cookie_to_header
281
278
 
282
279
  def set_cookie_header!(header, key, value)
283
280
  header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
284
281
  nil
285
282
  end
286
- module_function :set_cookie_header!
287
283
 
288
284
  def make_delete_cookie_header(header, key, value)
289
285
  case header
@@ -295,25 +291,30 @@ module Rack
295
291
  cookies = header
296
292
  end
297
293
 
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
- }
294
+ key = escape(key)
295
+ domain = value[:domain]
296
+ path = value[:path]
297
+ regexp = if domain
298
+ if path
299
+ /\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
300
+ else
301
+ /\A#{key}=.*domain=#{domain}(?:;|$)/
302
+ end
303
+ elsif path
304
+ /\A#{key}=.*path=#{path}(?:;|$)/
305
+ else
306
+ /\A#{key}=/
307
+ end
308
+
309
+ cookies.reject! { |cookie| regexp.match? cookie }
307
310
 
308
311
  cookies.join("\n")
309
312
  end
310
- module_function :make_delete_cookie_header
311
313
 
312
314
  def delete_cookie_header!(header, key, value = {})
313
315
  header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
314
316
  nil
315
317
  end
316
- module_function :delete_cookie_header!
317
318
 
318
319
  # Adds a cookie that will *remove* a cookie from the client. Hence the
319
320
  # strange method name.
@@ -321,17 +322,15 @@ module Rack
321
322
  new_header = make_delete_cookie_header(header, key, value)
322
323
 
323
324
  add_cookie_to_header(new_header, key,
324
- {:value => '', :path => nil, :domain => nil,
325
- :max_age => '0',
326
- :expires => Time.at(0) }.merge(value))
325
+ { value: '', path: nil, domain: nil,
326
+ max_age: '0',
327
+ expires: Time.at(0) }.merge(value))
327
328
 
328
329
  end
329
- module_function :add_remove_cookie_to_header
330
330
 
331
331
  def rfc2822(time)
332
332
  time.rfc2822
333
333
  end
334
- module_function :rfc2822
335
334
 
336
335
  # Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
337
336
  # of '% %b %Y'.
@@ -343,11 +342,10 @@ module Rack
343
342
  # weekday and month.
344
343
  #
345
344
  def rfc2109(time)
346
- wday = Time::RFC2822_DAY_NAME[time.wday]
347
- mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
345
+ wday = RFC2822_DAY_NAME[time.wday]
346
+ mon = RFC2822_MONTH_NAME[time.mon - 1]
348
347
  time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
349
348
  end
350
- module_function :rfc2109
351
349
 
352
350
  # Parses the "Range:" header, if present, into an array of Range objects.
353
351
  # Returns nil if the header is missing or syntactically invalid.
@@ -356,36 +354,38 @@ module Rack
356
354
  warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
357
355
  get_byte_ranges env['HTTP_RANGE'], size
358
356
  end
359
- module_function :byte_ranges
360
357
 
361
358
  def get_byte_ranges(http_range, size)
362
359
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
363
360
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
364
361
  ranges = []
365
362
  $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?
363
+ return nil unless range_spec.include?('-')
364
+ range = range_spec.split('-')
365
+ r0, r1 = range[0], range[1]
366
+ if r0.nil? || r0.empty?
367
+ return nil if r1.nil?
370
368
  # suffix-byte-range-spec, represents trailing suffix of file
371
369
  r0 = size - r1.to_i
372
370
  r0 = 0 if r0 < 0
373
371
  r1 = size - 1
374
372
  else
375
373
  r0 = r0.to_i
376
- if r1.empty?
374
+ if r1.nil?
377
375
  r1 = size - 1
378
376
  else
379
377
  r1 = r1.to_i
380
378
  return nil if r1 < r0 # backwards range is syntactically invalid
381
- r1 = size-1 if r1 >= size
379
+ r1 = size - 1 if r1 >= size
382
380
  end
383
381
  end
384
382
  ranges << (r0..r1) if r0 <= r1
385
383
  end
384
+
385
+ return [] if ranges.map(&:size).inject(0, :+) > size
386
+
386
387
  ranges
387
388
  end
388
- module_function :get_byte_ranges
389
389
 
390
390
  # Constant time string comparison.
391
391
  #
@@ -399,10 +399,9 @@ module Rack
399
399
  l = a.unpack("C*")
400
400
 
401
401
  r, i = 0, -1
402
- b.each_byte { |v| r |= v ^ l[i+=1] }
402
+ b.each_byte { |v| r |= v ^ l[i += 1] }
403
403
  r == 0
404
404
  end
405
- module_function :secure_compare
406
405
 
407
406
  # Context allows the use of a compatible middleware at different points
408
407
  # in a request handling stack. A compatible middleware must define
@@ -425,19 +424,25 @@ module Rack
425
424
  self.class.new(@for, app)
426
425
  end
427
426
 
428
- def context(env, app=@app)
427
+ def context(env, app = @app)
429
428
  recontext(app).call(env)
430
429
  end
431
430
  end
432
431
 
433
432
  # A case-insensitive Hash that preserves the original case of a
434
433
  # header when set.
435
- class HeaderHash < Hash
436
- def self.new(hash={})
437
- HeaderHash === hash ? hash : super(hash)
434
+ #
435
+ # @api private
436
+ class HeaderHash < Hash # :nodoc:
437
+ def self.[](headers)
438
+ if headers.is_a?(HeaderHash) && !headers.frozen?
439
+ return headers
440
+ else
441
+ return self.new(headers)
442
+ end
438
443
  end
439
444
 
440
- def initialize(hash={})
445
+ def initialize(hash = {})
441
446
  super()
442
447
  @names = {}
443
448
  hash.each { |k, v| self[k] = v }
@@ -449,6 +454,12 @@ module Rack
449
454
  @names = other.names.dup
450
455
  end
451
456
 
457
+ # on clear, we need to clear @names hash
458
+ def clear
459
+ super
460
+ @names.clear
461
+ end
462
+
452
463
  def each
453
464
  super do |k, v|
454
465
  yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
@@ -457,7 +468,7 @@ module Rack
457
468
 
458
469
  def to_hash
459
470
  hash = {}
460
- each { |k,v| hash[k] = v }
471
+ each { |k, v| hash[k] = v }
461
472
  hash
462
473
  end
463
474
 
@@ -510,13 +521,14 @@ module Rack
510
521
 
511
522
  # Every standard HTTP code mapped to the appropriate message.
512
523
  # 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,"'
524
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
525
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
526
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
516
527
  HTTP_STATUS_CODES = {
517
528
  100 => 'Continue',
518
529
  101 => 'Switching Protocols',
519
530
  102 => 'Processing',
531
+ 103 => 'Early Hints',
520
532
  200 => 'OK',
521
533
  201 => 'Created',
522
534
  202 => 'Accepted',
@@ -533,6 +545,7 @@ module Rack
533
545
  303 => 'See Other',
534
546
  304 => 'Not Modified',
535
547
  305 => 'Use Proxy',
548
+ 306 => '(Unused)',
536
549
  307 => 'Temporary Redirect',
537
550
  308 => 'Permanent Redirect',
538
551
  400 => 'Bad Request',
@@ -557,6 +570,7 @@ module Rack
557
570
  422 => 'Unprocessable Entity',
558
571
  423 => 'Locked',
559
572
  424 => 'Failed Dependency',
573
+ 425 => 'Too Early',
560
574
  426 => 'Upgrade Required',
561
575
  428 => 'Precondition Required',
562
576
  429 => 'Too Many Requests',
@@ -571,12 +585,13 @@ module Rack
571
585
  506 => 'Variant Also Negotiates',
572
586
  507 => 'Insufficient Storage',
573
587
  508 => 'Loop Detected',
588
+ 509 => 'Bandwidth Limit Exceeded',
574
589
  510 => 'Not Extended',
575
590
  511 => 'Network Authentication Required'
576
591
  }
577
592
 
578
593
  # 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 << 205 << 304)
594
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
580
595
 
581
596
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
582
597
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -584,12 +599,11 @@ module Rack
584
599
 
585
600
  def status_code(status)
586
601
  if status.is_a?(Symbol)
587
- SYMBOL_TO_STATUS_CODE[status] || 500
602
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
588
603
  else
589
604
  status.to_i
590
605
  end
591
606
  end
592
- module_function :status_code
593
607
 
594
608
  PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
595
609
 
@@ -603,18 +617,16 @@ module Rack
603
617
  part == '..' ? clean.pop : clean << part
604
618
  end
605
619
 
606
- clean.unshift '/' if parts.empty? || parts.first.empty?
607
-
608
- ::File.join(*clean)
620
+ clean_path = clean.join(::File::SEPARATOR)
621
+ clean_path.prepend("/") if parts.empty? || parts.first.empty?
622
+ clean_path
609
623
  end
610
- module_function :clean_path_info
611
624
 
612
- NULL_BYTE = "\0".freeze
625
+ NULL_BYTE = "\0"
613
626
 
614
627
  def valid_path?(path)
615
628
  path.valid_encoding? && !path.include?(NULL_BYTE)
616
629
  end
617
- module_function :valid_path?
618
630
 
619
631
  end
620
632
  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.17"
24
+
25
+ # Return the Rack release as a dotted string.
26
+ def self.release
27
+ RELEASE
28
+ end
29
+ end