quico 0.7.0 → 0.9.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 +247 -33
- package/index.d.ts +1034 -77
- package/index.js +7 -3
- package/package.json +9 -5
- package/src/client.js +1 -0
- package/src/connect_udp.js +5 -2
- package/src/connect_udp_client.js +10 -0
- package/src/crypto.js +6 -0
- package/src/h1_server.js +51 -9
- package/src/h2_server.js +60 -11
- package/src/h3.js +115 -70
- package/src/h3_client.js +25 -5
- package/src/h3_server.js +104 -6
- package/src/hpack_primitives.js +64 -25
- package/src/http_common.js +7 -0
- package/src/qpack.js +11 -2
- package/src/quic_connection.js +379 -63
- package/src/quic_server.js +213 -72
- package/src/quic_socket.js +5 -5
- package/src/server.js +277 -24
- package/src/streams.js +183 -10
- package/src/tcp_listener.js +11 -0
- package/src/tls_bridge.js +62 -38
- package/src/transport.js +6 -2
- package/src/utils.js +36 -0
- package/src/webtransport.js +432 -57
- package/src/ws.js +120 -0
- package/src/ws_client.js +68 -88
- package/src/ws_server.js +35 -53
- package/src/ws_stream.js +250 -0
- package/src/wt_stream.js +533 -76
package/src/quic_server.js
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import dgram from 'node:dgram';
|
|
16
|
+
import dns from 'node:dns';
|
|
17
|
+
import net from 'node:net';
|
|
16
18
|
import crypto from 'node:crypto';
|
|
17
|
-
import { DEBUG, Emitter } from './utils.js';
|
|
19
|
+
import { DEBUG, Emitter, checkedSocketBufferSize, setSocketBuffers } from './utils.js';
|
|
18
20
|
import {
|
|
19
21
|
parse_quic_datagram,
|
|
20
22
|
build_version_negotiation
|
|
@@ -118,6 +120,10 @@ function createQuicServer(options) {
|
|
|
118
120
|
// When socket/socket6 are provided, the UDP layer is owned externally.
|
|
119
121
|
// The caller demuxes packets and feeds them via handlePacket(msg, rinfo).
|
|
120
122
|
// See RFC 9443 for the shared-port multiplexing scheme.
|
|
123
|
+
// The UDP sockets' buffers (utils.js): 4 MB by default — external ones
|
|
124
|
+
// too, as before (the demultiplexer that owns them may not have, and
|
|
125
|
+
// asking twice costs nothing); 0 or false leaves them all at the OS's.
|
|
126
|
+
var socketBufferSize = checkedSocketBufferSize(options.socketBufferSize, 'socketBufferSize');
|
|
121
127
|
var externalSocket4 = options.socket || null;
|
|
122
128
|
var externalSocket6 = options.socket6 || null;
|
|
123
129
|
|
|
@@ -201,8 +207,28 @@ function createQuicServer(options) {
|
|
|
201
207
|
// gave it, with the time, sealed (AES-256-GCM) to the client's IP address
|
|
202
208
|
// under a key of this server's own. It lives seconds, so a key drawn at
|
|
203
209
|
// start is enough: a restart costs only the Retries in flight.
|
|
210
|
+
//
|
|
211
|
+
// tokenSecret (node:quic's name, at least 16 bytes) gives the key instead:
|
|
212
|
+
// servers that pass the same one open each other's tokens. That is what
|
|
213
|
+
// several servers sharing a port need (one per thread, SO_REUSEPORT): a
|
|
214
|
+
// client back with a NEW_TOKEN from another port is hashed to another of
|
|
215
|
+
// them, and without the key its token does not open — no harm, only the
|
|
216
|
+
// shortcut lost. The AES key is derived from it (SHA-256), so a 16-byte
|
|
217
|
+
// secret serves as well as a 32-byte one.
|
|
204
218
|
var validateAddress = options.validateAddress === true;
|
|
205
|
-
var
|
|
219
|
+
var tokenSecret = checkTokenSecret(options.tokenSecret) || crypto.randomBytes(32);
|
|
220
|
+
var retryTokenKey = tokenKeyOf(tokenSecret);
|
|
221
|
+
|
|
222
|
+
function checkTokenSecret(secret) {
|
|
223
|
+
if (secret === undefined || secret === null) return null;
|
|
224
|
+
if (!(secret instanceof Uint8Array) || secret.byteLength < 16) {
|
|
225
|
+
throw new TypeError('tokenSecret must be a Buffer or Uint8Array of at least 16 bytes');
|
|
226
|
+
}
|
|
227
|
+
return secret;
|
|
228
|
+
}
|
|
229
|
+
function tokenKeyOf(secret) {
|
|
230
|
+
return crypto.createHash('sha256').update('quico address-validation token key').update(secret).digest();
|
|
231
|
+
}
|
|
206
232
|
var RETRY_TOKEN_LIFETIME = 10000; // ms
|
|
207
233
|
// Tokens of two kinds, sealed alike (RFC 9000 §8.1.3): a Retry's, good for
|
|
208
234
|
// seconds and one connection attempt; and a NEW_TOKEN's, given to a client
|
|
@@ -478,6 +504,7 @@ function createQuicServer(options) {
|
|
|
478
504
|
// in length". Shorter ones would also be easy to guess and collide.
|
|
479
505
|
if (firstPkt.dcid.byteLength < 8) return;
|
|
480
506
|
if (connectionCount >= maxConnections) return;
|
|
507
|
+
if (!accepting) return;
|
|
481
508
|
|
|
482
509
|
var retry = null, knownAddress = false;
|
|
483
510
|
if (firstPkt.token && firstPkt.token.byteLength > 0) {
|
|
@@ -723,8 +750,8 @@ function createQuicServer(options) {
|
|
|
723
750
|
var SERVER_RUNTIME = ['validateAddress', 'handshakeLoadThreshold', 'maxHandshakesPerAddress', 'earlyData', 'earlyDataMaxAge'];
|
|
724
751
|
var ELSEWHERE = {
|
|
725
752
|
key: 'setSecureContext()', cert: 'setSecureContext()', ca: 'setSecureContext()', pfx: 'setSecureContext()',
|
|
726
|
-
passphrase: 'setSecureContext()', SNICallback: 'setSecureContext()',
|
|
727
|
-
ticketKeys: 'setTicketKeys()', ech: 'setEchKeys()', resetTokenSecret: 'setResetTokenSecret()',
|
|
753
|
+
passphrase: 'setSecureContext()', SNICallback: 'addContext() or setSecureContext()',
|
|
754
|
+
ticketKeys: 'setTicketKeys()', ech: 'setEchKeys()', resetTokenSecret: 'setResetTokenSecret()', tokenSecret: 'setTokenSecret()',
|
|
728
755
|
transportParams: 'connectionOptions, per connection'
|
|
729
756
|
};
|
|
730
757
|
function optErr(Ctor, code, msg) { var e = new Ctor(msg); e.code = code; return e; }
|
|
@@ -805,6 +832,17 @@ function createQuicServer(options) {
|
|
|
805
832
|
// UDP send
|
|
806
833
|
// ============================================================
|
|
807
834
|
|
|
835
|
+
// A client that has gone away: its port answers our datagrams with ICMP
|
|
836
|
+
// "port unreachable". Node does not report that on an unconnected UDP
|
|
837
|
+
// socket; Bun does, as ECONNREFUSED — an 'error' on the socket, or on a
|
|
838
|
+
// send's callback. It concerns that client alone (its connection ends at
|
|
839
|
+
// its idle timeout, as any silent peer's), not the socket: taken for a
|
|
840
|
+
// socket error it closed the server's socket, and with it every other
|
|
841
|
+
// client of that family (quico PR #12).
|
|
842
|
+
function isDepartedPeer(err) {
|
|
843
|
+
return !!err && err.code === 'ECONNREFUSED';
|
|
844
|
+
}
|
|
845
|
+
|
|
808
846
|
function sendUdp(toIp, toPort, data) {
|
|
809
847
|
var isV6 = toIp.indexOf(':') >= 0;
|
|
810
848
|
// Prefer external socket (shared mode) over internally-bound socket
|
|
@@ -815,7 +853,7 @@ function createQuicServer(options) {
|
|
|
815
853
|
sendsPending++;
|
|
816
854
|
socket.send(data, toPort, toIp, function (err) {
|
|
817
855
|
sendsPending--;
|
|
818
|
-
if (err && !closing) ev.emit('error', err);
|
|
856
|
+
if (err && !closing && !isDepartedPeer(err)) ev.emit('error', err);
|
|
819
857
|
if (closing && sendsPending === 0) closing();
|
|
820
858
|
});
|
|
821
859
|
}
|
|
@@ -826,6 +864,11 @@ function createQuicServer(options) {
|
|
|
826
864
|
// timeout. close() waits for them — not long (SEND_DRAIN_MAX).
|
|
827
865
|
var sendsPending = 0;
|
|
828
866
|
var shutDown = false;
|
|
867
|
+
// stopAccepting(): no new connection (an Initial for an unknown one is
|
|
868
|
+
// dropped); those open go on — a graceful close, the layer above deciding
|
|
869
|
+
// when they end.
|
|
870
|
+
var accepting = true;
|
|
871
|
+
var refed = true;
|
|
829
872
|
var closing = null;
|
|
830
873
|
var SEND_DRAIN_MAX = 200;
|
|
831
874
|
|
|
@@ -836,6 +879,7 @@ function createQuicServer(options) {
|
|
|
836
879
|
|
|
837
880
|
function listen(listenPort, host, callback) {
|
|
838
881
|
shutDown = false;
|
|
882
|
+
accepting = true;
|
|
839
883
|
if (typeof host === 'function') { callback = host; host = null; }
|
|
840
884
|
// `|| 443` turned port 0 into 443 — and port 0 is not "no port": it is how
|
|
841
885
|
// a caller asks the OS for a free one (server.js has the same fix).
|
|
@@ -855,14 +899,8 @@ function createQuicServer(options) {
|
|
|
855
899
|
// decrypted a second time and dropped as a duplicate, and every non-QUIC
|
|
856
900
|
// packet — media included, on the hottest path there is — went through
|
|
857
901
|
// the QUIC parser before being discarded.
|
|
858
|
-
if (externalSocket4)
|
|
859
|
-
|
|
860
|
-
try { externalSocket4.setRecvBufferSize(2 * 1024 * 1024); } catch (e) {}
|
|
861
|
-
}
|
|
862
|
-
if (externalSocket6) {
|
|
863
|
-
try { externalSocket6.setSendBufferSize(2 * 1024 * 1024); } catch (e) {}
|
|
864
|
-
try { externalSocket6.setRecvBufferSize(2 * 1024 * 1024); } catch (e) {}
|
|
865
|
-
}
|
|
902
|
+
if (externalSocket4) setSocketBuffers(externalSocket4, socketBufferSize);
|
|
903
|
+
if (externalSocket6) setSocketBuffers(externalSocket6, socketBufferSize);
|
|
866
904
|
startSweepTimer();
|
|
867
905
|
if (typeof callback === 'function') setImmediate(callback);
|
|
868
906
|
return;
|
|
@@ -875,63 +913,108 @@ function createQuicServer(options) {
|
|
|
875
913
|
// recovery engages and throughput collapses. See LOSS_PTO_PLAN 7.4: this
|
|
876
914
|
// is what defeated the third pacer-calibration attempt.
|
|
877
915
|
//
|
|
878
|
-
//
|
|
879
|
-
//
|
|
880
|
-
function growBuffers(sock) {
|
|
881
|
-
|
|
882
|
-
|
|
916
|
+
// Not being able to enlarge the buffer must not stop the server from
|
|
917
|
+
// listening (setSocketBuffers).
|
|
918
|
+
function growBuffers(sock) { setSocketBuffers(sock, socketBufferSize); }
|
|
919
|
+
|
|
920
|
+
// Which addresses: as node:net. An address binds its own family alone —
|
|
921
|
+
// '127.0.0.1' and '0.0.0.0' IPv4, '::1' IPv6; '::' (and no host) both;
|
|
922
|
+
// a hostname is looked up once, and each family it has binds its first
|
|
923
|
+
// address ('localhost': loopback, both families). Every address used to
|
|
924
|
+
// bind '::' unless it held a ':', and '0.0.0.0' unless it held a '.':
|
|
925
|
+
// listen(p, '127.0.0.1') answered on every IPv6 interface too, and
|
|
926
|
+
// listen(p, 'localhost') on every interface of both.
|
|
927
|
+
if (host === '::') bindFamilies('0.0.0.0', '::');
|
|
928
|
+
else if (net.isIP(host) === 4) bindFamilies(host, null);
|
|
929
|
+
else if (net.isIP(host) === 6) bindFamilies(null, host);
|
|
930
|
+
else {
|
|
931
|
+
dns.lookup(host, { all: true }, function (err, list) {
|
|
932
|
+
if (shutDown) return;
|
|
933
|
+
if (err) { ev.emit('error', err); return; }
|
|
934
|
+
bindFamilies(firstOf(list, 4), firstOf(list, 6));
|
|
935
|
+
});
|
|
883
936
|
}
|
|
884
937
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
//
|
|
891
|
-
//
|
|
892
|
-
//
|
|
893
|
-
// yet.
|
|
938
|
+
function firstOf(list, family) {
|
|
939
|
+
for (var i = 0; i < list.length; i++) if (list[i].family === family) return list[i].address;
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// The families one after the other, IPv4 first: after listen(0) IPv6
|
|
944
|
+
// takes the port the OS gave IPv4 — one server, one port, the one
|
|
945
|
+
// address() and Alt-Svc name. Each took a port of its own.
|
|
894
946
|
//
|
|
895
|
-
//
|
|
896
|
-
//
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
function
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
if (
|
|
905
|
-
|
|
906
|
-
|
|
947
|
+
// Listening succeeds when every family binds, but for one the host does
|
|
948
|
+
// not have (familyMissing: no IPv6 in a container) while another did.
|
|
949
|
+
// Any other failure fails the listen() — the sockets that bound are
|
|
950
|
+
// closed, and one 'error' says why. Before, either family binding was
|
|
951
|
+
// enough: with IPv4's port taken (another server already on it) this
|
|
952
|
+
// one served IPv6 alone, and nothing said so.
|
|
953
|
+
function bindFamilies(addr4, addr6) {
|
|
954
|
+
var steps = [];
|
|
955
|
+
if (addr4) steps.push([false, addr4]);
|
|
956
|
+
if (addr6) steps.push([true, addr6]);
|
|
957
|
+
var errs = [], bound = 0;
|
|
958
|
+
(function next(i) {
|
|
959
|
+
if (shutDown) return;
|
|
960
|
+
if (i === steps.length) { settle(errs, bound); return; }
|
|
961
|
+
bindOne(steps[i][0], steps[i][1], function (err) {
|
|
962
|
+
if (!err) bound++;
|
|
963
|
+
else { errs.push(err); if (!familyMissing(err)) { settle(errs, bound); return; } }
|
|
964
|
+
next(i + 1);
|
|
965
|
+
});
|
|
966
|
+
})(0);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function familyMissing(err) { return err.code === 'EAFNOSUPPORT' || err.code === 'EADDRNOTAVAIL'; }
|
|
970
|
+
|
|
971
|
+
// The callback used to hang off udp6.bind alone: on a host without IPv6
|
|
972
|
+
// it was never called, while the IPv4 socket served. It is called once
|
|
973
|
+
// the families are settled, whichever bound.
|
|
974
|
+
function settle(errs, bound) {
|
|
975
|
+
var fatal = null;
|
|
976
|
+
for (var i = 0; i < errs.length && !fatal; i++) if (!familyMissing(errs[i])) fatal = errs[i];
|
|
977
|
+
if (!fatal && bound === 0) fatal = errs[0] || new Error('failed to bind UDP on port ' + port);
|
|
978
|
+
if (fatal) {
|
|
979
|
+
if (udp4) { try { udp4.close(); } catch (e) {} udp4 = null; }
|
|
980
|
+
if (udp6) { try { udp6.close(); } catch (e) {} udp6 = null; }
|
|
981
|
+
ev.emit('error', fatal);
|
|
907
982
|
return;
|
|
908
983
|
}
|
|
909
984
|
if (typeof callback === 'function') callback();
|
|
910
985
|
}
|
|
911
986
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
udp4.
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
987
|
+
// A socket whose bind fails is closed, not only dropped: it stayed open
|
|
988
|
+
// (no IPv6 on the host, the port taken), an object nothing could close
|
|
989
|
+
// once `udp4` / `udp6` no longer named it. The failure goes to settle()
|
|
990
|
+
// alone, which reports one error; it was also emitted here, so a
|
|
991
|
+
// listen() that failed emitted 'error' twice.
|
|
992
|
+
//
|
|
993
|
+
// An error after the bind is a runtime socket error: it belongs to the
|
|
994
|
+
// consumer, and the socket goes on serving — it is not closed. It was:
|
|
995
|
+
// the close ran for every error, so on Bun one departed client's
|
|
996
|
+
// ECONNREFUSED (isDepartedPeer, not reported at all) closed the socket,
|
|
997
|
+
// and the server answered no one of that family again. And `on`, not
|
|
998
|
+
// `once`: a second error with no listener left would throw.
|
|
999
|
+
function bindOne(v6, addr, done) {
|
|
1000
|
+
var sock = dgram.createSocket(v6 ? { type: 'udp6', ipv6Only: true } : 'udp4'), isBound = false;
|
|
1001
|
+
if (v6) udp6 = sock; else udp4 = sock;
|
|
1002
|
+
sock.on('message', onUdpMessage);
|
|
1003
|
+
sock.on('error', function (err) {
|
|
1004
|
+
if (isBound) { if (!isDepartedPeer(err)) ev.emit('error', err); return; }
|
|
1005
|
+
try { sock.close(); } catch (e) {}
|
|
1006
|
+
if (udp4 === sock) udp4 = null;
|
|
1007
|
+
if (udp6 === sock) udp6 = null;
|
|
1008
|
+
done(err);
|
|
1009
|
+
});
|
|
1010
|
+
if (!refed) sock.unref();
|
|
1011
|
+
sock.bind(port, addr, function () {
|
|
1012
|
+
isBound = true;
|
|
1013
|
+
growBuffers(sock);
|
|
1014
|
+
if (port === 0) port = sock.address().port;
|
|
1015
|
+
done(null);
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
935
1018
|
|
|
936
1019
|
startSweepTimer();
|
|
937
1020
|
}
|
|
@@ -1004,13 +1087,47 @@ function createQuicServer(options) {
|
|
|
1004
1087
|
// External packet API (for shared UDP port scenarios)
|
|
1005
1088
|
// ============================================================
|
|
1006
1089
|
|
|
1007
|
-
// Query whether
|
|
1008
|
-
//
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1090
|
+
// Query whether a datagram belongs to an active QUIC connection.
|
|
1091
|
+
//
|
|
1092
|
+
// hasConnection(rinfo) — by the address it came from. Used by the demuxer
|
|
1093
|
+
// to disambiguate byte 0 in range 64-79 between QUIC short header and TURN
|
|
1094
|
+
// ChannelData (RFC 9443). Fresh connections must be classified by byte 0
|
|
1095
|
+
// range (80-127 or 192+ = QUIC).
|
|
1096
|
+
//
|
|
1097
|
+
// hasConnection(rinfo, msg) — by the connection ID the datagram is
|
|
1098
|
+
// addressed to, from wherever it came: the answer to "is this one mine"
|
|
1099
|
+
// when several servers share the port (one per thread, SO_REUSEPORT) and a
|
|
1100
|
+
// datagram can reach one that does not hold its connection — a client
|
|
1101
|
+
// whose address changed is hashed to another socket. Asked before
|
|
1102
|
+
// handlePacket: given a short-header packet for an ID it does not know,
|
|
1103
|
+
// handlePacket answers with a Stateless Reset, and that would end a
|
|
1104
|
+
// connection another server holds. The address does not count here: an ID
|
|
1105
|
+
// that leads nowhere is not ours, whatever address sent it.
|
|
1106
|
+
function hasConnection(rinfo, msg) {
|
|
1107
|
+
if (msg === undefined || msg === null) {
|
|
1108
|
+
var key = rinfo.address + ':' + rinfo.port;
|
|
1109
|
+
return (key in addressMap) && (addressMap[key] in connections);
|
|
1110
|
+
}
|
|
1111
|
+
var dcid = datagramDcid(msg);
|
|
1112
|
+
if (!dcid) return false;
|
|
1113
|
+
var k = cidKey(dcid);
|
|
1114
|
+
return (k in cidMap) && (cidMap[k] in connections);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// The Destination Connection ID of a datagram's first packet, without
|
|
1118
|
+
// parsing the rest (RFC 9000 §17.2, §17.3; RFC 8999 for any version): a
|
|
1119
|
+
// long header carries its length, a short header ours (SERVER_CID_LEN).
|
|
1120
|
+
// null for one too short to hold it.
|
|
1121
|
+
function datagramDcid(msg) {
|
|
1122
|
+
if (!(msg instanceof Uint8Array) || msg.length < 1) return null;
|
|
1123
|
+
if (msg[0] & 0x80) {
|
|
1124
|
+
if (msg.length < 6) return null;
|
|
1125
|
+
var len = msg[5];
|
|
1126
|
+
if (msg.length < 6 + len) return null;
|
|
1127
|
+
return msg.subarray(6, 6 + len);
|
|
1128
|
+
}
|
|
1129
|
+
if (msg.length < 1 + SERVER_CID_LEN) return null;
|
|
1130
|
+
return msg.subarray(1, 1 + SERVER_CID_LEN);
|
|
1014
1131
|
}
|
|
1015
1132
|
|
|
1016
1133
|
|
|
@@ -1021,14 +1138,27 @@ function createQuicServer(options) {
|
|
|
1021
1138
|
return {
|
|
1022
1139
|
listen: listen,
|
|
1023
1140
|
close: close,
|
|
1141
|
+
// node:dgram's address() of the socket listening — after listen(0), the
|
|
1142
|
+
// port the OS gave (both families have it); null when not listening.
|
|
1143
|
+
address: function () {
|
|
1144
|
+
var s = udp4 || udp6 || externalSocket4 || externalSocket6;
|
|
1145
|
+
try { return s ? s.address() : null; } catch (e) { return null; }
|
|
1146
|
+
},
|
|
1147
|
+
stopAccepting: function () { accepting = false; },
|
|
1148
|
+
get connectionCount() { return connectionCount; },
|
|
1149
|
+
// node:dgram's: whether the process may exit while this listens.
|
|
1150
|
+
ref: function () { refed = true; if (udp4) udp4.ref(); if (udp6) udp6.ref(); },
|
|
1151
|
+
unref: function () { refed = false; if (udp4) udp4.unref(); if (udp6) udp6.unref(); },
|
|
1024
1152
|
on: function (name, fn) { ev.on(name, fn); },
|
|
1025
1153
|
|
|
1026
1154
|
/** Feed an incoming UDP packet when running in external-socket mode.
|
|
1027
1155
|
* rinfo must be in Node's dgram format: { address, port, family, size }. */
|
|
1028
1156
|
handlePacket: onUdpMessage,
|
|
1029
1157
|
|
|
1030
|
-
/**
|
|
1031
|
-
*
|
|
1158
|
+
/** hasConnection(rinfo): the address matches an active QUIC connection.
|
|
1159
|
+
* hasConnection(rinfo, msg): the datagram is addressed to one (by its
|
|
1160
|
+
* connection ID). Used by demuxers for routing decisions on shared UDP
|
|
1161
|
+
* ports — the second when several servers share it (see above). */
|
|
1032
1162
|
hasConnection: hasConnection,
|
|
1033
1163
|
|
|
1034
1164
|
/** The session ticket keys — tls.Server's names. A new key applies to
|
|
@@ -1059,7 +1189,18 @@ function createQuicServer(options) {
|
|
|
1059
1189
|
setOptions: setOptions,
|
|
1060
1190
|
|
|
1061
1191
|
getResetTokenSecret: function () { return resetTokenSecret; },
|
|
1062
|
-
setResetTokenSecret: function (secret) { resetTokenSecret = checkResetTokenSecret(secret) || resetTokenSecret; }
|
|
1192
|
+
setResetTokenSecret: function (secret) { resetTokenSecret = checkResetTokenSecret(secret) || resetTokenSecret; },
|
|
1193
|
+
|
|
1194
|
+
/** The address-validation token secret (Retry, NEW_TOKEN — see
|
|
1195
|
+
* tokenSecret above). A new one applies from now on; tokens sealed
|
|
1196
|
+
* under the one before no longer open: a client back with a NEW_TOKEN
|
|
1197
|
+
* loses its shortcut, and one mid-Retry its attempt (it accepts no
|
|
1198
|
+
* second Retry). */
|
|
1199
|
+
getTokenSecret: function () { return tokenSecret; },
|
|
1200
|
+
setTokenSecret: function (secret) {
|
|
1201
|
+
var s = checkTokenSecret(secret);
|
|
1202
|
+
if (s) { tokenSecret = s; retryTokenKey = tokenKeyOf(s); }
|
|
1203
|
+
}
|
|
1063
1204
|
};
|
|
1064
1205
|
}
|
|
1065
1206
|
|
package/src/quic_socket.js
CHANGED
|
@@ -16,6 +16,7 @@ import dgram from 'node:dgram';
|
|
|
16
16
|
import { QUICConnection } from './quic_connection.js';
|
|
17
17
|
import lemonTls from 'lemon-tls';
|
|
18
18
|
import { SUPPORTED_VERSIONS, parseVersionList, versionName } from './quic_version.js';
|
|
19
|
+
import { checkedSocketBufferSize, setSocketBuffers } from './utils.js';
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -67,6 +68,7 @@ function createQuicClientSocket(opts) {
|
|
|
67
68
|
// lemon-tls's list. A name it does not know throws here.
|
|
68
69
|
var sigalgs = lemonTls.parseSigalgs(opts.sigalgs);
|
|
69
70
|
var isIPv6 = opts.remoteIp.indexOf(':') >= 0;
|
|
71
|
+
var socketBufferSize = checkedSocketBufferSize(opts.socketBufferSize, 'socketBufferSize');
|
|
70
72
|
var udpSocket = dgram.createSocket(isIPv6 ? 'udp6' : 'udp4');
|
|
71
73
|
var quic = null;
|
|
72
74
|
|
|
@@ -88,11 +90,9 @@ function createQuicClientSocket(opts) {
|
|
|
88
90
|
// pacer-calibration attempt (see LOSS_PTO_PLAN 7.4): larger bursts spilled
|
|
89
91
|
// over the buffer and turned a lossless link into a slow one.
|
|
90
92
|
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
try { udpSocket.setSendBufferSize(2 * 1024 * 1024); } catch (e) {}
|
|
95
|
-
try { udpSocket.setRecvBufferSize(2 * 1024 * 1024); } catch (e) {}
|
|
93
|
+
// 4 MB by default (utils.js), socketBufferSize where given; failing to
|
|
94
|
+
// enlarge the buffer is not a reason to fail the connection.
|
|
95
|
+
setSocketBuffers(udpSocket, socketBufferSize);
|
|
96
96
|
|
|
97
97
|
// One attempt; after a Version Negotiation, a second in a version the
|
|
98
98
|
// server offered (RFC 9000 §6.2) — once.
|