boazsegev / iodine

iodine - HTTP / WebSockets Server for Ruby with Pub/Sub support
MIT License
908 stars 51 forks source link

[FEAT] return reason for closed connection/client #146

Open akostadinov opened 1 year ago

akostadinov commented 1 year ago

It would be very useful to return also the reason for connection being closed in the on_close. Presently a closed connection is being passed to the method and there is no way to inform user of the reason or try to alleviate whatever is the problem.

boazsegev commented 1 year ago

Hi @akostadinov ,

Thank you for the input. However, since I didn't have a use-case for the issue, I'm not sure what your expectations might be.

Could you offer some use-case examples?

Maybe this feature could be pushed to the 0.8.x version cycle.

Thanks! Bo.

akostadinov commented 1 year ago

It is useful to know whether connection died or server disconnected us or we are shutting down (received a signal or whatever), etc.

If not for deciding whether to retry, it is important to log reasons when connection died. If I know the other server disconnected me, I would know to use shorter ping period or talk to the remote administrator. If I see there are timeouts or something local, then I would take other actions.

In this sense a feature for the server to log error events will also be a useful alternative.

Just as a comparison, this is how other libraries handle this: https://github.com/faye/faye-websocket-ruby#using-the-websocket-client

My particular use case is that I want to monitor data streamed 24/7 and record it for analysis. Knowing why disconnections happened is ultimate for making the listener reliable.

akostadinov commented 1 year ago

Just now I realized that my client connections to wss:// just don't work. And have zero idea what the issue is. And I have spent a lot of time thinking that the remote service was broken. Not entirely iodine fault because I had troubles with websocat official docker images which segfaulted but finally I've got a good websocat build that connected well to both ws:// and wss:// urls.

You can try a client connection to wss://websocket-echo-intustack.koyeb.app and see that iodine as a client silently fails.

boazsegev commented 1 year ago

I agree, there's a bug somewhere... but...

I connected using (full code below):

Iodine.connect url: "ws://websocket-echo-intustack.koyeb.app", handler: EchoClient.new, ping: 40
# # or
# Iodine.connect address: "websocket-echo-intustack.koyeb.app", port: "80", handler: EchoClient.new, ping: 40, service: :ws
Iodine.start

Failed using TLS:

Iodine.connect address: "websocket-echo-intustack.koyeb.app", port: "443", handler: EchoClient.new, ping: 40, service: :wss
Iodine.start

I'm not sure yet where the issue originates, but I assume TLS support is lacking (and apparently I can't connect to localhost except by using the loop-back ip 127.0.0.1)...

Anyway, client side is one of the big changes for the 0.8.x version (under the hood)... so I'm hoping to resolve this.

I assume TLS is a must...?


The full code was:

require 'iodine'

# The client class
class EchoClient

  def on_open(connection)
    puts "* Client open"
    @messages = [ "Hello World!",
      "I'm alive and sending messages",
      "I also receive messages",
      "now that we all know this...",
      "I can stop.",
      "Goodbye." ]
    send_one_message(connection)
  end

  def on_message(connection, message)
    puts "Received: #{message}"
    send_one_message(connection)
  end

  def on_close(connection)
    # in this example, we stop iodine once the client is closed
    puts "* Client closed."
    Iodine.stop
  end

  # We use this method to pop messages from the queue and send them
  #
  # When the queue is empty, we disconnect the client.
  def send_one_message(connection)
    msg = @messages.shift
    if(msg)
      connection.write msg
    else
      connection.close
    end
  end
end

Iodine.threads = 1

Iodine.connect address: "websocket-echo-intustack.koyeb.app", port: "80", handler: EchoClient.new, ping: 40, service: :ws
Iodine.start
akostadinov commented 1 year ago

Yeah, I used this example from readme to construct my client. And same - ws:// works, wss:// silently exits. The server code is https://github.com/akostadinov/websocket-echo-io

I assume TLS is a must...?

For client side - yes. Otherwise I see no way to connect to any respectable online websocket service.

For server side I don't care much because as you pointed out in another issue, this is usually handled by a proxy layer as is the case with my websocket echo service.

akostadinov commented 1 year ago

I made a desperate attempt to workaround to no avail:

$ socat "UNIX-LISTEN:/tmp/echo,mode=660,reuseaddr,fork" EXEC:"openssl s_client -connect websocket-echo-intustack.koyeb.app -port 443 -servername websocket-echo-intustack.koyeb.app"

then

Iodine.connect address: "/tmp/echo", service: :ws, handler: Dump.new, ping: 40
boazsegev commented 1 year ago

FYI:

On my local machine, works with TLS (but connection doesn't close properly)...

Tested with:

Iodine.connect address: "127.0.0.1", port: "3000", handler: EchoClient.new, ping: 40, service: :wss

I'm wondering if its a timing / timeout error...