mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 13:42:10 +02:00
HTTPServer: read requests from connected clients
`SocketHandlerConnected()` adapted from CConnman Testing this requires adding a new feature to the SocketTestingSetup, inserting a "request" payload into the mock client that connects to us. This commit also moves IOErrorIsPermanent() from sock.cpp to sock.h so it can be called from the socket handler in httpserver.cpp Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
This commit is contained in:
@@ -281,6 +281,8 @@ public:
|
||||
std::string StringifyHeaders() const;
|
||||
};
|
||||
|
||||
class HTTPRemoteClient;
|
||||
|
||||
class HTTPRequest
|
||||
{
|
||||
public:
|
||||
@@ -290,6 +292,13 @@ public:
|
||||
HTTPHeaders m_headers;
|
||||
std::string m_body;
|
||||
|
||||
//! Pointer to the client that made the request so we know who to respond to.
|
||||
std::shared_ptr<HTTPRemoteClient> m_client;
|
||||
|
||||
explicit HTTPRequest(std::shared_ptr<HTTPRemoteClient> client) : m_client{std::move(client)} {}
|
||||
//! Construct with a null client for unit tests
|
||||
explicit HTTPRequest() : m_client{} {}
|
||||
|
||||
/**
|
||||
* Methods that attempt to parse HTTP request fields line-by-line
|
||||
* from a receive buffer.
|
||||
@@ -305,8 +314,6 @@ public:
|
||||
/// @}
|
||||
};
|
||||
|
||||
class HTTPRemoteClient;
|
||||
|
||||
class HTTPServer
|
||||
{
|
||||
public:
|
||||
@@ -315,6 +322,8 @@ public:
|
||||
*/
|
||||
using Id = uint64_t;
|
||||
|
||||
explicit HTTPServer(std::function<void(std::unique_ptr<HTTPRequest>&&)> func) : m_request_dispatcher{std::move(func)} {}
|
||||
|
||||
virtual ~HTTPServer()
|
||||
{
|
||||
Assume(!m_thread_socket_handler.joinable()); // Missing call to JoinSocketsThreads()
|
||||
@@ -344,13 +353,6 @@ public:
|
||||
*/
|
||||
size_t GetConnectionsCount() const { return m_connected_size.load(std::memory_order_acquire); }
|
||||
|
||||
/**
|
||||
* This is a temporary method used to get a pointer to an HTTPRemoteClient
|
||||
* so its connection can be inspected in a unit test.
|
||||
* It will be removed in a future commit.
|
||||
*/
|
||||
const HTTPRemoteClient& GetFirstConnection() { return *m_connected.front(); }
|
||||
|
||||
/**
|
||||
* Start the necessary threads for sockets IO.
|
||||
*/
|
||||
@@ -436,6 +438,11 @@ private:
|
||||
*/
|
||||
std::thread m_thread_socket_handler;
|
||||
|
||||
/*
|
||||
* What to do with HTTP requests once received, validated and parsed
|
||||
*/
|
||||
std::function<void(std::unique_ptr<HTTPRequest>&&)> m_request_dispatcher;
|
||||
|
||||
/**
|
||||
* Accept a connection.
|
||||
* @param[in] listen_sock Socket on which to accept the connection.
|
||||
@@ -457,6 +464,12 @@ private:
|
||||
*/
|
||||
void NewSockAccepted(std::unique_ptr<Sock>&& sock, const CService& addr);
|
||||
|
||||
/**
|
||||
* Do the read/write for connected sockets that are ready for IO.
|
||||
* @param[in] io_readiness Which sockets are ready and their corresponding HTTPRemoteClients.
|
||||
*/
|
||||
void SocketHandlerConnected(const IOReadiness& io_readiness) const;
|
||||
|
||||
/**
|
||||
* Accept incoming connections, one from each read-ready listening socket.
|
||||
* @param[in] events_per_sock Sockets that are ready for IO.
|
||||
@@ -475,6 +488,15 @@ private:
|
||||
* This is the main I/O loop of the server.
|
||||
*/
|
||||
void ThreadSocketHandler();
|
||||
|
||||
/**
|
||||
* Try to read HTTPRequests from a client's receive buffer.
|
||||
* Complete requests are dispatched, incomplete requests are
|
||||
* left in the buffer to wait for more data. Some read errors
|
||||
* will mark this client for disconnection.
|
||||
* @param[in] client The HTTPRemoteClient to read requests from
|
||||
*/
|
||||
void MaybeDispatchRequestsFromClient(const std::shared_ptr<HTTPRemoteClient>& client) const;
|
||||
};
|
||||
|
||||
class HTTPRemoteClient
|
||||
@@ -489,6 +511,13 @@ public:
|
||||
//! IP:port of connected client, cached for logging purposes
|
||||
const std::string m_origin;
|
||||
|
||||
/**
|
||||
* In lieu of an intermediate transport class like p2p uses,
|
||||
* we copy data from the socket buffer to the client object
|
||||
* and attempt to read HTTP requests from here.
|
||||
*/
|
||||
std::vector<std::byte> m_recv_buffer{};
|
||||
|
||||
/**
|
||||
* Mutex that serializes the Send() and Recv() calls on `m_sock`. Reading
|
||||
* from the client occurs in the I/O thread but writing back to a client
|
||||
@@ -511,6 +540,13 @@ public:
|
||||
// Disable copies (should only be used as shared pointers)
|
||||
HTTPRemoteClient(const HTTPRemoteClient&) = delete;
|
||||
HTTPRemoteClient& operator=(const HTTPRemoteClient&) = delete;
|
||||
|
||||
/**
|
||||
* Try to read an HTTP request from the receive buffer.
|
||||
* @param[in] req A HTTPRequest to read into
|
||||
* @returns true upon reading a complete request, otherwise false (may throw).
|
||||
*/
|
||||
bool ReadRequest(HTTPRequest& req);
|
||||
};
|
||||
} // namespace http_bitcoin
|
||||
|
||||
|
||||
Reference in New Issue
Block a user