| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b68f8ea commit 3b59d12
13 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,7 +23,7 @@ const noRestrictedSyntax = [ | |||
| 23 | 23 | message: "`btoa` supports only latin-1 charset, use Buffer.from(str).toString('base64') instead", | |
| 24 | 24 | }, | |
| 25 | 25 | { | |
| 26 | - selector: 'NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError)$/])', | ||
| 26 | + selector: 'NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError|QuicError)$/])', | ||
| 27 | 27 | message: "Use an error exported by 'internal/errors' instead.", | |
| 28 | 28 | }, | |
| 29 | 29 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1676,14 +1676,12 @@ E('ERR_PERFORMANCE_INVALID_TIMESTAMP', | |||
| 1676 | 1676 | E('ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS', '%s', TypeError); | |
| 1677 | 1677 | E('ERR_PROXY_INVALID_CONFIG', '%s', Error); | |
| 1678 | 1678 | E('ERR_PROXY_TUNNEL', '%s', Error); | |
| 1679 | - E('ERR_QUIC_APPLICATION_ERROR', 'A QUIC application error occurred. %d [%s]', Error); | ||
| 1680 | 1679 | E('ERR_QUIC_CONNECTION_FAILED', 'QUIC connection failed', Error); | |
| 1681 | 1680 | E('ERR_QUIC_ENDPOINT_CLOSED', 'QUIC endpoint closed: %s (%d)', Error); | |
| 1682 | 1681 | E('ERR_QUIC_OPEN_STREAM_FAILED', 'Failed to open QUIC stream', Error); | |
| 1683 | 1682 | E('ERR_QUIC_STREAM_ABORTED', '%s', Error); | |
| 1684 | 1683 | E('ERR_QUIC_STREAM_RESET', | |
| 1685 | 1684 | 'The QUIC stream was reset by the peer with error code %d', Error); | |
| 1686 | - E('ERR_QUIC_TRANSPORT_ERROR', 'A QUIC transport error occurred. %d [%s]', Error); | ||
| 1687 | 1685 | E('ERR_QUIC_VERSION_NEGOTIATION_ERROR', 'The QUIC session requires version negotiation', Error); | |
| 1688 | 1686 | E('ERR_REQUIRE_ASYNC_MODULE', function(filename, parentFilename) { | |
| 1689 | 1687 | let message = 'require() cannot be used on an ESM ' + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,7 @@ const { | |||
| 9 | 9 | ArrayPrototypePush, | |
| 10 | 10 | BigInt, | |
| 11 | 11 | DataViewPrototypeGetByteLength, | |
| 12 | + ErrorCaptureStackTrace, | ||
| 12 | 13 | FunctionPrototypeBind, | |
| 13 | 14 | Number, | |
| 14 | 15 | ObjectDefineProperties, | |
@@ -108,13 +109,11 @@ const { | |||
| 108 | 109 | ERR_INVALID_THIS, | |
| 109 | 110 | ERR_MISSING_ARGS, | |
| 110 | 111 | ERR_OUT_OF_RANGE, | |
| 111 | - ERR_QUIC_APPLICATION_ERROR, | ||
| 112 | 112 | ERR_QUIC_CONNECTION_FAILED, | |
| 113 | 113 | ERR_QUIC_ENDPOINT_CLOSED, | |
| 114 | 114 | ERR_QUIC_OPEN_STREAM_FAILED, | |
| 115 | 115 | ERR_QUIC_STREAM_ABORTED, | |
| 116 | 116 | ERR_QUIC_STREAM_RESET, | |
| 117 | - ERR_QUIC_TRANSPORT_ERROR, | ||
| 118 | 117 | ERR_QUIC_VERSION_NEGOTIATION_ERROR, | |
| 119 | 118 | }, | |
| 120 | 119 | } = require('internal/errors'); | |
@@ -738,10 +737,12 @@ setCallbacks({ | |||
| 738 | 737 | * @param {number} errorType | |
| 739 | 738 | * @param {number} code | |
| 740 | 739 | * @param {string} [reason] | |
| 740 | + * @param {string} [errorName] Decoded TLS alert name when `code` is a | ||
| 741 | + * CRYPTO_ERROR; otherwise undefined. | ||
| 741 | 742 | */ | |
| 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); | ||
| 745 | 746 | }, | |
| 746 | 747 | ||
| 747 | 748 | /** | |
@@ -931,8 +932,12 @@ setCallbacks({ | |||
| 931 | 932 | // was an abnormal termination even if the session closed cleanly. | |
| 932 | 933 | const resetCode = getQuicStreamState(this[kOwner]).resetCode; | |
| 933 | 934 | 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}`); | ||
| 936 | 941 | } | |
| 937 | 942 | } | |
| 938 | 943 | debug(`stream ${this[kOwner].id} closed callback with error: ${error}`); | |
@@ -1054,21 +1059,50 @@ class QuicError extends Error { | |||
| 1054 | 1059 | } | |
| 1055 | 1060 | } | |
| 1056 | 1061 | ||
| 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 | + | ||
| 1059 | 1086 | function convertQuicError(error) { | |
| 1060 | 1087 | const type = error[0]; | |
| 1061 | 1088 | const code = error[1]; | |
| 1062 | 1089 | const reason = error[2]; | |
| 1090 | + const errorName = error[3]; | ||
| 1063 | 1091 | switch (type) { | |
| 1064 | 1092 | 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); | ||
| 1066 | 1096 | 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); | ||
| 1068 | 1100 | case 'version_negotiation': | |
| 1069 | 1101 | return new ERR_QUIC_VERSION_NEGOTIATION_ERROR(); | |
| 1070 | 1102 | 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); | ||
| 1072 | 1106 | } | |
| 1073 | 1107 | } | |
| 1074 | 1108 | ||
@@ -3575,7 +3609,7 @@ class QuicSession { | |||
| 3575 | 3609 | * @param {number} code | |
| 3576 | 3610 | * @param {string} [reason] | |
| 3577 | 3611 | */ | |
| 3578 | - [kFinishClose](errorType, code, reason) { | ||
| 3612 | + [kFinishClose](errorType, code, reason, errorName) { | ||
| 3579 | 3613 | // If code is zero, then we closed without an error. Yay! We can destroy | |
| 3580 | 3614 | // safely without specifying an error. | |
| 3581 | 3615 | if (code === 0n) { | |
@@ -3584,7 +3618,8 @@ class QuicSession { | |||
| 3584 | 3618 | return; | |
| 3585 | 3619 | } | |
| 3586 | 3620 | ||
| 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); | ||
| 3588 | 3623 | ||
| 3589 | 3624 | // If the local side initiated this close with an error code (via | |
| 3590 | 3625 | // close({ code })), this is an intentional shutdown; not an error. | |
@@ -3611,10 +3646,14 @@ class QuicSession { | |||
| 3611 | 3646 | // session would leak with `closed` hanging forever. | |
| 3612 | 3647 | switch (errorType) { | |
| 3613 | 3648 | 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)); | ||
| 3615 | 3652 | break; | |
| 3616 | 3653 | 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)); | ||
| 3618 | 3657 | break; | |
| 3619 | 3658 | case 2: /* Version Negotiation Error */ | |
| 3620 | 3659 | this.destroy(new ERR_QUIC_VERSION_NEGOTIATION_ERROR()); | |
@@ -3623,7 +3662,9 @@ class QuicSession { | |||
| 3623 | 3662 | this.destroy(); | |
| 3624 | 3663 | break; | |
| 3625 | 3664 | 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)); | ||
| 3627 | 3668 | break; | |
| 3628 | 3669 | } | |
| 3629 | 3670 | } | |
@@ -3874,9 +3915,13 @@ class QuicSession { | |||
| 3874 | 3915 | // decide. In 'strict' mode, the handshake already failed at the C++ | |
| 3875 | 3916 | // level (SSL_VERIFY_PEER) so we won't reach here. | |
| 3876 | 3917 | 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}]`); | ||
| 3880 | 3925 | inner.pendingOpen.reject?.(err); | |
| 3881 | 3926 | inner.pendingOpen.resolve = undefined; | |
| 3882 | 3927 | inner.pendingOpen.reject = undefined; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -435,6 +435,14 @@ QUIC_JS_CALLBACKS(V) | |||
| 435 | 435 | ||
| 436 | 436 | #undef V | |
| 437 | 437 | ||
| 438 | + Local<String> BindingData::error_name_string(const char* name) { | ||
| 439 | + auto& slot = error_name_strings_[name]; | ||
| 440 | + if (slot.IsEmpty()) { | ||
| 441 | + slot.Set(env()->isolate(), OneByteString(env()->isolate(), name)); | ||
| 442 | + } | ||
| 443 | + return slot.Get(env()->isolate()); | ||
| 444 | + } | ||
| 445 | + | ||
| 438 | 446 | JS_METHOD_IMPL(BindingData::SetCallbacks) { | |
| 439 | 447 | auto env = Environment::GetCurrent(args); | |
| 440 | 448 | auto isolate = env->isolate(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -305,6 +305,8 @@ class BindingData final | |||
| 305 | 305 | ||
| 306 | 306 | std::unordered_map<Endpoint*, BaseObjectPtr<BaseObject>> listening_endpoints; | |
| 307 | 307 | ||
| 308 | + v8::Local<v8::String> error_name_string(const char* name); | ||
| 309 | + | ||
| 308 | 310 | size_t current_ngtcp2_memory_ = 0; | |
| 309 | 311 | ||
| 310 | 312 | // The following set up various storage and accessors for common strings, | |
@@ -357,6 +359,9 @@ class BindingData final | |||
| 357 | 359 | QUIC_JS_CALLBACKS(V) | |
| 358 | 360 | #undef V | |
| 359 | 361 | ||
| 362 | + // Lazy cache backing error_name_string() | ||
| 363 | + std::unordered_map<const char*, v8::Eternal<v8::String>> error_name_strings_; | ||
| 364 | + | ||
| 360 | 365 | std::unique_ptr<SessionManager> session_manager_; | |
| 361 | 366 | ||
| 362 | 367 | // Type-erased arena storage. The concrete AliasedStructArena<T> types | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,13 +1,15 @@ | |||
| 1 | 1 | #if HAVE_OPENSSL && HAVE_QUIC | |
| 2 | 2 | #include "guard.h" | |
| 3 | 3 | #ifndef OPENSSL_NO_QUIC | |
| 4 | - #include "data.h" | ||
| 5 | 4 | #include <env-inl.h> | |
| 6 | 5 | #include <memory_tracker-inl.h> | |
| 7 | 6 | #include <ngtcp2/ngtcp2.h> | |
| 8 | 7 | #include <node_sockaddr-inl.h> | |
| 8 | + #include <openssl/ssl.h> | ||
| 9 | 9 | #include <string_bytes.h> | |
| 10 | 10 | #include <v8.h> | |
| 11 | + #include "bindingdata.h" | ||
| 12 | + #include "data.h" | ||
| 11 | 13 | #include "defs.h" | |
| 12 | 14 | #include "util.h" | |
| 13 | 15 | ||
@@ -363,6 +365,62 @@ std::optional<int> QuicError::get_crypto_error() const { | |||
| 363 | 365 | return code() & ~NGTCP2_CRYPTO_ERROR; | |
| 364 | 366 | } | |
| 365 | 367 | ||
| 368 | + const char* QuicError::name() const { | ||
| 369 | + // CRYPTO_ERROR carries a TLS alert in its low byte (RFC 9001 sec. 4.8). | ||
| 370 | + // OpenSSL's SSL_alert_desc_string_long owns a stable string for every | ||
| 371 | + // alert it knows about; we filter out the "unknown" placeholder so the | ||
| 372 | + // JS side can present `errorName` as undefined for unrecognised alerts. | ||
| 373 | + if (auto alert = get_crypto_error()) { | ||
| 374 | + const char* n = SSL_alert_desc_string_long(*alert); | ||
| 375 | + if (n != nullptr && std::string_view(n) != "unknown") return n; | ||
| 376 | + return nullptr; | ||
| 377 | + } | ||
| 378 | + // Named transport-layer error codes from RFC 9000 sec. 20.1 (and the | ||
| 379 | + // RFC 9368 version-negotiation extension). Application error codes are | ||
| 380 | + // opaque to QUIC, so we only decode for transport. | ||
| 381 | + if (type() != Type::TRANSPORT) return nullptr; | ||
| 382 | + switch (code()) { | ||
| 383 | + case NGTCP2_NO_ERROR: | ||
| 384 | + return "NO_ERROR"; | ||
| 385 | + case NGTCP2_INTERNAL_ERROR: | ||
| 386 | + return "INTERNAL_ERROR"; | ||
| 387 | + case NGTCP2_CONNECTION_REFUSED: | ||
| 388 | + return "CONNECTION_REFUSED"; | ||
| 389 | + case NGTCP2_FLOW_CONTROL_ERROR: | ||
| 390 | + return "FLOW_CONTROL_ERROR"; | ||
| 391 | + case NGTCP2_STREAM_LIMIT_ERROR: | ||
| 392 | + return "STREAM_LIMIT_ERROR"; | ||
| 393 | + case NGTCP2_STREAM_STATE_ERROR: | ||
| 394 | + return "STREAM_STATE_ERROR"; | ||
| 395 | + case NGTCP2_FINAL_SIZE_ERROR: | ||
| 396 | + return "FINAL_SIZE_ERROR"; | ||
| 397 | + case NGTCP2_FRAME_ENCODING_ERROR: | ||
| 398 | + return "FRAME_ENCODING_ERROR"; | ||
| 399 | + case NGTCP2_TRANSPORT_PARAMETER_ERROR: | ||
| 400 | + return "TRANSPORT_PARAMETER_ERROR"; | ||
| 401 | + case NGTCP2_CONNECTION_ID_LIMIT_ERROR: | ||
| 402 | + return "CONNECTION_ID_LIMIT_ERROR"; | ||
| 403 | + case NGTCP2_PROTOCOL_VIOLATION: | ||
| 404 | + return "PROTOCOL_VIOLATION"; | ||
| 405 | + case NGTCP2_INVALID_TOKEN: | ||
| 406 | + return "INVALID_TOKEN"; | ||
| 407 | + case NGTCP2_APPLICATION_ERROR: | ||
| 408 | + return "APPLICATION_ERROR"; | ||
| 409 | + case NGTCP2_CRYPTO_BUFFER_EXCEEDED: | ||
| 410 | + return "CRYPTO_BUFFER_EXCEEDED"; | ||
| 411 | + case NGTCP2_KEY_UPDATE_ERROR: | ||
| 412 | + return "KEY_UPDATE_ERROR"; | ||
| 413 | + case NGTCP2_AEAD_LIMIT_REACHED: | ||
| 414 | + return "AEAD_LIMIT_REACHED"; | ||
| 415 | + case NGTCP2_NO_VIABLE_PATH: | ||
| 416 | + return "NO_VIABLE_PATH"; | ||
| 417 | + case NGTCP2_VERSION_NEGOTIATION_ERROR: | ||
| 418 | + return "VERSION_NEGOTIATION_ERROR"; | ||
| 419 | + default: | ||
| 420 | + return nullptr; | ||
| 421 | + } | ||
| 422 | + } | ||
| 423 | + | ||
| 366 | 424 | MaybeLocal<Value> QuicError::ToV8Value(Environment* env) const { | |
| 367 | 425 | if ((type() == Type::TRANSPORT && code() == NGTCP2_NO_ERROR) || | |
| 368 | 426 | (type() == Type::APPLICATION && | |
@@ -384,6 +442,7 @@ MaybeLocal<Value> QuicError::ToV8Value(Environment* env) const { | |||
| 384 | 442 | type_str, | |
| 385 | 443 | BigInt::NewFromUnsigned(env->isolate(), code()), | |
| 386 | 444 | Undefined(env->isolate()), | |
| 445 | + Undefined(env->isolate()), | ||
| 387 | 446 | }; | |
| 388 | 447 | ||
| 389 | 448 | // Note that per the QUIC specification, the reason, if present, is | |
@@ -397,6 +456,13 @@ MaybeLocal<Value> QuicError::ToV8Value(Environment* env) const { | |||
| 397 | 456 | return {}; | |
| 398 | 457 | } | |
| 399 | 458 | ||
| 459 | + // Attach a human-readable name for known wire codes (RFC 9000 sec. 20.1 | ||
| 460 | + // names and OpenSSL TLS alert descriptions for CRYPTO_ERROR). Unknown | ||
| 461 | + // codes leave the slot as undefined. | ||
| 462 | + if (const char* n = name()) { | ||
| 463 | + argv[3] = BindingData::Get(env).error_name_string(n); | ||
| 464 | + } | ||
| 465 | + | ||
| 400 | 466 | return Array::New(env->isolate(), argv, arraysize(argv)).As<Value>(); | |
| 401 | 467 | } | |
| 402 | 468 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -265,6 +265,9 @@ class QuicError final : public MemoryRetainer { | |||
| 265 | 265 | bool is_crypto_error() const; | |
| 266 | 266 | std::optional<int> get_crypto_error() const; | |
| 267 | 267 | ||
| 268 | + // Returns a human-readable name for this error if known, or nullptr | ||
| 269 | + const char* name() const; | ||
| 270 | + | ||
| 268 | 271 | // Note that since application errors are application-specific and we | |
| 269 | 272 | // don't know which application is being used here, it is possible that | |
| 270 | 273 | // the comparing two different QuicError instances from different applications | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3073,7 +3073,7 @@ void Session::CheckStreamIdleTimeout(uint64_t now) { | |||
| 3073 | 3073 | // Without this, the peer's stream sits orphaned until the | |
| 3074 | 3074 | // session closes. | |
| 3075 | 3075 | auto error = | |
| 3076 | - QuicError::ForTransport(NGTCP2_ERR_PROTO, "stream idle timeout"); | ||
| 3076 | + QuicError::ForNgtcp2Error(NGTCP2_ERR_PROTO, "stream idle timeout"); | ||
| 3077 | 3077 | ShutdownStream(id, error); | |
| 3078 | 3078 | stream->Destroy(error); | |
| 3079 | 3079 | STAT_INCREMENT(Stats, streams_idle_timed_out); | |
@@ -3459,12 +3459,21 @@ void Session::EmitClose(const QuicError& error) { | |||
| 3459 | 3459 | Integer::New(env()->isolate(), static_cast<int>(error.type())), | |
| 3460 | 3460 | BigInt::NewFromUnsigned(env()->isolate(), error.code()), | |
| 3461 | 3461 | Undefined(env()->isolate()), | |
| 3462 | + Undefined(env()->isolate()), | ||
| 3462 | 3463 | }; | |
| 3463 | 3464 | if (error.reason().length() > 0 && | |
| 3464 | 3465 | !ToV8Value(env()->context(), error.reason()).ToLocal(&argv[2])) { | |
| 3465 | 3466 | return; | |
| 3466 | 3467 | } | |
| 3467 | 3468 | ||
| 3469 | + // Attach a human-readable name for known wire codes (RFC 9000 sec. 20.1 | ||
| 3470 | + // names and OpenSSL TLS alert descriptions for CRYPTO_ERROR). Unknown | ||
| 3471 | + // codes leave the slot as undefined. See QuicError::name() for the | ||
| 3472 | + // matching path on stream-level errors. | ||
| 3473 | + if (const char* n = error.name()) { | ||
| 3474 | + argv[3] = BindingData::Get(env()).error_name_string(n); | ||
| 3475 | + } | ||
| 3476 | + | ||
| 3468 | 3477 | MakeCallback( | |
| 3469 | 3478 | BindingData::Get(env()).session_close_callback(), arraysize(argv), argv); | |
| 3470 | 3479 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments