Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asyncore is deprecated #573

Open
RomanBelozerov opened this issue Feb 7, 2024 · 1 comment
Open

asyncore is deprecated #573

RomanBelozerov opened this issue Feb 7, 2024 · 1 comment
Labels
enhancement New feature or request Infrastructure The framework reworking and extensions
Milestone

Comments

@RomanBelozerov
Copy link
Contributor

Deprecated since version 3.6, will be removed in version 3.12: The asyncore module is deprecated (see PEP 594 for details). Please use asyncio instead.

I think we should consider using asyncio or create a base class from scratch if that's not possible.

@RomanBelozerov RomanBelozerov added enhancement New feature or request Infrastructure The framework reworking and extensions labels Feb 7, 2024
@RomanBelozerov RomanBelozerov added this to the 1.0 - GA milestone Feb 8, 2024
@RomanBelozerov
Copy link
Contributor Author

The simple example with asyncio:

class AsyncClient:
    """The client for sending bytes. It does not wait for responses and does not check them."""

    def __init__(self, conn_ip: str, conn_port: int, local_ip: str, ssl_: bool, http2: bool):
        self._conn_ip: str = conn_ip
        self._conn_port: int = conn_port
        self._local_ip: str = local_ip
        self._writer: asyncio.streams.StreamWriter | None = None
        self._reader: asyncio.streams.StreamReader | None = None
        self._ssl: bool = ssl_
        self._http2: bool = http2
        self._context: ssl.SSLContext | None = self._create_context()
        self._tasks: list = []

    def _create_context(self) -> ssl.SSLContext | None:
        if not self._ssl:
            return None
        self._context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        if run_config.SAVE_SECRETS:
            self._context.keylog_filename = "secrets.txt"
        self._context.check_hostname = False
        self._context.verify_mode = ssl.CERT_NONE
        self._apply_proto_settings()
        return self._context

    def _apply_proto_settings(self):
        if self._context is not None:
            self._context.set_alpn_protocols(["h2"] if self._http2 else ["http/1.1"])
            # Disable old proto
            self._context.options |= (
                ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
            )
            if self._http2:
                # RFC 9113 Section 9.2.1: A deployment of HTTP/2 over TLS 1.2 MUST disable
                # compression.
                self._context.options |= ssl.OP_NO_COMPRESSION

    def is_closing(self) -> bool:
        return self._writer.is_closing()

    async def wait_for_connection_close(self, timeout=5.0) -> bool:
        task = asyncio.create_task(self._reader.read(-1))
        try:
            await asyncio.wait_for(task, timeout=timeout)
        except asyncio.TimeoutError:
            task.cancel()
        return self._reader.at_eof()

    async def run_start(self) -> None:
        self._reader, self._writer = await asyncio.open_connection(
            self._conn_ip, self._conn_port, ssl=self._context, local_addr=(self._local_ip, 0)
        )
        if self._http2:
            # create HTTP/2.0 connection and send preamble
            sf = SettingsFrame(stream_id=0)
            await self.send_bytes(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + sf.serialize())

    async def send_bytes(self, data: bytes) -> None:
        self._writer.write(data)
        await self._writer.drain()

    async def aclose(self) -> None:
        if self._writer is not None:
            try:
                self._writer.close()
                await self._writer.wait_closed()
            # TODO: Should be removed after issue #1778. Tempesta sends an unexpected tls alert.
            except ssl.SSLError:
                ...

Most likely, we cannot use httpx, aiohttp or other high-level libraries because the client must be able to send http2 frames w/o end_stream, end_headers flags and etc. Also we use TCP fragmentation in tests.
If we decide to use asyncio then we should rework tester.TempestaTest using unittest.IsolatedAsyncioTestCase as base class. And these change requires a lot of time (we need to rework all services and remove threading and multiprocessing).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Infrastructure The framework reworking and extensions
Projects
None yet
Development

No branches or pull requests

1 participant