Skip to content

Commit

Permalink
[webtransport] Suppress benign error from StreamWriter.close()
Browse files Browse the repository at this point in the history
The error is logged when the client initially checks for connectivity,
which is unnecessarily confusing to users.

Needed for Chromium's python3.8 -> python3.11 upgrade.
  • Loading branch information
jonathan-j-lee committed Sep 26, 2024
1 parent ed11dbe commit 381c5c4
Showing 1 changed file with 28 additions and 3 deletions.
31 changes: 28 additions & 3 deletions tools/webtransport/h3/webtransport_h3_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# mypy: allow-subclassing-any, no-warn-return-any

import asyncio
import contextlib
import logging
import os
import ssl
Expand All @@ -9,17 +10,21 @@
import traceback
from enum import IntEnum
from urllib.parse import urlparse
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, cast

# TODO(bashi): Remove import check suppressions once aioquic dependency is resolved.
from aioquic.buffer import Buffer # type: ignore
from aioquic.asyncio import QuicConnectionProtocol, serve # type: ignore
from aioquic.asyncio.client import connect # type: ignore
from aioquic.asyncio.protocol import QuicStreamAdapter # type: ignore
from aioquic.h3.connection import H3_ALPN, FrameType, H3Connection, ProtocolError, SettingsError # type: ignore
from aioquic.h3.events import H3Event, HeadersReceived, WebTransportStreamDataReceived, DatagramReceived, DataReceived # type: ignore
from aioquic.quic.configuration import QuicConfiguration # type: ignore
from aioquic.quic.connection import logger as quic_connection_logger # type: ignore
from aioquic.quic.connection import stream_is_unidirectional
from aioquic.quic.connection import (
stream_is_client_initiated,
stream_is_unidirectional,
)
from aioquic.quic.events import QuicEvent, ProtocolNegotiated, ConnectionTerminated, StreamReset # type: ignore
from aioquic.tls import SessionTicket # type: ignore

Expand Down Expand Up @@ -602,12 +607,32 @@ async def _connect_server_with_timeout(host: str, port: int, timeout: float) ->
return True


def _close_unusable_writer(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
# Starting in python3.11, `StreamWriter.__del__` implicitly `close()`s
# itself [0], if it has not done so already. Because aioquic sometimes
# models a unidirectional stream with a bidirectional transport [1], the
# `writer` here for a server -> client stream may log a benign but
# scary-looking exception when it's GC'ed and unsuccessfully writes EOF.
#
# For the purpose of checking connectivity, work around this for now by
# preemptively closing such streams and suppressing the resulting
# exceptions.
#
# [0]: https://github.com/python/cpython/blob/3.11/Lib/asyncio/streams.py#L413
# [1]: https://github.com/aiortc/aioquic/blob/1.2.0/src/aioquic/asyncio/protocol.py#L241
stream_id = cast(QuicStreamAdapter, writer.transport).stream_id
if stream_is_unidirectional(stream_id) and not stream_is_client_initiated(stream_id):
with contextlib.suppress(ValueError):
writer.close()


async def _connect_to_server(host: str, port: int) -> None:
configuration = QuicConfiguration(
alpn_protocols=H3_ALPN,
is_client=True,
verify_mode=ssl.CERT_NONE,
)

async with connect(host, port, configuration=configuration) as protocol:
async with connect(host, port, configuration=configuration,
stream_handler=_close_unusable_writer) as protocol:
await protocol.ping()

0 comments on commit 381c5c4

Please sign in to comment.