99 ArrayPrototypePush,
1010 BigInt,
1111 DataViewPrototypeGetByteLength,
12+ ErrorCaptureStackTrace,
1213 FunctionPrototypeBind,
1314 Number,
1415 ObjectDefineProperties,
@@ -108,13 +109,11 @@ const {
108109ERR_INVALID_THIS ,
109110ERR_MISSING_ARGS ,
110111ERR_OUT_OF_RANGE ,
111- ERR_QUIC_APPLICATION_ERROR ,
112112ERR_QUIC_CONNECTION_FAILED ,
113113ERR_QUIC_ENDPOINT_CLOSED ,
114114ERR_QUIC_OPEN_STREAM_FAILED ,
115115ERR_QUIC_STREAM_ABORTED ,
116116ERR_QUIC_STREAM_RESET ,
117- ERR_QUIC_TRANSPORT_ERROR ,
118117ERR_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.
932933const resetCode = getQuicStreamState ( this [ kOwner ] ) . resetCode ;
933934if ( 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}
938943debug ( `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 ) {
10601087const type = error [ 0 ] ;
10611088const code = error [ 1 ] ;
10621089const reason = error [ 2 ] ;
1090+ const errorName = error [ 3 ] ;
10631091switch ( type ) {
10641092case '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 ) ;
10661096case '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 ) ;
10681100case 'version_negotiation' :
10691101return new ERR_QUIC_VERSION_NEGOTIATION_ERROR ( ) ;
10701102default :
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.
35813615if ( code === 0n ) {
@@ -3584,7 +3618,8 @@ class QuicSession {
35843618return ;
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.
36123647switch ( errorType ) {
36133648case 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 ) ) ;
36153652break ;
36163653case 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 ) ) ;
36183657break ;
36193658case 2 : /* Version Negotiation Error */
36203659this . destroy ( new ERR_QUIC_VERSION_NEGOTIATION_ERROR ( ) ) ;
@@ -3623,7 +3662,9 @@ class QuicSession {
36233662this . destroy ( ) ;
36243663break ;
36253664default :
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 ) ) ;
36273668break ;
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.
38763917if ( 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 } ]` ) ;
38803925inner . pendingOpen . reject ?. ( err ) ;
38813926inner . pendingOpen . resolve = undefined ;
38823927inner . pendingOpen . reject = undefined ;
0 commit comments