From eab15e37dd94cf428bac94a50785f6703d3df515 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:25:16 -1000 Subject: [PATCH 01/34] Fix ConnectionResetError not being raised when the transport is closed fixes #7172 --- aiohttp/http_writer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index db3d6a04897..4e2e7d9d5c5 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -72,7 +72,12 @@ def _write(self, chunk: bytes) -> None: self.buffer_size += size self.output_size += size - if self._transport is None or self._transport.is_closing(): + # self._protocol.transport will be None if connection is closed + # + # We cannot check if self._transport is None here because + # we hold a reference to it even after the connection is closed. + # + if self._protocol.transport is None or self._transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") self._transport.write(chunk) From 5a43af0c2e90756669a9ff1d698916232d66f0b9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:30:55 -1000 Subject: [PATCH 02/34] mypy --- aiohttp/http_writer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 4e2e7d9d5c5..7c2519cca5e 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -72,12 +72,13 @@ def _write(self, chunk: bytes) -> None: self.buffer_size += size self.output_size += size + # # self._protocol.transport will be None if connection is closed # # We cannot check if self._transport is None here because # we hold a reference to it even after the connection is closed. # - if self._protocol.transport is None or self._transport.is_closing(): + if self._protocol.transport is None or self._protocol.transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") self._transport.write(chunk) From 4a9da40225d4294483c931edad1dc713014c3684 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:31:36 -1000 Subject: [PATCH 03/34] mypy --- aiohttp/http_writer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 7c2519cca5e..343b3718626 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -78,9 +78,10 @@ def _write(self, chunk: bytes) -> None: # We cannot check if self._transport is None here because # we hold a reference to it even after the connection is closed. # - if self._protocol.transport is None or self._protocol.transport.is_closing(): + transport = self._protocol.transport + if transport is None or transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") - self._transport.write(chunk) + transport.write(chunk) async def write( self, chunk: bytes, *, drain: bool = True, LIMIT: int = 0x10000 From 2da406491e0b34ddaae8efc953b42c2cd75a77dd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:47:12 -1000 Subject: [PATCH 04/34] add cover --- tests/test_http_writer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 3fb5531ca1d..0f897edc368 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -262,6 +262,18 @@ async def test_write_to_closing_transport( await msg.write(b"After closing") +async def test_write_to_closed_transport( + protocol: Any, transport: Any, loop: Any +) -> None: + msg = http.StreamWriter(protocol, loop) + + await msg.write(b"Before transport closing") + protocol.transport = None + + with pytest.raises(ConnectionResetError): + await msg.write(b"After transport closing") + + async def test_drain(protocol: Any, transport: Any, loop: Any) -> None: msg = http.StreamWriter(protocol, loop) await msg.drain() From 8ac7f389b68d80fe24230da8e7d01488ca8287f7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:47:29 -1000 Subject: [PATCH 05/34] add cover --- tests/test_http_writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 0f897edc368..7536a83f91a 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -267,11 +267,11 @@ async def test_write_to_closed_transport( ) -> None: msg = http.StreamWriter(protocol, loop) - await msg.write(b"Before transport closing") + await msg.write(b"Before transport close") protocol.transport = None with pytest.raises(ConnectionResetError): - await msg.write(b"After transport closing") + await msg.write(b"After transport closed") async def test_drain(protocol: Any, transport: Any, loop: Any) -> None: From 9cb25dbd20b99b6bdb94c1602f2236c9b17b56a5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:48:40 -1000 Subject: [PATCH 06/34] change --- CHANGES/7180.buffix | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/7180.buffix diff --git a/CHANGES/7180.buffix b/CHANGES/7180.buffix new file mode 100644 index 00000000000..48b73d4bc3c --- /dev/null +++ b/CHANGES/7180.buffix @@ -0,0 +1 @@ +`ConnectionResetError` will always be raised when `StreamWriter.write` is called after `connection_lost` has been called on the `BaseProtocol` From 0c8b69a4f357980bee8bb1864482dab093ddbfe7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:50:15 -1000 Subject: [PATCH 07/34] contributors --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 23888634b22..a934dee9967 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -156,6 +156,7 @@ Ilya Gruzinov Ingmar Steen Ivan Lakovic Ivan Larin +J. Nick Koston Jacob Champion Jaesung Lee Jake Davis From 3b90d3c8b6a55b69a5f970aa2351c26ca53fe0b2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:52:21 -1000 Subject: [PATCH 08/34] typo --- CHANGES/{7180.buffix => 7180.bugfix} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CHANGES/{7180.buffix => 7180.bugfix} (100%) diff --git a/CHANGES/7180.buffix b/CHANGES/7180.bugfix similarity index 100% rename from CHANGES/7180.buffix rename to CHANGES/7180.bugfix From f0ae99e70d20c22bd92d92412b0068c6b181d909 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:53:31 -1000 Subject: [PATCH 09/34] typo --- aiohttp/http_writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 343b3718626..d60f70751f0 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -73,9 +73,9 @@ def _write(self, chunk: bytes) -> None: self.output_size += size # - # self._protocol.transport will be None if connection is closed + # self._protocol.transport will be None if the connection is closed # - # We cannot check if self._transport is None here because + # We cannot check if self._transport here because # we hold a reference to it even after the connection is closed. # transport = self._protocol.transport From 4f44c32558364779962649a0fdf1b851f57f02fa Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Jan 2023 10:54:06 -1000 Subject: [PATCH 10/34] typo --- aiohttp/http_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index d60f70751f0..16b28cc708f 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -75,7 +75,7 @@ def _write(self, chunk: bytes) -> None: # # self._protocol.transport will be None if the connection is closed # - # We cannot check if self._transport here because + # We cannot check self._transport here because # we hold a reference to it even after the connection is closed. # transport = self._protocol.transport From a393ecefdead54b170632259ededd66e665c849c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Jan 2023 11:33:45 -1000 Subject: [PATCH 11/34] single source of truth for the transport --- aiohttp/http_writer.py | 4 +--- tests/test_client_proto.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 16b28cc708f..4af237a2c44 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -35,7 +35,6 @@ def __init__( on_headers_sent: _T_OnHeadersSent = None, ) -> None: self._protocol = protocol - self._transport = protocol.transport self.loop = loop self.length = None @@ -52,7 +51,7 @@ def __init__( @property def transport(self) -> Optional[asyncio.Transport]: - return self._transport + return self._protocol.transport @property def protocol(self) -> BaseProtocol: @@ -166,7 +165,6 @@ async def write_eof(self, chunk: bytes = b"") -> None: await self.drain() self._eof = True - self._transport = None async def drain(self) -> None: """Flush the write buffer. diff --git a/tests/test_client_proto.py b/tests/test_client_proto.py index 08ae367e81b..5036f449e6d 100644 --- a/tests/test_client_proto.py +++ b/tests/test_client_proto.py @@ -136,3 +136,18 @@ async def test_eof_received(loop: Any) -> None: assert proto._read_timeout_handle is not None proto.eof_received() assert proto._read_timeout_handle is None + + +async def test_connection_lost_sets_transport_to_none(loop: Any) -> None: + """Ensure that the transport is set to None when the connection is list. + + This ensures the writer knows that the connection is closed. + """ + proto = ResponseHandler(loop=loop) + transport = mock.Mock() + proto.connection_made(transport) + assert proto.transport is not None + + proto.connection_lost(OSError()) + + assert proto.transport is None From 8670f0aaab0e65ca221c9f8111eabe82bc19e4df Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Jan 2023 11:41:10 -1000 Subject: [PATCH 12/34] preen --- aiohttp/http_writer.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 4af237a2c44..7bcd7d976da 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -70,13 +70,6 @@ def _write(self, chunk: bytes) -> None: size = len(chunk) self.buffer_size += size self.output_size += size - - # - # self._protocol.transport will be None if the connection is closed - # - # We cannot check self._transport here because - # we hold a reference to it even after the connection is closed. - # transport = self._protocol.transport if transport is None or transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") From 78273dddef62162c869e104de4b2f477f153ea32 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Jan 2023 11:54:03 -1000 Subject: [PATCH 13/34] empty From 7f95175b996b2c594429caf307b65858449c6bee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Jan 2023 15:38:19 -1000 Subject: [PATCH 14/34] empty From 860fde8aea3c78b3c5a769c38ce8308ac6787d22 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:03:26 -1000 Subject: [PATCH 15/34] rst syntax --- CHANGES/7180.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/7180.bugfix b/CHANGES/7180.bugfix index 48b73d4bc3c..66980638868 100644 --- a/CHANGES/7180.bugfix +++ b/CHANGES/7180.bugfix @@ -1 +1 @@ -`ConnectionResetError` will always be raised when `StreamWriter.write` is called after `connection_lost` has been called on the `BaseProtocol` +``ConnectionResetError`` will always be raised when ``StreamWriter.write`` is called after ``connection_lost`` has been called on the ``BaseProtocol`` From d34fc70e5131adb464f118d098deb6e8d2f217d6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:03:55 -1000 Subject: [PATCH 16/34] Update aiohttp/http_writer.py Co-authored-by: Sviatoslav Sydorenko --- aiohttp/http_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 7bcd7d976da..4506962bdd1 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -70,7 +70,7 @@ def _write(self, chunk: bytes) -> None: size = len(chunk) self.buffer_size += size self.output_size += size - transport = self._protocol.transport + transport = self.transport if transport is None or transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") transport.write(chunk) From 516a5c1aeb79fc3ad0b434a18338b0eaae5f904c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:05:21 -1000 Subject: [PATCH 17/34] docstring --- tests/test_http_writer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 7536a83f91a..9d18a3b4615 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -265,6 +265,11 @@ async def test_write_to_closing_transport( async def test_write_to_closed_transport( protocol: Any, transport: Any, loop: Any ) -> None: + """Test that writing to a closed transport raises ConnectionResetError. + + The StreamWriter checks to see if protocol.transport is None before + writing to the transport. If it is None, it raises ConnectionResetError. + """ msg = http.StreamWriter(protocol, loop) await msg.write(b"Before transport close") From 21bb53bbaed029df234a3961a7489d1b2977e4a6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:08:28 -1000 Subject: [PATCH 18/34] matches --- tests/test_http_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 9d18a3b4615..7b7fe4f34c5 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -275,7 +275,7 @@ async def test_write_to_closed_transport( await msg.write(b"Before transport close") protocol.transport = None - with pytest.raises(ConnectionResetError): + with pytest.raises(ConnectionResetError, matches="Cannot write to closing transport"): await msg.write(b"After transport closed") From c946e93cf1488a9c189d8e0e54ec334731f836b6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Jan 2023 07:09:01 +0000 Subject: [PATCH 19/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_http_writer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 7b7fe4f34c5..1ed478ef07b 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -275,7 +275,9 @@ async def test_write_to_closed_transport( await msg.write(b"Before transport close") protocol.transport = None - with pytest.raises(ConnectionResetError, matches="Cannot write to closing transport"): + with pytest.raises( + ConnectionResetError, matches="Cannot write to closing transport" + ): await msg.write(b"After transport closed") From 5833948074ba7b3eab752ea4c594e8279aabff24 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:12:02 -1000 Subject: [PATCH 20/34] mocker --- tests/test_client_proto.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_client_proto.py b/tests/test_client_proto.py index 5036f449e6d..bad7ebdd713 100644 --- a/tests/test_client_proto.py +++ b/tests/test_client_proto.py @@ -138,14 +138,13 @@ async def test_eof_received(loop: Any) -> None: assert proto._read_timeout_handle is None -async def test_connection_lost_sets_transport_to_none(loop: Any) -> None: +async def test_connection_lost_sets_transport_to_none(loop: Any, mocker: Any) -> None: """Ensure that the transport is set to None when the connection is list. This ensures the writer knows that the connection is closed. """ proto = ResponseHandler(loop=loop) - transport = mock.Mock() - proto.connection_made(transport) + proto.connection_made(mocker.Mock()) assert proto.transport is not None proto.connection_lost(OSError()) From 0ff872e60bc46ad5378825b4b46b4f5b09baa478 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:19:14 -1000 Subject: [PATCH 21/34] fix incorrect kwarg --- tests/test_http_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 1ed478ef07b..8a8da7a2740 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -276,7 +276,7 @@ async def test_write_to_closed_transport( protocol.transport = None with pytest.raises( - ConnectionResetError, matches="Cannot write to closing transport" + ConnectionResetError, match="Cannot write to closing transport" ): await msg.write(b"After transport closed") From 96090d54f2cda9aa6518c61db43b18ffd1871ebc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Jan 2023 07:19:46 +0000 Subject: [PATCH 22/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_http_writer.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 8a8da7a2740..014823f621d 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -275,9 +275,7 @@ async def test_write_to_closed_transport( await msg.write(b"Before transport close") protocol.transport = None - with pytest.raises( - ConnectionResetError, match="Cannot write to closing transport" - ): + with pytest.raises(ConnectionResetError, match="Cannot write to closing transport"): await msg.write(b"After transport closed") From b93ed94097206aceb0677eb968048277ee9cbdde Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:28:17 -1000 Subject: [PATCH 23/34] empty commit to rerun ci From 05da43e8d18555f472d07b96210fd2f5bbb5f26d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Jan 2023 21:33:55 -1000 Subject: [PATCH 24/34] empty commit to rerun ci From 4e1076002efdcfa8c45dd78bfcd06ab4d05d027a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jan 2023 07:53:08 -1000 Subject: [PATCH 25/34] empty From 90f42571b78cea86165adbab392218a7104d0d60 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jan 2023 09:58:44 -1000 Subject: [PATCH 26/34] add connected property --- aiohttp/base_protocol.py | 11 +++++++---- aiohttp/http_writer.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/aiohttp/base_protocol.py b/aiohttp/base_protocol.py index 8189835e211..9ca98ef7acf 100644 --- a/aiohttp/base_protocol.py +++ b/aiohttp/base_protocol.py @@ -18,11 +18,15 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None: self._loop: asyncio.AbstractEventLoop = loop self._paused = False self._drain_waiter: Optional[asyncio.Future[None]] = None - self._connection_lost = False self._reading_paused = False self.transport: Optional[asyncio.Transport] = None + @property + def connected(self) -> bool: + """Return True if the connection is open.""" + return bool(self.transport) + def pause_writing(self) -> None: assert not self._paused self._paused = True @@ -59,7 +63,6 @@ def connection_made(self, transport: asyncio.BaseTransport) -> None: self.transport = tr def connection_lost(self, exc: Optional[BaseException]) -> None: - self._connection_lost = True # Wake up the writer if currently paused. self.transport = None if not self._paused: @@ -76,8 +79,8 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: waiter.set_exception(exc) async def _drain_helper(self) -> None: - if self._connection_lost: - raise ConnectionResetError("Connection lost") + if not self.connected: + raise ConnectionResetError("Not connected") if not self._paused: return waiter = self._drain_waiter diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 4506962bdd1..73f0f96f0ae 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -71,7 +71,7 @@ def _write(self, chunk: bytes) -> None: self.buffer_size += size self.output_size += size transport = self.transport - if transport is None or transport.is_closing(): + if not self._protocol.connected or transport is None or transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") transport.write(chunk) From 471fa2e200009f1463e5743d6d986eec5e2cd89e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jan 2023 10:00:29 -1000 Subject: [PATCH 27/34] cleanup --- aiohttp/http_writer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 73f0f96f0ae..30e30a489fa 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -70,10 +70,9 @@ def _write(self, chunk: bytes) -> None: size = len(chunk) self.buffer_size += size self.output_size += size - transport = self.transport - if not self._protocol.connected or transport is None or transport.is_closing(): + if not self._protocol.connected: raise ConnectionResetError("Cannot write to closing transport") - transport.write(chunk) + self.transport.write(chunk) async def write( self, chunk: bytes, *, drain: bool = True, LIMIT: int = 0x10000 From 0172016c974120ea9a7b95770db6024d128b456b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jan 2023 13:42:11 -1000 Subject: [PATCH 28/34] revert change to make mypy happy --- aiohttp/http_writer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 30e30a489fa..f42e7acef91 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -70,9 +70,10 @@ def _write(self, chunk: bytes) -> None: size = len(chunk) self.buffer_size += size self.output_size += size - if not self._protocol.connected: + transport = self.transport + if not self._protocol.connected and transport is not None: raise ConnectionResetError("Cannot write to closing transport") - self.transport.write(chunk) + transport.write(chunk) async def write( self, chunk: bytes, *, drain: bool = True, LIMIT: int = 0x10000 From d51d68a41ae08cb45af19d22a75734bcedc23d35 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jan 2023 13:43:36 -1000 Subject: [PATCH 29/34] revert change to make mypy happy --- aiohttp/http_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index f42e7acef91..2f5221f066d 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -71,7 +71,7 @@ def _write(self, chunk: bytes) -> None: self.buffer_size += size self.output_size += size transport = self.transport - if not self._protocol.connected and transport is not None: + if not self._protocol.connected or transport is None: raise ConnectionResetError("Cannot write to closing transport") transport.write(chunk) From 74b7e77a65f1da83ce06147ea404517ee5121634 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jan 2023 13:49:56 -1000 Subject: [PATCH 30/34] update tests --- tests/test_base_protocol.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_base_protocol.py b/tests/test_base_protocol.py index ff7fd0be523..d13029aab8b 100644 --- a/tests/test_base_protocol.py +++ b/tests/test_base_protocol.py @@ -45,10 +45,10 @@ async def test_connection_lost_not_paused() -> None: pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) - assert not pr._connection_lost + assert pr.connected pr.connection_lost(None) assert pr.transport is None - assert pr._connection_lost + assert not pr.connected async def test_connection_lost_paused_without_waiter() -> None: @@ -56,11 +56,11 @@ async def test_connection_lost_paused_without_waiter() -> None: pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) - assert not pr._connection_lost + assert pr.connected pr.pause_writing() pr.connection_lost(None) assert pr.transport is None - assert pr._connection_lost + assert not pr.connected async def test_drain_lost() -> None: From 0f44ef248f9edb3c56d1db56efc8165448228f5b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jan 2023 13:55:59 -1000 Subject: [PATCH 31/34] still check for is_closing --- aiohttp/http_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index 2f5221f066d..73f0f96f0ae 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -71,7 +71,7 @@ def _write(self, chunk: bytes) -> None: self.buffer_size += size self.output_size += size transport = self.transport - if not self._protocol.connected or transport is None: + if not self._protocol.connected or transport is None or transport.is_closing(): raise ConnectionResetError("Cannot write to closing transport") transport.write(chunk) From a7e53ba3288af59bbf934b23b2ba419f75a326be Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 1 Feb 2023 17:28:00 +0000 Subject: [PATCH 32/34] Update tests/test_client_proto.py --- tests/test_client_proto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client_proto.py b/tests/test_client_proto.py index bad7ebdd713..f03ee53bf59 100644 --- a/tests/test_client_proto.py +++ b/tests/test_client_proto.py @@ -139,7 +139,7 @@ async def test_eof_received(loop: Any) -> None: async def test_connection_lost_sets_transport_to_none(loop: Any, mocker: Any) -> None: - """Ensure that the transport is set to None when the connection is list. + """Ensure that the transport is set to None when the connection is lost. This ensures the writer knows that the connection is closed. """ From ed7666911c0391ad14f00bce93a0345905cd7e59 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 1 Feb 2023 17:28:12 +0000 Subject: [PATCH 33/34] Update aiohttp/base_protocol.py --- aiohttp/base_protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/base_protocol.py b/aiohttp/base_protocol.py index 9ca98ef7acf..809e2289e2a 100644 --- a/aiohttp/base_protocol.py +++ b/aiohttp/base_protocol.py @@ -25,7 +25,7 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None: @property def connected(self) -> bool: """Return True if the connection is open.""" - return bool(self.transport) + return self.transport is not None def pause_writing(self) -> None: assert not self._paused From 53384ab948e5cca4cd39899cedf340194d21829f Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 1 Feb 2023 17:28:24 +0000 Subject: [PATCH 34/34] Update aiohttp/base_protocol.py --- aiohttp/base_protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/base_protocol.py b/aiohttp/base_protocol.py index 809e2289e2a..4c9f0a752e3 100644 --- a/aiohttp/base_protocol.py +++ b/aiohttp/base_protocol.py @@ -80,7 +80,7 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: async def _drain_helper(self) -> None: if not self.connected: - raise ConnectionResetError("Not connected") + raise ConnectionResetError("Connection lost") if not self._paused: return waiter = self._drain_waiter