mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(release): close cross-os artifact sockets
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { createServer } from "node:http";
|
||||
import { createConnection as createNetConnection, createServer as createNetServer } from "node:net";
|
||||
import type { Socket } from "node:net";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, relative, resolve, win32 as pathWin32 } from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
@@ -3686,6 +3687,7 @@ export async function startStaticFileServer(params) {
|
||||
const logStream = createWriteStream(params.logPath, { flags: "a" });
|
||||
const fileName = String(params.filePath.split(/[/\\]/u).at(-1) ?? "artifact");
|
||||
const fileStat = statSync(params.filePath);
|
||||
const sockets = new Set<Socket>();
|
||||
const server = createServer((request, response) => {
|
||||
logStream.write(`${new Date().toISOString()} ${request.method} ${request.url}\n`);
|
||||
if (request.url !== `/${fileName}`) {
|
||||
@@ -3710,6 +3712,12 @@ export async function startStaticFileServer(params) {
|
||||
});
|
||||
fileStream.pipe(response);
|
||||
});
|
||||
server.on("connection", (socket) => {
|
||||
sockets.add(socket);
|
||||
socket.once("close", () => {
|
||||
sockets.delete(socket);
|
||||
});
|
||||
});
|
||||
await new Promise((resolvePromise, rejectPromise) => {
|
||||
server.once("error", rejectPromise);
|
||||
server.listen(0, "127.0.0.1", resolvePromise);
|
||||
@@ -3731,6 +3739,9 @@ export async function startStaticFileServer(params) {
|
||||
}
|
||||
resolvePromise();
|
||||
});
|
||||
for (const socket of sockets) {
|
||||
socket.destroy();
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
symlinkSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { createServer as createNetServer } from "node:net";
|
||||
import { createConnection as createNetConnection, createServer as createNetServer } from "node:net";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, win32 } from "node:path";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
@@ -685,6 +685,36 @@ describe("scripts/openclaw-cross-os-release-checks", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("closes static release artifact sockets left by aborted clients", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "openclaw-cross-os-static-server-close-"));
|
||||
const filePath = join(dir, "openclaw-2026.4.14.tgz");
|
||||
const logPath = join(dir, "server.log");
|
||||
let server: Awaited<ReturnType<typeof startStaticFileServer>> | undefined;
|
||||
|
||||
try {
|
||||
writeFileSync(filePath, Buffer.alloc(1024 * 1024, "x"));
|
||||
server = await startStaticFileServer({ filePath, logPath });
|
||||
const url = new URL(server.url);
|
||||
const socket = createNetConnection(Number(url.port), url.hostname);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
socket.once("connect", resolve);
|
||||
socket.once("error", reject);
|
||||
});
|
||||
socket.write(`GET ${url.pathname} HTTP/1.1\r\nHost: ${url.host}\r\n\r\n`);
|
||||
await Promise.race([
|
||||
server.close(),
|
||||
delay(1_000).then(() => Promise.reject(new Error("close timed out"))),
|
||||
]);
|
||||
await Promise.race([
|
||||
new Promise<void>((resolve) => socket.once("close", resolve)),
|
||||
delay(1_000).then(() => Promise.reject(new Error("socket close timed out"))),
|
||||
]);
|
||||
} finally {
|
||||
await server?.close().catch(() => {});
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not preload static release artifacts before serving them", () => {
|
||||
const source = readFileSync("scripts/openclaw-cross-os-release-checks.ts", "utf8");
|
||||
const serverSource = source.slice(
|
||||
|
||||
Reference in New Issue
Block a user