mirror of
https://github.com/lostisland/faraday.git
synced 2025-10-04 00:02:03 -04:00
fix file uploads with Ruby 2.0 net/http
Rewrite Faraday::CompositeReadIO because the one inherited from multipart-post library doesn't behave well when used with IO.copy_stream which net/http uses internally in Ruby 2.0.
This commit is contained in:
parent
8775c07495
commit
f619a5d334
@ -8,13 +8,49 @@ rescue LoadError
|
||||
end
|
||||
|
||||
module Faraday
|
||||
class CompositeReadIO < ::CompositeReadIO
|
||||
attr_reader :length
|
||||
# Similar but not compatible with ::CompositeReadIO provided by multipart-post.
|
||||
class CompositeReadIO
|
||||
def initialize(*parts)
|
||||
@parts = parts.flatten
|
||||
@ios = @parts.map { |part| part.to_io }
|
||||
@index = 0
|
||||
end
|
||||
|
||||
def initialize(parts)
|
||||
@length = parts.inject(0) { |sum, part| sum + part.length }
|
||||
ios = parts.map{ |part| part.to_io }
|
||||
super(*ios)
|
||||
def length
|
||||
@parts.inject(0) { |sum, part| sum + part.length }
|
||||
end
|
||||
|
||||
def rewind
|
||||
@ios.each { |io| io.rewind }
|
||||
@index = 0
|
||||
end
|
||||
|
||||
# Read from IOs in order until `length` bytes have been received.
|
||||
def read(length = nil, outbuf = nil)
|
||||
got_result = false
|
||||
outbuf = outbuf ? outbuf.replace("") : ""
|
||||
|
||||
while io = current_io
|
||||
if result = io.read(length)
|
||||
got_result ||= !result.nil?
|
||||
result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
|
||||
outbuf << result
|
||||
length -= result.length if length
|
||||
break if length == 0
|
||||
end
|
||||
advance_io
|
||||
end
|
||||
(!got_result && length) ? nil : outbuf
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_io
|
||||
@ios[@index]
|
||||
end
|
||||
|
||||
def advance_io
|
||||
@index += 1
|
||||
end
|
||||
end
|
||||
|
||||
|
107
test/composite_read_io_test.rb
Normal file
107
test/composite_read_io_test.rb
Normal file
@ -0,0 +1,107 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
require 'stringio'
|
||||
|
||||
class CompositeReadIOTest < MiniTest::Unit::TestCase
|
||||
Part = Struct.new(:to_io) do
|
||||
def length() to_io.string.length end
|
||||
end
|
||||
|
||||
def part(str)
|
||||
Part.new StringIO.new(str)
|
||||
end
|
||||
|
||||
def composite_io(*parts)
|
||||
Faraday::CompositeReadIO.new(*parts)
|
||||
end
|
||||
|
||||
def test_empty
|
||||
io = composite_io
|
||||
assert_equal 0, io.length
|
||||
assert_equal "", io.read
|
||||
end
|
||||
|
||||
def test_empty_returns_nil_for_limited_read
|
||||
assert_nil composite_io.read(1)
|
||||
end
|
||||
|
||||
def test_empty_parts_returns_nil_for_limited_read
|
||||
io = composite_io(part(""), part(""))
|
||||
assert_nil io.read(1)
|
||||
end
|
||||
|
||||
def test_multipart_read_all
|
||||
io = composite_io(part("abcd"), part("1234"))
|
||||
assert_equal 8, io.length
|
||||
assert_equal "abcd1234", io.read
|
||||
end
|
||||
|
||||
def test_multipart_read_limited
|
||||
io = composite_io(part("abcd"), part("1234"))
|
||||
assert_equal "abc", io.read(3)
|
||||
assert_equal "d12", io.read(3)
|
||||
assert_equal "34", io.read(3)
|
||||
assert_equal nil, io.read(3)
|
||||
assert_equal nil, io.read(3)
|
||||
end
|
||||
|
||||
def test_multipart_read_limited_size_larger_than_part
|
||||
io = composite_io(part("abcd"), part("1234"))
|
||||
assert_equal "abcd12", io.read(6)
|
||||
assert_equal "34", io.read(6)
|
||||
assert_equal nil, io.read(6)
|
||||
end
|
||||
|
||||
def test_multipart_read_with_blank_parts
|
||||
io = composite_io(part(""), part("abcd"), part(""), part("1234"), part(""))
|
||||
assert_equal "abcd12", io.read(6)
|
||||
assert_equal "34", io.read(6)
|
||||
assert_equal nil, io.read(6)
|
||||
end
|
||||
|
||||
def test_multipart_rewind
|
||||
io = composite_io(part("abcd"), part("1234"))
|
||||
assert_equal "abc", io.read(3)
|
||||
assert_equal "d12", io.read(3)
|
||||
io.rewind
|
||||
assert_equal "abc", io.read(3)
|
||||
assert_equal "d1234", io.read(5)
|
||||
assert_equal nil, io.read(3)
|
||||
io.rewind
|
||||
assert_equal "ab", io.read(2)
|
||||
end
|
||||
|
||||
if IO.respond_to?(:copy_stream)
|
||||
def test_compatible_with_copy_stream
|
||||
target_io = StringIO.new
|
||||
io = composite_io(part("abcd"), part("1234"))
|
||||
|
||||
Faraday::Timer.timeout(1) do
|
||||
IO.copy_stream(io, target_io)
|
||||
end
|
||||
assert_equal "abcd1234", target_io.string
|
||||
end
|
||||
end
|
||||
|
||||
unless RUBY_VERSION < '1.9'
|
||||
def test_read_from_multibyte
|
||||
File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8|
|
||||
io = composite_io(part("\x86"), Part.new(utf8))
|
||||
assert_equal bin("\x86\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read
|
||||
end
|
||||
end
|
||||
|
||||
def test_limited_from_multibyte
|
||||
File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8|
|
||||
io = composite_io(part("\x86"), Part.new(utf8))
|
||||
assert_equal bin("\x86\xE3\x83"), io.read(3)
|
||||
assert_equal bin("\x95\xE3\x82"), io.read(3)
|
||||
assert_equal bin("\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read(8)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bin(str)
|
||||
str.force_encoding("BINARY") if str.respond_to?(:force_encoding)
|
||||
str
|
||||
end
|
||||
end
|
1
test/multibyte.txt
Normal file
1
test/multibyte.txt
Normal file
@ -0,0 +1 @@
|
||||
ファイル
|
Loading…
x
Reference in New Issue
Block a user