5#include <bitcoin-build-config.h>
30#include <condition_variable>
40#include <unordered_map>
86 if (subnet.Match(netaddr))
97 for (
const std::string& strAllow :
gArgs.
GetArgs(
"-rpcallowip")) {
99 if (!subnet.IsValid()) {
101 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)),
107 std::string strAllowed;
109 strAllowed += subnet.ToString() +
" ";
119 case GET:
return "GET";
120 case POST:
return "POST";
121 case HEAD:
return "HEAD";
122 case PUT:
return "PUT";
123 case UNKNOWN:
return "unknown";
132 LogDebug(
BCLog::HTTP,
"HTTP request from %s rejected: Client network is not allowed RPC access\n",
133 hreq->GetPeer().ToStringAddrPort());
141 hreq->GetPeer().ToStringAddrPort());
147 std::string strURI = hreq->GetURI();
150 std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
151 std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
152 for (; i != iend; ++i) {
155 match = (strURI == i->prefix);
157 match = strURI.starts_with(i->prefix);
159 path = strURI.substr(i->prefix.size());
167 LogWarning(
"Request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting");
172 auto item = [req = hreq, in_path = std::move(path), fn = i->handler]() {
175 fn(req.get(), in_path);
177 }
catch (
const std::exception& e) {
178 LogWarning(
"Unexpected error while processing request for '%s'. Error msg: '%s'", req->GetURI(), e.what());
181 LogWarning(
"Unknown error while processing request for '%s'", req->GetURI());
182 err_msg =
"unknown error";
185 req->WriteHeader(
"Connection",
"close");
191 Assume(hreq.use_count() == 1);
211 std::vector<std::pair<std::string, uint16_t>> endpoints;
219 endpoints.emplace_back(
"::1", http_port);
220 endpoints.emplace_back(
"127.0.0.1", http_port);
222 LogWarning(
"Option -rpcallowip was specified without -rpcbind; this doesn't usually make sense");
225 LogWarning(
"Option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect");
228 for (
const std::string& strRPCBind :
gArgs.
GetArgs(
"-rpcbind")) {
229 uint16_t port{http_port};
235 endpoints.emplace_back(host, port);
251 std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
252 std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
253 for (; i != iend; ++i)
254 if (i->prefix ==
prefix && i->exactMatch == exactMatch)
259 pathHandlers.erase(i);
278 std::vector<std::string_view>
ret;
281 ret.push_back(item.second);
289 m_headers.emplace_back(std::move(key), std::move(value));
294 auto moved = std::ranges::remove_if(
m_headers, [key] (
auto& pair) {
297 m_headers.erase(moved.begin(), moved.end());
307 const std::string_view& line = *maybe_line;
310 if (line.empty())
return true;
318 if (line.find_first_of(
"\r\n\0", 0, 3) != std::string_view::npos)
throw std::runtime_error(
"Header contains invalid character");
323 const size_t pos{line.find(
':')};
324 if (pos == std::string_view::npos)
throw std::runtime_error(
"HTTP header missing colon (:)");
328 std::string_view key = line.substr(0, pos);
329 if (key.find_first_of(
" \t\n\r\f\v") != std::string_view::npos)
throw std::runtime_error(
"Invalid header field-name contains whitespace");
331 std::string value =
util::TrimString(std::string_view(line).substr(pos + 1));
336 if (key.empty())
throw std::runtime_error(
"Empty HTTP header name");
338 Write(std::string(key), std::move(value));
347 for (
const auto& [key, value] :
m_headers) {
348 out += key +
": " + value +
"\r\n";
359 return strprintf(
"HTTP/%d.%d %d %s\r\n%s",
370 if (!maybe_line)
return false;
371 const std::string_view& request_line = *maybe_line;
380 if (request_line.find(
'\0') != std::string_view::npos)
throw std::runtime_error(
"Invalid request line contains NUL");
382 const std::vector<std::string_view> parts{Split<std::string_view>(request_line,
" ")};
383 if (parts.size() != 3)
throw std::runtime_error(
"HTTP request line malformed");
385 if (parts[0] ==
"GET") {
387 }
else if (parts[0] ==
"POST") {
389 }
else if (parts[0] ==
"HEAD") {
391 }
else if (parts[0] ==
"PUT") {
399 if (parts[2].rfind(
"HTTP/") != 0)
throw std::runtime_error(
"HTTP request line malformed");
403 const std::vector<std::string_view> version_parts{Split<std::string_view>(parts[2].substr(5),
".")};
404 if (version_parts.size() != 2)
throw std::runtime_error(
"HTTP request line malformed");
405 if (version_parts[0].size() != 1 || version_parts[1].size() != 1)
throw std::runtime_error(
"HTTP bad version");
406 auto major = ToIntegral<uint8_t>(version_parts[0]);
407 auto minor = ToIntegral<uint8_t>(version_parts[1]);
408 if (!major || !minor || major != 1 || minor > 9)
throw std::runtime_error(
"HTTP bad version");
424 if (transfer_encoding_header &&
ToLower(transfer_encoding_header.value()) ==
"chunked") {
430 if (!maybe_chunk_size)
return false;
434 std::string_view chunk_size_noext{maybe_chunk_size.value()};
435 const auto semicolon_pos = chunk_size_noext.find(
';');
436 if (semicolon_pos != chunk_size_noext.npos) {
437 chunk_size_noext.remove_suffix(chunk_size_noext.size() - semicolon_pos);
441 if (!chunk_size)
throw std::runtime_error(
"Cannot parse chunk length value");
448 if (*chunk_size == 0) {
457 throw std::runtime_error(
"HTTP chunked trailer exceeds size limit");
459 if (!maybe_trailer)
return false;
460 if (maybe_trailer->empty())
break;
484 if (!crlf.value().empty())
throw std::runtime_error(
"Improperly terminated chunk");
492 if (content_length_values.empty())
return true;
496 const auto& first_content_length_value{content_length_values[0]};
497 for (
size_t i = 1; i < content_length_values.size(); ++i) {
498 if (content_length_values[i] != first_content_length_value)
throw std::runtime_error(
"Differing Content-Length values");
501 const auto content_length{ToIntegral<uint64_t>(first_content_length_value)};
502 if (!content_length)
throw std::runtime_error(
"Cannot parse Content-Length value");
530 bool needs_body{status !=
HTTP_NO_CONTENT && (status < 100 || status >= 200)};
531 bool needs_content_length{
false};
533 bool keep_alive{
false};
541 if (connection_header &&
ToLower(connection_header.value()) ==
"keep-alive") {
546 if (needs_body) needs_content_length =
true;
552 const int64_t now_seconds{TicksSinceEpoch<std::chrono::seconds>(
NodeClock::now())};
556 if (needs_body) needs_content_length =
true;
563 if (needs_content_length) {
569 res.
m_headers.
Write(
"Content-Type",
"text/html; charset=ISO-8859-1");
573 if (connection_header &&
ToLower(connection_header.value()) ==
"close") {
581 m_client->m_keep_alive = keep_alive;
585 const auto headers_bytes{std::as_bytes(std::span{
headers})};
587 bool send_buffer_was_empty{
false};
591 send_buffer_was_empty =
m_client->m_send_buffer.empty();
592 m_client->m_send_buffer.insert(
m_client->m_send_buffer.end(), headers_bytes.begin(), headers_bytes.end());
597 m_client->m_send_buffer.insert(
m_client->m_send_buffer.end(), reply_body.begin(), reply_body.end());
602 "HTTPResponse (status code: %d size: %lld) added to send buffer for client %s (id=%llu)",
604 headers_bytes.size() + reply_body.size(),
612 if (send_buffer_was_empty) {
613 m_client->MaybeSendBytesFromBuffer();
639 size_t start = uri.find(
'?');
640 if (start == std::string::npos)
return std::nullopt;
641 size_t end = uri.find(
'#', start);
642 if (end == std::string::npos) {
645 const std::string_view query{uri.data() + start + 1, end - start - 1};
647 const std::vector<std::string_view> params{Split<std::string_view>(query,
"&")};
648 for (
const std::string_view& param : params) {
649 size_t delim = param.find(
'=');
650 if (key ==
UrlDecode(param.substr(0, delim))) {
651 if (delim == std::string::npos) {
654 return std::string(
UrlDecode(param.substr(delim + 1)));
664 return std::pair{found.has_value(), std::move(found).value_or(
"")};
675 sockaddr_storage storage;
676 auto sa =
reinterpret_cast<sockaddr*
>(&storage);
677 socklen_t len{
sizeof(storage)};
693 "Cannot set SO_REUSEADDR on %s listen socket: %s, continuing anyway",
704 "Cannot set IPV6_V6ONLY on %s listen socket: %s, continuing anyway",
710 int prot_level{PROTECTION_LEVEL_UNRESTRICTED};
711 if (sock->SetSockOpt(IPPROTO_IPV6,
712 IPV6_PROTECTION_LEVEL,
716 "Cannot set IPV6_PROTECTION_LEVEL on %s listen socket: %s, continuing anyway",
743 m_listen.emplace_back(std::move(sock));
770 Assume(std::ranges::any_of(
m_listen, [&](
const auto& sock) {
return sock.get() == &listen_sock; }));
772 sockaddr_storage storage;
773 socklen_t len{
sizeof(storage)};
774 auto sa =
reinterpret_cast<sockaddr*
>(&storage);
776 auto sock{listen_sock.
Accept(sa, &len)};
782 "Cannot accept new connection: %s",
793 "Unknown socket family");
801 return m_next_id.fetch_add(1, std::memory_order_relaxed);
806 if (!sock->IsSelectable()) {
808 "connection from %s dropped: non-selectable socket",
822 m_connected.push_back(std::make_shared<HTTPRemoteClient>(
id, addr, std::move(sock)));
827 "HTTP Connection accepted from %s (id=%llu)",
842 const std::shared_ptr<HTTPRemoteClient>& client{it->second};
852 if (!client->MaybeSendBytesFromBuffer()) {
857 if (recv_ready || err_ready) {
858 std::byte buf[0x10000];
861 client->m_sock_mutex,
862 return client->m_sock->Recv(buf,
sizeof(buf),
MSG_DONTWAIT);)};
869 "Permanent read error from %s (id=%llu): %s",
873 client->m_disconnect =
true;
875 }
else if (nrecv == 0) {
878 "Received EOF from %s (id=%llu)",
881 client->m_disconnect =
true;
884 client->m_idle_since = Now<SteadySeconds>();
887 client->m_connection_busy =
true;
890 client->m_recv_buffer.insert(
891 client->m_recv_buffer.end(),
911 const auto it = events_per_sock.find(sock);
912 if (it != events_per_sock.end() && it->second.occurred &
Sock::RecvEvent) {
934 std::shared_ptr<Sock> sock{
WITH_LOCK(http_client->m_sock_mutex,
return http_client->m_sock;)};
955 if (io_readiness.events_per_sock.empty() ||
957 !io_readiness.events_per_sock.begin()->first->WaitMany(
SELECT_TIMEOUT,
958 io_readiness.events_per_sock)) {
976 while (!client->m_recv_buffer.empty()) {
978 auto req = std::make_unique<HTTPRequest>(client);
981 if (!client->ReadRequest(*req))
break;
985 "HTTP request body too large from client %s (id=%llu): %s",
991 client->m_disconnect =
true;
993 }
catch (
const std::runtime_error& e) {
996 "Error reading HTTP request from client %s (id=%llu): %s",
1003 client->m_disconnect =
true;
1010 "Received a %s request for %s from %s (id=%llu)",
1017 client->m_req_queue.push_back(std::move(req));
1023 if (client->m_req_busy)
return;
1026 if (!client->m_req_queue.empty()) {
1028 client->m_req_busy =
true;
1029 m_request_dispatcher(std::move(client->m_req_queue.front()));
1030 client->m_req_queue.pop_front();
1036 const auto now{Now<SteadySeconds>()};
1046 !client->m_req_busy};
1050 if (client->m_disconnect || is_idle) {
1052 LogDebug(BCLog::HTTP,
1053 "HTTP client idle timeout %s (id=%llu)",
1062 if (client->m_connection_busy) {
1078 "Disconnecting HTTP client %s (id=%llu)",
1085 m_connected_size.fetch_sub(erased, std::memory_order_relaxed);
1089void HTTPServer::ClearConnectedClients()
1091 Assume(!m_thread_socket_handler.joinable());
1092 if (m_connected.empty())
return;
1093 LogWarning(
"Force-disconnecting %d HTTP client(s) that did not disconnect gracefully", m_connected.size());
1094 m_connected_size.fetch_sub(m_connected.size(), std::memory_order_relaxed);
1095 m_connected.clear();
1109 m_recv_buffer.erase(
1110 m_recv_buffer.begin(),
1116bool HTTPRemoteClient::MaybeSendBytesFromBuffer()
1120 if (!m_send_buffer.empty()) {
1135 bytes_sent = m_sock->Send(m_send_buffer.data(),
1136 m_send_buffer.size(),
1140 if (bytes_sent < 0) {
1145 m_send_ready =
true;
1146 m_connection_busy =
true;
1152 "Error sending HTTP response data to client %s (id=%llu): %s",
1156 m_send_ready =
false;
1157 m_disconnect =
true;
1165 Assume(
static_cast<size_t>(bytes_sent) <= m_send_buffer.size());
1166 m_send_buffer.erase(m_send_buffer.begin(),
1167 m_send_buffer.begin() + bytes_sent);
1171 "Sent %d bytes to client %s (id=%llu)",
1179 if (m_send_buffer.empty()) {
1180 m_send_ready =
false;
1181 m_connection_busy =
false;
1184 if (!m_keep_alive) {
1185 m_disconnect =
true;
1191 m_send_ready =
true;
1192 m_connection_busy =
true;
1196 m_idle_since = Now<SteadySeconds>();
1214 std::vector<std::pair<std::string, uint16_t>> endpoints{
GetBindAddresses()};
1215 bool bind_success{
false};
1216 for (
const auto& [address_string, port] : endpoints) {
1217 LogInfo(
"Binding RPC on address %s port %i", address_string, port);
1218 const std::optional<CService> addr{
Lookup(address_string, port,
false)};
1220 if (addr->IsBindAny()) {
1221 LogWarning(
"The RPC server is not safe to expose to untrusted networks such as the public internet");
1223 auto result{
g_http_server->BindAndStartListening(addr.value())};
1225 LogWarning(
"Binding RPC on address %s failed: %s", addr->ToStringAddrPort(), result.error());
1227 bind_success =
true;
1230 LogWarning(
"Could not bind RPC on address %s port %i: Address lookup failed.", address_string, port);
1234 if (!bind_success) {
1235 LogError(
"Unable to bind any endpoint for RPC server");
1250 LogInfo(
"Starting HTTP server with %d worker threads", rpcThreads);
1285 LogWarning(
"Timeout waiting for HTTP clients to disconnect gracefully, continuing shutdown");
1288 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.
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)
HTTPHeaders m_response_headers
Response headers may be set in advance before response body is known.
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::shared_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={})
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.
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.
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.
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...
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 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 bool InitHTTPAllowList()
Initialize ACL list for HTTP server.
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< CSubNet > rpc_allow_subnets
List of subnets to allow RPC connections from.
static bool ClientAllowed(const CNetAddr &netaddr)
Check if a network address is allowed to access the HTTP server.
static std::vector< HTTPPathHandler > pathHandlers GUARDED_BY(g_httppathhandlers_mutex)
static GlobalMutex g_httppathhandlers_mutex
Handlers for (sub)paths.
static const int DEFAULT_HTTP_SERVER_TIMEOUT
static const int DEFAULT_HTTP_WORKQUEUE
The default value for -rpcworkqueue.
static const int DEFAULT_HTTP_THREADS
The default value for -rpcthreads.
std::function< void(http_bitcoin::HTTPRequest *req, const std::string &)> HTTPRequestHandler
Handler for requests to a certain HTTP path.
CClientUIInterface uiInterface
#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.
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...
#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....