rack 2.2.7 → 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.
@@ -4,7 +4,7 @@ module Rack
4
4
  # Rack::MediaType parse media type and parameters out of content_type string
5
5
 
6
6
  class MediaType
7
- SPLIT_PATTERN = %r{\s*[;,]\s*}
7
+ SPLIT_PATTERN = /[;,]/
8
8
 
9
9
  class << self
10
10
  # The media type (type/subtype) portion of the CONTENT_TYPE header
@@ -15,7 +15,11 @@ module Rack
15
15
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
16
16
  def type(content_type)
17
17
  return nil unless content_type
18
- content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase!
18
+ if type = content_type.split(SPLIT_PATTERN, 2).first
19
+ type.rstrip!
20
+ type.downcase!
21
+ type
22
+ end
19
23
  end
20
24
 
21
25
  # The media type parameters provided in CONTENT_TYPE as a Hash, or
@@ -23,21 +27,27 @@ module Rack
23
27
  # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
24
28
  # this method responds with the following Hash:
25
29
  # { 'charset' => 'utf-8' }
30
+ #
31
+ # This will pass back parameters with empty strings in the hash if they
32
+ # lack a value (e.g., "text/plain;charset=" will return { 'charset' => '' },
33
+ # and "text/plain;charset" will return { 'charset' => '' }, similarly to
34
+ # the query params parser (barring the latter case, which returns nil instead)).
26
35
  def params(content_type)
27
36
  return {} if content_type.nil?
28
37
 
29
38
  content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
39
+ s.strip!
30
40
  k, v = s.split('=', 2)
31
-
32
- hsh[k.tap(&:downcase!)] = strip_doublequotes(v)
41
+ k.downcase!
42
+ hsh[k] = strip_doublequotes(v)
33
43
  end
34
44
  end
35
45
 
36
46
  private
37
47
 
38
- def strip_doublequotes(str)
39
- (str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
40
- end
48
+ def strip_doublequotes(str)
49
+ (str && str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str || ''
50
+ end
41
51
  end
42
52
  end
43
53
  end
data/lib/rack/mock.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'uri'
4
4
  require 'stringio'
5
5
  require_relative '../rack'
6
- require 'cgi/cookie'
7
6
 
8
7
  module Rack
9
8
  # Rack::MockRequest helps testing your Rack application without
@@ -171,6 +170,36 @@ module Rack
171
170
  # MockRequest.
172
171
 
173
172
  class MockResponse < Rack::Response
173
+ begin
174
+ # Recent versions of the CGI gem may not provide `CGI::Cookie`.
175
+ require 'cgi/cookie'
176
+ Cookie = CGI::Cookie
177
+ rescue LoadError
178
+ class Cookie
179
+ attr_reader :name, :value, :path, :domain, :expires, :secure
180
+
181
+ def initialize(args)
182
+ @name = args["name"]
183
+ @value = args["value"]
184
+ @path = args["path"]
185
+ @domain = args["domain"]
186
+ @expires = args["expires"]
187
+ @secure = args["secure"]
188
+ end
189
+
190
+ def method_missing(method_name, *args, &block)
191
+ @value.send(method_name, *args, &block)
192
+ end
193
+ # :nocov:
194
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
195
+ # :nocov:
196
+
197
+ def respond_to_missing?(method_name, include_all = false)
198
+ @value.respond_to?(method_name, include_all) || super
199
+ end
200
+ end
201
+ end
202
+
174
203
  class << self
175
204
  alias [] new
176
205
  end
@@ -236,7 +265,7 @@ module Rack
236
265
  set_cookie_header.split("\n").each do |cookie|
237
266
  cookie_name, cookie_filling = cookie.split('=', 2)
238
267
  cookie_attributes = identify_cookie_attributes cookie_filling
239
- parsed_cookie = CGI::Cookie.new(
268
+ parsed_cookie = Cookie.new(
240
269
  'name' => cookie_name.strip,
241
270
  'value' => cookie_attributes.fetch('value'),
242
271
  'path' => cookie_attributes.fetch('path', nil),
@@ -253,7 +282,7 @@ module Rack
253
282
  def identify_cookie_attributes(cookie_filling)
254
283
  cookie_bits = cookie_filling.split(';')
255
284
  cookie_attributes = Hash.new
256
- cookie_attributes.store('value', cookie_bits[0].strip)
285
+ cookie_attributes.store('value', Array(cookie_bits[0].strip))
257
286
  cookie_bits.each do |bit|
258
287
  if bit.include? '='
259
288
  cookie_attribute, attribute_value = bit.split('=')
@@ -13,7 +13,9 @@ module Rack
13
13
  BUFSIZE = 1_048_576
14
14
  TEXT_PLAIN = "text/plain"
15
15
  TEMPFILE_FACTORY = lambda { |filename, content_type|
16
- Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
16
+ extension = ::File.extname(filename.gsub("\0", '%00'))[0, 129]
17
+
18
+ Tempfile.new(["RackMultipart", extension])
17
19
  }
18
20
 
19
21
  BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
@@ -189,6 +191,7 @@ module Rack
189
191
 
190
192
  @sbuf = StringScanner.new("".dup)
191
193
  @body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
194
+ @end_boundary_size = boundary.bytesize + 6 # (-- at start, -- at finish, EOL at end)
192
195
  @rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
193
196
  @head_regex = /(.*?#{EOL})#{EOL}/m
194
197
  end
@@ -229,7 +232,12 @@ module Rack
229
232
  end
230
233
 
231
234
  def handle_fast_forward
232
- if consume_boundary
235
+ tok = consume_boundary
236
+
237
+ if tok == :END_BOUNDARY && @sbuf.pos == @end_boundary_size && @sbuf.eos?
238
+ # stop parsing a buffer if a buffer is only an end boundary.
239
+ @state = :DONE
240
+ elsif tok
233
241
  @state = :MIME_HEAD
234
242
  else
235
243
  raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
@@ -16,20 +16,47 @@ module Rack
16
16
  # sequence.
17
17
  class InvalidParameterError < ArgumentError; end
18
18
 
19
- # ParamsTooDeepError is the error that is raised when params are recursively
20
- # nested over the specified limit.
21
- class ParamsTooDeepError < RangeError; end
19
+ # QueryLimitError is for errors raised when the query provided exceeds one
20
+ # of the query parser limits.
21
+ class QueryLimitError < RangeError
22
+ end
23
+
24
+ # ParamsTooDeepError is the old name for the error that is raised when params
25
+ # are recursively nested over the specified limit. Make it the same as
26
+ # as QueryLimitError, so that code that rescues ParamsTooDeepError error
27
+ # to handle bad query strings also now handles other limits.
28
+ ParamsTooDeepError = QueryLimitError
22
29
 
23
- def self.make_default(key_space_limit, param_depth_limit)
24
- new Params, key_space_limit, param_depth_limit
30
+ def self.make_default(key_space_limit, param_depth_limit, **options)
31
+ new(Params, key_space_limit, param_depth_limit, **options)
25
32
  end
26
33
 
27
34
  attr_reader :key_space_limit, :param_depth_limit
28
35
 
29
- def initialize(params_class, key_space_limit, param_depth_limit)
36
+ env_int = lambda do |key, val|
37
+ if str_val = ENV[key]
38
+ begin
39
+ val = Integer(str_val, 10)
40
+ rescue ArgumentError
41
+ raise ArgumentError, "non-integer value provided for environment variable #{key}"
42
+ end
43
+ end
44
+
45
+ val
46
+ end
47
+
48
+ BYTESIZE_LIMIT = env_int.call("RACK_QUERY_PARSER_BYTESIZE_LIMIT", 4194304)
49
+ private_constant :BYTESIZE_LIMIT
50
+
51
+ PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
52
+ private_constant :PARAMS_LIMIT
53
+
54
+ def initialize(params_class, key_space_limit, param_depth_limit, bytesize_limit: BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
30
55
  @params_class = params_class
31
56
  @key_space_limit = key_space_limit
32
57
  @param_depth_limit = param_depth_limit
58
+ @bytesize_limit = bytesize_limit
59
+ @params_limit = params_limit
33
60
  end
34
61
 
35
62
  # Stolen from Mongrel, with some small modifications:
@@ -42,7 +69,7 @@ module Rack
42
69
 
43
70
  params = make_params
44
71
 
45
- (qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
72
+ check_query_string(qs, d).split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
46
73
  next if p.empty?
47
74
  k, v = p.split('=', 2).map!(&unescaper)
48
75
 
@@ -69,7 +96,7 @@ module Rack
69
96
  params = make_params
70
97
 
71
98
  unless qs.nil? || qs.empty?
72
- (qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
99
+ check_query_string(qs, d).split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
73
100
  k, v = p.split('=', 2).map! { |s| unescape(s) }
74
101
 
75
102
  normalize_params(params, k, v, param_depth_limit)
@@ -155,8 +182,24 @@ module Rack
155
182
  true
156
183
  end
157
184
 
158
- def unescape(s)
159
- Utils.unescape(s)
185
+ def check_query_string(qs, sep)
186
+ if qs
187
+ if qs.bytesize > @bytesize_limit
188
+ raise QueryLimitError, "total query size (#{qs.bytesize}) exceeds limit (#{@bytesize_limit})"
189
+ end
190
+
191
+ if (param_count = qs.count(sep.is_a?(String) ? sep : '&')) >= @params_limit
192
+ raise QueryLimitError, "total number of query parameters (#{param_count+1}) exceeds limit (#{@params_limit})"
193
+ end
194
+
195
+ qs
196
+ else
197
+ ''
198
+ end
199
+ end
200
+
201
+ def unescape(string)
202
+ Utils.unescape(string)
160
203
  end
161
204
 
162
205
  class Params
data/lib/rack/sendfile.rb CHANGED
@@ -133,7 +133,7 @@ module Rack
133
133
  end
134
134
  when '', nil
135
135
  else
136
- env[RACK_ERRORS].puts "Unknown x-sendfile variation: '#{type}'.\n"
136
+ env[RACK_ERRORS].puts "Unknown x-sendfile variation: #{type.inspect}"
137
137
  end
138
138
  end
139
139
  [status, headers, body]
@@ -4,7 +4,7 @@ require 'openssl'
4
4
  require 'zlib'
5
5
  require_relative 'abstract/id'
6
6
  require 'json'
7
- require 'base64'
7
+ require 'delegate'
8
8
 
9
9
  module Rack
10
10
 
@@ -50,11 +50,11 @@ module Rack
50
50
  # Encode session cookies as Base64
51
51
  class Base64
52
52
  def encode(str)
53
- ::Base64.strict_encode64(str)
53
+ [str].pack("m0")
54
54
  end
55
55
 
56
56
  def decode(str)
57
- ::Base64.decode64(str)
57
+ str.unpack("m").first
58
58
  end
59
59
 
60
60
  # Encode session cookies as Marshaled Base64 data
@@ -55,6 +55,7 @@ module Rack
55
55
 
56
56
  def write_session(req, session_id, new_session, options)
57
57
  with_lock(req) do
58
+ return false unless get_session_with_fallback(session_id)
58
59
  @pool.store session_id.private_id, new_session
59
60
  session_id
60
61
  end
@@ -64,7 +65,11 @@ module Rack
64
65
  with_lock(req) do
65
66
  @pool.delete(session_id.public_id)
66
67
  @pool.delete(session_id.private_id)
67
- generate_sid unless options[:drop]
68
+ unless options[:drop]
69
+ sid = generate_sid
70
+ @pool.store(sid.private_id, {})
71
+ sid
72
+ end
68
73
  end
69
74
  end
70
75
 
data/lib/rack/static.rb CHANGED
@@ -122,8 +122,9 @@ module Rack
122
122
 
123
123
  def call(env)
124
124
  path = env[PATH_INFO]
125
+ actual_path = Utils.clean_path_info(Utils.unescape_path(path))
125
126
 
126
- if can_serve(path)
127
+ if can_serve(actual_path)
127
128
  if overwrite_file_path(path)
128
129
  env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
129
130
  elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
data/lib/rack/utils.rb CHANGED
@@ -24,6 +24,7 @@ module Rack
24
24
 
25
25
  RFC2822_DAY_NAME = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
26
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
27
28
 
28
29
  class << self
29
30
  attr_accessor :default_query_parser
@@ -42,13 +43,13 @@ module Rack
42
43
  # Like URI escaping, but with %20 instead of +. Strictly speaking this is
43
44
  # true URI escaping.
44
45
  def escape_path(s)
45
- ::URI::DEFAULT_PARSER.escape s
46
+ RFC2396_PARSER.escape s
46
47
  end
47
48
 
48
49
  # Unescapes the **path** component of a URI. See Rack::Utils.unescape for
49
50
  # unescaping query parameters or form components.
50
51
  def unescape_path(s)
51
- ::URI::DEFAULT_PARSER.unescape s
52
+ RFC2396_PARSER.unescape s
52
53
  end
53
54
 
54
55
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
@@ -142,8 +143,8 @@ module Rack
142
143
  end
143
144
 
144
145
  def q_values(q_value_header)
145
- q_value_header.to_s.split(/\s*,\s*/).map do |part|
146
- 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)
147
148
  quality = 1.0
148
149
  if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
149
150
  quality = md[1].to_f
@@ -380,6 +381,9 @@ module Rack
380
381
  end
381
382
  ranges << (r0..r1) if r0 <= r1
382
383
  end
384
+
385
+ return [] if ranges.map(&:size).inject(0, :+) > size
386
+
383
387
  ranges
384
388
  end
385
389
 
data/lib/rack/version.rb CHANGED
@@ -20,7 +20,7 @@ module Rack
20
20
  VERSION.join(".")
21
21
  end
22
22
 
23
- RELEASE = "2.2.7"
23
+ RELEASE = "2.2.17"
24
24
 
25
25
  # Return the Rack release as a dotted string.
26
26
  def self.release
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.7
4
+ version: 2.2.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leah Neukirchen
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-04-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: minitest
@@ -77,9 +76,9 @@ executables:
77
76
  - rackup
78
77
  extensions: []
79
78
  extra_rdoc_files:
80
- - README.rdoc
81
79
  - CHANGELOG.md
82
80
  - CONTRIBUTING.md
81
+ - README.rdoc
83
82
  files:
84
83
  - CHANGELOG.md
85
84
  - CONTRIBUTING.md
@@ -169,7 +168,6 @@ metadata:
169
168
  changelog_uri: https://github.com/rack/rack/blob/master/CHANGELOG.md
170
169
  documentation_uri: https://rubydoc.info/github/rack/rack
171
170
  source_code_uri: https://github.com/rack/rack
172
- post_install_message:
173
171
  rdoc_options: []
174
172
  require_paths:
175
173
  - lib
@@ -184,8 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
182
  - !ruby/object:Gem::Version
185
183
  version: '0'
186
184
  requirements: []
187
- rubygems_version: 3.4.10
188
- signing_key:
185
+ rubygems_version: 3.7.0.dev
189
186
  specification_version: 4
190
187
  summary: A modular Ruby webserver interface.
191
188
  test_files: []