99 ArrayPrototypePush,
1010 BigInt,
1111 DataViewPrototypeGetByteLength,
12+ ErrorCaptureStackTrace,
1213 FunctionPrototypeBind,
1314 Number,
1415 ObjectDefineProperties,
@@ -108,13 +109,11 @@ const {
108109 ERR_INVALID_THIS ,
109110 ERR_MISSING_ARGS ,
110111 ERR_OUT_OF_RANGE ,
111- ERR_QUIC_APPLICATION_ERROR ,
112112 ERR_QUIC_CONNECTION_FAILED ,
113113 ERR_QUIC_ENDPOINT_CLOSED ,
114114 ERR_QUIC_OPEN_STREAM_FAILED ,
115115 ERR_QUIC_STREAM_ABORTED ,
116116 ERR_QUIC_STREAM_RESET ,
117- ERR_QUIC_TRANSPORT_ERROR ,
118117 ERR_QUIC_VERSION_NEGOTIATION_ERROR ,
119118 } ,
120119} = require ( 'internal/errors' ) ;
@@ -738,10 +737,12 @@ setCallbacks({
738737 * @param {number } errorType
739738 * @param {number } code
740739 * @param {string } [reason]
740+ * @param {string } [errorName] Decoded TLS alert name when `code` is a
741+ * CRYPTO_ERROR; otherwise undefined.
741742 */
742- onSessionClose ( errorType , code , reason ) {
743- debug ( 'session close callback' , errorType , code , reason ) ;
744- this [ kOwner ] [ kFinishClose ] ( errorType , code , reason ) ;
743+ onSessionClose ( errorType , code , reason , errorName ) {
744+ debug ( 'session close callback' , errorType , code , reason , errorName ) ;
745+ this [ kOwner ] [ kFinishClose ] ( errorType , code , reason , errorName ) ;
745746 } ,
746747
747748 /**
@@ -931,8 +932,12 @@ setCallbacks({
931932 // was an abnormal termination even if the session closed cleanly.
932933 const resetCode = getQuicStreamState ( this [ kOwner ] ) . resetCode ;
933934 if ( resetCode !== undefined && resetCode > 0n ) {
934- error = new ERR_QUIC_APPLICATION_ERROR (
935- resetCode , `stream reset with code ${ resetCode } ` ) ;
935+ error = makeQuicError (
936+ 'ERR_QUIC_APPLICATION_ERROR' ,
937+ 'QUIC application error' ,
938+ 'application' ,
939+ resetCode ,
940+ `stream reset with code ${ resetCode } ` ) ;
936941 }
937942 }
938943 debug ( `stream ${ this [ kOwner ] . id } closed callback with error: ${ error } ` ) ;
@@ -1054,21 +1059,50 @@ class QuicError extends Error {
10541059 }
10551060}
10561061
1057- // Converts a raw QuicError array [type, code, reason] from C++ into a
1058- // proper Node.js Error object.
1062+ // Build the human-readable message for an ERR_QUIC_TRANSPORT_ERROR or
1063+ // ERR_QUIC_APPLICATION_ERROR. `errorName` is the symbolic name for
1064+ // the wire code when known: either the OpenSSL-decoded TLS alert
1065+ // (CRYPTO_ERROR; 0x100..0x1ff) or one of the named transport codes
1066+ // from RFC 9000 (e.g. PROTOCOL_VIOLATION). Otherwise undefined.
1067+ // `reason` is the peer-supplied UTF-8 reason string from the
1068+ // CONNECTION_CLOSE / RESET_STREAM frame, often empty.
1069+ function quicErrorMessage ( prefix , errorCode , reason , errorName ) {
1070+ let msg = `${ prefix } ` ;
1071+ msg += errorName ? `${ errorName } (${ errorCode } )` : `${ errorCode } ` ;
1072+ if ( reason ) msg += `: ${ reason } ` ;
1073+ return msg ;
1074+ }
1075+
1076+ function makeQuicError ( code , prefix , type , errorCode , reason , errorName ) {
1077+ const err = new QuicError (
1078+ quicErrorMessage ( prefix , errorCode , reason , errorName ) ,
1079+ { errorCode, code, type } ) ;
1080+ ErrorCaptureStackTrace ( err , makeQuicError ) ;
1081+ if ( reason ) err . reason = reason ;
1082+ if ( errorName ) err . errorName = errorName ;
1083+ return err ;
1084+ }
1085+
10591086function convertQuicError ( error ) {
10601087 const type = error [ 0 ] ;
10611088 const code = error [ 1 ] ;
10621089 const reason = error [ 2 ] ;
1090+ const errorName = error [ 3 ] ;
10631091 switch ( type ) {
10641092 case 'transport' :
1065- return new ERR_QUIC_TRANSPORT_ERROR ( code , reason ) ;
1093+ return makeQuicError ( 'ERR_QUIC_TRANSPORT_ERROR' ,
1094+ 'QUIC transport error' ,
1095+ 'transport' , code , reason , errorName ) ;
10661096 case 'application' :
1067- return new ERR_QUIC_APPLICATION_ERROR ( code , reason ) ;
1097+ return makeQuicError ( 'ERR_QUIC_APPLICATION_ERROR' ,
1098+ 'QUIC application error' ,
1099+ 'application' , code , reason , errorName ) ;
10681100 case 'version_negotiation' :
10691101 return new ERR_QUIC_VERSION_NEGOTIATION_ERROR ( ) ;
10701102 default :
1071- return new ERR_QUIC_TRANSPORT_ERROR ( code , reason ) ;
1103+ return makeQuicError ( 'ERR_QUIC_TRANSPORT_ERROR' ,
1104+ 'QUIC transport error' ,
1105+ 'transport' , code , reason , errorName ) ;
10721106 }
10731107}
10741108
@@ -3575,7 +3609,7 @@ class QuicSession {
35753609 * @param {number } code
35763610 * @param {string } [reason]
35773611 */
3578- [ kFinishClose ] ( errorType , code , reason ) {
3612+ [ kFinishClose ] ( errorType , code , reason , errorName ) {
35793613 // If code is zero, then we closed without an error. Yay! We can destroy
35803614 // safely without specifying an error.
35813615 if ( code === 0n ) {
@@ -3584,7 +3618,8 @@ class QuicSession {
35843618 return ;
35853619 }
35863620
3587- debug ( 'finishing closing the session with an error' , errorType , code , reason ) ;
3621+ debug ( 'finishing closing the session with an error' ,
3622+ errorType , code , reason , errorName ) ;
35883623
35893624 // If the local side initiated this close with an error code (via
35903625 // close({ code })), this is an intentional shutdown; not an error.
@@ -3611,10 +3646,14 @@ class QuicSession {
36113646 // session would leak with `closed` hanging forever.
36123647 switch ( errorType ) {
36133648 case 0 : /* Transport Error */
3614- this . destroy ( new ERR_QUIC_TRANSPORT_ERROR ( code , reason ) ) ;
3649+ this . destroy ( makeQuicError ( 'ERR_QUIC_TRANSPORT_ERROR' ,
3650+ 'QUIC transport error' ,
3651+ 'transport' , code , reason , errorName ) ) ;
36153652 break ;
36163653 case 1 : /* Application Error */
3617- this . destroy ( new ERR_QUIC_APPLICATION_ERROR ( code , reason ) ) ;
3654+ this . destroy ( makeQuicError ( 'ERR_QUIC_APPLICATION_ERROR' ,
3655+ 'QUIC application error' ,
3656+ 'application' , code , reason , errorName ) ) ;
36183657 break ;
36193658 case 2 : /* Version Negotiation Error */
36203659 this . destroy ( new ERR_QUIC_VERSION_NEGOTIATION_ERROR ( ) ) ;
@@ -3623,7 +3662,9 @@ class QuicSession {
36233662 this . destroy ( ) ;
36243663 break ;
36253664 default :
3626- this . destroy ( new ERR_QUIC_TRANSPORT_ERROR ( code , reason ) ) ;
3665+ this . destroy ( makeQuicError ( 'ERR_QUIC_TRANSPORT_ERROR' ,
3666+ 'QUIC transport error' ,
3667+ 'transport' , code , reason , errorName ) ) ;
36273668 break ;
36283669 }
36293670 }
@@ -3874,9 +3915,13 @@ class QuicSession {
38743915 // decide. In 'strict' mode, the handshake already failed at the C++
38753916 // level (SSL_VERIFY_PEER) so we won't reach here.
38763917 if ( inner . verifyPeer === 'auto' && validationErrorReason !== undefined ) {
3877- const err = new ERR_QUIC_TRANSPORT_ERROR (
3878- 0 , `Peer certificate validation failed: ${ validationErrorReason } ` +
3879- ` [${ validationErrorCode } ]` ) ;
3918+ const err = makeQuicError (
3919+ 'ERR_QUIC_TRANSPORT_ERROR' ,
3920+ 'QUIC transport error' ,
3921+ 'transport' ,
3922+ 0n ,
3923+ `Peer certificate validation failed: ${ validationErrorReason } ` +
3924+ ` [${ validationErrorCode } ]` ) ;
38803925 inner . pendingOpen . reject ?. ( err ) ;
38813926 inner . pendingOpen . resolve = undefined ;
38823927 inner . pendingOpen . reject = undefined ;
0 commit comments