Skip to content

Commit 6b79d5a

Browse files
authored
Merge pull request #6 from muke1908/copilot/change-origin-based-namespaces
feat: use full URL (scheme + host + port) for namespace derivation
2 parents cf5113a + a110814 commit 6b79d5a

2 files changed

Lines changed: 20 additions & 7 deletions

File tree

packages/streamer/server/index.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,29 @@ const namespaceViewers = new Map();
2929
const activeLoggerCounts = new Map();
3030

3131
// Pattern used to identify viewer connections by URL path.
32-
// Matches namespace slugs that are either UUIDs or origin hostnames (e.g. www.domain1.com).
32+
// Matches namespace slugs that are either UUIDs or url-based slugs (e.g. https-www.domain1.com-3000).
3333
const NAMESPACE_PATH_RE = /^\/([a-zA-Z0-9](?:[a-zA-Z0-9._-]*[a-zA-Z0-9])?)\/?$/;
3434

35-
// Valid hostname characters (no colons so IPv6 addresses fall back to UUID).
35+
// Valid namespace characters (no colons so IPv6 addresses fall back to UUID).
3636
const VALID_NAMESPACE_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9._-]*[a-zA-Z0-9])?$/;
3737

38-
/** Derive a namespace slug from the WebSocket request's Origin header. */
38+
/** Derive a namespace slug from the WebSocket request's Origin header.
39+
* Uses the full URL (scheme + hostname + port) so that different origins
40+
* on the same host (e.g. http://localhost:3000 vs http://localhost:4000)
41+
* receive distinct namespaces.
42+
*/
3943
function getNamespaceFromOrigin(originHeader) {
4044
if (!originHeader) return null;
4145
try {
42-
const { hostname } = new URL(originHeader);
43-
return hostname && VALID_NAMESPACE_RE.test(hostname) ? hostname : null;
46+
const { protocol, hostname, port } = new URL(originHeader);
47+
const scheme = protocol.slice(0, -1); // strip trailing colon, e.g. "http:" → "http"
48+
// The URL API normalises default ports (80/443) to an empty string, so two
49+
// origins that differ only by the explicit presence of the default port
50+
// (e.g. http://host and http://host:80) produce the same namespace.
51+
const namespace = port
52+
? `${scheme}-${hostname}-${port}`
53+
: `${scheme}-${hostname}`;
54+
return VALID_NAMESPACE_RE.test(namespace) ? namespace : null;
4455
} catch {
4556
return null;
4657
}
@@ -158,7 +169,9 @@ wss.on('connection', (ws, req) => {
158169
} else {
159170
// ── Logger connection ─────────────────────────────────────────────────
160171
// Derive namespace from the Origin header so that all connections from
161-
// the same origin share one namespace. Fall back to a UUID when the
172+
// the same origin share one namespace. The full URL (scheme + hostname +
173+
// port) is encoded into a path-safe slug so that different ports on the
174+
// same host get distinct namespaces. Fall back to a UUID when the
162175
// header is absent (e.g. Node.js clients) or contains an unusable value.
163176
const originNamespace = getNamespaceFromOrigin(req.headers.origin);
164177
const namespace = originNamespace ?? crypto.randomUUID();

packages/streamer/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { NamespaceLanding } from './components/NamespaceLanding';
99
import type { FilterState, LogLevel } from './types';
1010

1111
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
12-
// Accept both UUID-style and origin-hostname-style namespaces (e.g. www.domain1.com)
12+
// Accept both UUID-style and url-based namespaces (e.g. https-www.domain1.com-3000)
1313
const NAMESPACE_PATH_RE = /^\/([a-zA-Z0-9][a-zA-Z0-9._-]+)\/?$/i;
1414

1515
function getNamespaceFromPath(): string | null {

0 commit comments

Comments
 (0)