@ledgerhq/hw-transport-http 6.24.1 → 6.25.1-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//@flow
|
|
2
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
3
|
+
import { TransportError } from "@ledgerhq/errors";
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
import { log } from "@ledgerhq/logs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* HTTP transport implementation
|
|
9
|
+
*/
|
|
10
|
+
export default class HttpTransport extends Transport<string> {
|
|
11
|
+
static isSupported = (): Promise<boolean> =>
|
|
12
|
+
Promise.resolve(typeof fetch === "function");
|
|
13
|
+
|
|
14
|
+
// this transport is not discoverable
|
|
15
|
+
static list = (): * => Promise.resolve([]);
|
|
16
|
+
static listen = (_observer: *) => ({
|
|
17
|
+
unsubscribe: () => {},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
static check = async (url: string, timeout: number = 5000) => {
|
|
21
|
+
const response = await axios({ url, timeout });
|
|
22
|
+
if (response.status !== 200) {
|
|
23
|
+
throw new TransportError(
|
|
24
|
+
"failed to access HttpTransport(" +
|
|
25
|
+
url +
|
|
26
|
+
"): status " +
|
|
27
|
+
response.status,
|
|
28
|
+
"HttpTransportNotAccessible"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
static async open(url: string, timeout?: number) {
|
|
34
|
+
await HttpTransport.check(url, timeout);
|
|
35
|
+
return new HttpTransport(url);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
url: string;
|
|
39
|
+
|
|
40
|
+
constructor(url: string) {
|
|
41
|
+
super();
|
|
42
|
+
this.url = url;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async exchange(apdu: Buffer): Promise<Buffer> {
|
|
46
|
+
const apduHex = apdu.toString("hex");
|
|
47
|
+
log("apdu", "=> " + apduHex);
|
|
48
|
+
const response = await axios({
|
|
49
|
+
method: "POST",
|
|
50
|
+
url: this.url,
|
|
51
|
+
headers: {
|
|
52
|
+
Accept: "application/json",
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
},
|
|
55
|
+
data: JSON.stringify({ apduHex }),
|
|
56
|
+
});
|
|
57
|
+
if (response.status !== 200) {
|
|
58
|
+
throw new TransportError(
|
|
59
|
+
"failed to communicate to server. code=" + response.status,
|
|
60
|
+
"HttpTransportStatus" + response.status
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const body = await response.data;
|
|
64
|
+
if (body.error) throw body.error;
|
|
65
|
+
log("apdu", "<= " + body.data);
|
|
66
|
+
return Buffer.from(body.data, "hex");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setScrambleKey() {}
|
|
70
|
+
|
|
71
|
+
close(): Promise<void> {
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//@flow
|
|
2
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
3
|
+
import { TransportError } from "@ledgerhq/errors";
|
|
4
|
+
import { log } from "@ledgerhq/logs";
|
|
5
|
+
|
|
6
|
+
const WebSocket = global.WebSocket || require("ws");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* WebSocket transport implementation
|
|
10
|
+
*/
|
|
11
|
+
export default class WebSocketTransport extends Transport<string> {
|
|
12
|
+
static isSupported = (): Promise<boolean> =>
|
|
13
|
+
Promise.resolve(typeof WebSocket === "function");
|
|
14
|
+
|
|
15
|
+
// this transport is not discoverable
|
|
16
|
+
static list = (): * => Promise.resolve([]);
|
|
17
|
+
static listen = (_observer: *) => ({
|
|
18
|
+
unsubscribe: () => {},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
static check = async (url: string, timeout: number = 5000) =>
|
|
22
|
+
new Promise((resolve, reject) => {
|
|
23
|
+
const socket = new WebSocket(url);
|
|
24
|
+
let success = false;
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
socket.close();
|
|
27
|
+
}, timeout);
|
|
28
|
+
socket.onopen = () => {
|
|
29
|
+
success = true;
|
|
30
|
+
socket.close();
|
|
31
|
+
};
|
|
32
|
+
socket.onclose = () => {
|
|
33
|
+
if (success) resolve();
|
|
34
|
+
else {
|
|
35
|
+
reject(
|
|
36
|
+
new TransportError(
|
|
37
|
+
"failed to access WebSocketTransport(" + url + ")",
|
|
38
|
+
"WebSocketTransportNotAccessible"
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
socket.onerror = () => {
|
|
44
|
+
reject(
|
|
45
|
+
new TransportError(
|
|
46
|
+
"failed to access WebSocketTransport(" + url + "): error",
|
|
47
|
+
"WebSocketTransportNotAccessible"
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
static async open(url: string) {
|
|
54
|
+
const exchangeMethods = await new Promise((resolve, reject) => {
|
|
55
|
+
try {
|
|
56
|
+
const socket = new WebSocket(url);
|
|
57
|
+
const exchangeMethods = {
|
|
58
|
+
resolveExchange: (_b: Buffer) => {},
|
|
59
|
+
rejectExchange: (_e: *) => {},
|
|
60
|
+
onDisconnect: () => {},
|
|
61
|
+
close: () => socket.close(),
|
|
62
|
+
send: (msg) => socket.send(msg),
|
|
63
|
+
};
|
|
64
|
+
socket.onopen = () => {
|
|
65
|
+
socket.send("open");
|
|
66
|
+
};
|
|
67
|
+
socket.onerror = (e) => {
|
|
68
|
+
exchangeMethods.onDisconnect();
|
|
69
|
+
reject(e);
|
|
70
|
+
};
|
|
71
|
+
socket.onclose = () => {
|
|
72
|
+
exchangeMethods.onDisconnect();
|
|
73
|
+
reject(new TransportError("OpenFailed", "OpenFailed"));
|
|
74
|
+
};
|
|
75
|
+
socket.onmessage = (e) => {
|
|
76
|
+
if (typeof e.data !== "string") return;
|
|
77
|
+
const data = JSON.parse(e.data);
|
|
78
|
+
switch (data.type) {
|
|
79
|
+
case "opened":
|
|
80
|
+
return resolve(exchangeMethods);
|
|
81
|
+
case "error":
|
|
82
|
+
reject(new Error(data.error));
|
|
83
|
+
return exchangeMethods.rejectExchange(
|
|
84
|
+
new TransportError(data.error, "WSError")
|
|
85
|
+
);
|
|
86
|
+
case "response":
|
|
87
|
+
return exchangeMethods.resolveExchange(
|
|
88
|
+
Buffer.from(data.data, "hex")
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
} catch (e) {
|
|
93
|
+
reject(e);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return new WebSocketTransport(exchangeMethods);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
hook: *;
|
|
100
|
+
|
|
101
|
+
constructor(hook: *) {
|
|
102
|
+
super();
|
|
103
|
+
this.hook = hook;
|
|
104
|
+
hook.onDisconnect = () => {
|
|
105
|
+
this.emit("disconnect");
|
|
106
|
+
this.hook.rejectExchange(
|
|
107
|
+
new TransportError("WebSocket disconnected", "WSDisconnect")
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async exchange(apdu: Buffer): Promise<Buffer> {
|
|
113
|
+
const hex = apdu.toString("hex");
|
|
114
|
+
log("apdu", "=> " + hex);
|
|
115
|
+
const res = await new Promise((resolve, reject) => {
|
|
116
|
+
this.hook.rejectExchange = (e: *) => reject(e);
|
|
117
|
+
this.hook.resolveExchange = (b: Buffer) => resolve(b);
|
|
118
|
+
this.hook.send(hex);
|
|
119
|
+
});
|
|
120
|
+
log("apdu", "<= " + res.toString("hex"));
|
|
121
|
+
return res;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
setScrambleKey() {}
|
|
125
|
+
|
|
126
|
+
async close() {
|
|
127
|
+
this.hook.close();
|
|
128
|
+
return new Promise((success) => {
|
|
129
|
+
setTimeout(success, 200);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import HttpTransport from "./HttpTransport";
|
|
3
|
+
import WebSocketTransport from "./WebSocketTransport";
|
|
4
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
5
|
+
import type {
|
|
6
|
+
Observer,
|
|
7
|
+
DescriptorEvent,
|
|
8
|
+
Subscription,
|
|
9
|
+
} from "@ledgerhq/hw-transport";
|
|
10
|
+
|
|
11
|
+
const getTransport = (url) =>
|
|
12
|
+
!url.startsWith("ws") ? HttpTransport : WebSocketTransport;
|
|
13
|
+
|
|
14
|
+
type InS = string | string[];
|
|
15
|
+
type InP = Promise<InS> | InS;
|
|
16
|
+
type In = InP | (() => InP);
|
|
17
|
+
|
|
18
|
+
const inferURLs = async (urls: In): Promise<string[]> => {
|
|
19
|
+
const r = await (typeof urls === "function" ? urls() : urls);
|
|
20
|
+
return typeof r === "string" ? [r] : r;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default (urls: In): Class<Transport<string>> => {
|
|
24
|
+
class StaticTransport extends Transport<string> {
|
|
25
|
+
static isSupported = HttpTransport.isSupported;
|
|
26
|
+
|
|
27
|
+
static list = (): Promise<*[]> =>
|
|
28
|
+
inferURLs(urls)
|
|
29
|
+
.then((urls) =>
|
|
30
|
+
Promise.all(
|
|
31
|
+
urls.map((url) =>
|
|
32
|
+
getTransport(url)
|
|
33
|
+
.check(url)
|
|
34
|
+
.then(() => [url])
|
|
35
|
+
.catch(() => [])
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
.then((arrs) => arrs.reduce((acc, a) => acc.concat(a), []));
|
|
40
|
+
|
|
41
|
+
static listen = (observer: Observer<DescriptorEvent<*>>): Subscription => {
|
|
42
|
+
let unsubscribed = false;
|
|
43
|
+
const seen = {};
|
|
44
|
+
function checkLoop() {
|
|
45
|
+
if (unsubscribed) return;
|
|
46
|
+
inferURLs(urls)
|
|
47
|
+
.then((urls) =>
|
|
48
|
+
Promise.all(
|
|
49
|
+
urls.map(async (url) => {
|
|
50
|
+
if (unsubscribed) return;
|
|
51
|
+
try {
|
|
52
|
+
await getTransport(url).check(url);
|
|
53
|
+
if (unsubscribed) return;
|
|
54
|
+
if (!seen[url]) {
|
|
55
|
+
seen[url] = 1;
|
|
56
|
+
observer.next({ type: "add", descriptor: url });
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// nothing
|
|
60
|
+
if (seen[url]) {
|
|
61
|
+
delete seen[url];
|
|
62
|
+
observer.next({ type: "remove", descriptor: url });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
.then(() => new Promise((success) => setTimeout(success, 5000)))
|
|
69
|
+
.then(checkLoop);
|
|
70
|
+
}
|
|
71
|
+
checkLoop();
|
|
72
|
+
return {
|
|
73
|
+
unsubscribe: () => {
|
|
74
|
+
unsubscribed = true;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
static open = (url) => getTransport(url).open(url);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return StaticTransport;
|
|
83
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/hw-transport-http",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.25.1-alpha.3+eb669e17",
|
|
4
4
|
"description": "Ledger Hardware Wallet communication layer over http",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@ledgerhq/errors": "^6.10.0",
|
|
31
|
-
"@ledgerhq/hw-transport": "^6.
|
|
31
|
+
"@ledgerhq/hw-transport": "^6.25.1-alpha.3+eb669e17",
|
|
32
32
|
"@ledgerhq/logs": "^6.10.0",
|
|
33
33
|
"axios": "^0.24.0",
|
|
34
34
|
"ws": "7"
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"build": "bash ../../script/build.sh",
|
|
39
39
|
"watch": "bash ../../script/watch.sh"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "eb669e17dd87d3ab568beab1f9a5ddb1a2536e83"
|
|
42
42
|
}
|