lib/httpx/plugins/multipart/decoder.rb in httpx-0.22.2 vs lib/httpx/plugins/multipart/decoder.rb in httpx-0.22.3

- old
+ new

@@ -3,14 +3,10 @@ require "tempfile" require "delegate" module HTTPX::Plugins module Multipart - using HTTPX::RegexpExtensions unless Regexp.method_defined?(:match?) - - CRLF = "\r\n" - class FilePart < SimpleDelegator attr_reader :original_filename, :content_type def initialize(filename, content_type) @original_filename = filename @@ -18,36 +14,18 @@ @file = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR) super(@file) end end - TOKEN = %r{[^\s()<>,;:\\"/\[\]?=]+}.freeze - VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/.freeze - CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i.freeze - BROKEN_QUOTED = /^#{CONDISP}.*;\s*filename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i.freeze - BROKEN_UNQUOTED = /^#{CONDISP}.*;\s*filename=(#{TOKEN})/i.freeze - MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze - MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze - MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze - # Updated definitions from RFC 2231 - ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}.freeze - ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/.freeze - SECTION = /\*[0-9]+/.freeze - REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/.freeze - REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/.freeze - EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/.freeze - EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/.freeze - EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/.freeze - EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/.freeze - EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9-]*'[a-zA-Z0-9-]*'#{EXTENDED_OTHER_VALUE}*/.freeze - EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/.freeze - EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/.freeze - DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/.freeze - RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i.freeze - class Decoder + include HTTPX::Utils + + CRLF = "\r\n" BOUNDARY_RE = /;\s*boundary=([^;]+)/i.freeze + MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze + MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze + MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze WINDOW_SIZE = 2 << 14 def initialize(response) @boundary = begin m = response.headers["content-type"].to_s[BOUNDARY_RE, 1] @@ -100,11 +78,11 @@ name else name = head[MULTIPART_CONTENT_ID, 1] end - filename = get_filename(head) + filename = HTTPX::Utils.get_filename(head) name = filename || +"#{content_type || "text/plain"}[]" if name.nil? || name.empty? @current = name @@ -151,37 +129,9 @@ return end when :done raise Error, "parsing should have been over by now" end until @buffer.empty? - end - - def get_filename(head) - filename = nil - case head - when RFC2183 - params = Hash[*head.scan(DISPPARM).flat_map(&:compact)] - - if (filename = params["filename"]) - filename = Regexp.last_match(1) if filename =~ /^"(.*)"$/ - elsif (filename = params["filename*"]) - encoding, _, filename = filename.split("'", 3) - end - when BROKEN_QUOTED, BROKEN_UNQUOTED - filename = Regexp.last_match(1) - 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 end end end