Skip to content

Commit cd6cfb0

Browse files
committed
quic: implement more of the internal js API
1 parent d4ea9c3 commit cd6cfb0

File tree

2 files changed

+143
-73
lines changed

2 files changed

+143
-73
lines changed

lib/internal/quic/quic.js

Lines changed: 118 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ const {
140140

141141
const {
142142
Buffer,
143-
} = require('node:buffer');
143+
} = require('buffer');
144144

145145
const {
146146
codes: {
@@ -194,6 +194,10 @@ const {
194194
validateObject,
195195
} = require('internal/validators');
196196

197+
const {
198+
InternalX509Certificate,
199+
} = require('internal/crypto/x509');
200+
197201
const kOwner = Symbol('kOwner');
198202
const kHandle = Symbol('kHandle');
199203
const kFinishClose = Symbol('kFinishClose');
@@ -209,6 +213,8 @@ const kNewStream = Symbol('kNewStream');
209213
const kRemoteAddress = Symbol('kRemoteAddress');
210214
const kIsPendingClose = Symbol('kIsPendingClose');
211215
const kPendingClose = Symbol('kPendingClose');
216+
const kCertificate = Symbol('kCertificate');
217+
const kPeerCertificate = Symbol('kPeerCertificate');
212218

213219
/**
214220
* @typedef {import('../socketaddress.js').SocketAddress} SocketAddress
@@ -1178,12 +1184,23 @@ class Stream extends EventTarget {
11781184

11791185
/** @type {bigint} */
11801186
get id() { return this[kState].id; }
1187+
1188+
/** @type {boolean} */
1189+
get destroyed() { return this[kHandle] === undefined; }
1190+
1191+
destroy(error) {
1192+
// TODO(@jasnell): Implement this.
1193+
// ordinarily the stream would close naturally when the streams
1194+
// are closed. Calling destory() forcefully closes the stream
1195+
// immediately.
1196+
}
11811197
}
11821198
ObjectDefineProperties(Stream.prototype, {
1199+
destroyed: { __proto__: null, enumerable: true },
1200+
id: { __proto__: null, enumerable: true },
11831201
stats: { __proto__: null, enumerable: true },
11841202
state: { __proto__: null, enumerable: true },
11851203
session: { __proto__: null, enumerable: true },
1186-
id: { __proto__: null, enumerable: true },
11871204
});
11881205

11891206
class BidirectionalStream extends Stream {
@@ -1197,6 +1214,22 @@ class BidirectionalStream extends Stream {
11971214
/** @type {WritableStream} */
11981215
get writable() { return this[kWritable]; }
11991216

1217+
get priority() {
1218+
if (this.destroyed) return 'default';
1219+
const priority = this[kHandle].getPriority();
1220+
if (priority === 0) return 'high';
1221+
if (priority === 7) return 'low';
1222+
return 'default';
1223+
}
1224+
1225+
set priority(val) {
1226+
if (this.destroyed) return;
1227+
let priority = 3;
1228+
if (val === 'high') priority = 0;
1229+
else if (val === 'low') priority = 7;
1230+
this[kHandle].setPriority(priority, 1);
1231+
}
1232+
12001233
[kInspect](depth, options) {
12011234
if (depth < 0)
12021235
return this;
@@ -1207,15 +1240,18 @@ class BidirectionalStream extends Stream {
12071240
};
12081241

12091242
return `BidirectionalStream ${inspect({
1210-
stats: this.stats,
1211-
state: this.state,
1212-
session: this.session,
1243+
destoyed: this.destroyed,
1244+
priority: this.priority,
12131245
readable: this.readable,
1246+
session: this.session,
1247+
state: this.state,
1248+
stats: this.stats,
12141249
writable: this.writable,
12151250
}, opts)}`;
12161251
}
12171252
}
12181253
ObjectDefineProperties(BidirectionalStream.prototype, {
1254+
priority: { __proto__: null, enumerable: true },
12191255
readable: { __proto__: null, enumerable: true },
12201256
writable: { __proto__: null, enumerable: true },
12211257
});
@@ -1238,10 +1274,12 @@ class UnidirectionalInboundStream extends Stream {
12381274
};
12391275

12401276
return `UnidirectionalInboundStream ${inspect({
1241-
stats: this.stats,
1242-
state: this.state,
1243-
session: this.session,
1277+
destroed: this.destroyed,
1278+
id: this.id,
12441279
readable: this.readable,
1280+
session: this.session,
1281+
state: this.state,
1282+
stats: this.stats,
12451283
}, opts)}`;
12461284
}
12471285
}
@@ -1257,6 +1295,22 @@ class UnidirectionalOutboundStream extends Stream {
12571295
/** @type {WritableStream} */
12581296
get writable() { return this[kWritable]; }
12591297

1298+
get priority() {
1299+
if (this.destroyed) return 'default';
1300+
const priority = this[kHandle].getPriority();
1301+
if (priority === 0) return 'high';
1302+
if (priority === 7) return 'low';
1303+
return 'default';
1304+
}
1305+
1306+
set priority(val) {
1307+
if (this.destroyed) return;
1308+
let priority = 3;
1309+
if (val === 'high') priority = 0;
1310+
else if (val === 'low') priority = 7;
1311+
this[kHandle].setPriority(priority, 1);
1312+
}
1313+
12601314
[kInspect](depth, options) {
12611315
if (depth < 0)
12621316
return this;
@@ -1267,14 +1321,18 @@ class UnidirectionalOutboundStream extends Stream {
12671321
};
12681322

12691323
return `UnidirectionalOutboundStream ${inspect({
1270-
stats: this.stats,
1271-
state: this.state,
1324+
destroyed: this.destroyed,
1325+
id: this.id,
1326+
priority: this.priority,
12721327
session: this.session,
1328+
state: this.state,
1329+
stats: this.stats,
12731330
writable: this.writable,
12741331
}, opts)}`;
12751332
}
12761333
}
12771334
ObjectDefineProperties(UnidirectionalOutboundStream.prototype, {
1335+
priority: { __proto__: null, enumerable: true },
12781336
writable: { __proto__: null, enumerable: true },
12791337
});
12801338

@@ -1722,7 +1780,13 @@ class SessionState {
17221780
[kFinishClose]() {
17231781
// Snapshot the state into a new DataView since the underlying
17241782
// buffer will be destroyed.
1725-
this[kHandle] = new DataView(this[kHandle]);
1783+
let u8 = new Uint8Array(this[kHandle].buffer,
1784+
this[kHandle].byteOffset,
1785+
this[kHandle].byteLength);
1786+
u8 = new Uint8Array(u8);
1787+
this[kHandle] = new DataView(u8.buffer,
1788+
u8.byteOffset,
1789+
u8.byteLength);
17261790
}
17271791
}
17281792
ObjectDefineProperties(SessionState.prototype, {
@@ -1787,6 +1851,33 @@ class Session extends EventTarget {
17871851
};
17881852
}
17891853

1854+
get certificate() {
1855+
if (this.#isClosedOrClosing) return undefined;
1856+
if (this[kCertificate] === undefined) {
1857+
const cert = this[kHandle].getPeerCertificate();
1858+
if (cert !== undefined) {
1859+
this[kCertificate] = new InternalX509Certificate(cert);
1860+
}
1861+
}
1862+
return this[kCertificate];
1863+
}
1864+
1865+
get peerCertificate() {
1866+
if (this.#isClosedOrClosing) return undefined;
1867+
if (this[kPeerCertificate] === undefined) {
1868+
const cert = this[kHandle].getPeerCertificate();
1869+
if (cert !== undefined) {
1870+
this[kPeerCertificate] = new InternalX509Certificate(cert);
1871+
}
1872+
}
1873+
return this[kPeerCertificate];
1874+
}
1875+
1876+
get ephemeralKeyInfo() {
1877+
if (this.#isClosedOrClosing) return undefined;
1878+
return this[kHandle].getEphemeralKeyInfo();
1879+
}
1880+
17901881
/**
17911882
* @returns {BidirectionalStream}
17921883
*/
@@ -1953,11 +2044,14 @@ class Session extends EventTarget {
19532044
};
19542045

19552046
return `Session ${inspect({
2047+
certificate: this.certificate,
19562048
closed: this[kPendingClose].promise,
19572049
closing: this[kIsPendingClose],
19582050
destroyed: this.destroyed,
19592051
endpoint: this.endpoint,
2052+
ephemeralKeyInfo: this.ephemeralKeyInfo,
19602053
path: this.path,
2054+
peerCertificate: this.peerCertificate,
19612055
state: this.state,
19622056
stats: this.stats,
19632057
streams: this[kStreams],
@@ -1969,11 +2063,13 @@ class Session extends EventTarget {
19692063
}
19702064
}
19712065
ObjectDefineProperties(Session.prototype, {
2066+
certificate: { __proto__: null, enumerable: true },
19722067
closed: { __proto__: null, enumerable: true },
19732068
destroyed: { __proto__: null, enumerable: true },
19742069
endpoint: { __proto__: null, enumerable: true },
1975-
localAddress: { __proto__: null, enumerable: true },
1976-
remoteAddress: { __proto__: null, enumerable: true },
2070+
ephemeralKeyInfo: { __proto__: null, enumerable: true },
2071+
path: { __proto__: null, enumerable: true },
2072+
peerCertificate: { __proto__: null, enumerable: true },
19772073
state: { __proto__: null, enumerable: true },
19782074
stats: { __proto__: null, enumerable: true },
19792075
});
@@ -1990,6 +2086,8 @@ function createSession(handle, endpoint) {
19902086
instance[kEndpoint] = endpoint;
19912087
instance[kStreams] = [];
19922088
instance[kRemoteAddress] = undefined;
2089+
instance[kCertificate] = undefined;
2090+
instance[kPeerCertificate] = undefined;
19932091
instance[kIsPendingClose] = false;
19942092
instance[kPendingClose] = Promise.withResolvers(); // eslint-disable-line node-core/prefer-primordials
19952093
handle[kOwner] = instance;
@@ -2215,7 +2313,13 @@ class EndpointState {
22152313
[kFinishClose]() {
22162314
// Snapshot the state into a new DataView since the underlying
22172315
// buffer will be destroyed.
2218-
this[kHandle] = new DataView(this[kHandle]);
2316+
let u8 = new Uint8Array(this[kHandle].buffer,
2317+
this[kHandle].byteOffset,
2318+
this[kHandle].byteLength);
2319+
u8 = new Uint8Array(u8);
2320+
this[kHandle] = new DataView(u8.buffer,
2321+
u8.byteOffset,
2322+
u8.byteLength);
22192323
}
22202324
}
22212325
ObjectDefineProperties(EndpointState.prototype, {
Lines changed: 25 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,42 @@
1-
// Flags: --expose-internals
1+
// Flags: --expose-internals --no-warnings
22
'use strict';
33

44
const common = require('../common');
55
if (!common.hasQuic)
66
common.skip('missing quic');
77

8-
const { internalBinding } = require('internal/test/binding');
8+
const { Endpoint } = require('internal/quic/quic');
9+
const { SocketAddress } = require('node:net');
910
const {
1011
ok,
1112
strictEqual,
12-
deepStrictEqual,
13+
notStrictEqual,
1314
} = require('node:assert');
1415

15-
const {
16-
SocketAddress: _SocketAddress,
17-
AF_INET,
18-
} = internalBinding('block_list');
19-
const quic = internalBinding('quic');
20-
21-
quic.setCallbacks({
22-
onEndpointClose: common.mustCall((...args) => {
23-
deepStrictEqual(args, [0, 0]);
24-
}),
25-
26-
// The following are unused in this test
27-
onSessionNew() {},
28-
onSessionClose() {},
29-
onSessionDatagram() {},
30-
onSessionDatagramStatus() {},
31-
onSessionHandshake() {},
32-
onSessionPathValidation() {},
33-
onSessionTicket() {},
34-
onSessionVersionNegotiation() {},
35-
onStreamCreated() {},
36-
onStreamBlocked() {},
37-
onStreamClose() {},
38-
onStreamReset() {},
39-
onStreamHeaders() {},
40-
onStreamTrailers() {},
41-
});
42-
43-
const endpoint = new quic.Endpoint({});
44-
45-
const state = new DataView(endpoint.state);
46-
ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING));
47-
ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING));
48-
ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND));
49-
strictEqual(endpoint.address(), undefined);
16+
const endpoint = new Endpoint();
5017

51-
endpoint.listen({});
18+
ok(!endpoint.state.isListening);
19+
ok(!endpoint.state.isReceiving);
20+
ok(!endpoint.state.isBound);
21+
strictEqual(endpoint.address, undefined);
5222

53-
ok(state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING));
54-
ok(state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING));
55-
ok(state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND));
56-
const address = endpoint.address();
57-
ok(address instanceof _SocketAddress);
23+
endpoint.listen();
5824

59-
const detail = address.detail({
60-
address: undefined,
61-
port: undefined,
62-
family: undefined,
63-
flowlabel: undefined,
64-
});
25+
ok(endpoint.state.isListening);
26+
ok(endpoint.state.isReceiving);
27+
ok(endpoint.state.isBound);
6528

66-
strictEqual(detail.address, '127.0.0.1');
67-
strictEqual(detail.family, AF_INET);
68-
strictEqual(detail.flowlabel, 0);
69-
ok(detail.port !== 0);
29+
ok(endpoint.address instanceof SocketAddress);
7030

71-
endpoint.closeGracefully();
31+
strictEqual(endpoint.address.address, '127.0.0.1');
32+
strictEqual(endpoint.address.family, 'ipv4');
33+
strictEqual(endpoint.address.flowlabel, 0);
34+
notStrictEqual(endpoint.address.port, 0);
7235

73-
ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING));
74-
ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING));
75-
ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND));
76-
strictEqual(endpoint.address(), undefined);
36+
endpoint.close().then(common.mustCall(() => {
37+
ok(!endpoint.state.isListening);
38+
ok(!endpoint.state.isReceiving);
39+
ok(!endpoint.state.isBound);
40+
strictEqual(endpoint.address, undefined);
41+
notStrictEqual(endpoint.stats.destroyedAt, 0n);
42+
}));

0 commit comments

Comments
 (0)