Skip to content

Instantly share code, notes, and snippets.

@orenmazor
Created April 29, 2015 14:11
Show Gist options
  • Save orenmazor/4b4909f5ac75b5ced102 to your computer and use it in GitHub Desktop.
Save orenmazor/4b4909f5ac75b5ced102 to your computer and use it in GitHub Desktop.
# -----------------------------------------------------------------------------
#
# This file is the copyrighted property of Tableau Software and is protected
# by registered patents and other applicable U.S. and international laws and
# regulations.
#
# Unlicensed use of the contents of this file is prohibited. Please refer to
# the NOTICES.txt file for further details.
#
# -----------------------------------------------------------------------------
module Rack
module Utils
def rfc2822(time)
wday = Time::RFC2822_DAY_NAME[time.wday]
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
#time.strftime("#{wday}, %d-#{mon}-%Y %T GMT") but %T is broken in 1.8.7 and windows
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
end
module_function :rfc2822
# Patch Rack::Utils::Multipart#parse_multipart in rack-1.2.2 to incorporate
# some bug fixes as well as some Tableau-specific customizations. Most of
# this function is copied from parse_multipart in gems/rack-1.2.2/lib/rack/utils.rb.
# Changes to the original function are indicated with a PATCH comment below.
module Multipart
def self.parse_multipart(env)
unless env['CONTENT_TYPE'] =~
%r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
nil
else
boundary = "--#{$1}"
params = {}
buf = ""
content_length = env['CONTENT_LENGTH'].to_i
input = env['rack.input']
input.rewind
boundary_size = Utils.bytesize(boundary) + EOL.size
bufsize = 16384
content_length -= boundary_size
read_buffer = ''
status = input.read(boundary_size, read_buffer)
raise EOFError, "bad content body" unless status == boundary + EOL
rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
loop {
head = nil
body = ''
filename = content_type = name = nil
until head && buf =~ rx
if !head && i = buf.index(EOL+EOL)
head = buf.slice!(0, i+2) # First \r\n
buf.slice!(0, 2) # Second \r\n
token = /[^\s()<>,;:\\"\/\[\]?=]+/
condisp = /Content-Disposition:\s*#{token}\s*/i
dispparm = /;\s*(#{token})=("(?:\\"|[^"])*"|#{token})*/
rfc2183 = /^#{condisp}(#{dispparm})+$/i
broken_quoted = /^#{condisp}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{token}=)/i
broken_unquoted = /^#{condisp}.*;\sfilename=(#{token})/i
if head =~ rfc2183
filename = Hash[head.scan(dispparm)]['filename']
filename = $1 if filename and filename =~ /^"(.*)"$/
elsif head =~ broken_quoted
filename = $1
elsif head =~ broken_unquoted
filename = $1
end
if filename && filename !~ /\\[^\\"]/
filename = Utils.unescape(filename).gsub(/\\(.)/, '\1')
end
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
body = Tempfile.new("RackMultipart")
body.binmode if body.respond_to?(:binmode)
end
next
end
# Save the read body part.
if head && (boundary_size+4 < buf.size)
body << buf.slice!(0, buf.size - (boundary_size+4))
end
c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
raise EOFError, "bad content body" if c.nil? || c.empty?
buf << c
content_length -= c.size
end
# Save the rest.
if i = buf.index(rx)
body << buf.slice!(0, i)
# PATCH: The original code advanced the buffer by boundary_size+2
# which is incorrect when the boundary is not preceeded by an EOL.
# Change this to advance the buffer by the length of the boundary
# matched by rx.
buf.slice!(0, $&.length)
content_length = -1 if $1 == "--"
end
if filename == ""
# filename is blank which means no file has been selected
data = nil
elsif filename
body.rewind
# Take the basename of the upload's original filename.
# This handles the full Windows paths given by Internet Explorer
# (and perhaps other broken user agents) without affecting
# those which give the lone filename.
filename = filename.split(/[\/\\]/).last
data = {:filename => filename, :type => content_type,
:name => name, :tempfile => body, :head => head}
## PATCH: Fix bug where rewind would be called on a String.
## @see https://github.com/brendan/rack/commit/8f7fcf0fdd9fd99aa529159d05c63dc2b0bfe08a
elsif !filename && content_type && body.is_a?(Tempfile)
body.rewind
# Generic multipart cases, not coming from a form
data = {:type => content_type,
:name => name, :tempfile => body, :head => head}
else
data = body
end
Utils.normalize_params(params, name, data) unless data.nil?
# break if we're at the end of a buffer, but not if it is the end of a field
break if (buf.empty? && $1 != EOL) || content_length == -1
}
input.rewind
params
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment