@@ -2,6 +2,9 @@ import { consume, type ConsumerOptions } from "@ndn/endpoint";
22import { type Forwarder , type FwFace , TapFace } from "@ndn/fw" ;
33import { Interest , Name , type NameLike } from "@ndn/packet" ;
44import type { H3Transport } from "@ndn/quic-transport" ;
5+ import { assert } from "@ndn/util" ;
6+ import { pEvent } from "p-event" ;
7+ import type { Arrayable } from "type-fest" ;
58
69import { createFace } from "./platform_node" ;
710
@@ -50,7 +53,7 @@ export interface ConnectRouterOptions {
5053 * If string ends with "/*", it's replaced with a random component.
5154 * - function: execute the custom tester function.
5255 */
53- testConnection ?: false | TestConnectionPacket | TestConnectionPacket [ ] |
56+ testConnection ?: false | Arrayable < TestConnectionPacket > |
5457 ( ( face : FwFace ) => Promise < unknown > ) ;
5558
5659 /**
@@ -66,7 +69,10 @@ export interface ConnectRouterOptions {
6669 * Routes to be added on the created face.
6770 * @defaultValue `["/"]`
6871 */
69- addRoutes ?: NameLike [ ] ;
72+ addRoutes ?: readonly NameLike [ ] ;
73+
74+ /** AbortSignal that allows canceling the attempt via AbortController. */
75+ signal ?: AbortSignal ;
7076}
7177
7278/** {@link connectToRouter } result. */
@@ -83,14 +89,34 @@ export interface ConnectRouterResult {
8389
8490/** Connect to a router and test the connection. */
8591export async function connectToRouter ( router : string , opts : ConnectRouterOptions = { } ) : Promise < ConnectRouterResult > {
86- const face = await createFace ( router , opts ) ;
92+ const { signal } = opts ;
93+ let face : FwFace | undefined ;
94+ const promises : Array < Promise < void > > = [
95+ ( async ( ) => {
96+ // createFace does not take AbortSignal, but clear it to protect against future changes
97+ face = await createFace ( router , { ...opts , signal : undefined } ) ;
98+ } ) ( ) ,
99+ ] ;
100+ if ( signal ) {
101+ promises . push ( ( async ( ) => {
102+ if ( ! signal . aborted ) {
103+ await pEvent ( signal , "abort" ) ;
104+ }
105+ } ) ( ) ) ;
106+ }
107+ await Promise . race ( promises ) ;
108+ if ( ! face ) {
109+ assert ( signal ?. aborted ) ;
110+ throw signal . reason ; // eslint-disable-line @typescript-eslint/only-throw-error
111+ }
87112
88113 const testConnectionStart = performance . now ( ) ;
89114 let testConnectionDuration : number ;
90115 let testConnectionResult : unknown ;
91116 try {
92117 testConnectionResult = await testConnection ( face , opts ) ;
93118 testConnectionDuration = performance . now ( ) - testConnectionStart ;
119+ signal ?. throwIfAborted ( ) ;
94120 } catch ( err : unknown ) {
95121 face . close ( ) ;
96122 throw err ;
@@ -103,8 +129,11 @@ async function testConnection(
103129 {
104130 testConnection : tc = new Name ( "/localhop/nfd/rib/list" ) ,
105131 testConnectionTimeout = 2000 ,
132+ signal : parentSignal ,
106133 } : ConnectRouterOptions ,
107134) : Promise < unknown > {
135+ parentSignal ?. throwIfAborted ( ) ;
136+
108137 if ( tc === false ) {
109138 return undefined ;
110139 }
@@ -117,20 +146,23 @@ async function testConnection(
117146
118147 const tapFace = TapFace . create ( face ) ;
119148 tapFace . addRoute ( "/" ) ;
120- const abort = new AbortController ( ) ;
121- const cOpts : ConsumerOptions = { fw : tapFace . fw , signal : abort . signal } ;
149+ const raceAbort = new AbortController ( ) ;
150+ const cOpts : ConsumerOptions = {
151+ fw : tapFace . fw ,
152+ signal : parentSignal ? AbortSignal . any ( [ parentSignal , raceAbort . signal ] ) : raceAbort . signal ,
153+ } ;
122154 try {
123- await Promise . any ( tc . map ( ( pkt ) => {
155+ return await Promise . any ( tc . map ( async ( pkt , i ) => {
124156 if ( typeof pkt === "string" && pkt . endsWith ( "/*" ) ) {
125157 pkt = new Name ( pkt . slice ( 0 , - 2 ) ) . append ( Math . trunc ( Math . random ( ) * 1e8 ) . toString ( ) . padStart ( 8 , "0" ) ) ;
126158 }
127159 const interest = pkt instanceof Interest ? pkt :
128160 new Interest ( pkt , Interest . CanBePrefix , Interest . Lifetime ( testConnectionTimeout ) ) ;
129- return consume ( interest , cOpts ) ;
161+ const data = await consume ( interest , cOpts ) ;
162+ return { i, interest, data } ;
130163 } ) ) ;
131164 } finally {
132- abort . abort ( ) ;
165+ raceAbort . abort ( ) ;
133166 tapFace . close ( ) ;
134167 }
135- return undefined ;
136168}
0 commit comments