Project

General

Profile

Actions

Bug #18661

closed

Net::HTTP behavior changed between 2.6 and 3.1 on windows.

Bug #18661: Net::HTTP behavior changed between 2.6 and 3.1 on windows.

Added by jleblanc (Joshua LeBlanc) over 4 years ago. Updated 18 days ago.

Status:
Closed
Assignee:
Target version:
-
ruby -v:
ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x64-mingw-ucrt]
[ruby-core:108069]

Description

We are upgrading a rails application from Ruby 2.6 to Ruby 3.1 on Windows.

Running rails systems tests hang on Ruby 3.1, while they succeed on Ruby 2.6.

I tracked this down to Ruby 3.1's Net::HTTP using Socket.tcp rather than the old TCPSocket.

Specifically, in socket.rb, connect_internal calls connect_nonblock(self, exception: false), which ultimately hangs until timing out on windows.
Modifying the socket.rb source to use connect(self) instead results in a successful operation.

To be clear, the hanging operation is socket.rb#connect_nonblock, which is on line 1214

Reproduction:

  1. Install Ruby 3.1 on Windows - I used RubyInstaller: https://github.com/oneclick/rubyinstaller2/releases/download/RubyInstaller-3.1.1-1/rubyinstaller-devkit-3.1.1-1-x64.exe
  2. Clone the reproduction project: https://github.com/joshleblanc/windows_net_http_problem
  3. Run bundle install
  4. Run rails test:system

Chrome will open, however a connection will never be made, ultimately timing out.

To test this same process in earlier versions of ruby, simply create a new rails project with rails new -O -J -S <name>, add the ffi and tzinfo-data gems to the gemfile, and scaffold a new resource. Running rails test:system from this point should succeed.

Updated by hsbt (Hiroshi SHIBATA) 2 months ago Actions #1

  • Assignee set to windows

Updated by hsbt (Hiroshi SHIBATA) 18 days ago Actions #2 [ruby-core:126672]

  • Status changed from Open to Closed

Fixed on master by https://github.com/ruby/ruby/pull/18611.

The hang is not inside connect_nonblock. Winsock reports a failed non-blocking connect(2) only through the exceptfds of select(2), and IO#wait_writable never asks for that set, so Addrinfo#connect_internal kept waiting on a connection that had already been refused and burned the whole timeout. On Windows Socket.tcp("127.0.0.1", <closed port>, connect_timeout: 5) raised Errno::ETIMEDOUT after 5.01 seconds and now raises Errno::ECONNREFUSED after 2.06 seconds, which is Winsock's own latency before it reports the reset.

SO_ERROR also carried a raw WinSock error code instead of an errno, so the failure surfaced as a bare SystemCallError. That is fixed as well.

I could not reproduce the Rails system test end to end, so please reopen if it still hangs when you try it after the 4.1.0 release.

Actions

Also available in: PDF Atom