076914e5b7
- Split client, native, remote, serve, markdown, and SDK internals into focused packages with direct imports. - Move local and remote transport framing/protocol helpers behind clearer module boundaries. - Break up the extension injected DOM logic into a separate content dispatch bundle and dedicated content modules. - Add explicit client handling for passive remote discovery without noisy PQ warnings. - Keep behavior covered with updated unit, integration, and extension tests.
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
"""Local IPC transport for browser-cli client commands."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import socket
|
|
from collections.abc import Callable
|
|
from multiprocessing.connection import Client as PipeClient
|
|
|
|
from browser_cli.framing import async_recv_exact as framing_async_recv_exact
|
|
from browser_cli.framing import async_recv_frame, async_send_frame, frame
|
|
from browser_cli.platform import is_windows
|
|
from browser_cli.remote.transport import _recv_all
|
|
|
|
async def async_recv_exact(reader: asyncio.StreamReader, n: int) -> bytes | None:
|
|
try:
|
|
return await framing_async_recv_exact(reader, n, allow_eof=True)
|
|
except ConnectionError:
|
|
return None
|
|
|
|
async def async_recv_all(reader: asyncio.StreamReader) -> bytes | None:
|
|
try:
|
|
return await async_recv_frame(reader, allow_eof=True)
|
|
except ConnectionError:
|
|
return None
|
|
|
|
async def async_send_all(writer: asyncio.StreamWriter, data: bytes) -> None:
|
|
await async_send_frame(writer, data)
|
|
|
|
def send_local_sync(profile: str | None, payload: bytes, resolve_socket: Callable[[str | None], str]) -> bytes | None:
|
|
sock_path = resolve_socket(profile)
|
|
if is_windows():
|
|
with PipeClient(sock_path, family="AF_PIPE") as conn:
|
|
conn.send_bytes(payload)
|
|
return conn.recv_bytes()
|
|
|
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
|
sock.connect(sock_path)
|
|
sock.sendall(frame(payload))
|
|
return _recv_all(sock)
|
|
|
|
async def send_local_async(profile: str | None, payload: bytes, resolve_socket: Callable[[str | None], str]) -> bytes | None:
|
|
sock_path = await asyncio.to_thread(resolve_socket, profile)
|
|
if is_windows():
|
|
return await _send_windows_pipe_async(sock_path, payload)
|
|
return await _send_local_unix_async(sock_path, payload)
|
|
|
|
async def _send_local_unix_async(sock_path: str, payload: bytes) -> bytes | None:
|
|
reader, writer = await asyncio.open_unix_connection(sock_path)
|
|
try:
|
|
await async_send_all(writer, payload)
|
|
return await async_recv_all(reader)
|
|
finally:
|
|
writer.close()
|
|
try:
|
|
await writer.wait_closed()
|
|
except Exception:
|
|
pass
|
|
|
|
async def _send_windows_pipe_async(sock_path: str, payload: bytes) -> bytes:
|
|
def _roundtrip():
|
|
with PipeClient(sock_path, family="AF_PIPE") as conn:
|
|
conn.send_bytes(payload)
|
|
return conn.recv_bytes()
|
|
return await asyncio.to_thread(_roundtrip)
|