6fa931aa36
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s
- Gate TCP serve commands with safe-by-default policies, per-key allow tokens, per-key rate limiting, and audit labels. - Reuse authenticated encrypted remote sessions and parallelize/caches multi-browser fanout to reduce repeated handshake roundtrips. - Increase paged native-host batch size with extension-side byte budgeting to speed large tab listings safely. - Point install output at public Chrome Web Store / Firefox AMO listings by default, with --dev preserving unpacked workflows. - Share search-engine metadata between CLI and SDK and bump the package/extension version to 0.16.0. - Cover the new security, pooling, paging, install, and fanout behavior with expanded Python and extension tests.
58 lines
1.9 KiB
Python
58 lines
1.9 KiB
Python
"""Socket helpers for remote TCP/TLS transport."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import socket
|
|
from contextlib import contextmanager
|
|
|
|
from browser_cli.endpoints import _resolve_connect_endpoint
|
|
from browser_cli.framing import async_recv_exact, async_recv_frame, recv_exact, recv_frame
|
|
|
|
def recv_exact_bytes(sock: socket.socket, n: int) -> bytes:
|
|
return recv_exact(sock, n) or b""
|
|
|
|
def recv_all(sock: socket.socket) -> bytes:
|
|
return recv_frame(sock, label="Response") or b""
|
|
|
|
async def async_recv_exact_bytes(reader: asyncio.StreamReader, n: int) -> bytes:
|
|
return await async_recv_exact(reader, n) or b""
|
|
|
|
async def async_recv_all(reader: asyncio.StreamReader) -> bytes:
|
|
return await async_recv_frame(reader, label="Response") or b""
|
|
|
|
def split_endpoint(endpoint: str) -> tuple[str, int]:
|
|
connect_ep = _resolve_connect_endpoint(endpoint)
|
|
host, _, port_str = connect_ep.rpartition(":")
|
|
return host, int(port_str)
|
|
|
|
def connect_socket(endpoint: str) -> socket.socket:
|
|
"""Open and (on :443) TLS-wrap a socket. Caller owns closing it."""
|
|
host, port = split_endpoint(endpoint)
|
|
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
raw_sock.settimeout(30)
|
|
try:
|
|
raw_sock.connect((host, port))
|
|
if port == 443:
|
|
import ssl
|
|
sock = ssl.create_default_context().wrap_socket(raw_sock, server_hostname=host)
|
|
else:
|
|
sock = raw_sock
|
|
except Exception:
|
|
raw_sock.close()
|
|
raise
|
|
return sock
|
|
|
|
@contextmanager
|
|
def open_socket(endpoint: str):
|
|
sock = connect_socket(endpoint)
|
|
with sock:
|
|
yield sock
|
|
|
|
async def open_async_connection(endpoint: str) -> tuple[asyncio.StreamReader, asyncio.StreamWriter]:
|
|
host, port = split_endpoint(endpoint)
|
|
ssl_ctx = None
|
|
if port == 443:
|
|
import ssl
|
|
ssl_ctx = ssl.create_default_context()
|
|
return await asyncio.open_connection(host, port, ssl=ssl_ctx, server_hostname=host if ssl_ctx else None)
|