smoldot 1.0.0 → 1.0.1
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/index-browser.js +66 -19
- package/dist/cjs/index-deno.js +79 -56
- package/dist/cjs/index-nodejs.js +91 -40
- package/dist/cjs/instance/autogen/wasm0.js +1 -1
- package/dist/cjs/instance/autogen/wasm1.js +1 -1
- package/dist/cjs/instance/autogen/wasm2.js +1 -1
- package/dist/cjs/instance/bindings-smoldot-light.d.ts +36 -2
- package/dist/cjs/instance/bindings-smoldot-light.js +18 -4
- package/dist/cjs/instance/bindings.d.ts +3 -2
- package/dist/mjs/index-browser.js +66 -19
- package/dist/mjs/index-deno.js +79 -56
- package/dist/mjs/index-nodejs.js +91 -40
- package/dist/mjs/instance/autogen/wasm0.js +1 -1
- package/dist/mjs/instance/autogen/wasm1.js +1 -1
- package/dist/mjs/instance/autogen/wasm2.js +1 -1
- package/dist/mjs/instance/bindings-smoldot-light.d.ts +36 -2
- package/dist/mjs/instance/bindings-smoldot-light.js +18 -4
- package/dist/mjs/instance/bindings.d.ts +3 -2
- package/package.json +2 -2
|
@@ -63,12 +63,33 @@ export interface Connection {
|
|
|
63
63
|
/**
|
|
64
64
|
* Queues data to be sent on the given connection.
|
|
65
65
|
*
|
|
66
|
-
* The connection must currently be in the `Open` state.
|
|
66
|
+
* The connection and stream must currently be in the `Open` state.
|
|
67
|
+
*
|
|
68
|
+
* The number of bytes most never exceed the number of "writable bytes" of the stream.
|
|
69
|
+
* `onWritableBytes` can be used in order to notify that more writable bytes are available.
|
|
67
70
|
*
|
|
68
71
|
* The `streamId` must be provided if and only if the connection is of type "multi-stream".
|
|
69
72
|
* It indicates which substream to send the data on.
|
|
73
|
+
*
|
|
74
|
+
* Must not be called after `closeSend` has been called.
|
|
70
75
|
*/
|
|
71
76
|
send(data: Uint8Array, streamId?: number): void;
|
|
77
|
+
/**
|
|
78
|
+
* Closes the writing side of the given stream of the given connection.
|
|
79
|
+
*
|
|
80
|
+
* Never called for connection types where this isn't possible to implement (i.e. WebSocket
|
|
81
|
+
* and WebRTC at the moment).
|
|
82
|
+
*
|
|
83
|
+
* The connection and stream must currently be in the `Open` state.
|
|
84
|
+
*
|
|
85
|
+
* Implicitly sets the "writable bytes" of the stream to zero.
|
|
86
|
+
*
|
|
87
|
+
* The `streamId` must be provided if and only if the connection is of type "multi-stream".
|
|
88
|
+
* It indicates which substream to send the data on.
|
|
89
|
+
*
|
|
90
|
+
* Must only be called once per stream.
|
|
91
|
+
*/
|
|
92
|
+
closeSend(streamId?: number): void;
|
|
72
93
|
/**
|
|
73
94
|
* Start opening an additional outbound substream on the given connection.
|
|
74
95
|
*
|
|
@@ -104,6 +125,8 @@ export interface ConnectionConfig {
|
|
|
104
125
|
onOpen: (info: {
|
|
105
126
|
type: 'single-stream';
|
|
106
127
|
handshake: 'multistream-select-noise-yamux';
|
|
128
|
+
initialWritableBytes: number;
|
|
129
|
+
writeClosable: boolean;
|
|
107
130
|
} | {
|
|
108
131
|
type: 'multi-stream';
|
|
109
132
|
handshake: 'webrtc';
|
|
@@ -121,7 +144,7 @@ export interface ConnectionConfig {
|
|
|
121
144
|
*
|
|
122
145
|
* This function must only be called for connections of type "multi-stream".
|
|
123
146
|
*/
|
|
124
|
-
onStreamOpened: (streamId: number, direction: 'inbound' | 'outbound') => void;
|
|
147
|
+
onStreamOpened: (streamId: number, direction: 'inbound' | 'outbound', initialWritableBytes: number) => void;
|
|
125
148
|
/**
|
|
126
149
|
* Callback called when a stream transitions to the `Reset` state.
|
|
127
150
|
*
|
|
@@ -130,6 +153,17 @@ export interface ConnectionConfig {
|
|
|
130
153
|
* This function must only be called for connections of type "multi-stream".
|
|
131
154
|
*/
|
|
132
155
|
onStreamReset: (streamId: number) => void;
|
|
156
|
+
/**
|
|
157
|
+
* Callback called when more data can be written on the stream.
|
|
158
|
+
*
|
|
159
|
+
* Can only happen while the connection is in the `Open` state.
|
|
160
|
+
*
|
|
161
|
+
* This callback must not be called after `closeSend` has been called.
|
|
162
|
+
*
|
|
163
|
+
* The `streamId` parameter must be provided if and only if the connection is of type
|
|
164
|
+
* "multi-stream".
|
|
165
|
+
*/
|
|
166
|
+
onWritableBytes: (numExtra: number, streamId?: number) => void;
|
|
133
167
|
/**
|
|
134
168
|
* Callback called when a message sent by the remote has been received.
|
|
135
169
|
*
|
|
@@ -97,7 +97,7 @@ function default_1(config) {
|
|
|
97
97
|
// In browsers, `setTimeout` works as expected when `ms` equals 0. However, NodeJS
|
|
98
98
|
// requires a minimum of 1 millisecond (if `0` is passed, it is automatically replaced
|
|
99
99
|
// with `1`) and wants you to use `setImmediate` instead.
|
|
100
|
-
if (ms
|
|
100
|
+
if (ms < 1 && typeof setImmediate === "function") {
|
|
101
101
|
setImmediate(() => {
|
|
102
102
|
if (killedTracked.killed)
|
|
103
103
|
return;
|
|
@@ -140,7 +140,7 @@ function default_1(config) {
|
|
|
140
140
|
try {
|
|
141
141
|
switch (info.type) {
|
|
142
142
|
case 'single-stream': {
|
|
143
|
-
instance.exports.connection_open_single_stream(connectionId, 0);
|
|
143
|
+
instance.exports.connection_open_single_stream(connectionId, 0, info.initialWritableBytes, info.writeClosable ? 1 : 0);
|
|
144
144
|
break;
|
|
145
145
|
}
|
|
146
146
|
case 'multi-stream': {
|
|
@@ -168,6 +168,14 @@ function default_1(config) {
|
|
|
168
168
|
}
|
|
169
169
|
catch (_error) { }
|
|
170
170
|
},
|
|
171
|
+
onWritableBytes: (numExtra, streamId) => {
|
|
172
|
+
if (killedTracked.killed)
|
|
173
|
+
return;
|
|
174
|
+
try {
|
|
175
|
+
instance.exports.stream_writable_bytes(connectionId, streamId || 0, numExtra);
|
|
176
|
+
}
|
|
177
|
+
catch (_error) { }
|
|
178
|
+
},
|
|
171
179
|
onMessage: (message, streamId) => {
|
|
172
180
|
if (killedTracked.killed)
|
|
173
181
|
return;
|
|
@@ -178,11 +186,11 @@ function default_1(config) {
|
|
|
178
186
|
}
|
|
179
187
|
catch (_error) { }
|
|
180
188
|
},
|
|
181
|
-
onStreamOpened: (streamId, direction) => {
|
|
189
|
+
onStreamOpened: (streamId, direction, initialWritableBytes) => {
|
|
182
190
|
if (killedTracked.killed)
|
|
183
191
|
return;
|
|
184
192
|
try {
|
|
185
|
-
instance.exports.connection_stream_opened(connectionId, streamId, direction === 'outbound' ? 1 : 0);
|
|
193
|
+
instance.exports.connection_stream_opened(connectionId, streamId, direction === 'outbound' ? 1 : 0, initialWritableBytes);
|
|
186
194
|
}
|
|
187
195
|
catch (_error) { }
|
|
188
196
|
},
|
|
@@ -244,6 +252,12 @@ function default_1(config) {
|
|
|
244
252
|
const connection = connections[connectionId];
|
|
245
253
|
connection.send(data, streamId); // TODO: docs says the streamId is provided only for multi-stream connections, but here it's always provided
|
|
246
254
|
},
|
|
255
|
+
stream_send_close: (connectionId, streamId) => {
|
|
256
|
+
if (killedTracked.killed)
|
|
257
|
+
return;
|
|
258
|
+
const connection = connections[connectionId];
|
|
259
|
+
connection.closeSend(streamId); // TODO: docs says the streamId is provided only for multi-stream connections, but here it's always provided
|
|
260
|
+
},
|
|
247
261
|
current_task_entered: (ptr, len) => {
|
|
248
262
|
if (killedTracked.killed)
|
|
249
263
|
return;
|
|
@@ -19,10 +19,11 @@ export interface SmoldotWasmExports extends WebAssembly.Exports {
|
|
|
19
19
|
json_rpc_responses_peek: (chainId: number) => number;
|
|
20
20
|
json_rpc_responses_pop: (chainId: number) => void;
|
|
21
21
|
timer_finished: (timerId: number) => void;
|
|
22
|
-
connection_open_single_stream: (connectionId: number, handshakeTy: number) => void;
|
|
22
|
+
connection_open_single_stream: (connectionId: number, handshakeTy: number, initialWritableBytes: number, writeClosable: number) => void;
|
|
23
23
|
connection_open_multi_stream: (connectionId: number, handshakeTyPtr: number, handshakeTyLen: number) => void;
|
|
24
|
+
stream_writable_bytes: (connectionId: number, streamId: number, numBytes: number) => void;
|
|
24
25
|
stream_message: (connectionId: number, streamId: number, ptr: number, len: number) => void;
|
|
25
|
-
connection_stream_opened: (connectionId: number, streamId: number, outbound: number) => void;
|
|
26
|
+
connection_stream_opened: (connectionId: number, streamId: number, outbound: number, initialWritableBytes: number) => void;
|
|
26
27
|
connection_reset: (connectionId: number, ptr: number, len: number) => void;
|
|
27
28
|
stream_reset: (connectionId: number, streamId: number) => void;
|
|
28
29
|
}
|
|
@@ -40,6 +40,8 @@ export function start(options) {
|
|
|
40
40
|
return Promise.resolve(inflate(classicDecode(input)));
|
|
41
41
|
},
|
|
42
42
|
registerShouldPeriodicallyYield: (callback) => {
|
|
43
|
+
if (typeof document === 'undefined') // We might be in a web worker.
|
|
44
|
+
return [false, () => { }];
|
|
43
45
|
const wrappedCallback = () => callback(document.visibilityState === 'visible');
|
|
44
46
|
document.addEventListener('visibilitychange', wrappedCallback);
|
|
45
47
|
return [document.visibilityState === 'visible', () => { document.removeEventListener('visibilitychange', wrappedCallback); }];
|
|
@@ -68,9 +70,8 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
68
70
|
// Attempt to parse the multiaddress.
|
|
69
71
|
// TODO: remove support for `/wss` in a long time (https://github.com/paritytech/smoldot/issues/1940)
|
|
70
72
|
const wsParsed = config.address.match(/^\/(ip4|ip6|dns4|dns6|dns)\/(.*?)\/tcp\/(.*?)\/(ws|wss|tls\/ws)$/);
|
|
71
|
-
const webRTCParsed = config.address.match(/^\/(ip4|ip6)\/(.*?)\/udp\/(.*?)\/webrtc\/certhash\/(.*?)$/);
|
|
73
|
+
const webRTCParsed = config.address.match(/^\/(ip4|ip6)\/(.*?)\/udp\/(.*?)\/webrtc-direct\/certhash\/(.*?)$/);
|
|
72
74
|
if (wsParsed != null) {
|
|
73
|
-
let connection;
|
|
74
75
|
const proto = (wsParsed[4] == 'ws') ? 'ws' : 'wss';
|
|
75
76
|
if ((proto == 'ws' && forbidWs) ||
|
|
76
77
|
(proto == 'ws' && wsParsed[2] != 'localhost' && wsParsed[2] != '127.0.0.1' && forbidNonLocalWs) ||
|
|
@@ -80,10 +81,36 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
80
81
|
const url = (wsParsed[1] == 'ip6') ?
|
|
81
82
|
(proto + "://[" + wsParsed[2] + "]:" + wsParsed[3]) :
|
|
82
83
|
(proto + "://" + wsParsed[2] + ":" + wsParsed[3]);
|
|
83
|
-
connection = new WebSocket(url);
|
|
84
|
+
const connection = new WebSocket(url);
|
|
84
85
|
connection.binaryType = 'arraybuffer';
|
|
86
|
+
const bufferedAmountCheck = { quenedUnreportedBytes: 0, nextTimeout: 10 };
|
|
87
|
+
const checkBufferedAmount = () => {
|
|
88
|
+
if (connection.readyState != 1)
|
|
89
|
+
return;
|
|
90
|
+
// Note that we might expect `bufferedAmount` to always be <= the sum of the lengths
|
|
91
|
+
// of all the data that has been sent, but that might not be the case. For this
|
|
92
|
+
// reason, we use `bufferedAmount` as a hint rather than a correct value.
|
|
93
|
+
const bufferedAmount = connection.bufferedAmount;
|
|
94
|
+
let wasSent = bufferedAmountCheck.quenedUnreportedBytes - bufferedAmount;
|
|
95
|
+
if (wasSent < 0)
|
|
96
|
+
wasSent = 0;
|
|
97
|
+
bufferedAmountCheck.quenedUnreportedBytes -= wasSent;
|
|
98
|
+
if (bufferedAmountCheck.quenedUnreportedBytes != 0) {
|
|
99
|
+
setTimeout(checkBufferedAmount, bufferedAmountCheck.nextTimeout);
|
|
100
|
+
bufferedAmountCheck.nextTimeout *= 2;
|
|
101
|
+
if (bufferedAmountCheck.nextTimeout > 500)
|
|
102
|
+
bufferedAmountCheck.nextTimeout = 500;
|
|
103
|
+
}
|
|
104
|
+
// Note: it is important to call `onWritableBytes` at the very end, as it might
|
|
105
|
+
// trigger a call to `send`.
|
|
106
|
+
if (wasSent != 0)
|
|
107
|
+
config.onWritableBytes(wasSent);
|
|
108
|
+
};
|
|
85
109
|
connection.onopen = () => {
|
|
86
|
-
config.onOpen({
|
|
110
|
+
config.onOpen({
|
|
111
|
+
type: 'single-stream', handshake: 'multistream-select-noise-yamux',
|
|
112
|
+
initialWritableBytes: 1024 * 1024, writeClosable: false,
|
|
113
|
+
});
|
|
87
114
|
};
|
|
88
115
|
connection.onclose = (event) => {
|
|
89
116
|
const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
|
|
@@ -102,7 +129,13 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
102
129
|
},
|
|
103
130
|
send: (data) => {
|
|
104
131
|
connection.send(data);
|
|
132
|
+
if (bufferedAmountCheck.quenedUnreportedBytes == 0) {
|
|
133
|
+
bufferedAmountCheck.nextTimeout = 10;
|
|
134
|
+
setTimeout(checkBufferedAmount, 10);
|
|
135
|
+
}
|
|
136
|
+
bufferedAmountCheck.quenedUnreportedBytes += data.length;
|
|
105
137
|
},
|
|
138
|
+
closeSend: () => { throw new Error('Wrong connection type'); },
|
|
106
139
|
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
107
140
|
};
|
|
108
141
|
}
|
|
@@ -152,16 +185,18 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
152
185
|
pc.onnegotiationneeded = null;
|
|
153
186
|
pc.ondatachannel = null;
|
|
154
187
|
for (const channel of Array.from(dataChannels.values())) {
|
|
155
|
-
channel.onopen = null;
|
|
156
|
-
channel.onerror = null;
|
|
157
|
-
channel.onclose = null;
|
|
158
|
-
channel.
|
|
188
|
+
channel.channel.onopen = null;
|
|
189
|
+
channel.channel.onerror = null;
|
|
190
|
+
channel.channel.onclose = null;
|
|
191
|
+
channel.channel.onbufferedamountlow = null;
|
|
192
|
+
channel.channel.onmessage = null;
|
|
159
193
|
}
|
|
160
194
|
dataChannels.clear();
|
|
161
195
|
if (handshakeDataChannel) {
|
|
162
196
|
handshakeDataChannel.onopen = null;
|
|
163
197
|
handshakeDataChannel.onerror = null;
|
|
164
198
|
handshakeDataChannel.onclose = null;
|
|
199
|
+
handshakeDataChannel.onbufferedamountlow = null;
|
|
165
200
|
handshakeDataChannel.onmessage = null;
|
|
166
201
|
}
|
|
167
202
|
handshakeDataChannel = undefined;
|
|
@@ -190,7 +225,7 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
190
225
|
}
|
|
191
226
|
else {
|
|
192
227
|
console.assert(direction !== 'outbound' || !handshakeDataChannel, "handshakeDataChannel still defined");
|
|
193
|
-
config.onStreamOpened(dataChannelId, direction);
|
|
228
|
+
config.onStreamOpened(dataChannelId, direction, 65536);
|
|
194
229
|
}
|
|
195
230
|
};
|
|
196
231
|
dataChannel.onerror = dataChannel.onclose = (_error) => {
|
|
@@ -212,6 +247,7 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
212
247
|
handshakeDataChannel.onopen = null;
|
|
213
248
|
handshakeDataChannel.onerror = null;
|
|
214
249
|
handshakeDataChannel.onclose = null;
|
|
250
|
+
handshakeDataChannel.onbufferedamountlow = null;
|
|
215
251
|
handshakeDataChannel.onmessage = null;
|
|
216
252
|
handshakeDataChannel = undefined;
|
|
217
253
|
}
|
|
@@ -228,12 +264,18 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
228
264
|
config.onStreamReset(dataChannelId);
|
|
229
265
|
}
|
|
230
266
|
};
|
|
267
|
+
dataChannel.onbufferedamountlow = () => {
|
|
268
|
+
const channel = dataChannels.get(dataChannelId);
|
|
269
|
+
const val = channel.bufferedBytes;
|
|
270
|
+
channel.bufferedBytes = 0;
|
|
271
|
+
config.onWritableBytes(val, dataChannelId);
|
|
272
|
+
};
|
|
231
273
|
dataChannel.onmessage = (m) => {
|
|
232
274
|
// The `data` field is an `ArrayBuffer`.
|
|
233
275
|
config.onMessage(new Uint8Array(m.data), dataChannelId);
|
|
234
276
|
};
|
|
235
277
|
if (direction !== 'first-outbound')
|
|
236
|
-
dataChannels.set(dataChannelId, dataChannel);
|
|
278
|
+
dataChannels.set(dataChannelId, { channel: dataChannel, bufferedBytes: 0 });
|
|
237
279
|
else
|
|
238
280
|
handshakeDataChannel = dataChannel;
|
|
239
281
|
};
|
|
@@ -375,7 +417,8 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
375
417
|
// (UDP or TCP)
|
|
376
418
|
"a=sctp-port:5000" + "\n" +
|
|
377
419
|
// The maximum SCTP user message size (in bytes) (RFC8841)
|
|
378
|
-
|
|
420
|
+
// Setting this field is part of the libp2p spec.
|
|
421
|
+
"a=max-message-size:16384" + "\n" +
|
|
379
422
|
// A transport address for a candidate that can be used for connectivity
|
|
380
423
|
// checks (RFC8839).
|
|
381
424
|
"a=candidate:1 1 UDP 1 " + targetIp + " " + targetPort + " typ host" + "\n";
|
|
@@ -404,17 +447,21 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
404
447
|
}
|
|
405
448
|
else {
|
|
406
449
|
const channel = dataChannels.get(streamId);
|
|
407
|
-
channel.onopen = null;
|
|
408
|
-
channel.onerror = null;
|
|
409
|
-
channel.onclose = null;
|
|
410
|
-
channel.
|
|
411
|
-
channel.
|
|
450
|
+
channel.channel.onopen = null;
|
|
451
|
+
channel.channel.onerror = null;
|
|
452
|
+
channel.channel.onclose = null;
|
|
453
|
+
channel.channel.onbufferedamountlow = null;
|
|
454
|
+
channel.channel.onmessage = null;
|
|
455
|
+
channel.channel.close();
|
|
412
456
|
dataChannels.delete(streamId);
|
|
413
457
|
}
|
|
414
458
|
},
|
|
415
459
|
send: (data, streamId) => {
|
|
416
|
-
dataChannels.get(streamId)
|
|
460
|
+
const channel = dataChannels.get(streamId);
|
|
461
|
+
channel.channel.send(data);
|
|
462
|
+
channel.bufferedBytes += data.length;
|
|
417
463
|
},
|
|
464
|
+
closeSend: () => { throw new Error('Wrong connection type'); },
|
|
418
465
|
openOutSubstream: () => {
|
|
419
466
|
// `openOutSubstream` can only be called after we have called `config.onOpen`, therefore
|
|
420
467
|
// `pc` is guaranteed to be non-null.
|
|
@@ -426,8 +473,8 @@ function connect(config, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRTC) {
|
|
|
426
473
|
// We need to check again if `handshakeDataChannel` is still defined, as the
|
|
427
474
|
// connection might have been closed.
|
|
428
475
|
if (handshakeDataChannel) {
|
|
429
|
-
config.onStreamOpened(handshakeDataChannel.id, 'outbound');
|
|
430
|
-
dataChannels.set(handshakeDataChannel.id, handshakeDataChannel);
|
|
476
|
+
config.onStreamOpened(handshakeDataChannel.id, 'outbound', 1024 * 1024);
|
|
477
|
+
dataChannels.set(handshakeDataChannel.id, { channel: handshakeDataChannel, bufferedBytes: 0 });
|
|
431
478
|
handshakeDataChannel = undefined;
|
|
432
479
|
}
|
|
433
480
|
}))();
|
package/dist/mjs/index-deno.js
CHANGED
|
@@ -99,7 +99,6 @@ function trustedBase64Decode(base64) {
|
|
|
99
99
|
* @throws {@link ConnectionError} If the multiaddress couldn't be parsed or contains an invalid protocol.
|
|
100
100
|
*/
|
|
101
101
|
function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
|
|
102
|
-
let connection;
|
|
103
102
|
// Attempt to parse the multiaddress.
|
|
104
103
|
// TODO: remove support for `/wss` in a long time (https://github.com/paritytech/smoldot/issues/1940)
|
|
105
104
|
const wsParsed = config.address.match(/^\/(ip4|ip6|dns4|dns6|dns)\/(.*?)\/tcp\/(.*?)\/(ws|wss|tls\/ws)$/);
|
|
@@ -114,24 +113,72 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
|
|
|
114
113
|
const url = (wsParsed[1] == 'ip6') ?
|
|
115
114
|
(proto + "://[" + wsParsed[2] + "]:" + wsParsed[3]) :
|
|
116
115
|
(proto + "://" + wsParsed[2] + ":" + wsParsed[3]);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const socket = new WebSocket(url);
|
|
117
|
+
socket.binaryType = 'arraybuffer';
|
|
118
|
+
const bufferedAmountCheck = { quenedUnreportedBytes: 0, nextTimeout: 10 };
|
|
119
|
+
const checkBufferedAmount = () => {
|
|
120
|
+
if (socket.readyState != 1)
|
|
121
|
+
return;
|
|
122
|
+
// Note that we might expect `bufferedAmount` to always be <= the sum of the lengths
|
|
123
|
+
// of all the data that has been sent, but that might not be the case. For this
|
|
124
|
+
// reason, we use `bufferedAmount` as a hint rather than a correct value.
|
|
125
|
+
const bufferedAmount = socket.bufferedAmount;
|
|
126
|
+
let wasSent = bufferedAmountCheck.quenedUnreportedBytes - bufferedAmount;
|
|
127
|
+
if (wasSent < 0)
|
|
128
|
+
wasSent = 0;
|
|
129
|
+
bufferedAmountCheck.quenedUnreportedBytes -= wasSent;
|
|
130
|
+
if (bufferedAmountCheck.quenedUnreportedBytes != 0) {
|
|
131
|
+
setTimeout(checkBufferedAmount, bufferedAmountCheck.nextTimeout);
|
|
132
|
+
bufferedAmountCheck.nextTimeout *= 2;
|
|
133
|
+
if (bufferedAmountCheck.nextTimeout > 500)
|
|
134
|
+
bufferedAmountCheck.nextTimeout = 500;
|
|
135
|
+
}
|
|
136
|
+
// Note: it is important to call `onWritableBytes` at the very end, as it might
|
|
137
|
+
// trigger a call to `send`.
|
|
138
|
+
if (wasSent != 0)
|
|
139
|
+
config.onWritableBytes(wasSent);
|
|
120
140
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux' });
|
|
141
|
+
socket.onopen = () => {
|
|
142
|
+
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: false });
|
|
124
143
|
};
|
|
125
|
-
|
|
144
|
+
socket.onclose = (event) => {
|
|
126
145
|
const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
|
|
127
146
|
config.onConnectionReset(message);
|
|
128
147
|
};
|
|
129
|
-
|
|
148
|
+
socket.onmessage = (msg) => {
|
|
130
149
|
config.onMessage(new Uint8Array(msg.data));
|
|
131
150
|
};
|
|
151
|
+
return {
|
|
152
|
+
reset: () => {
|
|
153
|
+
// We can't set these fields to null because the TypeScript definitions don't
|
|
154
|
+
// allow it, but we can set them to dummy values.
|
|
155
|
+
socket.onopen = () => { };
|
|
156
|
+
socket.onclose = () => { };
|
|
157
|
+
socket.onmessage = () => { };
|
|
158
|
+
socket.onerror = () => { };
|
|
159
|
+
socket.close();
|
|
160
|
+
},
|
|
161
|
+
send: (data) => {
|
|
162
|
+
// The WebSocket library that we use seems to spontaneously transition connections
|
|
163
|
+
// to the "closed" state but not call the `onclosed` callback immediately. Calling
|
|
164
|
+
// `send` on that object throws an exception. In order to avoid panicking smoldot,
|
|
165
|
+
// we thus absorb any exception thrown here.
|
|
166
|
+
// See also <https://github.com/paritytech/smoldot/issues/2937>.
|
|
167
|
+
try {
|
|
168
|
+
socket.send(data);
|
|
169
|
+
if (bufferedAmountCheck.quenedUnreportedBytes == 0) {
|
|
170
|
+
bufferedAmountCheck.nextTimeout = 10;
|
|
171
|
+
setTimeout(checkBufferedAmount, 10);
|
|
172
|
+
}
|
|
173
|
+
bufferedAmountCheck.quenedUnreportedBytes += data.length;
|
|
174
|
+
}
|
|
175
|
+
catch (_error) { }
|
|
176
|
+
},
|
|
177
|
+
closeSend: () => { throw new Error('Wrong connection type'); },
|
|
178
|
+
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
179
|
+
};
|
|
132
180
|
}
|
|
133
181
|
else if (tcpParsed != null) {
|
|
134
|
-
// `net` module will be missing when we're not in NodeJS.
|
|
135
182
|
if (forbidTcp) {
|
|
136
183
|
throw new ConnectionError('TCP connections not available');
|
|
137
184
|
}
|
|
@@ -146,13 +193,12 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
|
|
|
146
193
|
return null;
|
|
147
194
|
})
|
|
148
195
|
};
|
|
149
|
-
connection = { ty: 'tcp', socket };
|
|
150
196
|
socket.inner = socket.inner.then((established) => {
|
|
151
197
|
// TODO: at the time of writing of this comment, `setNoDelay` is still unstable
|
|
152
198
|
//established.setNoDelay();
|
|
153
199
|
if (socket.destroyed)
|
|
154
200
|
return established;
|
|
155
|
-
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux' });
|
|
201
|
+
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: true });
|
|
156
202
|
// Spawns an asynchronous task that continuously reads from the socket.
|
|
157
203
|
// Every time data is read, the task re-executes itself in order to continue reading.
|
|
158
204
|
// The task ends automatically if an EOF or error is detected, which should also happen
|
|
@@ -184,53 +230,21 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
|
|
|
184
230
|
read(new Uint8Array(1024));
|
|
185
231
|
return established;
|
|
186
232
|
});
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (connection.ty == 'websocket') {
|
|
194
|
-
// WebSocket
|
|
195
|
-
// We can't set these fields to null because the TypeScript definitions don't
|
|
196
|
-
// allow it, but we can set them to dummy values.
|
|
197
|
-
connection.socket.onopen = () => { };
|
|
198
|
-
connection.socket.onclose = () => { };
|
|
199
|
-
connection.socket.onmessage = () => { };
|
|
200
|
-
connection.socket.onerror = () => { };
|
|
201
|
-
connection.socket.close();
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
// TCP
|
|
205
|
-
connection.socket.destroyed = true;
|
|
206
|
-
connection.socket.inner.then((connec) => connec.close());
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
send: (data) => {
|
|
210
|
-
if (connection.ty == 'websocket') {
|
|
211
|
-
// WebSocket
|
|
212
|
-
// The WebSocket library that we use seems to spontaneously transition connections
|
|
213
|
-
// to the "closed" state but not call the `onclosed` callback immediately. Calling
|
|
214
|
-
// `send` on that object throws an exception. In order to avoid panicking smoldot,
|
|
215
|
-
// we thus absorb any exception thrown here.
|
|
216
|
-
// See also <https://github.com/paritytech/smoldot/issues/2937>.
|
|
217
|
-
try {
|
|
218
|
-
connection.socket.send(data);
|
|
219
|
-
}
|
|
220
|
-
catch (_error) { }
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
// TCP
|
|
224
|
-
// TODO: at the moment, sending data doesn't have any back-pressure mechanism; as such, we just buffer data indefinitely
|
|
233
|
+
return {
|
|
234
|
+
reset: () => {
|
|
235
|
+
socket.destroyed = true;
|
|
236
|
+
socket.inner.then((connec) => connec.close());
|
|
237
|
+
},
|
|
238
|
+
send: (data) => {
|
|
225
239
|
let dataCopy = Uint8Array.from(data); // Deep copy of the data
|
|
226
|
-
|
|
227
|
-
connection.socket.inner = connection.socket.inner.then((c) => __awaiter(this, void 0, void 0, function* () {
|
|
240
|
+
socket.inner = socket.inner.then((c) => __awaiter(this, void 0, void 0, function* () {
|
|
228
241
|
while (dataCopy.length > 0) {
|
|
229
242
|
if (socket.destroyed || c === null)
|
|
230
243
|
return c;
|
|
231
244
|
let outcome;
|
|
232
245
|
try {
|
|
233
246
|
outcome = yield c.write(dataCopy);
|
|
247
|
+
config.onWritableBytes(dataCopy.length);
|
|
234
248
|
}
|
|
235
249
|
catch (error) {
|
|
236
250
|
// The type of `error` is unclear, but we assume that it implements `Error`
|
|
@@ -250,8 +264,17 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
|
|
|
250
264
|
}
|
|
251
265
|
return c;
|
|
252
266
|
}));
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
267
|
+
},
|
|
268
|
+
closeSend: () => {
|
|
269
|
+
socket.inner = socket.inner.then((c) => __awaiter(this, void 0, void 0, function* () {
|
|
270
|
+
yield (c === null || c === void 0 ? void 0 : c.closeWrite());
|
|
271
|
+
return c;
|
|
272
|
+
}));
|
|
273
|
+
},
|
|
274
|
+
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
throw new ConnectionError('Unrecognized multiaddr format');
|
|
279
|
+
}
|
|
257
280
|
}
|