mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-01 11:53:31 +00:00
fix(e2e): bound release fixture response bodies
This commit is contained in:
@@ -11,6 +11,10 @@ const CLICKCLACK_HTTP_TIMEOUT_MS = readPositiveInt(
|
||||
process.env.OPENCLAW_RELEASE_USER_JOURNEY_HTTP_TIMEOUT_MS,
|
||||
5000,
|
||||
);
|
||||
const CLICKCLACK_HTTP_BODY_MAX_BYTES = readPositiveInt(
|
||||
process.env.OPENCLAW_RELEASE_USER_JOURNEY_HTTP_BODY_MAX_BYTES,
|
||||
1024 * 1024,
|
||||
);
|
||||
|
||||
function readJson(file) {
|
||||
return JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
@@ -38,6 +42,55 @@ async function withClickClackFixtureResponse(url, init, consume, options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function bodyTooLargeError(label, byteLimit) {
|
||||
return Object.assign(new Error(`${label} response body exceeded ${byteLimit} bytes`), {
|
||||
code: "ETOOBIG",
|
||||
});
|
||||
}
|
||||
|
||||
async function readBoundedResponseText(
|
||||
response,
|
||||
label,
|
||||
byteLimit = CLICKCLACK_HTTP_BODY_MAX_BYTES,
|
||||
) {
|
||||
const contentLength = response.headers.get("content-length");
|
||||
if (contentLength) {
|
||||
const parsedLength = Number(contentLength);
|
||||
if (Number.isSafeInteger(parsedLength) && parsedLength > byteLimit) {
|
||||
await response.body?.cancel().catch(() => {});
|
||||
throw bodyTooLargeError(label, byteLimit);
|
||||
}
|
||||
}
|
||||
if (!response.body) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let byteCount = 0;
|
||||
let text = "";
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
return text + decoder.decode();
|
||||
}
|
||||
byteCount += value.byteLength;
|
||||
if (byteCount > byteLimit) {
|
||||
await reader.cancel().catch(() => {});
|
||||
throw bodyTooLargeError(label, byteLimit);
|
||||
}
|
||||
text += decoder.decode(value, { stream: true });
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
async function readBoundedResponseJson(response, label) {
|
||||
return JSON.parse(await readBoundedResponseText(response, label));
|
||||
}
|
||||
|
||||
function resolveHomePath(value) {
|
||||
if (value === "~") {
|
||||
return process.env.HOME;
|
||||
@@ -242,7 +295,8 @@ async function postClickClackInbound() {
|
||||
body: JSON.stringify({ body }),
|
||||
},
|
||||
async (response) => {
|
||||
assert(response.ok, `fixture inbound failed: ${response.status} ${await response.text()}`);
|
||||
const text = response.ok ? "" : await readBoundedResponseText(response, "ClickClack inbound");
|
||||
assert(response.ok, `fixture inbound failed: ${response.status} ${text}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -256,7 +310,10 @@ async function waitClickClackSocket() {
|
||||
const state = await withClickClackFixtureResponse(
|
||||
`${baseUrl}/fixture/state`,
|
||||
{},
|
||||
async (response) => (response.ok ? await response.json() : undefined),
|
||||
async (response) =>
|
||||
response.ok
|
||||
? await readBoundedResponseJson(response, "ClickClack fixture state")
|
||||
: undefined,
|
||||
{
|
||||
timeoutMs: Math.min(CLICKCLACK_HTTP_TIMEOUT_MS, remainingMs),
|
||||
},
|
||||
|
||||
@@ -244,4 +244,43 @@ describe("release user journey assertions", () => {
|
||||
rmSync(root, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("bounds ClickClack fixture error response bodies", async () => {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-"));
|
||||
const home = path.join(root, "home");
|
||||
const portPath = path.join(root, "port.txt");
|
||||
const server = startTcpFixture(
|
||||
portPath,
|
||||
[
|
||||
"(socket) => {",
|
||||
' const body = "x".repeat(128);',
|
||||
" socket.end(`HTTP/1.1 500 Internal Server Error\\r\\nContent-Type: text/plain\\r\\nContent-Length: ${Buffer.byteLength(body)}\\r\\n\\r\\n${body}`);",
|
||||
"}",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
try {
|
||||
const port = Number.parseInt((await waitForFile(portPath)).trim(), 10);
|
||||
const result = runAssertion(
|
||||
home,
|
||||
["post-clickclack-inbound", `http://127.0.0.1:${port}`, "hello"],
|
||||
{
|
||||
env: {
|
||||
OPENCLAW_RELEASE_USER_JOURNEY_HTTP_BODY_MAX_BYTES: "16",
|
||||
OPENCLAW_RELEASE_USER_JOURNEY_HTTP_TIMEOUT_MS: "1000",
|
||||
},
|
||||
timeoutMs: 2500,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.error).toBeUndefined();
|
||||
expect(result.signal).not.toBe("SIGKILL");
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain("ClickClack inbound response body exceeded 16 bytes");
|
||||
expect(result.stderr).not.toContain("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||
} finally {
|
||||
await stopChild(server);
|
||||
rmSync(root, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user