Project

General

Profile

Bug #12674

Updated by shyouhei (Shyouhei Urabe) over 7 years ago

I wrote the following script to show the problem: 

 ```ruby <pre><code class="ruby"> 
 require 'socket' 
 require 'io/wait' 

 Thread.new do 
   server = TCPServer.new 2000 # Server bind to port 2000 
   loop do 
     client = server.accept      # Wait for a client to connect 
     client.puts "Hello !" 
     client.puts "Time is #{Time.now}" 
     sleep 3 
     client.close 
   end 
 end 

 puts "server is starting..." 
 sleep 2 

 puts "connecting now" 
 s = TCPSocket.new 'localhost', 2000 

 puts "connected" 

 10.times do 
   k = s.wait_readable(2) 
   puts "wait: #{k.inspect}" 
   v = s.read_nonblock(8, exception: false) 
   puts "val: #{v.inspect}" 

   break if v == nil  
 end 

 # server is starting... 
 # connecting now 
 # connected 
 # wait: #<TCPSocket:fd 11> 
 # val: "Hello !\n" 
 # wait: #<TCPSocket:fd 11> 
 # val: "Time is " 
 # wait: #<TCPSocket:fd 11> 
 # val: "2016-08-" 
 # wait: #<TCPSocket:fd 11> 
 # val: "14 16:30" 
 # wait: #<TCPSocket:fd 11> 
 # val: ":35 +020" 
 # wait: #<TCPSocket:fd 11> 
 # val: "0\n" 
 # wait: nil 
 # val: :wait_readable 
 # ISSUE! 
 # wait: nil 
 # val: nil 
 ``` </code></pre> 

 The problem there is, I'm going to `wait_readable(timeout)` ```wait_readable(timeout)``` on the socket, and it's going to return nil. This nil is ambiguous, as it can mean in the spec "timed out on wait" or "server has closed the socket". The only way I have to know exactly what it means is to call read_nonblock again. If nil, it's EOF. If wait_readable, it timed out. So I can't fail early.  

 The wait methods could benefit from a more explicit return value for #wait. This might have been subject to discussion previously, as "false" was removed as of ruby 2.3 from the possible return values, but I think it's an issue that might prevent correctness in socket programming. Take the example from the standard library: https://github.com/ruby/ruby/blob/trunk/lib/net/protocol.rb#L154-L159 

 If the server closes after :wait_readable was returned, wait_readable will return nil, and this will be interpreted as a timeout, instead of EOF. I don't know of any real-world issues with this using net/http, but there was one [here](https://github.com/httprb/http/issues/298).  


Back