Skip to content

Commit

Permalink
πŸ‘” (webhid): Update reconnection logic
Browse files Browse the repository at this point in the history
  • Loading branch information
valpinkman committed Jan 24, 2025
1 parent 760f6e5 commit b839078
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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([]),
}),
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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<number> = Nothing;
/** Flag to indicate if the connection is terminated */
private terminated = false;

Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -178,14 +184,21 @@ 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") {
resolve(Right(undefined));
} else {
resolve(Left(res));
}

sub.unsubscribe();
},
});
Expand All @@ -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");
Expand All @@ -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");
Expand Down

0 comments on commit b839078

Please sign in to comment.