Skip to content

Commit

Permalink
Add MIME::Multipart.parse(HTTP::Client::Response, &) (#12890)
Browse files Browse the repository at this point in the history
Co-authored-by: Jamie Gaskins <[email protected]>
  • Loading branch information
straight-shoota and jgaskins authored Jan 25, 2023
1 parent 9fca36b commit 8e1a9a8
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
15 changes: 15 additions & 0 deletions spec/std/mime/multipart_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,20 @@ describe MIME::Multipart do
io.gets_to_end.should eq("body")
end
end

it "parses multipart messages from HTTP client responses" do
headers = HTTP::Headers{"Content-Type" => "multipart/byteranges; boundary=aA40"}
body = "--aA40\r\nContent-Type: text/plain\r\n\r\nbody\r\n--aA40--"
response = HTTP::Client::Response.new(
status: :ok,
headers: headers,
body: body,
)

MIME::Multipart.parse(response) do |headers, io|
headers["Content-Type"].should eq("text/plain")
io.gets_to_end.should eq("body")
end
end
end
end
45 changes: 44 additions & 1 deletion src/mime/multipart.cr
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,57 @@ module MIME::Multipart
#
# See: `Multipart::Parser`
def self.parse(request : HTTP::Request, &)
boundary = parse_boundary(request.headers["Content-Type"])
if content_type = request.headers["Content-Type"]?
boundary = parse_boundary(content_type)
end
return nil unless boundary

body = request.body
return nil unless body
parse(body, boundary) { |headers, io| yield headers, io }
end

# Parses a MIME multipart message, yielding `HTTP::Headers` and an `IO` for
# each body part.
#
# Please note that the IO object yielded to the block is only valid while the
# block is executing. The IO is closed as soon as the supplied block returns.
#
# ```
# require "http"
# require "mime/multipart"
#
# headers = HTTP::Headers{"Content-Type" => "multipart/byteranges; boundary=aA40"}
# body = "--aA40\r\nContent-Type: text/plain\r\n\r\nbody\r\n--aA40--"
# response = HTTP::Client::Response.new(
# status: :ok,
# headers: headers,
# body: body,
# )
#
# MIME::Multipart.parse(response) do |headers, io|
# headers["Content-Type"] # => "text/plain"
# io.gets_to_end # => "body"
# end
# ```
#
# See: `Multipart::Parser`
def self.parse(response : HTTP::Client::Response, &)
if content_type = response.headers["Content-Type"]?
boundary = parse_boundary(content_type)
end
return nil unless boundary

if body = response.body.presence
body = IO::Memory.new(body)
else
body = response.body_io?
end
return nil unless body

parse(body, boundary) { |headers, io| yield headers, io }
end

# Yields a `Multipart::Builder` to the given block, writing to *io* and
# using *boundary*. `#finish` is automatically called on the builder.
def self.build(io : IO, boundary : String = Multipart.generate_boundary, &)
Expand Down

0 comments on commit 8e1a9a8

Please sign in to comment.