diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb index 96396b0a..5071d2ad 100644 --- a/lib/net/http/generic_request.rb +++ b/lib/net/http/generic_request.rb @@ -341,7 +341,7 @@ def encode_multipart_form_data(out, params, opt) end chunked_p = chunked? - buf = +'' + buf = ''.b params.each do |key, value, h={}| key = quote_string(key, charset) filename = @@ -349,7 +349,7 @@ def encode_multipart_form_data(out, params, opt) value.respond_to?(:to_path) ? File.basename(value.to_path) : nil - buf << "--#{boundary}\r\n" + buf << "--#{boundary}\r\n".b if filename filename = quote_string(filename, charset) type = (h[:content_type] || 'application/octet-stream').to_s @@ -358,10 +358,10 @@ def encode_multipart_form_data(out, params, opt) end buf << "Content-Disposition: form-data; " \ "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \ - "Content-Type: #{type}\r\n\r\n" + "Content-Type: #{type}\r\n\r\n".b if !out.respond_to?(:write) || !value.respond_to?(:read) # if +out+ is not an IO or +value+ is not an IO - buf << (value.respond_to?(:read) ? value.read : value) + buf << (value.respond_to?(:read) ? value.read : value).b elsif value.respond_to?(:size) && chunked_p # if +out+ is an IO and +value+ is a File, use IO.copy_stream flush_buffer(out, buf, chunked_p) @@ -378,12 +378,12 @@ def encode_multipart_form_data(out, params, opt) # HTML5 says, "The parts of the generated multipart/form-data # resource that correspond to non-file fields must not have a # Content-Type header specified." - buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n" - buf << (value.respond_to?(:read) ? value.read : value) + buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n".b + buf << (value.respond_to?(:read) ? value.read : value).b end buf << "\r\n" end - buf << "--#{boundary}--\r\n" + buf << "--#{boundary}--\r\n".b flush_buffer(out, buf, chunked_p) out << "0\r\n\r\n" if chunked_p end diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index 590f8a2f..140a614b 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -897,6 +897,46 @@ def test_set_form } end + def test_set_form_multipart_with_mixed_encodings + utf8 = "r\u{e4}ksm\u{f6}rg\u{e5}s".freeze + binary = "r\u{e4}kmacka".b.freeze + data = [ + ['utf8', StringIO.new(utf8)], + ["bin\u{e4}ry", StringIO.new(binary)], + ['file', utf8, filename: "f\u{e4}le"], + ['file', binary, filename: 'binary'] + ] + expected = <<"__EOM__".b.gsub(/\n/, "\r\n") +-- +Content-Disposition: form-data; name="utf8" + +r\u{e4}ksm\u{f6}rg\u{e5}s +-- +Content-Disposition: form-data; name="bin\u{e4}ry" + +r\u{e4}kmacka +-- +Content-Disposition: form-data; name="file"; filename="f\u{e4}le" +Content-Type: application/octet-stream + +r\u{e4}ksm\u{f6}rg\u{e5}s +-- +Content-Disposition: form-data; name="file"; filename="binary" +Content-Type: application/octet-stream + +r\u{e4}kmacka +---- +__EOM__ + [false, true].each do |chunked| + start {|http| + @server.mount('/', lambda {|req, res| res.body = req.body }) + _test_set_form_multipart(http, chunked, data, expected) + } + end + assert_equal(Encoding::UTF_8, utf8.encoding) + assert_equal(Encoding::BINARY, binary.encoding) + end + def _test_set_form_urlencoded(http, data) req = Net::HTTP::Post.new('/') req.set_form(data)