fix(canvas): port remaining iOS branch stability fixes (#18228)

* fix(canvas): prevent snapshot disconnects on proxied gateways

(cherry picked from commit 2a3c9f746a65f3301c0cfe58ebe6596fed06230f)

* fix(canvas): accept url alias for present and navigate

(cherry picked from commit 674ee86a0b776cbb738add1920a4031246125312)

---------

Co-authored-by: Nimrod Gutman <nimrod.g@singular.net>
This commit is contained in:
Mariano
2026-02-16 16:42:28 +00:00
committed by GitHub
parent c8a536e30a
commit 44ef045614
4 changed files with 55 additions and 12 deletions

View File

@@ -25,14 +25,25 @@ const normalizeHost = (value: HostSource, rejectLoopback: boolean) => {
return trimmed;
};
const parseHostHeader = (value: HostSource) => {
type ParsedHostHeader = {
host: string;
port?: number;
};
const parseHostHeader = (value: HostSource): ParsedHostHeader => {
if (!value) {
return "";
return { host: "" };
}
try {
return new URL(`http://${String(value).trim()}`).hostname;
const parsed = new URL(`http://${String(value).trim()}`);
const portRaw = parsed.port.trim();
const port = portRaw ? Number.parseInt(portRaw, 10) : undefined;
return {
host: parsed.hostname,
port: Number.isFinite(port) ? port : undefined,
};
} catch {
return "";
return { host: "" };
}
};
@@ -54,13 +65,29 @@ export function resolveCanvasHostUrl(params: CanvasHostUrlParams) {
(parseForwardedProto(params.forwardedProto)?.trim() === "https" ? "https" : "http");
const override = normalizeHost(params.hostOverride, true);
const requestHost = normalizeHost(parseHostHeader(params.requestHost), !!override);
const parsedRequestHost = parseHostHeader(params.requestHost);
const requestHost = normalizeHost(parsedRequestHost.host, !!override);
const localAddress = normalizeHost(params.localAddress, Boolean(override || requestHost));
const host = override || requestHost || localAddress;
if (!host) {
return undefined;
}
// When the websocket is proxied over HTTPS (for example Tailscale Serve), the gateway's
// internal listener still runs on 18789. In that case, expose the public port instead of
// advertising the internal one back to clients.
let exposedPort = port;
if (!override && requestHost && port === 18789) {
if (parsedRequestHost.port && parsedRequestHost.port > 0) {
exposedPort = parsedRequestHost.port;
} else if (scheme === "https") {
exposedPort = 443;
} else if (scheme === "http") {
exposedPort = 80;
}
}
const formatted = host.includes(":") ? `[${host}]` : host;
return `${scheme}://${formatted}:${port}`;
return `${scheme}://${formatted}:${exposedPort}`;
}