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

How to identify the client making the request? (By IP?) #85

Closed ArniDagur closed 4 years ago

ArniDagur commented 5 years ago

Say I have code like this, where clients can request an event stream through which they're perpetually notified about what's going on:

class MyBase(SomeBase):
    def __init__(self):
        super().__init__()
        self._event_queues = []

    async GetEventStream(self, stream):
        try:
            event_queue = asyncio.Queue()
            self._event_queues.append(event_queue)
            while True:
                event = await event_queue.get()
                await stream.send_message(event)
        finally:
            print("Client disconnected. Dropping connection...")
            # Remove disconnected client's event queue
            self._event_queues.remove(queue)

    async DoSomeAction(self, stream):
        action = await stream.recv_message()

        # Tell everyone connected to the server that something happened
        event = Event(value="some action happened")
        for queue in self._event_queues:
            await queue.put(event)

        await stream.send_message(google.protobuf.empty_pb2.Empty())

(Note that this code was written in the Github issue editor, and thus may not run)

In this example, everyone is notified through their event stream if any one person calls DoSomeAction. That is fine, but what if we only wanted to notify the specific client that made the call to DoSomeAction (perhaps along with another connected client picked at random)?

How can you tell that the client that called DoSomeAction is the same as the one that called GetEventStream five minutes earlier? Can you perhaps access the ip address from which the request was made?

vmagamedov commented 5 years ago

I think that relying on client's IP is not an ideal solution. There is a possibility that your server will be deployed behind some proxy. In HTTP world there is a common practice to use X-Forwarded-For header. I think that it is possible to use custom metadata for the same reasons.

For example, you can use uuid4().hex value as a client-id metadata. Here is a documentation describing how to send/receive custom metadata: https://grpclib.readthedocs.io/en/latest/metadata.html#server-side

Nevertheless, I think it would be helpful to expose client's address. So let's keep this issue opened.

aaliddell commented 5 years ago

To extend on this, it'd also be useful to be able to get any client certificate info from a connection also. Getting the client's IP is useful for logging purposes etc.

I have been using the following code to get the IP, port and cert info, but it obviously depends on accessing 'internal' parts of the stream object:

(address, port) = stream._stream._transport.get_extra_info('peername')
cert = stream._stream._transport.get_extra_info('peercert')
peer_ssl = stream._stream._transport.get_extra_info('ssl_object')
binary_cert = peer_ssl.getpeercert(True) if peer_ssl else None

Hopefully this gives you the calls you need to expose the info at a higher level.

gtaylor commented 4 years ago

How does the official grpc package expose the client's IP?

vmagamedov commented 4 years ago

@gtaylor

>>> context.peer()
'ipv4:127.0.0.1:52415'