Skip to content

Commit 4815765

Browse files
[PR #8597/c99a1e27 backport][3.10] Fix reading of body when ignoring an upgrade request (#8629)
Co-authored-by: Sam Bull <[email protected]> Co-authored-by: J. Nick Koston <[email protected]> Fixes #8414.
1 parent 266608d commit 4815765

File tree

4 files changed

+33
-15
lines changed

4 files changed

+33
-15
lines changed

CHANGES/8597.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed request body not being read when ignoring an Upgrade request -- by :user:`Dreamsorcerer`.

aiohttp/_http_parser.pyx

+11-8
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ include "_headers.pxi"
4747

4848
from aiohttp cimport _find_header
4949

50+
ALLOWED_UPGRADES = frozenset({"websocket"})
5051
DEF DEFAULT_FREELIST_SIZE = 250
5152

5253
cdef extern from "Python.h":
@@ -417,16 +418,20 @@ cdef class HttpParser:
417418
cdef _on_headers_complete(self):
418419
self._process_header()
419420

420-
method = http_method_str(self._cparser.method)
421421
should_close = not cparser.llhttp_should_keep_alive(self._cparser)
422422
upgrade = self._cparser.upgrade
423423
chunked = self._cparser.flags & cparser.F_CHUNKED
424424

425425
raw_headers = tuple(self._raw_headers)
426426
headers = CIMultiDictProxy(self._headers)
427427

428-
if upgrade or self._cparser.method == cparser.HTTP_CONNECT:
429-
self._upgraded = True
428+
if self._cparser.type == cparser.HTTP_REQUEST:
429+
allowed = upgrade and headers.get("upgrade", "").lower() in ALLOWED_UPGRADES
430+
if allowed or self._cparser.method == cparser.HTTP_CONNECT:
431+
self._upgraded = True
432+
else:
433+
if upgrade and self._cparser.status_code == 101:
434+
self._upgraded = True
430435

431436
# do not support old websocket spec
432437
if SEC_WEBSOCKET_KEY1 in headers:
@@ -441,6 +446,7 @@ cdef class HttpParser:
441446
encoding = enc
442447

443448
if self._cparser.type == cparser.HTTP_REQUEST:
449+
method = http_method_str(self._cparser.method)
444450
msg = _new_request_message(
445451
method, self._path,
446452
self.http_version(), headers, raw_headers,
@@ -565,7 +571,7 @@ cdef class HttpParser:
565571
if self._upgraded:
566572
return messages, True, data[nb:]
567573
else:
568-
return messages, False, b''
574+
return messages, False, b""
569575

570576
def set_upgraded(self, val):
571577
self._upgraded = val
@@ -748,10 +754,7 @@ cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1:
748754
pyparser._last_error = exc
749755
return -1
750756
else:
751-
if (
752-
pyparser._cparser.upgrade or
753-
pyparser._cparser.method == cparser.HTTP_CONNECT
754-
):
757+
if pyparser._upgraded or pyparser._cparser.method == cparser.HTTP_CONNECT:
755758
return 2
756759
else:
757760
return 0

tests/test_http_parser.py

+20
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,26 @@ def test_http_request_upgrade(parser: Any) -> None:
823823
assert tail == b"some raw data"
824824

825825

826+
async def test_http_request_upgrade_unknown(parser: Any) -> None:
827+
text = (
828+
b"POST / HTTP/1.1\r\n"
829+
b"Connection: Upgrade\r\n"
830+
b"Content-Length: 2\r\n"
831+
b"Upgrade: unknown\r\n"
832+
b"Content-Type: application/json\r\n\r\n"
833+
b"{}"
834+
)
835+
messages, upgrade, tail = parser.feed_data(text)
836+
837+
msg = messages[0][0]
838+
assert not msg.should_close
839+
assert msg.upgrade
840+
assert not upgrade
841+
assert not msg.chunked
842+
assert tail == b""
843+
assert await messages[0][-1].read() == b"{}"
844+
845+
826846
@pytest.fixture
827847
def xfail_c_parser_url(request) -> None:
828848
if isinstance(request.getfixturevalue("parser"), HttpRequestParserPy):

tests/test_web_server.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from aiohttp import client, helpers, web
7+
from aiohttp import client, web
88

99

1010
async def test_simple_server(aiohttp_raw_server, aiohttp_client) -> None:
@@ -19,12 +19,6 @@ async def handler(request):
1919
assert txt == "/path/to"
2020

2121

22-
@pytest.mark.xfail(
23-
not helpers.NO_EXTENSIONS,
24-
raises=client.ServerDisconnectedError,
25-
reason="The behavior of C-extensions differs from pure-Python: "
26-
"https://github.com/aio-libs/aiohttp/issues/6446",
27-
)
2822
async def test_unsupported_upgrade(aiohttp_raw_server, aiohttp_client) -> None:
2923
# don't fail if a client probes for an unsupported protocol upgrade
3024
# https://github.com/aio-libs/aiohttp/issues/6446#issuecomment-999032039

0 commit comments

Comments
 (0)