Skip to content
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
2 changes: 2 additions & 0 deletions CHANGES/13536.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed the WebSocket reader accepting close code ``1006`` in a Close frame
received from the peer -- by :user:`arshsmith1`.
6 changes: 5 additions & 1 deletion aiohttp/_websocket/reader_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
WSMsgType,
)

ALLOWED_CLOSE_CODES: set[int] = {int(i) for i in WSCloseCode}
# ABNORMAL_CLOSURE is used internally, should never be accepted from a client.
# https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1
ALLOWED_CLOSE_CODES = {int(i) for i in WSCloseCode} - {
int(WSCloseCode.ABNORMAL_CLOSURE)
}

# States for the reader, used to parse the WebSocket frame
# integer values are used so they can be cythonized
Expand Down
2 changes: 0 additions & 2 deletions tests/autobahn/test_autobahn.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ def test_client(report_dir: Path, request: pytest.FixtureRequest) -> None:

results = get_test_results(report_dir / "clients", "aiohttp")
xfail = {
"7.9.5": "The close code should have been 1002 or empty",
"9.1.4": "Did not receive message within 100 seconds.",
"9.1.5": "Did not receive message within 100 seconds.",
"9.1.6": "Did not receive message within 100 seconds.",
Expand Down Expand Up @@ -194,7 +193,6 @@ def test_server(report_dir: Path, request: pytest.FixtureRequest) -> None:

results = get_test_results(report_dir / "servers", "AutobahnServer")
xfail = {
"7.9.5": "The close code should have been 1002 or empty",
"9.1.4": "Did not receive message within 100 seconds.",
"9.1.5": "Did not receive message within 100 seconds.",
"9.1.6": "Did not receive message within 100 seconds.",
Expand Down
14 changes: 14 additions & 0 deletions tests/test_websocket_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,20 @@ def test_close_frame_invalid_code_above_range(
assert ctx.value.code == WSCloseCode.PROTOCOL_ERROR


@pytest.mark.parametrize("code", (1004, 1005, 1006, 1015))
def test_close_frame_reserved_code(parser: PatchableWebSocketReader, code: int) -> None:
# https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1
# 1004, 1005, 1006 and 1015 are resreved and forbidden as a
# status code in a Close frame on the wire. 1006 is a WSCloseCode member
# (aiohttp uses it locally), so it must still be rejected on receipt.
data = build_close_frame(code=code)

with pytest.raises(WebSocketError) as ctx:
parser._feed_data(data)

assert ctx.value.code == WSCloseCode.PROTOCOL_ERROR


def test_close_frame_unicode_err(parser: PatchableWebSocketReader) -> None:
data = build_close_frame(code=1000, message=b"\xf4\x90\x80\x80")

Expand Down
Loading