Created
April 29, 2015 14:11
-
-
Save orenmazor/4b4909f5ac75b5ced102 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ----------------------------------------------------------------------------- | |
# | |
# 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