😎

【Python】selectors で TLS 対応の HTTP/1 サーバーを試すも失敗

に公開

selectors の公式マニュアルのサンプルコードを少し修正して試すも curl で1回リクエスト送信するだけでクラッシュしてしまった

# https://docs.python.org/3/library/selectors.html#examples
import selectors
import socket
import ssl

sel = selectors.DefaultSelector()

def accept(sock, mask):
    conn, addr = sock.accept()
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    msg =(
    f"HTTP/1.1 200 OK\r\n"
      f"Content-Type: text/html; charset=UTF-8\r\n"
      f"Content-Length: 11\r\n"
      f"Connection: Close\r\n"
      f"\r\n"
      f"Hello World"
    ).encode()

    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain('localhost.pem', 'localhost-key.pem')
    context.set_alpn_protocols(['http/1.1'])
    stream = context.wrap_socket(sock=conn, server_side=True)

    data = stream.recv(1000)
    if data:
        stream.send(msg)
    else:
        print('closing', stream)
        stream.close()

        conn.close()

sock = socket.socket()
sock.bind(('0.0.0.0', 8000))
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.listen(100)
sock.setblocking(False)

sel.register(sock, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)
python server.py
Traceback (most recent call last):
  File "/home/masakielastic/asyncio-project/server.py", line 46, in <module>
    callback(key.fileobj, mask)
    ~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "/home/masakielastic/asyncio-project/server.py", line 9, in accept
    sel.register(conn, selectors.EVENT_READ, read)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/masakielastic/.local/share/mise/installs/python/3.13.0b3/lib/python3.13/selectors.py", line 341, in register
    key = super().register(fileobj, events, data)
  File "/home/masakielastic/.local/share/mise/installs/python/3.13.0b3/lib/python3.13/selectors.py", line 245, in register
    raise KeyError("{!r} (FD {}) is already registered"
                   .format(fileobj, key.fd))
KeyError: "<socket.socket fd=5, family=2, type=1, proto=0, laddr=('127.0.0.1', 8000), raddr=('127.0.0.1', 39402)> (FD 5) is already registered"

Discussion