module Rack::Utils

Experimental RBS support (using type sampling data from the type_fusion project).

# sig/rack/utils.rbs

module Rack::Utils
  def clean_path_info: (String path_info) -> String
  def parse_cookies_header: (String header) -> untyped
  def status_code: (Integer status) -> Integer
  def unescape: (String s, ?Encoding encoding) -> String
  def unescape_path: (String s) -> String
  def valid_path?: (String path) -> true
end

def self.key_space_limit

def self.key_space_limit
  default_query_parser.key_space_limit
end

def self.key_space_limit=(v)

def self.key_space_limit=(v)
  self.default_query_parser = self.default_query_parser.new_space_limit(v)
end

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 add_cookie_to_header(header, key, value)

def add_cookie_to_header(header, key, value)
  case value
  when Hash
    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 SameSite value: #{value[:same_site].inspect}"
      end
    value = value[:value]
  end
  value = [value] unless Array === value
  cookie = "#{escape(key)}=#{value.map { |v| escape v }.join('&')}#{domain}" \
    "#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
  case header
  when nil, ''
    cookie
  when String
    [header, cookie].join("\n")
  when Array
    (header + [cookie]).join("\n")
  else
    raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
  end
end

def add_remove_cookie_to_header(header, key, value = {})

strange method name.
Adds a cookie that will *remove* a cookie from the client. Hence the
def add_remove_cookie_to_header(header, key, value = {})
  new_header = make_delete_cookie_header(header, key, value)
  add_cookie_to_header(new_header, key,
             { value: '', path: nil, domain: nil,
               max_age: '0',
               expires: Time.at(0) }.merge(value))
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 && 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}[#{escape(k)}]" : escape(k))
    }.delete_if(&:empty?).join('&')
  when nil
    prefix
  else
    raise ArgumentError, "value must be a Hash" if prefix.nil?
    "#{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)
  warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
  get_byte_ranges env['HTTP_RANGE'], size
end

def clean_path_info(path_info)

Experimental RBS support (using type sampling data from the type_fusion project).

def clean_path_info: (String path_info) -> String

This signature was generated using 1 sample from 1 application.

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!(header, key, value = {})

def delete_cookie_header!(header, key, value = {})
  header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
  nil
end

def escape(s)

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

def escape_html(string)

Escape ampersands, brackets and quotes to their HTML/XML entities.
def escape_html(string)
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
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 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>
  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
  ranges
end

def make_delete_cookie_header(header, key, value)

def make_delete_cookie_header(header, key, value)
  case header
  when nil, ''
    cookies = []
  when String
    cookies = header.split("\n")
  when Array
    cookies = header
  end
  key = escape(key)
  domain = value[:domain]
  path = value[:path]
  regexp = if domain
             if path
               /\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
             else
               /\A#{key}=.*domain=#{domain}(?:;|$)/
             end
           elsif path
             /\A#{key}=.*path=#{path}(?:;|$)/
           else
             /\A#{key}=/
           end
  cookies.reject! { |cookie| regexp.match? cookie }
  cookies.join("\n")
end

def parse_cookies(env)

def parse_cookies(env)
  parse_cookies_header env[HTTP_COOKIE]
end

def parse_cookies_header(header)

Experimental RBS support (using type sampling data from the type_fusion project).

def parse_cookies_header: (String header) -> untyped

This signature was generated using 1 sample from 1 application.

def parse_cookies_header(header)
  # According to RFC 6265:
  # The syntax for cookie headers only supports semicolons
  # User Agent -> Server ==
  # Cookie: SID=31d4d96e407aad42; lang=en-US
  return {} unless header
  header.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(/\s*,\s*/).map do |part|
    value, parameters = part.split(/\s*;\s*/, 2)
    quality = 1.0
    if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
      quality = md[1].to_f
    end
    [value, quality]
  end
end

def rfc2109(time)


weekday and month.
Do not use %a and %b from Time.strptime, it would use localized names for
that I'm certain someone implemented only that option.
NOTE: I'm not sure the RFC says it requires GMT, but is ambiguous enough

It assumes that the time is in GMT to comply to the RFC 2109.
of '% %b %Y'.
Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
def rfc2109(time)
  wday = RFC2822_DAY_NAME[time.wday]
  mon = RFC2822_MONTH_NAME[time.mon - 1]
  time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
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
  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!(header, key, value)

def set_cookie_header!(header, key, value)
  header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
  nil
end

def status_code(status)

Experimental RBS support (using type sampling data from the type_fusion project).

def status_code: (Integer status) -> Integer

This signature was generated using 4 samples from 1 application.

def status_code(status)
  if status.is_a?(Symbol)
    SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
  else
    status.to_i
  end
end

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

Experimental RBS support (using type sampling data from the type_fusion project).

def unescape: (String s, ?Encoding encoding) -> String

This signature was generated using 9 samples from 1 application.

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)

Experimental RBS support (using type sampling data from the type_fusion project).

def unescape_path: (String s) -> String

This signature was generated using 6 samples from 1 application.

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)

Experimental RBS support (using type sampling data from the type_fusion project).

def valid_path?: (String path) -> true

This signature was generated using 1 sample from 1 application.

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