Skip to content

Optimize _WS_EXT_RE backtracking on Python 3.11+#12346

Open
HarshithReddy01 wants to merge 8 commits intoaio-libs:masterfrom
HarshithReddy01:fix-connection-leak
Open

Optimize _WS_EXT_RE backtracking on Python 3.11+#12346
HarshithReddy01 wants to merge 8 commits intoaio-libs:masterfrom
HarshithReddy01:fix-connection-leak

Conversation

@HarshithReddy01
Copy link
Copy Markdown

What do these changes do?

This change optimizes WebSocket extension parsing in aiohttp/_websocket/helpers.py by using an atomic outer group for Python 3.11+ in _WS_EXT_RE.

The previous pattern used a repeating outer non-atomic group: (?:;\s*(?:...))*.
With many valid extension tokens followed by an invalid suffix, regex matching could spend extra time backtracking across prior iterations before failing.

On Python 3.11+, the outer group is changed to atomic (?>...)*, which prevents that backtracking path while preserving the same matching intent.
On Python 3.10 and lower, behavior is unchanged because atomic groups are not supported there.

Are there changes in behavior for the user?

No user-facing behavior change is intended.

Accepted/rejected extension strings remain the same for valid inputs.
This is a performance-oriented change focused on reducing worst-case backtracking on failing matches.

Is it a substantial burden for the maintainers to support this?

No.

The implementation is a small version-gated regex compile-time branch, with both variants kept structurally identical except for the atomic-group syntax on 3.11+.

Related issue number

Discussed in GHSA-qhr8-wxhx-9q9w (closed).
This PR is submitted as a performance improvement, not a security fix/CVE claim.

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
  • Add a new news fragment into the CHANGES/ folder

Harshith Reddy added 2 commits April 9, 2026 08:28
Use an atomic group for Python 3.11+ in websocket extension parsing and add focused tests to validate behavior and guard against worst-case backtracking regressions.
Include contributor attribution and a misc changelog note for the websocket extension regex optimization change.
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Apr 9, 2026
Comment thread tests/test_ws_ext_helpers.py Fixed
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 9, 2026

Merging this PR will not alter performance

✅ 61 untouched benchmarks
⏩ 4 skipped benchmarks1


Comparing HarshithReddy01:fix-connection-leak (d5f8764) with master (f55503d)

Open in CodSpeed

Footnotes

  1. 4 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

❌ 12 Tests Failed:

Tests completed Failed Passed Skipped
4476 12 4464 68
View the top 3 failed test(s) by shortest run time
tests.test_websocket_helpers::test_ws_ext_parse[permessage-deflate; client_max_window_bits=10-False-expected4]
Stack Traces | 0.029s run time
msg = 'permessage-deflate; client_max_window_bits=10', server = False
expected = (10, False)

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[33m"#x1B[39;49;00m#x1B[33mmsg#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mserver#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mexpected#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=12#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m12#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_max_window_bits=10#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m10#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# out-of-range wbits on server side → skip rather than fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=8#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# unknown param on server side → no match, return zero#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; unknown_param#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_ext_parse#x1B[39;49;00m(msg: #x1B[96mstr#x1B[39;49;00m, server: #x1B[96mbool#x1B[39;49;00m, expected: #x1B[96mtuple#x1B[39;49;00m[#x1B[96mint#x1B[39;49;00m, #x1B[96mbool#x1B[39;49;00m]) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m ws_ext_parse(msg, isserver=server) == expected#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert (15, False) == (10, False)#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         At index 0 diff: #x1B[0m#x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m != #x1B[0m#x1B[94m10#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         Full diff:#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m#x1B[90m #x1B[39;49;00m (#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[91m-     10,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         ?      ^#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[92m+     15,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         ?      ^#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m     False,#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m )#x1B[90m#x1B[39;49;00m#x1B[0m

expected   = (10, False)
msg        = 'permessage-deflate; client_max_window_bits=10'
server     = False

#x1B[1m#x1B[31mtests/test_websocket_helpers.py#x1B[0m:24: AssertionError
tests.test_websocket_helpers::test_ws_ext_parse[permessage-deflate; client_no_context_takeover-False-expected2]
Stack Traces | 0.038s run time
msg = 'permessage-deflate; client_no_context_takeover', server = False
expected = (15, True)

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[33m"#x1B[39;49;00m#x1B[33mmsg#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mserver#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mexpected#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=12#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m12#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_max_window_bits=10#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m10#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# out-of-range wbits on server side → skip rather than fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=8#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# unknown param on server side → no match, return zero#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; unknown_param#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_ext_parse#x1B[39;49;00m(msg: #x1B[96mstr#x1B[39;49;00m, server: #x1B[96mbool#x1B[39;49;00m, expected: #x1B[96mtuple#x1B[39;49;00m[#x1B[96mint#x1B[39;49;00m, #x1B[96mbool#x1B[39;49;00m]) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m ws_ext_parse(msg, isserver=server) == expected#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert (15, False) == (15, True)#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         At index 1 diff: #x1B[0m#x1B[94mFalse#x1B[39;49;00m#x1B[90m#x1B[39;49;00m != #x1B[0m#x1B[94mTrue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         Full diff:#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m#x1B[90m #x1B[39;49;00m (#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m     15,#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[91m-     True,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[92m+     False,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m )#x1B[90m#x1B[39;49;00m#x1B[0m

expected   = (15, True)
msg        = 'permessage-deflate; client_no_context_takeover'
server     = False

#x1B[1m#x1B[31mtests/test_websocket_helpers.py#x1B[0m:24: AssertionError
tests.test_websocket_helpers::test_ws_ext_parse_raises[permessage-deflate; client_max_window_bits=8-False]
Stack Traces | 0.039s run time
msg = 'permessage-deflate; client_max_window_bits=8', server = False

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[33m"#x1B[39;49;00m#x1B[33mmsg#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mserver#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_max_window_bits=8#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; unknown_param#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_ext_parse_raises#x1B[39;49;00m(msg: #x1B[96mstr#x1B[39;49;00m, server: #x1B[96mbool#x1B[39;49;00m) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>       #x1B[94mwith#x1B[39;49;00m pytest.raises(WSHandshakeError):#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       Failed: DID NOT RAISE <class 'aiohttp._websocket.models.WSHandshakeError'>#x1B[0m

msg        = 'permessage-deflate; client_max_window_bits=8'
server     = False

#x1B[1m#x1B[31mtests/test_websocket_helpers.py#x1B[0m:35: Failed
tests.test_websocket_helpers::test_ws_ext_parse[permessage-deflate; server_max_window_bits=12-True-expected3]
Stack Traces | 0.051s run time
msg = 'permessage-deflate; server_max_window_bits=12', server = True
expected = (12, False)

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[33m"#x1B[39;49;00m#x1B[33mmsg#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mserver#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mexpected#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=12#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m12#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_max_window_bits=10#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m10#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# out-of-range wbits on server side → skip rather than fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=8#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# unknown param on server side → no match, return zero#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; unknown_param#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_ext_parse#x1B[39;49;00m(msg: #x1B[96mstr#x1B[39;49;00m, server: #x1B[96mbool#x1B[39;49;00m, expected: #x1B[96mtuple#x1B[39;49;00m[#x1B[96mint#x1B[39;49;00m, #x1B[96mbool#x1B[39;49;00m]) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m ws_ext_parse(msg, isserver=server) == expected#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert (15, False) == (12, False)#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         At index 0 diff: #x1B[0m#x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m != #x1B[0m#x1B[94m12#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         Full diff:#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m#x1B[90m #x1B[39;49;00m (#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[91m-     12,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         ?      ^#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[92m+     15,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         ?      ^#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m     False,#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m )#x1B[90m#x1B[39;49;00m#x1B[0m

expected   = (12, False)
msg        = 'permessage-deflate; server_max_window_bits=12'
server     = True

#x1B[1m#x1B[31mtests/test_websocket_helpers.py#x1B[0m:24: AssertionError
tests.test_websocket_helpers::test_ws_ext_parse[permessage-deflate; server_max_window_bits=8-True-expected5]
Stack Traces | 0.051s run time
msg = 'permessage-deflate; server_max_window_bits=8', server = True
expected = (0, False)

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[33m"#x1B[39;49;00m#x1B[33mmsg#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mserver#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mexpected#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=12#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m12#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_max_window_bits=10#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m10#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# out-of-range wbits on server side → skip rather than fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=8#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# unknown param on server side → no match, return zero#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; unknown_param#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_ext_parse#x1B[39;49;00m(msg: #x1B[96mstr#x1B[39;49;00m, server: #x1B[96mbool#x1B[39;49;00m, expected: #x1B[96mtuple#x1B[39;49;00m[#x1B[96mint#x1B[39;49;00m, #x1B[96mbool#x1B[39;49;00m]) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m ws_ext_parse(msg, isserver=server) == expected#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert (15, False) == (0, False)#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         At index 0 diff: #x1B[0m#x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m != #x1B[0m#x1B[94m0#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         Full diff:#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m#x1B[90m #x1B[39;49;00m (#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[91m-     0,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         ?     ^#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[92m+     15,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         ?     ^^#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m     False,#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m )#x1B[90m#x1B[39;49;00m#x1B[0m

expected   = (0, False)
msg        = 'permessage-deflate; server_max_window_bits=8'
server     = True

#x1B[1m#x1B[31mtests/test_websocket_helpers.py#x1B[0m:24: AssertionError
tests.test_websocket_handshake::test_handshake_compress_server_notakeover
Stack Traces | 0.059s run time
#x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_handshake_compress_server_notakeover#x1B[39;49;00m() -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        hdrs, sec_key = gen_ws_headers(compress=#x1B[94m15#x1B[39;49;00m, server_notakeover=#x1B[94mTrue#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        req = make_mocked_request(#x1B[33m"#x1B[39;49;00m#x1B[33mGET#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33m/#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, headers=hdrs)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        ws = web.WebSocketResponse()#x1B[90m#x1B[39;49;00m
        headers, _, compress, notakeover = ws._handshake(req)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94massert#x1B[39;49;00m compress == #x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m notakeover #x1B[95mis#x1B[39;49;00m #x1B[94mTrue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert False is True#x1B[0m

_          = None
compress   = 15
hdrs       = [('Upgrade', 'websocket'), ('Connection', 'upgrade'), ('Sec-Websocket-Version', '13'), ('Sec-Websocket-Key', 'LJlgZ0aw4oWHxLGbZiVw5Q=='), ('Sec-Websocket-Extensions', 'permessage-deflate; server_no_context_takeover')]
headers    = <CIMultiDict('Upgrade': 'websocket', 'Connection': 'upgrade', 'Sec-WebSocket-Accept': 'UVrdoXZ2mO+mjL8+xcEubmx0B3k=', 'Sec-WebSocket-Extensions': 'permessage-deflate')>
notakeover = False
req        = <Request GET / >
sec_key    = 'LJlgZ0aw4oWHxLGbZiVw5Q=='
ws         = <WebSocketResponse Switching Protocols not prepared>

#x1B[1m#x1B[31mtests/test_websocket_handshake.py#x1B[0m:202: AssertionError
tests.test_websocket_handshake::test_handshake_compress_wbits
Stack Traces | 0.1s run time
#x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_handshake_compress_wbits#x1B[39;49;00m() -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        hdrs, sec_key = gen_ws_headers(compress=#x1B[94m9#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        req = make_mocked_request(#x1B[33m"#x1B[39;49;00m#x1B[33mGET#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33m/#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, headers=hdrs)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        ws = web.WebSocketResponse()#x1B[90m#x1B[39;49;00m
        headers, _, compress, notakeover = ws._handshake(req)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94massert#x1B[39;49;00m #x1B[33m"#x1B[39;49;00m#x1B[33mSec-Websocket-Extensions#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m #x1B[95min#x1B[39;49;00m headers#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m headers[#x1B[33m"#x1B[39;49;00m#x1B[33mSec-Websocket-Extensions#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m] == (#x1B[90m#x1B[39;49;00m
            #x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=9#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       AssertionError: assert 'permessage-deflate' == 'permessage-d...window_bits=9'#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m#x1B[91m- permessage-deflate; server_max_window_bits=9#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[92m+ permessage-deflate#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m

_          = None
compress   = 15
hdrs       = [('Upgrade', 'websocket'), ('Connection', 'upgrade'), ('Sec-Websocket-Version', '13'), ('Sec-Websocket-Key', '938MJWaNPd03JzdGrFp7NA=='), ('Sec-Websocket-Extensions', 'permessage-deflate; server_max_window_bits=9')]
headers    = <CIMultiDict('Upgrade': 'websocket', 'Connection': 'upgrade', 'Sec-WebSocket-Accept': 'UEGN98zpDj8tJsbD4IGRtxNeP6A=', 'Sec-WebSocket-Extensions': 'permessage-deflate')>
notakeover = False
req        = <Request GET / >
sec_key    = '938MJWaNPd03JzdGrFp7NA=='
ws         = <WebSocketResponse Switching Protocols not prepared>

#x1B[1m#x1B[31mtests/test_websocket_handshake.py#x1B[0m:232: AssertionError
tests.test_client_ws::test_ws_connect_deflate_notakeover[pyloop]
Stack Traces | 0.245s run time
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
ws_key = 'OUe68QiCelyKejuSX/Bta/rs34I='
key_data = b'z{\xdc\xd7%o_\\\xf0\xd5-\\\xeb3#\xba'

    #x1B[0m#x1B[94masync#x1B[39;49;00m #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_connect_deflate_notakeover#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        loop: asyncio.AbstractEventLoop, ws_key: #x1B[96mstr#x1B[39;49;00m, key_data: #x1B[96mbytes#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    ) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        resp = mock.Mock()#x1B[90m#x1B[39;49;00m
        resp.status = #x1B[94m101#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        resp.headers = {#x1B[90m#x1B[39;49;00m
            hdrs.UPGRADE: #x1B[33m"#x1B[39;49;00m#x1B[33mwebsocket#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            hdrs.CONNECTION: #x1B[33m"#x1B[39;49;00m#x1B[33mupgrade#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            hdrs.SEC_WEBSOCKET_ACCEPT: ws_key,#x1B[90m#x1B[39;49;00m
            hdrs.SEC_WEBSOCKET_EXTENSIONS: #x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; #x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[33m"#x1B[39;49;00m#x1B[33mclient_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        }#x1B[90m#x1B[39;49;00m
        resp.connection.protocol.read_timeout = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mwith#x1B[39;49;00m mock.patch(#x1B[33m"#x1B[39;49;00m#x1B[33maiohttp.client.os#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m) #x1B[94mas#x1B[39;49;00m m_os:#x1B[90m#x1B[39;49;00m
            #x1B[94mwith#x1B[39;49;00m mock.patch(#x1B[33m"#x1B[39;49;00m#x1B[33maiohttp.client.ClientSession.request#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m) #x1B[94mas#x1B[39;49;00m m_req:#x1B[90m#x1B[39;49;00m
                m_os.urandom.return_value = key_data#x1B[90m#x1B[39;49;00m
                m_req.return_value = loop.create_future()#x1B[90m#x1B[39;49;00m
                m_req.return_value.set_result(resp)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                res = #x1B[94mawait#x1B[39;49;00m aiohttp.ClientSession().ws_connect(#x1B[90m#x1B[39;49;00m
                    #x1B[33m"#x1B[39;49;00m#x1B[33mhttp://test.org#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, compress=#x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
                )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94massert#x1B[39;49;00m res.compress == #x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m res.client_notakeover #x1B[95mis#x1B[39;49;00m #x1B[94mTrue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert False is True#x1B[0m
#x1B[1m#x1B[31mE        +  where False = <aiohttp.client_ws.ClientWebSocketResponse object at 0x0000000037de08a8>.client_notakeover#x1B[0m

key_data   = b'z{\xdc\xd7%o_\\\xf0\xd5-\\\xeb3#\xba'
loop       = <_UnixSelectorEventLoop running=False closed=False debug=False>
m_os       = <MagicMock name='os' id='1057178976'>
m_req      = <MagicMock name='request' id='1018791320'>
res        = <aiohttp.client_ws.ClientWebSocketResponse object at 0x0000000037de08a8>
resp       = <Mock id='1023604616'>
ws_key     = 'OUe68QiCelyKejuSX/Bta/rs34I='

#x1B[1m#x1B[31mtests/test_client_ws.py#x1B[0m:992: AssertionError
tests.test_client_ws::test_ws_connect_deflate_client_wbits_bad[pyloop]
Stack Traces | 0.258s run time
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
ws_key = '7CPuZw1vWoryRLZmcr8Jxpgem0M='
key_data = b"\x05\x82\xe9\xf1'ld\xd8\x026#=)o\x9e("

    #x1B[0m#x1B[94masync#x1B[39;49;00m #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_connect_deflate_client_wbits_bad#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        loop: asyncio.AbstractEventLoop, ws_key: #x1B[96mstr#x1B[39;49;00m, key_data: #x1B[96mbytes#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    ) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        resp = mock.Mock()#x1B[90m#x1B[39;49;00m
        resp.status = #x1B[94m101#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        resp.headers = {#x1B[90m#x1B[39;49;00m
            hdrs.UPGRADE: #x1B[33m"#x1B[39;49;00m#x1B[33mwebsocket#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            hdrs.CONNECTION: #x1B[33m"#x1B[39;49;00m#x1B[33mupgrade#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            hdrs.SEC_WEBSOCKET_ACCEPT: ws_key,#x1B[90m#x1B[39;49;00m
            hdrs.SEC_WEBSOCKET_EXTENSIONS: #x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; #x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[33m"#x1B[39;49;00m#x1B[33mclient_max_window_bits=6#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        }#x1B[90m#x1B[39;49;00m
        #x1B[94mwith#x1B[39;49;00m mock.patch(#x1B[33m"#x1B[39;49;00m#x1B[33maiohttp.client.os#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m) #x1B[94mas#x1B[39;49;00m m_os:#x1B[90m#x1B[39;49;00m
            #x1B[94mwith#x1B[39;49;00m mock.patch(#x1B[33m"#x1B[39;49;00m#x1B[33maiohttp.client.ClientSession.request#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m) #x1B[94mas#x1B[39;49;00m m_req:#x1B[90m#x1B[39;49;00m
                m_os.urandom.return_value = key_data#x1B[90m#x1B[39;49;00m
                m_req.return_value = loop.create_future()#x1B[90m#x1B[39;49;00m
                m_req.return_value.set_result(resp)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
>               #x1B[94mwith#x1B[39;49;00m pytest.raises(client.WSServerHandshakeError):#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE               Failed: DID NOT RAISE <class 'aiohttp.client_exceptions.WSServerHandshakeError'>#x1B[0m

key_data   = b"\x05\x82\xe9\xf1'ld\xd8\x026#=)o\x9e("
loop       = <_UnixSelectorEventLoop running=False closed=False debug=False>
m_os       = <MagicMock name='os' id='140680473614440'>
m_req      = <MagicMock name='request' id='140680473613600'>
resp       = <Mock id='140680473614384'>
ws_key     = '7CPuZw1vWoryRLZmcr8Jxpgem0M='

#x1B[1m#x1B[31mtests/test_client_ws.py#x1B[0m:1040: Failed
tests.test_client_ws::test_ws_connect_deflate_client_wbits[pyloop]
Stack Traces | 0.263s run time
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
ws_key = 'jFMUUvBPR+nsICO0A3YWWFXnV5M='
key_data = b'\x06\xb4D\x9778\xec\xcc\xa3\x8c\xe1\x19?\x17F\t'

    #x1B[0m#x1B[94masync#x1B[39;49;00m #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_connect_deflate_client_wbits#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        loop: asyncio.AbstractEventLoop, ws_key: #x1B[96mstr#x1B[39;49;00m, key_data: #x1B[96mbytes#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    ) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        resp = mock.Mock()#x1B[90m#x1B[39;49;00m
        resp.status = #x1B[94m101#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        resp.headers = {#x1B[90m#x1B[39;49;00m
            hdrs.UPGRADE: #x1B[33m"#x1B[39;49;00m#x1B[33mwebsocket#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            hdrs.CONNECTION: #x1B[33m"#x1B[39;49;00m#x1B[33mupgrade#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            hdrs.SEC_WEBSOCKET_ACCEPT: ws_key,#x1B[90m#x1B[39;49;00m
            hdrs.SEC_WEBSOCKET_EXTENSIONS: #x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; #x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            #x1B[33m"#x1B[39;49;00m#x1B[33mclient_max_window_bits=10#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        }#x1B[90m#x1B[39;49;00m
        resp.connection.protocol.read_timeout = #x1B[94mNone#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mwith#x1B[39;49;00m mock.patch(#x1B[33m"#x1B[39;49;00m#x1B[33maiohttp.client.os#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m) #x1B[94mas#x1B[39;49;00m m_os:#x1B[90m#x1B[39;49;00m
            #x1B[94mwith#x1B[39;49;00m mock.patch(#x1B[33m"#x1B[39;49;00m#x1B[33maiohttp.client.ClientSession.request#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m) #x1B[94mas#x1B[39;49;00m m_req:#x1B[90m#x1B[39;49;00m
                m_os.urandom.return_value = key_data#x1B[90m#x1B[39;49;00m
                m_req.return_value = loop.create_future()#x1B[90m#x1B[39;49;00m
                m_req.return_value.set_result(resp)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
                res = #x1B[94mawait#x1B[39;49;00m aiohttp.ClientSession().ws_connect(#x1B[90m#x1B[39;49;00m
                    #x1B[33m"#x1B[39;49;00m#x1B[33mhttp://test.org#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, compress=#x1B[94m15#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
                )#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m res.compress == #x1B[94m10#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert 15 == 10#x1B[0m
#x1B[1m#x1B[31mE        +  where 15 = <aiohttp.client_ws.ClientWebSocketResponse object at 0x0000000041331da8>.compress#x1B[0m

key_data   = b'\x06\xb4D\x9778\xec\xcc\xa3\x8c\xe1\x19?\x17F\t'
loop       = <_UnixSelectorEventLoop running=False closed=False debug=False>
m_os       = <MagicMock name='os' id='1009392448'>
m_req      = <MagicMock name='request' id='1009306328'>
res        = <aiohttp.client_ws.ClientWebSocketResponse object at 0x0000000041331da8>
resp       = <Mock id='1163807000'>
ws_key     = 'jFMUUvBPR+nsICO0A3YWWFXnV5M='

#x1B[1m#x1B[31mtests/test_client_ws.py#x1B[0m:1018: AssertionError
tests.test_websocket_helpers::test_ws_ext_parse[permessage-deflate; server_no_context_takeover-True-expected1]
Stack Traces | 0.368s run time
msg = 'permessage-deflate; server_no_context_takeover', server = True
expected = (15, True)

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[33m"#x1B[39;49;00m#x1B[33mmsg#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mserver#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33mexpected#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_no_context_takeover#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m15#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=12#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m12#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; client_max_window_bits=10#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m, (#x1B[94m10#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# out-of-range wbits on server side → skip rather than fail#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; server_max_window_bits=8#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
            #x1B[90m# unknown param on server side → no match, return zero#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            (#x1B[33m"#x1B[39;49;00m#x1B[33mpermessage-deflate; unknown_param#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[94mTrue#x1B[39;49;00m, (#x1B[94m0#x1B[39;49;00m, #x1B[94mFalse#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_ws_ext_parse#x1B[39;49;00m(msg: #x1B[96mstr#x1B[39;49;00m, server: #x1B[96mbool#x1B[39;49;00m, expected: #x1B[96mtuple#x1B[39;49;00m[#x1B[96mint#x1B[39;49;00m, #x1B[96mbool#x1B[39;49;00m]) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m ws_ext_parse(msg, isserver=server) == expected#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert (15, False) == (15, True)#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         At index 1 diff: #x1B[0m#x1B[94mFalse#x1B[39;49;00m#x1B[90m#x1B[39;49;00m != #x1B[0m#x1B[94mTrue#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m
#x1B[1m#x1B[31mE         Full diff:#x1B[0m
#x1B[1m#x1B[31mE         #x1B[0m#x1B[90m #x1B[39;49;00m (#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m     15,#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[91m-     True,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[92m+     False,#x1B[39;49;00m#x1B[90m#x1B[39;49;00m#x1B[0m
#x1B[1m#x1B[31mE         #x1B[90m #x1B[39;49;00m )#x1B[90m#x1B[39;49;00m#x1B[0m

expected   = (15, True)
msg        = 'permessage-deflate; server_no_context_takeover'
server     = True

#x1B[1m#x1B[31mtests/test_websocket_helpers.py#x1B[0m:24: AssertionError
View the full list of 1 ❄️ flaky test(s)
tests.test_websocket_handshake::test_handshake_compress_wbits_error

Flake rate in main: 4.76% (Passed 20 times, Failed 1 times)

Stack Traces | 0.086s run time
#x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_handshake_compress_wbits_error#x1B[39;49;00m() -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        hdrs, sec_key = gen_ws_headers(compress=#x1B[94m6#x1B[39;49;00m)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        req = make_mocked_request(#x1B[33m"#x1B[39;49;00m#x1B[33mGET#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, #x1B[33m"#x1B[39;49;00m#x1B[33m/#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, headers=hdrs)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        ws = web.WebSocketResponse()#x1B[90m#x1B[39;49;00m
        headers, _, compress, notakeover = ws._handshake(req)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m #x1B[33m"#x1B[39;49;00m#x1B[33mSec-Websocket-Extensions#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m #x1B[95mnot#x1B[39;49;00m #x1B[95min#x1B[39;49;00m headers#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       AssertionError: assert 'Sec-Websocket-Extensions' not in <CIMultiDict('Upgrade': 'websocket', 'Connection': 'upgrade', 'Sec-WebSocket-Accept': 'bFyOqOuIteKTQQARO8bRaDwidDk=', 'Sec-WebSocket-Extensions': 'permessage-deflate')>#x1B[0m

_          = None
compress   = 15
hdrs       = [('Upgrade', 'websocket'), ('Connection', 'upgrade'), ('Sec-Websocket-Version', '13'), ('Sec-Websocket-Key', 'Zu+6WRylR4ZoYSy2f5Ud4w=='), ('Sec-Websocket-Extensions', 'permessage-deflate; server_max_window_bits=6')]
headers    = <CIMultiDict('Upgrade': 'websocket', 'Connection': 'upgrade', 'Sec-WebSocket-Accept': 'bFyOqOuIteKTQQARO8bRaDwidDk=', 'Sec-WebSocket-Extensions': 'permessage-deflate')>
notakeover = False
req        = <Request GET / >
sec_key    = 'Zu+6WRylR4ZoYSy2f5Ud4w=='
ws         = <WebSocketResponse Switching Protocols not prepared>

#x1B[1m#x1B[31mtests/test_websocket_handshake.py#x1B[0m:246: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Comment thread CHANGES/12346.misc.rst
Comment thread tests/test_ws_ext_helpers.py Outdated
Comment thread tests/test_ws_ext_helpers.py Outdated
@HarshithReddy01
Copy link
Copy Markdown
Author

The Test (ubuntu, 3.14t, false) failure is pre-existing on master and unrelated to this PR, it's test_parse_set_cookie_headers_uses_unquote_with_octal failing due to Python 3.14t's new strict control character check in http.cookies.Morsel. The fail-fast strategy cancelled all other jobs. Happy to rerun CI once confirmed.

@Dreamsorcerer
Copy link
Copy Markdown
Member

The Test (ubuntu, 3.14t, false) failure is pre-existing on master and unrelated to this PR, it's test_parse_set_cookie_headers_uses_unquote_with_octal failing due to Python 3.14t's new strict control character check in http.cookies.Morsel. The fail-fast strategy cancelled all other jobs. Happy to rerun CI once confirmed.

Yeah, it's a security patch to 3.14 that's broken it. We'll need to sort a separate PR for that shortly.

@Dreamsorcerer Dreamsorcerer added the backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot label Apr 9, 2026
@Dreamsorcerer
Copy link
Copy Markdown
Member

Seems to have broken something on PyPy...

@Dreamsorcerer
Copy link
Copy Markdown
Member

@HarshithReddy01 Any idea on why PyPy is not parsing these correctly? Maybe we need the condition to also exclude PyPy as well as Python 3.10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants