Skip to content

Commit c7d77f8

Browse files
committed
fix(gateway): defer plugin HTTP dispatch
1 parent 32aa631 commit c7d77f8

3 files changed

Lines changed: 47 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Docs: https://docs.openclaw.ai
3535
- Gateway/startup: keep core request handlers, setup wizard, and channel runtime helpers off the boot path until the first matching request, wizard run, or channel start, reducing no-plugin Gateway ready RSS and avoidable startup imports. Thanks @vincentkoc.
3636
- Gateway/startup: keep CLI outbound channel send dependencies as lazy request-time senders so Gateway boot no longer imports channel plugin registration just to construct default deps. Thanks @vincentkoc.
3737
- Gateway/startup: split lightweight HTTP auth helpers away from model-override helpers so Gateway bind no longer imports model catalog selection while wiring base HTTP routes. Thanks @vincentkoc.
38+
- Gateway/startup: lazy-load plugin HTTP route dispatch when active plugin routes exist so no-plugin Gateway boot skips plugin route runtime scope setup. Thanks @vincentkoc.
3839
- CLI/Gateway: use a parse-only config snapshot for plain `gateway status` reads and reuse same-path service config context so status no longer spends tens of seconds in full config validation before printing. Thanks @vincentkoc.
3940
- Lobster/Gateway: memoize repeated Ajv schema compilation before loading the embedded Lobster runtime so scheduled workflows and `llm.invoke` loops stop growing gateway heap on content-identical schemas. Fixes #71148. Thanks @cmi525, @vsolaz, and @vincentkoc.
4041
- Codex harness: normalize cached input tokens before session/context accounting so prompt cache reads are not double-counted in `/status`, `session_status`, or persisted `sessionEntry.totalTokens`. Fixes #69298. Thanks @richardmqq.

src/gateway/server-http.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,24 @@ import { resolvePluginRouteRuntimeOperatorScopes } from "./server/plugin-route-r
7070
import {
7171
isProtectedPluginRoutePathFromContext,
7272
resolvePluginRoutePathContext,
73-
type PluginHttpRequestHandler,
7473
type PluginRoutePathContext,
75-
} from "./server/plugins-http.js";
74+
} from "./server/plugins-http/path-context.js";
7675
import type { PreauthConnectionBudget } from "./server/preauth-connection-budget.js";
7776
import type { ReadinessChecker } from "./server/readiness.js";
7877
import type { GatewayWsClient } from "./server/ws-types.js";
7978
import { VOICECLAW_REALTIME_PATH } from "./voiceclaw-realtime/paths.js";
8079

8180
type SubsystemLogger = ReturnType<typeof createSubsystemLogger>;
81+
type PluginHttpRequestHandler = (
82+
req: IncomingMessage,
83+
res: ServerResponse,
84+
pathContext?: PluginRoutePathContext,
85+
dispatchContext?: {
86+
gatewayAuthSatisfied?: boolean;
87+
gatewayRequestAuth?: AuthorizedGatewayHttpRequest;
88+
gatewayRequestOperatorScopes?: readonly string[];
89+
},
90+
) => Promise<boolean>;
8291

8392
const HOOK_AUTH_FAILURE_LIMIT = 20;
8493
const HOOK_AUTH_FAILURE_WINDOW_MS = 60_000;

src/gateway/server-runtime-state.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Server as HttpServer } from "node:http";
1+
import type { IncomingMessage, Server as HttpServer, ServerResponse } from "node:http";
22
import { WebSocketServer } from "ws";
33
import { CANVAS_HOST_PATH } from "../canvas-host/a2ui.js";
44
import { type CanvasHostHandler, createCanvasHostHandler } from "../canvas-host/server.js";
@@ -18,6 +18,7 @@ import type { ResolvedGatewayAuth } from "./auth.js";
1818
import type { ChatAbortControllerEntry } from "./chat-abort.js";
1919
import type { ControlUiRootState } from "./control-ui.js";
2020
import type { HooksConfigResolved } from "./hooks.js";
21+
import type { AuthorizedGatewayHttpRequest } from "./http-auth-utils.js";
2122
import { isLoopbackHost, resolveGatewayListenHosts } from "./net.js";
2223
import type { GatewayBroadcastFn, GatewayBroadcastToConnIdsFn } from "./server-broadcast-types.js";
2324
import { createGatewayBroadcaster } from "./server-broadcast.js";
@@ -35,11 +36,8 @@ import {
3536
import type { DedupeEntry } from "./server-shared.js";
3637
import { createGatewayHooksRequestHandler } from "./server/hooks.js";
3738
import { listenGatewayHttpServer } from "./server/http-listen.js";
38-
import {
39-
createGatewayPluginRequestHandler,
40-
shouldEnforceGatewayAuthForPluginPath,
41-
type PluginRoutePathContext,
42-
} from "./server/plugins-http.js";
39+
import type { PluginRoutePathContext } from "./server/plugins-http/path-context.js";
40+
import { shouldEnforceGatewayAuthForPluginPath } from "./server/plugins-http/route-auth.js";
4341
import {
4442
createPreauthConnectionBudget,
4543
type PreauthConnectionBudget,
@@ -48,6 +46,17 @@ import type { ReadinessChecker } from "./server/readiness.js";
4846
import type { GatewayTlsRuntime } from "./server/tls.js";
4947
import type { GatewayWsClient } from "./server/ws-types.js";
5048

49+
type GatewayPluginRequestHandler = (
50+
req: IncomingMessage,
51+
res: ServerResponse,
52+
pathContext?: PluginRoutePathContext,
53+
dispatchContext?: {
54+
gatewayAuthSatisfied?: boolean;
55+
gatewayRequestAuth?: AuthorizedGatewayHttpRequest;
56+
gatewayRequestOperatorScopes?: readonly string[];
57+
},
58+
) => Promise<boolean>;
59+
5160
export async function createGatewayRuntimeState(params: {
5261
cfg: import("../config/config.js").OpenClawConfig;
5362
bindHost: string;
@@ -145,10 +154,26 @@ export async function createGatewayRuntimeState(params: {
145154
logHooks: params.logHooks,
146155
});
147156

148-
const handlePluginRequest = createGatewayPluginRequestHandler({
149-
registry: params.pluginRegistry,
150-
log: params.logPlugins,
151-
});
157+
let loadedPluginRequestHandler: GatewayPluginRequestHandler | null = null;
158+
const handlePluginRequest: GatewayPluginRequestHandler = async (
159+
req,
160+
res,
161+
pathContext,
162+
dispatchContext,
163+
) => {
164+
const registry = resolveActivePluginHttpRouteRegistry(params.pluginRegistry);
165+
if ((registry.httpRoutes ?? []).length === 0) {
166+
return false;
167+
}
168+
if (!loadedPluginRequestHandler) {
169+
const { createGatewayPluginRequestHandler } = await import("./server/plugins-http.js");
170+
loadedPluginRequestHandler = createGatewayPluginRequestHandler({
171+
registry: params.pluginRegistry,
172+
log: params.logPlugins,
173+
});
174+
}
175+
return await loadedPluginRequestHandler(req, res, pathContext, dispatchContext);
176+
};
152177
const shouldEnforcePluginGatewayAuth = (pathContext: PluginRoutePathContext): boolean => {
153178
return shouldEnforceGatewayAuthForPluginPath(
154179
resolveActivePluginHttpRouteRegistry(params.pluginRegistry),

0 commit comments

Comments
 (0)