mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-10 06:44:04 +00:00
fix(ssh-tunnel): ignore stderr stream errors during teardown (#100855)
* fix(ssh-tunnel): ignore stderr stream errors during teardown * chore(proof): add real behavior proof for ssh-tunnel stderr stream errors * chore(proof): fix promise executor lint in ssh-tunnel proof * test(ssh-tunnel): keep stream error proof in regression suite * test(ssh): cover active stream errors --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -205,4 +205,29 @@ describe("startSshPortForward", () => {
|
||||
await rejection;
|
||||
expect(kill).toHaveBeenCalledWith("SIGTERM");
|
||||
});
|
||||
|
||||
it.each(["active", "teardown"] as const)(
|
||||
"does not crash when stderr errors while the tunnel is %s",
|
||||
async (phase) => {
|
||||
vi.useFakeTimers();
|
||||
spawnFakeSshListening();
|
||||
|
||||
const tunnel = await startSshPortForward({
|
||||
target: "me@example.com:2222",
|
||||
localPortPreferred: 43210,
|
||||
remotePort: 18789,
|
||||
timeoutMs: 1000,
|
||||
});
|
||||
|
||||
const child = mocks.spawn.mock.results[0]?.value as EventEmitter & {
|
||||
killed: boolean;
|
||||
stderr: EventEmitter;
|
||||
};
|
||||
const stopping = phase === "teardown" ? tunnel.stop() : undefined;
|
||||
expect(child.killed).toBe(phase === "teardown");
|
||||
expect(() => child.stderr.emit("error", new Error("stderr EPIPE"))).not.toThrow();
|
||||
|
||||
await expect(stopping ?? tunnel.stop()).resolves.toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -165,8 +165,12 @@ export async function startSshPortForward(opts: {
|
||||
const child = spawn("/usr/bin/ssh", args, {
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
});
|
||||
child.stderr?.setEncoding("utf8");
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
const stderrStream = child.stderr;
|
||||
// Child events own tunnel failure. Keep the diagnostic pipe observed so a
|
||||
// stream error cannot become an uncaught exception during active use or teardown.
|
||||
stderrStream?.on("error", () => {});
|
||||
stderrStream?.setEncoding("utf8");
|
||||
stderrStream?.on("data", (chunk) => {
|
||||
const lines = normalizeStringEntries(String(chunk).split("\n"));
|
||||
stderr.push(...lines);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user