Webmock: Posting a tempfile tries to rewind a closed stream
I'm sure you're loving all these webmock issues: ```rb require "webmock" require "httpx" require "httpx/adapters/webmock" include WebMock::API WebMock.enable! def open(path) open_tempfile do |file| download path, file yield file end end def open_tempfile file = Tempfile.open("tmp") begin yield file ensure file.close! end end def download(path, file) file.binmode download_impl(path) { |chunk| file.write(chunk) } file.flush file.rewind end def download_impl(path, &block) File.open(path, "rb") do |file| while data = file.read(5242880) yield data end end end open(__FILE__) do |file| HTTPX.post("https://example.com", form: { file: file }) end ``` This results in webmock trying to close an already closed stream: ``` $ ruby test.rb /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/3.3.0/delegate.rb:349:in `rewind': closed stream (IOError) from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/3.3.0/delegate.rb:349:in `block in delegating_block' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/transcoder/multipart/encoder.rb:39:in `block in rewind' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/transcoder/multipart/encoder.rb:37:in `each' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/transcoder/multipart/encoder.rb:37:in `each_with_object' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/transcoder/multipart/encoder.rb:37:in `rewind' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/transcoder/multipart/encoder.rb:24:in `to_s' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/3.3.0/delegate.rb:87:in `method_missing' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/adapters/webmock.rb:23:in `build_webmock_request_signature' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/adapters/webmock.rb:104:in `send' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:132:in `block in send_request' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:130:in `catch' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:130:in `send_request' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:254:in `block in _send_requests' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:253:in `each' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:253:in `_send_requests' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:245:in `send_requests' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/session.rb:72:in `request' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/chainable.rb:17:in `request' from /home/earlopain/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/httpx-1.3.3/lib/httpx/chainable.rb:10:in `post' from test.rb:40:in `block in <main>' from test.rb:11:in `block in open' from test.rb:18:in `open_tempfile' from test.rb:9:in `open' from test.rb:39:in `<main>' ``` This is caused by the last webmock fix in https://gitlab.com/os85/httpx/-/merge_requests/352. The code above is how Rails ActiveStorage works with the DiskService and real code would look more along the lines of this: ```rb user.avatar.open do |file| HTTPX.post("https://example.com", form: { file: file }) end ``` I believe the code that is making trouble here is this: https://gitlab.com/os85/httpx/-/blob/91b9e13cd07247609d8504409c7c3628e8279ece/lib/httpx/transcoder/multipart/encoder.rb#L38 A "real" Tempfile created with `Tempfile.open`/`Tempfile.new` is not `is_a?(File)`. That at least is the most immediate place to look at for a cause. Simply changing the condition to `if (val.is_a?(File) || val.is_a?(Tempfile)) && val.closed?` does work but I'm not sure if that is a proper fix.
issue