From b8390787c2a5c00bad09d923cf4f97e05aa5b380 Mon Sep 17 00:00:00 2001 From: "Valentin D. Pinkman" Date: Fri, 24 Jan 2025 15:00:06 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=94=20(webhid):=20Update=20reconnectio?= =?UTF-8?q?n=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transport/WebHidDeviceConnection.test.ts | 10 ++++- .../api/transport/WebHidDeviceConnection.ts | 39 ++++++++++--------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.test.ts b/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.test.ts index f2f9c7f5f..c6b8c8bee 100644 --- a/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.test.ts +++ b/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.test.ts @@ -11,7 +11,6 @@ import { import { Left, Right } from "purify-ts"; import { RECONNECT_DEVICE_TIMEOUT } from "@api/data/WebHidConfig"; -import { WebHidSendReportError } from "@api/model/Errors"; import { hidDeviceStubBuilder } from "@api/model/HIDDevice.stub"; import { WebHidDeviceConnection } from "./WebHidDeviceConnection"; @@ -130,12 +129,14 @@ describe("WebHidDeviceConnection", () => { } as HIDInputReportEvent), ), ); + const connection = new WebHidDeviceConnection( { device, apduSender, apduReceiver, onConnectionTerminated, deviceId }, logger, ); let hasResolved = false; + const responsePromise = connection .sendApdu(Uint8Array.from([]), true) .then((response) => { @@ -283,7 +284,12 @@ describe("WebHidDeviceConnection", () => { const response = await responsePromise; - expect(response).toEqual(Left(new WebHidSendReportError())); + expect(response).toEqual( + Right({ + statusCode: new Uint8Array([0x55, 0x15]), + data: new Uint8Array([]), + }), + ); }); }); }); diff --git a/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.ts b/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.ts index edc4d3f96..1fba0c3f5 100644 --- a/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.ts +++ b/packages/transport/web-hid/src/api/transport/WebHidDeviceConnection.ts @@ -10,7 +10,7 @@ import { ReconnectionFailedError, } from "@ledgerhq/device-management-kit"; import { type Either, Left, Maybe, Nothing, Right } from "purify-ts"; -import { Subject } from "rxjs"; +import { firstValueFrom, from, retry, Subject } from "rxjs"; import { RECONNECT_DEVICE_TIMEOUT } from "@api/data/WebHidConfig"; import { WebHidSendReportError } from "@api/model/Errors"; @@ -47,8 +47,6 @@ export class WebHidDeviceConnection implements DeviceConnection { private waitingForReconnection = false; /** Timeout to wait for the device to reconnect */ private lostConnectionTimeout: Timer | null = null; - /** Time since disconnection */ - private timeSinceDisconnection: Maybe = Nothing; /** Flag to indicate if the connection is terminated */ private terminated = false; @@ -133,8 +131,16 @@ export class WebHidDeviceConnection implements DeviceConnection { this._logger.debug("Sending Frame", { data: { frame: frame.getRawData() }, }); + try { - await this._device.sendReport(0, frame.getRawData()); + await firstValueFrom( + from(this._device.sendReport(0, frame.getRawData())).pipe( + retry({ + count: 3, + delay: 500, + }), + ), + ); } catch (error) { this._logger.error("Error sending frame", { data: { error } }); return Promise.resolve(Left(new WebHidSendReportError(error))); @@ -178,7 +184,13 @@ export class WebHidDeviceConnection implements DeviceConnection { const sub = this.reconnectionSubject.subscribe({ next: (res) => { if (waitingForDeviceResponse) { - this._sendApduSubject.error(new WebHidSendReportError()); + this._sendApduSubject.error( + new WebHidSendReportError( + new Error( + "Device disconnected while waiting for device repsonse", + ), + ), + ); } if (res === "success") { @@ -186,6 +198,7 @@ export class WebHidDeviceConnection implements DeviceConnection { } else { resolve(Left(res)); } + sub.unsubscribe(); }, }); @@ -198,7 +211,6 @@ export class WebHidDeviceConnection implements DeviceConnection { * */ public lostConnection() { this._logger.info("⏱️ Lost connection, starting timer"); - this.timeSinceDisconnection = Maybe.of(Date.now()); this.waitingForReconnection = true; this.lostConnectionTimeout = setTimeout(() => { this._logger.info("❌ Disconnection timeout, terminating connection"); @@ -212,24 +224,13 @@ export class WebHidDeviceConnection implements DeviceConnection { this._device.oninputreport = (event) => this.receiveHidInputReport(event); if (this.lostConnectionTimeout) { - this._logger.info("⏱️🔌 Device reconnected"); clearTimeout(this.lostConnectionTimeout); } await device.open(); + this._logger.info("⏱️🔌 Device reconnected"); - if (this._pendingApdu.isJust()) { - if (this.timeSinceDisconnection.isJust()) { - const now = Date.now(); - const timeSinceDisconnection = - now - this.timeSinceDisconnection.extract(); - // 3 seconds timeout - if (timeSinceDisconnection > RECONNECT_DEVICE_TIMEOUT / 2) { - this._sendApduSubject.error(new WebHidSendReportError()); - } - } - this.timeSinceDisconnection = Nothing; - } + // this.timeOfReconnection = Maybe.of(Date.now()); this.waitingForReconnection = false; this.reconnectionSubject.next("success");