Skip to content

[Web runtime] Add SSE subscribe + CORS to HttpTransport#1537

Open
minggangw wants to merge 4 commits into
RobotWebTools:developfrom
minggangw:fix-1510-sse
Open

[Web runtime] Add SSE subscribe + CORS to HttpTransport#1537
minggangw wants to merge 4 commits into
RobotWebTools:developfrom
minggangw:fix-1510-sse

Conversation

@minggangw

Copy link
Copy Markdown
Member

Browsers and HTTP-only clients can now subscribe over HTTP via Server-Sent Events and reach the transport cross-origin via CORS. Both are off by default and configurable in code, the demos, and the CLI.

  • transports/http.js: add HttpSseConnection and a GET /capability/subscribe/<name> route (opt-in sse, sseKeepAliveMs); add cors option with OPTIONS preflight. Headers are deferred until the dispatcher acks, so rejected subscribes still return a normal JSON error.
  • cli-config.js + bin/rclnodejs-web.js: new --http-sse, --http-sse-keep-alive <ms>, repeatable --http-cors <origin> flags and matching http.sse/sseKeepAliveMs/cors web.json keys; banner reflects SSE.
  • demo/web/javascript: enable sse+cors, expose /topic to pair with the publisher example, add a native EventSource panel, document it.
  • demo/web/typescript: note HTTP subscribe-over-SSE is opt-in.
  • test/test-web-cli.js: 11 new tests (35 passing).

Fix: #1510

Copilot AI review requested due to automatic review settings June 12, 2026 07:03

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

Adds opt-in HTTP subscribe support for the Web Runtime via Server-Sent Events (SSE) and introduces configurable CORS handling so browser/HTTP-only clients can access the HTTP transport cross-origin.

Changes:

  • Add HttpSseConnection and a GET /capability/subscribe/<name> SSE route to HttpTransport (opt-in via sse / sseKeepAliveMs), plus CORS + OPTIONS preflight support.
  • Extend the CLI/config system with --http-sse, --http-sse-keep-alive <ms>, and repeatable --http-cors <origin> flags (and matching web.json keys).
  • Update the web demos/docs to showcase SSE + native browser EventSource, and add CLI parsing/validation tests.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/test-web-cli.js Adds unit tests for new CLI flags and config validation/merge behavior (SSE + CORS).
lib/runtime/transports/http.js Implements SSE subscribe connection/route and adds CORS handling + OPTIONS preflight to the HTTP transport.
lib/runtime/cli-config.js Adds defaults, argv parsing, config validation, merge rules, and help text for http.sse, http.sseKeepAliveMs, http.cors.
demo/web/typescript/README.md Documents that HTTP subscribe-over-SSE is opt-in and points to the JS demo for an SSE example.
demo/web/javascript/runtime.mjs Enables SSE + CORS for the JS demo runtime and exposes /topic for pairing with the publisher example.
demo/web/javascript/README.md Documents SSE subscribe usage (curl + browser EventSource) and the required CORS setting.
demo/web/javascript/index.html Adds a native EventSource panel demonstrating SSE subscribe from the browser.
bin/rclnodejs-web.js Wires new config fields into HttpTransport construction and updates the startup banner wording.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +721 to +736
function _normaliseCors(value) {
if (value === undefined || value === null || value === false) return false;
if (value === true) return true;
if (typeof value === 'string') return new Set([value]);
if (Array.isArray(value)) {
if (!value.every((v) => typeof v === 'string')) {
throw new TypeError(
'HttpTransport: cors array must contain only origin strings'
);
}
return new Set(value);
}
throw new TypeError(
`HttpTransport: cors must be a boolean, string, or string[], got ${typeof value}`
);
}
Comment on lines +469 to +474
// Preflight: browsers send OPTIONS before a cross-origin POST with a
// JSON content-type. Answer it directly (no auth, no body).
if (req.method === 'OPTIONS' && this.cors) {
res.writeHead(204).end();
return;
}
Comment on lines +389 to +402
* @param {boolean} [options.sse=false]
* Enable `GET /capability/subscribe/<name>` Server-Sent Events
* streaming. Off by default; `subscribe` returns `unsupported_kind`
* unless this is set.
* @param {number} [options.sseKeepAliveMs=15000]
* Heartbeat comment interval for SSE streams, in milliseconds. Set to
* `0` to disable heartbeats.
* @param {boolean|string|string[]} [options.cors=false]
* Cross-Origin Resource Sharing policy. `false` (default) sends no
* CORS headers. `true` allows any origin (`Access-Control-Allow-Origin:
* *`). A string or array of strings allows only those origins (the
* request's `Origin` is echoed back when it matches). Required for a
* browser on a different origin to `fetch()` / `EventSource()` this
* transport.
Comment on lines +539 to +556
if (kind === 'subscribe') {
if (!this.sse) {
return _writeJson(res, 404, {
ok: false,
error:
'subscribe over HTTP is disabled (enable `sse` or use WebSocket)',
code: 'unsupported_kind',
});
}
if (req.method !== 'GET') {
res.setHeader('allow', 'GET');
return _writeJson(res, 405, {
ok: false,
error: `method not allowed: ${req.method} (use GET for SSE subscribe)`,
code: 'method_not_allowed',
});
}
const conn = new HttpSseConnection(req, res, name, {
@coveralls

coveralls commented Jun 12, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 90.465% (-0.8%) from 91.22% — minggangw:fix-1510-sse into RobotWebTools:develop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Browser ↔ ROS 2 capability runtime (Web Runtime)

3 participants