Project

General

Profile

Bug #12674

io/wait: not handling the case when the socket is closed before doing wait_readable/writable with timeout

Added by chucke (Tiago Cardoso) about 3 years ago. Updated almost 2 years ago.

Status:
Rejected
Priority:
Normal
Target version:
-
[ruby-core:76864]

Description

I wrote the following script to show the problem:

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

The problem there is, I'm going to 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.

History

Updated by shyouhei (Shyouhei Urabe) about 3 years ago

  • Description updated (diff)

Updated by nobu (Nobuyoshi Nakada) about 3 years ago

  • Status changed from Open to Feedback

What's your platform?
IIRC, it depends on platforms.

Updated by chucke (Tiago Cardoso) about 3 years ago

this was executed on a MAC (darwin, I guess).

Updated by chucke (Tiago Cardoso) about 3 years ago

Just tested on linux (x86_64-linux), same output.

Updated by nobu (Nobuyoshi Nakada) almost 3 years ago

  • Assignee set to nobu (Nobuyoshi Nakada)
  • Status changed from Feedback to Assigned

Updated by nobu (Nobuyoshi Nakada) almost 2 years ago

  • Status changed from Assigned to Rejected

IO#wait_readable should return the receiver at EOF, since 2.3.
nil before EOF is the behavior of the OS.

Also available in: Atom PDF