5#include <bitcoin-build-config.h>
30#include <condition_variable>
39#include <unordered_map>
84 if (subnet.Match(netaddr))
99 for (
const std::string& strAllow :
gArgs.
GetArgs(
"-rpcallowip")) {
101 if (!subnet.IsValid()) {
103 Untranslated(
strprintf(
"Invalid -rpcallowip subnet specification: %s. Valid values are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0), a network/CIDR (e.g. 1.2.3.4/24), all ipv4 (0.0.0.0/0), or all ipv6 (::/0). RFC4193 is allowed only if -cjdnsreachable=0.", strAllow)),
109 std::string strAllowed;
111 strAllowed += subnet.ToString() +
" ";
122 case GET:
return "GET";
123 case POST:
return "POST";
124 case HEAD:
return "HEAD";
125 case PUT:
return "PUT";
126 case UNKNOWN:
return "unknown";
142 hreq->GetPeer().ToStringAddrPort());
148 std::string strURI = hreq->GetURI();
151 std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
152 std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
153 for (; i != iend; ++i) {
156 match = (strURI == i->prefix);
158 match = strURI.starts_with(i->prefix);
160 path = strURI.substr(i->prefix.size());
168 LogWarning(
"Request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting");
173 auto item = [req = hreq, in_path = std::move(path), fn = i->handler]() {
176 fn(req.get(), in_path);
178 }
catch (
const std::exception& e) {
179 LogWarning(
"Unexpected error while processing request for '%s'. Error msg: '%s'", req->
GetURI(), e.what());
182 LogWarning(
"Unknown error while processing request for '%s'", req->
GetURI());
183 err_msg =
"unknown error";
192 Assume(hreq.use_count() == 1);
212 std::vector<std::pair<std::string, uint16_t>> endpoints;
220 endpoints.emplace_back(
"::1", http_port);
221 endpoints.emplace_back(
"127.0.0.1", http_port);
223 LogWarning(
"Option -rpcallowip was specified without -rpcbind; this doesn't usually make sense");
226 LogWarning(
"Option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect");
229 for (
const std::string& strRPCBind :
gArgs.
GetArgs(
"-rpcbind")) {
230 uint16_t port{http_port};
236 endpoints.emplace_back(host, port);
252 std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
253 std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
254 for (; i != iend; ++i)
255 if (i->prefix ==
prefix && i->exactMatch == exactMatch)
260 pathHandlers.erase(i);
279 std::vector<std::string_view>
ret;
282 ret.push_back(item.second);
290 m_headers.emplace_back(std::move(key), std::move(value));
295 auto moved = std::ranges::remove_if(
m_headers, [key] (
auto& pair) {
298 m_headers.erase(moved.begin(), moved.end());
309 const std::string_view& line = *maybe_line;
324 if (line.find_first_of(
"\r\n\0", 0, 3) != std::string_view::npos)
throw std::runtime_error(
"Header contains invalid character");
329 const size_t pos{line.find(
':')};
330 if (pos == std::string_view::npos)
throw std::runtime_error(
"HTTP header missing colon (:)");
334 std::string_view key = line.substr(0, pos);
335 if (key.find_first_of(
" \t\n\r\f\v") != std::string_view::npos)
throw std::runtime_error(
"Invalid header field-name contains whitespace");
337 std::string value =
util::TrimString(std::string_view(line).substr(pos + 1));
342 if (key.empty())
throw std::runtime_error(
"Empty HTTP header name");
345 Write(std::string(key), std::move(value));
360 for (
const auto& [key, value] :
m_headers) {
361 out += key +
": " + value +
"\r\n";
372 return strprintf(
"HTTP/%d.%d %d %s\r\n%s",
383 if (!maybe_line)
return false;
384 const std::string_view& request_line = *maybe_line;
393 if (request_line.find(
'\0') != std::string_view::npos)
throw std::runtime_error(
"Invalid request line contains NUL");
395 const std::vector<std::string_view> parts{Split<std::string_view>(request_line,
" ")};
396 if (parts.size() != 3)
throw std::runtime_error(
"HTTP request line malformed");
398 if (parts[0] ==
"GET") {
400 }
else if (parts[0] ==
"POST") {
402 }
else if (parts[0] ==
"HEAD") {
404 }
else if (parts[0] ==
"PUT") {
412 if (parts[2].rfind(
"HTTP/") != 0)
throw std::runtime_error(
"HTTP request line malformed");
416 const std::vector<std::string_view> version_parts{Split<std::string_view>(parts[2].substr(5),
".")};
417 if (version_parts.size() != 2)
throw std::runtime_error(
"HTTP request line malformed");
418 if (version_parts[0].size() != 1 || version_parts[1].size() != 1)
throw std::runtime_error(
"HTTP bad version");
419 auto major = ToIntegral<uint8_t>(version_parts[0]);
420 auto minor = ToIntegral<uint8_t>(version_parts[1]);
421 if (!major || !minor || major != 1 || minor > 9)
throw std::runtime_error(
"HTTP bad version");
437 if (transfer_encoding_header &&
ToLower(transfer_encoding_header.value()) ==
"chunked") {
444 if (!maybe_chunk_size)
return false;
448 std::string_view chunk_size_noext{maybe_chunk_size.value()};
449 const auto semicolon_pos = chunk_size_noext.find(
';');
450 if (semicolon_pos != chunk_size_noext.npos) {
451 chunk_size_noext.remove_suffix(chunk_size_noext.size() - semicolon_pos);
455 if (!
m_chunk_size)
throw std::runtime_error(
"Cannot parse chunk length value");
481 const uint64_t buffer_has{std::min(chunk_need,
static_cast<uint64_t
>(
reader.
Remaining()))};
498 if (!crlf.value().empty())
throw std::runtime_error(
"Improperly terminated chunk");
511 if (content_length_values.empty())
return true;
515 const auto& first_content_length_value{content_length_values[0]};
516 for (
size_t i = 1; i < content_length_values.size(); ++i) {
517 if (content_length_values[i] != first_content_length_value)
throw std::runtime_error(
"Differing Content-Length values");
520 const auto content_length{ToIntegral<uint64_t>(first_content_length_value)};
521 if (!content_length)
throw std::runtime_error(
"Cannot parse Content-Length value");
527 const uint64_t body_need{*content_length -
m_body.size()};
528 const uint64_t buffer_has{std::min(body_need,
static_cast<uint64_t
>(
reader.
Remaining()))};
533 return m_body.size() == *content_length;
552 bool needs_body{status !=
HTTP_NO_CONTENT && (status < 100 || status >= 200)};
553 bool needs_content_length{
false};
555 bool keep_alive{
false};
563 if (connection_header &&
ToLower(connection_header.value()) ==
"keep-alive") {
568 if (needs_body) needs_content_length =
true;
574 const int64_t now_seconds{TicksSinceEpoch<std::chrono::seconds>(
NodeClock::now())};
578 if (needs_body) needs_content_length =
true;
585 if (needs_content_length) {
591 res.
m_headers.
Write(
"Content-Type",
"text/html; charset=ISO-8859-1");
595 if (connection_header &&
ToLower(connection_header.value()) ==
"close") {
606 client->m_keep_alive = keep_alive;
610 const auto headers_bytes{std::as_bytes(std::span{
headers})};
612 bool send_buffer_was_empty{
false};
616 send_buffer_was_empty =
client->m_send_buffer.empty();
617 client->m_send_buffer.insert(
client->m_send_buffer.end(), headers_bytes.begin(), headers_bytes.end());
622 client->m_send_buffer.insert(
client->m_send_buffer.end(), reply_body.begin(), reply_body.end());
632 if (!send_buffer_was_empty)
client->m_send_ready =
true;
637 "HTTPResponse (status code: %d size: %lld) added to send buffer for client %s (id=%llu)",
639 headers_bytes.size() + reply_body.size(),
647 if (send_buffer_was_empty) {
648 client->MaybeSendBytesFromBuffer();
652 client->m_req_busy =
false;
657 if (std::shared_ptr c{
m_client.lock()}) {
674 size_t start = uri.find(
'?');
675 if (start == std::string::npos)
return std::nullopt;
676 size_t end = uri.find(
'#', start);
677 if (end == std::string::npos) {
680 const std::string_view query{uri.data() + start + 1, end - start - 1};
682 const std::vector<std::string_view> params{Split<std::string_view>(query,
"&")};
683 for (
const std::string_view& param : params) {
684 size_t delim = param.find(
'=');
685 if (key ==
UrlDecode(param.substr(0, delim))) {
686 if (delim == std::string::npos) {
689 return std::string(
UrlDecode(param.substr(delim + 1)));
699 return std::pair{found.has_value(), std::move(found).value_or(
"")};
710 sockaddr_storage storage;
711 auto sa =
reinterpret_cast<sockaddr*
>(&storage);
712 socklen_t len{
sizeof(storage)};
728 "Cannot set SO_REUSEADDR on %s listen socket: %s, continuing anyway",
739 "Cannot set IPV6_V6ONLY on %s listen socket: %s, continuing anyway",
745 int prot_level{PROTECTION_LEVEL_UNRESTRICTED};
746 if (sock->SetSockOpt(IPPROTO_IPV6,
747 IPV6_PROTECTION_LEVEL,
751 "Cannot set IPV6_PROTECTION_LEVEL on %s listen socket: %s, continuing anyway",
778 m_listen.emplace_back(std::move(sock));
810 Assume(std::ranges::any_of(
m_listen, [&](
const auto& sock) {
return sock.get() == &listen_sock; }));
812 sockaddr_storage storage;
813 socklen_t len{
sizeof(storage)};
814 auto sa =
reinterpret_cast<sockaddr*
>(&storage);
816 auto sock{listen_sock.
Accept(sa, &len)};
822 "Cannot accept new connection: %s",
831 "Unknown socket family");
836 LogDebug(
BCLog::HTTP,
"Connection from %s rejected: Client network is not allowed HTTP access\n",
847 return m_next_id.fetch_add(1, std::memory_order_relaxed);
852 if (!sock->IsSelectable()) {
854 "connection from %s dropped: non-selectable socket",
868 m_connected.push_back(std::make_shared<HTTPRemoteClient>(
id, addr, std::move(sock)));
873 "HTTP Connection accepted from %s (id=%llu)",
888 const std::shared_ptr<HTTPRemoteClient>&
client{it->second};
898 if (!
client->MaybeSendBytesFromBuffer()) {
903 if (recv_ready || err_ready) {
915 "Permanent read error from %s (id=%llu): %s",
919 client->m_disconnect =
true;
921 }
else if (nrecv == 0) {
924 "Received EOF from %s (id=%llu)",
927 client->m_disconnect =
true;
930 client->m_idle_since = Now<SteadySeconds>();
933 client->m_connection_busy =
true;
936 client->m_recv_buffer.insert(
937 client->m_recv_buffer.end(),
957 const auto it = events_per_sock.find(sock);
958 if (it != events_per_sock.end() && it->second.occurred &
Sock::RecvEvent) {
980 std::shared_ptr<Sock> sock{
WITH_LOCK(http_client->m_sock_mutex,
return http_client->m_sock;)};
988 const bool send_ready{
WITH_LOCK(http_client->m_send_mutex,
return http_client->m_send_ready;)};
1006 if (io_readiness.events_per_sock.empty() ||
1008 !io_readiness.events_per_sock.begin()->first->WaitMany(
SELECT_TIMEOUT,
1009 io_readiness.events_per_sock)) {
1029 if (
client->m_req_busy)
return;
1041 "HTTP request body too large from client %s (id=%llu): %s",
1047 client->m_disconnect =
true;
1049 }
catch (
const std::runtime_error& e) {
1052 "Error reading HTTP request from client %s (id=%llu): %s",
1059 client->m_disconnect =
true;
1067 "Received a %s request for %s from %s (id=%llu)",
1074 client->m_req_busy =
true;
1075 m_request_dispatcher(std::move(
client->m_req));
1081 const auto now{Now<SteadySeconds>()};
1096 if (
client->m_disconnect || is_idle) {
1098 LogDebug(BCLog::HTTP,
1099 "HTTP client idle timeout %s (id=%llu)",
1108 if (
client->m_connection_busy) {
1124 "Disconnecting HTTP client %s (id=%llu)",
1131 m_connected_size.fetch_sub(erased, std::memory_order_relaxed);
1135void HTTPServer::ClearConnectedClients()
1137 Assume(!m_thread_socket_handler.joinable());
1138 if (m_connected.empty())
return;
1139 LogWarning(
"Force-disconnecting %d HTTP client(s) that did not disconnect gracefully", m_connected.size());
1140 m_connected_size.fetch_sub(m_connected.size(), std::memory_order_relaxed);
1141 m_connected.clear();
1146 if (m_recv_buffer.empty())
return;
1152 case HTTPRequest::State::Init:
1154 req.
SetState(HTTPRequest::State::NeedsHeaders);
1157 case HTTPRequest::State::NeedsHeaders:
1159 req.
SetState(HTTPRequest::State::NeedsBody);
1162 case HTTPRequest::State::NeedsBody:
1164 req.
SetState(HTTPRequest::State::Complete);
1167 case HTTPRequest::State::Complete:
1170 case HTTPRequest::State::Error:
1175 req.
SetState(HTTPRequest::State::Error);
1177 m_recv_buffer.clear();
1182 m_recv_buffer.erase(
1183 m_recv_buffer.begin(),
1187bool HTTPRemoteClient::MaybeSendBytesFromBuffer()
1191 if (!m_send_buffer.empty()) {
1206 bytes_sent = m_sock->Send(m_send_buffer.data(),
1207 m_send_buffer.size(),
1211 if (bytes_sent < 0) {
1216 m_send_ready =
true;
1217 m_connection_busy =
true;
1223 "Error sending HTTP response data to client %s (id=%llu): %s",
1227 m_send_ready =
false;
1228 m_disconnect =
true;
1236 Assume(
static_cast<size_t>(bytes_sent) <= m_send_buffer.size());
1237 m_send_buffer.erase(m_send_buffer.begin(),
1238 m_send_buffer.begin() + bytes_sent);
1242 "Sent %d bytes to client %s (id=%llu)",
1250 if (m_send_buffer.empty()) {
1251 m_send_ready =
false;
1252 m_connection_busy =
false;
1255 if (!m_keep_alive) {
1256 m_disconnect =
true;
1262 m_send_ready =
true;
1263 m_connection_busy =
true;
1267 m_idle_since = Now<SteadySeconds>();
1285 std::vector<std::pair<std::string, uint16_t>> endpoints{
GetBindAddresses()};
1286 bool bind_success{
false};
1287 for (
const auto& [address_string, port] : endpoints) {
1288 LogInfo(
"Binding RPC on address %s port %i", address_string, port);
1289 const std::optional<CService> addr{
Lookup(address_string, port,
false)};
1291 if (addr->IsBindAny()) {
1292 LogWarning(
"The RPC server is not safe to expose to untrusted networks such as the public internet");
1294 auto result{
g_http_server->BindAndStartListening(addr.value())};
1296 LogWarning(
"Binding RPC on address %s failed: %s", addr->ToStringAddrPort(), result.error());
1298 bind_success =
true;
1301 LogWarning(
"Could not bind RPC on address %s port %i: Address lookup failed.", address_string, port);
1305 if (!bind_success) {
1306 LogError(
"Unable to bind any endpoint for RPC server");
1321 LogInfo(
"Starting HTTP server with %d worker threads", rpcThreads);
1356 LogWarning(
"Timeout waiting for HTTP clients to disconnect gracefully, continuing shutdown");
1359 std::this_thread::sleep_for(50ms);
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
#define Assume(val)
Assume is the identity function.
std::vector< std::string > GetArgs(const std::string &strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return a vector of strings of the given argument.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return string argument or default value.
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
btcsignals::signal< void(const bilingual_str &message, unsigned int style)> ThreadSafeMessageBox
Show message box.
prevector< ADDR_IPV6_SIZE, uint8_t > m_addr
Raw representation of the network address.
A combination of a network address (CNetAddr) and a (TCP) port.
bool SetSockAddr(const struct sockaddr *paddr, socklen_t addrlen)
Set CService from a network sockaddr.
sa_family_t GetSAFamily() const
Get the address family.
bool GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const
Obtain the IPv4/6 socket address this represents.
std::string ToStringAddrPort() const
virtual bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut)
Sleep for the given duration.
Different type to mark Mutex at global scope.
RAII helper class that manages a socket and closes it automatically when it goes out of scope.
static constexpr Event RecvEvent
If passed to Wait(), then it will wait for readiness to read from the socket.
virtual std::unique_ptr< Sock > Accept(sockaddr *addr, socklen_t *addr_len) const
accept(2) wrapper.
static constexpr Event SendEvent
If passed to Wait(), then it will wait for readiness to send to the socket.
static constexpr Event ErrorEvent
Ignored if passed to Wait(), but could be set in the occurred events if an exceptional condition has ...
std::unordered_map< std::shared_ptr< const Sock >, Events, HashSharedPtrSock, EqualSharedPtrSock > EventsPerSock
On which socket to wait for what events in WaitMany().
Fixed-size thread pool for running arbitrary tasks concurrently.
void Start(int num_workers) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Start worker threads.
util::Expected< Future< F >, SubmitError > Submit(F &&fn) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Enqueues a new task for asynchronous execution.
void Stop() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Stop all worker threads and wait for them to exit.
void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Stop accepting new tasks and begin asynchronous shutdown.
size_t WorkQueueSize() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
std::string GetURI() const
HTTPHeaders m_response_headers
Response headers may be set in advance before response body is known.
std::optional< uint64_t > m_chunk_size
HTTPRequestMethod m_method
std::optional< std::string > GetQueryParameter(std::string_view key) const
bool LoadHeaders(LineReader &reader)
std::pair< bool, std::string > GetHeader(std::string_view hdr) const
void WriteHeader(std::string &&hdr, std::string &&value)
std::weak_ptr< HTTPRemoteClient > m_client
Pointer to the client that made the request so we know who to respond to.
bool LoadControlData(LineReader &reader)
Methods that attempt to parse HTTP request fields line-by-line from a receive buffer.
void WriteReply(HTTPStatusCode status, std::span< const std::byte > reply_body={})
void SetState(State state)
bool LoadBody(LineReader &reader)
std::string StringifyHeaders() const
void NewSockAccepted(std::unique_ptr< Sock > &&sock, const CService &addr)
After a new socket with a client has been created, configure its flags, make a new HTTPRemoteClient a...
void ThreadSocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_request_dispatcher_mutex)
Check connected and listening sockets for IO readiness and process them accordingly.
bool InitHTTPAllowList()
Parse the user's -rpcallowip settings and populate m_allow_subnets.
std::vector< std::shared_ptr< Sock > > m_listen
List of listening sockets.
CThreadInterrupt m_interrupt_net
This is signaled when network activity should cease.
std::atomic_bool m_disconnect_all_clients
Flag used during shutdown.
void DisconnectClients()
Close underlying socket connections for flagged clients by removing their shared pointer from m_conne...
void StopListening()
Stop listening by closing all listening sockets.
void SocketHandlerListening(const Sock::EventsPerSock &events_per_sock)
Accept incoming connections, one from each read-ready listening socket.
std::vector< std::shared_ptr< HTTPRemoteClient > > m_connected
List of HTTPRemoteClients with connected sockets.
std::vector< CSubNet > m_allow_subnets
List of subnets to allow HTTP connections from.
IOReadiness GenerateWaitSockets() const
Generate a collection of sockets to check for IO readiness.
std::atomic< Id > m_next_id
The id to assign to the next created connection.
void StartSocketsThreads()
Start the necessary threads for sockets IO.
void MaybeDispatchRequestsFromClient(const std::shared_ptr< HTTPRemoteClient > &client) const EXCLUSIVE_LOCKS_REQUIRED(!m_request_dispatcher_mutex)
Try to read HTTPRequests from a client's receive buffer.
void JoinSocketsThreads()
Join (wait for) the threads started by StartSocketsThreads() to exit.
bool ClientAllowed(const CNetAddr &netaddr) const
Check an incoming connection's source IP against the allow list.
std::chrono::seconds m_rpcservertimeout
Idle timeout after which clients are disconnected.
std::thread m_thread_socket_handler
Thread that sends to and receives from sockets and accepts connections.
std::unique_ptr< Sock > AcceptConnection(const Sock &listen_sock, CService &addr)
Accept a connection.
std::atomic< size_t > m_connected_size
The number of connected sockets.
util::Expected< void, std::string > BindAndStartListening(const CService &to)
Bind to a new address:port, start listening and add the listen socket to m_listen.
Id GetNewId()
Generate an id for a newly created connection.
Mutex m_request_dispatcher_mutex
std::atomic_bool m_stop_accepting
Flag used during shutdown to stop accepting new connections.
void SocketHandlerConnected(const IOReadiness &io_readiness) const EXCLUSIVE_LOCKS_REQUIRED(!m_request_dispatcher_mutex)
Do the read/write for connected sockets that are ready for IO.
uint64_t Id
Each connection is assigned an unique id of this type.
The util::Expected class provides a standard way for low-level functions to return either error value...
size_t Consumed() const
Returns number of bytes already read from buffer.
std::optional< std::string_view > ReadLine() LIFETIMEBOUND
Returns a string from current iterator position up to (but not including) next and advances iterator...
size_t Remaining() const
Returns remaining size of bytes in buffer.
std::string_view ReadLength(size_t len) LIFETIMEBOUND
Returns string from current iterator position of specified length if possible and advances iterator o...
The util::Unexpected class represents an unexpected value stored in util::Expected.
#define WSAGetLastError()
static std::vector< std::pair< std::string, uint16_t > > GetBindAddresses()
static void WriteNoStoreErrorReply(HTTPRequest &req, HTTPStatusCode status, std::string_view reply={})
static ThreadPool g_threadpool_http("http")
Http thread pool - future: encapsulate in HttpContext
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
std::string_view RequestMethodString(HTTPRequestMethod m)
HTTP request method as string - use for logging only.
static void MaybeDispatchRequestToWorker(std::shared_ptr< HTTPRequest > hreq)
static constexpr auto SELECT_TIMEOUT
The set of sockets cannot be modified while waiting, so the sleep time needs to be small to avoid new...
static constexpr int SOCKET_OPTION_TRUE
Explicit alias for setting socket option methods.
static int g_max_queue_depth
static std::unique_ptr< http_bitcoin::HTTPServer > g_http_server
HTTP module state.
static void RejectRequest(std::unique_ptr< http_bitcoin::HTTPRequest > hreq)
static std::vector< HTTPPathHandler > pathHandlers GUARDED_BY(g_httppathhandlers_mutex)
static GlobalMutex g_httppathhandlers_mutex
Handlers for (sub)paths.
constexpr int DEFAULT_HTTP_SERVER_TIMEOUT
constexpr int DEFAULT_HTTP_THREADS
The default value for -rpcthreads.
constexpr int DEFAULT_HTTP_WORKQUEUE
The default value for -rpcworkqueue.
std::function< void(http_bitcoin::HTTPRequest *req, const std::string &)> HTTPRequestHandler
Handler for requests to a certain HTTP path.
CClientUIInterface uiInterface
std::unique_ptr< ProxyClient< messages::FooInterface > > client
std::thread thread
Thread variable should be after other struct members so the thread does not start until the other mem...
#define LogDebug(category,...)
is a home for simple string functions returning descriptive messages that are used in RPC and GUI int...
bilingual_str InvalidPortErrMsg(const std::string &optname, const std::string &invalid_value)
constexpr size_t MIN_REQUEST_LINE_LENGTH
Shortest valid request line, used by libevent in evhttp_parse_request_line()
void StartHTTPServer()
Start HTTP server.
std::optional< std::string > GetQueryParameterFromUri(const std::string_view uri, const std::string_view key)
constexpr uint64_t MAX_BODY_SIZE
Maximum size of an HTTP request body.
constexpr size_t MAX_HEADERS_SIZE
Maximum size of each headers line in an HTTP request, also the maximum size of all headers total.
void StopHTTPServer()
Stop HTTP server.
void InterruptHTTPServer()
Interrupt HTTP server threads.
bool InitHTTPServer()
Initialize HTTP server.
std::string_view TrimStringView(std::string_view str, std::string_view pattern=" \f\n\r\t\v")
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
std::string TrimString(std::string_view str, std::string_view pattern=" \f\n\r\t\v")
std::vector< T > Split(const std::span< const char > &sp, std::string_view separators, bool include_sep=false)
Split a string on any char found in separators, returning a vector.
CSubNet LookupSubNet(const std::string &subnet_str)
Parse and resolve a specified subnet string into the appropriate internal representation.
std::vector< CNetAddr > LookupHost(const std::string &name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
Resolve a host string to its corresponding network addresses.
std::vector< CService > Lookup(const std::string &name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
Resolve a service string to its corresponding service.
std::function< std::unique_ptr< Sock >(int, int, int)> CreateSock
Socket factory.
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
std::string_view HTTPStatusReasonString(HTTPStatusCode code)
Mapping of HTTP status codes to short string explanation.
HTTPStatusCode
HTTP status codes.
@ HTTP_SERVICE_UNAVAILABLE
@ HTTP_INTERNAL_SERVER_ERROR
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
bool IOErrorIsPermanent(int err)
HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler)
HTTPRequestHandler handler
static time_point now() noexcept
Return current system time or mocked time, if set.
Auxiliary requested/occurred events to wait for in WaitMany().
Thrown when a request body exceeds MAX_BODY_SIZE (or will exceed, in chunked transfer) so the server ...
Info about which socket has which event ready and a reverse map back to the HTTPRemoteClient that own...
std::unordered_map< Sock::EventsPerSock::key_type, std::shared_ptr< HTTPRemoteClient >, Sock::HashSharedPtrSock, Sock::EqualSharedPtrSock > httpclients_per_sock
Map of socket -> HTTPRemoteClient.
Sock::EventsPerSock events_per_sock
Map of socket -> socket events.
uint8_t major
Default HTTP protocol version 1.1 is used by error responses when a request is unreadable.
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
constexpr std::string_view SubmitErrorString(const ThreadPool::SubmitError err) noexcept
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
std::string UrlDecode(std::string_view url_encoded)
bool CaseInsensitiveEqual(std::string_view s1, std::string_view s2)
Locale-independent, ASCII-only comparator.
bool SplitHostPort(std::string_view in, uint16_t &portOut, std::string &hostOut)
Splits socket address string into host string and port value.
std::string ToLower(std::string_view str)
Returns the lowercase equivalent of the given string.
std::string FormatRFC1123DateTime(int64_t time)
RFC1123 formatting https://www.rfc-editor.org/rfc/rfc1123#section-5.2.14 Used in HTTP/1....