@@ -247,8 +247,34 @@ class QUICClient {
247247 // the client, because the client bridges the push flow from the connection
248248 // to the socket.
249249 socket . connectionMap . set ( connection . connectionId , connection ) ;
250+ // Set up intermediate abort signal
251+ const abortController = new AbortController ( ) ;
252+ const abortHandler = ( ) => {
253+ abortController . abort ( ctx . signal . reason ) ;
254+ } ;
255+ if ( ctx . signal . aborted ) abortController . abort ( ctx . signal . reason ) ;
256+ else ctx . signal . addEventListener ( 'abort' , abortHandler ) ;
257+ const handleEventQUICClientErrorSend = (
258+ evt : events . EventQUICClientErrorSend ,
259+ ) => {
260+ // @ts -ignore: the error contains `code` but not part of the type
261+ if ( evt . detail . code === 'EINVAL' ) {
262+ abortController . abort (
263+ new errors . ErrorQUICClientInvalidArgument ( undefined , {
264+ cause : evt . detail ,
265+ } ) ,
266+ ) ;
267+ }
268+ } ;
269+ client . addEventListener (
270+ `${ events . EventQUICClientErrorSend . name } -${ connection . sendId } ` ,
271+ handleEventQUICClientErrorSend ,
272+ ) ;
250273 try {
251- await connection . start ( undefined , ctx ) ;
274+ await connection . start ( undefined , {
275+ timer : ctx . timer ,
276+ signal : abortController . signal ,
277+ } ) ;
252278 } catch ( e ) {
253279 socket . connectionMap . delete ( connection . connectionId ) ;
254280 socket . removeEventListener (
@@ -284,6 +310,12 @@ class QUICClient {
284310 client . handleEventQUICClientClose ,
285311 ) ;
286312 throw e ;
313+ } finally {
314+ ctx . signal . removeEventListener ( 'abort' , abortHandler ) ;
315+ client . removeEventListener (
316+ `${ events . EventQUICClientErrorSend . name } -${ connection . sendId } ` ,
317+ handleEventQUICClientErrorSend ,
318+ ) ;
287319 }
288320 address = utils . buildAddress ( host_ , port ) ;
289321 logger . info ( `Created ${ this . name } to ${ address } ` ) ;
@@ -299,6 +331,10 @@ class QUICClient {
299331 protected config : Config ;
300332 protected _closed : boolean = false ;
301333 protected resolveClosedP : ( ) => void ;
334+ /**
335+ * Flag used to make sure network fail warnings are only logged once per failure
336+ */
337+ protected networkWarned : boolean = false ;
302338
303339 /**
304340 * Handles `EventQUICClientError`.
@@ -458,15 +494,49 @@ class QUICClient {
458494 evt . detail . port ,
459495 evt . detail . address ,
460496 ) ;
497+ this . networkWarned = false ;
461498 } catch ( e ) {
462- const e_ = new errors . ErrorQUICClientInternal (
463- 'Failed to send data on the QUICSocket' ,
464- {
465- data : evt . detail ,
466- cause : e ,
467- } ,
468- ) ;
469- this . dispatchEvent ( new events . EventQUICClientError ( { detail : e_ } ) ) ;
499+ switch ( e . code ) {
500+ case 'EINVAL' :
501+ {
502+ this . dispatchEvent (
503+ new events . EventQUICClientErrorSend (
504+ `${ events . EventQUICClientErrorSend . name } -${ evt . detail . id } ` ,
505+ {
506+ detail : e ,
507+ } ,
508+ ) ,
509+ ) ;
510+ }
511+ break ;
512+ case 'ENETUNREACH' :
513+ {
514+ // We consider this branch a temp failure.
515+ // For these error codes we rely on the connection's timeout to handle.
516+ if ( ! this . networkWarned ) {
517+ this . logger . warn (
518+ `client send failed with 'ENETUNREACH', likely due to network failure` ,
519+ ) ;
520+ this . networkWarned = true ;
521+ }
522+ }
523+ break ;
524+ default :
525+ {
526+ this . dispatchEvent (
527+ new events . EventQUICClientError ( {
528+ detail : new errors . ErrorQUICClientInternal (
529+ 'Failed to send data on the QUICSocket' ,
530+ {
531+ data : evt . detail ,
532+ cause : e ,
533+ } ,
534+ ) ,
535+ } ) ,
536+ ) ;
537+ }
538+ break ;
539+ }
470540 }
471541 } ;
472542
0 commit comments