5#include <bitcoin-build-config.h>
30#include <condition_variable>
40#include <unordered_map>
85 if (subnet.Match(netaddr))
100 for (
const std::string& strAllow :
gArgs.
GetArgs(
"-rpcallowip")) {
102 if (!subnet.IsValid()) {
104 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)),
110 std::string strAllowed;
112 strAllowed += subnet.ToString() +
" ";
123 case GET:
return "GET";
124 case POST:
return "POST";
125 case HEAD:
return "HEAD";
126 case PUT:
return "PUT";
127 case UNKNOWN:
return "unknown";
143 hreq->GetPeer().ToStringAddrPort());
149 std::string strURI = hreq->GetURI();
152 std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
153 std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
154 for (; i != iend; ++i) {
157 match = (strURI == i->prefix);
159 match = strURI.starts_with(i->prefix);
161 path = strURI.substr(i->prefix.size());
169 LogWarning(
"Request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting");
174 auto item = [req = hreq, in_path = std::move(path), fn = i->handler]() {
177 fn(req.get(), in_path);
179 }
catch (
const std::exception& e) {
180 LogWarning(
"Unexpected error while processing request for '%s'. Error msg: '%s'", req->
GetURI(), e.what());
183 LogWarning(
"Unknown error while processing request for '%s'", req->
GetURI());
184 err_msg =
"unknown error";
193 Assume(hreq.use_count() == 1);
213 std::vector<std::pair<std::string, uint16_t>> endpoints;
221 endpoints.emplace_back(
"::1", http_port);
222 endpoints.emplace_back(
"127.0.0.1", http_port);
224 LogWarning(
"Option -rpcallowip was specified without -rpcbind; this doesn't usually make sense");
227 LogWarning(
"Option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect");
230 for (
const std::string& strRPCBind :
gArgs.
GetArgs(
"-rpcbind")) {
231 uint16_t port{http_port};
237 endpoints.emplace_back(host, port);
253 std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
254 std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
255 for (; i != iend; ++i)
256 if (i->prefix ==
prefix && i->exactMatch == exactMatch)
261 pathHandlers.erase(i);
280 std::vector<std::string_view>
ret;
283 ret.push_back(item.second);
291 m_headers.emplace_back(std::move(key), std::move(value));
296 auto moved = std::ranges::remove_if(
m_headers, [key] (
auto& pair) {
299 m_headers.erase(moved.begin(), moved.end());
309 const std::string_view& line = *maybe_line;
312 if (line.empty())
return true;
320 if (line.find_first_of(
"\r\n\0", 0, 3) != std::string_view::npos)
throw std::runtime_error(
"Header contains invalid character");
325 const size_t pos{line.find(
':')};
326 if (pos == std::string_view::npos)
throw std::runtime_error(
"HTTP header missing colon (:)");
330 std::string_view key = line.substr(0, pos);
331 if (key.find_first_of(
" \t\n\r\f\v") != std::string_view::npos)
throw std::runtime_error(
"Invalid header field-name contains whitespace");
333 std::string value =
util::TrimString(std::string_view(line).substr(pos + 1));
338 if (key.empty())
throw std::runtime_error(
"Empty HTTP header name");
340 Write(std::string(key), std::move(value));
349 for (
const auto& [key, value] :
m_headers) {
350 out += key +
": " + value +
"\r\n";
361 return strprintf(
"HTTP/%d.%d %d %s\r\n%s",
372 if (!maybe_line)
return false;
373 const std::string_view& request_line = *maybe_line;
382 if (request_line.find(
'\0') != std::string_view::npos)
throw std::runtime_error(
"Invalid request line contains NUL");
384 const std::vector<std::string_view> parts{Split<std::string_view>(request_line,
" ")};
385 if (parts.size() != 3)
throw std::runtime_error(
"HTTP request line malformed");
387 if (parts[0] ==
"GET") {
389 }
else if (parts[0] ==
"POST") {
391 }
else if (parts[0] ==
"HEAD") {
393 }
else if (parts[0] ==
"PUT") {
401 if (parts[2].rfind(
"HTTP/") != 0)
throw std::runtime_error(
"HTTP request line malformed");
405 const std::vector<std::string_view> version_parts{Split<std::string_view>(parts[2].substr(5),
".")};
406 if (version_parts.size() != 2)
throw std::runtime_error(
"HTTP request line malformed");
407 if (version_parts[0].size() != 1 || version_parts[1].size() != 1)
throw std::runtime_error(
"HTTP bad version");
408 auto major = ToIntegral<uint8_t>(version_parts[0]);
409 auto minor = ToIntegral<uint8_t>(version_parts[1]);
410 if (!major || !minor || major != 1 || minor > 9)
throw std::runtime_error(
"HTTP bad version");
426 if (transfer_encoding_header &&
ToLower(transfer_encoding_header.value()) ==
"chunked") {
432 if (!maybe_chunk_size)
return false;
436 std::string_view chunk_size_noext{maybe_chunk_size.value()};
437 const auto semicolon_pos = chunk_size_noext.find(
';');
438 if (semicolon_pos != chunk_size_noext.npos) {
439 chunk_size_noext.remove_suffix(chunk_size_noext.size() - semicolon_pos);
443 if (!chunk_size)
throw std::runtime_error(
"Cannot parse chunk length value");
450 if (*chunk_size == 0) {
459 throw std::runtime_error(
"HTTP chunked trailer exceeds size limit");
461 if (!maybe_trailer)
return false;
462 if (maybe_trailer->empty())
break;
486 if (!crlf.value().empty())
throw std::runtime_error(
"Improperly terminated chunk");
494 if (content_length_values.empty())
return true;
498 const auto& first_content_length_value{content_length_values[0]};
499 for (
size_t i = 1; i < content_length_values.size(); ++i) {
500 if (content_length_values[i] != first_content_length_value)
throw std::runtime_error(
"Differing Content-Length values");
503 const auto content_length{ToIntegral<uint64_t>(first_content_length_value)};
504 if (!content_length)
throw std::runtime_error(
"Cannot parse Content-Length value");
532 bool needs_body{status !=
HTTP_NO_CONTENT && (status < 100 || status >= 200)};
533 bool needs_content_length{
false};
535 bool keep_alive{
false};
543 if (connection_header &&
ToLower(connection_header.value()) ==
"keep-alive") {
548 if (needs_body) needs_content_length =
true;
554 const int64_t now_seconds{TicksSinceEpoch<std::chrono::seconds>(
NodeClock::now())};
558 if (needs_body) needs_content_length =
true;
565 if (needs_content_length) {
571 res.
m_headers.
Write(
"Content-Type",
"text/html; charset=ISO-8859-1");
575 if (connection_header &&
ToLower(connection_header.value()) ==
"close") {
583 m_client->m_keep_alive = keep_alive;
587 const auto headers_bytes{std::as_bytes(std::span{
headers})};
589 bool send_buffer_was_empty{
false};
593 send_buffer_was_empty =
m_client->m_send_buffer.empty();
594 m_client->m_send_buffer.insert(
m_client->m_send_buffer.end(), headers_bytes.begin(), headers_bytes.end());
599 m_client->m_send_buffer.insert(
m_client->m_send_buffer.end(), reply_body.begin(), reply_body.end());
609 if (!send_buffer_was_empty)
m_client->m_send_ready =
true;
614 "HTTPResponse (status code: %d size: %lld) added to send buffer for client %s (id=%llu)",
616 headers_bytes.size() + reply_body.size(),
624 if (send_buffer_was_empty) {
625 m_client->MaybeSendBytesFromBuffer();
647 size_t start = uri.find(
'?');
648 if (start == std::string::npos)
return std::nullopt;
649 size_t end = uri.find(
'#', start);
650 if (end == std::string::npos) {
653 const std::string_view query{uri.data() + start + 1, end - start - 1};
655 const std::vector<std::string_view> params{Split<std::string_view>(query,
"&")};
656 for (
const std::string_view& param : params) {
657 size_t delim = param.find(
'=');
658 if (key ==
UrlDecode(param.substr(0, delim))) {
659 if (delim == std::string::npos) {
662 return std::string(
UrlDecode(param.substr(delim + 1)));
672 return std::pair{found.has_value(), std::move(found).value_or(
"")};
683 sockaddr_storage storage;
684 auto sa =
reinterpret_cast<sockaddr*
>(&storage);
685 socklen_t len{
sizeof(storage)};
701 "Cannot set SO_REUSEADDR on %s listen socket: %s, continuing anyway",
712 "Cannot set IPV6_V6ONLY on %s listen socket: %s, continuing anyway",
718 int prot_level{PROTECTION_LEVEL_UNRESTRICTED};
719 if (sock->SetSockOpt(IPPROTO_IPV6,
720 IPV6_PROTECTION_LEVEL,
724 "Cannot set IPV6_PROTECTION_LEVEL on %s listen socket: %s, continuing anyway",
751 m_listen.emplace_back(std::move(sock));
783 Assume(std::ranges::any_of(
m_listen, [&](
const auto& sock) {
return sock.get() == &listen_sock; }));
785 sockaddr_storage storage;
786 socklen_t len{
sizeof(storage)};
787 auto sa =
reinterpret_cast<sockaddr*
>(&storage);
789 auto sock{listen_sock.
Accept(sa, &len)};
795 "Cannot accept new connection: %s",
804 "Unknown socket family");
809 LogDebug(
BCLog::HTTP,
"Connection from %s rejected: Client network is not allowed HTTP access\n",
820 return m_next_id.fetch_add(1, std::memory_order_relaxed);
825 if (!sock->IsSelectable()) {
827 "connection from %s dropped: non-selectable socket",
841 m_connected.push_back(std::make_shared<HTTPRemoteClient>(
id, addr, std::move(sock)));
846 "HTTP Connection accepted from %s (id=%llu)",
861 const std::shared_ptr<HTTPRemoteClient>&
client{it->second};
871 if (!
client->MaybeSendBytesFromBuffer()) {
876 if (recv_ready || err_ready) {
888 "Permanent read error from %s (id=%llu): %s",
892 client->m_disconnect =
true;
894 }
else if (nrecv == 0) {
897 "Received EOF from %s (id=%llu)",
900 client->m_disconnect =
true;
903 client->m_idle_since = Now<SteadySeconds>();
906 client->m_connection_busy =
true;
909 client->m_recv_buffer.insert(
910 client->m_recv_buffer.end(),
930 const auto it = events_per_sock.find(sock);
931 if (it != events_per_sock.end() && it->second.occurred &
Sock::RecvEvent) {
953 std::shared_ptr<Sock> sock{
WITH_LOCK(http_client->m_sock_mutex,
return http_client->m_sock;)};
961 const bool send_ready{
WITH_LOCK(http_client->m_send_mutex,
return http_client->m_send_ready;)};
979 if (io_readiness.events_per_sock.empty() ||
981 !io_readiness.events_per_sock.begin()->first->WaitMany(
SELECT_TIMEOUT,
982 io_readiness.events_per_sock)) {
1000 while (!
client->m_recv_buffer.empty()) {
1002 auto req = std::make_unique<HTTPRequest>(
client);
1005 if (!
client->ReadRequest(*req))
break;
1009 "HTTP request body too large from client %s (id=%llu): %s",
1015 client->m_disconnect =
true;
1017 }
catch (
const std::runtime_error& e) {
1020 "Error reading HTTP request from client %s (id=%llu): %s",
1027 client->m_disconnect =
true;
1034 "Received a %s request for %s from %s (id=%llu)",
1041 client->m_req_queue.push_back(std::move(req));
1047 if (
client->m_req_busy)
return;
1050 if (!
client->m_req_queue.empty()) {
1052 client->m_req_busy =
true;
1053 m_request_dispatcher(std::move(
client->m_req_queue.front()));
1054 client->m_req_queue.pop_front();
1060 const auto now{Now<SteadySeconds>()};
1074 if (
client->m_disconnect || is_idle) {
1076 LogDebug(BCLog::HTTP,
1077 "HTTP client idle timeout %s (id=%llu)",
1086 if (
client->m_connection_busy) {
1102 "Disconnecting HTTP client %s (id=%llu)",
1109 m_connected_size.fetch_sub(erased, std::memory_order_relaxed);
1113void HTTPServer::ClearConnectedClients()
1115 Assume(!m_thread_socket_handler.joinable());
1116 if (m_connected.empty())
return;
1117 LogWarning(
"Force-disconnecting %d HTTP client(s) that did not disconnect gracefully", m_connected.size());
1118 m_connected_size.fetch_sub(m_connected.size(), std::memory_order_relaxed);
1119 m_connected.clear();
1133 m_recv_buffer.erase(
1134 m_recv_buffer.begin(),
1140bool HTTPRemoteClient::MaybeSendBytesFromBuffer()
1144 if (!m_send_buffer.empty()) {
1159 bytes_sent = m_sock->Send(m_send_buffer.data(),
1160 m_send_buffer.size(),
1164 if (bytes_sent < 0) {
1169 m_send_ready =
true;
1170 m_connection_busy =
true;
1176 "Error sending HTTP response data to client %s (id=%llu): %s",
1180 m_send_ready =
false;
1181 m_disconnect =
true;
1189 Assume(
static_cast<size_t>(bytes_sent) <= m_send_buffer.size());
1190 m_send_buffer.erase(m_send_buffer.begin(),
1191 m_send_buffer.begin() + bytes_sent);
1195 "Sent %d bytes to client %s (id=%llu)",
1203 if (m_send_buffer.empty()) {
1204 m_send_ready =
false;
1205 m_connection_busy =
false;
1208 if (!m_keep_alive) {
1209 m_disconnect =
true;
1215 m_send_ready =
true;
1216 m_connection_busy =
true;
1220 m_idle_since = Now<SteadySeconds>();
1238 std::vector<std::pair<std::string, uint16_t>> endpoints{
GetBindAddresses()};
1239 bool bind_success{
false};
1240 for (
const auto& [address_string, port] : endpoints) {
1241 LogInfo(
"Binding RPC on address %s port %i", address_string, port);
1242 const std::optional<CService> addr{
Lookup(address_string, port,
false)};
1244 if (addr->IsBindAny()) {
1245 LogWarning(
"The RPC server is not safe to expose to untrusted networks such as the public internet");
1247 auto result{
g_http_server->BindAndStartListening(addr.value())};
1249 LogWarning(
"Binding RPC on address %s failed: %s", addr->ToStringAddrPort(), result.error());
1251 bind_success =
true;
1254 LogWarning(
"Could not bind RPC on address %s port %i: Address lookup failed.", address_string, port);
1258 if (!bind_success) {
1259 LogError(
"Unable to bind any endpoint for RPC server");
1274 LogInfo(
"Starting HTTP server with %d worker threads", rpcThreads);
1309 LogWarning(
"Timeout waiting for HTTP clients to disconnect gracefully, continuing shutdown");
1312 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)
std::string GetURI() const
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.
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....