HTTP::WebSocket. Handling Closed stream (IO::Error)

When running a benchmark against simple chat server using HTTP::WebSocket I’ve noticed a bunch of Closed stream (IO::Error).

Here is the code which fixes errors and the commented out unless socket.closed? branch is what didn’t work.

require "http/server"

SOCKETS = [] of HTTP::WebSocket

ws_handler = HTTP::WebSocketHandler.new do |socket|
  SOCKETS << socket
  puts "Socket opened (#{SOCKETS.size} clients)"
  
  socket.on_message do |message|
    SOCKETS.each do |socket|
      socket.send "Echo: #{message}" # unless socket.closed? # why this doesn't work?
    rescue IO::Error # to handle Closed stream (IO::Error)
      # ignore
    end
  end

  socket.on_close do
    SOCKETS.delete(socket) 
    puts "Socket closed (#{SOCKETS.size} clients)"
  end
end

server = HTTP::Server.new([ws_handler])
address = server.bind_tcp "0.0.0.0", 3000

puts "Listening on ws://#{address}"
server.listen

I’ve checked the HTTP::WebSocket source and it has essentially the same socket.closed? check. See HTTP::WebSocket#send source.

I’ve tried using commented out unless socket.closed? in my code but it didn’t fix the error.

The rescue IO::Error version does work however.

So why is calling unless socket.closed? from the outside code is not the same as check inside HTTP::WebSocket#send?

Not sure tbh. If I had to guess, maybe on_message is called just a tad bit late before socket.closed? is updated?

My guess is the socket gets closed while your fiber is sleeping inside socket.send.