Skip to content

Commit ce8ee4b

Browse files
committed
quic: WT implement close session callback
1 parent 16807e7 commit ce8ee4b

9 files changed

Lines changed: 145 additions & 4 deletions

File tree

lib/internal/quic/diagnostics.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ const onSessionGoawayChannel = dc.channel('quic.session.goaway');
3333
const onSessionEarlyRejectedChannel = dc.channel('quic.session.early.rejected');
3434
const onStreamClosedChannel = dc.channel('quic.stream.closed');
3535
const onStreamHeadersChannel = dc.channel('quic.stream.headers');
36-
const onStreamSessionIdChannel = dc.channel('quic.stream.sessioid');
36+
const onStreamSessionIdChannel = dc.channel('quic.stream.sessionid');
37+
const onStreamWTSessionCloseChannel = dc.channel('quic.stream.wtsessionclose')
3738
const onStreamTrailersChannel = dc.channel('quic.stream.trailers');
3839
const onStreamInfoChannel = dc.channel('quic.stream.info');
3940
const onStreamResetChannel = dc.channel('quic.stream.reset');
@@ -70,6 +71,7 @@ module.exports = {
7071
onStreamClosedChannel,
7172
onStreamHeadersChannel,
7273
onStreamSessionIdChannel,
74+
onStreamWTSessionCloseChannel,
7375
onStreamTrailersChannel,
7476
onStreamInfoChannel,
7577
onStreamResetChannel,

lib/internal/quic/quic.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ const {
195195
kVerifyPeer,
196196
kHeaders,
197197
kSessionId,
198+
kWTSessionClose,
198199
kOwner,
199200
kRemoveSession,
200201
kKeylog,
@@ -270,6 +271,7 @@ const {
270271
onStreamClosedChannel,
271272
onStreamHeadersChannel,
272273
onStreamSessionIdChannel,
274+
onStreamWTSessionCloseChannel,
273275
onStreamTrailersChannel,
274276
onStreamInfoChannel,
275277
onStreamResetChannel,
@@ -310,6 +312,7 @@ const endpointRegistry = new SafeSet();
310312
* webtransport support. If no headers are provided, it has no effect.
311313
* @property {OnHeadersCallback} [onheaders] Callback for incoming initial headers
312314
* @property {OnSessionIdCallback} [onsessionid] Callback for incoming sessionid
315+
* @property {OnWTSessionCloseCallback} [onwtsessionclose] Callback for incoming close capsules
313316
* @property {OnTrailersCallback} [ontrailers] Callback for incoming trailing headers
314317
* @property {OnInfoCallback} [oninfo] Callback for informational (1xx) headers
315318
* @property {OnWantTrailersCallback} [onwanttrailers] Callback fired when the
@@ -473,6 +476,7 @@ const endpointRegistry = new SafeSet();
473476
* @property {OnApplicationCallback} [onapplication] application options callback.
474477
* @property {OnHeadersCallback} [onheaders] Default per-stream initial-headers callback.
475478
* @property {OnSessionIdCallback} [onsessionid] Default perstream initial callback for incoming sessionid.
479+
* @property {OnWTSessionCloseCallback} [onwtsessionclose] Default perstream initial callback for incoming close capsules
476480
* @property {OnTrailersCallback} [ontrailers] Default per-stream trailing-headers callback.
477481
* @property {OnInfoCallback} [oninfo] Default per-stream informational-headers callback.
478482
* @property {OnWantTrailersCallback} [onwanttrailers] Default per-stream
@@ -729,6 +733,15 @@ const endpointRegistry = new SafeSet();
729733
* @returns {void}
730734
*/
731735

736+
/**
737+
* Called when session id (e.g. for Webtransport streams) is determined
738+
* @callback OnWTSessionCloseCallback
739+
* @this {QuicStream}
740+
* @param {number} error code from the webtransport session
741+
* @param {string|undefined} error message from the webtransport session
742+
* @returns {void}
743+
*/
744+
732745

733746

734747
/**
@@ -1025,6 +1038,12 @@ setCallbacks({
10251038
this[kOwner][kSessionId](sessionId);
10261039
},
10271040

1041+
onStreamWTSessionClose(errorcode, errormessage) {
1042+
// Called when the stream C++ handle has received a close capsule
1043+
debug(`stream ${this[kOwner].id} wtsessionclose callback`, errorcode, errormessage);
1044+
this[kOwner][kWTSessionClose](errorcode, errormessage);
1045+
},
1046+
10281047
onStreamTrailers() {
10291048
// Called when the stream C++ handle is ready to receive trailing headers.
10301049
debug('stream want trailers callback', this[kOwner]);
@@ -1347,6 +1366,7 @@ function applyCallbacks(session, cbs) {
13471366
};
13481367
}
13491368
if (cbs.onsessionid) session.onsessionid = cbs.onsessionid;
1369+
if (cbs.onwtsessionclose) session.onwtsessionclose = cbs.onwtsessionclose;
13501370
}
13511371

13521372
/**
@@ -1607,6 +1627,7 @@ class QuicStream {
16071627
onreset: undefined,
16081628
onheaders: undefined,
16091629
onsessionid: undefined,
1630+
onwtsessionclose: undefined,
16101631
ontrailers: undefined,
16111632
oninfo: undefined,
16121633
onwanttrailers: undefined,
@@ -1850,6 +1871,26 @@ class QuicStream {
18501871
}
18511872
}
18521873

1874+
/** @type {OnWTSessionCloseCallback} */
1875+
get onwtsessionclose() {
1876+
assertIsQuicStream(this);
1877+
return this.#inner.onwtsessionclose;
1878+
}
1879+
1880+
set onwtsessionclose(fn) {
1881+
assertIsQuicStream(this);
1882+
const inner = this.#inner;
1883+
if (fn === undefined) {
1884+
inner.onwtsessionclose = undefined;
1885+
inner.state.wantsWTSessionClose = false;
1886+
} else {
1887+
console.log('Set wantsWTSessionClose');
1888+
validateFunction(fn, 'onwtsessionclose');
1889+
inner.onwtsessionclose = FunctionPrototypeBind(fn, this);
1890+
inner.state.wantsWTSessionClose = true;
1891+
}
1892+
}
1893+
18531894
/** @type {Function|undefined} */
18541895
get oninfo() {
18551896
assertIsQuicStream(this);
@@ -2614,6 +2655,7 @@ class QuicStream {
26142655
inner.onreset = undefined;
26152656
inner.onheaders = undefined;
26162657
inner.onsessionid = undefined;
2658+
inner.onwtsessionclose = undefined;
26172659
inner.onerror = undefined;
26182660
inner.ontrailers = undefined;
26192661
inner.oninfo = undefined;
@@ -2728,6 +2770,23 @@ class QuicStream {
27282770
}
27292771
}
27302772

2773+
[kWTSessionClose](errorcode, errormessage) {
2774+
if (this.destroyed) return;
2775+
const inner = this.#inner;
2776+
if (onStreamWTSessionCloseChannel.hasSubscribers) {
2777+
onStreamWTSessionCloseChannel.publish({
2778+
__proto__: null,
2779+
stream: this,
2780+
session: inner.session,
2781+
errorcode,
2782+
errormessage
2783+
});
2784+
}
2785+
if (typeof inner.onwtsessionclose === 'function') {
2786+
safeCallbackInvoke(inner.onwtsessionclose, this, errorcode, errormessage);
2787+
}
2788+
}
2789+
27312790
[kTrailers]() {
27322791
if (this.destroyed) return;
27332792
const inner = this.#inner;
@@ -3348,6 +3407,7 @@ class QuicSession {
33483407
headers,
33493408
onheaders,
33503409
onsessionid,
3410+
onwtsessionclose,
33513411
ontrailers,
33523412
oninfo,
33533413
onwanttrailers,
@@ -3405,6 +3465,7 @@ class QuicSession {
34053465
// Set stream callbacks before sending headers to avoid missing events.
34063466
if (onheaders) stream.onheaders = onheaders;
34073467
if (onsessionid) stream.onsessionid = onsessionid;
3468+
if (onwtsessionclose) stream.onwtsessionclose = onwtsessionclose;
34083469
if (ontrailers) stream.ontrailers = ontrailers;
34093470
if (oninfo) stream.oninfo = oninfo;
34103471
if (onwanttrailers) stream.onwanttrailers = onwanttrailers;
@@ -4189,6 +4250,7 @@ class QuicSession {
41894250
if (scbs) {
41904251
if (scbs.onheaders) stream.onheaders = scbs.onheaders;
41914252
if (scbs.onsessionid) stream.onsessionid = scbs.onsessionid;
4253+
if (scbs.onwtsessionclose) stream.onwtsessionclose = scbs.onwtsessionclose;
41924254
if (scbs.ontrailers) stream.ontrailers = scbs.ontrailers;
41934255
if (scbs.oninfo) stream.oninfo = scbs.oninfo;
41944256
if (scbs.onwanttrailers) stream.onwanttrailers = scbs.onwanttrailers;
@@ -4567,6 +4629,7 @@ class QuicEndpoint {
45674629
// Stream-level callbacks applied to each incoming stream.
45684630
onheaders,
45694631
onsessionid,
4632+
onwtsessionclose,
45704633
ontrailers,
45714634
oninfo,
45724635
onwanttrailers,
@@ -4593,6 +4656,7 @@ class QuicEndpoint {
45934656
onapplication,
45944657
onheaders,
45954658
onsessionid,
4659+
onwtsessionclose,
45964660
ontrailers,
45974661
oninfo,
45984662
onwanttrailers,
@@ -5329,6 +5393,7 @@ function processSessionOptions(options, config = kEmptyObject) {
53295393
// Stream-level callbacks.
53305394
onheaders,
53315395
onsessionid,
5396+
onwtsessionclose,
53325397
ontrailers,
53335398
oninfo,
53345399
onwanttrailers,
@@ -5451,6 +5516,7 @@ function processSessionOptions(options, config = kEmptyObject) {
54515516
onapplication,
54525517
onheaders,
54535518
onsessionid,
5519+
onwtsessionclose,
54545520
ontrailers,
54555521
oninfo,
54565522
onwanttrailers,

lib/internal/quic/state.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ const {
101101
IDX_STATE_STREAM_WANTS_BLOCK,
102102
IDX_STATE_STREAM_WANTS_HEADERS,
103103
IDX_STATE_STREAM_WANTS_SESSIONID,
104+
IDX_STATE_STREAM_WANTS_WTSESSIONCLOSE,
104105
IDX_STATE_STREAM_WANTS_RESET,
105106
IDX_STATE_STREAM_WANTS_TRAILERS,
106107
IDX_STATE_STREAM_RECEIVED_EARLY_DATA,
@@ -828,6 +829,20 @@ class QuicStreamState {
828829
DataViewPrototypeSetUint8(handle, this.#offset + IDX_STATE_STREAM_WANTS_SESSIONID, val ? 1 : 0);
829830
}
830831

832+
/** @type {boolean} */
833+
get wantsWTSessionClose() {
834+
const handle = this.#handle;
835+
if (handle === undefined) return undefined;
836+
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_STREAM_WANTS_WTSESSIONCLOSE) !== 0;
837+
}
838+
839+
/** @type {boolean} */
840+
set wantsWTSessionClose(val) {
841+
const handle = this.#handle;
842+
if (handle === undefined) return;
843+
DataViewPrototypeSetUint8(handle, this.#offset + IDX_STATE_STREAM_WANTS_WTSESSIONCLOSE, val ? 1 : 0);
844+
}
845+
831846

832847
/** @type {boolean} */
833848
get wantsReset() {
@@ -922,6 +937,7 @@ class QuicStreamState {
922937
wantsReset,
923938
wantsHeaders,
924939
wantsSessionId,
940+
wantsWTSessionClose,
925941
wantsTrailers,
926942
early,
927943
resetCode,
@@ -943,6 +959,7 @@ class QuicStreamState {
943959
wantsReset,
944960
wantsHeaders,
945961
wantsSessionId,
962+
wantsWTSessionClose,
946963
wantsTrailers,
947964
early,
948965
resetCode,
@@ -980,6 +997,7 @@ class QuicStreamState {
980997
wantsReset,
981998
wantsHeaders,
982999
wantsSessionId,
1000+
wantsWTSessionClose,
9831001
wantsTrailers,
9841002
early,
9851003
resetCode,
@@ -1001,6 +1019,7 @@ class QuicStreamState {
10011019
wantsReset,
10021020
wantsHeaders,
10031021
wantsSessionId,
1022+
wantsWTSessionClose,
10041023
wantsTrailers,
10051024
early,
10061025
resetCode,

lib/internal/quic/symbols.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const kHandshakeCompleted = Symbol('kHandshakeCompleted');
4141
const kVerifyPeer = Symbol('kVerifyPeer');
4242
const kHeaders = Symbol('kHeaders');
4343
const kSessionId = Symbol('kSessionId');
44+
const kWTSessionClose = Symbol('kWTSessionClose');
4445
const kKeylog = Symbol('kKeylog');
4546
const kListen = Symbol('kListen');
4647
const kQlog = Symbol('kQlog');

src/quic/bindingdata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class SessionManager;
6161
V(stream_drain, StreamDrain) \
6262
V(stream_headers, StreamHeaders) \
6363
V(stream_sessionid, StreamSessionId) \
64+
V(stream_wtsessionclose, StreamWTSessionClose) \
6465
V(stream_reset, StreamReset) \
6566
V(stream_trailers, StreamTrailers)
6667

src/quic/http3.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,22 @@ class Http3ApplicationImpl final : public Session::Application {
12971297
return NGHTTP3_ERR_CALLBACK_FAILURE;
12981298
}
12991299

1300+
static int on_recv_wt_close_session(nghttp3_conn *conn,
1301+
int64_t session_id,
1302+
uint32_t wt_error_code,
1303+
const uint8_t *msg,
1304+
size_t msglen,
1305+
void *conn_user_data,
1306+
void *stream_user_data) {
1307+
NGHTTP3_CALLBACK_SCOPE(app);
1308+
auto& session = app.session();
1309+
if (auto stream = FindOrCreateStream(conn, &session, session_id)) [[likely]] {
1310+
stream->NotifyWTSessionClose(wt_error_code, msg, msglen);
1311+
return NGTCP2_SUCCESS;
1312+
}
1313+
return NGHTTP3_ERR_CALLBACK_FAILURE;
1314+
}
1315+
13001316
static int on_deferred_consume(nghttp3_conn* conn,
13011317
stream_id id,
13021318
size_t consumed,
@@ -1496,7 +1512,8 @@ class Http3ApplicationImpl final : public Session::Application {
14961512
on_rand,
14971513
on_receive_settings,
14981514
on_receive_wt_data,
1499-
on_wt_data_stream_open};
1515+
on_wt_data_stream_open,
1516+
on_recv_wt_close_session};
15001517
};
15011518

15021519
std::optional<PendingTicketAppData> ParseHttp3TicketData(const uv_buf_t& data) {

src/quic/streams.cc

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ namespace quic {
6262
V(WANTS_HEADERS, wants_headers, uint8_t) \
6363
/* Set when the stream has a sessionid event handler */ \
6464
V(WANTS_SESSIONID, wants_sessionid, uint8_t) \
65+
/* Set when the stream has a event handler for closing a WT session */ \
66+
V(WANTS_WTSESSIONCLOSE, wants_wtsessionclose, uint8_t) \
6567
/* Set when the stream has a reset event handler */ \
6668
V(WANTS_RESET, wants_reset, uint8_t) \
6769
/* Set when the stream has a trailers event handler */ \
@@ -1636,12 +1638,18 @@ bool Stream::AddHeader(std::unique_ptr<Header> header) {
16361638
return true;
16371639
}
16381640

1639-
void Stream::NotifyWTSession(stream_id session_id) {
1641+
void Stream::NotifyWTSession(stream_id session_id) {
16401642
if (state()->session_id != session_id) {
16411643
state()->session_id = session_id;
16421644
EmitSessionid(session_id);
16431645
}
1644-
}
1646+
}
1647+
1648+
void Stream::NotifyWTSessionClose(uint32_t wt_error_code,
1649+
const uint8_t *msg,
1650+
size_t msglen) {
1651+
EmitWTSessionClose(wt_error_code, msg, msglen);
1652+
}
16451653

16461654
void Stream::Acknowledge(size_t datalen) {
16471655
if (outbound_ == nullptr) return;
@@ -1992,6 +2000,22 @@ void Stream::EmitSessionid(stream_id session_id) {
19922000
MakeCallback(BindingData::Get(env()).stream_sessionid_callback(), 1, &sid);
19932001
}
19942002

2003+
2004+
void Stream::EmitWTSessionClose(uint32_t wt_error_code,
2005+
const uint8_t *msg,
2006+
size_t msglen) {
2007+
if (!env()->can_call_into_js() || !state()->wants_wtsessionclose) return;
2008+
CallbackScope<Stream> cb_scope(this);
2009+
Local<Value> argv[] = {
2010+
Integer::NewFromUnsigned(env()->isolate(),
2011+
wt_error_code),
2012+
String::NewFromUtf8(env()->isolate(), reinterpret_cast<const char *>(msg),
2013+
v8::NewStringType::kNormal, msglen).ToLocalChecked()
2014+
};
2015+
MakeCallback(BindingData::Get(env()).stream_wtsessionclose_callback(),
2016+
arraysize(argv), argv);
2017+
}
2018+
19952019
void Stream::EmitReset(const QuicError& error) {
19962020
// state()->wants_reset will be set from the javascript side if the
19972021
// stream object has a handler for the reset event.

src/quic/streams.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ class Stream final : public AsyncWrap,
359359
// Currently only http/3 can have a session stream in WebTransport
360360
void NotifyWTSession(stream_id session_id);
361361

362+
// Currently only http/3 can have a session stream that receives a close capsule
363+
void NotifyWTSessionClose(uint32_t wt_error_code,
364+
const uint8_t *msg,
365+
size_t msglen);
366+
362367
// TODO(@jasnell): Implement MemoryInfo to track outbound_, inbound_,
363368
// reader_, headers_, and pending_headers_queue_.
364369
SET_NO_MEMORY_INFO()
@@ -434,6 +439,11 @@ class Stream final : public AsyncWrap,
434439
// Delivers the session_id aka the stream that holds e.g. the WT session.
435440
void EmitSessionid(stream_id session_id);
436441

442+
// delivers the content of the close capsule
443+
void EmitWTSessionClose(uint32_t wt_error_code,
444+
const uint8_t *msg,
445+
size_t msglen);
446+
437447
void NotifyReadableEnded(error_code code);
438448
void NotifyWritableEnded(error_code code);
439449

0 commit comments

Comments
 (0)