Skip to content

Commit

Permalink
better multipart handling
Browse files Browse the repository at this point in the history
  • Loading branch information
eTM authored and leahneukirchen committed Apr 11, 2009
1 parent 3690709 commit 6674f36
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 6 deletions.
16 changes: 15 additions & 1 deletion lib/rack/request.rb
Expand Up @@ -92,6 +92,14 @@ def head?; request_method == "HEAD" end
'multipart/form-data'
]

# The set of media-types. Requests that do not indicate
# one of the media types presents in this list will not be eligible
# for param parsing like soap attachments or generic multiparts
PARSEABLE_DATA_MEDIA_TYPES = [
'multipart/related',
'multipart/mixed'
]

# Determine whether the request body contains form-data by checking
# the request media_type against registered form-data media-types:
# "application/x-www-form-urlencoded" and "multipart/form-data". The
Expand All @@ -101,6 +109,12 @@ def form_data?
FORM_DATA_MEDIA_TYPES.include?(media_type)
end

# Determine whether the request body contains data by checking
# the request media_type against registered parse-data media-types
def parseable_data?
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
end

# Returns the data recieved in the query string.
def GET
if @env["rack.request.query_string"] == query_string
Expand All @@ -119,7 +133,7 @@ def GET
def POST
if @env["rack.request.form_input"].eql? @env["rack.input"]
@env["rack.request.form_hash"]
elsif form_data?
elsif form_data? || parseable_data?
@env["rack.request.form_input"] = @env["rack.input"]
unless @env["rack.request.form_hash"] =
Utils::Multipart.parse_multipart(env)
Expand Down
16 changes: 11 additions & 5 deletions lib/rack/utils.rb
Expand Up @@ -293,7 +293,7 @@ module Multipart

def self.parse_multipart(env)
unless env['CONTENT_TYPE'] =~
%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
%r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
nil
else
boundary = "--#{$1}"
Expand All @@ -319,15 +319,15 @@ def self.parse_multipart(env)
filename = content_type = name = nil

until head && buf =~ rx
if !head && i = buf.index("\r\n\r\n")
if !head && i = buf.index(EOL+EOL)
head = buf.slice!(0, i+2) # First \r\n
buf.slice!(0, 2) # Second \r\n

filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
content_type = head[/Content-Type: (.*)\r\n/ni, 1]
name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]

if filename
if content_type || filename
body = Tempfile.new("RackMultipart")
body.binmode if body.respond_to?(:binmode)
end
Expand Down Expand Up @@ -369,6 +369,12 @@ def self.parse_multipart(env)

data = {:filename => filename, :type => content_type,
:name => name, :tempfile => body, :head => head}
elsif !filename && content_type
body.rewind

# Generic multipart cases, not coming from a form
data = {:type => content_type,
:name => name, :tempfile => body, :head => head}
else
data = body
end
Expand Down

1 comment on commit 6674f36

@josh
Copy link
Contributor

@josh josh commented on 6674f36 Apr 11, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should include at least one test case for “multipart/related” uploads.

Please sign in to comment.