/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v26.3.1
src/quic/data.h
358 строк
15 KB
Tim Perry
quic: add proper error codes & messages for QUIC failures
30 май 2026, 20:26
Не верифицирован
30 май 2026, 20:26
6a97b09
Код
Авторство
О чём код?
#pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include <env.h> #include <memory_tracker.h> #include <nghttp3/nghttp3.h> #include <ngtcp2/ngtcp2.h> #include <node_internals.h> #include <node_realm.h> #include <node_sockaddr.h> #include <v8.h> #include <concepts> #include <string> #include "defs.h" namespace node::quic { template <typename T> concept OneByteType = sizeof(T) == 1; // Lightweight wrapper around ngtcp2_pkt_info. Insulates the Node.js QUIC // code from the ngtcp2 struct layout and provides a clean API boundary // for per-packet metadata (currently ECN codepoint; may grow as ngtcp2 // and libuv evolve). // // Default-constructed PacketInfo is zero-initialized, which ngtcp2 treats // as ECN Not-ECT — identical to passing nullptr for the pkt_info parameter. class PacketInfo final { public: // ECN codepoints as defined by RFC 3168. enum class Ecn : uint32_t { NOT_ECT = 0, // Not ECN-Capable Transport ECT_1 = 1, // ECN-Capable Transport(1) ECT_0 = 2, // ECN-Capable Transport(0) CE = 3, // Congestion Experienced }; PacketInfo() : info_{} {} explicit PacketInfo(const ngtcp2_pkt_info& info) : info_(info) {} // ECN codepoint for this packet. When libuv gains per-packet ECN // reporting, populate via set_ecn() from the receive metadata // before passing to ReadPacket(). Ecn ecn() const { return static_cast<Ecn>(info_.ecn); } void set_ecn(Ecn ecn) { info_.ecn = static_cast<uint32_t>(ecn); } // Conversion operators for ngtcp2 API calls. operator const ngtcp2_pkt_info*() const { return &info_; } operator ngtcp2_pkt_info*() { return &info_; } private: ngtcp2_pkt_info info_; }; struct Path final : public ngtcp2_path { explicit Path(const SocketAddress& local, const SocketAddress& remote); Path(Path&& other) noexcept = default; Path& operator=(Path&& other) noexcept = default; DISALLOW_COPY(Path) std::string ToString() const; }; struct PathStorage final : public ngtcp2_path_storage { explicit PathStorage(); PathStorage(PathStorage&& other) noexcept = default; PathStorage& operator=(PathStorage&& other) noexcept = default; DISALLOW_COPY(PathStorage) void Reset(); void CopyTo(PathStorage* path) const; bool operator==(const PathStorage& other) const; bool operator!=(const PathStorage& other) const; }; // Store acts as a wrapper around a v8::BackingStore, providing a convenient // abstraction to map it to various buffer types used in QUIC, HTTP/3, and // libuv, taking care of the necessary adjustments for length and offset. class Store final : public MemoryRetainer { public: Store() = default; Store(std::shared_ptr<v8::BackingStore> store, size_t length, size_t offset = 0); Store(std::unique_ptr<v8::BackingStore> store, size_t length, size_t offset = 0); // Creates a Store by copying the contents of an ArrayBuffer into a fresh // BackingStore. The caller's buffer is not modified, so callers can safely // reuse or mutate it after the call returns. Returns an empty Maybe on // allocation failure, in which case an `ERR_MEMORY_ALLOCATION_FAILED` // exception will have been scheduled on the isolate. static v8::Maybe<Store> From(v8::Local<v8::ArrayBuffer> buffer); // Creates a Store by copying the contents of an ArrayBufferView into a // fresh BackingStore. The caller's view (and its underlying ArrayBuffer) // is not modified. Returns an empty Maybe on allocation failure, in which // case an `ERR_MEMORY_ALLOCATION_FAILED` exception will have been // scheduled on the isolate. static v8::Maybe<Store> From(v8::Local<v8::ArrayBufferView> view); // Creates a Store from the contents of an ArrayBuffer, always copying the // content. Equivalent to `From()` but returns a Store directly without // surfacing allocation failure. static Store CopyFrom(v8::Local<v8::ArrayBuffer> buffer); // Creates a Store from the contents of an ArrayBufferView, always copying // the content. Equivalent to `From()` but returns a Store directly without // surfacing allocation failure. static Store CopyFrom(v8::Local<v8::ArrayBufferView> view); v8::Local<v8::Uint8Array> ToUint8Array(Environment* env) const; inline v8::Local<v8::Uint8Array> ToUint8Array(Realm* realm) const { return ToUint8Array(realm->env()); } operator uv_buf_t() const; operator ngtcp2_vec() const; operator nghttp3_vec() const; operator bool() const; size_t length() const; // Returns the total length of the underlying store, not just the // length of the view. This is useful for memory accounting. size_t total_length() const; void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(Store) SET_SELF_SIZE(Store) private: template <typename T, OneByteType N> T convert() const; std::shared_ptr<v8::BackingStore> store_; size_t length_ = 0; size_t offset_ = 0; // Because Store holds the v8::BackingStore and not the v8::ArrayBuffer, // etc, the memory held by the Store is not directly, automatically // associated with a v8::Isolate, and is therefore not accounted for // as external memory. It is the responsibility of the owner of the // Store instance to ensure the memory remains accounted for. }; // Periodically, these need to be updated to match the latest ngtcp2 defs. #define QUIC_TRANSPORT_ERRORS(V) \ V(INTERNAL_ERROR) \ V(CONNECTION_REFUSED) \ V(FLOW_CONTROL_ERROR) \ V(STREAM_LIMIT_ERROR) \ V(STREAM_STATE_ERROR) \ V(FINAL_SIZE_ERROR) \ V(FRAME_ENCODING_ERROR) \ V(TRANSPORT_PARAMETER_ERROR) \ V(CONNECTION_ID_LIMIT_ERROR) \ V(PROTOCOL_VIOLATION) \ V(INVALID_TOKEN) \ V(APPLICATION_ERROR) \ V(CRYPTO_BUFFER_EXCEEDED) \ V(KEY_UPDATE_ERROR) \ V(AEAD_LIMIT_REACHED) \ V(NO_VIABLE_PATH) \ V(CRYPTO_ERROR) \ V(VERSION_NEGOTIATION_ERROR) // Periodically, these need to be updated to match the latest nghttp3 defs. #define HTTP3_APPLICATION_ERRORS(V) \ V(H3_NO_ERROR) \ V(H3_GENERAL_PROTOCOL_ERROR) \ V(H3_INTERNAL_ERROR) \ V(H3_STREAM_CREATION_ERROR) \ V(H3_CLOSED_CRITICAL_STREAM) \ V(H3_FRAME_UNEXPECTED) \ V(H3_FRAME_ERROR) \ V(H3_EXCESSIVE_LOAD) \ V(H3_ID_ERROR) \ V(H3_SETTINGS_ERROR) \ V(H3_MISSING_SETTINGS) \ V(H3_REQUEST_REJECTED) \ V(H3_REQUEST_CANCELLED) \ V(H3_REQUEST_INCOMPLETE) \ V(H3_MESSAGE_ERROR) \ V(H3_CONNECT_ERROR) \ V(H3_VERSION_FALLBACK) \ V(QPACK_DECOMPRESSION_FAILED) \ V(QPACK_ENCODER_STREAM_ERROR) \ V(QPACK_DECODER_STREAM_ERROR) class QuicError final : public MemoryRetainer { public: // The known error codes for the transport namespace. enum class TransportError : error_code { // NO_ERROR has to be treated specially since it is a macro on // some Windows cases and results in a compile error if we leave // it as is. NO_ERROR_ = NGTCP2_NO_ERROR, #define V(name) name = NGTCP2_##name, QUIC_TRANSPORT_ERRORS(V) #undef V }; // Every QUIC application defines its own error codes in the application // namespace. These are managed independently of each other and may overlap // with other applications and even the transport namespace. The only way // to correctly interpret an application error code is to know which // application is being used. For convenience, we define constants for the // known HTTP/3 application error codes here. enum class Http3Error : error_code { #define V(name) name = NGHTTP3_##name, HTTP3_APPLICATION_ERRORS(V) #undef V }; static constexpr error_code QUIC_NO_ERROR = NGTCP2_NO_ERROR; static constexpr error_code HTTP3_NO_ERROR_CODE = NGHTTP3_H3_NO_ERROR; // In QUIC, Errors are represented as namespaced 64-bit error codes. // The error code only has meaning within the context of a specific // namespace. The QuicError::Type enum defines the available namespaces. // There are essentially two namespaces: transport and application, with // a few additional special-cases that are variants of the transport // namespace. enum class Type { TRANSPORT = NGTCP2_CCERR_TYPE_TRANSPORT, APPLICATION = NGTCP2_CCERR_TYPE_APPLICATION, // These are special cases of transport errors. VERSION_NEGOTIATION = NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION, IDLE_CLOSE = NGTCP2_CCERR_TYPE_IDLE_CLOSE, DROP_CONNECTION = NGTCP2_CCERR_TYPE_DROP_CONN, RETRY = NGTCP2_CCERR_TYPE_RETRY, }; // Do not use the constructors directly in regular use. Use the static // factory methods instead. Those will ensure that the underlying // ngtcp2_ccerr is initialized correctly based on the type of error // being created. explicit QuicError(const std::string& reason = ""); explicit QuicError(const ngtcp2_ccerr* ptr); explicit QuicError(const ngtcp2_ccerr& error); // Move constructor and assignment must fix up ptr_ when it points // to the internal error_ member (as set by the default constructor // and the ForTransport/ForApplication factory methods). QuicError(QuicError&& other) noexcept; QuicError& operator=(QuicError&& other) noexcept; // Copy constructor and assignment must also fix up ptr_. QuicError(const QuicError& other); QuicError& operator=(const QuicError& other); Type type() const; error_code code() const; const std::string_view reason() const; uint64_t frame_type() const; operator const ngtcp2_ccerr&() const; operator const ngtcp2_ccerr*() const; // Crypto errors are a subset of transport errors. The error code includes // the TLS alert code embedded within it. bool is_crypto_error() const; std::optional<int> get_crypto_error() const; // Returns a human-readable name for this error if known, or nullptr const char* name() const; // Note that since application errors are application-specific and we // don't know which application is being used here, it is possible that // the comparing two different QuicError instances from different applications // will return true even if they are not semantically equivalent. This should // not be a problem in practice. bool operator==(const QuicError& other) const; bool operator!=(const QuicError& other) const; void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(QuicError) SET_SELF_SIZE(QuicError) std::string ToString() const; // Returns an array containing [type, code, reason], where type is a string // representation of the error type, code is a bigint representation of the // error code, and reason is a string representation of the error reason, or // undefined if the reason is the empty string. This is expected to be used // to construct a JS error object from the information in JS. v8::MaybeLocal<v8::Value> ToV8Value(Environment* env) const; inline v8::MaybeLocal<v8::Value> ToV8Value(Realm* realm) const { return ToV8Value(realm->env()); } // Utility functions for getting the default error reason strings for // internal error codes returned by the underlying ngtcp2/nghttp3 libraries. static std::string reason_for_liberr(int liberr); static std::string reason_for_h3_liberr(int liberr); // Utility functions for checking if the given internal error codes are // considered to be fatal by the underlying ngtcp2/nghttp3 libraries. static bool is_fatal_liberror(int liberr); static bool is_fatal_h3_liberror(int liberr); // Utility functions for converting between ngtcp2/nghttp3 internal error // codes to the corresponding QUIC error codes. static error_code liberr_to_code(int liberr); static error_code h3_liberr_to_code(int liberr); // Utility functions for creating QuicError instances. // The reason is expected to always be UTF-8 encoded. static QuicError ForTransport(TransportError code = TransportError::NO_ERROR_, std::string reason = ""); static QuicError ForTransport(error_code code, std::string reason = ""); static QuicError ForApplication(Http3Error code, std::string reason = ""); static QuicError ForApplication(error_code code, std::string reason = ""); static QuicError ForVersionNegotiation(std::string reason = ""); static QuicError ForIdleClose(std::string reason = ""); static QuicError ForDropConnection(std::string reason = ""); static QuicError ForRetry(std::string reason = ""); static QuicError ForNgtcp2Error(int code, std::string reason = ""); static QuicError ForTlsAlert(int code, std::string reason = ""); static QuicError FromConnectionClose(ngtcp2_conn* session); static const QuicError TRANSPORT_NO_ERROR; #define V(name) static const QuicError TRANSPORT_##name; QUIC_TRANSPORT_ERRORS(V) #undef V static const QuicError HTTP3_NO_ERROR; static const QuicError VERSION_NEGOTIATION; static const QuicError IDLE_CLOSE; static const QuicError DROP_CONNECTION; static const QuicError RETRY; static const QuicError INTERNAL_ERROR; private: const uint8_t* reason_c_str() const; std::string reason_; ngtcp2_ccerr error_; const ngtcp2_ccerr* ptr_ = nullptr; }; // Marked maybe_unused because these are used in the tests but not in the // production code. [[maybe_unused]] static bool operator==(const QuicError::TransportError& lhs, error_code rhs) { return static_cast<error_code>(lhs) == rhs; } [[maybe_unused]] static bool operator==(const QuicError::Http3Error& lhs, error_code rhs) { return static_cast<error_code>(lhs) == rhs; } } // namespace node::quic #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS