diff --git a/lib/http/timeout/per_operation.rb b/lib/http/timeout/per_operation.rb
index <HASH>..<HASH> 100644
--- a/lib/http/timeout/per_operation.rb
+++ b/lib/http/timeout/per_operation.rb
@@ -29,7 +29,7 @@ module HTTP
       def connect_ssl
         rescue_readable do
           rescue_writable do
-            socket.connect_nonblock
+            @socket.connect_nonblock
           end
         end
       end
@@ -66,7 +66,7 @@ module HTTP
               return result
             end
 
-            unless @socket.to_io.wait_readable(read_timeout)
+            unless IO.select([@socket], nil, nil, read_timeout)
               fail TimeoutError, "Read timed out after #{read_timeout} seconds"
             end
           end
@@ -78,13 +78,12 @@ module HTTP
             result = @socket.write_nonblock(data, :exception => false)
             return result unless result == :wait_writable
 
-            unless @socket.to_io.wait_writable(write_timeout)
+            unless IO.select(nil, [@socket], nil, write_timeout)
               fail TimeoutError, "Write timed out after #{write_timeout} seconds"
             end
           end
         end
       end
-      # rubocop:enable Metrics/BlockNesting
     end
   end
 end
Revert to IO.select for PerOperation timeouts (fixes #<I>)

I have tried the repro case from #<I> from this and this appears to
fix the problem.

Not sure why: the intended semantics appear to be equivalent. Possibly
a MRI bug that was fixed in <I>?