Getting socket options in asyncio server

To my knowledge, the only way to get/set socket options in Python is on a socket object (or using ctypes, which I won’t count here). When asyncio servers spawn client tasks, there’s no socket object, just generic readers and writers. What is the recommended way to query socket options?

What I was able to do involves grabbing private attributes, not something I’d normally want to do.

import os
import socket
import asyncio

def getsockopt(fd, level, opt):
    sock = socket.socket(fileno=fd)
    try: return sock.getsockopt(level, opt)
    finally: sock.detach() # Detach even if we hit an error of some sort

async def client(reader, writer):
    print("Got connection", reader)
    fd = reader._transport._sock_fd
    pid = getsockopt(fd, socket.SOL_SOCKET, socket.SO_PEERCRED);
    print("Connection from", pid)

async def main():
    print("I am", os.getpid())
    await asyncio.start_unix_server(client, "/tmp/samplesocket")
    await asyncio.Future()

asyncio.run(main())

Connect to this socket using another process, and it should correctly report your process ID. (This example uses a Unix socket - sorry Windows folks - but to my knowledge, this should also apply to TCP socket options.)

Is there a better way to do this? If not, should there be?

EDIT TO ADD: There is a tempting-looking API writer.get_extra_info("sock") but it returned None so I don’t know whether this fails on Unix sockets or if something else is wrong, but that was a dead end for me.

Does writer.transport.get_extra_info(“socket”) work?

Erm.

Okay. Now I feel really REALLY dumb. I tried that right at the beginning, except that I had the keyword wrong (using "sock" instead). All through my testing, I had a printout of extra_info("sock") and it was just None everywhere, so I thought that this just wasn’t supported for Unix sockets or something and moved on.

Thanks. That does, in fact, work perfectly.

1 Like