mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 21:52:53 +02:00
http: Introduce HTTPServer class and implement binding to listening socket
Introduce a new low-level socket managing class `HTTPServer`. BindAndStartListening() was copied from CConnMan's BindListenPort() in net.cpp and modernized. Unit-test it with a new class `SocketTestingSetup` which mocks `CreateSock()` and will enable mock client I/O in future commits. Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
|
||||
#include <httpserver.h>
|
||||
|
||||
#include <chainparamsbase.h>
|
||||
@@ -15,6 +17,7 @@
|
||||
#include <sync.h>
|
||||
#include <util/check.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/sock.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/threadnames.h>
|
||||
#include <util/threadpool.h>
|
||||
@@ -46,6 +49,9 @@
|
||||
|
||||
#include <support/events.h>
|
||||
|
||||
//! Explicit alias for setting socket option methods.
|
||||
static constexpr int SOCKET_OPTION_TRUE{1};
|
||||
|
||||
using common::InvalidPortErrMsg;
|
||||
using http_libevent::HTTPRequest;
|
||||
|
||||
@@ -889,4 +895,85 @@ bool HTTPRequest::LoadBody(LineReader& reader)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
util::Expected<void, std::string> HTTPServer::BindAndStartListening(const CService& to)
|
||||
{
|
||||
// Create socket for listening for incoming connections
|
||||
sockaddr_storage storage;
|
||||
auto sa = reinterpret_cast<sockaddr*>(&storage);
|
||||
socklen_t len{sizeof(storage)};
|
||||
if (!to.GetSockAddr(sa, &len)) {
|
||||
return util::Unexpected{strprintf("Bind address family for %s not supported", to.ToStringAddrPort())};
|
||||
}
|
||||
|
||||
std::unique_ptr<Sock> sock{CreateSock(to.GetSAFamily(), SOCK_STREAM, IPPROTO_TCP)};
|
||||
if (!sock) {
|
||||
return util::Unexpected{strprintf("Cannot create %s listen socket: %s",
|
||||
to.ToStringAddrPort(),
|
||||
NetworkErrorString(WSAGetLastError()))};
|
||||
}
|
||||
|
||||
// Allow binding if the port is still in TIME_WAIT state after
|
||||
// the program was closed and restarted.
|
||||
if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {
|
||||
LogDebug(BCLog::HTTP,
|
||||
"Cannot set SO_REUSEADDR on %s listen socket: %s, continuing anyway",
|
||||
to.ToStringAddrPort(),
|
||||
NetworkErrorString(WSAGetLastError()));
|
||||
}
|
||||
|
||||
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
|
||||
// and enable it by default or not. Try to enable it, if possible.
|
||||
if (to.IsIPv6()) {
|
||||
#ifdef IPV6_V6ONLY
|
||||
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, &SOCKET_OPTION_TRUE, sizeof(SOCKET_OPTION_TRUE)) == SOCKET_ERROR) {
|
||||
LogDebug(BCLog::HTTP,
|
||||
"Cannot set IPV6_V6ONLY on %s listen socket: %s, continuing anyway",
|
||||
to.ToStringAddrPort(),
|
||||
NetworkErrorString(WSAGetLastError()));
|
||||
}
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
int prot_level{PROTECTION_LEVEL_UNRESTRICTED};
|
||||
if (sock->SetSockOpt(IPPROTO_IPV6,
|
||||
IPV6_PROTECTION_LEVEL,
|
||||
&prot_level,
|
||||
sizeof(prot_level)) == SOCKET_ERROR) {
|
||||
LogDebug(BCLog::HTTP,
|
||||
"Cannot set IPV6_PROTECTION_LEVEL on %s listen socket: %s, continuing anyway",
|
||||
to.ToStringAddrPort(),
|
||||
NetworkErrorString(WSAGetLastError()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sock->Bind(sa, len) == SOCKET_ERROR) {
|
||||
const int err{WSAGetLastError()};
|
||||
if (err == WSAEADDRINUSE) {
|
||||
return util::Unexpected{strprintf("Unable to bind to %s on this computer. %s is probably already running.",
|
||||
to.ToStringAddrPort(),
|
||||
CLIENT_NAME)};
|
||||
} else {
|
||||
return util::Unexpected{strprintf("Unable to bind to %s on this computer (bind returned error %s)",
|
||||
to.ToStringAddrPort(),
|
||||
NetworkErrorString(err))};
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for incoming connections
|
||||
if (sock->Listen(SOMAXCONN) == SOCKET_ERROR) {
|
||||
return util::Unexpected{strprintf("Cannot listen on %s: %s",
|
||||
to.ToStringAddrPort(),
|
||||
NetworkErrorString(WSAGetLastError()))};
|
||||
}
|
||||
|
||||
m_listen.emplace_back(std::move(sock));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void HTTPServer::StopListening()
|
||||
{
|
||||
m_listen.clear();
|
||||
}
|
||||
} // namespace http_bitcoin
|
||||
|
||||
Reference in New Issue
Block a user