vmagamedov / grpclib

Pure-Python gRPC implementation for asyncio
http://grpclib.readthedocs.io
BSD 3-Clause "New" or "Revised" License
936 stars 92 forks source link

Detect when clients disconnect or cancel from server using UnaryStream #155

Open PeqNP opened 2 years ago

PeqNP commented 2 years ago

Thank you so much for this great library!

I need a way to capture the signal when a client disconnects or cancels their request on a UnaryStream.

I tried using grpclib.events.listen and listening to several different events.

SendTrailingMetadata appears to be called when a client cancels the request. The problem is, I don't know which client it is. I tried storing a "session ID" on respective unary function stream. stream.metadata.add("session_id", "aaaa-bbbb") and then read that from the respective SendTrailingMetadata event event.metadata.pop("session_id", None). That didn't work.

None of the other events were called when a user disconnects. That being said I see a respective log message request_handler: * telling me that the client canceled and that the connection was lost! So I know they are being handled. But I don't know how to get access to those signals. It wasn't immediately obvious, after reading the code, how I could tap into those signals.

I also tried using contextlib but could not figure out how to use it.

Ideally, I would either like an exception be raised when I attempt to send_message on a stream OR have some sort of callback that I can associate to the stream which gets called. But it looks like the function is dumped immediately after the client disconnects. I guess that makes sense as the client is no longer there.

If there isn't a solution to this, one idea is to register an event callback stream.register_callback(event_handler). Where event_handler could be class instance that implements all of the possible events that a client could receive after they connect (could be used as a way to intercept / transform messages). OR simply have some type of stream.disconnect_handler = disconnect_handler where disconnect_handler is a reference to a function... I would define disconnect_handler in the respective method of the IServable def so that I could retain context of that connected client.