mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-10 00:11:19 +00:00
fix(browser): preserve request lifetime on Node 24
This commit is contained in:
parent
eac4214ff8
commit
c0d99ed26e
3 changed files with 58 additions and 9 deletions
|
|
@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai
|
|||
|
||||
### Fixes
|
||||
|
||||
- **Browser actions on Node 24:** keep browser request cancellation bound to the client and response lifetime instead of Node 24.16+'s prematurely aborted body-stream signal, preventing valid POST actions from failing after JSON parsing. Thanks @obviyus and @vincentkoc.
|
||||
- **SecretRef model credentials:** keep resolved provider secrets behind process-local sentinels through auth storage, stream setup, SDK configuration, and managed local-provider probing, then inject plaintext only at the final network or provider-plugin boundary while retaining exact-value log redaction. (#102008, #102009)
|
||||
- **Lean local model shell access:** keep `exec` directly visible beside the default structured Tool Search controls so coding-tuned local models can use their shell fallback instead of searching for missing domain tools. (#87587) Thanks @vincentkoc.
|
||||
- **OAuth refresh contention diagnostics:** keep local lock paths out of user-facing refresh failures and avoid duplicate failure prefixes while preserving structured provider and profile classification. (#83383) Thanks @vincentkoc.
|
||||
|
|
|
|||
51
extensions/browser/src/browser/server-middleware.test.ts
Normal file
51
extensions/browser/src/browser/server-middleware.test.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { EventEmitter } from "node:events";
|
||||
import type { Express, NextFunction, Request, Response } from "express";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { installBrowserCommonMiddleware } from "./server-middleware.js";
|
||||
|
||||
type Middleware = (req: Request, res: Response, next: NextFunction) => void;
|
||||
|
||||
describe("installBrowserCommonMiddleware", () => {
|
||||
it("shadows native request signals with the browser response-lifetime signal", () => {
|
||||
const middleware: Middleware[] = [];
|
||||
const app = {
|
||||
use: vi.fn((...handlers: unknown[]) => {
|
||||
for (const handler of handlers) {
|
||||
if (typeof handler === "function") {
|
||||
middleware.push(handler as Middleware);
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}),
|
||||
} as unknown as Express;
|
||||
installBrowserCommonMiddleware(app);
|
||||
|
||||
const nativeController = new AbortController();
|
||||
const req = new EventEmitter() as EventEmitter & Request;
|
||||
const requestPrototype = Object.create(Object.getPrototypeOf(req)) as object;
|
||||
Object.defineProperty(requestPrototype, "signal", {
|
||||
configurable: true,
|
||||
get: () => nativeController.signal,
|
||||
});
|
||||
Object.setPrototypeOf(req, requestPrototype);
|
||||
|
||||
const res = new EventEmitter() as EventEmitter & Response;
|
||||
Object.defineProperty(res, "writableEnded", { value: false, writable: true });
|
||||
const next = vi.fn();
|
||||
const commonMiddleware = middleware[0];
|
||||
if (!commonMiddleware) {
|
||||
throw new Error("browser common middleware was not installed");
|
||||
}
|
||||
|
||||
commonMiddleware(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledOnce();
|
||||
expect(Object.hasOwn(req, "signal")).toBe(true);
|
||||
expect(req.signal).not.toBe(nativeController.signal);
|
||||
expect(req.signal.aborted).toBe(false);
|
||||
|
||||
req.emit("aborted");
|
||||
expect(req.signal.aborted).toBe(true);
|
||||
expect(req.signal.reason).toEqual(new Error("request aborted"));
|
||||
});
|
||||
});
|
||||
|
|
@ -33,15 +33,12 @@ export function installBrowserCommonMiddleware(app: Express) {
|
|||
abort();
|
||||
}
|
||||
});
|
||||
// Make the signal available to browser route handlers on Node versions
|
||||
// whose IncomingMessage does not already expose a native read-only signal.
|
||||
const requestWithSignal = req as Request & { signal?: AbortSignal };
|
||||
if (!(requestWithSignal.signal instanceof AbortSignal)) {
|
||||
Object.defineProperty(req, "signal", {
|
||||
value: ctrl.signal,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
// Node 24.16+'s native request signal aborts when a POST body finishes.
|
||||
// Browser work follows the client/response lifetime instead.
|
||||
Object.defineProperty(req, "signal", {
|
||||
value: ctrl.signal,
|
||||
configurable: true,
|
||||
});
|
||||
next();
|
||||
});
|
||||
app.use(express.json({ limit: "1mb" }));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue