diff --git a/wolf/stream/quic/datatypes/common_flags.hpp b/wolf/stream/quic/datatypes/common_flags.hpp new file mode 100644 index 000000000..001a4c81c --- /dev/null +++ b/wolf/stream/quic/datatypes/common_flags.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +/** + * @brief The equivalent enum flags for QUIC_CONNECTION_SHUTDOWN_FLAGS. + */ +enum class w_connection_shutdown_flag { + None = QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, + Silent = QUIC_CONNECTION_SHUTDOWN_FLAG_SILENT +}; diff --git a/wolf/stream/quic/datatypes/w_address.cpp b/wolf/stream/quic/datatypes/w_address.cpp new file mode 100644 index 000000000..7e2eeb5f6 --- /dev/null +++ b/wolf/stream/quic/datatypes/w_address.cpp @@ -0,0 +1 @@ +#include "stream/quic/datatypes/w_address.hpp" diff --git a/wolf/stream/quic/datatypes/w_address.hpp b/wolf/stream/quic/datatypes/w_address.hpp new file mode 100644 index 000000000..c173bf8cf --- /dev/null +++ b/wolf/stream/quic/datatypes/w_address.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include "wolf.hpp" + +#include "stream/quic/internal/common.hpp" + +#include + +#include +#include + +namespace wolf::stream::quic { + +/** + * @brief equivalent of QUIC_ADDRESS_FAMILY enum. + */ +enum class w_address_family { + Unspecified = QUIC_ADDRESS_FAMILY_UNSPEC, + INET = QUIC_ADDRESS_FAMILY_INET, + INET6 = QUIC_ADDRESS_FAMILY_INET6 +}; + +/** + * @brief Wrapping QUIC_ADDRESS. + * + * Commonly used to open a listener. + */ +class w_address { + friend class internal::w_raw_access; + +public: + w_address(w_address_family p_address_family, std::uint16_t p_port) noexcept + { + set_address_family(p_address_family); + set_port(p_port); + } + + /** + * @brief get address family. + */ + [[nodiscard]] w_address_family get_address_family() const noexcept + { + return static_cast(QuicAddrGetFamily(&_address)); + } + + /** + * @brief set address family. + */ + void set_address_family(w_address_family p_address_family) noexcept + { + QuicAddrSetFamily(&_address, static_cast(p_address_family)); + } + + /** + * @brief get port. + */ + [[nodiscard]] std::uint16_t get_port() const noexcept + { + return QuicAddrGetPort(&_address); + } + + /** + * @brief set port. + */ + void set_port(std::uint16_t p_port) noexcept + { + QuicAddrSetPort(&_address, p_port); + } + +private: + auto raw() noexcept { return &_address; } + auto raw() const noexcept { return &_address; } + + w_address(internal::w_raw_tag, const QUIC_ADDR& p_raw_addr) + { + std::memcpy(&_address, &p_raw_addr, sizeof(QUIC_ADDR)); + } + + QUIC_ADDR _address; +}; + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/datatypes/w_credential_config.cpp b/wolf/stream/quic/datatypes/w_credential_config.cpp new file mode 100644 index 000000000..0e1302d06 --- /dev/null +++ b/wolf/stream/quic/datatypes/w_credential_config.cpp @@ -0,0 +1 @@ +#include "stream/quic/datatypes/w_credential_config.hpp" diff --git a/wolf/stream/quic/datatypes/w_credential_config.hpp b/wolf/stream/quic/datatypes/w_credential_config.hpp new file mode 100644 index 000000000..a9a8c5c79 --- /dev/null +++ b/wolf/stream/quic/datatypes/w_credential_config.hpp @@ -0,0 +1,157 @@ +#pragma once + +#include "wolf.hpp" + +#include "stream/quic/internal/common.hpp" +#include "system/w_flags.hpp" +#include "system/w_overloaded.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace wolf::stream::quic { + +/** + * @brief Equivalent flags enum to QUIC_CREDENTIAL_FLAG enum. + */ +enum class w_credential_flag { + None = QUIC_CREDENTIAL_FLAG_NONE, + Client = QUIC_CREDENTIAL_FLAG_CLIENT, + LoadAsynchronous = QUIC_CREDENTIAL_FLAG_LOAD_ASYNCHRONOUS, + NoCertificationValidation = QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION, + EnableOCSP = QUIC_CREDENTIAL_FLAG_ENABLE_OCSP, + IndicateCertificateReceived = QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED, + DeferCertificateValidation = QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION, + RequireClientAuthenticate = QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION, + UseTlsBuiltinCertificateValidation = QUIC_CREDENTIAL_FLAG_USE_TLS_BUILTIN_CERTIFICATE_VALIDATION, + RevocationCheckEndCert = QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_END_CERT, + RevocationCheckChain = QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CHAIN, + RevocationCheckChainExcludeRoot = QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, + IgnoreNoRevocationCheck = QUIC_CREDENTIAL_FLAG_IGNORE_NO_REVOCATION_CHECK, + IgnoreRevocationOffline = QUIC_CREDENTIAL_FLAG_IGNORE_REVOCATION_OFFLINE, + SetAllowedCipherSuites = QUIC_CREDENTIAL_FLAG_SET_ALLOWED_CIPHER_SUITES, + UsePortableCertificates = QUIC_CREDENTIAL_FLAG_USE_PORTABLE_CERTIFICATES, + UseSuppliedCredentials = QUIC_CREDENTIAL_FLAG_USE_SUPPLIED_CREDENTIALS, + UseSystemMapper = QUIC_CREDENTIAL_FLAG_USE_SYSTEM_MAPPER, + CacheOnlyURLRetrieval = QUIC_CREDENTIAL_FLAG_CACHE_ONLY_URL_RETRIEVAL, + RevocationCheckCacheOnly = QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY, + InprocPeerCertificate = QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE, + SetCACertificateFile = QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE +}; + +/** + * @brief An empty tag type indicating no certificate. + */ +class W_API w_certificate_none { /* nothing. tag type. */ }; + +/** + * @brief Wrapper of QUIC_CERTIFICATE_HASH. + * + * Holds sha1 hash binary digest (20 bytes). + */ +class w_certificate_hash { + friend class internal::w_raw_access; + +public: + constexpr static auto hash_size = 20; + + /** + * @brief construct certificate with given sha1 bytes/digest. + * @param p_hash sha1 bytes/digest. + */ + explicit w_certificate_hash(std::span p_hash) + { + std::memcpy(_cert.ShaHash, p_hash.data(), hash_size); + } + +private: + auto raw() noexcept { return &_cert; } + auto raw() const noexcept { return &_cert; } + + QUIC_CERTIFICATE_HASH _cert; +}; + +/** + * @brief Wrapper of QUIC_CERTIFICATE_FILE. + * + * Holding file paths of certificate and key, + * which are loaded when loading credentials into + * configuration handle. + */ +class w_certificate_file { + friend class internal::w_raw_access; + +public: + w_certificate_file(std::string p_cert_file_path, std::string p_key_file_path) + : _cert_file_path(std::move(p_cert_file_path)) + , _key_file_path(std::move(p_key_file_path)) + { + _cert.CertificateFile = _cert_file_path.c_str(); + _cert.PrivateKeyFile = _key_file_path.c_str(); + } + +private: + auto raw() noexcept { return &_cert; } + auto raw() const noexcept { return &_cert; } + + const std::string _cert_file_path; + const std::string _key_file_path; + + QUIC_CERTIFICATE_FILE _cert; +}; + +/** + * @brief Wrapper of QUIC_CREDENTIAL_CONFIG. + * + * Used to setup configuration handle. + */ +class w_credential_config { + friend class internal::w_raw_access; + +public: + using cert_variant_type = std::variant; + + /** + * @brief construct credential config with given certificate type, and flags. + * @param p_cert a supported certificate object. (w_certificate_) + * @param p_flags flags to associate with the credentials. + */ + explicit w_credential_config(cert_variant_type p_cert, + wolf::system::w_flags p_flags = w_credential_flag::None) noexcept + : _cert_variant(p_cert) + { + std::memset(&_config, 0, sizeof(_config)); + + _config.Flags = static_cast(p_flags.to_underlying()); + + std::visit(wolf::system::w_overloaded{ + [this](w_certificate_none&) { + _config.Type = QUIC_CREDENTIAL_TYPE_NONE; + }, + [this](w_certificate_hash& p_cert) { + _config.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH; + _config.CertificateHash = internal::w_raw_access::raw(p_cert); + }, + [this](w_certificate_file& p_cert) { + _config.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; + _config.CertificateFile = internal::w_raw_access::raw(p_cert); + } + }, _cert_variant); + } + +private: + auto raw() noexcept { return &_config; } + auto raw() const noexcept { return &_config; } + + QUIC_CREDENTIAL_CONFIG _config; + cert_variant_type _cert_variant; +}; + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/datatypes/w_new_connection_info.cpp b/wolf/stream/quic/datatypes/w_new_connection_info.cpp new file mode 100644 index 000000000..4481d2103 --- /dev/null +++ b/wolf/stream/quic/datatypes/w_new_connection_info.cpp @@ -0,0 +1 @@ +#include "stream/quic/datatypes/w_new_connection_info.hpp" diff --git a/wolf/stream/quic/datatypes/w_new_connection_info.hpp b/wolf/stream/quic/datatypes/w_new_connection_info.hpp new file mode 100644 index 000000000..1aadbe79c --- /dev/null +++ b/wolf/stream/quic/datatypes/w_new_connection_info.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "wolf.hpp" + +#include "stream/quic/internal/common.hpp" +#include "stream/quic/datatypes/w_address.hpp" + +#include + +#include +#include + +namespace wolf::stream::quic { + +/** + * @brief Accessor wrapper of QUIC_NEW_CONNECTION_INFO type. + * + * This class wraps the raw C type, and provides simple accessor methods. + */ +class w_new_connection_info { + friend class internal::w_raw_access; + +public: + w_new_connection_info() = delete; + + /** + * @brief negotiated quic version. + */ + [[nodiscard]] auto quic_version() const + { + return _conn_info.QuicVersion; + } + + /** + * @brief the local address of the new incoming connection. + */ + [[nodiscard]] w_address local_address() const + { + return internal::w_raw_access::from_raw(*_conn_info.LocalAddress); + } + + /** + * @brief the remote address of the new incoming connection. + */ + [[nodiscard]] w_address remote_address() const + { + return internal::w_raw_access::from_raw(*_conn_info.RemoteAddress); + } + + /** + * @brief the server name (SNI) of the new incoming connection. + */ + [[nodiscard]] std::string_view server_name() const + { + return std::string_view(_conn_info.ServerName, _conn_info.ServerNameLength); + } + + /** + * @brief the negotiated ALPN between server and client. + */ + [[nodiscard]] std::string_view negotiated_alpn() const + { + auto c_str = (const char*)(_conn_info.NegotiatedAlpn); + return std::string_view(c_str, _conn_info.NegotiatedAlpnLength); + } + +private: + w_new_connection_info(internal::w_raw_tag, const QUIC_NEW_CONNECTION_INFO& p_conn_info) + : _conn_info(p_conn_info) + {} + + QUIC_NEW_CONNECTION_INFO _conn_info = { 0 }; +}; + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/datatypes/w_registration_config.cpp b/wolf/stream/quic/datatypes/w_registration_config.cpp new file mode 100644 index 000000000..f00f69a75 --- /dev/null +++ b/wolf/stream/quic/datatypes/w_registration_config.cpp @@ -0,0 +1 @@ +#include "stream/quic/datatypes/w_registration_config.hpp" diff --git a/wolf/stream/quic/datatypes/w_registration_config.hpp b/wolf/stream/quic/datatypes/w_registration_config.hpp new file mode 100644 index 000000000..46d5c135b --- /dev/null +++ b/wolf/stream/quic/datatypes/w_registration_config.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "wolf.hpp" + +#include "stream/quic/internal/common.hpp" + +#include + +#include + +namespace wolf::stream::quic { + +/** + * @brief equivalent enum to QUIC_EXECUTION_PROFILE enum. + * + * Used with registration config to specify aimed execution profile. + */ +enum class w_execution_profile { + LowLatency = QUIC_EXECUTION_PROFILE_LOW_LATENCY, + MaxThroughput = QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT, + Scavenger = QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER, + RealTime = QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME +}; + +/** + * @brief wrapper around QUIC_REGISTRATION_CONFIG. + */ +class w_registration_config { + friend class internal::w_raw_access; + +public: + /** + * @brief construct simple registration config. + */ + w_registration_config(std::string p_app_name, + w_execution_profile p_profile = w_execution_profile::LowLatency) noexcept + : _app_name(std::move(p_app_name)) + { + _config.AppName = _app_name.c_str(); + _config.ExecutionProfile = static_cast(p_profile); + } + + /** + * @brief get app name. + */ + [[nodiscard]] const std::string& get_app_name() const noexcept + { + return _app_name; + } + + /** + * @brief get execution profile. + */ + [[nodiscard]] w_execution_profile get_execution_profile() const noexcept + { + return static_cast(_config.ExecutionProfile); + } + + /** + * @brief set execution profile. + */ + void set_execution_profile(w_execution_profile p_profile) noexcept + { + _config.ExecutionProfile = static_cast(p_profile); + } + +private: + auto raw() noexcept { return &_config; } + auto raw() const noexcept { return &_config; } + + QUIC_REGISTRATION_CONFIG _config = { 0 }; + const std::string _app_name; +}; + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/datatypes/w_settings.cpp b/wolf/stream/quic/datatypes/w_settings.cpp new file mode 100644 index 000000000..7b83eec01 --- /dev/null +++ b/wolf/stream/quic/datatypes/w_settings.cpp @@ -0,0 +1 @@ +#include "stream/quic/datatypes/w_settings.hpp" diff --git a/wolf/stream/quic/datatypes/w_settings.hpp b/wolf/stream/quic/datatypes/w_settings.hpp new file mode 100644 index 000000000..12714c29e --- /dev/null +++ b/wolf/stream/quic/datatypes/w_settings.hpp @@ -0,0 +1,207 @@ +#pragma once + +#include "wolf.hpp" + +#include "stream/quic/internal/common.hpp" + +#include + +#include +#include + +namespace wolf::stream::quic { + +/** + * @brief Equivalent enum of QUIC_SERVER_RESUMPTION_LEVEL. + * + * Its values are defined in QUIC_SETTINGS document in msquic. + */ +enum class w_server_resumption_level { + NoResume = QUIC_SERVER_NO_RESUME, + ResumeOnly = QUIC_SERVER_RESUME_ONLY, + ResumeAndZeroRTT = QUIC_SERVER_RESUME_AND_ZERORTT +}; + +/** + * @brief Equivalent enum of QUIC_CONGESTION_CONTROL_ALGORITHM. + */ +enum class w_congestion_control_algorithm { + Cubic = QUIC_CONGESTION_CONTROL_ALGORITHM_CUBIC, + Max = QUIC_CONGESTION_CONTROL_ALGORITHM_MAX +}; + +/** + * @brief Wrapper of QUIC_SETTINGS. + * + * QUIC_SETTINGS has a set of fields to tweak + * application's protocol behavior. + * + * A use-case of it to setup the configuration handle + * for registration execution context. + * + * For each field in QUIC_SETTINGS, an equivalent + * std::optional wrapped typed field has been defined. + * + * This type has and should be kept with support of aggregate initialization. + */ +class w_settings { + friend class internal::w_raw_access; + +public: + std::optional max_bytes_per_key; + std::optional handshake_idle_timeout_ms; + std::optional idle_timeout_ms; + std::optional mtu_discovery_search_complete_timeout_us; + std::optional tls_client_max_send_buffer; + std::optional tls_server_max_send_buffer; + std::optional stream_recv_window_default; + std::optional stream_recv_buffer_default; + std::optional conn_flow_control_window; + std::optional max_worker_queue_delay_us; + std::optional max_stateless_operations; + std::optional initial_window_packets; + std::optional send_idle_timeout_ms; + std::optional initial_rtt_ms; + std::optional max_ack_delay_ms; + std::optional disconnect_timeout_ms; + std::optional keep_alive_interval_ms; + std::optional congestion_control_algorithm; + std::optional peer_bidi_stream_count; + std::optional peer_unidi_stream_count; + std::optional max_binding_stateless_operations; + std::optional stateless_operation_expiration_ms; + std::optional minimum_mtu; + std::optional maximum_mtu; + std::optional send_buffering_enabled; + std::optional pacing_enabled; + std::optional migration_enabled; + std::optional datagram_receive_enabled; + std::optional server_resumption_level; + std::optional grease_quic_bit_enabled; + std::optional ecn_enabled; + std::optional max_operations_per_drain; + std::optional mtu_discovery_missing_probe_count; + std::optional dest_cid_update_idle_timeout_ms; + +private: + auto raw() const noexcept + { + QUIC_SETTINGS ret; + std::memset(&ret, 0, sizeof(ret)); + + // NOTE the default value used by `value_or` is irrelevant to settings + // as if there isn't a value, then the `IsSet` correspondant flag will be false too. + + ret.MaxBytesPerKey = max_bytes_per_key.value_or(0); + ret.IsSet.MaxBytesPerKey = max_bytes_per_key.has_value(); + + ret.HandshakeIdleTimeoutMs = handshake_idle_timeout_ms.value_or(0); + ret.IsSet.HandshakeIdleTimeoutMs = handshake_idle_timeout_ms.has_value(); + + ret.IdleTimeoutMs = idle_timeout_ms.value_or(0); + ret.IsSet.IdleTimeoutMs = idle_timeout_ms.has_value(); + + ret.MtuDiscoverySearchCompleteTimeoutUs = + mtu_discovery_search_complete_timeout_us.value_or(0); + ret.IsSet.MtuDiscoverySearchCompleteTimeoutUs = + mtu_discovery_search_complete_timeout_us.has_value(); + + ret.TlsClientMaxSendBuffer = tls_client_max_send_buffer.value_or(0); + ret.IsSet.TlsClientMaxSendBuffer = tls_client_max_send_buffer.has_value(); + + ret.TlsServerMaxSendBuffer = tls_server_max_send_buffer.value_or(0); + ret.IsSet.TlsServerMaxSendBuffer = tls_server_max_send_buffer.has_value(); + + ret.StreamRecvWindowDefault = stream_recv_window_default.value_or(0); + ret.IsSet.StreamRecvWindowDefault = stream_recv_window_default.has_value(); + + ret.StreamRecvBufferDefault = stream_recv_buffer_default.value_or(0); + ret.IsSet.StreamRecvBufferDefault = stream_recv_buffer_default.has_value(); + + ret.ConnFlowControlWindow = conn_flow_control_window.value_or(0); + ret.IsSet.ConnFlowControlWindow = conn_flow_control_window.has_value(); + + ret.MaxWorkerQueueDelayUs = max_worker_queue_delay_us.value_or(0); + ret.IsSet.MaxWorkerQueueDelayUs = max_worker_queue_delay_us.has_value(); + + ret.MaxStatelessOperations = max_stateless_operations.value_or(0); + ret.IsSet.MaxStatelessOperations = max_stateless_operations.has_value(); + + ret.InitialWindowPackets = initial_window_packets.value_or(0); + ret.IsSet.InitialWindowPackets = initial_window_packets.has_value(); + + ret.SendIdleTimeoutMs = send_idle_timeout_ms.value_or(0); + ret.IsSet.SendIdleTimeoutMs = send_idle_timeout_ms.has_value(); + + ret.InitialRttMs = initial_rtt_ms.value_or(0); + ret.IsSet.InitialRttMs = initial_rtt_ms.has_value(); + + ret.MaxAckDelayMs = max_ack_delay_ms.value_or(0); + ret.IsSet.MaxAckDelayMs = max_ack_delay_ms.has_value(); + + ret.DisconnectTimeoutMs = disconnect_timeout_ms.value_or(0); + ret.IsSet.DisconnectTimeoutMs = disconnect_timeout_ms.has_value(); + + ret.KeepAliveIntervalMs = keep_alive_interval_ms.value_or(0); + ret.IsSet.KeepAliveIntervalMs = keep_alive_interval_ms.has_value(); + + ret.CongestionControlAlgorithm = static_cast( + congestion_control_algorithm.value_or(w_congestion_control_algorithm::Cubic) + ); + ret.IsSet.CongestionControlAlgorithm = congestion_control_algorithm.has_value(); + + ret.PeerBidiStreamCount = peer_bidi_stream_count.value_or(0); + ret.IsSet.PeerBidiStreamCount = peer_bidi_stream_count.has_value(); + + ret.PeerUnidiStreamCount = peer_unidi_stream_count.value_or(0); + ret.IsSet.PeerUnidiStreamCount = peer_unidi_stream_count.has_value(); + + ret.MaxBindingStatelessOperations = max_binding_stateless_operations.value_or(0); + ret.IsSet.MaxBindingStatelessOperations = max_binding_stateless_operations.has_value(); + + ret.StatelessOperationExpirationMs = stateless_operation_expiration_ms.value_or(0); + ret.IsSet.StatelessOperationExpirationMs = stateless_operation_expiration_ms.has_value(); + + ret.MinimumMtu = minimum_mtu.value_or(0); + ret.IsSet.MinimumMtu = minimum_mtu.has_value(); + + ret.MaximumMtu = maximum_mtu.value_or(0); + ret.IsSet.MaximumMtu = maximum_mtu.has_value(); + + ret.SendBufferingEnabled = send_buffering_enabled.value_or(0); + ret.IsSet.SendBufferingEnabled = send_buffering_enabled.has_value(); + + ret.PacingEnabled = pacing_enabled.value_or(0); + ret.IsSet.PacingEnabled = pacing_enabled.has_value(); + + ret.MigrationEnabled = migration_enabled.value_or(0); + ret.IsSet.MigrationEnabled = migration_enabled.has_value(); + + ret.DatagramReceiveEnabled = datagram_receive_enabled.value_or(0); + ret.IsSet.DatagramReceiveEnabled = datagram_receive_enabled.has_value(); + + ret.ServerResumptionLevel = static_cast( + server_resumption_level.value_or(w_server_resumption_level::NoResume) + ); + ret.IsSet.ServerResumptionLevel = server_resumption_level.has_value(); + + ret.GreaseQuicBitEnabled = grease_quic_bit_enabled.value_or(0); + ret.IsSet.GreaseQuicBitEnabled = grease_quic_bit_enabled.has_value(); + + ret.EcnEnabled = ecn_enabled.value_or(0); + ret.IsSet.EcnEnabled = ecn_enabled.has_value(); + + ret.MaxOperationsPerDrain = max_operations_per_drain.value_or(0); + ret.IsSet.MaxOperationsPerDrain = max_operations_per_drain.has_value(); + + ret.MtuDiscoveryMissingProbeCount = mtu_discovery_missing_probe_count.value_or(0); + ret.IsSet.MtuDiscoveryMissingProbeCount = mtu_discovery_missing_probe_count.has_value(); + + ret.DestCidUpdateIdleTimeoutMs = dest_cid_update_idle_timeout_ms.value_or(0); + ret.IsSet.DestCidUpdateIdleTimeoutMs = dest_cid_update_idle_timeout_ms.has_value(); + + return ret; + } +}; + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/datatypes/w_status.cpp b/wolf/stream/quic/datatypes/w_status.cpp new file mode 100644 index 000000000..25174efdf --- /dev/null +++ b/wolf/stream/quic/datatypes/w_status.cpp @@ -0,0 +1,50 @@ +#include "stream/quic/datatypes/w_status.hpp" + +namespace wolf::stream::quic { + +std::string_view status_to_str(w_status p_status) +{ + auto status = static_cast(w_status_code(p_status)); + + switch (status) { + case QUIC_STATUS_SUCCESS: return "success"; + case QUIC_STATUS_PENDING: return "pending"; + case QUIC_STATUS_CONTINUE: return "continue"; + case QUIC_STATUS_ABORTED: return "aborted"; + case QUIC_STATUS_UNREACHABLE: return "unreachable"; + case QUIC_STATUS_OUT_OF_MEMORY: return "out-of-memory"; + case QUIC_STATUS_NOT_SUPPORTED: return "not-supported"; + case QUIC_STATUS_NOT_FOUND: return "not-found"; + case QUIC_STATUS_BUFFER_TOO_SMALL: return "buffer-too-small"; + case QUIC_STATUS_USER_CANCELED: return "user-canceled"; + case QUIC_STATUS_INVALID_PARAMETER: return "invalid-parameter"; + case QUIC_STATUS_INVALID_STATE: return "invalid-state"; + case QUIC_STATUS_INTERNAL_ERROR: return "internal-error"; + case QUIC_STATUS_HANDSHAKE_FAILURE: return "handshake-failure"; + case QUIC_STATUS_INVALID_ADDRESS: return "invalid-address"; + case QUIC_STATUS_ADDRESS_IN_USE: return "address-in-use"; + case QUIC_STATUS_CONNECTION_TIMEOUT: return "connection-timeout"; + case QUIC_STATUS_CONNECTION_IDLE: return "connection-idle"; + case QUIC_STATUS_CONNECTION_REFUSED: return "connection-refused"; + case QUIC_STATUS_PROTOCOL_ERROR: return "protocol-error"; + case QUIC_STATUS_VER_NEG_ERROR: return "version-negotiation-error"; + case QUIC_STATUS_ALPN_NEG_FAILURE: return "alpn-negotiation-error"; + case QUIC_STATUS_STREAM_LIMIT_REACHED: return "stream-limit-reached"; + case QUIC_STATUS_TLS_ERROR: return "tls-error"; + case QUIC_STATUS_BAD_CERTIFICATE: return "bad-certificate"; + case QUIC_STATUS_REQUIRED_CERTIFICATE: return "required-certificate"; + case QUIC_STATUS_REVOKED_CERTIFICATE: return "revoked-certificate"; + case QUIC_STATUS_UNKNOWN_CERTIFICATE: return "unkown-certificate"; + case QUIC_STATUS_EXPIRED_CERTIFICATE: return "expired-certificate"; + case QUIC_STATUS_ALPN_IN_USE: return "alpn-in-use"; + case QUIC_STATUS_CERT_EXPIRED: return "cert-expired"; + case QUIC_STATUS_CERT_NO_CERT: return "no-cert"; + case QUIC_STATUS_CERT_UNTRUSTED_ROOT: return "cert-untrusted-root"; + default: + return "unknown"; + } +} + +// namespace internal + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/datatypes/w_status.hpp b/wolf/stream/quic/datatypes/w_status.hpp new file mode 100644 index 000000000..ef10c28fb --- /dev/null +++ b/wolf/stream/quic/datatypes/w_status.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include "wolf.hpp" + +#include + +#include + +namespace wolf::stream::quic { + +/** + * @brief equivalent of QUIC_STATUS. + */ +enum class w_status_code { + Success = QUIC_STATUS_SUCCESS, + Pending = QUIC_STATUS_PENDING, + Continue = QUIC_STATUS_CONTINUE, + Aborted = QUIC_STATUS_ABORTED, + Unreachable = QUIC_STATUS_UNREACHABLE, + OutOfMemory = QUIC_STATUS_OUT_OF_MEMORY, + NotSupported = QUIC_STATUS_NOT_SUPPORTED, + NotFound = QUIC_STATUS_NOT_FOUND, + BufferTooSmall = QUIC_STATUS_BUFFER_TOO_SMALL, + UserCancelled = QUIC_STATUS_USER_CANCELED, + InvalidParameter = QUIC_STATUS_INVALID_PARAMETER, + InvalidState = QUIC_STATUS_INVALID_STATE, + InternalError = QUIC_STATUS_INTERNAL_ERROR, + HandshakeFailure = QUIC_STATUS_HANDSHAKE_FAILURE, + InvalidAddress = QUIC_STATUS_INVALID_ADDRESS, + AddressInUse = QUIC_STATUS_ADDRESS_IN_USE, + ConnectionTimeout = QUIC_STATUS_CONNECTION_TIMEOUT, + ConnectionIdle = QUIC_STATUS_CONNECTION_IDLE, + ConnectionRefused = QUIC_STATUS_CONNECTION_REFUSED, + ProtocolError = QUIC_STATUS_PROTOCOL_ERROR, + VersionNegotiationError = QUIC_STATUS_VER_NEG_ERROR, + AlpnNegotiationFailure = QUIC_STATUS_ALPN_NEG_FAILURE, + StreamLimitReached = QUIC_STATUS_STREAM_LIMIT_REACHED, + TlsError = QUIC_STATUS_TLS_ERROR, + BadCertificate = QUIC_STATUS_BAD_CERTIFICATE, + RequiredCertificate = QUIC_STATUS_REQUIRED_CERTIFICATE, + RevokedCertificate = QUIC_STATUS_REVOKED_CERTIFICATE, + UnknonwCertificate = QUIC_STATUS_UNKNOWN_CERTIFICATE, + ExpiredCertificate = QUIC_STATUS_EXPIRED_CERTIFICATE, + AlpnInUse = QUIC_STATUS_ALPN_IN_USE, + CertExpired = QUIC_STATUS_CERT_EXPIRED, + CertNoCert = QUIC_STATUS_CERT_NO_CERT, + CertUntrustedRoot = QUIC_STATUS_CERT_UNTRUSTED_ROOT +}; + +/** + * @brief A helper utility providing characteristics of QUIC_STATUS. + * + * Providing helpers such as its QUIC_SUCCEEDED/QUIC_FAILED through + * succeeded/failed methods, along with implicit bool conversion + * equivalent to QUIC_SUCCEEDED macro. + */ +class w_status { +public: + constexpr w_status(QUIC_STATUS p_code) noexcept + : _status_code(static_cast(p_code)) + {} + + constexpr w_status(w_status_code p_code) noexcept + : _status_code(p_code) + {} + + /** + * @brief whether status indicates succession or not. + */ + [[nodiscard]] constexpr bool succeeded() const noexcept + { + return QUIC_SUCCEEDED(static_cast(_status_code)); + } + + /** + * @brief whether status indicates failure or not. + */ + [[nodiscard]] constexpr bool failed() const noexcept + { + return QUIC_FAILED(static_cast(_status_code)); + } + + constexpr operator bool() const noexcept + { + return succeeded(); + } + + constexpr operator w_status_code() const noexcept + { + return _status_code; + } + +private: + w_status_code _status_code = w_status_code::Success; +}; + +/** + * @brief convert given status to an equivalent short string. + */ +W_API std::string_view status_to_str(w_status p_status); + +} // namespace wolf::stream::quic diff --git a/wolf/stream/quic/internal/common.cpp b/wolf/stream/quic/internal/common.cpp new file mode 100644 index 000000000..d4ffd499e --- /dev/null +++ b/wolf/stream/quic/internal/common.cpp @@ -0,0 +1 @@ +#include "stream/quic/internal/common.hpp" diff --git a/wolf/stream/quic/internal/common.hpp b/wolf/stream/quic/internal/common.hpp new file mode 100644 index 000000000..1281be4af --- /dev/null +++ b/wolf/stream/quic/internal/common.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "wolf.hpp" + +#include + +namespace wolf::stream::quic::internal { + +/** + * @brief Tag/Indicator of raw interaction constructor/method overload. + */ +class W_API w_raw_tag {}; + +/** + * @brief A type to befriended by types which want + * to expose their private raw access api to + * other types within the library. + * + * This type allow for sharing a common api between + * any library type declaring it as friend to any other library type. + * + * This is to avoid redundant/hardcoded/forward-declared friend access pattern. + * + * It also limits the access to a certain private api, unlike direct friendship. + * + * "Friends of friend are also friend" :) + */ +class W_API w_raw_access { +public: + w_raw_access() = delete; + + template + static auto from_raw(Args&& ...args) + { + return T(w_raw_tag{}, std::forward(args)...); + } + + template + static auto raw(T&& p_object) + { + return std::forward(p_object).raw(); + } +}; + +} // namespace wolf::stream::quic::internal diff --git a/wolf/stream/quic/internal/w_msquic_api.cpp b/wolf/stream/quic/internal/w_msquic_api.cpp new file mode 100644 index 000000000..f1d3f5a69 --- /dev/null +++ b/wolf/stream/quic/internal/w_msquic_api.cpp @@ -0,0 +1,24 @@ +#include "stream/quic/internal/w_msquic_api.hpp" + +#include + +namespace wolf::stream::quic::internal { + +std::mutex w_msquic_api::_s_mutex{}; +const QUIC_API_TABLE* w_msquic_api::_s_instance = nullptr; + +auto w_msquic_api::api() -> const QUIC_API_TABLE* +{ + std::unique_lock lk{_s_mutex}; + + if (!_s_instance) { + QUIC_STATUS status; + if (QUIC_FAILED(status = MsQuicOpen2(&_s_instance))) { + throw std::exception("error occured when initializing msquic library."); + } + } + + return _s_instance; +} + +} // namespace wolf::stream::quic::internal diff --git a/wolf/stream/quic/internal/w_msquic_api.hpp b/wolf/stream/quic/internal/w_msquic_api.hpp new file mode 100644 index 000000000..bf1638a7a --- /dev/null +++ b/wolf/stream/quic/internal/w_msquic_api.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "wolf.hpp" + +#include + +#include + +namespace wolf::stream::quic::internal { + +/** + * @brief singleton msquic api function table provider. + */ +class W_API w_msquic_api { +public: + w_msquic_api() = delete; + + /** + * @brief make a valid QUIC_API_TABLE pointer instance. + */ + static auto api() -> const QUIC_API_TABLE*; + +private: + static std::mutex _s_mutex; + static const QUIC_API_TABLE* _s_instance; +}; + +} // namespace wolf::stream::quic::internal diff --git a/wolf/stream/quic/w_quic.cpp b/wolf/stream/quic/w_quic.cpp index 0a0e5a2f6..4f8c037fc 100644 --- a/wolf/stream/quic/w_quic.cpp +++ b/wolf/stream/quic/w_quic.cpp @@ -1,19 +1 @@ #include "wolf/stream/quic/w_quic.hpp" - -#include - -auto foo() -> const QUIC_API_TABLE* -{ - static std::mutex mutex{}; - static const QUIC_API_TABLE* api_table = nullptr; - - std::lock_guard lk{mutex}; - - if (!api_table) { - if (QUIC_FAILED(MsQuicOpen2(&api_table))) { - throw "fatal error."; - } - } - - return api_table; -} diff --git a/wolf/stream/quic/w_quic.hpp b/wolf/stream/quic/w_quic.hpp index d9fc61acf..afb969eb8 100644 --- a/wolf/stream/quic/w_quic.hpp +++ b/wolf/stream/quic/w_quic.hpp @@ -4,8 +4,9 @@ #pragma once -#include - -#include - -W_API auto foo() -> const QUIC_API_TABLE*; +#include "stream/quic/datatypes/w_status.hpp" +#include "stream/quic/datatypes/w_settings.hpp" +#include "stream/quic/datatypes/w_address.hpp" +#include "stream/quic/datatypes/w_credential_config.hpp" +#include "stream/quic/datatypes/w_registration_config.hpp" +#include "stream/quic/datatypes/w_new_connection_info.hpp" diff --git a/wolf/stream/test/quic.hpp b/wolf/stream/test/quic.hpp index 8e7b714f6..603911848 100644 --- a/wolf/stream/test/quic.hpp +++ b/wolf/stream/test/quic.hpp @@ -8,15 +8,17 @@ #include -BOOST_AUTO_TEST_CASE(quic) -{ - std::cout << "entering test case 'quic'" << std::endl; +namespace quic = wolf::stream::quic; - const wolf::system::w_leak_detector detector{}; +BOOST_AUTO_TEST_CASE(msquic_base_types) { + BOOST_REQUIRE(quic::w_status(quic::w_status_code::Success).succeeded()); + BOOST_REQUIRE(quic::w_status(quic::w_status_code::Aborted).failed()); - BOOST_REQUIRE(foo()); - - std::cout << "leaving test case 'quic'" << std::endl; + { + auto status_code = quic::w_status_code::Success; + auto status = quic::w_status(status_code); + BOOST_REQUIRE(quic::status_to_str(status_code) == quic::status_to_str(status)); + } } #endif // defined(WOLF_TEST) && defined(WOLF_STREAM_QUIC) diff --git a/wolf/system/w_flags.hpp b/wolf/system/w_flags.hpp new file mode 100644 index 000000000..907dfa8a0 --- /dev/null +++ b/wolf/system/w_flags.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include "wolf.hpp" + +#include + +namespace wolf::system { + +/** + * @brief combinable and comparable bit flags of given enum. + * + * Enum types, specially in C programming language, are commonly + * used for bit flags. but in C++ an enum type requires a cast + * and bitwise operators can't be applied to it directly. + * + * This class helps with avoiding repeating cast + * and mistaken implicit numeric conversions. + * + * It's implicitly convertible to boolean, and supports & and | operators. + * It's also explicitly convertible to given enum's underlying type and std::size_t. + * + * @tparam T enum type to wrap. + */ +template + requires std::is_enum_v +class W_API w_flags { +public: + using enum_type = std::remove_cvref_t; + using underlying_type = std::underlying_type_t; + + constexpr w_flags(enum_type value) noexcept + : _bits(static_cast(value)) + {} + + /** + * @brief to underlying integral type. + */ + constexpr auto to_underlying() const noexcept { return _bits; } + + constexpr friend w_flags operator&(w_flags lhs, w_flags rhs) noexcept + { + return w_flags(lhs._bits | rhs._bits); + } + + constexpr friend w_flags operator&(w_flags lhs, enum_type rhs) noexcept + { + return w_flags(lhs._bits & static_cast(rhs)); + } + + constexpr friend w_flags operator|(w_flags lhs, w_flags rhs) noexcept + { + return w_flags(lhs._bits | rhs._bits); + } + + constexpr friend w_flags operator|(w_flags lhs, enum_type rhs) noexcept + { + return w_flags(lhs._bits | static_cast(rhs)); + } + + constexpr friend bool operator==(w_flags lhs, w_flags rhs) noexcept + { + return lhs._bits == rhs._bits; + } + + constexpr friend bool operator!=(w_flags lhs, w_flags rhs) noexcept + { + return lhs._bits != rhs._bits; + } + + constexpr friend bool operator==(w_flags lhs, enum_type rhs) noexcept + { + return lhs._bits == static_cast(rhs); + } + + constexpr friend bool operator!=(w_flags lhs, enum_type rhs) noexcept + { + return lhs._bits != static_cast(rhs); + } + + constexpr operator bool() const noexcept + { + return bool(_bits); + } + + constexpr explicit operator underlying_type() const noexcept + { + return _bits; + } + + constexpr explicit operator std::size_t() const noexcept + { + return std::size_t(_bits); + } + +private: + constexpr explicit w_flags(underlying_type bits) noexcept + : _bits(bits) + {} + + underlying_type _bits; +}; + +} // namespace wolf::system + diff --git a/wolf/system/w_overloaded.hpp b/wolf/system/w_overloaded.hpp new file mode 100644 index 000000000..b94dffa5f --- /dev/null +++ b/wolf/system/w_overloaded.hpp @@ -0,0 +1,30 @@ +/* + Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar + https://github.com/WolfEngine/WolfEngine +*/ + +#pragma once + +namespace wolf::system { + +/** + * @brief famous `overloaded` to combine callables (specially lambdas) into one overloaded callable. + * + * example: + * ```cpp + * double res = std::visit( + * w_overloaded{ + * [](long long int val) { return val * 2.0; }, + * [](double val) { return val / 2.0; } + * }, + * my_variant + * ); + * ``` + */ +template +struct w_overloaded : public Fs... { using Fs::operator()...; }; + +template +w_overloaded(Fs&& ...) -> w_overloaded; + +} // namespace wolf::system