-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a squashed commit of [PR#12][] with some tiny cleanups applied on top of that. [PR#12]: #12 Allow any IO object in FormData::File ------------------------------------- Previously we allowed only File and StringIO objects as an input to `FormData::File`, but we can generalize that to any IO object that responds to `#read` and `#size` (which includes `Tempfile`, `ActionDispatch::Http::UploadedFile` etc). Open File for given path in binary mode --------------------------------------- That way different operating systems won't attempt to convert newline characters to their internal representation, instead the file content will always be retrieved byte-for-byte as is. Officially support Pathname in FormData::File.new ------------------------------------------------- Previously Pathname was implicitly supported, though extracting filename wasn't working. With the recent refactoring this stopped working, so we make the Pathname support explicit. Make all components into IO objects ----------------------------------- By changing all components to use an IO object as a base, we can implement a common IO interface for all components, which delegates to the underlying IO object. This enables streaming multipart data into the request body, avoiding loading the whole multipart data into memory when File parts are backed by File objects. See httprb/http#409 for the new streaming API. Make CompositeIO convert strings to StringIOs --------------------------------------------- By delegating handling strings to CompositeIO we can remove a lot of the StringIO.new clutter when instantiating CompositeIO objects. Use a buffer when reading IO files in CompositeIO ------------------------------------------------- This way we're not creating a new string for each chunk read, instead each chunk will be read into an existing string object (a "buffer"), replacing any previous content.
- Loading branch information
Showing
11 changed files
with
462 additions
and
135 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# frozen_string_literal: true | ||
|
||
require "stringio" | ||
|
||
module HTTP | ||
module FormData | ||
# Provides IO interface across multiple IO objects. | ||
class CompositeIO | ||
# @param [Array<IO>] ios Array of IO objects | ||
def initialize(ios) | ||
@index = 0 | ||
@buffer = String.new | ||
@ios = ios.map do |io| | ||
if io.is_a?(String) | ||
StringIO.new(io) | ||
elsif io.respond_to?(:read) | ||
io | ||
else | ||
raise ArgumentError, | ||
"#{io.inspect} is neither a String nor an IO object" | ||
end | ||
end | ||
end | ||
|
||
# Reads and returns partial content acrosss multiple IO objects. | ||
# | ||
# @param [Integer] length Number of bytes to retrieve | ||
# @param [String] outbuf String to be replaced with retrieved data | ||
# | ||
# @return [String, nil] | ||
def read(length = nil, outbuf = nil) | ||
outbuf = outbuf.to_s.replace("") | ||
|
||
while current_io | ||
current_io.read(length, @buffer) | ||
outbuf << @buffer | ||
|
||
if length | ||
length -= @buffer.length | ||
break if length.zero? | ||
end | ||
|
||
advance_io | ||
end | ||
|
||
outbuf unless length && outbuf.empty? | ||
end | ||
|
||
# Returns sum of all IO sizes. | ||
def size | ||
@size ||= @ios.map(&:size).inject(0, :+) | ||
end | ||
|
||
# Rewinds all IO objects and set cursor to the first IO object. | ||
def rewind | ||
@ios.each(&:rewind) | ||
@index = 0 | ||
end | ||
|
||
private | ||
|
||
# Returns IO object under the cursor. | ||
def current_io | ||
@ios[@index] | ||
end | ||
|
||
# Advances cursor to the next IO object. | ||
def advance_io | ||
@index += 1 | ||
end | ||
end | ||
end | ||
end |
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 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 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 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
Oops, something went wrong.