Checking whether a HTTP client is still listening

Hello everyone,

I want to check whether a client which has been waiting for a HTTP::Response is still there, listening to what I’m about to send (note: the content of the reply doesn’t matter, so it’s fine if it contains random garbage, as long as it isn’t anything, which makes the client stop listening).

My idea was to simply try to send something (as in myResponseObject.write Bytes[20] or myResponseObject.output.write Bytes[20]) and hoping for some error thrown at me, in case the client has left (e.g. the browser which was used for the request got closed).

Unfortunately it obviously doesn’t work this way, because whatever I try, I don’t get any error thrown at me (nor can I see it on some return value (e.g. maybe the amount of bytes successfully written, then 0 successful transferred bytes would give me a similar feedback).

So how could I achieve this instead? I was trying to do so with Kemal, but this isn’t a requirement on my side, so if it’s easier using something else or just by plain crystal, I would be fine with it, too.

I am able to do it via LibC.send, but I was hoping there might be some better, less hacky way!?

Thanks in advance!

Most likely you don’t get an error because the data you send gets buffered. Try flush.

I believe on Linux you can query a socket’s open status via recv with flags MSG_PEEK | MSG_DONTWAIT. This won’t read anything from the socket and return immediately. If the return value is 0, the socket is closed. This is not implemented in Crystal’s socket API, though.

1 Like

Thanks, flush indeed solved it! I’ve had already tried it, but it didn’t worked for me the first time, because my code had some bug. Because of your reply I’ve tried it again and did some debugging on the surrounding code and yeah, write and flush are indeed all it needs, and gives you eventually a #<IO::Error:Closed stream>.

As this is the newbie corner of the forum, just in case someone tries to do something similar in the future and reads this, you might want to avoid two of my mistakes: keep in mind, that for some clients (especially browsers) the sockets might still be open and receiving for a (surprising long) time after you’ve closed them (for me it could take up to 90s before I would see an error raised). And if you absolutely can’t get it working, try to raise manually an error just to see if your code handles errors as intended in the first place.