Skip to content

feat(examples): Introduce Chromium CDP - #151

Open
razvand wants to merge 4 commits into
unikraft-cloud:mainfrom
razvand:razvand/new/chromium-cdp
Open

feat(examples): Introduce Chromium CDP#151
razvand wants to merge 4 commits into
unikraft-cloud:mainfrom
razvand:razvand/new/chromium-cdp

Conversation

@razvand

@razvand razvand commented Feb 2, 2025

Copy link
Copy Markdown
Contributor

Introduce Chromium running as a browser service using CDP (Chrome DevTools Protocol). It uses Node Playwright to start the browser and the Node http-proxy module to proxy websocket communication.

Add:

  • Kraftfile: build / run rules
  • Dockerfile: placeholder to extract the filesystem
  • package.json / package-lock.json: Node package requirements
  • proxy.js: Node-based websocket proxy
  • wrapper.sh: helper script to start the Node proxy service (and the browser)
  • README.md: document how to use
  • .dockerignore / .gitignore: ignore generated files
  • test/: CDP test client (in Python using Playwright)

@razvand
razvand force-pushed the razvand/new/chromium-cdp branch from e1b1e5b to 7ba0200 Compare February 6, 2025 09:49
@razvand
razvand marked this pull request as ready for review February 6, 2025 09:53
@razvand razvand added the enhancement New feature or request label Feb 6, 2025
Introduce Chromium running as a browser service using CDP (Chrome
DevTools Protocol). It uses Node Playwright to start the browser and
the Node `http-proxy` module to proxy websocket communication.

Add:

* `Kraftfile`: build / run rules
* `Dockerfile`: placeholder to extract the filesystem
* `package.json` / `package-lock.json`: Node package requirements
* `server.js`: Node-based websocket proxy
* `wrapper.sh`: helper script to start the Node proxy service (and the
  browser)
* `README.md`: document how to use
* `.dockerignore` / `.gitignore`: ignore generated files
* `test/`: CDP test client (in Python using Playwright)

Signed-off-by: Razvan Deaconescu <razvand@unikraft.io>
@razvand
razvand force-pushed the razvand/new/chromium-cdp branch from 7ba0200 to 5792db8 Compare February 7, 2025 06:39
Introduce an echo-reply WebSocket server to be deployed on Unikraft
Cloud with Node. It uses the `node:21` image.

Add:

* `Kraftfile`: build / run rules
* `Dockerfile`: placeholder to extract the filesystem
* `package.json` / `package-lock.json`: NPM configuration file
* `server.js`: implementation of Node WebSocket server
* `README.md`: document how to use
* `.dockerignore` / `.gitignore`: ignore generated files
* workflow files in `../.github/workflows/`

Update top-level `README.md` to feature the example.

Signed-off-by: Razvan Deaconescu <razvand@unikraft.io>
Install `wscat` globally using `npm install -g wscat`, as instructed:
https://www.npmjs.com/package/wscat

Signed-off-by: Razvan Deaconescu <razvand@unikraft.io>
Use `websocat` instead of `wscat` as the WebSocket client, as it can be
used non-interactively.

Signed-off-by: Razvan Deaconescu <razvand@unikraft.io>
@craciunoiuc

Copy link
Copy Markdown
Contributor

@razvand can you rebase this just in case so I can get to reviewing?

I can see that the tests failed but those were because of the missing token and/or metro on the fork. If that is the case, we can merge and see afterwards if tests pass

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new chromium-cdp example that runs Chromium as a remote browser service on Unikraft Cloud, exposing a CDP endpoint via a Node-based proxy (Playwright + http-proxy). It also introduces a separate node21-websocket echo server example and wires it into the top-level examples list and CI workflows.

Changes:

  • Add chromium-cdp/ example: container rootfs build, Node proxy server, wrapper script, and Python Playwright CDP client.
  • Add node21-websocket/ example: minimal ws echo server with Kraft/Docker packaging and docs.
  • Add GitHub Actions workflows for node21-websocket stable/staging integration tests and list the example in the root README.

Reviewed changes

Copilot reviewed 20 out of 23 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
README.md Adds node21-websocket to the examples table (and its CI badges).
node21-websocket/server.js Implements a simple WebSocket echo server.
node21-websocket/README.md Deployment + usage instructions for the WebSocket example.
node21-websocket/package.json Declares ws dependency and ESM mode.
node21-websocket/package-lock.json Locks ws dependency version.
node21-websocket/Kraftfile Unikraft runtime/config for the WebSocket example.
node21-websocket/Dockerfile Builds a minimal rootfs containing Node deps and server script.
chromium-cdp/README.md Documents deploying the Chromium CDP browser service.
chromium-cdp/proxy.js Starts Chromium and proxies HTTP/WS traffic, rewriting CDP WS URLs.
chromium-cdp/wrapper.sh Wrapper to set HOME, cd into /app, and exec the server process.
chromium-cdp/package.json Node dependencies for Playwright + proxying/JSON rewrite.
chromium-cdp/package-lock.json Locks Node dependencies for chromium-cdp.
chromium-cdp/Kraftfile Unikraft runtime/config for the Chromium CDP service.
chromium-cdp/Dockerfile Builds a scratch rootfs containing Node, Playwright Chromium, and required libs.
chromium-cdp/.gitignore Ignores node_modules/.unikraft artifacts and generated PNGs.
chromium-cdp/.dockerignore Avoids copying node_modules/.unikraft artifacts and generated PNGs into builds.
chromium-cdp/test/README.md Instructions for setting up/running the Python CDP client.
chromium-cdp/test/pyproject.toml Poetry project config for the Python test client.
chromium-cdp/test/poetry.lock Locked Python dependencies for the CDP client.
chromium-cdp/test/cdp-screenshot.py Python Playwright client that connects over CDP and takes a screenshot.
chromium-cdp/test/.gitignore Ignores venv and generated screenshots.
.github/workflows/example-node21-websocket-stable.yaml Stable integration workflow for node21-websocket.
.github/workflows/example-node21-websocket-staging.yaml Staging integration workflow for node21-websocket.
Files not reviewed (2)
  • chromium-cdp/package-lock.json: Generated file
  • node21-websocket/package-lock.json: Generated file
Suppressed comments (1)

.github/workflows/example-node21-websocket-staging.yaml:107

  • In the debug re-test, the workflow deploys node21-websocket-staging-${GITHUB_RUN_ID}-dbg but connects to the stable subdomain. This should connect to the -dbg staging instance that was just started.
          echo "hello" | ./websocat.x86_64-unknown-linux-musl wss://node21-websocket-stable-${GITHUB_RUN_ID}.${UKC_METRO}.kraft.host | grep "hello" > /dev/null

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

kraft cloud vm start -w 60s node21-websocket-staging-${GITHUB_RUN_ID};
sleep 5;

echo "hello" | ./websocat.x86_64-unknown-linux-musl wss://node21-websocket-stable-${GITHUB_RUN_ID}.${UKC_METRO}.kraft.host | grep "hello" > /dev/null
Comment thread chromium-cdp/wrapper.sh

export HOME=/root
cd /app
exec $@
Comment thread chromium-cdp/proxy.js
Comment on lines +10 to +14
//
// Launch Chromium browser with CDP enabled.
//
chromium.launch({headless: true, args: [`--remote-debugging-port=${cdp_port}`]})

Comment thread chromium-cdp/proxy.js
Comment on lines +33 to +48
proxy.on('proxyRes', function (proxyRes, req, res) {
if (res.req.url.startsWith("/json")) {
const isHost = (element) => element == 'Host';
host = res.req.rawHeaders[res.req.rawHeaders.findIndex(isHost)+1];

modifyResponse(res, proxyRes, function (body) {
if (body) {
body.webSocketDebuggerUrl = body.webSocketDebuggerUrl.replace(`${cdp_host}:${cdp_port}`, host);
body.webSocketDebuggerUrl = body.webSocketDebuggerUrl.replace("ws://", "wss://");
}
return body; // return value can be a promise
});

res.setHeader('Host', host);
}
});
Comment on lines +44 to +48
# Take a single screenshot of the entire page
screenshot = new_page.screenshot()

with open("screenshot.png", "wb") as stream:
stream.write(screenshot)
@@ -0,0 +1,18 @@
# CDP Screenshot Test / Client

This is Python client using [CDP (Chrome DevTools Protocol)](https://chromedevtools.github.io/devtools-protocol/) to create a screenshot from an existing Chromium instances.
Comment thread README.md
[`node18-nextjs`](https://github.com/unikraft-cloud/examples/tree/main/node21-nextjs) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node18-nextjs-stable.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node18-nextjs-stable.yaml) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node18-nextjs-staging.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node18-nextjs-staging.yaml) |
[`node21-solidstart`](https://github.com/unikraft-cloud/examples/tree/main/node21-solid-start) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-solidstart-stable.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-solidstart-stable.yaml) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-solidstart-staging.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-solidstart-staging.yaml) |
[`node21-remix`](https://github.com/unikraft-cloud/examples/tree/main/node21-remix) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-remix-stable.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-remix-stable.yaml) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-remix-staging.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-remix-staging.yaml) |
[`node21-websocket`](https://github.com/unikraft-cloud/examples/tree/main/node21-websocket) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-websocket-stable.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-websocket-stable.yaml) | [![](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-websocket-staging.yaml/badge.svg)](https://github.com/unikraft-cloud/examples/actions/workflows/example-node21-websocket-staging.yaml) |
Comment on lines +4 to +6
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} wss://...", file=sys.stderr)
sys.exit(1)

## Learn more

- [WebSocket documentation](https://nextjs.org/docs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants