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

Update close status codes ws #5198

Merged
merged 6 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/5192.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ABNORMAL_CLOSURE and BAD_GATEWAY to WSCloseCode
21 changes: 11 additions & 10 deletions aiohttp/client_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
WS_CLOSED_MESSAGE,
WS_CLOSING_MESSAGE,
WebSocketError,
WSCloseCode,
WSMessage,
WSMsgType,
)
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions aiohttp/http_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ 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
MANDATORY_EXTENSION = 1010
INTERNAL_ERROR = 1011
SERVICE_RESTART = 1012
TRY_AGAIN_LATER = 1013
BAD_GATEWAY = 1014


ALLOWED_CLOSE_CODES = {int(i) for i in WSCloseCode}
Expand Down
21 changes: 11 additions & 10 deletions aiohttp/web_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
WebSocketError,
WebSocketReader,
WebSocketWriter,
WSCloseCode,
WSMessage,
WSMsgType as WSMsgType,
ws_ext_gen,
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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")

Expand All @@ -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

Expand All @@ -384,18 +385,18 @@ 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

if msg.type == WSMsgType.CLOSE:
self._close_code = msg.data
return True

self._close_code = 1006
self._close_code = WSCloseCode.ABNORMAL_CLOSURE
self._exception = asyncio.TimeoutError()
return True
else:
Expand Down Expand Up @@ -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:
Expand All @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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<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`.
Expand Down
4 changes: 2 additions & 2 deletions docs/web_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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<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)
Expand Down
12 changes: 12 additions & 0 deletions docs/websocket_utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ 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.


.. class:: WSMsgType

Expand Down
28 changes: 14 additions & 14 deletions tests/test_web_websocket_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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


Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down