diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2015-08-28 09:32:22 -0700 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2015-08-28 09:32:22 -0700 |
commit | 3b510485e3cddaf07bdc54550d3075e8957fe6ad (patch) | |
tree | f55ae5c5dc116eb53f7786cb91304a511ba9079d | |
parent | e00488827a0c100712f4f8ac6c17b2ce414c8bb7 (diff) | |
download | rack-3b510485e3cddaf07bdc54550d3075e8957fe6ad.tar.gz |
wrap bounded IO objects
If we have a content length, wrap the IO object with a new object that knows about the content length and will act like an IO object with the length specified
-rw-r--r-- | lib/rack/multipart.rb | 1 | ||||
-rw-r--r-- | lib/rack/multipart/parser.rb | 57 |
2 files changed, 41 insertions, 17 deletions
diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb index b1625437..1c7c4c35 100644 --- a/lib/rack/multipart.rb +++ b/lib/rack/multipart.rb @@ -36,6 +36,7 @@ module Rack class << self def parse_multipart(env, params = Rack::Utils.default_query_parser) + return if env['CONTENT_LENGTH'] == '0' Parser.create(env, params).parse end diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb index b04d39bb..7fab856e 100644 --- a/lib/rack/multipart/parser.rb +++ b/lib/rack/multipart/parser.rb @@ -10,6 +10,41 @@ module Rack DUMMY = Struct.new(:parse).new + class BoundedIO # :nodoc: + def initialize(io, content_length) + @io = io + @content_length = content_length + @cursor = 0 + end + + def read(size) + return if @cursor >= @content_length + + left = @content_length - @cursor + + str = if left < size + @io.read left + else + @io.read size + end + + if str + @cursor += str.bytesize + else + # Raise an error for mismatching Content-Length and actual contents + raise EOFError, "bad content body" + end + + str + end + + def eof?; @content_length == @cursor; end + + def rewind + @io.rewind + end + end + def self.create(env, query_parser) return DUMMY unless env['CONTENT_TYPE'] =~ MULTIPART @@ -22,27 +57,23 @@ module Rack tempfile = env[RACK_MULTIPART_TEMPFILE_FACTORY] || lambda { |filename, content_type| Tempfile.new(["RackMultipart", ::File.extname(filename)]) } bufsize = env[RACK_MULTIPART_BUFFER_SIZE] || BUFSIZE + io = BoundedIO.new(io, content_length) if content_length - new($1, io, content_length, env, tempfile, bufsize, query_parser) + new($1, io, env, tempfile, bufsize, query_parser) end - def initialize(boundary, io, content_length, env, tempfile, bufsize, query_parser) + def initialize(boundary, io, env, tempfile, bufsize, query_parser) @buf = "".force_encoding(Encoding::ASCII_8BIT) @query_parser = query_parser @params = query_parser.make_params @boundary = "--#{boundary}" @io = io - @content_length = content_length @boundary_size = @boundary.bytesize + EOL.size @env = env @tempfile = tempfile @bufsize = bufsize - if @content_length - @content_length -= @boundary_size - end - @rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n @full_boundary = @boundary + EOL end @@ -148,11 +179,10 @@ 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(@bufsize) handle_empty_content!(content) and break @buf << content - @content_length -= content.size if @content_length end [head, filename, content_type, name, body, file] @@ -262,14 +292,7 @@ module Rack def handle_empty_content!(content) if content.nil? || content.empty? - # Raise an error for mismatching Content-Length and actual contents - raise EOFError, "bad content body" if @content_length.to_i > 0 - - # In case we receive a POST request with empty body, reset @content_length - # and return empty string - @content_length = 0 - @buf = "" - + raise EOFError if @io.eof? return true end end |