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
4 changes: 4 additions & 0 deletions CHANGES/12895.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Rejected WebSocket ``CLOSE`` frames carrying a status code above the
valid range (greater than ``4999``) with a protocol error, matching the
existing handling of out-of-range low codes, per :rfc:`6455#section-7.4.1`
-- by :user:`dxbjavid`.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Dmitry Trofimov
Dmytro Bohomiakov
Dmytro Kuznetsov
Dustin J. Mitchell
dxbjavid
Earle Lowe
Eduard Iskandarov
Eli Ribble
Expand Down
5 changes: 4 additions & 1 deletion aiohttp/_websocket/reader_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,10 @@ def _handle_frame(
payload_len = len(payload)
if payload_len >= 2:
close_code = UNPACK_CLOSE_CODE(payload[:2])[0]
if close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES:
# https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.2
if close_code > 4999 or (
close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES
):
raise WebSocketError(
WSCloseCode.PROTOCOL_ERROR,
f"Invalid close code: {close_code}",
Expand Down
6 changes: 3 additions & 3 deletions requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ blockbuster==1.5.26
# -r requirements/test-common.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
build==1.5.0
build==1.5.1
# via pip-tools
certifi==2026.6.17
# via requests
Expand Down Expand Up @@ -118,7 +118,7 @@ jinja2==3.1.6
# sphinx
# sphinxcontrib-mermaid
# towncrier
librt==0.12.0
librt==0.13.0
# via mypy
markdown-it-py==3.0.0
# via
Expand Down Expand Up @@ -233,7 +233,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
python-discovery==1.4.3
python-discovery==1.4.4
# via virtualenv
python-on-whales==0.81.0
# via
Expand Down
6 changes: 3 additions & 3 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ blockbuster==1.5.26
# -r requirements/test-common.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
build==1.5.0
build==1.5.1
# via pip-tools
certifi==2026.6.17
# via requests
Expand Down Expand Up @@ -116,7 +116,7 @@ jinja2==3.1.6
# sphinx
# sphinxcontrib-mermaid
# towncrier
librt==0.12.0
librt==0.13.0
# via mypy
markdown-it-py==3.0.0
# via
Expand Down Expand Up @@ -228,7 +228,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
python-discovery==1.4.3
python-discovery==1.4.4
# via virtualenv
python-on-whales==0.81.0
# via
Expand Down
4 changes: 2 additions & 2 deletions requirements/lint.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0
# via -r requirements/lint.in
librt==0.12.0
librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
Expand Down Expand Up @@ -123,7 +123,7 @@ pytest-mock==3.15.1
# via -r requirements/lint.in
python-dateutil==2.9.0.post0
# via freezegun
python-discovery==1.4.3
python-discovery==1.4.4
# via virtualenv
python-on-whales==0.81.0
# via -r requirements/lint.in
Expand Down
2 changes: 1 addition & 1 deletion requirements/test-common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
librt==0.12.0
librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
Expand Down
2 changes: 1 addition & 1 deletion requirements/test-ft.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
librt==0.12.0
librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
Expand Down
2 changes: 1 addition & 1 deletion requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
librt==0.12.0
librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
Expand Down
16 changes: 14 additions & 2 deletions tests/test_websocket_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,9 @@ def test_close_frame(out: WebSocketDataQueue, parser: PatchableWebSocketReader)
def test_close_frame_info(
out: WebSocketDataQueue, parser: PatchableWebSocketReader
) -> None:
parser._handle_frame(True, WSMsgType.CLOSE, b"0112345", 0)
parser._handle_frame(True, WSMsgType.CLOSE, b"\x03\xe912345", 0)
res = out._buffer[0]
assert res == WSMessageClose(data=12337, size=7, extra="12345")
assert res == WSMessageClose(data=1001, size=7, extra="12345")


def test_close_frame_invalid(
Expand All @@ -311,6 +311,18 @@ def test_close_frame_invalid_2(
assert ctx.value.code == WSCloseCode.PROTOCOL_ERROR


@pytest.mark.parametrize("code", (5000, 9999, 65535))
def test_close_frame_invalid_code_above_range(
parser: PatchableWebSocketReader, code: int
) -> None:
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