about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2019-09-05 05:32:53 +0200
committerEugen Rochko <eugen@zeonfederated.com>2019-09-05 05:32:53 +0200
commit1653b587778db0df201507115d19c0530096a5e3 (patch)
treeb7b0c7a51371d3d72d9cebd6ca05bb0aa0cd4c07
parent529856a6087a88a14fe19ec0ede96573761bfe34 (diff)
Attempt to concurrently connect to remote IP addresses (#11757)
* Attempt to concurrently connect to remote IP addresses

* Reduce code length to please CodeClimate 🤷
-rw-r--r--app/lib/request.rb52
1 files changed, 35 insertions, 17 deletions
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 9d874fe2c..42ccc6513 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -191,6 +191,9 @@ class Request
           end
         end
 
+        socks = []
+        addr_by_socket = {}
+
         addresses.each do |address|
           begin
             check_private_address(address)
@@ -200,30 +203,45 @@ class Request
 
             sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
 
-            begin
-              sock.connect_nonblock(sockaddr)
-            rescue IO::WaitWritable
-              if IO.select(nil, [sock], nil, Request::TIMEOUT[:connect])
-                begin
-                  sock.connect_nonblock(sockaddr)
-                rescue Errno::EISCONN
-                  # Yippee!
-                rescue
-                  sock.close
-                  raise
-                end
-              else
-                sock.close
-                raise HTTP::TimeoutError, "Connect timed out after #{Request::TIMEOUT[:connect]} seconds"
-              end
-            end
+            sock.connect_nonblock(sockaddr)
 
+            # If that hasn't raised an exception, we somehow managed to connect
+            # immediately, close pending sockets and return immediately
+            socks.each(&:close)
             return sock
+          rescue IO::WaitWritable
+            socks << sock
+            addr_by_socket[sock] = sockaddr
           rescue => e
             outer_e = e
           end
         end
 
+        until socks.empty?
+          _, available_socks, = IO.select(nil, socks, nil, Request::TIMEOUT[:connect])
+
+          if available_socks.nil?
+            socks.each(&:close)
+            raise HTTP::TimeoutError, "Connect timed out after #{Request::TIMEOUT[:connect]} seconds"
+          end
+
+          available_socks.each do |sock|
+            socks.delete(sock)
+
+            begin
+              sock.connect_nonblock(addr_by_socket[sock])
+            rescue Errno::EISCONN
+            rescue => e
+              sock.close
+              outer_e = e
+              next
+            end
+
+            socks.each(&:close)
+            return sock
+          end
+        end
+
         if outer_e
           raise outer_e
         else