Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 7 additions & 3 deletions src/browser_harness/_ipc.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,19 @@ def connect(name, timeout=1.0):

def request(c, token, req):
"""One-shot send + recv + parse on an open socket. Injects token on Windows.
Returns the parsed JSON response. Caller closes the socket."""
Returns the parsed JSON response. Caller closes the socket.
Raises ConnectionError if the peer closes before sending any response."""
if token: req = {**req, "token": token}
c.sendall((json.dumps(req) + "\n").encode())
data = b""
while not data.endswith(b"\n"):
chunk = c.recv(1 << 16)
if not chunk: break
if not chunk:
if not data:
raise ConnectionError("IPC peer closed without a response")
break
data += chunk
return json.loads(data or b"{}")
return json.loads(data)


def ping(name, timeout=1.0):
Expand Down
2 changes: 2 additions & 0 deletions src/browser_harness/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ def _daemon_browser_connection(name):
try:
c, token = ipc.connect(name, timeout=1.0)
response = ipc.request(c, token, {"meta": "connection_status"})
if not isinstance(response, dict):
return None
if "error" in response:
return None
page = response.get("page")
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,26 @@ def test_browser_connections_returns_attached_page(monkeypatch):
]


def test_browser_connections_skips_daemon_that_closes_without_response(monkeypatch):
monkeypatch.setattr(admin, "_daemon_endpoint_names", lambda: ["default", "stale"])

def fake_connect(name, timeout=1.0):
if name == "stale":
return FakeSocket(b""), None
return FakeSocket(), None

monkeypatch.setattr(admin.ipc, "connect", fake_connect)

assert admin.browser_connections() == [{"name": "default", "page": None}]


def test_browser_connections_skips_non_dict_connection_status_response(monkeypatch):
monkeypatch.setattr(admin, "_daemon_endpoint_names", lambda: ["stale"])
monkeypatch.setattr(admin.ipc, "connect", lambda name, timeout=1.0: (FakeSocket(b"[]\n"), None))

assert admin.browser_connections() == []


def test_chrome_running_detects_helium_on_linux(monkeypatch):
monkeypatch.setattr("platform.system", lambda: "Linux")
monkeypatch.setattr(
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/test_ipc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
import pytest

from browser_harness import _ipc as ipc


# --- request(): EOF handling ---

class _EOFConn:
def __init__(self):
self.sent = b""

def sendall(self, data):
self.sent += data

def recv(self, _size):
return b""


def test_request_raises_connection_error_when_peer_closes_without_response():
conn = _EOFConn()

with pytest.raises(ConnectionError, match="closed without a response"):
ipc.request(conn, None, {"meta": "ping"})

assert conn.sent


# --- identify(): ping payload sanitation ---

class _FakeConn:
Expand Down