smoldot 3.5.0 → 3.7.0
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.
- package/dist/cjs/internals/bytecode/wasm0.js +1 -1
- package/dist/cjs/internals/bytecode/wasm1.js +1 -1
- package/dist/cjs/internals/bytecode/wasm2.js +1 -1
- package/dist/cjs/internals/bytecode/wasm3.js +1 -1
- package/dist/cjs/internals/dns-over-https.d.ts +31 -0
- package/dist/cjs/internals/dns-over-https.js +255 -0
- package/dist/cjs/internals/local-instance.d.ts +7 -1
- package/dist/cjs/internals/local-instance.js +13 -3
- package/dist/cjs/no-auto-bytecode-browser.js +65 -13
- package/dist/cjs/no-auto-bytecode-nodejs.js +27 -10
- package/dist/mjs/internals/bytecode/wasm0.js +1 -1
- package/dist/mjs/internals/bytecode/wasm1.js +1 -1
- package/dist/mjs/internals/bytecode/wasm2.js +1 -1
- package/dist/mjs/internals/bytecode/wasm3.js +1 -1
- package/dist/mjs/internals/dns-over-https.d.ts +31 -0
- package/dist/mjs/internals/dns-over-https.js +249 -0
- package/dist/mjs/internals/local-instance.d.ts +7 -1
- package/dist/mjs/internals/local-instance.js +13 -3
- package/dist/mjs/no-auto-bytecode-browser.js +65 -13
- package/dist/mjs/no-auto-bytecode-nodejs.js +27 -10
- package/package.json +1 -1
|
@@ -14,6 +14,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.startWithBytecode = exports.QueueFullError = exports.JsonRpcDisabledError = exports.CrashError = exports.AlreadyDestroyedError = exports.AddChainError = void 0;
|
|
16
16
|
const client_js_1 = require("./internals/client.js");
|
|
17
|
+
const dns_over_https_js_1 = require("./internals/dns-over-https.js");
|
|
17
18
|
var public_types_js_1 = require("./public-types.js");
|
|
18
19
|
Object.defineProperty(exports, "AddChainError", { enumerable: true, get: function () { return public_types_js_1.AddChainError; } });
|
|
19
20
|
Object.defineProperty(exports, "AlreadyDestroyedError", { enumerable: true, get: function () { return public_types_js_1.AlreadyDestroyedError; } });
|
|
@@ -194,17 +195,46 @@ function connect(config) {
|
|
|
194
195
|
openOutSubstream: () => { throw new Error('Connection is closed'); },
|
|
195
196
|
};
|
|
196
197
|
}
|
|
197
|
-
const { targetPort,
|
|
198
|
+
const { targetPort, remoteTlsCertificateSha256 } = config.address;
|
|
198
199
|
const state = {
|
|
199
200
|
pc: undefined,
|
|
200
201
|
dataChannels: new Map(),
|
|
201
202
|
nextStreamId: 0,
|
|
202
203
|
isFirstOutSubstream: true,
|
|
204
|
+
dnsAbort: null,
|
|
203
205
|
};
|
|
206
|
+
// The SDP answer needs the literal IP address of the remote.
|
|
207
|
+
// Browsers expose no DNS API, so a multiaddress with a domain name is resolved over
|
|
208
|
+
// DNS-over-HTTPS. Resolution is started here and is only awaited in
|
|
209
|
+
// `onnegotiationneeded`, where the IP is actually needed.
|
|
210
|
+
let target;
|
|
211
|
+
if ("targetIp" in config.address) {
|
|
212
|
+
target = Promise.resolve({ targetIp: config.address.targetIp, ipVersion: config.address.ipVersion });
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const { hostname, family } = config.address;
|
|
216
|
+
const controller = new AbortController();
|
|
217
|
+
state.dnsAbort = controller;
|
|
218
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
219
|
+
const finished = () => {
|
|
220
|
+
clearTimeout(timeout);
|
|
221
|
+
state.dnsAbort = null;
|
|
222
|
+
};
|
|
223
|
+
target = (0, dns_over_https_js_1.resolveDnsOverHttps)(hostname, family, controller.signal).then((ip) => {
|
|
224
|
+
finished();
|
|
225
|
+
return { targetIp: ip, ipVersion: ip.includes(':') ? 6 : 4 };
|
|
226
|
+
}, (error) => {
|
|
227
|
+
finished();
|
|
228
|
+
throw error;
|
|
229
|
+
});
|
|
230
|
+
target.catch(() => { });
|
|
231
|
+
}
|
|
204
232
|
// Kills all the JavaScript objects (the connection and all its substreams), ensuring that no
|
|
205
233
|
// callback will be called again. Doesn't report anything to smoldot, as this should be done
|
|
206
234
|
// by the caller.
|
|
207
235
|
const killAllJs = () => {
|
|
236
|
+
if (state.dnsAbort)
|
|
237
|
+
state.dnsAbort.abort();
|
|
208
238
|
// The `RTCPeerConnection` is created pretty quickly. It is however still possible for
|
|
209
239
|
// smoldot to cancel the opening, in which case `pc` will still be undefined.
|
|
210
240
|
if (!state.pc) {
|
|
@@ -225,6 +255,18 @@ function connect(config) {
|
|
|
225
255
|
state.dataChannels.clear();
|
|
226
256
|
state.pc.close(); // Not necessarily necessary, but it doesn't hurt to do so.
|
|
227
257
|
};
|
|
258
|
+
// Due to <https://bugzilla.mozilla.org/show_bug.cgi?id=1659672>,
|
|
259
|
+
// connections from Firefox to a localhost WebRTC server always fail:
|
|
260
|
+
// Firefox gathers no loopback ICE candidate.
|
|
261
|
+
//
|
|
262
|
+
// Returns `true` if the connection was refused, in which case the caller must stop.
|
|
263
|
+
const refuseIfFirefoxLoopback = (ip) => {
|
|
264
|
+
if ((ip !== '127.0.0.1' && ip !== '::1') || navigator.userAgent.indexOf('Firefox') === -1)
|
|
265
|
+
return false;
|
|
266
|
+
killAllJs();
|
|
267
|
+
config.onConnectionReset("Firefox can't connect to a localhost WebRTC server");
|
|
268
|
+
return true;
|
|
269
|
+
};
|
|
228
270
|
// Function that configures a newly-opened channel and adds it to the map. Used for both
|
|
229
271
|
// inbound and outbound substreams.
|
|
230
272
|
const addChannel = (dataChannel, direction) => {
|
|
@@ -287,19 +329,10 @@ function connect(config) {
|
|
|
287
329
|
RTCPeerConnection.generateCertificate({ name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" }).then((localCertificate) => __awaiter(this, void 0, void 0, function* () {
|
|
288
330
|
if (state.pc === null)
|
|
289
331
|
return;
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
// term. In order to provider better user feedback, we straight up refuse connecting
|
|
294
|
-
// and stop the connection.
|
|
295
|
-
// Note that this is just a hint. Failing to detect this will lead to the WebRTC
|
|
296
|
-
// handshake timing out.
|
|
297
|
-
// TODO: eventually remove this if the Firefox bug is fixed
|
|
298
|
-
if ((targetIp == 'localhost' || targetIp == '127.0.0.1' || targetIp == '::1') && navigator.userAgent.indexOf('Firefox') !== -1) {
|
|
299
|
-
killAllJs();
|
|
300
|
-
config.onConnectionReset("Firefox can't connect to a localhost WebRTC server");
|
|
332
|
+
// A literal loopback address can be refused before anything is created. A domain
|
|
333
|
+
// name is checked once resolved, in `onnegotiationneeded`.
|
|
334
|
+
if ("targetIp" in config.address && refuseIfFirefoxLoopback(config.address.targetIp))
|
|
301
335
|
return;
|
|
302
|
-
}
|
|
303
336
|
// Create a new WebRTC connection.
|
|
304
337
|
state.pc = new RTCPeerConnection({ certificates: [localCertificate] });
|
|
305
338
|
// We need to build the multihash corresponding to the local certificate.
|
|
@@ -374,6 +407,25 @@ function connect(config) {
|
|
|
374
407
|
sdpOffer = sdpOffer.replace(/^a=ice-ufrag.*$/m, 'a=ice-ufrag:' + ufragPwd);
|
|
375
408
|
sdpOffer = sdpOffer.replace(/^a=ice-pwd.*$/m, 'a=ice-pwd:' + ufragPwd);
|
|
376
409
|
yield state.pc.setLocalDescription({ type: 'offer', sdp: sdpOffer });
|
|
410
|
+
// The answer needs the literal IP address of the remote, resolution was already stared.
|
|
411
|
+
let targetIp;
|
|
412
|
+
let ipVersion;
|
|
413
|
+
try {
|
|
414
|
+
({ targetIp, ipVersion } = yield target);
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
// `reset()` aborts the resolution, and a failure reported through
|
|
418
|
+
// `onconnectionstatechange` closes the connection.
|
|
419
|
+
if (state.pc.signalingState === "closed")
|
|
420
|
+
return;
|
|
421
|
+
killAllJs();
|
|
422
|
+
config.onConnectionReset("DNS resolution failed: " + (error instanceof Error ? error.message : String(error)));
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (state.pc.signalingState === "closed")
|
|
426
|
+
return;
|
|
427
|
+
if (refuseIfFirefoxLoopback(targetIp))
|
|
428
|
+
return;
|
|
377
429
|
// Transform certificate hash into fingerprint (upper-hex; each byte separated by ":").
|
|
378
430
|
const fingerprint = Array.from(remoteTlsCertificateSha256).map((n) => ("0" + n.toString(16)).slice(-2).toUpperCase()).join(':');
|
|
379
431
|
// Note that the trailing line feed is important, as otherwise Chrome
|
|
@@ -39,6 +39,17 @@ function startWithBytecode(options) {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
exports.startWithBytecode = startWithBytecode;
|
|
42
|
+
/**
|
|
43
|
+
* Human-readable reason for a socket error. An `AggregateError` (every address of a
|
|
44
|
+
* multi-address host failed) has an empty `message`; its causes are in `errors`.
|
|
45
|
+
*/
|
|
46
|
+
function errorReason(error) {
|
|
47
|
+
var _a;
|
|
48
|
+
return ((_a = error.errors) === null || _a === void 0 ? void 0 : _a.map((cause) => cause.message).filter(Boolean).join("; "))
|
|
49
|
+
|| error.message
|
|
50
|
+
|| error.code
|
|
51
|
+
|| "Error";
|
|
52
|
+
}
|
|
42
53
|
/**
|
|
43
54
|
* Tries to open a new connection using the given configuration.
|
|
44
55
|
*
|
|
@@ -86,7 +97,7 @@ function connect(config) {
|
|
|
86
97
|
socket.onerror = () => { };
|
|
87
98
|
};
|
|
88
99
|
socket.onerror = (event) => {
|
|
89
|
-
config.onConnectionReset(event.
|
|
100
|
+
config.onConnectionReset(errorReason(event.error));
|
|
90
101
|
socket.onopen = () => { };
|
|
91
102
|
socket.onclose = () => { };
|
|
92
103
|
socket.onmessage = () => { };
|
|
@@ -126,30 +137,35 @@ function connect(config) {
|
|
|
126
137
|
});
|
|
127
138
|
// Number of bytes queued using `socket.write` and where `write` has returned false.
|
|
128
139
|
const drainingBytes = { num: 0 };
|
|
140
|
+
// Set by `reset()`. Events that arrive after that must be ignored. This can't be
|
|
141
|
+
// `socket.destroyed`: NodeJS marks the socket destroyed before emitting `close`, so
|
|
142
|
+
// that check would swallow every close, including remote ones.
|
|
143
|
+
let resetCalled = false;
|
|
144
|
+
// Message of the last `error` event, reported through the `close` event that follows it.
|
|
145
|
+
let lastError = null;
|
|
129
146
|
socket.setNoDelay();
|
|
130
147
|
socket.on('connect', () => {
|
|
131
|
-
if (
|
|
148
|
+
if (resetCalled)
|
|
132
149
|
return;
|
|
133
150
|
config.onWritableBytes(socket.writableHighWaterMark);
|
|
134
151
|
});
|
|
152
|
+
socket.on('error', (error) => {
|
|
153
|
+
lastError = errorReason(error);
|
|
154
|
+
});
|
|
135
155
|
socket.on('close', (hasError) => {
|
|
136
|
-
if (
|
|
156
|
+
if (resetCalled)
|
|
137
157
|
return;
|
|
138
|
-
|
|
139
|
-
// whether it was caused by an error.
|
|
140
|
-
const message = hasError ? "Error" : "Closed gracefully";
|
|
141
|
-
config.onConnectionReset(message);
|
|
158
|
+
config.onConnectionReset(lastError !== null && lastError !== void 0 ? lastError : (hasError ? "Error" : "Closed gracefully"));
|
|
142
159
|
});
|
|
143
|
-
socket.on('error', () => { });
|
|
144
160
|
socket.on('data', (message) => {
|
|
145
|
-
if (
|
|
161
|
+
if (resetCalled)
|
|
146
162
|
return;
|
|
147
163
|
config.onMessage(new Uint8Array(message.buffer));
|
|
148
164
|
});
|
|
149
165
|
socket.on('drain', () => {
|
|
150
166
|
// The bytes queued using `socket.write` and where `write` has returned false have now
|
|
151
167
|
// been sent. Notify the API that it can write more data.
|
|
152
|
-
if (
|
|
168
|
+
if (resetCalled)
|
|
153
169
|
return;
|
|
154
170
|
const val = drainingBytes.num;
|
|
155
171
|
drainingBytes.num = 0;
|
|
@@ -157,6 +173,7 @@ function connect(config) {
|
|
|
157
173
|
});
|
|
158
174
|
return {
|
|
159
175
|
reset: () => {
|
|
176
|
+
resetCalled = true;
|
|
160
177
|
socket.destroy();
|
|
161
178
|
},
|
|
162
179
|
send: (data) => {
|