Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
183 commits
Select commit Hold shift + click to select a range
5968707
Test chunk splits after pause
Dreamsorcerer Jan 15, 2026
2eeeb5e
Update tests/test_http_parser.py
Dreamsorcerer Jan 15, 2026
8b0cfee
Apply suggestions from code review
Dreamsorcerer Jan 15, 2026
9cd9154
Update tests/test_http_parser.py
Dreamsorcerer Jan 15, 2026
83fc87c
Update tests/test_http_parser.py
Dreamsorcerer Jan 15, 2026
efe2ca9
Read small chunks from decompressors
Dreamsorcerer Jan 15, 2026
57bf4a2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2026
82a4dfb
Fix
Dreamsorcerer Jan 15, 2026
c44fd1e
Merge branch 'Dreamsorcerer-patch-5' of github.com:aio-libs/aiohttp i…
Dreamsorcerer Jan 15, 2026
a85f38b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2026
602eb51
Update compression_utils.py
Dreamsorcerer Jan 15, 2026
32f0a84
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
94c70a9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
bd34cea
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
0d4683c
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
d3df801
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
b81f901
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
4dd2279
Fix
Dreamsorcerer Jan 16, 2026
3a604a4
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
10feb8b
Fix
Dreamsorcerer Jan 16, 2026
d02bbf5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
67bd57d
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
04b717f
Fix
Dreamsorcerer Jan 16, 2026
53ac968
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
44c12a1
Fix
Dreamsorcerer Jan 16, 2026
7986da3
Merge branch 'Dreamsorcerer-patch-5' of github.com:aio-libs/aiohttp i…
Dreamsorcerer Jan 16, 2026
75794c2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
ab73626
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
ae46ee1
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
d738031
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
1d5fc0e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
28dffda
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
2eb92be
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
3dbf9c8
Update test_http_parser.py
Dreamsorcerer Jan 16, 2026
3ea25ca
Fix
Dreamsorcerer Jan 16, 2026
d99fc34
Update streams.py
Dreamsorcerer Jan 16, 2026
11cf432
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
de6fca2
Update http_parser.py
Dreamsorcerer Jan 16, 2026
3a80456
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
7da23c1
Update http_parser.py
Dreamsorcerer Jan 16, 2026
a4fc7c1
Update _http_parser.pyx
Dreamsorcerer Jan 16, 2026
b3c77d7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
f26edc0
Update _http_parser.pyx
Dreamsorcerer Jan 16, 2026
1997c8b
Update _http_parser.pyx
Dreamsorcerer Jan 16, 2026
b4443b7
Apply suggestions from code review
Dreamsorcerer Jan 16, 2026
b17c013
Fix
Dreamsorcerer Jan 16, 2026
d628cee
Update _http_parser.pyx
Dreamsorcerer Jan 16, 2026
cd2e07a
Update http_parser.py
Dreamsorcerer Jan 17, 2026
fd74c03
Update compression_utils.py
Dreamsorcerer Jan 17, 2026
087de36
Update streams.py
Dreamsorcerer Jan 17, 2026
103c4a6
Update _http_parser.pyx
Dreamsorcerer Jan 17, 2026
a249c04
Update _http_parser.pyx
Dreamsorcerer Jan 17, 2026
f20ddc7
Update http_parser.py
Dreamsorcerer Jan 17, 2026
0fe7000
Update web_protocol.py
Dreamsorcerer Jan 17, 2026
5130266
Fix
Dreamsorcerer Jan 17, 2026
7db0120
Update web_protocol.py
Dreamsorcerer Jan 17, 2026
d113ccb
Update http_parser.py
Dreamsorcerer Jan 17, 2026
185db0b
Update aiohttp/_http_parser.pyx
Dreamsorcerer Jan 17, 2026
3be7ac6
Update base_protocol.py
Dreamsorcerer Jan 17, 2026
80eb8c7
Update _http_parser.pyx
Dreamsorcerer Jan 19, 2026
5183441
Update _http_parser.pyx
Dreamsorcerer Jan 19, 2026
60c61e3
Update test_http_parser.py
Dreamsorcerer Jan 19, 2026
2a6eff8
Update _http_parser.pyx
Dreamsorcerer Jan 19, 2026
dbfc0c8
Update _http_parser.pyx
Dreamsorcerer Jan 19, 2026
eac561e
Update _http_parser.pyx
Dreamsorcerer Jan 21, 2026
1a40781
Update _http_parser.pyx
Dreamsorcerer Jan 21, 2026
520b64a
Update _http_parser.pyx
Dreamsorcerer Jan 22, 2026
9c2987b
Update _http_parser.pyx
Dreamsorcerer Jan 25, 2026
c4b058d
Update _http_parser.pyx
Dreamsorcerer Jan 25, 2026
fa644c7
Update test_client_functional.py
Dreamsorcerer Jan 25, 2026
dd82b9f
Update test_base_protocol.py
Dreamsorcerer Jan 25, 2026
3099a40
Update _http_parser.pyx
Dreamsorcerer Jan 26, 2026
7775793
Update _http_parser.pyx
Dreamsorcerer Jan 26, 2026
9525459
Update base_protocol.py
Dreamsorcerer Jan 26, 2026
f4985a2
Update client_proto.py
Dreamsorcerer Jan 26, 2026
32d5c5f
Update web_protocol.py
Dreamsorcerer Jan 26, 2026
afa2b55
Update test_base_protocol.py
Dreamsorcerer Jan 26, 2026
30c23d4
Update test_client_proto.py
Dreamsorcerer Jan 26, 2026
6a5a2c7
Update test_http_parser.py
Dreamsorcerer Jan 26, 2026
45f66d1
Update test_http_parser.py
Dreamsorcerer Jan 26, 2026
80d955f
Update test_flowcontrol_streams.py
Dreamsorcerer Jan 26, 2026
0cc6275
Update test_client_proto.py
Dreamsorcerer Jan 26, 2026
69e3bbd
Update test_http_parser.py
Dreamsorcerer Jan 26, 2026
397e905
Update test_http_parser.py
Dreamsorcerer Jan 26, 2026
aed6863
Update test_websocket_parser.py
Dreamsorcerer Jan 26, 2026
a0fb83b
Update test_base_protocol.py
Dreamsorcerer Jan 26, 2026
360f6de
Update test_client_proto.py
Dreamsorcerer Jan 26, 2026
6e04d89
Update test_http_parser.py
Dreamsorcerer Jan 26, 2026
698e0cc
Update compression_utils.py
Dreamsorcerer Jan 27, 2026
48d4119
Update streams.py
Dreamsorcerer Jan 28, 2026
6598ff6
Update base_protocol.py
Dreamsorcerer Jan 28, 2026
3f76e2c
Update base_protocol.py
Dreamsorcerer Jan 28, 2026
faf6e40
Update client_proto.py
Dreamsorcerer Jan 28, 2026
add2b70
Update base_protocol.py
Dreamsorcerer Jan 28, 2026
3396079
Update compression_utils.py
Dreamsorcerer Jan 28, 2026
69a59a8
Update compression_utils.py
Dreamsorcerer Jan 28, 2026
c5f6e6a
Update compression_utils.py
Dreamsorcerer Jan 29, 2026
51eda1b
Update compression_utils.py
Dreamsorcerer Jan 29, 2026
156fb3c
Update base_protocol.py
Dreamsorcerer Jan 29, 2026
8b05bfc
Update streams.py
Dreamsorcerer Jan 29, 2026
f1bfba5
Update test_http_parser.py
Dreamsorcerer Jan 29, 2026
9c1ffa9
Update test_base_protocol.py
Dreamsorcerer Jan 29, 2026
79c9da6
Update test_streams.py
Dreamsorcerer Jan 29, 2026
9467ad2
Update test_http_parser.py
Dreamsorcerer Jan 29, 2026
5e8068f
Apply suggestions from code review
Dreamsorcerer Jan 30, 2026
e1de722
Update http_parser.py
Dreamsorcerer Jan 30, 2026
46713f5
Update test_client_functional.py
Dreamsorcerer Jan 30, 2026
67c210d
Update http_exceptions.py
Dreamsorcerer Jan 30, 2026
2bc5d17
Update test_multipart.py
Dreamsorcerer Feb 3, 2026
46eeeda
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 3, 2026
618309f
Update test_web_functional.py
Dreamsorcerer Feb 3, 2026
2cc7567
Apply suggestions from code review
Dreamsorcerer Feb 3, 2026
1f256df
Update test_multipart.py
Dreamsorcerer Feb 3, 2026
70a51f5
Update multipart.py
Dreamsorcerer Feb 3, 2026
06c5ec9
Update multipart.py
Dreamsorcerer Feb 3, 2026
69c1602
Update multipart.py
Dreamsorcerer Feb 3, 2026
36c2bc3
Update web_request.py
Dreamsorcerer Feb 3, 2026
78e7099
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 3, 2026
2c47750
Update test_multipart.py
Dreamsorcerer Feb 3, 2026
e47504c
Update multipart.py
Dreamsorcerer Feb 3, 2026
d92ed4c
Apply suggestions from code review
Dreamsorcerer Feb 3, 2026
ad2c9ab
Update multipart.py
Dreamsorcerer Feb 3, 2026
bd7c69c
Update multipart.py
Dreamsorcerer Feb 3, 2026
2106fbe
Update multipart.py
Dreamsorcerer Feb 3, 2026
4f7f928
Update multipart.py
Dreamsorcerer Feb 3, 2026
2677720
Update web_request.py
Dreamsorcerer Feb 4, 2026
5cb6324
Update multipart.py
Dreamsorcerer Feb 4, 2026
bd6b520
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 4, 2026
bd625ba
Apply suggestion from @Dreamsorcerer
Dreamsorcerer Feb 4, 2026
ef3b5d9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 4, 2026
c2ba043
Update multipart.py
Dreamsorcerer Feb 4, 2026
dcdc386
Update multipart.py
Dreamsorcerer Feb 4, 2026
3525d3d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 4, 2026
271d10b
Update multipart.py
Dreamsorcerer Feb 4, 2026
6749571
Update multipart.py
Dreamsorcerer Feb 4, 2026
28c6bcf
Merge branch 'master' into Dreamsorcerer-patch-5
Dreamsorcerer Feb 5, 2026
e874ebe
Apply suggestion from @Dreamsorcerer
Dreamsorcerer Feb 5, 2026
389b0e6
Update test_multipart.py
Dreamsorcerer Feb 5, 2026
df03906
Merge branch 'master' into Dreamsorcerer-patch-5
Dreamsorcerer Mar 26, 2026
b420704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 26, 2026
045ffb0
Apply suggestion from @Dreamsorcerer
Dreamsorcerer Mar 30, 2026
09062db
Merge branch 'master' into Dreamsorcerer-patch-5
Dreamsorcerer Mar 30, 2026
1c1ea45
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 30, 2026
7f4e807
Fix when compressed data just trips the high water mark
Dreamsorcerer Mar 30, 2026
8256f07
attempt to fix connection closed cleanly while decompression still in…
bdraco Apr 1, 2026
6b08dee
rely on timeout instead
bdraco Apr 1, 2026
97c9b1c
check parser as its the source of truth
bdraco Apr 1, 2026
d719a0d
Revert "check parser as its the source of truth"
bdraco Apr 1, 2026
d6df346
Revert "rely on timeout instead"
bdraco Apr 1, 2026
e093db5
Revert "attempt to fix connection closed cleanly while decompression …
bdraco Apr 1, 2026
f5aa703
one more attempt
bdraco Apr 1, 2026
cb380ef
Revert "one more attempt"
bdraco Apr 1, 2026
bda59d2
Update _http_parser.pyx
Dreamsorcerer Apr 3, 2026
e2c7e07
Update test_multipart.py
Dreamsorcerer Apr 3, 2026
010c507
Update test_multipart.py
Dreamsorcerer Apr 3, 2026
7d5a9be
Update test_http_parser.py
Dreamsorcerer Apr 3, 2026
e499e20
Update test_http_parser.py
Dreamsorcerer Apr 3, 2026
aece402
Update test_http_parser.py
Dreamsorcerer Apr 3, 2026
7659f5b
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
d748132
Apply suggestions from code review
Dreamsorcerer Apr 3, 2026
c009eed
Merge branch 'master' into Dreamsorcerer-patch-5
Dreamsorcerer Apr 3, 2026
518bb35
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
0e6bea5
Update _http_parser.pyx
Dreamsorcerer Apr 3, 2026
7380568
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 3, 2026
742436d
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
4ac1cec
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
ae697a2
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
a6d8f5e
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
b5ea384
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
c4dadbe
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
ee9bc19
Update test_web_protocol.py
Dreamsorcerer Apr 3, 2026
5f00f8d
Update _http_parser.pyx
Dreamsorcerer Apr 3, 2026
54d16f9
Update client_proto.py
Dreamsorcerer Apr 3, 2026
a7a7c73
Update multipart.py
Dreamsorcerer Apr 3, 2026
b45b20f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 3, 2026
f4b7166
Update web_request.py
Dreamsorcerer Apr 3, 2026
ab02eef
Update test_http_parser.py
Dreamsorcerer Apr 4, 2026
d038681
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 4, 2026
ac0dc88
Update _http_parser.pyx
Dreamsorcerer Apr 4, 2026
d2220d3
Another test
Apr 4, 2026
9f09578
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 4, 2026
aaefa3c
Another test
Apr 4, 2026
d09ca51
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 4, 2026
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 aiohttp/_cparser.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ cdef extern from "llhttp.h":

int llhttp_should_keep_alive(const llhttp_t* parser)

void llhttp_resume(llhttp_t* parser)
void llhttp_resume_after_upgrade(llhttp_t* parser)

llhttp_errno_t llhttp_get_errno(const llhttp_t* parser)
Expand Down
95 changes: 66 additions & 29 deletions aiohttp/_http_parser.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ include "_headers.pxi"

from aiohttp cimport _find_header

ALLOWED_UPGRADES = frozenset({"websocket"})

cdef frozenset ALLOWED_UPGRADES = frozenset({"websocket"})
DEF DEFAULT_FREELIST_SIZE = 250

cdef extern from "Python.h":
Expand All @@ -69,7 +70,7 @@ cdef object CONTENT_ENCODING = hdrs.CONTENT_ENCODING
cdef object EMPTY_PAYLOAD = _EMPTY_PAYLOAD
cdef object StreamReader = _StreamReader
cdef object DeflateBuffer = _DeflateBuffer
cdef bytes EMPTY_BYTES = b""
cdef tuple EMPTY_FEED_DATA_RESULT = ((), False, b"")

# RFC 9110 singleton headers — duplicates are rejected in strict mode.
# In lax mode (response parser default), the check is skipped entirely
Expand Down Expand Up @@ -298,7 +299,7 @@ cdef class HttpParser:
bint _has_value
int _header_name_size

object _protocol
readonly object protocol
object _loop
object _timer

Expand All @@ -309,6 +310,7 @@ cdef class HttpParser:
bint _read_until_eof
bint _lax

bytes _tail
bint _started
object _url
bytearray _buf
Expand All @@ -319,6 +321,8 @@ cdef class HttpParser:
list _raw_headers
bint _upgraded
list _messages
bint _more_data_available
bint _paused
object _payload
bint _payload_error
object _payload_exception
Expand Down Expand Up @@ -359,18 +363,21 @@ cdef class HttpParser:
self._cparser.data = <void*>self
self._cparser.content_length = 0

self._protocol = protocol
self.protocol = protocol
self._loop = loop
self._timer = timer

self._buf = bytearray()
self._more_data_available = False
self._paused = False
self._payload = None
self._payload_error = 0
self._payload_exception = payload_exception
self._messages = []

self._raw_name = EMPTY_BYTES
self._raw_value = EMPTY_BYTES
self._raw_name = b""
self._raw_value = b""
self._tail = b""
self._has_value = False
self._header_name_size = 0

Expand Down Expand Up @@ -401,7 +408,7 @@ cdef class HttpParser:

cdef _process_header(self):
cdef str value
if self._raw_name is not EMPTY_BYTES:
if self._raw_name is not b"":
name = find_header(self._raw_name)
value = self._raw_value.decode('utf-8', 'surrogateescape')

Expand All @@ -426,20 +433,20 @@ cdef class HttpParser:
self._has_value = False
self._header_name_size = 0
self._raw_headers.append((self._raw_name, self._raw_value))
self._raw_name = EMPTY_BYTES
self._raw_value = EMPTY_BYTES
self._raw_name = b""
self._raw_value = b""

cdef _on_header_field(self, char* at, size_t length):
if self._has_value:
self._process_header()

if self._raw_name is EMPTY_BYTES:
if self._raw_name is b"":
self._raw_name = at[:length]
else:
self._raw_name += at[:length]

cdef _on_header_value(self, char* at, size_t length):
if self._raw_value is EMPTY_BYTES:
if self._raw_value is b"":
self._raw_value = at[:length]
else:
self._raw_value += at[:length]
Expand Down Expand Up @@ -495,7 +502,7 @@ cdef class HttpParser:
self._read_until_eof)
):
payload = StreamReader(
self._protocol, timer=self._timer, loop=self._loop,
self.protocol, timer=self._timer, loop=self._loop,
limit=self._limit)
else:
payload = EMPTY_PAYLOAD
Expand Down Expand Up @@ -535,6 +542,10 @@ cdef class HttpParser:

### Public API ###

def pause_reading(self):
assert self._payload is not None
self._paused = True

def feed_eof(self):
cdef bytes desc

Expand Down Expand Up @@ -562,6 +573,21 @@ cdef class HttpParser:
char* base
cdef cparser.llhttp_errno_t errno

# Proactor loop sends bytearray.
if type(data) is not bytes:
data = bytes(data)

if self._tail:
data, self._tail = self._tail + data, b""

had_more_data = self._more_data_available
if self._more_data_available:
result = cb_on_body(self._cparser, b"", 0)
if result is cparser.HPE_PAUSED:
self._tail = data
return EMPTY_FEED_DATA_RESULT
# TODO: Do we need to handle error case (-1)?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cdef int cb_on_body(cparser.llhttp_t* parser,
                    const char *at, size_t length) except -1:

I think this is already covered, but could use a test.

Marked with except -1 so should be automatic Cython should check PyErr_Occurred and re-raise the caught BaseException in cb_on_body


PyObject_GetBuffer(data, &self.py_buf, PyBUF_SIMPLE)
# Cache buffer pointer before PyBuffer_Release to avoid use-after-release.
base = <char*>self.py_buf.buf
Expand All @@ -574,12 +600,15 @@ cdef class HttpParser:

if errno is cparser.HPE_PAUSED_UPGRADE:
cparser.llhttp_resume_after_upgrade(self._cparser)

nb = cparser.llhttp_get_error_pos(self._cparser) - base
elif errno is cparser.HPE_PAUSED:
cparser.llhttp_resume(self._cparser)
pos = cparser.llhttp_get_error_pos(self._cparser) - base
self._tail = data[pos:]

PyBuffer_Release(&self.py_buf)

if errno not in (cparser.HPE_OK, cparser.HPE_PAUSED_UPGRADE):
if errno not in (cparser.HPE_OK, cparser.HPE_PAUSED, cparser.HPE_PAUSED_UPGRADE):
if self._payload_error == 0:
if self._last_error is not None:
ex = self._last_error
Expand All @@ -603,8 +632,9 @@ cdef class HttpParser:

if self._upgraded:
return messages, True, data[nb:]
else:
return messages, False, b""
if not messages: # Shortcut to reduce Python overhead
return EMPTY_FEED_DATA_RESULT
return messages, False, b""

def set_upgraded(self, val):
self._upgraded = val
Expand Down Expand Up @@ -799,19 +829,26 @@ cdef int cb_on_body(cparser.llhttp_t* parser,
const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data
cdef bytes body = at[:length]
try:
pyparser._payload.feed_data(body)
except BaseException as underlying_exc:
reraised_exc = underlying_exc
if pyparser._payload_exception is not None:
reraised_exc = pyparser._payload_exception(str(underlying_exc))

set_exception(pyparser._payload, reraised_exc, underlying_exc)

pyparser._payload_error = 1
return -1
else:
return 0
while body or pyparser._more_data_available:
try:
pyparser._more_data_available = pyparser._payload.feed_data(body)
except BaseException as underlying_exc:
reraised_exc = underlying_exc
if pyparser._payload_exception is not None:
reraised_exc = pyparser._payload_exception(str(underlying_exc))

set_exception(pyparser._payload, reraised_exc, underlying_exc)

pyparser._payload_error = 1
pyparser._paused = False
return -1
body = b""

if pyparser._paused:
pyparser._paused = False
return cparser.HPE_PAUSED
pyparser._paused = False
return 0


cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1:
Expand Down
33 changes: 27 additions & 6 deletions aiohttp/base_protocol.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import asyncio
from typing import cast
from typing import TYPE_CHECKING, Any, cast

from .client_exceptions import ClientConnectionResetError
from .helpers import set_exception
from .tcp_helpers import tcp_nodelay

if TYPE_CHECKING:
from .http_parser import HttpParser


class BaseProtocol(asyncio.Protocol):
__slots__ = (
"_loop",
"_paused",
"_parser",
"_drain_waiter",
"_connection_lost",
"_reading_paused",
"_upgraded",
"transport",
)

def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
def __init__(
self, loop: asyncio.AbstractEventLoop, parser: "HttpParser[Any] | None" = None
) -> None:
self._loop: asyncio.AbstractEventLoop = loop
self._paused = False
self._drain_waiter: asyncio.Future[None] | None = None
self._reading_paused = False
self._parser = parser
self._upgraded = False

self.transport: asyncio.Transport | None = None

Expand Down Expand Up @@ -48,15 +57,27 @@ def resume_writing(self) -> None:
waiter.set_result(None)

def pause_reading(self) -> None:
if not self._reading_paused and self.transport is not None:
self._reading_paused = True
# Parser shouldn't be paused on websockets.
if not self._upgraded:
assert self._parser is not None
self._parser.pause_reading()
if self.transport is not None:
try:
self.transport.pause_reading()
except (AttributeError, NotImplementedError, RuntimeError):
pass
self._reading_paused = True

def resume_reading(self) -> None:
if self._reading_paused and self.transport is not None:
def resume_reading(self, resume_parser: bool = True) -> None:
self._reading_paused = False

# This will resume parsing any unprocessed data from the last pause.
if not self._upgraded and resume_parser:
self.data_received(b"")

# Reading may have been paused again in the above call if there was a lot of
# compressed data still pending.
if not self._reading_paused and self.transport is not None:
try:
self.transport.resume_reading()
except (AttributeError, NotImplementedError, RuntimeError):
Expand Down
17 changes: 7 additions & 10 deletions aiohttp/client_proto.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ResponseHandler(BaseProtocol, DataQueue[tuple[RawResponseMessage, StreamRe
"""Helper class to adapt between Protocol and StreamReader."""

def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
BaseProtocol.__init__(self, loop=loop)
BaseProtocol.__init__(self, loop=loop, parser=None)
DataQueue.__init__(self, loop)

self._should_close = False
Expand All @@ -43,10 +43,7 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self._data_received_cb: Callable[[], None] | None = None

self._timer = None

self._tail = b""
self._upgraded = False
self._parser: HttpResponseParser | None = None

self._read_timeout: float | None = None
self._read_timeout_handle: asyncio.TimerHandle | None = None
Expand Down Expand Up @@ -197,8 +194,8 @@ def pause_reading(self) -> None:
super().pause_reading()
self._drop_timeout()

def resume_reading(self) -> None:
super().resume_reading()
def resume_reading(self, resume_parser: bool = True) -> None:
super().resume_reading(resume_parser)
self._reschedule_timeout()

def set_exception(
Expand Down Expand Up @@ -299,10 +296,10 @@ def _on_read_timeout(self) -> None:
set_exception(self._payload, exc)

def data_received(self, data: bytes) -> None:
self._reschedule_timeout()

if not data:
return
# If no data, then we are resuming decompression. We haven't received
# data from the socket, so we can avoid the reschedule overhead.
if data:
self._reschedule_timeout()

# custom payload parser - currently always WebSocketReader
if self._payload_parser is not None:
Expand Down
Loading
Loading