


















@@ -623,6 +623,34 @@ describe("GatewayBrowserClient", () => {
623623vi.useRealTimers();
624624});
625625626+it("reconnects without cached device token for DNS hosts beginning with a 127 label", async () => {
627+useNodeFakeTimers();
628+const client = new GatewayBrowserClient({
629+url: "ws://127.example.invalid:18789",
630+token: "shared-auth-token",
631+});
632+633+try {
634+const { ws: firstWs, connectFrame: firstConnect } = await startConnect(client);
635+expect(firstConnect.params?.auth?.token).toBe("shared-auth-token");
636+expect(firstConnect.params?.auth?.deviceToken).toBeUndefined();
637+638+emitRetryableTokenMismatch(firstWs, firstConnect.id);
639+await expectSocketClosed(firstWs);
640+firstWs.emitClose(4008, "connect failed");
641+642+await vi.advanceTimersByTimeAsync(800);
643+const secondWs = getLatestWebSocket();
644+expect(secondWs).not.toBe(firstWs);
645+const { connectFrame: secondConnect } = await continueConnect(secondWs, "nonce-2");
646+expect(secondConnect.params?.auth?.token).toBe("shared-auth-token");
647+expect(secondConnect.params?.auth?.deviceToken).toBeUndefined();
648+} finally {
649+client.stop();
650+vi.useRealTimers();
651+}
652+});
653+626654it("retries startup-unavailable connect responses without terminal callbacks", async () => {
627655useNodeFakeTimers();
628656const onClose = vi.fn();
@@ -910,6 +938,44 @@ describe("shouldRetryWithDeviceToken", () => {
910938).toBe(true);
911939});
912940941+it("allows a bounded retry for loopback IPv4 addresses in the 127 block", () => {
942+expect(
943+shouldRetryWithDeviceToken({
944+deviceTokenRetryBudgetUsed: false,
945+authDeviceToken: undefined,
946+explicitGatewayToken: "shared-auth-token",
947+deviceIdentity: {
948+deviceId: "device-1",
949+privateKey: "private-key", // pragma: allowlist secret
950+publicKey: "public-key", // pragma: allowlist secret
951+},
952+storedToken: "stored-device-token",
953+canRetryWithDeviceTokenHint: true,
954+url: "ws://127.255.10.42:18789",
955+}),
956+).toBe(true);
957+});
958+959+it("blocks the retry for DNS hosts beginning with a 127 label", () => {
960+for (const url of ["ws://127.example.invalid:18789", "ws://127.0.0.1.example.invalid:18789"]) {
961+expect(
962+shouldRetryWithDeviceToken({
963+deviceTokenRetryBudgetUsed: false,
964+authDeviceToken: undefined,
965+explicitGatewayToken: "shared-auth-token",
966+deviceIdentity: {
967+deviceId: "device-1",
968+privateKey: "private-key", // pragma: allowlist secret
969+publicKey: "public-key", // pragma: allowlist secret
970+},
971+storedToken: "stored-device-token",
972+canRetryWithDeviceTokenHint: true,
973+ url,
974+}),
975+).toBe(false);
976+}
977+});
978+913979it("blocks the retry after the one-shot budget is spent", () => {
914980expect(
915981shouldRetryWithDeviceToken({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。