Class: HTTPX::Transcoder::Multipart::Decoder
- Inherits:
-
Object
- Object
- HTTPX::Transcoder::Multipart::Decoder
- Includes:
- Utils
- Defined in:
- lib/httpx/transcoder/multipart/decoder.rb,
sig/transcoder/multipart.rbs
Constant Summary collapse
- 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
Constants included from Utils
Utils::FILENAME_EXTENSION_REGEX, Utils::FILENAME_REGEX, Utils::TOKEN, Utils::URIParser, Utils::VALUE
Instance Method Summary collapse
- #call(response) ⇒ Hash[String, untyped]
-
#initialize(response) ⇒ Decoder
constructor
A new instance of Decoder.
- #parse ⇒ void
Methods included from Utils
elapsed_time, get_filename, #in_ractor?, now, parse_retry_after, #self?.elapsed_time, #self?.get_filename, #self?.in_ractor?, #self?.now, #self?.parse_retry_after, #self?.to_uri, to_uri
Constructor Details
#initialize(response) ⇒ Decoder
Returns a new instance of Decoder.
30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/httpx/transcoder/multipart/decoder.rb', line 30 def initialize(response) @boundary = begin m = response.headers["content-type"].to_s[BOUNDARY_RE, 1] raise Error, "no boundary declared in content-type header" unless m m.strip end @buffer = "".b @parts = {} @intermediate_boundary = "--#{@boundary}" @state = :idle @current = nil end |
Instance Method Details
#call(response) ⇒ Hash[String, untyped]
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/httpx/transcoder/multipart/decoder.rb', line 44 def call(response, *) response.body.each do |chunk| @buffer << chunk parse end raise Error, "invalid or unsupported multipart format" unless @buffer.empty? @parts end |
#parse ⇒ void
This method returns an undefined value.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/httpx/transcoder/multipart/decoder.rb', line 58 def parse case @state when :idle raise Error, "payload does not start with boundary" unless @buffer.start_with?("#{@intermediate_boundary}#{CRLF}") @buffer = @buffer.byteslice((@intermediate_boundary.bytesize + 2)..-1) @state = :part_header when :part_header idx = @buffer.index("#{CRLF}#{CRLF}") # raise Error, "couldn't parse part headers" unless idx return unless idx # @type var head: String head = @buffer.byteslice(0..(idx + 4 - 1)) @buffer = @buffer.byteslice(head.bytesize..-1) content_type = head[MULTIPART_CONTENT_TYPE, 1] || "text/plain" if (name = head[MULTIPART_CONTENT_DISPOSITION, 1]) name = /\A"(.*)"\Z/ =~ name ? Regexp.last_match(1) : name.dup name.gsub!(/\\(.)/, "\\1") name else name = head[MULTIPART_CONTENT_ID, 1] end filename = HTTPX::Utils.get_filename(head) name = filename || +"#{content_type}[]" if name.nil? || name.empty? @current = name @parts[name] = if filename FilePart.new(filename, content_type) else "".b end @state = :part_body when :part_body part = @parts[@current] body_separator = if part.is_a?(FilePart) "#{CRLF}#{CRLF}" else CRLF end idx = @buffer.index(body_separator) if idx payload = @buffer.byteslice(0..(idx - 1)) @buffer = @buffer.byteslice((idx + body_separator.bytesize)..-1) part << payload part.rewind if part.respond_to?(:rewind) @state = :parse_boundary else part << @buffer @buffer.clear end when :parse_boundary raise Error, "payload does not start with boundary" unless @buffer.start_with?(@intermediate_boundary) @buffer = @buffer.byteslice(@intermediate_boundary.bytesize..-1) if @buffer == "--" @buffer.clear @state = :done return elsif @buffer.start_with?(CRLF) @buffer = @buffer.byteslice(2..-1) @state = :part_header else return end when :done raise Error, "parsing should have been over by now" end until @buffer.empty? end |