feat(python): headers for local_browser.connect
SY
17h ago+168+168+012345678901234567890123456789-22-22-01234567890123456789
Why
local_browser.connect takes only a URL. To reach a CDP endpoint behind an authenticated gateway, the credentials have to go in the query string. URLs leak easily: into exception messages, tracebacks and proxy logs. So callers end up redacting them by hand. Playwright solves this with connect_over_cdp(..., headers=...).
What
browser = await local_browser.connect(
cdp_url="https://gateway/.../cdp",
headers={"X-Api-Key": "..."},
)
headersare sent with the/json/versionrequest and with the CDP WebSocket handshake (websockets'additional_headers).- They follow the advertised
webSocketDebuggerUrlonly when it's on the same host and port ascdp_url, so a gateway can't redirect credentials elsewhere. They're never passed to the extension. - Names must be RFC 9110 tokens, and values may only contain visible characters, spaces and tabs, so CR/LF injection is rejected.
LocalBrowserConnectOptionssetshide_input_in_errorsso a rejected credential isn't echoed in theValidationError. - When
headersis unset, nothing changes:CDPClient.connectisn't even passed the argument. - Python SDK only, with a minor changeset. Independent of #3223; the two compose.
Tests
- New tests cover headers on
/json/version, resolve passing them through, the same-host rule (6 cases), connect forwarding them only when given, and rejection of unsafe names and values without echoing them. - The existing
test_cdp_clientfakes now accept the new keyword argument. ruff format --check,ruff check,ty checkandgenerate.py --checkpass.pytest: 798 passed.test_official_api_routes_through_and_never_closes_a_caller_http_clientfails with and without this change, because it needs the built extension inpackages/extension/dist.- Live: against a LangSmith browser sandbox's CDP gateway,
/json/versionwith anX-Api-Keyheader returned a credential-free WebSocket URL, the header-authenticated WebSocket reached Chrome (Browser.getVersion), and the same WebSocket without the header was refused.
🤖 Generated with Claude Code
Summary by cubic
Adds optional headers to local_browser.connect in the Python SDK so callers can authenticate against CDP gateways without putting credentials in the URL.
- Headers are sent with the
/json/versionrequest and the CDP WebSocket handshake, and follow the advertisedwebSocketDebuggerUrlonly when it's on the same host and port ascdp_url; they're never passed to another host or to the extension. - Header names and values are validated against RFC 9110 rules, and validation errors never echo header values since they may be credentials.
- When
headersis unset, nothing changes: no new keyword argument is passed toCDPClient.connect.