Skip to content

Commit 3a983b8

Browse files
committed
quic: add ipv6-only handshake test and fix
1 parent c881ca7 commit 3a983b8

File tree

2 files changed

+80
-21
lines changed

2 files changed

+80
-21
lines changed

lib/internal/quic/quic.js

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,21 +2079,13 @@ class QuicEndpoint {
20792079
*/
20802080
function processEndpointOption(endpoint) {
20812081
if (endpoint === undefined) {
2082-
return {
2083-
endpoint: new QuicEndpoint(),
2084-
created: true,
2085-
};
2082+
// No endpoint or endpoint options were given. Create a default.
2083+
return new QuicEndpoint();
20862084
} else if (endpoint instanceof QuicEndpoint) {
2087-
return {
2088-
endpoint,
2089-
created: false,
2090-
};
2085+
// We were given an existing endpoint. Use it as-is.
2086+
return endpoint;
20912087
}
2092-
validateObject(endpoint, 'options.endpoint');
2093-
return {
2094-
endpoint: new QuicEndpoint(endpoint),
2095-
created: true,
2096-
};
2088+
return new QuicEndpoint(endpoint);
20972089
}
20982090

20992091
/**
@@ -2226,10 +2218,10 @@ function getPreferredAddressPolicy(policy = 'default') {
22262218

22272219
/**
22282220
* @param {SessionOptions} options
2229-
* @param {boolean} [forServer]
2221+
* @param {{forServer: boolean, addressFamily: string}} [config]
22302222
* @returns {SessionOptions}
22312223
*/
2232-
function processSessionOptions(options, forServer = false) {
2224+
function processSessionOptions(options, config = {}) {
22332225
validateObject(options, 'options');
22342226
const {
22352227
endpoint,
@@ -2248,6 +2240,10 @@ function processSessionOptions(options, forServer = false) {
22482240
[kApplicationProvider]: provider,
22492241
} = options;
22502242

2243+
const {
2244+
forServer = false,
2245+
} = config;
2246+
22512247
if (provider !== undefined) {
22522248
validateObject(provider, 'options[kApplicationProvider]');
22532249
}
@@ -2256,15 +2252,11 @@ function processSessionOptions(options, forServer = false) {
22562252
validateOneOf(cc, 'options.cc', [CC_ALGO_RENO, CC_ALGO_BBR, CC_ALGO_CUBIC]);
22572253
}
22582254

2259-
const {
2260-
endpoint: actualEndpoint,
2261-
created: endpointCreated,
2262-
} = processEndpointOption(endpoint);
2255+
const actualEndpoint = processEndpointOption(endpoint);
22632256

22642257
return {
22652258
__proto__: null,
22662259
endpoint: actualEndpoint,
2267-
endpointCreated,
22682260
version,
22692261
minVersion,
22702262
preferredAddressPolicy: getPreferredAddressPolicy(preferredAddressPolicy),
@@ -2294,7 +2286,7 @@ async function listen(callback, options = kEmptyObject) {
22942286
const {
22952287
endpoint,
22962288
...sessionOptions
2297-
} = processSessionOptions(options, true /* for server */);
2289+
} = processSessionOptions(options, { forServer: true });
22982290
endpoint[kListen](callback, sessionOptions);
22992291

23002292
if (onEndpointListeningChannel.hasSubscribers) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
import { hasQuic, hasIPv6, skip } from '../common/index.mjs';
4+
import { ok, partialDeepStrictEqual } from 'node:assert';
5+
import { readKey } from '../common/fixtures.mjs';
6+
7+
if (!hasQuic) {
8+
skip('QUIC is not enabled');
9+
}
10+
11+
if (!hasIPv6) {
12+
skip('IPv6 is not supported');
13+
}
14+
15+
// Import after the hasQuic check
16+
const { listen, connect } = await import('node:quic');
17+
const { createPrivateKey } = await import('node:crypto');
18+
19+
const keys = createPrivateKey(readKey('agent1-key.pem'));
20+
const certs = readKey('agent1-cert.pem');
21+
22+
const check = {
23+
// The SNI value
24+
servername: 'localhost',
25+
// The selected ALPN protocol
26+
protocol: 'h3',
27+
// The negotiated cipher suite
28+
cipher: 'TLS_AES_128_GCM_SHA256',
29+
cipherVersion: 'TLSv1.3',
30+
};
31+
32+
// The opened promise should resolve when the handshake is complete.
33+
34+
const serverOpened = Promise.withResolvers();
35+
const clientOpened = Promise.withResolvers();
36+
37+
const serverEndpoint = await listen(async (serverSession) => {
38+
const info = await serverSession.opened;
39+
partialDeepStrictEqual(info, check);
40+
serverOpened.resolve();
41+
serverSession.close();
42+
}, { keys, certs, endpoint: {
43+
address: {
44+
address: '::1',
45+
family: 'ipv6',
46+
},
47+
ipv6Only: true,
48+
} });
49+
50+
// The server must have an address to connect to after listen resolves.
51+
ok(serverEndpoint.address !== undefined);
52+
53+
const clientSession = await connect(serverEndpoint.address, {
54+
endpoint: {
55+
address: {
56+
address: '::',
57+
family: 'ipv6',
58+
},
59+
}
60+
});
61+
clientSession.opened.then((info) => {
62+
partialDeepStrictEqual(info, check);
63+
clientOpened.resolve();
64+
});
65+
66+
await Promise.all([serverOpened.promise, clientOpened.promise]);
67+
clientSession.close();

0 commit comments

Comments
 (0)