🦔

【Python】HTTP/1 サーバーで epoll を使う

2024/07/22に公開
import socket, select

# https://www.mtclinton.com/posts/simple-python-epoll-server/
# http://scotdoyle.com/python-epoll-howto.html

EOL1 = b'\n\n'
EOL2 = b'\n\r\n'

response  = b'HTTP/1.0 200 OK\r\n' \
            b'Content-Type: text/plain\r\n' \
            b'Content-Length: 15\r\n' \
            b'\r\n' \
            b'Hello, client\r\n'

HOST = "127.0.0.1"
PORT = 8000

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((HOST, PORT))
sock.listen(1)
sock.setblocking(0)

epoll = select.epoll()
epoll.register(sock.fileno(), select.EPOLLIN)

print('serving {}:{}'.format(HOST, PORT))

try:
    connections = {}; requests = {}; responses = {}
    while True:
        events = epoll.poll(1)
        for fileno, event in events:
            if fileno == sock.fileno():
                conn, addr = sock.accept()
                conn.setblocking(0)
                epoll.register(conn.fileno(), select.EPOLLIN)
                connections[conn.fileno()] = conn
                requests[conn.fileno()] = b''
                responses[conn.fileno()] = response
            elif event & select.EPOLLIN:
                requests[fileno] += connections[fileno].recv(1024)
                if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
                    epoll.modify(fileno, select.EPOLLOUT)
                    print('Client sent: ' + requests[fileno].decode()[:-2])
            elif event & select.EPOLLOUT:
                byteswritten = connections[fileno].send(responses[fileno])
                responses[fileno] = responses[fileno][byteswritten:]
                if len(responses[fileno]) == 0:
                    epoll.modify(fileno, 0)
                    connections[fileno].shutdown(socket.SHUT_RDWR)
            elif event & select.EPOLLHUP:
                epoll.unregister(fileno)
                connections[fileno].close()
                del connections[fileno]

finally:
    epoll.unregister(sock.fileno())
    epoll.close()
    sock.close()

Discussion