Skip to content

Commit 4552bee

Browse files
committed
quic: test for establishing a wt session
1 parent b894b6b commit 4552bee

1 file changed

Lines changed: 125 additions & 0 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Flags: --experimental-quic --experimental-stream-iter --no-warnings
2+
3+
// Test: HTTP/3 webtransport session establishment
4+
// Verifies that a session established and properly closed
5+
6+
import { hasQuic, skip, mustCall } from '../common/index.mjs';
7+
import assert from 'node:assert';
8+
import * as fixtures from '../common/fixtures.mjs';
9+
10+
if (!hasQuic) {
11+
skip('QUIC is not enabled');
12+
}
13+
14+
const { listen, connect } = await import('node:quic');
15+
const { createPrivateKey } = await import('node:crypto');
16+
const { setTimeout: sleep } = await import('timers/promises');
17+
18+
const key = createPrivateKey(fixtures.readKey('agent1-key.pem'));
19+
const cert = fixtures.readKey('agent1-cert.pem');
20+
21+
const serverSessionOpened = Promise.withResolvers();
22+
23+
24+
const serverEndpoint = await listen(mustCall(async (ss) => {
25+
ss.onapplication = mustCall((aopts) => {
26+
assert.strictEqual(!aopts.enableDatagrams, false);
27+
});
28+
ss.onhandshake = mustCall(function() {
29+
assert.strictEqual(BigInt(this?.remoteTransportParams?.maxDatagramFrameSize) > 0, true);
30+
});
31+
}), {
32+
sni: { '*': { keys: [key], certs: [cert] } },
33+
application: {
34+
enableConnectProtocol: true,
35+
enableDatagrams: true,
36+
enableWebtransport: true
37+
},
38+
transportParams: { maxDatagramFrameSize: 100 },
39+
onheaders: mustCall(function(headers) {
40+
try {
41+
assert.strictEqual(headers[':scheme'], 'https');
42+
assert.strictEqual(headers[':method'], 'CONNECT');
43+
assert.strictEqual(headers[':protocol'], 'webtransport'); // depends on the draft
44+
assert.strictEqual(headers[':path'], '/testwtpath');
45+
// We could also check for wt-available protocols
46+
this.sendHeaders(
47+
{ ':status': '200' },
48+
{ terminal: false, webtransport: true }
49+
);
50+
serverSessionOpened.resolve();
51+
} catch (error) {
52+
serverSessionOpened.reject(error);
53+
}
54+
// Should only be installed on wt streams
55+
this.onwtsessionclose = mustCall((code, reason) => {
56+
assert.strictEqual(code, 200);
57+
assert.strictEqual(reason, 'all perfect');
58+
this.session.close();
59+
});
60+
}),
61+
});
62+
63+
const clientSession = await connect(serverEndpoint.address, {
64+
servername: 'localhost',
65+
verifyPeer: 'manual',
66+
application: {
67+
enableConnectProtocol: true,
68+
enableDatagrams: true,
69+
enableWebtransport: true
70+
},
71+
transportParams: { maxDatagramFrameSize: 1000 },
72+
});
73+
74+
const webtransportSupport = Promise.withResolvers();
75+
76+
clientSession.onapplication = mustCall((aopts) => {
77+
try {
78+
// Test for webtransport support
79+
assert.strictEqual(aopts.enableConnectProtocol, true);
80+
assert.strictEqual(aopts.enableDatagrams, true);
81+
assert.strictEqual(aopts.enableWebtransport, true);
82+
// Ok we have wt support
83+
webtransportSupport.resolve();
84+
} catch (error) {
85+
webtransportSupport.reject(error);
86+
}
87+
});
88+
89+
clientSession.onstream = mustCall((stream) => {
90+
stream.onheaders = mustCall((stream) => {
91+
// Well this should not happen on client side
92+
assert.strictEqual(false, true);
93+
});
94+
});
95+
96+
await clientSession.opened;
97+
await webtransportSupport.promise;
98+
// Now we open a webtransport session, which is actually
99+
// a special bidirectional stream
100+
const wtSessionStream = await clientSession.createBidirectionalStream({
101+
body: '',
102+
});
103+
wtSessionStream.sendHeaders({
104+
':method': 'CONNECT',
105+
':scheme': 'https',
106+
// This one depends on draft, draft14 says "webtransport", draft15 says "webtransport-h3"
107+
':protocol': 'webtransport',
108+
':path': '/testwtpath',
109+
':authority': 'testserver:' + serverEndpoint.address.port
110+
}, {
111+
webtransport: true // Tell nghttp3 to treat the stream as a WT session stream
112+
});
113+
114+
await serverSessionOpened.promise;
115+
await sleep(500);
116+
await wtSessionStream.closeWebtransportSessionStream(200, 'all perfect');
117+
118+
try {
119+
await wtSessionStream.closed;
120+
} catch (error) {
121+
assert.strictEqual(error.errorCode, 200n);
122+
assert.strictEqual(error.reason, 'all perfect');
123+
}
124+
await clientSession.close();
125+
await serverEndpoint.close();

0 commit comments

Comments
 (0)