Repository navigation
feat: add ConnectRemote to drive a remote Playwright server without a local driver - #644
Open
minhhuynh-tinyfish wants to merge 1 commit into
Open
minhhuynh-tinyfish wants to merge 1 commit into
minhhuynh-tinyfish wants to merge 1 commit into
Conversation
… local driver ConnectRemote speaks the Playwright protocol directly to a BrowserType.launchServer endpoint over WebSocket (one JSON text frame per message), so no Node.js driver is downloaded or started on the client. BrowserType.Connect still needs one, because it opens the WebSocket through LocalUtils.connect on the local driver. The handshake always sends a Playwright/<driver version> User-Agent and does not let callers override it: the server only enforces its major.minor compatibility check for a User-Agent it can parse. A 428 refusal is returned as ErrVersionMismatch naming both versions. Without a local driver there is no LocalUtils, so tracing skips client-side stack collection instead of dereferencing nil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds
playwright.ConnectRemote(ctx, wsEndpoint, opts). It connects to aBrowserType.launchServer()endpoint directly over WebSocket, so the client machine doesn't need a Node.js driver.Why
BrowserType.Connectstill needsplaywright.Run(). The local driver opens the WebSocket (LocalUtils.connect), and every message goes through it as aJsonPipe. If you run many Go clients against a remote browser fleet, that means one Node.js process per client. It adds no capability, but it costs memory, CPU and image size, and the driver has to be installed on every client host.How
The Go client already speaks the raw protocol to the driver's dispatcher. A
launchServerendpoint is the same dispatcher reached over a WebSocket, so the only wire difference is framing: each message is one JSON text frame instead of a length-prefixed stdio message.ws_transport.go: atransportimplementation ongithub.com/coder/websocket, which is already ingo.mod(the tests use it). Compression is off. The read limit is raised to 256 MiB to match the server's own limit, since file payloads travel base64-encoded inside a single message.connect_remote.go: runsconnection.Start()over that transport and returns thepreLaunchedBrowser, wired up the same wayBrowserType.Connectdoes. The connection is marked remote, so uploads send file contents (payloads/streams) and artifacts download throughsaveAsStream. Neither touches the server's filesystem.Version compatibility
The server compares the major.minor version in a
Playwright/x.y.zUser-Agent with its own, and refuses a mismatch with HTTP 428. If it can't parse the User-Agent, it skips the check entirely. SoConnectRemotehandles the header like this:Playwright/<playwrightCliVersion> (<arch>; <os>) go/<version>, so the header follows each roll automatically.User-Agentsupplied throughHeaders. Otherwise a caller could silently turn the check off and hit confusing protocol errors later.ErrVersionMismatch, naming both versions and the release to use. For example:the server at ws://… runs Playwright 1.63 but this client speaks Playwright 1.62; use the playwright-go release built for Playwright 1.63 (tag v0.63xx.x).Running without LocalUtils
A remote server doesn't provide
LocalUtils(utilsis omitted whenisServer).tracing.goandconnection.gonow skip client-side stack collection when it is missing instead of dereferencing nil. In the existing local andBrowserType.ConnectmodeslocalUtilsis always set, so nothing changes there.Known limitations, also listed in the godoc:
Devicesis empty.RouteFromHARand recording HAR to a non-zip file are unsupported. They currently hit a nilLocalUtils; I'd follow up with a clear error.Testing
connect_remote_test.go. They check that the User-Agent passes the server's own regex and major.minor comparison, that caller headers are forwarded butUser-Agentcan't be overridden, and that a 428 maps toErrVersionMismatch. The 428 body was captured from a real server. They also check that other handshake failures stay distinct.firefox.launchServer()from playwright-core 1.62.1, reached through a pass-through WebSocket relay:Goto,Evaluate, screenshotSetInputFilesfrom a path and from a buffer, a download plusSaveAsRouteplusFulfill, tracing start and stop to a valid zipBrowser.Close(), after which further calls report target closedPW_VERSION_OVERRIDE: 1.63.0 and 1.61.5 were refused withErrVersionMismatch, and 1.62.0 connected.-raceis clean,golangci-lintreports 0 issues, and the existing root-package tests pass.Not included yet: an integration test under
tests/that starts alaunchServerand runsConnectRemotein CI (remote_server_test.goalready starts such a server). I'm happy to add it to this PR, or to change the API shape if you'd prefer something else.🤖 Generated with Claude Code