Open joankaradimov opened 7 years ago
I think this is useful. Thanks to your deep investigation, we know it's probably something to do with JRuby. However, please keep us updated! :)
I haven't logged a bug in JRuby yet. Should I? Could this be a problem in multipart-post?
Based on the backtrace, it looks like the issue is receiving the response:
from C:/dev/jruby-9.1.6.0/lib/ruby/stdlib/net/http/response.rb:40:in `read_status_line'
But it looks like it's not reading the response correctly and blocking, then timing out.
I don't think it's the fault of multipart-post
. If you want to dig a bit deeper, you could try doing a packet capture to see what data is actually being sent back to JRuby. this would be most informative.
I'm experiencing the same problem and I've written some dummy app/client to reproduce. I've put the code into the following repository: https://github.com/universal/jruby-multipart-post-test . I've been experiencing this problem for a while now and have circumvented it by using a "java based uploader", but this is not ideal ;)
Also not sure if it is an error of the multipart-post lib, or jruby... maybe there is a flush missing somewhere ;) Non-multipart-posts work fine.
The weirdest thing happened. A colleague of mine finished the piece of code on his Linux. And it now works on my Windows too. The file he is uploading is a plain text file full of the x
character. So it's obviously the content of the file. After a little digging I came up this piece of code that fails:
path = 'C:\dev\test.txt'
File.open(path, 'w') do |f|
f.print "\r\n"
end
File.open(path) do |file|
req = Net::HTTP::Post::Multipart.new(
uri('/objects'),
'uploadId' => upload_id.to_s,
'files[]' => UploadIO.new(file, 'text/plain', file_name),
'size' => size,
)
req[HttpHeaders::COOKIE] = cookie
req[CsrfProtectionFilter::HEADER_NAME] = 'UNUSED'
http.request(req)
end
... yep, CRLF
, obviously. Pretty much anything else I tried printing to the file works just fine (my other guesses included "\n", "\0", and a lone "\r").
P.S.
Opening the file like this File.open(path, 'rb')
worked fine.
@universal can you confirm this? I wasn't able to run the rails endpoint in jruby-multipart-post-test.
My initial guess was that the content_len
parameter of the build_head
function in parts.rb
had the wrong value. But that's not it. So I'll have to dig some more.
This is really awesome, everyone. Thanks for digging into this. So, it looks like a bug to do with the multi-part escaping if the file contains CRLF. Is that correct?
Opening the file in 'rb' mode works. My files have been binary all the time, so not sure it is really CRLF causing the error, though they obviously could have contained the byte sequence for CRLF. Maybe it is an encoding issue where too many bytes read in non-binary mode? To be honest I'm kinda clueless.
The variant with File.new also works if you supply the optional mode parameter: File.new("some_file", "rb")
Going to do some tries with the text-files :-)
I made some tests with the text-files, and as soon as the file contains a CRLF somewhere it fails, only on JRuby though. So I'm not sure if it just works by accident on Ruby, and it is actually correct to fail on JRuby, since one is supposed to read stuff in binary mode for multipart?
Checks with text files
####################
Trying with mode: r
Trying content: "\r"
success
Trying content: "\n"
success
Trying content: "\u0000"
success
Trying content: "\r\n"
>>> Failed! <<<
Trying content: "a\r"
success
Trying content: "a\n"
success
Trying content: "a\u0000"
success
Trying content: "a\r\n"
>>> Failed! <<<
Trying content: "\ra"
success
Trying content: "\na"
success
Trying content: "\u0000a"
success
Trying content: "\r\na"
>>> Failed! <<<
####################
Trying with mode: rb
Trying content: "\r"
success
Trying content: "\n"
success
Trying content: "\u0000"
success
Trying content: "\r\n"
success
Trying content: "a\r"
success
Trying content: "a\n"
success
Trying content: "a\u0000"
success
Trying content: "a\r\n"
success
Trying content: "\ra"
success
Trying content: "\na"
success
Trying content: "\u0000a"
success
Trying content: "\r\na"
success
finished
The problem appears to be a mismatch between the file size and the length of the byte array that is read from the file.
Using the example image.jpg
from universal/jruby-multipart-post-test I get:
irb(main):001:0> f = File.new('image.jpg')
=> #<File:image.jpg>
irb(main):002:0> f.size
=> 135524
irb(main):003:0> f.read.length
=> 135523
irb(main):004:0> f.close
=> nil
However, this keeps getting weirder. That mismatch exists in MRI as well (2.4.1-1 from rubyinstaller.org). And it looks even worse...
irb(main):001:0> f = File.new('image.jpg')
=> #<File:image.jpg>
irb(main):002:0> f.size
=> 135524
irb(main):003:0> f.read.length
=> 708
irb(main):004:0> f.close
=> nil
@universal What flavour of MRI Ruby are you using when testing? Is it from https://rubyinstaller.org/ or is it by chance something from Cygwin/MSYS2?
the mri ruby is the rubyinstaller one, 2.3.3 iirc. no cygwin. is there anything you'd like me to check/try?
following tests are on 2.4.1 under ubuntu :-)
2.4.1 :005 > f = File.open("image.jpg", "rb")
=> #<File:image.jpg>
2.4.1 :006 > f.size
=> 135524
2.4.1 :007 > f.read.length
=> 135524
2.4.1 :008 > rbr = f.read[0..100]
=> ""
2.4.1 :009 > f.rewind
=> 0
2.4.1 :010 > rbr = f.read[0..100]
=> "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x01\x01,\x01,\x00\x00\xFF\xE1\x01\x04Exif\x00\x00MM\x00*\x00\x00\x00\b\x00\x04\x01\x0F\x00\x02\x00\x00\x00\x12\x00\x00\x00>\x01\x10\x00\x02\x00\x00\x00\v\x00\x00\x00P\x82\x9A\x00\x05\x00\x00\x00\x01\x00\x00\x00[\x87i\x00\x04\x00\x00\x00\x01\x00\x00\x00c\x00\x00\x00\x00NIKON COR"
2.4.1 :011 > f.close
=> nil
2.4.1 :012 > f = File.open("image.jpg")
=> #<File:image.jpg>
2.4.1 :013 > rdr = f.read[0..100]
=> "\xFF\xD8\xFF\xE0\u0000\u0010JFIF\u0000\u0001\u0001\u0001\u0001,\u0001,\u0000\u0000\xFF\xE1\u0001\u0004Exif\u0000\u0000MM\u0000*\u0000\u0000\u0000\b\u0000\u0004\u0001\u000F\u0000\u0002\u0000\u0000\u0000\u0012\u0000\u0000\u0000>\u0001\u0010\u0000\u0002\u0000\u0000\u0000\v\u0000\u0000\u0000P\x82\x9A\u0000\u0005\u0000\u0000\u0000\u0001\u0000\u0000\u0000[\x87i\u0000\u0004\u0000\u0000\u0000\u0001\u0000\u0000\u0000c\u0000\u0000\u0000\u0000NIKON COR"
2.4.1 :014 > f.close
=> nil
2.4.1 :016 > rdr.encoding
=> #<Encoding:UTF-8>
2.4.1 :017 > rbr.encoding
=> #<Encoding:ASCII-8BIT>
f = File.open("image.jpg")
=> #<File:image.jpg>
2.4.1 :020 > f.read.bytesize
=> 135524
so reading in default mode, reads it as a utf-8 encoded string, so multiple bytes get coerced into one "char", so size in bytes and length don't match anymore!
I am experiencing an issue in
multipart-post
2.0.0. I have a client piece of code that looks roughly like this:The code runs on JRuby 9.1.6.0 on Windows 10. When I run it I get this stack trace:
On the server side there is a Java + Tomcat on Windows. The output from the server is
java.net.SocketTimeoutException: null
. The stack trace on the server is most likely irrelevant.I've tried identical code on a JRuby/Linux client with the exact same Java/Windows server and it worked. Furthermore - I tried MRI/Windows client and it worked too. I also tried some older builds of JRuby I have locally and they failed. So there appears to be some incompatibility in
multipart-post
with JRuby on Windows.I'm not even sure if I should be logging this here. Still it's probably good to have it here so that it's visible for users of the library. Is there something that can be done other than forwarding this to the JRuby guys?