httpx/lib/httpx/utils.rb
HoneyryderChuck db939b56d2 Adding HTTPX::Response::Body#filename
This returns the filename advertised in the content-disposition header.
It reuses the same logic which existed for parsing multipart responses,
which itself was based on `rack`'s.
2022-12-29 01:09:14 +00:00

86 lines
2.2 KiB
Ruby

# frozen_string_literal: true
module HTTPX
module Utils
using URIExtensions
using HTTPX::RegexpExtensions unless Regexp.method_defined?(:match?)
TOKEN = %r{[^\s()<>,;:\\"/\[\]?=]+}.freeze
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/.freeze
FILENAME_REGEX = /\s*filename=(#{VALUE})/.freeze
FILENAME_EXTENSION_REGEX = /\s*filename\*=(#{VALUE})/.freeze
module_function
def now
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
def elapsed_time(monotonic_timestamp)
Process.clock_gettime(Process::CLOCK_MONOTONIC) - monotonic_timestamp
end
# The value of this field can be either an HTTP-date or a number of
# seconds to delay after the response is received.
def parse_retry_after(retry_after)
# first: bet on it being an integer
Integer(retry_after)
rescue ArgumentError
# Then it's a datetime
time = Time.httpdate(retry_after)
time - Time.now
end
def get_filename(header, _prefix_regex = nil)
filename = nil
case header
when FILENAME_REGEX
filename = Regexp.last_match(1)
filename = Regexp.last_match(1) if filename =~ /^"(.*)"$/
when FILENAME_EXTENSION_REGEX
filename = Regexp.last_match(1)
encoding, _, filename = filename.split("'", 3)
end
return unless filename
filename = URI::DEFAULT_PARSER.unescape(filename) if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
filename.scrub!
filename = filename.gsub(/\\(.)/, '\1') unless /\\[^\\"]/.match?(filename)
filename.force_encoding ::Encoding.find(encoding) if encoding
filename
end
if RUBY_VERSION < "2.3"
def to_uri(uri)
URI(uri)
end
else
URIParser = URI::RFC2396_Parser.new
def to_uri(uri)
return URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
uri = URI(URIParser.escape(uri))
non_ascii_hostname = URIParser.unescape(uri.host)
non_ascii_hostname.force_encoding(Encoding::UTF_8)
idna_hostname = Punycode.encode_hostname(non_ascii_hostname)
uri.host = idna_hostname
uri.non_ascii_hostname = non_ascii_hostname
uri
end
end
end
end