Skip to content

Commit 6d77818

Browse files
committed
quic: WT implement close session callback
1 parent c11fb3c commit 6d77818

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
@@ -191,6 +191,7 @@ const {
191191
kVerifyPeer,
192192
kHeaders,
193193
kSessionId,
194+
kWTSessionClose,
194195
kOwner,
195196
kRemoveSession,
196197
kKeylog,
@@ -266,6 +267,7 @@ const {
266267
onStreamClosedChannel,
267268
onStreamHeadersChannel,
268269
onStreamSessionIdChannel,
270+
onStreamWTSessionCloseChannel,
269271
onStreamTrailersChannel,
270272
onStreamInfoChannel,
271273
onStreamResetChannel,
@@ -306,6 +308,7 @@ const endpointRegistry = new SafeSet();
306308
* webtransport support. If no headers are provided, it has no effect.
307309
* @property {OnHeadersCallback} [onheaders] Callback for incoming initial headers
308310
* @property {OnSessionIdCallback} [onsessionid] Callback for incoming sessionid
311+
* @property {OnWTSessionCloseCallback} [onwtsessionclose] Callback for incoming close capsules
309312
* @property {OnTrailersCallback} [ontrailers] Callback for incoming trailing headers
310313
* @property {OnInfoCallback} [oninfo] Callback for informational (1xx) headers
311314
* @property {OnWantTrailersCallback} [onwanttrailers] Callback fired when the
@@ -469,6 +472,7 @@ const endpointRegistry = new SafeSet();
469472
* @property {OnApplicationCallback} [onapplication] application options callback.
470473
* @property {OnHeadersCallback} [onheaders] Default per-stream initial-headers callback.
471474
* @property {OnSessionIdCallback} [onsessionid] Default perstream initial callback for incoming sessionid.
475+
* @property {OnWTSessionCloseCallback} [onwtsessionclose] Default perstream initial callback for incoming close capsules
472476
* @property {OnTrailersCallback} [ontrailers] Default per-stream trailing-headers callback.
473477
* @property {OnInfoCallback} [oninfo] Default per-stream informational-headers callback.
474478
* @property {OnWantTrailersCallback} [onwanttrailers] Default per-stream
@@ -725,6 +729,15 @@ const endpointRegistry = new SafeSet();
725729
* @returns {void}
726730
*/
727731

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

729742

730743
/**
@@ -1021,6 +1034,12 @@ setCallbacks({
10211034
this[kOwner][kSessionId](sessionId);
10221035
},
10231036

1037+
onStreamWTSessionClose(errorcode, errormessage) {
1038+
// Called when the stream C++ handle has received a close capsule
1039+
debug(`stream ${this[kOwner].id} wtsessionclose callback`, errorcode, errormessage);
1040+
this[kOwner][kWTSessionClose](errorcode, errormessage);
1041+
},
1042+
10241043
onStreamTrailers() {
10251044
// Called when the stream C++ handle is ready to receive trailing headers.
10261045
debug('stream want trailers callback', this[kOwner]);
@@ -1343,6 +1362,7 @@ function applyCallbacks(session, cbs) {
13431362
};
13441363
}
13451364
if (cbs.onsessionid) session.onsessionid = cbs.onsessionid;
1365+
if (cbs.onwtsessionclose) session.onwtsessionclose = cbs.onwtsessionclose;
13461366
}
13471367

13481368
/**
@@ -1603,6 +1623,7 @@ class QuicStream {
16031623
onreset: undefined,
16041624
onheaders: undefined,
16051625
onsessionid: undefined,
1626+
onwtsessionclose: undefined,
16061627
ontrailers: undefined,
16071628
oninfo: undefined,
16081629
onwanttrailers: undefined,
@@ -1846,6 +1867,26 @@ class QuicStream {
18461867
}
18471868
}
18481869

1870+
/** @type {OnWTSessionCloseCallback} */
1871+
get onwtsessionclose() {
1872+
assertIsQuicStream(this);
1873+
return this.#inner.onwtsessionclose;
1874+
}
1875+
1876+
set onwtsessionclose(fn) {
1877+
assertIsQuicStream(this);
1878+
const inner = this.#inner;
1879+
if (fn === undefined) {
1880+
inner.onwtsessionclose = undefined;
1881+
inner.state.wantsWTSessionClose = false;
1882+
} else {
1883+
console.log('Set wantsWTSessionClose');
1884+
validateFunction(fn, 'onwtsessionclose');
1885+
inner.onwtsessionclose = FunctionPrototypeBind(fn, this);
1886+
inner.state.wantsWTSessionClose = true;
1887+
}
1888+
}
1889+
18491890
/** @type {Function|undefined} */
18501891
get oninfo() {
18511892
assertIsQuicStream(this);
@@ -2610,6 +2651,7 @@ class QuicStream {
26102651
inner.onreset = undefined;
26112652
inner.onheaders = undefined;
26122653
inner.onsessionid = undefined;
2654+
inner.onwtsessionclose = undefined;
26132655
inner.onerror = undefined;
26142656
inner.ontrailers = undefined;
26152657
inner.oninfo = undefined;
@@ -2724,6 +2766,23 @@ class QuicStream {
27242766
}
27252767
}
27262768

2769+
[kWTSessionClose](errorcode, errormessage) {
2770+
if (this.destroyed) return;
2771+
const inner = this.#inner;
2772+
if (onStreamWTSessionCloseChannel.hasSubscribers) {
2773+
onStreamWTSessionCloseChannel.publish({
2774+
__proto__: null,
2775+
stream: this,
2776+
session: inner.session,
2777+
errorcode,
2778+
errormessage
2779+
});
2780+
}
2781+
if (typeof inner.onwtsessionclose === 'function') {
2782+
safeCallbackInvoke(inner.onwtsessionclose, this, errorcode, errormessage);
2783+
}
2784+
}
2785+
27272786
[kTrailers]() {
27282787
if (this.destroyed) return;
27292788
const inner = this.#inner;
@@ -3331,6 +3390,7 @@ class QuicSession {
33313390
headers,
33323391
onheaders,
33333392
onsessionid,
3393+
onwtsessionclose,
33343394
ontrailers,
33353395
oninfo,
33363396
onwanttrailers,
@@ -3388,6 +3448,7 @@ class QuicSession {
33883448
// Set stream callbacks before sending headers to avoid missing events.
33893449
if (onheaders) stream.onheaders = onheaders;
33903450
if (onsessionid) stream.onsessionid = onsessionid;
3451+
if (onwtsessionclose) stream.onwtsessionclose = onwtsessionclose;
33913452
if (ontrailers) stream.ontrailers = ontrailers;
33923453
if (oninfo) stream.oninfo = oninfo;
33933454
if (onwanttrailers) stream.onwanttrailers = onwanttrailers;
@@ -4172,6 +4233,7 @@ class QuicSession {
41724233
if (scbs) {
41734234
if (scbs.onheaders) stream.onheaders = scbs.onheaders;
41744235
if (scbs.onsessionid) stream.onsessionid = scbs.onsessionid;
4236+
if (scbs.onwtsessionclose) stream.onwtsessionclose = scbs.onwtsessionclose;
41754237
if (scbs.ontrailers) stream.ontrailers = scbs.ontrailers;
41764238
if (scbs.oninfo) stream.oninfo = scbs.oninfo;
41774239
if (scbs.onwanttrailers) stream.onwanttrailers = scbs.onwanttrailers;
@@ -4550,6 +4612,7 @@ class QuicEndpoint {
45504612
// Stream-level callbacks applied to each incoming stream.
45514613
onheaders,
45524614
onsessionid,
4615+
onwtsessionclose,
45534616
ontrailers,
45544617
oninfo,
45554618
onwanttrailers,
@@ -4576,6 +4639,7 @@ class QuicEndpoint {
45764639
onapplication,
45774640
onheaders,
45784641
onsessionid,
4642+
onwtsessionclose,
45794643
ontrailers,
45804644
oninfo,
45814645
onwanttrailers,
@@ -5312,6 +5376,7 @@ function processSessionOptions(options, config = kEmptyObject) {
53125376
// Stream-level callbacks.
53135377
onheaders,
53145378
onsessionid,
5379+
onwtsessionclose,
53155380
ontrailers,
53165381
oninfo,
53175382
onwanttrailers,
@@ -5434,6 +5499,7 @@ function processSessionOptions(options, config = kEmptyObject) {
54345499
onapplication,
54355500
onheaders,
54365501
onsessionid,
5502+
onwtsessionclose,
54375503
ontrailers,
54385504
oninfo,
54395505
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 */ \
@@ -1628,12 +1630,18 @@ bool Stream::AddHeader(std::unique_ptr<Header> header) {
16281630
return true;
16291631
}
16301632

1631-
void Stream::NotifyWTSession(stream_id session_id) {
1633+
void Stream::NotifyWTSession(stream_id session_id) {
16321634
if (state()->session_id != session_id) {
16331635
state()->session_id = session_id;
16341636
EmitSessionid(session_id);
16351637
}
1636-
}
1638+
}
1639+
1640+
void Stream::NotifyWTSessionClose(uint32_t wt_error_code,
1641+
const uint8_t *msg,
1642+
size_t msglen) {
1643+
EmitWTSessionClose(wt_error_code, msg, msglen);
1644+
}
16371645

16381646
void Stream::Acknowledge(size_t datalen) {
16391647
if (outbound_ == nullptr) return;
@@ -1984,6 +1992,22 @@ void Stream::EmitSessionid(stream_id session_id) {
19841992
MakeCallback(BindingData::Get(env()).stream_sessionid_callback(), 1, &sid);
19851993
}
19861994

1995+
1996+
void Stream::EmitWTSessionClose(uint32_t wt_error_code,
1997+
const uint8_t *msg,
1998+
size_t msglen) {
1999+
if (!env()->can_call_into_js() || !state()->wants_wtsessionclose) return;
2000+
CallbackScope<Stream> cb_scope(this);
2001+
Local<Value> argv[] = {
2002+
Integer::NewFromUnsigned(env()->isolate(),
2003+
wt_error_code),
2004+
String::NewFromUtf8(env()->isolate(), reinterpret_cast<const char *>(msg),
2005+
v8::NewStringType::kNormal, msglen).ToLocalChecked()
2006+
};
2007+
MakeCallback(BindingData::Get(env()).stream_wtsessionclose_callback(),
2008+
arraysize(argv), argv);
2009+
}
2010+
19872011
void Stream::EmitReset(const QuicError& error) {
19882012
// state()->wants_reset will be set from the javascript side if the
19892013
// 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)