mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 14:30:45 +00:00
fix(gateway): preserve canvas tls urls
This commit is contained in:
@@ -63,6 +63,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis.
|
- Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis.
|
||||||
- Control UI/Talk: stop and clear failed realtime Talk sessions when dismissing runtime error banners, so the next Talk click starts a fresh session instead of only stopping the stale one. Thanks @vincentkoc.
|
- Control UI/Talk: stop and clear failed realtime Talk sessions when dismissing runtime error banners, so the next Talk click starts a fresh session instead of only stopping the stale one. Thanks @vincentkoc.
|
||||||
- Control UI/Talk: retry from a failed realtime Talk session on the next Talk click instead of requiring a separate stale-session stop click first. Thanks @vincentkoc.
|
- Control UI/Talk: retry from a failed realtime Talk session on the next Talk click instead of requiring a separate stale-session stop click first. Thanks @vincentkoc.
|
||||||
|
- Canvas host: preserve the Gateway TLS scheme in browser canvas host URLs and startup mount logs, so direct HTTPS gateways do not advertise insecure canvas links. Thanks @vincentkoc.
|
||||||
- Google Chat: create an isolated Google auth transport per auth client, so google-auth-library interceptor mutations do not accumulate across webhook verification and access-token clients. Thanks @vincentkoc.
|
- Google Chat: create an isolated Google auth transport per auth client, so google-auth-library interceptor mutations do not accumulate across webhook verification and access-token clients. Thanks @vincentkoc.
|
||||||
- Control UI/performance: cap long-task and long-animation-frame diagnostics in the shared event log, so slow-render telemetry does not evict gateway/plugin events from the Debug and Overview views. Thanks @vincentkoc.
|
- Control UI/performance: cap long-task and long-animation-frame diagnostics in the shared event log, so slow-render telemetry does not evict gateway/plugin events from the Debug and Overview views. Thanks @vincentkoc.
|
||||||
- Gateway/startup: log the canvas host mount only after the HTTP server has bound, so startup logs no longer report the canvas host as mounted before it can serve requests.
|
- Gateway/startup: log the canvas host mount only after the HTTP server has bound, so startup logs no longer report the canvas host as mounted before it can serve requests.
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export function attachGatewayWsHandlers(params: GatewayWsRuntimeParams) {
|
|||||||
port: params.port,
|
port: params.port,
|
||||||
gatewayHost: params.gatewayHost,
|
gatewayHost: params.gatewayHost,
|
||||||
canvasHostEnabled: params.canvasHostEnabled,
|
canvasHostEnabled: params.canvasHostEnabled,
|
||||||
|
canvasHostScheme: params.canvasHostScheme,
|
||||||
canvasHostServerPort: params.canvasHostServerPort,
|
canvasHostServerPort: params.canvasHostServerPort,
|
||||||
resolvedAuth: params.resolvedAuth,
|
resolvedAuth: params.resolvedAuth,
|
||||||
getResolvedAuth: params.getResolvedAuth,
|
getResolvedAuth: params.getResolvedAuth,
|
||||||
|
|||||||
@@ -1329,6 +1329,7 @@ export async function startGatewayServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { attachGatewayWsHandlers } = await import("./server-ws-runtime.js");
|
const { attachGatewayWsHandlers } = await import("./server-ws-runtime.js");
|
||||||
|
const canvasHostScheme = gatewayTls.enabled ? "https" : "http";
|
||||||
attachGatewayWsHandlers({
|
attachGatewayWsHandlers({
|
||||||
wss,
|
wss,
|
||||||
clients,
|
clients,
|
||||||
@@ -1336,6 +1337,7 @@ export async function startGatewayServer(
|
|||||||
port,
|
port,
|
||||||
gatewayHost: bindHost ?? undefined,
|
gatewayHost: bindHost ?? undefined,
|
||||||
canvasHostEnabled: Boolean(canvasHost),
|
canvasHostEnabled: Boolean(canvasHost),
|
||||||
|
canvasHostScheme,
|
||||||
canvasHostServerPort,
|
canvasHostServerPort,
|
||||||
resolvedAuth,
|
resolvedAuth,
|
||||||
getResolvedAuth,
|
getResolvedAuth,
|
||||||
@@ -1357,7 +1359,7 @@ export async function startGatewayServer(
|
|||||||
await startListening();
|
await startListening();
|
||||||
if (canvasHost?.rootDir) {
|
if (canvasHost?.rootDir) {
|
||||||
logCanvas.info(
|
logCanvas.info(
|
||||||
`canvas host mounted at http://${bindHost}:${port}${CANVAS_HOST_PATH}/ (root ${canvasHost.rootDir})`,
|
`canvas host mounted at ${canvasHostScheme}://${bindHost}:${port}${CANVAS_HOST_PATH}/ (root ${canvasHost.rootDir})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
startupTrace.mark("http.bound");
|
startupTrace.mark("http.bound");
|
||||||
|
|||||||
@@ -111,6 +111,63 @@ describe("attachGatewayWsConnectionHandler", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the gateway TLS scheme for canvas host URLs", async () => {
|
||||||
|
const listeners = new Map<string, (...args: unknown[]) => void>();
|
||||||
|
const wss = {
|
||||||
|
on: vi.fn((event: string, handler: (...args: unknown[]) => void) => {
|
||||||
|
listeners.set(event, handler);
|
||||||
|
}),
|
||||||
|
} as unknown as WebSocketServer;
|
||||||
|
const socket = Object.assign(new EventEmitter(), {
|
||||||
|
_socket: {
|
||||||
|
remoteAddress: "127.0.0.1",
|
||||||
|
remotePort: 1234,
|
||||||
|
localAddress: "127.0.0.1",
|
||||||
|
localPort: 5678,
|
||||||
|
},
|
||||||
|
send: vi.fn(),
|
||||||
|
close: vi.fn(),
|
||||||
|
});
|
||||||
|
const upgradeReq = {
|
||||||
|
headers: { host: "gateway.example.com" },
|
||||||
|
socket: { localAddress: "127.0.0.1" },
|
||||||
|
};
|
||||||
|
|
||||||
|
attachGatewayWsConnectionHandler({
|
||||||
|
wss,
|
||||||
|
clients: new Set(),
|
||||||
|
preauthConnectionBudget: { release: vi.fn() } as never,
|
||||||
|
port: 18789,
|
||||||
|
canvasHostEnabled: true,
|
||||||
|
canvasHostScheme: "https",
|
||||||
|
resolvedAuth: createResolvedAuth("token"),
|
||||||
|
gatewayMethods: [],
|
||||||
|
events: [],
|
||||||
|
refreshHealthSnapshot: vi.fn(async () => ({}) as never),
|
||||||
|
logGateway: createLogger() as never,
|
||||||
|
logHealth: createLogger() as never,
|
||||||
|
logWsControl: createLogger() as never,
|
||||||
|
extraHandlers: {},
|
||||||
|
broadcast: vi.fn(),
|
||||||
|
buildRequestContext: () =>
|
||||||
|
({
|
||||||
|
unsubscribeAllSessionEvents: vi.fn(),
|
||||||
|
nodeRegistry: { unregister: vi.fn() },
|
||||||
|
nodeUnsubscribeAll: vi.fn(),
|
||||||
|
}) as never,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onConnection = listeners.get("connection");
|
||||||
|
expect(onConnection).toBeTypeOf("function");
|
||||||
|
onConnection?.(socket, upgradeReq);
|
||||||
|
await waitForLazyMessageHandler();
|
||||||
|
|
||||||
|
const passed = attachGatewayWsMessageHandlerMock.mock.calls[0]?.[0] as {
|
||||||
|
canvasHostUrl?: string;
|
||||||
|
};
|
||||||
|
expect(passed.canvasHostUrl).toBe("https://gateway.example.com:443");
|
||||||
|
});
|
||||||
|
|
||||||
it("rejects late client registration after a pre-connect socket close", async () => {
|
it("rejects late client registration after a pre-connect socket close", async () => {
|
||||||
const listeners = new Map<string, (...args: unknown[]) => void>();
|
const listeners = new Map<string, (...args: unknown[]) => void>();
|
||||||
const wss = {
|
const wss = {
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export type GatewayWsSharedHandlerParams = {
|
|||||||
port: number;
|
port: number;
|
||||||
gatewayHost?: string;
|
gatewayHost?: string;
|
||||||
canvasHostEnabled: boolean;
|
canvasHostEnabled: boolean;
|
||||||
|
canvasHostScheme?: "http" | "https";
|
||||||
canvasHostServerPort?: number;
|
canvasHostServerPort?: number;
|
||||||
resolvedAuth: ResolvedGatewayAuth;
|
resolvedAuth: ResolvedGatewayAuth;
|
||||||
getResolvedAuth?: () => ResolvedGatewayAuth;
|
getResolvedAuth?: () => ResolvedGatewayAuth;
|
||||||
@@ -199,6 +200,7 @@ export function attachGatewayWsConnectionHandler(params: AttachGatewayWsConnecti
|
|||||||
port,
|
port,
|
||||||
gatewayHost,
|
gatewayHost,
|
||||||
canvasHostEnabled,
|
canvasHostEnabled,
|
||||||
|
canvasHostScheme,
|
||||||
canvasHostServerPort,
|
canvasHostServerPort,
|
||||||
resolvedAuth,
|
resolvedAuth,
|
||||||
getResolvedAuth = () => resolvedAuth,
|
getResolvedAuth = () => resolvedAuth,
|
||||||
@@ -253,6 +255,7 @@ export function attachGatewayWsConnectionHandler(params: AttachGatewayWsConnecti
|
|||||||
requestHost: upgradeReq.headers.host,
|
requestHost: upgradeReq.headers.host,
|
||||||
forwardedProto: upgradeReq.headers["x-forwarded-proto"],
|
forwardedProto: upgradeReq.headers["x-forwarded-proto"],
|
||||||
localAddress: upgradeReq.socket?.localAddress,
|
localAddress: upgradeReq.socket?.localAddress,
|
||||||
|
scheme: canvasHostScheme,
|
||||||
});
|
});
|
||||||
|
|
||||||
logWs("in", "open", { connId, remoteAddr, remotePort, localAddr, localPort, endpoint });
|
logWs("in", "open", { connId, remoteAddr, remotePort, localAddr, localPort, endpoint });
|
||||||
|
|||||||
Reference in New Issue
Block a user