Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit 147150c

Browse files
jasnelladuh95
authored andcommitted
quic: add listEndpoints API
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63536 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 3b59d12 commit 147150c

5 files changed

Lines changed: 161 additions & 43 deletions

File tree

β€Ždoc/api/quic.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ with either a `QuicEndpoint` or `EndpointOptions` as the argument.
511511
At most, any single `QuicEndpoint` can only be configured to listen as
512512
a server once.
513513

514+
## `quic.listEndpoints([options])`
515+
516+
<!-- YAML
517+
added: REPLACEME
518+
-->
519+
520+
*`options` {object}
521+
*`active` {boolean} If `true` (the default), only returns endpoints that are
522+
active (not destroyed, not closing, and not busy). If `false` returns all
523+
endpoints.
524+
* Returns: {quic.QuicEndpoint\[]}
525+
526+
Returns the list of all `QuicEndpoint` instances. By default, only active
527+
endpoints are returned.
528+
514529
## `quic.constants`
515530

516531
<!-- YAML

β€Žlib/internal/quic/quic.jsβ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* c8 ignore start */
66

77
const{
8+
ArrayFrom,
89
ArrayIsArray,
910
ArrayPrototypePush,
1011
BigInt,
@@ -4737,6 +4738,24 @@ function findSuitableEndpoint(targetAddress) {
47374738
returnundefined;
47384739
}
47394740

4741+
/**
4742+
* Returns a list of all active endpoints.
4743+
* @param {object} [options]
4744+
* @param {boolean} [options.active] When true, only return endpoints that are not destroyed, closing, or busy.
4745+
* @returns {QuicEndpoint[]}
4746+
*/
4747+
functionlistEndpoints(options=kEmptyObject){
4748+
validateObject(options,'options');
4749+
const{ active =true}=options;
4750+
validateBoolean(active,'options.active');
4751+
if(!active){
4752+
returnArrayFrom(endpointRegistry);
4753+
}
4754+
returnArrayFrom(endpointRegistry).filter((endpoint)=>{
4755+
return!endpoint.destroyed&&!endpoint.closing&&!endpoint.busy;
4756+
});
4757+
}
4758+
47404759
/**
47414760
* @param {EndpointOptions|QuicEndpoint|undefined} endpoint
47424761
* @param {boolean} reuseEndpoint
@@ -5340,6 +5359,7 @@ module.exports = {
53405359
getQuicStreamState,
53415360
getQuicSessionState,
53425361
getQuicEndpointState,
5362+
listEndpoints,
53435363
};
53445364

53455365
/* c8 ignore stop */

β€Žlib/quic.jsβ€Ž

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const{
4-
ObjectCreate,
5-
ObjectSeal,
6-
}=primordials;
7-
83
const{
94
emitExperimentalWarning,
105
}=require('internal/util');
@@ -24,43 +19,32 @@ const {
2419
DEFAULT_GROUPS,
2520
}=require('internal/quic/quic');
2621

27-
functiongetEnumerableConstant(value){
28-
return{
29-
__proto__: null,
30-
value,
31-
enumerable: true,
32-
configurable: false,
33-
writable: false,
34-
};
35-
}
36-
37-
constcc=ObjectSeal(ObjectCreate(null,{
38-
__proto__: null,
39-
RENO: getEnumerableConstant(CC_ALGO_RENO),
40-
CUBIC: getEnumerableConstant(CC_ALGO_CUBIC),
41-
BBR: getEnumerableConstant(CC_ALGO_BBR),
42-
}));
22+
constcc={
23+
getRENO(){returnCC_ALGO_RENO;},
24+
getCUBIC(){returnCC_ALGO_CUBIC;},
25+
getBBR(){returnCC_ALGO_BBR;},
26+
};
4327

44-
constconstants=ObjectSeal(ObjectCreate(null,{
45-
__proto__: null,
46-
cc: getEnumerableConstant(cc),
47-
DEFAULT_CIPHERS: getEnumerableConstant(DEFAULT_CIPHERS),
48-
DEFAULT_GROUPS: getEnumerableConstant(DEFAULT_GROUPS),
49-
}));
28+
constconstants={
29+
getcc(){returncc;},
30+
getDEFAULT_CIPHERS(){returnDEFAULT_CIPHERS;},
31+
getDEFAULT_GROUPS(){returnDEFAULT_GROUPS;},
32+
};
5033

51-
module.exports=ObjectSeal(ObjectCreate(null,{
52-
__proto__: null,
53-
connect: getEnumerableConstant(connect),
54-
listen: getEnumerableConstant(listen),
55-
QuicEndpoint: getEnumerableConstant(QuicEndpoint),
56-
QuicError: getEnumerableConstant(QuicError),
57-
QuicSession: getEnumerableConstant(QuicSession),
58-
QuicStream: getEnumerableConstant(QuicStream),
59-
constants: getEnumerableConstant(constants),
34+
module.exports={
35+
connect,
36+
listen,
37+
QuicEndpoint,
38+
QuicError,
39+
QuicSession,
40+
QuicStream,
41+
getconstants(){
42+
returnconstants;
43+
},
6044

6145
CC_ALGO_RENO,
6246
CC_ALGO_CUBIC,
6347
CC_ALGO_BBR,
6448
DEFAULT_CIPHERS,
6549
DEFAULT_GROUPS,
66-
}));
50+
};

β€Žtest/parallel/test-quic-exports.mjsβ€Ž

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,5 @@ strictEqual(quic.constants.DEFAULT_GROUPS, 'X25519:P-256:P-384:P-521');
3535
throws(()=>{quic.constants.cc.RENO='foo';},TypeError);
3636
strictEqual(quic.constants.cc.RENO,'reno');
3737

38-
throws(()=>{quic.constants.cc.NEW_CONSTANT='bar';},TypeError);
39-
strictEqual(quic.constants.cc.NEW_CONSTANT,undefined);
40-
4138
throws(()=>{quic.constants.DEFAULT_CIPHERS=123;},TypeError);
4239
strictEqual(typeofquic.constants.DEFAULT_CIPHERS,'string');
43-
44-
throws(()=>{quic.constants.NEW_CONSTANT=456;},TypeError);
45-
strictEqual(quic.constants.NEW_CONSTANT,undefined);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Flags: --experimental-quic --no-warnings
2+
3+
// Test: listEndpoints returns live endpoints filtered by active state.
4+
// - Returns an empty array when no endpoints exist.
5+
// - Includes all created endpoints.
6+
// - active=true (default) filters out destroyed, closing, and busy endpoints.
7+
// - active=false returns all registered endpoints regardless of state.
8+
// - Validates options argument types.
9+
10+
import{hasQuic,skip}from'../common/index.mjs';
11+
importassertfrom'node:assert';
12+
13+
const{ deepStrictEqual, strictEqual, throws, ok }=assert;
14+
15+
if(!hasQuic){
16+
skip('QUIC is not enabled');
17+
}
18+
19+
const{ listEndpoints, QuicEndpoint }=awaitimport('node:quic');
20+
21+
// listEndpoints is exported as a function.
22+
strictEqual(typeoflistEndpoints,'function');
23+
24+
// Empty when no endpoints have been created.
25+
deepStrictEqual(listEndpoints(),[]);
26+
deepStrictEqual(listEndpoints({active: false}),[]);
27+
28+
// Created endpoints appear in the list.
29+
{
30+
constep1=newQuicEndpoint();
31+
constep2=newQuicEndpoint();
32+
33+
constlist=listEndpoints();
34+
strictEqual(list.length,2);
35+
ok(list.includes(ep1));
36+
ok(list.includes(ep2));
37+
38+
// active=false also returns them.
39+
constall=listEndpoints({active: false});
40+
strictEqual(all.length,2);
41+
ok(all.includes(ep1));
42+
ok(all.includes(ep2));
43+
44+
// Returns plain QuicEndpoint instances, not wrapped in arrays.
45+
ok(list[0]instanceofQuicEndpoint);
46+
ok(list[1]instanceofQuicEndpoint);
47+
48+
awaitep1.close();
49+
awaitep2.close();
50+
}
51+
52+
// Destroyed endpoints are excluded by active filter.
53+
{
54+
constep=newQuicEndpoint();
55+
strictEqual(listEndpoints().length,1);
56+
57+
awaitep.close();
58+
awaitep.closed;
59+
strictEqual(ep.destroyed,true);
60+
61+
// Default (active=true) excludes destroyed endpoint.
62+
strictEqual(listEndpoints().length,0);
63+
64+
// active=false still includes it... but destroyed endpoints are removed
65+
// from the registry entirely in kFinishClose, so it won't appear at all.
66+
strictEqual(listEndpoints({active: false}).length,0);
67+
}
68+
69+
// Busy endpoints are excluded by the active filter.
70+
{
71+
constep=newQuicEndpoint();
72+
strictEqual(listEndpoints().length,1);
73+
74+
ep.busy=true;
75+
strictEqual(ep.busy,true);
76+
77+
// active=true excludes busy endpoint.
78+
strictEqual(listEndpoints().length,0);
79+
80+
// active=false includes it.
81+
constall=listEndpoints({active: false});
82+
strictEqual(all.length,1);
83+
ok(all.includes(ep));
84+
85+
ep.busy=false;
86+
87+
// After un-busying, it reappears in the active list.
88+
strictEqual(listEndpoints().length,1);
89+
90+
awaitep.close();
91+
}
92+
93+
// Options validation.
94+
throws(()=>listEndpoints('bad'),{
95+
code: 'ERR_INVALID_ARG_TYPE',
96+
});
97+
throws(()=>listEndpoints(null),{
98+
code: 'ERR_INVALID_ARG_TYPE',
99+
});
100+
throws(()=>listEndpoints({active: 'yes'}),{
101+
code: 'ERR_INVALID_ARG_TYPE',
102+
});
103+
throws(()=>listEndpoints({active: 1}),{
104+
code: 'ERR_INVALID_ARG_TYPE',
105+
});

0 commit comments

Comments
Β (0)