diff options
| author | zousar <[email protected]> | 2024-02-06 10:02:45 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-02-06 16:02:45 +0100 |
| commit | 3eecbb0c8a6e9bd906b4d313c3d690a2c4365ca4 (patch) | |
| tree | e7b9bcae2e1e3df11b8789ebe987ec41fe7159c6 /src/zenhttp/servers | |
| parent | 5.4.1-pre1 (diff) | |
| download | zen-3eecbb0c8a6e9bd906b4d313c3d690a2c4365ca4.tar.xz zen-3eecbb0c8a6e9bd906b4d313c3d690a2c4365ca4.zip | |
Add testcase and fix for ASIO ipv4 localhost support (#649)
- Bugfix: Implement two listening sockets in ASIO (ipv4+ipv6) when either we start with `--http-forceloopback` or we resort to that mode because of a failure to bind to the "any" address
Diffstat (limited to 'src/zenhttp/servers')
| -rw-r--r-- | src/zenhttp/servers/httpasio.cpp | 73 |
1 files changed, 59 insertions, 14 deletions
diff --git a/src/zenhttp/servers/httpasio.cpp b/src/zenhttp/servers/httpasio.cpp index 9fca314b3..6b45c259d 100644 --- a/src/zenhttp/servers/httpasio.cpp +++ b/src/zenhttp/servers/httpasio.cpp @@ -573,29 +573,42 @@ struct HttpAcceptor : m_Server(Server) , m_IoService(IoService) , m_Acceptor(m_IoService, asio::ip::tcp::v6()) + , m_AlternateProtocolAcceptor(m_IoService, asio::ip::tcp::v4()) { m_Acceptor.set_option(asio::ip::v6_only(false)); #if ZEN_PLATFORM_WINDOWS // Special option for Windows settings as !asio::socket_base::reuse_address is not the same as exclusive access on Windows platforms - typedef asio::detail::socket_option::boolean<ASIO_OS_DEF(SOL_SOCKET), SO_EXCLUSIVEADDRUSE> excluse_address; - m_Acceptor.set_option(excluse_address(true)); + typedef asio::detail::socket_option::boolean<ASIO_OS_DEF(SOL_SOCKET), SO_EXCLUSIVEADDRUSE> exclusive_address; + m_Acceptor.set_option(exclusive_address(true)); + m_AlternateProtocolAcceptor.set_option(exclusive_address(true)); #else // ZEN_PLATFORM_WINDOWS m_Acceptor.set_option(asio::socket_base::reuse_address(false)); + m_AlternateProtocolAcceptor.set_option(asio::socket_base::reuse_address(false)); #endif // ZEN_PLATFORM_WINDOWS m_Acceptor.set_option(asio::ip::tcp::no_delay(true)); m_Acceptor.set_option(asio::socket_base::receive_buffer_size(128 * 1024)); m_Acceptor.set_option(asio::socket_base::send_buffer_size(256 * 1024)); + m_AlternateProtocolAcceptor.set_option(asio::ip::tcp::no_delay(true)); + m_AlternateProtocolAcceptor.set_option(asio::socket_base::receive_buffer_size(128 * 1024)); + m_AlternateProtocolAcceptor.set_option(asio::socket_base::send_buffer_size(256 * 1024)); + asio::ip::address_v6 BindAddress = ForceLoopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); uint16_t EffectivePort = BasePort; + if (BindAddress.is_loopback()) + { + m_Acceptor.set_option(asio::ip::v6_only(true)); + } + asio::error_code BindErrorCode; m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); if (BindErrorCode == asio::error::access_denied && !BindAddress.is_loopback()) { // Access denied for a public port - lets try fall back to local port only BindAddress = asio::ip::address_v6::loopback(); + m_Acceptor.set_option(asio::ip::v6_only(true)); m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); } // Sharing violation implies the port is being used by another process @@ -615,6 +628,8 @@ struct HttpAcceptor } else if (BindAddress.is_loopback()) { + m_AlternateProtocolAcceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v4::loopback(), EffectivePort), BindErrorCode); + m_UseAlternateProtocolAcceptor = true; ZEN_INFO("Registered local-only handler 'http://{}:{}/' - this is not accessible from remote hosts", "[::1]", EffectivePort); } @@ -635,8 +650,25 @@ struct HttpAcceptor &OptionNumberOfBytesReturned, 0, 0); + if (m_UseAlternateProtocolAcceptor) + { + NativeSocket = m_AlternateProtocolAcceptor.native_handle(); + WSAIoctl(NativeSocket, + SIO_LOOPBACK_FAST_PATH, + &LoopbackOptionValue, + sizeof(LoopbackOptionValue), + NULL, + 0, + &OptionNumberOfBytesReturned, + 0, + 0); + } #endif m_Acceptor.listen(); + if (m_UseAlternateProtocolAcceptor) + { + m_AlternateProtocolAcceptor.listen(); + } ZEN_INFO("Started asio server at 'http://{}:{}'", BindAddress.is_loopback() ? "[::1]" : "*", EffectivePort); } @@ -644,22 +676,30 @@ struct HttpAcceptor void Start() { m_Acceptor.listen(); - InitAccept(); + InitAcceptInternal(m_Acceptor); + if (m_UseAlternateProtocolAcceptor) + { + m_AlternateProtocolAcceptor.listen(); + InitAcceptInternal(m_AlternateProtocolAcceptor); + } } void Stop() { m_IsStopped = true; } - void InitAccept() + int GetAcceptPort() { return m_Acceptor.local_endpoint().port(); } + +private: + void InitAcceptInternal(asio::ip::tcp::acceptor& Acceptor) { auto SocketPtr = std::make_unique<asio::ip::tcp::socket>(m_IoService); asio::ip::tcp::socket& SocketRef = *SocketPtr.get(); - m_Acceptor.async_accept(SocketRef, [this, Socket = std::move(SocketPtr)](const asio::error_code& Ec) mutable { + Acceptor.async_accept(SocketRef, [this, &Acceptor, Socket = std::move(SocketPtr)](const asio::error_code& Ec) mutable { if (Ec) { ZEN_WARN("asio async_accept, connection failed to '{}:{}' reason '{}'", - m_Acceptor.local_endpoint().address().to_string(), - m_Acceptor.local_endpoint().port(), + Acceptor.local_endpoint().address().to_string(), + Acceptor.local_endpoint().port(), Ec.message()); } else @@ -679,12 +719,12 @@ struct HttpAcceptor if (!m_IsStopped.load()) { - InitAccept(); + InitAcceptInternal(Acceptor); } else { std::error_code CloseEc; - m_Acceptor.close(CloseEc); + Acceptor.close(CloseEc); if (CloseEc) { ZEN_WARN("acceptor close ERROR, reason '{}'", CloseEc.message()); @@ -693,12 +733,11 @@ struct HttpAcceptor }); } - int GetAcceptPort() { return m_Acceptor.local_endpoint().port(); } - -private: HttpAsioServerImpl& m_Server; asio::io_service& m_IoService; asio::ip::tcp::acceptor m_Acceptor; + asio::ip::tcp::acceptor m_AlternateProtocolAcceptor; + bool m_UseAlternateProtocolAcceptor{false}; std::atomic<bool> m_IsStopped{false}; }; @@ -884,11 +923,17 @@ HttpAsioServerImpl::Start(uint16_t Port, bool ForceLooopback, int ThreadCount) void HttpAsioServerImpl::Stop() { - m_Acceptor->Stop(); + if (m_Acceptor) + { + m_Acceptor->Stop(); + } m_IoService.stop(); for (auto& Thread : m_ThreadPool) { - Thread.join(); + if (Thread.joinable()) + { + Thread.join(); + } } } |