From 4acb2da3c455cbfe43a4f98e343cf6631012cc54 Mon Sep 17 00:00:00 2001 From: Dmitry Erlikh Date: Tue, 3 Nov 2020 04:49:22 +0100 Subject: [PATCH 1/6] Update ws close codes enum --- aiohttp/http_websocket.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index 1e29c01274e..62db49ca63c 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -33,6 +33,7 @@ class WSCloseCode(IntEnum): GOING_AWAY = 1001 PROTOCOL_ERROR = 1002 UNSUPPORTED_DATA = 1003 + ABNORMAL_CLOSURE = 1006 INVALID_TEXT = 1007 POLICY_VIOLATION = 1008 MESSAGE_TOO_BIG = 1009 @@ -40,6 +41,8 @@ class WSCloseCode(IntEnum): INTERNAL_ERROR = 1011 SERVICE_RESTART = 1012 TRY_AGAIN_LATER = 1013 + BAD_GATEWAY = 1014 + TLS_HANDSHAKE = 1015 ALLOWED_CLOSE_CODES = {int(i) for i in WSCloseCode} From b665436ec424d65669024bedfed74ebb903edfb0 Mon Sep 17 00:00:00 2001 From: Dmitry Erlikh Date: Tue, 3 Nov 2020 05:02:12 +0100 Subject: [PATCH 2/6] Use WSCloseCode enum instead of numerics --- aiohttp/client_ws.py | 21 ++++++++++--------- aiohttp/web_ws.py | 21 ++++++++++--------- tests/test_web_websocket_functional.py | 28 +++++++++++++------------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/aiohttp/client_ws.py b/aiohttp/client_ws.py index 37d5ac73f0a..e94e7dfdc21 100644 --- a/aiohttp/client_ws.py +++ b/aiohttp/client_ws.py @@ -13,6 +13,7 @@ WS_CLOSED_MESSAGE, WS_CLOSING_MESSAGE, WebSocketError, + WSCloseCode, WSMessage, WSMsgType, ) @@ -109,7 +110,7 @@ def _send_heartbeat(self) -> None: def _pong_not_received(self) -> None: if not self._closed: self._closed = True - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = asyncio.TimeoutError() self._response.close() @@ -171,7 +172,7 @@ async def send_json( ) -> None: await self.send_str(dumps(data), compress=compress) - async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: + async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool: # we need to break `receive()` cycle first, # `close()` may be called from different task if self._waiting is not None and not self._closed: @@ -184,11 +185,11 @@ async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: try: await self._writer.close(code, message) except asyncio.CancelledError: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._response.close() raise except Exception as exc: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = exc self._response.close() return True @@ -202,11 +203,11 @@ async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: async with async_timeout.timeout(self._timeout.ws_close): msg = await self._reader.read() except asyncio.CancelledError: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._response.close() raise except Exception as exc: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = exc self._response.close() return True @@ -242,15 +243,15 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage: self._waiting = None set_result(waiter, True) except (asyncio.CancelledError, asyncio.TimeoutError): - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE raise except EofStream: - self._close_code = 1000 + self._close_code = WSCloseCode.OK await self.close() return WSMessage(WSMsgType.CLOSED, None, None) except ClientError: self._closed = True - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE return WS_CLOSED_MESSAGE except WebSocketError as exc: self._close_code = exc.code @@ -259,7 +260,7 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage: except Exception as exc: self._exception = exc self._closing = True - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE await self.close() return WSMessage(WSMsgType.ERROR, exc, None) diff --git a/aiohttp/web_ws.py b/aiohttp/web_ws.py index 7a571a63339..bc5d6440ceb 100644 --- a/aiohttp/web_ws.py +++ b/aiohttp/web_ws.py @@ -19,6 +19,7 @@ WebSocketError, WebSocketReader, WebSocketWriter, + WSCloseCode, WSMessage, WSMsgType as WSMsgType, ws_ext_gen, @@ -144,7 +145,7 @@ def _send_heartbeat(self) -> None: def _pong_not_received(self) -> None: if self._req is not None and self._req.transport is not None: self._closed = True - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = asyncio.TimeoutError() self._req.transport.close() @@ -346,7 +347,7 @@ async def write_eof(self) -> None: # type: ignore await self.close() self._eof_sent = True - async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: + async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool: if self._writer is None: raise RuntimeError("Call .prepare() first") @@ -368,10 +369,10 @@ async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: assert writer is not None await writer.drain() except (asyncio.CancelledError, asyncio.TimeoutError): - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE raise except Exception as exc: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = exc return True @@ -384,10 +385,10 @@ async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: async with async_timeout.timeout(self._timeout): msg = await reader.read() except asyncio.CancelledError: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE raise except Exception as exc: - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = exc return True @@ -395,7 +396,7 @@ async def close(self, *, code: int = 1000, message: bytes = b"") -> bool: self._close_code = msg.data return True - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE self._exception = asyncio.TimeoutError() return True else: @@ -430,10 +431,10 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage: set_result(waiter, True) self._waiting = None except (asyncio.CancelledError, asyncio.TimeoutError): - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE raise except EofStream: - self._close_code = 1000 + self._close_code = WSCloseCode.OK await self.close() return WSMessage(WSMsgType.CLOSED, None, None) except WebSocketError as exc: @@ -443,7 +444,7 @@ async def receive(self, timeout: Optional[float] = None) -> WSMessage: except Exception as exc: self._exception = exc self._closing = True - self._close_code = 1006 + self._close_code = WSCloseCode.ABNORMAL_CLOSURE await self.close() return WSMessage(WSMsgType.ERROR, exc, None) diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index f1ae72b968f..eb972b20f0a 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -6,7 +6,7 @@ import aiohttp from aiohttp import WSServerHandshakeError, web -from aiohttp.http import WSMsgType +from aiohttp.http import WSCloseCode, WSMsgType async def test_websocket_can_prepare(loop, aiohttp_client) -> None: @@ -153,11 +153,11 @@ async def handler(request): msg = await ws.receive() assert msg.type == aiohttp.WSMsgType.CLOSE - assert msg.data == 1000 + assert msg.data == WSCloseCode.OK assert msg.extra == "" assert ws.closed - assert ws.close_code == 1000 + assert ws.close_code == WSCloseCode.OK await closed @@ -188,11 +188,11 @@ async def handler(request): msg = await ws.receive() assert msg.type == aiohttp.WSMsgType.CLOSE - assert msg.data == 1000 + assert msg.data == WSCloseCode.OK assert msg.extra == "" assert ws.closed - assert ws.close_code == 1000 + assert ws.close_code == WSCloseCode.OK await closed @@ -223,7 +223,7 @@ async def handler(request): msg = await ws.receive() assert msg.type == aiohttp.WSMsgType.CLOSE - assert msg.data == 1000 + assert msg.data == WSCloseCode.OK assert msg.extra == "" await ws.close() @@ -244,7 +244,7 @@ async def handler(request): begin = ws._loop.time() assert await ws.close() elapsed = ws._loop.time() - begin - assert ws.close_code == 1006 + assert ws.close_code == WSCloseCode.ABNORMAL_CLOSURE assert isinstance(ws.exception(), asyncio.TimeoutError) aborted.set_result(1) return ws @@ -300,7 +300,7 @@ async def handler(request): ws = await client.ws_connect("/", autoclose=False, protocols=("eggs", "bar")) - await srv_ws.close(code=1007) + await srv_ws.close(code=WSCloseCode.INVALID_TEXT) msg = await ws.receive() assert msg.type == WSMsgType.CLOSE @@ -321,7 +321,7 @@ async def handler(request): msg = await ws.receive() assert msg.type == WSMsgType.CLOSE - assert msg.data == 1000 + assert msg.data == WSCloseCode.OK assert msg.extra == "exit message" closed.set_result(None) return ws @@ -336,7 +336,7 @@ async def handler(request): msg = await ws.receive() assert msg.type == WSMsgType.PONG - await ws.close(code=1000, message="exit message") + await ws.close(code=WSCloseCode.OK, message="exit message") await closed @@ -407,7 +407,7 @@ async def handler(request): msg = await ws.receive() assert msg.type == WSMsgType.CLOSE - assert msg.data == 1000 + assert msg.data == WSCloseCode.OK assert msg.extra == "exit message" closed.set_result(None) return ws @@ -423,7 +423,7 @@ async def handler(request): assert msg.type == WSMsgType.PONG assert msg.data == b"data" - await ws.close(code=1000, message="exit message") + await ws.close(code=WSCloseCode.OK, message="exit message") await closed @@ -511,7 +511,7 @@ async def handler(request): assert not ws.closed await ws.close() assert ws.closed - assert ws.close_code == 1007 + assert ws.close_code == WSCloseCode.INVALID_TEXT msg = await ws.receive() assert msg.type == WSMsgType.CLOSED @@ -525,7 +525,7 @@ async def handler(request): ws = await client.ws_connect("/", autoclose=False, protocols=("eggs", "bar")) - await ws.close(code=1007) + await ws.close(code=WSCloseCode.INVALID_TEXT) msg = await ws.receive() assert msg.type == WSMsgType.CLOSED await closed From 4186c3bff9e3349c211e74151a2e0e319c159cb3 Mon Sep 17 00:00:00 2001 From: Dmitry Erlikh Date: Tue, 3 Nov 2020 05:19:08 +0100 Subject: [PATCH 3/6] Update the docs --- docs/websocket_utilities.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/websocket_utilities.rst b/docs/websocket_utilities.rst index 5087cedf0f4..3cb421ce408 100644 --- a/docs/websocket_utilities.rst +++ b/docs/websocket_utilities.rst @@ -80,6 +80,24 @@ WebSocket utilities connect to a different IP (when there are multiple for the target) or reconnect to the same IP upon user action. + .. attribute:: ABNORMAL_CLOSURE + + Used to indicate that a connection was closed abnormally + (that is, with no close frame being sent) when a status code + is expected. + + .. attribute:: BAD_GATEWAY + + The server was acting as a gateway or proxy and received + an invalid response from the upstream server. + This is similar to 502 HTTP Status Code. + + .. attribute:: TLS_HANDSHAKE + + Indicates that the connection was closed due to a failure + to perform a TLS handshake + (e.g., the server certificate can't be verified). + .. class:: WSMsgType From cb7ae237f2f1861738d22dc0d00a67d0ff496368 Mon Sep 17 00:00:00 2001 From: Dmitry Erlikh Date: Tue, 3 Nov 2020 05:23:33 +0100 Subject: [PATCH 4/6] Remove TLS_HANDSHAKE ws close code --- aiohttp/http_websocket.py | 1 - docs/websocket_utilities.rst | 6 ------ 2 files changed, 7 deletions(-) diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index 62db49ca63c..d0dee7f4519 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -42,7 +42,6 @@ class WSCloseCode(IntEnum): SERVICE_RESTART = 1012 TRY_AGAIN_LATER = 1013 BAD_GATEWAY = 1014 - TLS_HANDSHAKE = 1015 ALLOWED_CLOSE_CODES = {int(i) for i in WSCloseCode} diff --git a/docs/websocket_utilities.rst b/docs/websocket_utilities.rst index 3cb421ce408..29dc8108e07 100644 --- a/docs/websocket_utilities.rst +++ b/docs/websocket_utilities.rst @@ -92,12 +92,6 @@ WebSocket utilities an invalid response from the upstream server. This is similar to 502 HTTP Status Code. - .. attribute:: TLS_HANDSHAKE - - Indicates that the connection was closed due to a failure - to perform a TLS handshake - (e.g., the server certificate can't be verified). - .. class:: WSMsgType From d804581788c1694aba683937e39813da20b342c0 Mon Sep 17 00:00:00 2001 From: Dmitry Erlikh Date: Tue, 3 Nov 2020 05:43:10 +0100 Subject: [PATCH 5/6] Update close() doc in client_reference.rst and web_reference.rst --- docs/client_reference.rst | 4 ++-- docs/web_reference.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 407006fda17..7ea33d1035f 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -1581,14 +1581,14 @@ manually. The method is converted into :term:`coroutine`, *compress* parameter added. - .. comethod:: close(*, code=1000, message=b'') + .. comethod:: close(*, code=WSCloseCode.OK, message=b'') A :ref:`coroutine` that initiates closing handshake by sending :const:`~aiohttp.WSMsgType.CLOSE` message. It waits for close response from server. To add a timeout to `close()` call just wrap the call with `asyncio.wait()` or `asyncio.wait_for()`. - :param int code: closing code + :param int code: closing code. See also :class:`~aiohttp.WSCloseCode`. :param message: optional payload of *close* message, :class:`str` (converted to *UTF-8* encoded bytes) or :class:`bytes`. diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 4769a89749e..78d352f5adc 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -1117,14 +1117,14 @@ WebSocketResponse The method is converted into :term:`coroutine`, *compress* parameter added. - .. comethod:: close(*, code=1000, message=b'') + .. comethod:: close(*, code=WSCloseCode.OK, message=b'') A :ref:`coroutine` that initiates closing handshake by sending :const:`~aiohttp.WSMsgType.CLOSE` message. It is safe to call `close()` from different task. - :param int code: closing code + :param int code: closing code. See also :class:`~aiohttp.WSCloseCode`. :param message: optional payload of *close* message, :class:`str` (converted to *UTF-8* encoded bytes) From c292b66df7bdcf0bb808e3fc9f2912b3ccb684c3 Mon Sep 17 00:00:00 2001 From: Dmitry Erlikh Date: Tue, 3 Nov 2020 05:47:22 +0100 Subject: [PATCH 6/6] Add changes file --- CHANGES/5192.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/5192.bugfix diff --git a/CHANGES/5192.bugfix b/CHANGES/5192.bugfix new file mode 100644 index 00000000000..9f5b7cd3dcb --- /dev/null +++ b/CHANGES/5192.bugfix @@ -0,0 +1 @@ +Add ABNORMAL_CLOSURE and BAD_GATEWAY to WSCloseCode