Skip to content

Commit 0eae096

Browse files
committed
Broaden receive data buffer types
1 parent 73a232d commit 0eae096

5 files changed

Lines changed: 56 additions & 9 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ testing = [
6565

6666
linting = [
6767
"ruff>=0.8.0,<1",
68-
"mypy>=1.13.0,<2",
68+
"mypy>=1.16.0,<2",
6969
"typing_extensions>=4.12.2",
7070
]
7171

@@ -190,7 +190,7 @@ commands = [
190190
dependency_groups = ["linting"]
191191
commands = [
192192
["ruff", "check", "src/"],
193-
["mypy", "src/"],
193+
["mypy", "--strict-bytes", "src/", "tests/typing/strict_bytes.py"],
194194
]
195195

196196
[tool.tox.env.docs]

src/h2/_typing.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
h2/_typing
3+
~~~~~~~~~~
4+
5+
Shared typing helpers.
6+
"""
7+
from __future__ import annotations
8+
9+
from typing import Protocol
10+
11+
12+
class Buffer(Protocol):
13+
"""
14+
An object implementing the PEP 688 buffer protocol.
15+
"""
16+
17+
def __buffer__(self, flags: int, /) -> memoryview:
18+
"""
19+
Return a memoryview over this object's bytes.
20+
"""
21+
...

src/h2/connection.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070

7171
from hpack.struct import Header, HeaderWeaklyTyped
7272

73+
from ._typing import Buffer
74+
7375

7476
class ConnectionState(Enum):
7577
IDLE = 0
@@ -1496,12 +1498,12 @@ def _inbound_flow_control_change_from_settings(self, old_value: int | None, new_
14961498
for stream in self.streams.values():
14971499
stream._inbound_flow_control_change_from_settings(delta)
14981500

1499-
def receive_data(self, data: bytes) -> list[Event]:
1501+
def receive_data(self, data: Buffer) -> list[Event]:
15001502
"""
15011503
Pass some received HTTP/2 data to the connection for handling.
15021504
15031505
:param data: The data received from the remote peer on the network.
1504-
:type data: ``bytes``
1506+
:type data: An object implementing the buffer protocol.
15051507
:returns: A list of events that the remote peer triggered by sending
15061508
this data.
15071509
"""

src/h2/frame_buffer.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
"""
88
from __future__ import annotations
99

10+
from typing import TYPE_CHECKING
11+
1012
from hyperframe.exceptions import InvalidDataError, InvalidFrameError
1113
from hyperframe.frame import ContinuationFrame, Frame, HeadersFrame, PushPromiseFrame
1214

1315
from .exceptions import FrameDataMissingError, FrameTooLargeError, ProtocolError
1416

17+
if TYPE_CHECKING: # pragma: no cover
18+
from ._typing import Buffer
19+
1520
# To avoid a DOS attack based on sending loads of continuation frames, we limit
1621
# the maximum number we're prepared to receive. In this case, we'll set the
1722
# limit to 64, which means the largest encoded header block we can receive by
@@ -36,25 +41,27 @@ def __init__(self, server: bool = False) -> None:
3641
self._preamble_len = len(self._preamble)
3742
self._headers_buffer: list[HeadersFrame | ContinuationFrame | PushPromiseFrame] = []
3843

39-
def add_data(self, data: bytes) -> None:
44+
def add_data(self, data: Buffer) -> None:
4045
"""
4146
Add more data to the frame buffer.
4247
4348
:param data: A bytestring containing the byte buffer.
4449
"""
50+
data_view = memoryview(data)
51+
4552
if self._preamble_len:
46-
data_len = len(data)
53+
data_len = len(data_view)
4754
of_which_preamble = min(self._preamble_len, data_len)
4855

49-
if self._preamble[:of_which_preamble] != data[:of_which_preamble]:
56+
if self._preamble[:of_which_preamble] != data_view[:of_which_preamble]:
5057
msg = "Invalid HTTP/2 preamble."
5158
raise ProtocolError(msg)
5259

53-
data = data[of_which_preamble:]
60+
data_view = data_view[of_which_preamble:]
5461
self._preamble_len -= of_which_preamble
5562
self._preamble = self._preamble[of_which_preamble:]
5663

57-
self._data += data
64+
self._data += data_view
5865

5966
def _validate_frame_length(self, length: int) -> None:
6067
"""

tests/typing/strict_bytes.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from __future__ import annotations
2+
3+
from h2.connection import H2Connection
4+
from h2.frame_buffer import FrameBuffer
5+
6+
7+
def receive_data_accepts_buffer_types(
8+
connection: H2Connection,
9+
frame_buffer: FrameBuffer,
10+
) -> None:
11+
bytearray_data = bytearray(b"")
12+
memoryview_data = memoryview(b"")
13+
14+
connection.receive_data(bytearray_data)
15+
connection.receive_data(memoryview_data)
16+
frame_buffer.add_data(bytearray_data)
17+
frame_buffer.add_data(memoryview_data)

0 commit comments

Comments
 (0)