mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(e2e): time out npm registry upstream requests (#108718)
This commit is contained in:
@@ -25,6 +25,9 @@ function normalizeUpstreamRegistry(raw) {
|
||||
}
|
||||
|
||||
const upstreamRegistry = normalizeUpstreamRegistry(process.env.OPENCLAW_NPM_REGISTRY_UPSTREAM);
|
||||
// Match other E2E package-download budgets while keeping a stalled public-registry hop
|
||||
// from consuming the much larger install phase deadline.
|
||||
const UPSTREAM_REQUEST_TIMEOUT_MS = 120_000;
|
||||
|
||||
if (!portFile || packageArgs.length === 0 || packageArgs.length % 3 !== 0) {
|
||||
console.error(
|
||||
@@ -139,7 +142,10 @@ async function proxyUpstream(rawRequestUrl, response) {
|
||||
}
|
||||
try {
|
||||
const upstreamUrl = resolveUpstreamRequestUrl(rawRequestUrl);
|
||||
const upstreamResponse = await fetch(upstreamUrl, { redirect: "manual" });
|
||||
const upstreamResponse = await fetch(upstreamUrl, {
|
||||
redirect: "manual",
|
||||
signal: AbortSignal.timeout(UPSTREAM_REQUEST_TIMEOUT_MS),
|
||||
});
|
||||
const body = Buffer.from(await upstreamResponse.arrayBuffer());
|
||||
// Fetch decodes compressed bodies but preserves upstream length metadata.
|
||||
// Emit the decoded size so npm clients do not truncate proxied responses.
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { createServer, request as httpRequest } from "node:http";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { gzipSync } from "node:zlib";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createBoundedChildOutput } from "../helpers/bounded-child-output.js";
|
||||
@@ -740,6 +741,90 @@ test -d "$OPENCLAW_PLUGINS_TMP_DIR"
|
||||
}
|
||||
});
|
||||
|
||||
it("times out stalled upstream response bodies without stopping the fixture registry", async () => {
|
||||
const tempDirs: string[] = [];
|
||||
const root = makeTempDir(tempDirs, "openclaw-plugin-npm-fixture-proxy-timeout-");
|
||||
const portFile = path.join(root, "port");
|
||||
const preloadPath = path.join(root, "shorten-abort-timeout.mjs");
|
||||
const tarballPath = path.join(root, "demo-plugin.tgz");
|
||||
let upstreamHits = 0;
|
||||
writeFileSync(
|
||||
preloadPath,
|
||||
[
|
||||
"const nativeTimeout = AbortSignal.timeout.bind(AbortSignal);",
|
||||
"AbortSignal.timeout = () => nativeTimeout(50);",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
writeFileSync(tarballPath, "fixture package archive", "utf8");
|
||||
|
||||
const upstream = createServer((_request, response) => {
|
||||
upstreamHits += 1;
|
||||
response.writeHead(200, { "content-type": "application/json" });
|
||||
response.write('{"partial":');
|
||||
});
|
||||
await new Promise<void>((resolve) => {
|
||||
upstream.listen(0, "127.0.0.1", resolve);
|
||||
});
|
||||
const upstreamAddress = upstream.address();
|
||||
if (!upstreamAddress || typeof upstreamAddress === "string") {
|
||||
throw new Error("expected upstream registry address");
|
||||
}
|
||||
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
[
|
||||
"--import",
|
||||
pathToFileURL(preloadPath).href,
|
||||
"scripts/e2e/lib/plugins/npm-registry-server.mjs",
|
||||
portFile,
|
||||
"@openclaw/demo-plugin-npm",
|
||||
"1.0.0",
|
||||
tarballPath,
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_NPM_REGISTRY_UPSTREAM: `http://127.0.0.1:${upstreamAddress.port}`,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
const stderr = createBoundedChildOutput();
|
||||
child.stderr.setEncoding("utf8");
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr.append(chunk);
|
||||
});
|
||||
|
||||
try {
|
||||
const port = await waitForPortFile(portFile);
|
||||
const stalled = await requestFixtureRegistry(port, "/stalled-package");
|
||||
|
||||
expect(stalled.statusCode, stderr.text()).toBe(502);
|
||||
expect(stalled.body).toContain("upstream registry request failed");
|
||||
expect(upstreamHits).toBe(1);
|
||||
|
||||
const local = await requestFixtureRegistry(port, "/@openclaw%2Fdemo-plugin-npm");
|
||||
|
||||
expect(local.statusCode, stderr.text()).toBe(200);
|
||||
expect(child.exitCode, stderr.text()).toBeNull();
|
||||
} finally {
|
||||
if (child.exitCode === null) {
|
||||
child.kill();
|
||||
await new Promise((resolve) => {
|
||||
child.once("close", resolve);
|
||||
});
|
||||
}
|
||||
upstream.closeAllConnections();
|
||||
await new Promise<void>((resolve) => {
|
||||
upstream.close(() => resolve());
|
||||
});
|
||||
cleanupTempDirs(tempDirs);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not let absolute-form request targets escape the configured upstream", async () => {
|
||||
const tempDirs: string[] = [];
|
||||
const root = makeTempDir(tempDirs, "openclaw-plugin-npm-fixture-proxy-origin-");
|
||||
|
||||
Reference in New Issue
Block a user