8000 Implement zerocopy writes for the encrypted protocol · Jc2k/aiohomekit@eca8ae5 · GitHub
[go: up one dir, main page]

Skip to content

Commit eca8ae5

Browse files
committed
Implement zerocopy writes for the encrypted protocol
With Python 3.12+ and later `transport.writelines` is implemented as [`sendmsg(..., IOV_MAX)`](python/cpython#91166) which allows us to avoid joining the bytes and sending them in one go. Older Python will effectively do the same thing we do now `b"".join(...)`
1 parent 6a69925 commit eca8ae5

File tree

1 file changed

+16
-15
lines changed

1 file changed

+16
-15
lines changed

aiohomekit/controller/ip/connection.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
# limitations under the License.
1515
#
1616
from __future__ import annotations
17-
17+
from struct import Struct
1818
import asyncio
1919
import logging
2020
import socket
21-
from typing import TYPE_CHECKING, Any
21+
from typing import TYPE_CHECKING, Any, Iterable
2222

2323
import aiohappyeyeballs
2424
from async_interrupt import interrupt
@@ -45,6 +45,9 @@
4545
from aiohomekit.protocol.tlv import TLV
4646
from aiohomekit.utils import async_create_task, asyncio_timeout
4747

48+
PACK_UNSIGNED_SHORT = Struct(">H").pack
49+
50+
4851
if TYPE_CHECKING:
4952
from .pairing import IpPairing
5053

@@ -80,11 +83,11 @@ def __init__(self, connection: HomeKitConnection) -> None:
8083
self.current_response = HttpResponse()
8184
self.loop = asyncio.get_running_loop()
8285

83-
def connection_made(self, transport):
86+
def connection_made(self, transport: asyncio.Transport) -> None:
8487
super().connection_made(transport)
8588
self.transport = transport
8689

87-
def connection_lost(self, exception):
90+
def connection_lost(self, exception: Exception) -> None:
8891
self.connection._connection_lost(exception)
8992
self._cancel_pending_requests()
9093

@@ -94,10 +97,14 @@ def _handle_timeout(self, fut: asyncio.Future[Any]) -> None:
9497
fut.set_exception(asyncio.TimeoutError)
9598

9699
async def send_bytes(self, payload: bytes) -> HttpResponse:
100+
"""Send bytes to the device."""
101+
return await self.send_lines((payload,))
102+
103+
async def send_lines(self, payload: Iterable[bytes]) -> HttpResponse:
97104
"""Send bytes to the device."""
98105
if self.transport.is_closing():
99106
# FIXME: It would be nice to try and wait for the reconnect in future.
100-
# In that case we need to make sure we do it at a layer above send_bytes otherwise
107+
# In that case we need to make sure we do it at a layer above send_lines otherwise
101108
# we might encrypt payloads with the last sessions keys t F921 hen wait for a new connection
102109
# to send them - and on that connection the keys would be different.
103110
# Also need to make sure that the new connection has chance to pair-verify before
@@ -113,7 +120,7 @@ async def send_bytes(self, payload: bytes) -> HttpResponse:
113120
timeout_handle = loop.call_at(loop.time() + 30, self._handle_timeout, result)
114121
timeout_expired = False
115122
try:
116-
self.transport.write(payload)
123+
self.transport.writelines(payload)
117124
return await result
118125
except (asyncio.TimeoutError, BaseException) as ex:
119126
# If we get a timeout or any other exception then we need to
@@ -188,18 +195,12 @@ async def send_bytes(self, payload: bytes) -> HttpResponse:
188195
while len(payload) > 0:
189196
current = payload[:1024]
190197
payload = payload[1024:]
191-
len_bytes = len(current).to_bytes(2, byteorder="little")
198+
len_bytes = PACK_UNSIGNED_SHORT(len(current))
192199
buffer.append(len_bytes)
193-
buffer.append(
194-
self.encryptor.encrypt(
195-
len_bytes,
196-
PACK_NONCE(self.c2a_counter),
197-
bytes(current),
198-
)
199-
)
200+
buffer.append(self.encryptor.encrypt(len_bytes, PACK_NONCE(self.c2a_counter), current))
200201
self.c2a_counter += 1
201202

202-
return await super().send_bytes(b"".join(buffer))
203+
return await self.send_lines(buffer)
203204

204205
def data_received(self, data: bytes) -> None:
205206
"""

0 commit comments

Comments
 (0)
0