module Rack::Utils

def self.param_depth_limit

def self.param_depth_limit
  default_query_parser.param_depth_limit
end

def self.param_depth_limit=(v)

def self.param_depth_limit=(v)
  self.default_query_parser = self.default_query_parser.new_depth_limit(v)
end

def best_q_match(q_value_header, available_mimes)

is arbitrary.
matches (same specificity and quality), the value returned
in RFC 2616 Section 14. If there are multiple best
Return best accept value to use, based on the algorithm
def best_q_match(q_value_header, available_mimes)
  values = q_values(q_value_header)
  matches = values.map do |req_mime, quality|
    match = available_mimes.find { |am| Rack::Mime.match?(am, req_mime) }
    next unless match
    [match, quality]
  end.compact.sort_by do |match, quality|
    (match.split('/', 2).count('*') * -10) + quality
  end.last
  matches&.first
end

def build_nested_query(value, prefix = nil)

def build_nested_query(value, prefix = nil)
  case value
  when Array
    value.map { |v|
      build_nested_query(v, "#{prefix}[]")
    }.join("&")
  when Hash
    value.map { |k, v|
      build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
    }.delete_if(&:empty?).join('&')
  when nil
    escape(prefix)
  else
    raise ArgumentError, "value must be a Hash" if prefix.nil?
    "#{escape(prefix)}=#{escape(value)}"
  end
end

def build_query(params)

def build_query(params)
  params.map { |k, v|
    if v.class == Array
      build_query(v.map { |x| [k, x] })
    else
      v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
    end
  }.join("&")
end

def byte_ranges(env, size)

Returns an empty array if none of the ranges are satisfiable.
Returns nil if the header is missing or syntactically invalid.
Parses the "Range:" header, if present, into an array of Range objects.
def byte_ranges(env, size)
  get_byte_ranges env['HTTP_RANGE'], size
end

def clean_path_info(path_info)

def clean_path_info(path_info)
  parts = path_info.split PATH_SEPS
  clean = []
  parts.each do |part|
    next if part.empty? || part == '.'
    part == '..' ? clean.pop : clean << part
  end
  clean_path = clean.join(::File::SEPARATOR)
  clean_path.prepend("/") if parts.empty? || parts.first.empty?
  clean_path
end

def clock_time

def clock_time
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

def clock_time

:nocov:
def clock_time
  Time.now.to_f
end

def delete_cookie_header!(headers, key, value = {})

def delete_cookie_header!(headers, key, value = {})
  headers[SET_COOKIE] = delete_set_cookie_header!(headers[SET_COOKIE], key, value)
  return nil
end

def delete_set_cookie_header(key, value = {})


# => "myname=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
delete_set_cookie_header("myname")

to *remove* any matching cookie.
+value+. When used with the +set-cookie+ header, it will cause the client
a +max_age+ of 0 seconds, an +expires+ date in the past and an empty
attributes as outlined by set_cookie_header. The encoded cookie will have
deleted. The +value+ may be an instance of +Hash+ and can include
set_cookie_header for the purpose of causing the specified cookie to be
Generate an encoded string based on the given +key+ and +value+ using

delete_set_cookie_header(key, value = {}) -> encoded string
:call-seq:
def delete_set_cookie_header(key, value = {})
  set_cookie_header(key, value.merge(max_age: '0', expires: Time.at(0), value: ''))
end

def delete_set_cookie_header!(header, key, value = {})


# => ["mycookie=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"]
header
# => ["mycookie=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"]
delete_set_cookie_header!(header, "mycookie")
header = []

If the header is non-nil, it will be modified in place.

# => "mycookie=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
delete_set_cookie_header!(nil, "mycookie")

the client to immediately delete the specified cookie.
+key+ and +value+ using delete_set_cookie_header. This causes
Set an expired cookie in the specified headers with the given cookie

delete_set_cookie_header!(header, key, value = {}) -> header value
:call-seq:
def delete_set_cookie_header!(header, key, value = {})
  if header
    header = Array(header)
    header << delete_set_cookie_header(key, value)
  else
    header = delete_set_cookie_header(key, value)
  end
  return header
end

def escape(s)

URI escapes. (CGI style space to +)
def escape(s)
  URI.encode_www_form_component(s)
end

def escape_cookie_key(key)

def escape_cookie_key(key)
 =~ VALID_COOKIE_KEY
 "Cookie key #{key.inspect} is not valid according to RFC2616; it will be escaped. This behaviour is deprecated and will be removed in a future version of Rack.", uplevel: 2
pe(key)

def escape_html(string)

Escape ampersands, brackets and quotes to their HTML/XML entities.
def escape_html(string)
  CGI.escapeHTML(string.to_s)
end

def escape_path(s)

true URI escaping.
Like URI escaping, but with %20 instead of +. Strictly speaking this is
def escape_path(s)
  ::URI::DEFAULT_PARSER.escape s
end

def forwarded_values(forwarded_header)

def forwarded_values(forwarded_header)
  return nil unless forwarded_header
  forwarded_header = forwarded_header.to_s.gsub("\n", ";")
  forwarded_header.split(';').each_with_object({}) do |field, values|
    field.split(',').each do |pair|
      pair = pair.split('=').map(&:strip).join('=')
      return nil unless pair =~ /\A(by|for|host|proto)="?([^"]+)"?\Z/i
      (values[$1.downcase.to_sym] ||= []) << $2
    end
  end
end

def get_byte_ranges(http_range, size)

def get_byte_ranges(http_range, size)
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
  # Ignore Range when file size is 0 to avoid a 416 error.
  return nil if size.zero?
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
  ranges = []
  $1.split(/,\s*/).each do |range_spec|
    return nil unless range_spec.include?('-')
    range = range_spec.split('-')
    r0, r1 = range[0], range[1]
    if r0.nil? || r0.empty?
      return nil if r1.nil?
      # suffix-byte-range-spec, represents trailing suffix of file
      r0 = size - r1.to_i
      r0 = 0  if r0 < 0
      r1 = size - 1
    else
      r0 = r0.to_i
      if r1.nil?
        r1 = size - 1
      else
        r1 = r1.to_i
        return nil  if r1 < r0  # backwards range is syntactically invalid
        r1 = size - 1  if r1 >= size
      end
    end
    ranges << (r0..r1)  if r0 <= r1
  end
  return [] if ranges.map(&:size).sum > size
  ranges
end

def parse_cookies(env)


# => {'myname' => 'myvalue'}
parse_cookies({'HTTP_COOKIE' => 'myname=myvalue'})

parse_cookies_header. Returns a map of cookie +key+ to cookie +value+.
Parse cookies from the provided request environment using

parse_cookies(env) -> hash
:call-seq:
def parse_cookies(env)
  parse_cookies_header env[HTTP_COOKIE]
end

def parse_cookies_header(value)


# => {"myname"=>"myvalue", "max-age"=>"0"}
parse_cookies_header('myname=myvalue; max-age=0')

cookie +key+ to cookie +value+.
syntax for cookie headers only supports semicolons. Returns a map of
Parse cookies from the provided header +value+ according to RFC6265. The

parse_cookies_header(value) -> hash
:call-seq:
def parse_cookies_header(value)
  return {} unless value
  value.split(/; */n).each_with_object({}) do |cookie, cookies|
    next if cookie.empty?
    key, value = cookie.split('=', 2)
    cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
  end
end

def parse_nested_query(qs, d = nil)

def parse_nested_query(qs, d = nil)
  Rack::Utils.default_query_parser.parse_nested_query(qs, d)
end

def parse_query(qs, d = nil, &unescaper)

def parse_query(qs, d = nil, &unescaper)
  Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
end

def q_values(q_value_header)

def q_values(q_value_header)
  q_value_header.to_s.split(',').map do |part|
    value, parameters = part.split(';', 2).map(&:strip)
    quality = 1.0
    if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
      quality = md[1].to_f
    end
    [value, quality]
  end
end

def rfc2822(time)

def rfc2822(time)
  time.rfc2822
end

def secure_compare(a, b)

via timing attacks.
on variable length plaintext strings because it could leak length info
that have already been processed by HMAC. This should not be used
NOTE: the values compared should be of fixed length, such as strings

Constant time string comparison.
def secure_compare(a, b)
  return false unless a.bytesize == b.bytesize
  OpenSSL.fixed_length_secure_compare(a, b)
end

def secure_compare(a, b)

def secure_compare(a, b)
  return false unless a.bytesize == b.bytesize
  l = a.unpack("C*")
  r, i = 0, -1
  b.each_byte { |v| r |= v ^ l[i += 1] }
  r == 0
end

def select_best_encoding(available_encodings, accept_encoding)

def select_best_encoding(available_encodings, accept_encoding)
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
  expanded_accept_encoding = []
  accept_encoding.each do |m, q|
    preference = available_encodings.index(m) || available_encodings.size
    if m == "*"
      (available_encodings - accept_encoding.map(&:first)).each do |m2|
        expanded_accept_encoding << [m2, q, preference]
      end
    else
      expanded_accept_encoding << [m, q, preference]
    end
  end
  encoding_candidates = expanded_accept_encoding
    .sort_by { |_, q, p| [-q, p] }
    .map!(&:first)
  unless encoding_candidates.include?("identity")
    encoding_candidates.push("identity")
  end
  expanded_accept_encoding.each do |m, q|
    encoding_candidates.delete(m) if q == 0.0
  end
  (encoding_candidates & available_encodings)[0]
end

def set_cookie_header(key, value)


# => "myname=myvalue; max-age=10"
set_cookie_header("myname", {value: "myvalue", max_age: 10})

# => "myname=myvalue"
set_cookie_header("myname", "myvalue")

cookie key name will not be url encoded (escaped). The default is +true+.
or not the cookie key is URL encoded. If explicitly set to +false+, the
An extra cookie attribute +escape_key+ can be provided to control whether

[RFC6265 Section 5.2](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2).
details about the interpretation of these fields, consult
of +Time+), +secure+, +http_only+, +same_site+ and +value+. For more
cookie attribute keys: +domain+, +max_age+, +expires+ (must be instance
If the cookie +value+ is an instance of +Hash+, it considers the following

instance of either +String+ or +Hash+.
for the +set-cookie+ header according to RFC6265. The +value+ may be an
Generate an encoded string using the provided +key+ and +value+ suitable

set_cookie_header(key, value) -> encoded string
:call-seq:
def set_cookie_header(key, value)
  case value
  when Hash
    key = escape_cookie_key(key) unless value[:escape_key] == false
    domain  = "; domain=#{value[:domain]}"   if value[:domain]
    path    = "; path=#{value[:path]}"       if value[:path]
    max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
    expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
    secure = "; secure"  if value[:secure]
    httponly = "; httponly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
    same_site =
      case value[:same_site]
      when false, nil
        nil
      when :none, 'None', :None
        '; samesite=none'
      when :lax, 'Lax', :Lax
        '; samesite=lax'
      when true, :strict, 'Strict', :Strict
        '; samesite=strict'
      else
        raise ArgumentError, "Invalid :same_site value: #{value[:same_site].inspect}"
      end
    partitioned = "; partitioned" if value[:partitioned]
    value = value[:value]
  else
    key = escape_cookie_key(key)
  end
  value = [value] unless Array === value
  return "#{key}=#{value.map { |v| escape v }.join('&')}#{domain}" \
    "#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}#{partitioned}"
end

def set_cookie_header!(headers, key, value)

to an +Array+ if not already, and appended to.
If the headers already contains a +set-cookie+ key, it will be converted

+value+ using set_cookie_header.
Append a cookie in the specified headers with the given cookie +key+ and

set_cookie_header!(headers, key, value) -> header value
:call-seq:
def set_cookie_header!(headers, key, value)
  if header = headers[SET_COOKIE]
    if header.is_a?(Array)
      header << set_cookie_header(key, value)
    else
      headers[SET_COOKIE] = [header, set_cookie_header(key, value)]
    end
  else
    headers[SET_COOKIE] = set_cookie_header(key, value)
  end
end

def status_code(status)

def status_code(status)
  if status.is_a?(Symbol)
    SYMBOL_TO_STATUS_CODE.fetch(status) do
      fallback_code = OBSOLETE_SYMBOLS_TO_STATUS_CODES.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
      message = "Status code #{status.inspect} is deprecated and will be removed in a future version of Rack."
      if canonical_symbol = OBSOLETE_SYMBOL_MAPPINGS[status]
        # message = "#{message} Please use #{canonical_symbol.inspect} instead."
        # For now, let's not emit any warning when there is a mapping.
      else
        warn message, uplevel: 3
      end
      fallback_code
    end
  else
    status.to_i
  end
end

def unescape(s, encoding = Encoding::UTF_8)

target encoding of the string returned, and it defaults to UTF-8
Unescapes a URI escaped string with +encoding+. +encoding+ will be the
def unescape(s, encoding = Encoding::UTF_8)
  URI.decode_www_form_component(s, encoding)
end

def unescape_path(s)

unescaping query parameters or form components.
Unescapes the **path** component of a URI. See Rack::Utils.unescape for
def unescape_path(s)
  ::URI::DEFAULT_PARSER.unescape s
end

def valid_path?(path)

def valid_path?(path)
  path.valid_encoding? && !path.include?(NULL_BYTE)
end