smoldot 0.7.13 → 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.
@@ -51,7 +51,7 @@ function start(options) {
51
51
  return node_perf_hooks_1.performance.now();
52
52
  },
53
53
  getRandomValues: (buffer) => {
54
- if (buffer.length >= 65536)
54
+ if (buffer.length >= 1024 * 1024)
55
55
  throw new Error('getRandomValues buffer too large');
56
56
  (0, node_crypto_1.randomFillSync)(buffer);
57
57
  },
@@ -68,7 +68,6 @@ exports.start = start;
68
68
  * @throws {@link ConnectionError} If the multiaddress couldn't be parsed or contains an invalid protocol.
69
69
  */
70
70
  function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
71
- let connection;
72
71
  // Attempt to parse the multiaddress.
73
72
  // TODO: remove support for `/wss` in a long time (https://github.com/paritytech/smoldot/issues/1940)
74
73
  const wsParsed = config.address.match(/^\/(ip4|ip6|dns4|dns6|dns)\/(.*?)\/tcp\/(.*?)\/(ws|wss|tls\/ws)$/);
@@ -85,8 +84,33 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
85
84
  (proto + "://" + wsParsed[2] + ":" + wsParsed[3]);
86
85
  const socket = new ws_1.WebSocket(url);
87
86
  socket.binaryType = 'arraybuffer';
87
+ const bufferedAmountCheck = { quenedUnreportedBytes: 0, nextTimeout: 10 };
88
+ const checkBufferedAmount = () => {
89
+ if (socket.readyState != 1)
90
+ return;
91
+ // Note that we might expect `bufferedAmount` to always be <= the sum of the lengths
92
+ // of all the data that has been sent, but that seems to not be the case. It is
93
+ // unclear whether this is intended or a bug, but is is likely that `bufferedAmount`
94
+ // also includes WebSocket headers. For this reason, we use `bufferedAmount` as a hint
95
+ // rather than a correct value.
96
+ const bufferedAmount = socket.bufferedAmount;
97
+ let wasSent = bufferedAmountCheck.quenedUnreportedBytes - bufferedAmount;
98
+ if (wasSent < 0)
99
+ wasSent = 0;
100
+ bufferedAmountCheck.quenedUnreportedBytes -= wasSent;
101
+ if (bufferedAmountCheck.quenedUnreportedBytes != 0) {
102
+ setTimeout(checkBufferedAmount, bufferedAmountCheck.nextTimeout);
103
+ bufferedAmountCheck.nextTimeout *= 2;
104
+ if (bufferedAmountCheck.nextTimeout > 500)
105
+ bufferedAmountCheck.nextTimeout = 500;
106
+ }
107
+ // Note: it is important to call `onWritableBytes` at the very end, as it might
108
+ // trigger a call to `send`.
109
+ if (wasSent != 0)
110
+ config.onWritableBytes(wasSent);
111
+ };
88
112
  socket.onopen = () => {
89
- config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux' });
113
+ config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: false });
90
114
  };
91
115
  socket.onclose = (event) => {
92
116
  const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
@@ -106,7 +130,27 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
106
130
  socket.onmessage = (msg) => {
107
131
  config.onMessage(new Uint8Array(msg.data));
108
132
  };
109
- connection = { ty: 'websocket', socket };
133
+ return {
134
+ reset: () => {
135
+ // We can't set these fields to null because the TypeScript definitions don't
136
+ // allow it, but we can set them to dummy values.
137
+ socket.onopen = () => { };
138
+ socket.onclose = () => { };
139
+ socket.onmessage = () => { };
140
+ socket.onerror = () => { };
141
+ socket.close();
142
+ },
143
+ send: (data) => {
144
+ socket.send(data);
145
+ if (bufferedAmountCheck.quenedUnreportedBytes == 0) {
146
+ bufferedAmountCheck.nextTimeout = 10;
147
+ setTimeout(checkBufferedAmount, 10);
148
+ }
149
+ bufferedAmountCheck.quenedUnreportedBytes += data.length;
150
+ },
151
+ closeSend: () => { throw new Error('Wrong connection type'); },
152
+ openOutSubstream: () => { throw new Error('Wrong connection type'); }
153
+ };
110
154
  }
111
155
  else if (tcpParsed != null) {
112
156
  // `net` module will be missing when we're not in NodeJS.
@@ -117,14 +161,18 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
117
161
  host: tcpParsed[2],
118
162
  port: parseInt(tcpParsed[3], 10),
119
163
  });
120
- connection = { ty: 'tcp', socket };
121
- connection.socket.setNoDelay();
122
- connection.socket.on('connect', () => {
164
+ // Number of bytes queued using `socket.write` and where `write` has returned false.
165
+ const drainingBytes = { num: 0 };
166
+ socket.setNoDelay();
167
+ socket.on('connect', () => {
123
168
  if (socket.destroyed)
124
169
  return;
125
- config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux' });
170
+ config.onOpen({
171
+ type: 'single-stream', handshake: 'multistream-select-noise-yamux',
172
+ initialWritableBytes: socket.writableHighWaterMark, writeClosable: true
173
+ });
126
174
  });
127
- connection.socket.on('close', (hasError) => {
175
+ socket.on('close', (hasError) => {
128
176
  if (socket.destroyed)
129
177
  return;
130
178
  // NodeJS doesn't provide a reason why the closing happened, but only
@@ -132,43 +180,46 @@ function connect(config, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss) {
132
180
  const message = hasError ? "Error" : "Closed gracefully";
133
181
  config.onConnectionReset(message);
134
182
  });
135
- connection.socket.on('error', () => { });
136
- connection.socket.on('data', (message) => {
183
+ socket.on('error', () => { });
184
+ socket.on('data', (message) => {
137
185
  if (socket.destroyed)
138
186
  return;
139
187
  config.onMessage(new Uint8Array(message.buffer));
140
188
  });
189
+ socket.on('drain', () => {
190
+ // The bytes queued using `socket.write` and where `write` has returned false have now
191
+ // been sent. Notify the API that it can write more data.
192
+ if (socket.destroyed)
193
+ return;
194
+ const val = drainingBytes.num;
195
+ drainingBytes.num = 0;
196
+ config.onWritableBytes(val);
197
+ });
198
+ return {
199
+ reset: () => {
200
+ socket.destroy();
201
+ },
202
+ send: (data) => {
203
+ const dataLen = data.length;
204
+ const allWritten = socket.write(data);
205
+ if (allWritten) {
206
+ setImmediate(() => {
207
+ if (!socket.writable)
208
+ return;
209
+ config.onWritableBytes(dataLen);
210
+ });
211
+ }
212
+ else {
213
+ drainingBytes.num += dataLen;
214
+ }
215
+ },
216
+ closeSend: () => {
217
+ socket.end();
218
+ },
219
+ openOutSubstream: () => { throw new Error('Wrong connection type'); }
220
+ };
141
221
  }
142
222
  else {
143
223
  throw new instance_js_1.ConnectionError('Unrecognized multiaddr format');
144
224
  }
145
- return {
146
- reset: () => {
147
- if (connection.ty == 'websocket') {
148
- // WebSocket
149
- // We can't set these fields to null because the TypeScript definitions don't
150
- // allow it, but we can set them to dummy values.
151
- connection.socket.onopen = () => { };
152
- connection.socket.onclose = () => { };
153
- connection.socket.onmessage = () => { };
154
- connection.socket.onerror = () => { };
155
- connection.socket.close();
156
- }
157
- else {
158
- // TCP
159
- connection.socket.destroy();
160
- }
161
- },
162
- send: (data) => {
163
- if (connection.ty == 'websocket') {
164
- // WebSocket
165
- connection.socket.send(data);
166
- }
167
- else {
168
- // TCP
169
- connection.socket.write(data);
170
- }
171
- },
172
- openOutSubstream: () => { throw new Error('Wrong connection type'); }
173
- };
174
225
  }