diff options
author | Brian Palmer <brianp@instructure.com> | 2014-01-06 16:14:21 -0700 |
---|---|---|
committer | Santiago Pastorino <santiago@wyeworks.com> | 2014-11-27 11:14:37 -0200 |
commit | a7cb4302dd4b3fe532799b8303af2e6a8ad0a85c (patch) | |
tree | 42b4611238687ba3ea535a5f4d1b39209d28b332 | |
parent | 99a1a62572e6d75d29ef569c58eae70ed9ad3255 (diff) | |
download | rack-a7cb4302dd4b3fe532799b8303af2e6a8ad0a85c.tar.gz |
allow overriding the rack multipart parser tempfile class
This allows for more flexibility in how to buffer (or stream) multipart file uploads, rather than always using Tempfile and buffering to local TMPDIR.
-rw-r--r-- | lib/rack/multipart/parser.rb | 20 | ||||
-rw-r--r-- | test/spec_multipart.rb | 9 |
2 files changed, 21 insertions, 8 deletions
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb index fa90ca96..faa98c7e 100644 --- a/lib/rack/multipart/parser.rb +++ b/lib/rack/multipart/parser.rb @@ -18,10 +18,13 @@ module Rack content_length = env['CONTENT_LENGTH'] content_length = content_length.to_i if content_length - new($1, io, content_length, env) + tempfile = env['rack.multipart.tempfile_factory'] || lambda { |filename, content_type| Tempfile.new("RackMultipart") } + bufsize = env['rack.multipart.buffer_size'] || BUFSIZE + + new($1, io, content_length, env, tempfile, bufsize) end - def initialize(boundary, io, content_length, env) + def initialize(boundary, io, content_length, env, tempfile, bufsize) @buf = "" if @buf.respond_to? :force_encoding @@ -34,6 +37,8 @@ module Rack @content_length = content_length @boundary_size = Utils.bytesize(@boundary) + EOL.size @env = env + @tempfile = tempfile + @bufsize = bufsize if @content_length @content_length -= @boundary_size @@ -86,7 +91,7 @@ module Rack def fast_forward_to_first_boundary loop do - content = @io.read(BUFSIZE) + content = @io.read(@bufsize) raise EOFError, "bad content body" unless content @buf << content @@ -95,7 +100,7 @@ module Rack return if read_buffer == full_boundary end - raise EOFError, "bad content body" if Utils.bytesize(@buf) >= BUFSIZE + raise EOFError, "bad content body" if Utils.bytesize(@buf) >= @bufsize end end @@ -125,8 +130,7 @@ module Rack end if filename - extname = ::File.extname(filename) - (@env['rack.tempfiles'] ||= []) << body = Tempfile.new(["RackMultipart", extname]) + (@env['rack.tempfiles'] ||= []) << body = @tempfile.call(filename, content_type) body.binmode if body.respond_to?(:binmode) end @@ -138,7 +142,7 @@ module Rack body << @buf.slice!(0, @buf.size - (@boundary_size+4)) end - content = @io.read(@content_length && BUFSIZE >= @content_length ? @content_length : BUFSIZE) + content = @io.read(@content_length && @bufsize >= @content_length ? @content_length : @bufsize) raise EOFError, "bad content body" if content.nil? || content.empty? @buf << content @@ -223,7 +227,7 @@ module Rack # filename is blank which means no file has been selected return elsif filename - body.rewind + body.rewind if body.respond_to?(:rewind) # Take the basename of the upload's original filename. # This handles the full Windows paths given by Internet Explorer diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb index ca733dae..327c6a2a 100644 --- a/test/spec_multipart.rb +++ b/test/spec_multipart.rb @@ -171,6 +171,15 @@ describe Rack::Multipart do params["file1.txt"][:tempfile].read.should.equal "contents" end + should "parse multipart upload file using custom tempfile class" do + env = Rack::MockRequest.env_for("/", multipart_fixture(:text)) + my_tempfile = "" + env['rack.multipart.tempfile_factory'] = lambda { |filename, content_type| my_tempfile } + params = Rack::Multipart.parse_multipart(env) + params["files"][:tempfile].object_id.should.equal my_tempfile.object_id + my_tempfile.should.equal "contents" + end + should "parse multipart upload with nested parameters" do env = Rack::MockRequest.env_for("/", multipart_fixture(:nested)) params = Rack::Multipart.parse_multipart(env) |