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 = {})
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)
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 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
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)
def escape(s) URI.encode_www_form_component(s) end
def escape_html(string)
def escape_html(string) string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] } end
def escape_path(s)
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)
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.
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.
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