mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-01 18:49:15 +01:00
Replace the HTTP server's WorkQueue implementation and single threads handling code with ThreadPool for processing HTTP requests. The ThreadPool class encapsulates all this functionality on a reusable class, properly unit and fuzz tested (the previous code was not unit nor fuzz tested at all). This cleanly separates responsibilities: The HTTP server now focuses solely on receiving and dispatching requests, while ThreadPool handles concurrency, queuing, and execution. It simplifies init, shutdown and requests tracking. This also allows us to experiment with further performance improvements at the task queuing and execution level, such as a lock-free structure, task prioritization or any other performance improvement in the future, without having to deal with HTTP code that lives on a different layer.
186 lines
5.7 KiB
C++
186 lines
5.7 KiB
C++
// Copyright (c) 2015-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_HTTPSERVER_H
|
|
#define BITCOIN_HTTPSERVER_H
|
|
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <span>
|
|
#include <string>
|
|
|
|
namespace util {
|
|
class SignalInterrupt;
|
|
} // namespace util
|
|
|
|
/**
|
|
* The default value for `-rpcthreads`. This number of threads will be created at startup.
|
|
*/
|
|
static const int DEFAULT_HTTP_THREADS=16;
|
|
|
|
/**
|
|
* The default value for `-rpcworkqueue`. This is the maximum depth of the work queue,
|
|
* we don't allocate this number of work queue items upfront.
|
|
*/
|
|
static const int DEFAULT_HTTP_WORKQUEUE=64;
|
|
|
|
static const int DEFAULT_HTTP_SERVER_TIMEOUT=30;
|
|
|
|
struct evhttp_request;
|
|
struct event_base;
|
|
class CService;
|
|
class HTTPRequest;
|
|
|
|
/** Initialize HTTP server.
|
|
* Call this before RegisterHTTPHandler or EventBase().
|
|
*/
|
|
bool InitHTTPServer(const util::SignalInterrupt& interrupt);
|
|
/** Start HTTP server.
|
|
* This is separate from InitHTTPServer to give users race-condition-free time
|
|
* to register their handlers between InitHTTPServer and StartHTTPServer.
|
|
*/
|
|
void StartHTTPServer();
|
|
/** Interrupt HTTP server threads */
|
|
void InterruptHTTPServer();
|
|
/** Stop HTTP server */
|
|
void StopHTTPServer();
|
|
|
|
/** Change logging level for libevent. */
|
|
void UpdateHTTPServerLogging(bool enable);
|
|
|
|
/** Handler for requests to a certain HTTP path */
|
|
typedef std::function<bool(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
|
|
/** Register handler for prefix.
|
|
* If multiple handlers match a prefix, the first-registered one will
|
|
* be invoked.
|
|
*/
|
|
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler);
|
|
/** Unregister handler for prefix */
|
|
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch);
|
|
|
|
/** Return evhttp event base. This can be used by submodules to
|
|
* queue timers or custom events.
|
|
*/
|
|
struct event_base* EventBase();
|
|
|
|
/** In-flight HTTP request.
|
|
* Thin C++ wrapper around evhttp_request.
|
|
*/
|
|
class HTTPRequest
|
|
{
|
|
private:
|
|
struct evhttp_request* req;
|
|
const util::SignalInterrupt& m_interrupt;
|
|
bool replySent;
|
|
|
|
public:
|
|
explicit HTTPRequest(struct evhttp_request* req, const util::SignalInterrupt& interrupt, bool replySent = false);
|
|
~HTTPRequest();
|
|
|
|
enum RequestMethod {
|
|
UNKNOWN,
|
|
GET,
|
|
POST,
|
|
HEAD,
|
|
PUT
|
|
};
|
|
|
|
/** Get requested URI.
|
|
*/
|
|
std::string GetURI() const;
|
|
|
|
/** Get CService (address:ip) for the origin of the http request.
|
|
*/
|
|
CService GetPeer() const;
|
|
|
|
/** Get request method.
|
|
*/
|
|
RequestMethod GetRequestMethod() const;
|
|
|
|
/** Get the query parameter value from request uri for a specified key, or std::nullopt if the
|
|
* key is not found.
|
|
*
|
|
* If the query string contains duplicate keys, the first value is returned. Many web frameworks
|
|
* would instead parse this as an array of values, but this is not (yet) implemented as it is
|
|
* currently not needed in any of the endpoints.
|
|
*
|
|
* @param[in] key represents the query parameter of which the value is returned
|
|
*/
|
|
std::optional<std::string> GetQueryParameter(const std::string& key) const;
|
|
|
|
/**
|
|
* Get the request header specified by hdr, or an empty string.
|
|
* Return a pair (isPresent,string).
|
|
*/
|
|
std::pair<bool, std::string> GetHeader(const std::string& hdr) const;
|
|
|
|
/**
|
|
* Read request body.
|
|
*
|
|
* @note As this consumes the underlying buffer, call this only once.
|
|
* Repeated calls will return an empty string.
|
|
*/
|
|
std::string ReadBody();
|
|
|
|
/**
|
|
* Write output header.
|
|
*
|
|
* @note call this before calling WriteErrorReply or Reply.
|
|
*/
|
|
void WriteHeader(const std::string& hdr, const std::string& value);
|
|
|
|
/**
|
|
* Write HTTP reply.
|
|
* nStatus is the HTTP status code to send.
|
|
* reply is the body of the reply. Keep it empty to send a standard message.
|
|
*
|
|
* @note Can be called only once. As this will give the request back to the
|
|
* main thread, do not call any other HTTPRequest methods after calling this.
|
|
*/
|
|
void WriteReply(int nStatus, std::string_view reply = "")
|
|
{
|
|
WriteReply(nStatus, std::as_bytes(std::span{reply}));
|
|
}
|
|
void WriteReply(int nStatus, std::span<const std::byte> reply);
|
|
};
|
|
|
|
/** Get the query parameter value from request uri for a specified key, or std::nullopt if the key
|
|
* is not found.
|
|
*
|
|
* If the query string contains duplicate keys, the first value is returned. Many web frameworks
|
|
* would instead parse this as an array of values, but this is not (yet) implemented as it is
|
|
* currently not needed in any of the endpoints.
|
|
*
|
|
* Helper function for HTTPRequest::GetQueryParameter.
|
|
*
|
|
* @param[in] uri is the entire request uri
|
|
* @param[in] key represents the query parameter of which the value is returned
|
|
*/
|
|
std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key);
|
|
|
|
/** Event class. This can be used either as a cross-thread trigger or as a timer.
|
|
*/
|
|
class HTTPEvent
|
|
{
|
|
public:
|
|
/** Create a new event.
|
|
* deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
|
|
* handler is the handler to call when the event is triggered.
|
|
*/
|
|
HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void()>& handler);
|
|
~HTTPEvent();
|
|
|
|
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
|
|
* the given time has elapsed.
|
|
*/
|
|
void trigger(struct timeval* tv);
|
|
|
|
bool deleteWhenTriggered;
|
|
std::function<void()> handler;
|
|
private:
|
|
struct event* ev;
|
|
};
|
|
|
|
#endif // BITCOIN_HTTPSERVER_H
|