mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(coding-agent): reconnect after abnormal Radius closure
This commit is contained in:
parent
35741a456d
commit
35c49350fd
2 changed files with 41 additions and 5 deletions
|
|
@ -18,6 +18,11 @@ const HOST_RETRY_MAX_MS = 30_000;
|
|||
const MISSING_AUTH_RETRY_MS = 30_000;
|
||||
const CLIENT_RETRY_INITIAL_MS = 1_000;
|
||||
const CLIENT_RETRY_MAX_MS = 30_000;
|
||||
// Undici implements the browser WebSocket API, which permits callers to send
|
||||
// only code 1000 or application codes from 3000 through 4999. RFC protocol
|
||||
// codes such as 1002 and 1011 may be received but cannot be passed to close().
|
||||
const LOCAL_PROTOCOL_ERROR_CLOSE_CODE = 4000;
|
||||
const LOCAL_TRANSPORT_ERROR_CLOSE_CODE = 4001;
|
||||
|
||||
interface RadiusRelayMessageEvent extends Event {
|
||||
readonly data: unknown;
|
||||
|
|
@ -193,7 +198,7 @@ export class RadiusRelayHost {
|
|||
try {
|
||||
this.#handleHostMessage(event.data);
|
||||
} catch (error) {
|
||||
socket.close(1002, "Radius relay protocol error");
|
||||
closeWebSocket(socket, LOCAL_PROTOCOL_ERROR_CLOSE_CODE, "Radius relay protocol error");
|
||||
finish(toError(error));
|
||||
}
|
||||
};
|
||||
|
|
@ -216,9 +221,11 @@ export class RadiusRelayHost {
|
|||
const control = parseHostControlMessage(value);
|
||||
switch (control.type) {
|
||||
case "ping":
|
||||
void this.#sendControl({ version: 1, type: "pong" }).catch((error: unknown) =>
|
||||
this.#socket?.close(1011, errorMessage(error)),
|
||||
);
|
||||
void this.#sendControl({ version: 1, type: "pong" }).catch(() => {
|
||||
if (this.#socket !== undefined) {
|
||||
closeWebSocket(this.#socket, LOCAL_TRANSPORT_ERROR_CLOSE_CODE, "Radius relay send failed");
|
||||
}
|
||||
});
|
||||
return;
|
||||
case "pong":
|
||||
return;
|
||||
|
|
@ -458,7 +465,7 @@ class RadiusClientByteTransport implements ByteTransport {
|
|||
|
||||
#fail(error: Error): void {
|
||||
if (!this.#markClosed()) return;
|
||||
this.#socket.close(1011, "Radius relay transport error");
|
||||
closeWebSocket(this.#socket, LOCAL_TRANSPORT_ERROR_CLOSE_CODE, "Radius relay transport error");
|
||||
this.#handlers.onError(error);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ class FakeWebSocket {
|
|||
}
|
||||
|
||||
close(code = 1000, reason = ""): void {
|
||||
if (code !== 1000 && (code < 3000 || code > 4999)) {
|
||||
throw new DOMException("Invalid close code", "InvalidAccessError");
|
||||
}
|
||||
if (this.readyState === 3) return;
|
||||
this.readyState = 3;
|
||||
this.emit("close", { code, reason });
|
||||
|
|
@ -69,6 +72,12 @@ class FakeWebSocket {
|
|||
this.emit("error", { error, message: error.message });
|
||||
}
|
||||
|
||||
abnormalClose(error: Error): void {
|
||||
this.readyState = 3;
|
||||
this.emit("error", { error, message: error.message });
|
||||
this.emit("close", { code: 1006, reason: "" });
|
||||
}
|
||||
|
||||
private emit(type: string, event: unknown): void {
|
||||
for (const listener of [...(this.listeners.get(type) ?? [])]) listener(event);
|
||||
}
|
||||
|
|
@ -260,6 +269,26 @@ describe("experimental Radius relay", () => {
|
|||
expect(onError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("reports established abnormal closures so the client can reconnect", async () => {
|
||||
const webSockets = socketFactory();
|
||||
const onClose = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const transportPromise = createRadiusClientTransportFactory({
|
||||
serverId,
|
||||
auth: new RadiusRelayAuthResolver({ type: "token", token: "secret" }),
|
||||
webSocketFactory: webSockets.factory,
|
||||
})({ onData: vi.fn(), onClose, onError });
|
||||
await vi.waitFor(() => expect(webSockets.sockets).toHaveLength(1));
|
||||
const { socket, options } = webSockets.sockets[0]!;
|
||||
socket.open(options.protocol);
|
||||
await transportPromise;
|
||||
|
||||
expect(() => socket.abnormalClose(new Error("network lost"))).not.toThrow();
|
||||
expect(onError).toHaveBeenCalledOnce();
|
||||
expect(onError).toHaveBeenCalledWith(expect.objectContaining({ message: "network lost" }));
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("reports a useful error when Undici omits WebSocket failure details", async () => {
|
||||
const webSockets = socketFactory();
|
||||
const opening = createRadiusClientTransportFactory({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue