diff --git a/wolf/cmake/stream.cmake b/wolf/cmake/stream.cmake index c0bdb2bc2..b111a0862 100644 --- a/wolf/cmake/stream.cmake +++ b/wolf/cmake/stream.cmake @@ -51,33 +51,40 @@ if (WOLF_STREAM_JANUS) endif() -# fetch lsquic +# fetch msquic if (WOLF_STREAM_QUIC) if (EMSCRIPTEN) message(FATAL_ERROR "WOLF_STREAM_QUIC is not supported for wasm32 target") endif() - if (NOT WOLF_SYSTEM_SSL) - message(FATAL_ERROR "WOLF_SYSTEM_SSL is required for WOLF_STREAM_QUIC") + + if (NOT WIN32) + message(FATAL_ERROR "WOLF_STREAM_QUIC feature is not avilable on non-windows yet.") endif() - - message("fetching https://github.com/litespeedtech/lsquic.git") - FetchContent_Declare( - lsquic - GIT_REPOSITORY https://github.com/litespeedtech/lsquic.git - GIT_TAG master + + file(GLOB_RECURSE WOLF_STREAM_QUIC_SRCS + "${CMAKE_CURRENT_SOURCE_DIR}/stream/quic/*" ) - set(LSQUIC_TESTS OFF CACHE BOOL "LSQUIC_TESTS") + if (WIN32 OR WIN64) + FetchContent_Declare( + msquic + URL https://github.com/microsoft/msquic/releases/download/v2.2.0/msquic_windows_x64_Release_schannel.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + FetchContent_Populate(msquic) + else() + message(FATAL_ERROR "WOLF_STREAM_QUIC feature is not supported on target platform.") + endif() - set(FETCHCONTENT_QUIET OFF) - FetchContent_MakeAvailable(lsquic) + add_library(msquic-lib INTERFACE) + add_library(msquic::msquic ALIAS msquic-lib) + target_include_directories(msquic-lib INTERFACE ${msquic_SOURCE_DIR}/include) + target_link_directories(msquic-lib INTERFACE BEFORE ${msquic_SOURCE_DIR}/bin) + target_link_directories(msquic-lib INTERFACE BEFORE ${msquic_SOURCE_DIR}/lib) + target_link_libraries(msquic-lib INTERFACE msquic) - list(APPEND INCLUDES - ${lsquic_SOURCE_DIR}/include - ${lsquic_SOURCE_DIR}/wincompat - ) - list(APPEND LIBS lsquic) - + list(APPEND SRCS ${WOLF_STREAM_QUIC_SRCS}) + list(APPEND LIBS msquic::msquic) endif() if (WOLF_STREAM_RIST) diff --git a/wolf/stream/quic/w_quic.cpp b/wolf/stream/quic/w_quic.cpp index 9b7c59021..0a0e5a2f6 100644 --- a/wolf/stream/quic/w_quic.cpp +++ b/wolf/stream/quic/w_quic.cpp @@ -1,439 +1,19 @@ -//#ifdef WOLF_STREAM_QUIC -// -//#include "w_quic.hpp" -// -//#ifdef WIN32 -//#pragma warning(disable:5105) -//#endif -// -//#include "msquic.h" -// -//using w_quic = wolf::stream::quic::w_quic; -//using w_quic_profile = wolf::stream::quic::w_quic_profile; -// -//struct QUIC_CREDENTIAL_CONFIG_HELPER { -// QUIC_CREDENTIAL_CONFIG CredConfig; -// union { -// QUIC_CERTIFICATE_HASH CertHash; -// QUIC_CERTIFICATE_HASH_STORE CertHashStore; -// QUIC_CERTIFICATE_FILE CertFile; -// QUIC_CERTIFICATE_FILE_PROTECTED CertFileProtected; -// }; -//}; -// -//// The protocol name used in the Application Layer Protocol Negotiation (ALPN). -//const QUIC_BUFFER S_ALPN = { sizeof("sample") - 1, (uint8_t*)"sample" }; -// -//// forward declrations for callbacks -// -//// The server's callback for stream events from MsQuic. -//_IRQL_requires_max_(DISPATCH_LEVEL) -//_Function_class_(QUIC_STREAM_CALLBACK) -//QUIC_STATUS QUIC_API s_server_stream_callback( -// _In_ HQUIC p_stream, -// _In_opt_ void* p_context, -// _Inout_ QUIC_STREAM_EVENT* p_event); -// -//// The server's callback for connection events from MsQuic. -//_IRQL_requires_max_(DISPATCH_LEVEL) -//_Function_class_(QUIC_CONNECTION_CALLBACK) -//QUIC_STATUS QUIC_API s_server_connection_callback( -// _In_ HQUIC p_connection, -// _In_opt_ void* p_context, -// _Inout_ QUIC_CONNECTION_EVENT* p_event) noexcept; -// -// -//// The server's callback for listener events from MsQuic. -//_IRQL_requires_max_(PASSIVE_LEVEL) -//_Function_class_(QUIC_LISTENER_CALLBACK) -//QUIC_STATUS QUIC_API s_server_listener_callback( -// _In_ HQUIC p_listener, -// _In_opt_ void* p_context, -// _Inout_ QUIC_LISTENER_EVENT* p_event); -// -//struct w_quic::q_ctx -//{ -// const QUIC_API_TABLE* api = nullptr; -// QUIC_REGISTRATION_CONFIG reg_config = {}; -// HQUIC handle = nullptr; -// HQUIC listener = nullptr; -// HQUIC config = nullptr; -// -// w_quic::q_ctx(std::string p_app_name, w_quic_profile p_profile) noexcept -// { -// this->reg_config = -// { -// p_app_name.c_str(), -// gsl::narrow_cast(p_profile) -// }; -// } -//}; -// -//w_quic::w_quic( -// std::string p_app_name, -// uint16_t p_port, -// w_quic_type p_quic_type, -// w_quic_profile p_profile) : -// _port(p_port), -// _quic_type(p_quic_type), -// ctx(std::make_unique(p_app_name, p_profile)) -//{ -//} -// -//w_quic::~w_quic() -//{ -// _release(); -//} -// -//tl::expected w_quic::init() -//{ -// constexpr auto TRACE = "w_quic::init"; -// -// if (this->ctx == nullptr) -// { -// auto _msg = fmt::format("bad context. trace: {} \n", TRACE); -// return tl::unexpected(_msg); -// } -// -// // Open a handle to the library and get the API function table. -// auto _ret = MsQuicOpen2(&this->ctx->api); -// if (_ret != S_OK) -// { -// auto _msg = fmt::format("MsQuicOpen2 failed, {:x}. trace: {} \n", _ret, TRACE); -// return tl::unexpected(_msg); -// } -// -// // Create a registration for the app's connections. -// _ret = this->ctx->api->RegistrationOpen(&this->ctx->reg_config, &this->ctx->handle); -// if (_ret != S_OK) -// { -// auto _msg = fmt::format("RegistrationOpen failed, {:x}. trace: {} \n", _ret, TRACE); -// return tl::unexpected(_msg); -// } -// -// switch (this->_quic_type) -// { -// default: -// { -// auto _msg = fmt::format("Undefined quic server type. trace: {} \n", TRACE); -// return tl::unexpected(_msg); -// } -// case w_quic_type::CLIENT: -// { -// return init_client(); -// } -// case w_quic_type::SERVER: -// { -// return init_client(); -// } -// } -//} -// -//tl::expected w_quic::init_client() -//{ -// return tl::expected(); -//} -// -//tl::expected w_quic::init_server() -//{ -// constexpr auto TRACE = "w_quic::init_server"; -// -// QUIC_STATUS _ret; -// -// // configures the address used for the listener to listen on all IP addresses and the given UDP port -// QUIC_ADDR _address = { 0 }; -// QuicAddrSetFamily(&_address, QUIC_ADDRESS_FAMILY_UNSPEC); -// QuicAddrSetPort(&_address, this->_port); -// -// // Load the server configuration based on the command line. -// //if (!ServerLoadConfiguration(argc, argv)) -// //{ -// // return; -// //} -// -// // create/allocate a new listener object. -// //auto _hr = this->ctx->api->ListenerOpen( -// // this->ctx->handle, -// // s_server_listener_callback, -// // gsl::narrow_cast(this->ctx.get()), -// // &this->ctx->listener); -// //if (_hr != S_OK) -// //{ -// // auto _msg = fmt::format("ListenerOpen failed, error code:{:x}. trace: {}\n", _ret, TRACE); -// // return tl::unexpected(_msg); -// //} -// -// // Starts listening for incoming connections. -// _ret = this->ctx->api->ListenerStart( -// this->ctx->listener, -// &S_ALPN, -// 1, -// &_address); -// if (_ret != S_OK) -// { -// auto _msg = fmt::format("Undefined quic server type. trace: {} \n", TRACE); -// return tl::unexpected(_msg); -// -// //printf("ListenerStart failed, 0x%x!\n", Status); -// } -// -// return tl::expected(); -//} -// -//void w_quic::_release() noexcept -//{ -// if (this->ctx != nullptr && -// this->ctx->api != nullptr && -// this->ctx->listener != nullptr) -// { -// this->ctx->api->ListenerClose(this->ctx->listener); -// } -//} -// -//// The server's callback for stream events from MsQuic. -//_IRQL_requires_max_(DISPATCH_LEVEL) -//_Function_class_(QUIC_STREAM_CALLBACK) -//QUIC_STATUS QUIC_API s_server_stream_callback( -// _In_ HQUIC p_stream, -// _In_opt_ void* p_context, -// _Inout_ QUIC_STREAM_EVENT* p_event) -//{ -// auto _ctx = gsl::narrow_cast(p_context); -// if (_ctx == nullptr) -// { -// return QUIC_STATUS_INVALID_PARAMETER; -// } -// -// switch (p_event->Type) -// { -// case QUIC_STREAM_EVENT_SEND_COMPLETE: -// { -// // A previous StreamSend call has completed, and the context is being -// // returned back to the app. -// free(p_event->SEND_COMPLETE.ClientContext); -// //fmt::print("stream {} data sent\n", p_stream); -// break; -// } -// case QUIC_STREAM_EVENT_RECEIVE: -// { -// // Data was received from the peer on the stream. -// //fmt::print("stream {} data received\n", p_stream); -// break; -// } -// case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: -// { -// // The peer gracefully shut down its send direction of the stream. -// //printf("[strm][%p] Peer shut down\n", Stream); -// //ServerSend(Stream); -// break; -// } -// case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: -// { -// // -// // The peer aborted its send direction of the stream. -// // -// //printf("[strm][%p] Peer aborted\n", Stream); -// _ctx->api->StreamShutdown(p_stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); -// break; -// } -// case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: -// { -// // -// // Both directions of the stream have been shut down and MsQuic is done -// // with the stream. It can now be safely cleaned up. -// // -// //printf("[strm][%p] All done\n", Stream); -// _ctx->api->StreamClose(p_stream); -// break; -// } -// default: -// break; -// } -// return QUIC_STATUS_SUCCESS; -//} -// -//// Helper function to load a server configuration. Uses the command line -//// arguments to load the credential part of the configuration. -//W_RESULT s_server_load_configuration(w_quic::q_ctx* p_ctx) -//{ -// if (p_ctx == nullptr) -// { -// return W_RESULT::FAILURE; -// } -// -// // the following setting should be provided from inputs -// constexpr uint64_t _idle_timeout_ms = 1000; -// -// QUIC_SETTINGS settings = { 0 }; -// // Configures the server's idle timeout. -// settings.IdleTimeoutMs = _idle_timeout_ms; -// settings.IsSet.IdleTimeoutMs = 1; -// // Configures the server's resumption level to allow for resumption and 0-RTT. -// settings.ServerResumptionLevel = QUIC_SERVER_RESUME_AND_ZERORTT; -// settings.IsSet.ServerResumptionLevel = 1; -// // Configures the server's settings to allow for the peer to open a single -// // bidirectional stream. By default connections are not configured to allow -// // any streams from the peer. -// settings.PeerBidiStreamCount = 1; -// settings.IsSet.PeerBidiStreamCount = 1; -// -// QUIC_CREDENTIAL_CONFIG_HELPER _config; -// memset(&_config, 0, sizeof(_config)); -// _config.CredConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE; -// -// const char* _cert = nullptr; -// const char* _key_file = nullptr; -// //if ((Cert = GetValue(argc, argv, "cert_hash")) != NULL) { -// // // -// // // Load the server's certificate from the default certificate store, -// // // using the provided certificate hash. -// // // -// // uint32_t CertHashLen = -// // DecodeHexBuffer( -// // Cert, -// // sizeof(Config.CertHash.ShaHash), -// // Config.CertHash.ShaHash); -// // if (CertHashLen != sizeof(Config.CertHash.ShaHash)) { -// // return FALSE; -// // } -// // Config.CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH; -// // Config.CredConfig.CertificateHash = &Config.CertHash; -// -// //} -// //else if ((Cert = GetValue(argc, argv, "cert_file")) != NULL && -// // (KeyFile = GetValue(argc, argv, "key_file")) != NULL) { -// // // -// // // Loads the server's certificate from the file. -// // // -// // const char* Password = GetValue(argc, argv, "password"); -// // if (Password != nullptr) -// // { -// // Config.CertFileProtected.CertificateFile = (char*)Cert; -// // Config.CertFileProtected.PrivateKeyFile = (char*)KeyFile; -// // Config.CertFileProtected.PrivateKeyPassword = (char*)Password; -// // Config.CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED; -// // Config.CredConfig.CertificateFileProtected = &Config.CertFileProtected; -// // } -// // else { -// // Config.CertFile.CertificateFile = (char*)Cert; -// // Config.CertFile.PrivateKeyFile = (char*)KeyFile; -// // Config.CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; -// // Config.CredConfig.CertificateFile = &Config.CertFile; -// // } -// -// //} -// //else { -// // printf("Must specify ['-cert_hash'] or ['cert_file' and 'key_file' (and optionally 'password')]!\n"); -// // return FALSE; -// //} -// -// // -// // Allocate/initialize the configuration object, with the configured ALPN -// // and settings. -// // -// //auto _status = QUIC_STATUS_SUCCESS; -// //p_ctx->api->ConfigurationOpen(Registration, &Alpn, 1, &Settings, sizeof(Settings), NULL, &Configuration) -// //if (_status != S_OK) -// //{ -// // printf("ConfigurationOpen failed, 0x%x!\n", Status); -// // return FALSE; -// //} -// -// // -// // Loads the TLS credential part of the configuration. -// // -// //if (QUIC_FAILED(Status = MsQuic->ConfigurationLoadCredential(Configuration, &Config.CredConfig))) { -// // printf("ConfigurationLoadCredential failed, 0x%x!\n", Status); -// // return FALSE; -// //} -// -// return W_RESULT::SUCCESS; -//} -// -//_IRQL_requires_max_(DISPATCH_LEVEL) -//_Function_class_(QUIC_CONNECTION_CALLBACK) -//QUIC_STATUS QUIC_API s_server_connection_callback( -// _In_ HQUIC p_connection, -// _In_opt_ void* p_context, -// _Inout_ QUIC_CONNECTION_EVENT* p_event) noexcept -//{ -// auto _ctx = gsl::narrow_cast(p_context); -// if (_ctx == nullptr || p_event == nullptr) -// { -// return QUIC_STATUS_INVALID_PARAMETER; -// } -// -// switch (p_event->Type) -// { -// case QUIC_CONNECTION_EVENT_CONNECTED: -// { -// // The handshake has completed for the connection. -// -// //printf("[conn][%p] Connected\n", p_connection); -// _ctx->api->ConnectionSendResumptionTicket( -// p_connection, -// QUIC_SEND_RESUMPTION_FLAG_NONE, -// 0, -// nullptr); -// break; -// } -// case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: -// { -// // The connection has been shut down by the transport. Generally, this -// // is the expected way for the connection to shut down with this -// // protocol, since we let idle timeout kill the connection. -// if (p_event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status == QUIC_STATUS_CONNECTION_IDLE) -// { -// //fmt::print("connection {} successfully shut down on idle.\n", p_connection); -// } -// else -// { -// /*fmt::print("connection {} shut down by transport, {:x}\n", -// p_connection, -// p_event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status); -// */ -// } -// break; -// } -// case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: -// { -// // The connection was explicitly shut down by the peer. -// /*fmt::print("connection {} shut down by peer, {}\n", -// p_connection, -// (unsigned long long)p_event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode); -// */ -// break; -// } -// case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: -// { -// // The connection has completed the shutdown process and is ready to be -// // safely cleaned up. -// //fmt::print("connection {} all done\n", p_connection); -// _ctx->api->ConnectionClose(p_connection); -// break; -// } -// case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED: -// { -// // The peer has started/created a new stream. The app MUST set the -// // callback handler before returning. -// //fmt::print("stream {} peer started\n", p_event->PEER_STREAM_STARTED.Stream); -// _ctx->api->SetCallbackHandler( -// p_event->PEER_STREAM_STARTED.Stream, -// (void*)s_server_stream_callback, -// nullptr); -// break; -// } -// case QUIC_CONNECTION_EVENT_RESUMED: -// { -// // The connection succeeded in doing a TLS resumption of a previous -// // connection's session. -// //fmt::print("connection {} connection resumed!\n", p_connection); -// break; -// } -// default: -// break; -// } -// return QUIC_STATUS_SUCCESS; -//} -// -//#endif \ No newline at end of file +#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 9d0732193..d9fc61acf 100644 --- a/wolf/stream/quic/w_quic.hpp +++ b/wolf/stream/quic/w_quic.hpp @@ -1,59 +1,11 @@ -///* -// Project: Wolf Engine. Copyright � 2014-2023 Pooya Eimandar -// https://github.com/WolfEngine/WolfEngine -//*/ -// -// #pragma once -// -// #ifdef WOLF_STREAM_QUIC -// -// #include -// #include -// -// namespace wolf::stream::quic -//{ -// enum class w_quic_type -// { -// CLIENT, -// SERVER, -// }; -// -// enum class w_quic_profile -// { -// LOW_LATENCY, -// MAX_THROUGHPUT, -// SCAVENGER, -// REAL_TIME, -// }; -// -// class w_quic -// { -// public: -// //struct service_port; -// w_quic( -// std::string p_app_name, -// uint16_t p_port, -// w_quic_type p_quic_type, -// w_quic_profile p_profile); -// virtual ~w_quic(); -// -// tl::expected init(); -// -// //boost::signals2::signal on_connection_new_sig; -// //boost::signals2::signal on_connection_closed_sig; -// -// struct q_ctx; -// std::unique_ptr ctx; -// private: -// w_quic_type _quic_type; -// uint16_t _port; -// -// tl::expected init_client(); -// tl::expected init_server(); -// void _release() noexcept; -// }; -//} // namespace wolf::stream::quic -// -// #endif -// -// +#ifndef WOLF_STREAM_QUIC +#error "WOLF_STREAM_QUIC feature is not enabled." +#endif + +#pragma once + +#include + +#include + +W_API auto foo() -> const QUIC_API_TABLE*; diff --git a/wolf/stream/test/quic.hpp b/wolf/stream/test/quic.hpp new file mode 100644 index 000000000..8e7b714f6 --- /dev/null +++ b/wolf/stream/test/quic.hpp @@ -0,0 +1,22 @@ +#if defined(WOLF_TEST) && defined(WOLF_STREAM_QUIC) + +#pragma once + +#include +#include +#include + +#include + +BOOST_AUTO_TEST_CASE(quic) +{ + std::cout << "entering test case 'quic'" << std::endl; + + const wolf::system::w_leak_detector detector{}; + + BOOST_REQUIRE(foo()); + + std::cout << "leaving test case 'quic'" << std::endl; +} + +#endif // defined(WOLF_TEST) && defined(WOLF_STREAM_QUIC) diff --git a/wolf/tests.cpp b/wolf/tests.cpp index 53f608cc7..fca3beded 100644 --- a/wolf/tests.cpp +++ b/wolf/tests.cpp @@ -42,6 +42,7 @@ BOOST_AUTO_TEST_CASE(wolf_test) { //#include #include #include +#include #pragma endregion