MagicStack/uvloop

TLS/SSL asyncio/uvloop leaks memory

rojamit opened this issue · 1 comments

python3.9 or uvloop seems to leak smaller, but python3.11 (and others?) leaks A LOT OF memory under load
test command: ab -n15000 -c15000 -r https://127.0.0.1/
(apt install apache2-utils)

import asyncio, ssl, uvloop

class HTTP(asyncio.Protocol):
    
    def __init__(self):
        self.transport = None
        
    def connection_made(self, transport) -> None:
        self.transport = transport

    def data_received(self, data) -> None:
        self.transport.write(
            b'HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n'
        )
        self.transport.close()

    def connection_lost(self, _=0) -> bool | None:
        self.transport.close()

def make_tls_context():
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain('cert.crt', 'cert.key')
    return ctx

tls_context = make_tls_context()
loop = uvloop.new_event_loop()
loop.set_debug(True)

async def start_server(loop):
    return await loop.create_server(
        HTTP, '127.0.0.1', 443, backlog=65535,
        ssl=tls_context)

loop.run_until_complete(start_server(loop))
loop.run_forever()
loop.close()

CPython versions tested on:
3.9, 3.11

Operating systems tested on:
Linux


import asyncio
import ssl
import uvloop

class HTTP(asyncio.Protocol):
    def __init__(self):
        self.transport = None

    def connection_made(self, transport) -> None:
        self.transport = transport

    def data_received(self, data) -> None:
        print(data)
        self.transport.write(
            b'HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n'
        )
        self.transport.close()

    def connection_lost(self, exc) -> None:
        if self.transport is not None:
            self.transport.close()
            self.transport = None

def make_tls_context():
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain('cert.crt', 'cert.key')  # Adjust paths to your certificates
    return ctx

async def start_server():
    loop = asyncio.get_running_loop()
    server = await loop.create_server(
        lambda: HTTP(), '127.0.0.1', 443, backlog=65535, ssl=make_tls_context()
    )
    async with server:
        await server.serve_forever()

uvloop.install()
asyncio.run(start_server())