mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-16 23:53:29 +01:00
evhttpd implementation
- *Replace usage of boost::asio with [libevent2](http://libevent.org/)*. boost::asio is not part of C++11, so unlike other boost there is no forwards-compatibility reason to stick with it. Together with #4738 (convert json_spirit to UniValue), this rids Bitcoin Core of the worst offenders with regard to compile-time slowness. - *Replace spit-and-duct-tape http server with evhttp*. Front-end http handling is handled by libevent, a work queue (with configurable depth and parallelism) is used to handle application requests. - *Wrap HTTP request in C++ class*; this makes the application code mostly HTTP-server-neutral - *Refactor RPC to move all http-specific code to a separate file*. Theoreticaly this can allow building without HTTP server but with another RPC backend, e.g. Qt's debug console (currently not implemented) or future RPC mechanisms people may want to use. - *HTTP dispatch mechanism*; services (e.g., RPC, REST) register which URL paths they want to handle. By using a proven, high-performance asynchronous networking library (also used by Tor) and HTTP server, problems such as #5674, #5655, #344 should be avoided. What works? bitcoind, bitcoin-cli, bitcoin-qt. Unit tests and RPC/REST tests pass. The aim for now is everything but SSL support. Configuration options: - `-rpcthreads`: repurposed as "number of work handler threads". Still defaults to 4. - `-rpcworkqueue`: maximum depth of work queue. When this is reached, new requests will return a 500 Internal Error. - `-rpctimeout`: inactivity time, in seconds, after which to disconnect a client. - `-debug=http`: low-level http activity logging
This commit is contained in:
@@ -10,10 +10,6 @@
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "univalue/univalue.h"
|
||||
@@ -26,6 +22,7 @@ enum HTTPStatusCode
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
HTTP_FORBIDDEN = 403,
|
||||
HTTP_NOT_FOUND = 404,
|
||||
HTTP_BAD_METHOD = 405,
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_SERVICE_UNAVAILABLE = 503,
|
||||
};
|
||||
@@ -79,88 +76,6 @@ enum RPCErrorCode
|
||||
RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked
|
||||
};
|
||||
|
||||
/**
|
||||
* IOStream device that speaks SSL but can also speak non-SSL
|
||||
*/
|
||||
template <typename Protocol>
|
||||
class SSLIOStreamDevice : public boost::iostreams::device<boost::iostreams::bidirectional> {
|
||||
public:
|
||||
SSLIOStreamDevice(boost::asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
|
||||
{
|
||||
fUseSSL = fUseSSLIn;
|
||||
fNeedHandshake = fUseSSLIn;
|
||||
}
|
||||
|
||||
void handshake(boost::asio::ssl::stream_base::handshake_type role)
|
||||
{
|
||||
if (!fNeedHandshake) return;
|
||||
fNeedHandshake = false;
|
||||
stream.handshake(role);
|
||||
}
|
||||
std::streamsize read(char* s, std::streamsize n)
|
||||
{
|
||||
handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first
|
||||
if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n));
|
||||
return stream.next_layer().read_some(boost::asio::buffer(s, n));
|
||||
}
|
||||
std::streamsize write(const char* s, std::streamsize n)
|
||||
{
|
||||
handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first
|
||||
if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n));
|
||||
return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n));
|
||||
}
|
||||
bool connect(const std::string& server, const std::string& port)
|
||||
{
|
||||
using namespace boost::asio::ip;
|
||||
tcp::resolver resolver(stream.get_io_service());
|
||||
tcp::resolver::iterator endpoint_iterator;
|
||||
#if BOOST_VERSION >= 104300
|
||||
try {
|
||||
#endif
|
||||
// The default query (flags address_configured) tries IPv6 if
|
||||
// non-localhost IPv6 configured, and IPv4 if non-localhost IPv4
|
||||
// configured.
|
||||
tcp::resolver::query query(server.c_str(), port.c_str());
|
||||
endpoint_iterator = resolver.resolve(query);
|
||||
#if BOOST_VERSION >= 104300
|
||||
} catch (const boost::system::system_error&) {
|
||||
// If we at first don't succeed, try blanket lookup (IPv4+IPv6 independent of configured interfaces)
|
||||
tcp::resolver::query query(server.c_str(), port.c_str(), resolver_query_base::flags());
|
||||
endpoint_iterator = resolver.resolve(query);
|
||||
}
|
||||
#endif
|
||||
boost::system::error_code error = boost::asio::error::host_not_found;
|
||||
tcp::resolver::iterator end;
|
||||
while (error && endpoint_iterator != end)
|
||||
{
|
||||
stream.lowest_layer().close();
|
||||
stream.lowest_layer().connect(*endpoint_iterator++, error);
|
||||
}
|
||||
if (error)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool fNeedHandshake;
|
||||
bool fUseSSL;
|
||||
boost::asio::ssl::stream<typename Protocol::socket>& stream;
|
||||
};
|
||||
|
||||
std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders);
|
||||
std::string HTTPError(int nStatus, bool keepalive,
|
||||
bool headerOnly = false);
|
||||
std::string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength,
|
||||
const char *contentType = "application/json");
|
||||
std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive,
|
||||
bool headerOnly = false,
|
||||
const char *contentType = "application/json");
|
||||
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
||||
std::string& http_method, std::string& http_uri);
|
||||
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto);
|
||||
int ReadHTTPHeaders(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet);
|
||||
int ReadHTTPMessage(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet, int nProto, size_t max_size);
|
||||
std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id);
|
||||
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
|
||||
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
|
||||
|
||||
Reference in New Issue
Block a user