diff options
| author | Stefan Boberg <[email protected]> | 2026-03-10 17:27:26 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-10 17:27:26 +0100 |
| commit | d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7 (patch) | |
| tree | 2dfe1e3e0b620043d358e0b7f8bdf8320d985491 /src/zenhttp/servers/wsasio.cpp | |
| parent | changelog entry which was inadvertently omitted from PR merge (diff) | |
| download | zen-d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7.tar.xz zen-d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7.zip | |
HttpClient using libcurl, Unix Sockets for HTTP. HTTPS support (#770)
The main goal of this change is to eliminate the cpr back-end altogether and replace it with the curl implementation. I would expect to drop cpr as soon as we feel happy with the libcurl back-end. That would leave us with a direct dependency on libcurl only, and cpr can be eliminated as a dependency.
### HttpClient Backend Overhaul
- Implemented a new **libcurl-based HttpClient** backend (`httpclientcurl.cpp`, ~2000 lines)
as an alternative to the cpr-based one
- Made HttpClient backend **configurable at runtime** via constructor arguments
and `-httpclient=...` CLI option (for zen, zenserver, and tests)
- Extended HttpClient test suite to cover multipart/content-range scenarios
### Unix Domain Socket Support
- Added Unix domain socket support to **httpasio** (server side)
- Added Unix domain socket support to **HttpClient**
- Added Unix domain socket support to **HttpWsClient** (WebSocket client)
- Templatized `HttpServerConnectionT<SocketType>` and `WsAsioConnectionT<SocketType>`
to handle TCP, Unix, and SSL sockets uniformly via `if constexpr` dispatch
### HTTPS Support
- Added **preliminary HTTPS support to httpasio** (for Mac/Linux via OpenSSL)
- Added **basic HTTPS support for http.sys** (Windows)
- Implemented HTTPS test for httpasio
- Split `InitializeServer` into smaller sub-functions for http.sys
### Other Notable Changes
- Improved **zenhttp-test stability** with dynamic port allocation
- Enhanced port retry logic in http.sys (handles ERROR_ACCESS_DENIED)
- Fatal signal/exception handlers for backtrace generation in tests
- Added `zen bench http` subcommand to exercise network + HTTP client/server communication stack
Diffstat (limited to 'src/zenhttp/servers/wsasio.cpp')
| -rw-r--r-- | src/zenhttp/servers/wsasio.cpp | 64 |
1 files changed, 46 insertions, 18 deletions
diff --git a/src/zenhttp/servers/wsasio.cpp b/src/zenhttp/servers/wsasio.cpp index b2543277a..5ae48f5b3 100644 --- a/src/zenhttp/servers/wsasio.cpp +++ b/src/zenhttp/servers/wsasio.cpp @@ -1,6 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include "wsasio.h" +#include "asio_socket_traits.h" #include "wsframecodec.h" #include <zencore/logging.h> @@ -17,14 +18,16 @@ WsLog() ////////////////////////////////////////////////////////////////////////// -WsAsioConnection::WsAsioConnection(std::unique_ptr<asio::ip::tcp::socket> Socket, IWebSocketHandler& Handler, HttpServer* Server) +template<typename SocketType> +WsAsioConnectionT<SocketType>::WsAsioConnectionT(std::unique_ptr<SocketType> Socket, IWebSocketHandler& Handler, HttpServer* Server) : m_Socket(std::move(Socket)) , m_Handler(Handler) , m_HttpServer(Server) { } -WsAsioConnection::~WsAsioConnection() +template<typename SocketType> +WsAsioConnectionT<SocketType>::~WsAsioConnectionT() { m_IsOpen.store(false); if (m_HttpServer) @@ -33,14 +36,16 @@ WsAsioConnection::~WsAsioConnection() } } +template<typename SocketType> void -WsAsioConnection::Start() +WsAsioConnectionT<SocketType>::Start() { EnqueueRead(); } +template<typename SocketType> bool -WsAsioConnection::IsOpen() const +WsAsioConnectionT<SocketType>::IsOpen() const { return m_IsOpen.load(std::memory_order_relaxed); } @@ -50,23 +55,25 @@ WsAsioConnection::IsOpen() const // Read loop // +template<typename SocketType> void -WsAsioConnection::EnqueueRead() +WsAsioConnectionT<SocketType>::EnqueueRead() { if (!m_IsOpen.load(std::memory_order_relaxed)) { return; } - Ref<WsAsioConnection> Self(this); + Ref<WsAsioConnectionT> Self(this); asio::async_read(*m_Socket, m_ReadBuffer, asio::transfer_at_least(1), [Self](const asio::error_code& Ec, std::size_t ByteCount) { Self->OnDataReceived(Ec, ByteCount); }); } +template<typename SocketType> void -WsAsioConnection::OnDataReceived(const asio::error_code& Ec, [[maybe_unused]] std::size_t ByteCount) +WsAsioConnectionT<SocketType>::OnDataReceived(const asio::error_code& Ec, [[maybe_unused]] std::size_t ByteCount) { if (Ec) { @@ -90,8 +97,9 @@ WsAsioConnection::OnDataReceived(const asio::error_code& Ec, [[maybe_unused]] st } } +template<typename SocketType> void -WsAsioConnection::ProcessReceivedData() +WsAsioConnectionT<SocketType>::ProcessReceivedData() { while (m_ReadBuffer.size() > 0) { @@ -162,8 +170,8 @@ WsAsioConnection::ProcessReceivedData() // Shut down the socket std::error_code ShutdownEc; - m_Socket->shutdown(asio::socket_base::shutdown_both, ShutdownEc); - m_Socket->close(ShutdownEc); + SocketTraits<SocketType>::ShutdownBoth(*m_Socket, ShutdownEc); + SocketTraits<SocketType>::Close(*m_Socket, ShutdownEc); return; } @@ -179,8 +187,9 @@ WsAsioConnection::ProcessReceivedData() // Write queue // +template<typename SocketType> void -WsAsioConnection::SendText(std::string_view Text) +WsAsioConnectionT<SocketType>::SendText(std::string_view Text) { if (!m_IsOpen.load(std::memory_order_relaxed)) { @@ -192,8 +201,9 @@ WsAsioConnection::SendText(std::string_view Text) EnqueueWrite(std::move(Frame)); } +template<typename SocketType> void -WsAsioConnection::SendBinary(std::span<const uint8_t> Data) +WsAsioConnectionT<SocketType>::SendBinary(std::span<const uint8_t> Data) { if (!m_IsOpen.load(std::memory_order_relaxed)) { @@ -204,14 +214,16 @@ WsAsioConnection::SendBinary(std::span<const uint8_t> Data) EnqueueWrite(std::move(Frame)); } +template<typename SocketType> void -WsAsioConnection::Close(uint16_t Code, std::string_view Reason) +WsAsioConnectionT<SocketType>::Close(uint16_t Code, std::string_view Reason) { DoClose(Code, Reason); } +template<typename SocketType> void -WsAsioConnection::DoClose(uint16_t Code, std::string_view Reason) +WsAsioConnectionT<SocketType>::DoClose(uint16_t Code, std::string_view Reason) { if (!m_IsOpen.exchange(false)) { @@ -227,8 +239,9 @@ WsAsioConnection::DoClose(uint16_t Code, std::string_view Reason) m_Handler.OnWebSocketClose(*this, Code, Reason); } +template<typename SocketType> void -WsAsioConnection::EnqueueWrite(std::vector<uint8_t> Frame) +WsAsioConnectionT<SocketType>::EnqueueWrite(std::vector<uint8_t> Frame) { if (m_HttpServer) { @@ -252,8 +265,9 @@ WsAsioConnection::EnqueueWrite(std::vector<uint8_t> Frame) } } +template<typename SocketType> void -WsAsioConnection::FlushWriteQueue() +WsAsioConnectionT<SocketType>::FlushWriteQueue() { std::vector<uint8_t> Frame; @@ -272,7 +286,7 @@ WsAsioConnection::FlushWriteQueue() return; } - Ref<WsAsioConnection> Self(this); + Ref<WsAsioConnectionT> Self(this); // Move Frame into a shared_ptr so we can create the buffer and capture ownership // in the same async_write call without evaluation order issues. @@ -283,8 +297,9 @@ WsAsioConnection::FlushWriteQueue() [Self, OwnedFrame](const asio::error_code& Ec, std::size_t ByteCount) { Self->OnWriteComplete(Ec, ByteCount); }); } +template<typename SocketType> void -WsAsioConnection::OnWriteComplete(const asio::error_code& Ec, [[maybe_unused]] std::size_t ByteCount) +WsAsioConnectionT<SocketType>::OnWriteComplete(const asio::error_code& Ec, [[maybe_unused]] std::size_t ByteCount) { if (Ec) { @@ -308,4 +323,17 @@ WsAsioConnection::OnWriteComplete(const asio::error_code& Ec, [[maybe_unused]] s FlushWriteQueue(); } +////////////////////////////////////////////////////////////////////////// +// Explicit template instantiations + +template class WsAsioConnectionT<asio::ip::tcp::socket>; + +#if defined(ASIO_HAS_LOCAL_SOCKETS) +template class WsAsioConnectionT<asio::local::stream_protocol::socket>; +#endif + +#if ZEN_USE_OPENSSL +template class WsAsioConnectionT<asio::ssl::stream<asio::ip::tcp::socket>>; +#endif + } // namespace zen::asio_http |