Skip to content
This repository has been archived by the owner on Jun 27, 2022. It is now read-only.

Commit

Permalink
LL-8941 Conditionally release usb connection hold after timeout (#797)
Browse files Browse the repository at this point in the history
  • Loading branch information
juan-cortes authored Mar 4, 2022
1 parent 29d607a commit 585f125
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
21 changes: 20 additions & 1 deletion packages/hw-transport-node-hid-singleton/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ Allows to communicate with Ledger Hardware Wallets.

* [TransportNodeHidSingleton](#transportnodehidsingleton)
* [Examples](#examples)
* [exchange](#exchange)
* [Parameters](#parameters)
* [isSupported](#issupported)
* [list](#list)
* [listen](#listen)
* [Parameters](#parameters)
* [Parameters](#parameters-1)
* [autoDisconnect](#autodisconnect)
* [disconnect](#disconnect)
* [open](#open)

Expand All @@ -38,6 +41,16 @@ import TransportNodeHid from "@ledgerhq/hw-transport-node-hid-singleton";
TransportNodeHid.create().then(transport => ...)
```

#### exchange

Exchange with the device using APDU protocol.

##### Parameters

* `apdu` **[Buffer](https://nodejs.org/api/buffer.html)**

Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** a promise of apdu response

#### isSupported

#### list
Expand All @@ -50,6 +63,12 @@ TransportNodeHid.create().then(transport => ...)

Returns **Subscription**

#### autoDisconnect

convenience wrapper for auto-disconnect logic

Returns **void**

#### disconnect

globally disconnect the transport singleton
Expand Down
56 changes: 54 additions & 2 deletions packages/hw-transport-node-hid-singleton/src/TransportNodeHid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@ import { identifyUSBProductId } from "@ledgerhq/devices";
import { CantOpenDevice } from "@ledgerhq/errors";
import { listenDevices } from "./listenDevices";
let transportInstance;

const DISCONNECT_TIMEOUT = 5000;
let disconnectTimeout;
const clearDisconnectTimeout = () => {
if (disconnectTimeout) {
clearTimeout(disconnectTimeout);
}
};

const setDisconnectTimeout = () => {
clearDisconnectTimeout();
disconnectTimeout = setTimeout(
() => TransportNodeHidSingleton.autoDisconnect(),
DISCONNECT_TIMEOUT
);
};

/**
* node-hid Transport implementation
* @example
Expand All @@ -21,6 +38,7 @@ let transportInstance;
*/

export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents {
preventAutoDisconnect = false;
/**
*
*/
Expand Down Expand Up @@ -88,6 +106,20 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
};
};

/**
* convenience wrapper for auto-disconnect logic
*/
static async autoDisconnect(): void {
if (transportInstance && !transportInstance.preventAutoDisconnect) {
log("hid-verbose", "triggering auto disconnect");
TransportNodeHidSingleton.disconnect();
} else if (transportInstance) {
// If we have disabled the auto-disconnect, try again in DISCONNECT_TIMEOUT
clearDisconnectTimeout();
setDisconnectTimeout();
}
}

/**
* globally disconnect the transport singleton
*/
Expand All @@ -97,12 +129,14 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
transportInstance.emit("disconnect");
transportInstance = null;
}
clearDisconnectTimeout();
}

/**
* if path="" is not provided, the library will take the first device
*/
static open(): Promise<TransportNodeHidSingleton> {
clearDisconnectTimeout();
return Promise.resolve().then(() => {
if (transportInstance) {
log("hid-verbose", "reusing opened transport instance");
Expand Down Expand Up @@ -138,8 +172,26 @@ export default class TransportNodeHidSingleton extends TransportNodeHidNoEvents
});
}

close() {
// intentionally, a close will not effectively close the hid connection
setAllowAutoDisconnect(allow: boolean): void {
this.preventAutoDisconnect = !allow;
}

/**
* Exchange with the device using APDU protocol.
* @param apdu
* @returns a promise of apdu response
*/
async exchange(apdu: Buffer): Promise<Buffer> {
clearDisconnectTimeout();
const result = await super.exchange(apdu);
setDisconnectTimeout();
return result;
}

close(): Promise<void> {
// intentionally, a close will not effectively close the hid connection but
// will allow an auto-disconnection after some inactivity
this.preventAutoDisconnect = false;
return Promise.resolve();
}
}

0 comments on commit 585f125

Please sign in to comment.