aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/servers/httpasio.cpp
diff options
context:
space:
mode:
authorzousar <[email protected]>2024-02-06 10:02:45 -0500
committerGitHub <[email protected]>2024-02-06 16:02:45 +0100
commit3eecbb0c8a6e9bd906b4d313c3d690a2c4365ca4 (patch)
treee7b9bcae2e1e3df11b8789ebe987ec41fe7159c6 /src/zenhttp/servers/httpasio.cpp
parent5.4.1-pre1 (diff)
downloadzen-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/httpasio.cpp')
-rw-r--r--src/zenhttp/servers/httpasio.cpp73
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();
+ }
}
}