quico 0.2.0 → 0.4.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/README.md +854 -125
- package/index.js +8 -2
- package/package.json +2 -2
- package/src/agent.js +67 -1
- package/src/client.js +40 -6
- package/src/crypto.js +37 -5
- package/src/h3.js +52 -43
- package/src/h3_client.js +103 -79
- package/src/h3_server.js +52 -183
- package/src/quic_connection.js +1397 -123
- package/src/quic_server.js +372 -0
- package/src/quic_socket.js +127 -0
- package/src/server.js +30 -22
- package/src/streams.js +15 -1
- package/src/tls_bridge.js +116 -14
- package/src/transport.js +143 -34
- package/src/utils.js +59 -4
- package/src/webtransport.js +25 -78
package/src/webtransport.js
CHANGED
|
@@ -14,11 +14,10 @@
|
|
|
14
14
|
|
|
15
15
|
import { Duplex, Readable, Writable } from 'node:stream';
|
|
16
16
|
import { EventEmitter } from 'node:events';
|
|
17
|
-
import dgram from 'node:dgram';
|
|
18
17
|
import dns from 'node:dns';
|
|
19
|
-
import { QUICConnection } from './quic_connection.js';
|
|
20
18
|
import { H3Connection } from './h3.js';
|
|
21
|
-
import {
|
|
19
|
+
import { createQuicClientSocket } from './quic_socket.js';
|
|
20
|
+
import { readVarInt, concatUint8Arrays, wt_stream_header, wt_datagram_payload } from './utils.js';
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
// ============================================================
|
|
@@ -101,13 +100,7 @@ class WebTransportBidiStream extends Duplex {
|
|
|
101
100
|
// First write on this stream: prepend VarInt(0x41) + VarInt(session_id)
|
|
102
101
|
if (!this._prefixSent) {
|
|
103
102
|
this._prefixSent = true;
|
|
104
|
-
|
|
105
|
-
var sessionPrefix = writeVarInt(this._sessionId);
|
|
106
|
-
var combined = new Uint8Array(typePrefix.length + sessionPrefix.length + chunk.byteLength);
|
|
107
|
-
combined.set(typePrefix, 0);
|
|
108
|
-
combined.set(sessionPrefix, typePrefix.length);
|
|
109
|
-
combined.set(chunk, typePrefix.length + sessionPrefix.length);
|
|
110
|
-
this._quic.sendStream(this._streamId, combined, false);
|
|
103
|
+
this._quic.sendStream(this._streamId, wt_stream_header(true, this._sessionId, chunk), false);
|
|
111
104
|
} else {
|
|
112
105
|
this._quic.sendStream(this._streamId, chunk, false);
|
|
113
106
|
}
|
|
@@ -118,12 +111,7 @@ class WebTransportBidiStream extends Duplex {
|
|
|
118
111
|
// Send FIN (with type + session_id prefix if nothing was written yet)
|
|
119
112
|
if (!this._prefixSent) {
|
|
120
113
|
this._prefixSent = true;
|
|
121
|
-
|
|
122
|
-
var sessionPrefix = writeVarInt(this._sessionId);
|
|
123
|
-
var header = new Uint8Array(typePrefix.length + sessionPrefix.length);
|
|
124
|
-
header.set(typePrefix, 0);
|
|
125
|
-
header.set(sessionPrefix, typePrefix.length);
|
|
126
|
-
this._quic.sendStream(this._streamId, header, true);
|
|
114
|
+
this._quic.sendStream(this._streamId, wt_stream_header(true, this._sessionId), true);
|
|
127
115
|
} else {
|
|
128
116
|
this._quic.sendStream(this._streamId, new Uint8Array(0), true);
|
|
129
117
|
}
|
|
@@ -168,13 +156,7 @@ class WebTransportUniWriteStream extends Writable {
|
|
|
168
156
|
if (!this._headerSent) {
|
|
169
157
|
this._headerSent = true;
|
|
170
158
|
// Uni stream: VarInt(0x54) (WEBTRANSPORT_STREAM) + VarInt(session_id) + data
|
|
171
|
-
|
|
172
|
-
var sessionPrefix = writeVarInt(this._sessionId);
|
|
173
|
-
var header = new Uint8Array(typePrefix.length + sessionPrefix.length + chunk.byteLength);
|
|
174
|
-
header.set(typePrefix, 0);
|
|
175
|
-
header.set(sessionPrefix, typePrefix.length);
|
|
176
|
-
header.set(chunk, typePrefix.length + sessionPrefix.length);
|
|
177
|
-
this._quic.sendStream(this._streamId, header, false);
|
|
159
|
+
this._quic.sendStream(this._streamId, wt_stream_header(false, this._sessionId, chunk), false);
|
|
178
160
|
} else {
|
|
179
161
|
this._quic.sendStream(this._streamId, chunk, false);
|
|
180
162
|
}
|
|
@@ -184,12 +166,7 @@ class WebTransportUniWriteStream extends Writable {
|
|
|
184
166
|
_final(callback) {
|
|
185
167
|
if (!this._headerSent) {
|
|
186
168
|
this._headerSent = true;
|
|
187
|
-
|
|
188
|
-
var sessionPrefix = writeVarInt(this._sessionId);
|
|
189
|
-
var header = new Uint8Array(typePrefix.length + sessionPrefix.length);
|
|
190
|
-
header.set(typePrefix, 0);
|
|
191
|
-
header.set(sessionPrefix, typePrefix.length);
|
|
192
|
-
this._quic.sendStream(this._streamId, header, true);
|
|
169
|
+
this._quic.sendStream(this._streamId, wt_stream_header(false, this._sessionId), true);
|
|
193
170
|
} else {
|
|
194
171
|
this._quic.sendStream(this._streamId, new Uint8Array(0), true);
|
|
195
172
|
}
|
|
@@ -350,43 +327,31 @@ class WebTransport extends EventEmitter {
|
|
|
350
327
|
_startQuic(remoteIp) {
|
|
351
328
|
var self = this;
|
|
352
329
|
var remotePort = this._port;
|
|
353
|
-
var isIPv6 = remoteIp.indexOf(':') >= 0;
|
|
354
330
|
|
|
355
|
-
this._udpSocket =
|
|
331
|
+
this._udpSocket = createQuicClientSocket({
|
|
332
|
+
remoteIp: remoteIp,
|
|
333
|
+
remotePort: remotePort,
|
|
334
|
+
hostname: this._hostname,
|
|
356
335
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
this._udpSocket.on('error', function (err) {
|
|
362
|
-
if (self._state === 'connecting') self._readyReject(err);
|
|
363
|
-
self.emit('error', err);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
this._udpSocket.bind(0, function () {
|
|
367
|
-
self._quic = new QUICConnection({
|
|
368
|
-
isServer: false,
|
|
369
|
-
hostname: self._hostname
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
self._quic.on('packet', function (data) {
|
|
373
|
-
self._udpSocket.send(data, remotePort, remoteIp);
|
|
374
|
-
});
|
|
336
|
+
onError: function (err) {
|
|
337
|
+
if (self._state === 'connecting') self._readyReject(err);
|
|
338
|
+
self.emit('error', err);
|
|
339
|
+
},
|
|
375
340
|
|
|
376
|
-
|
|
341
|
+
onConnect: function (q, s) {
|
|
342
|
+
self._quic = q;
|
|
343
|
+
self._udpSocket = s;
|
|
377
344
|
self._h3 = new H3Connection({ quicConnection: self._quic, isServer: false, enableWebTransport: true });
|
|
378
345
|
self._setupH3Handlers();
|
|
379
346
|
self._sendConnect();
|
|
380
|
-
}
|
|
347
|
+
},
|
|
381
348
|
|
|
382
|
-
|
|
349
|
+
onClose: function () {
|
|
383
350
|
self._state = 'closed';
|
|
384
351
|
self._closedResolve();
|
|
385
352
|
self.emit('close');
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
self._quic.connect();
|
|
353
|
+
// The helper closes the UDP socket.
|
|
354
|
+
}
|
|
390
355
|
});
|
|
391
356
|
}
|
|
392
357
|
|
|
@@ -424,7 +389,7 @@ class WebTransport extends EventEmitter {
|
|
|
424
389
|
});
|
|
425
390
|
|
|
426
391
|
// Datagrams — QUIC DATAGRAM payload = VarInt(quarter_stream_id) + app data (RFC 9297)
|
|
427
|
-
this._quic.on('datagram', function (
|
|
392
|
+
this._quic.on('datagram', function (rawData) {
|
|
428
393
|
if (!rawData || rawData.byteLength === 0) return;
|
|
429
394
|
|
|
430
395
|
// Always parse VarInt quarter_stream_id from payload
|
|
@@ -484,7 +449,7 @@ class WebTransport extends EventEmitter {
|
|
|
484
449
|
buf.totalLen += data.byteLength;
|
|
485
450
|
|
|
486
451
|
// Try to parse prefix
|
|
487
|
-
var combined = buf.totalLen === data.byteLength ? data :
|
|
452
|
+
var combined = buf.totalLen === data.byteLength ? data : concatUint8Arrays(buf.chunks);
|
|
488
453
|
var isBidi = (streamId & 0x2) === 0;
|
|
489
454
|
var offset = 0;
|
|
490
455
|
|
|
@@ -552,18 +517,6 @@ class WebTransport extends EventEmitter {
|
|
|
552
517
|
}
|
|
553
518
|
}
|
|
554
519
|
|
|
555
|
-
_combineChunks(chunks, totalLen) {
|
|
556
|
-
if (chunks.length === 1) return chunks[0];
|
|
557
|
-
var out = new Uint8Array(totalLen);
|
|
558
|
-
var off = 0;
|
|
559
|
-
for (var i = 0; i < chunks.length; i++) {
|
|
560
|
-
out.set(chunks[i], off);
|
|
561
|
-
off += chunks[i].byteLength;
|
|
562
|
-
}
|
|
563
|
-
return out;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
520
|
// ---- Send CONNECT ----
|
|
568
521
|
|
|
569
522
|
_sendConnect() {
|
|
@@ -660,14 +613,8 @@ class WebTransport extends EventEmitter {
|
|
|
660
613
|
if (typeof data === 'string') data = Buffer.from(data);
|
|
661
614
|
if (Buffer.isBuffer(data)) data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
662
615
|
|
|
663
|
-
//
|
|
664
|
-
var
|
|
665
|
-
|
|
666
|
-
// Build datagram: VarInt(contextId) + payload
|
|
667
|
-
var contextPrefix = writeVarInt(contextId);
|
|
668
|
-
var frame = new Uint8Array(contextPrefix.length + data.byteLength);
|
|
669
|
-
frame.set(contextPrefix, 0);
|
|
670
|
-
frame.set(data, contextPrefix.length);
|
|
616
|
+
// Build datagram: VarInt(quarter_stream_id) + payload (RFC 9297)
|
|
617
|
+
var frame = wt_datagram_payload(this._sessionId, data);
|
|
671
618
|
|
|
672
619
|
// Send via QUIC DATAGRAM frame
|
|
673
620
|
if (this._quic && this._quic.sendDatagram) {
|