mirror of
https://github.com/monero-project/monero.git
synced 2025-02-22 16:39:51 -05:00
Merge pull request #9765
ec74ff4a3 Set response limits on http server connections (Lee *!* Clagett)
This commit is contained in:
commit
257db6dff2
@ -65,6 +65,7 @@
|
|||||||
#define MONERO_DEFAULT_LOG_CATEGORY "net"
|
#define MONERO_DEFAULT_LOG_CATEGORY "net"
|
||||||
|
|
||||||
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000
|
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000
|
||||||
|
#define ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT 100 * 1024 * 1024
|
||||||
|
|
||||||
namespace epee
|
namespace epee
|
||||||
{
|
{
|
||||||
@ -170,6 +171,7 @@ namespace net_utils
|
|||||||
} read;
|
} read;
|
||||||
struct {
|
struct {
|
||||||
std::deque<epee::byte_slice> queue;
|
std::deque<epee::byte_slice> queue;
|
||||||
|
std::size_t total_bytes;
|
||||||
bool wait_consume;
|
bool wait_consume;
|
||||||
} write;
|
} write;
|
||||||
};
|
};
|
||||||
@ -268,11 +270,17 @@ namespace net_utils
|
|||||||
struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type
|
struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type
|
||||||
{
|
{
|
||||||
shared_state()
|
shared_state()
|
||||||
: connection_basic_shared_state(), t_protocol_handler::config_type(), pfilter(nullptr), plimit(nullptr), stop_signal_sent(false)
|
: connection_basic_shared_state(),
|
||||||
|
t_protocol_handler::config_type(),
|
||||||
|
pfilter(nullptr),
|
||||||
|
plimit(nullptr),
|
||||||
|
response_soft_limit(ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT),
|
||||||
|
stop_signal_sent(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
i_connection_filter* pfilter;
|
i_connection_filter* pfilter;
|
||||||
i_connection_limit* plimit;
|
i_connection_limit* plimit;
|
||||||
|
std::size_t response_soft_limit;
|
||||||
bool stop_signal_sent;
|
bool stop_signal_sent;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -380,6 +388,7 @@ namespace net_utils
|
|||||||
|
|
||||||
void set_connection_filter(i_connection_filter* pfilter);
|
void set_connection_filter(i_connection_filter* pfilter);
|
||||||
void set_connection_limit(i_connection_limit* plimit);
|
void set_connection_limit(i_connection_limit* plimit);
|
||||||
|
void set_response_soft_limit(std::size_t limit);
|
||||||
|
|
||||||
void set_default_remote(epee::net_utils::network_address remote)
|
void set_default_remote(epee::net_utils::network_address remote)
|
||||||
{
|
{
|
||||||
|
@ -497,10 +497,12 @@ namespace net_utils
|
|||||||
if (m_state.socket.cancel_write) {
|
if (m_state.socket.cancel_write) {
|
||||||
m_state.socket.cancel_write = false;
|
m_state.socket.cancel_write = false;
|
||||||
m_state.data.write.queue.clear();
|
m_state.data.write.queue.clear();
|
||||||
|
m_state.data.write.total_bytes = 0;
|
||||||
state_status_check();
|
state_status_check();
|
||||||
}
|
}
|
||||||
else if (ec.value()) {
|
else if (ec.value()) {
|
||||||
m_state.data.write.queue.clear();
|
m_state.data.write.queue.clear();
|
||||||
|
m_state.data.write.total_bytes = 0;
|
||||||
interrupt();
|
interrupt();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -525,8 +527,11 @@ namespace net_utils
|
|||||||
|
|
||||||
start_timer(get_default_timeout(), true);
|
start_timer(get_default_timeout(), true);
|
||||||
}
|
}
|
||||||
assert(bytes_transferred == m_state.data.write.queue.back().size());
|
const std::size_t byte_count = m_state.data.write.queue.back().size();
|
||||||
|
assert(bytes_transferred == byte_count);
|
||||||
m_state.data.write.queue.pop_back();
|
m_state.data.write.queue.pop_back();
|
||||||
|
m_state.data.write.total_bytes -=
|
||||||
|
std::min(m_state.data.write.total_bytes, byte_count);
|
||||||
m_state.condition.notify_all();
|
m_state.condition.notify_all();
|
||||||
start_write();
|
start_write();
|
||||||
}
|
}
|
||||||
@ -670,8 +675,9 @@ namespace net_utils
|
|||||||
return;
|
return;
|
||||||
if (m_state.timers.throttle.out.wait_expire)
|
if (m_state.timers.throttle.out.wait_expire)
|
||||||
return;
|
return;
|
||||||
if (m_state.socket.wait_write)
|
// \NOTE See on_terminating() comments
|
||||||
return;
|
//if (m_state.socket.wait_write)
|
||||||
|
// return;
|
||||||
if (m_state.socket.wait_shutdown)
|
if (m_state.socket.wait_shutdown)
|
||||||
return;
|
return;
|
||||||
if (m_state.protocol.wait_init)
|
if (m_state.protocol.wait_init)
|
||||||
@ -729,8 +735,13 @@ namespace net_utils
|
|||||||
return;
|
return;
|
||||||
if (m_state.timers.throttle.out.wait_expire)
|
if (m_state.timers.throttle.out.wait_expire)
|
||||||
return;
|
return;
|
||||||
if (m_state.socket.wait_write)
|
// Writes cannot be canceled due to `async_write` being a "composed"
|
||||||
return;
|
// handler. ASIO has new cancellation routines, not available in 1.66, to
|
||||||
|
// handle this situation. The problem is that if cancel is called after an
|
||||||
|
// intermediate handler is queued, the op will not check the cancel flag in
|
||||||
|
// our code, and will instead queue up another write.
|
||||||
|
//if (m_state.socket.wait_write)
|
||||||
|
// return;
|
||||||
if (m_state.socket.wait_shutdown)
|
if (m_state.socket.wait_shutdown)
|
||||||
return;
|
return;
|
||||||
if (m_state.protocol.wait_init)
|
if (m_state.protocol.wait_init)
|
||||||
@ -757,6 +768,8 @@ namespace net_utils
|
|||||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||||
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
|
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
|
||||||
return false;
|
return false;
|
||||||
|
if (std::numeric_limits<std::size_t>::max() - m_state.data.write.total_bytes < message.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Wait for the write queue to fall below the max. If it doesn't after a
|
// Wait for the write queue to fall below the max. If it doesn't after a
|
||||||
// randomized delay, drop the connection.
|
// randomized delay, drop the connection.
|
||||||
@ -774,7 +787,14 @@ namespace net_utils
|
|||||||
std::uniform_int_distribution<>(5000, 6000)(rng)
|
std::uniform_int_distribution<>(5000, 6000)(rng)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
|
|
||||||
|
// The bytes check intentionally does not include incoming message size.
|
||||||
|
// This allows for a soft overflow; a single http response will never fail
|
||||||
|
// this check, but multiple responses could. Clients can avoid this case
|
||||||
|
// by reading the entire response before making another request. P2P
|
||||||
|
// should never hit the MAX_BYTES check (when using default values).
|
||||||
|
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
|
||||||
|
m_state.data.write.total_bytes <= static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit)
|
||||||
return true;
|
return true;
|
||||||
m_state.data.write.wait_consume = true;
|
m_state.data.write.wait_consume = true;
|
||||||
bool success = m_state.condition.wait_for(
|
bool success = m_state.condition.wait_for(
|
||||||
@ -783,14 +803,23 @@ namespace net_utils
|
|||||||
[this]{
|
[this]{
|
||||||
return (
|
return (
|
||||||
m_state.status != status_t::RUNNING ||
|
m_state.status != status_t::RUNNING ||
|
||||||
m_state.data.write.queue.size() <=
|
(
|
||||||
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
|
m_state.data.write.queue.size() <=
|
||||||
|
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
|
||||||
|
m_state.data.write.total_bytes <=
|
||||||
|
static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
m_state.data.write.wait_consume = false;
|
m_state.data.write.wait_consume = false;
|
||||||
if (!success) {
|
if (!success) {
|
||||||
terminate();
|
// synchronize with intermediate writes on `m_strand`
|
||||||
|
auto self = connection<T>::shared_from_this();
|
||||||
|
boost::asio::post(m_strand, [this, self] {
|
||||||
|
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||||
|
terminate();
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -816,7 +845,9 @@ namespace net_utils
|
|||||||
) {
|
) {
|
||||||
if (!wait_consume())
|
if (!wait_consume())
|
||||||
return false;
|
return false;
|
||||||
|
const std::size_t byte_count = message.size();
|
||||||
m_state.data.write.queue.emplace_front(std::move(message));
|
m_state.data.write.queue.emplace_front(std::move(message));
|
||||||
|
m_state.data.write.total_bytes += byte_count;
|
||||||
start_write();
|
start_write();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -826,6 +857,7 @@ namespace net_utils
|
|||||||
m_state.data.write.queue.emplace_front(
|
m_state.data.write.queue.emplace_front(
|
||||||
message.take_slice(CHUNK_SIZE)
|
message.take_slice(CHUNK_SIZE)
|
||||||
);
|
);
|
||||||
|
m_state.data.write.total_bytes += m_state.data.write.queue.front().size();
|
||||||
start_write();
|
start_write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1369,6 +1401,13 @@ namespace net_utils
|
|||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------
|
||||||
template<class t_protocol_handler>
|
template<class t_protocol_handler>
|
||||||
|
void boosted_tcp_server<t_protocol_handler>::set_response_soft_limit(const std::size_t limit)
|
||||||
|
{
|
||||||
|
assert(m_state != nullptr); // always set in constructor
|
||||||
|
m_state->response_soft_limit = limit;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
|
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
|
||||||
{
|
{
|
||||||
TRY_ENTRY();
|
TRY_ENTRY();
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <boost/optional/optional.hpp>
|
#include <boost/optional/optional.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include "net_utils_base.h"
|
#include "net_utils_base.h"
|
||||||
#include "http_auth.h"
|
#include "http_auth.h"
|
||||||
#include "http_base.h"
|
#include "http_base.h"
|
||||||
@ -54,8 +55,13 @@ namespace net_utils
|
|||||||
{
|
{
|
||||||
std::string m_folder;
|
std::string m_folder;
|
||||||
std::vector<std::string> m_access_control_origins;
|
std::vector<std::string> m_access_control_origins;
|
||||||
|
std::unordered_map<std::string, std::size_t> m_connections;
|
||||||
boost::optional<login> m_user;
|
boost::optional<login> m_user;
|
||||||
size_t m_max_content_length{std::numeric_limits<size_t>::max()};
|
size_t m_max_content_length{std::numeric_limits<size_t>::max()};
|
||||||
|
std::size_t m_connection_count{0};
|
||||||
|
std::size_t m_max_public_ip_connections{3};
|
||||||
|
std::size_t m_max_private_ip_connections{25};
|
||||||
|
std::size_t m_max_connections{100};
|
||||||
critical_section m_lock;
|
critical_section m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,7 +76,7 @@ namespace net_utils
|
|||||||
typedef http_server_config config_type;
|
typedef http_server_config config_type;
|
||||||
|
|
||||||
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
|
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
|
||||||
virtual ~simple_http_connection_handler(){}
|
virtual ~simple_http_connection_handler();
|
||||||
|
|
||||||
bool release_protocol()
|
bool release_protocol()
|
||||||
{
|
{
|
||||||
@ -86,10 +92,7 @@ namespace net_utils
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool after_init_connection()
|
bool after_init_connection();
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual bool handle_recv(const void* ptr, size_t cb);
|
virtual bool handle_recv(const void* ptr, size_t cb);
|
||||||
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
|
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
|
||||||
|
|
||||||
@ -146,6 +149,7 @@ namespace net_utils
|
|||||||
protected:
|
protected:
|
||||||
i_service_endpoint* m_psnd_hndlr;
|
i_service_endpoint* m_psnd_hndlr;
|
||||||
t_connection_context& m_conn_context;
|
t_connection_context& m_conn_context;
|
||||||
|
bool m_initialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class t_connection_context>
|
template<class t_connection_context>
|
||||||
@ -212,10 +216,6 @@ namespace net_utils
|
|||||||
}
|
}
|
||||||
void handle_qued_callback()
|
void handle_qued_callback()
|
||||||
{}
|
{}
|
||||||
bool after_init_connection()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//simple_http_connection_handler::config_type m_stub_config;
|
//simple_http_connection_handler::config_type m_stub_config;
|
||||||
|
@ -208,11 +208,46 @@ namespace net_utils
|
|||||||
m_newlines(0),
|
m_newlines(0),
|
||||||
m_bytes_read(0),
|
m_bytes_read(0),
|
||||||
m_psnd_hndlr(psnd_hndlr),
|
m_psnd_hndlr(psnd_hndlr),
|
||||||
m_conn_context(conn_context)
|
m_conn_context(conn_context),
|
||||||
|
m_initialized(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
simple_http_connection_handler<t_connection_context>::~simple_http_connection_handler()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (m_initialized)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_config.m_lock);
|
||||||
|
if (m_config.m_connection_count)
|
||||||
|
--m_config.m_connection_count;
|
||||||
|
auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
|
||||||
|
if (elem != m_config.m_connections.end())
|
||||||
|
{
|
||||||
|
if (elem->second == 1 || elem->second == 0)
|
||||||
|
m_config.m_connections.erase(elem);
|
||||||
|
else
|
||||||
|
--(elem->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
bool simple_http_connection_handler<t_connection_context>::after_init_connection()
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_config.m_lock);
|
||||||
|
++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
|
||||||
|
++m_config.m_connection_count;
|
||||||
|
m_initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
template<class t_connection_context>
|
template<class t_connection_context>
|
||||||
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
|
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
#include <boost/bind/bind.hpp>
|
#include <boost/bind/bind.hpp>
|
||||||
|
|
||||||
|
#include "cryptonote_config.h"
|
||||||
#include "net/abstract_tcp_server2.h"
|
#include "net/abstract_tcp_server2.h"
|
||||||
#include "http_protocol_handler.h"
|
#include "http_protocol_handler.h"
|
||||||
#include "net/http_server_handlers_map2.h"
|
#include "net/http_server_handlers_map2.h"
|
||||||
@ -44,7 +45,8 @@ namespace epee
|
|||||||
{
|
{
|
||||||
|
|
||||||
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
|
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
|
||||||
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>
|
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>,
|
||||||
|
net_utils::i_connection_limit
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -60,8 +62,16 @@ namespace epee
|
|||||||
const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
|
const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
|
||||||
std::vector<std::string> access_control_origins = std::vector<std::string>(),
|
std::vector<std::string> access_control_origins = std::vector<std::string>(),
|
||||||
boost::optional<net_utils::http::login> user = boost::none,
|
boost::optional<net_utils::http::login> user = boost::none,
|
||||||
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect)
|
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect,
|
||||||
|
const std::size_t max_public_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP,
|
||||||
|
const std::size_t max_private_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP,
|
||||||
|
const std::size_t max_connections = DEFAULT_RPC_MAX_CONNECTIONS,
|
||||||
|
const std::size_t response_soft_limit = DEFAULT_RPC_SOFT_LIMIT_SIZE)
|
||||||
{
|
{
|
||||||
|
if (max_connections < max_public_ip_connections)
|
||||||
|
throw std::invalid_argument{"Max public IP connections cannot be more than max connections"};
|
||||||
|
if (max_connections < max_private_ip_connections)
|
||||||
|
throw std::invalid_argument{"Max private IP connections cannot be more than max connections"};
|
||||||
|
|
||||||
//set self as callback handler
|
//set self as callback handler
|
||||||
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
|
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
|
||||||
@ -75,6 +85,11 @@ namespace epee
|
|||||||
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
|
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
|
||||||
|
|
||||||
m_net_server.get_config_object().m_user = std::move(user);
|
m_net_server.get_config_object().m_user = std::move(user);
|
||||||
|
m_net_server.get_config_object().m_max_public_ip_connections = max_public_ip_connections;
|
||||||
|
m_net_server.get_config_object().m_max_private_ip_connections = max_private_ip_connections;
|
||||||
|
m_net_server.get_config_object().m_max_connections = max_connections;
|
||||||
|
m_net_server.set_response_soft_limit(response_soft_limit);
|
||||||
|
m_net_server.set_connection_limit(this);
|
||||||
|
|
||||||
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
|
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
|
||||||
if (use_ipv6)
|
if (use_ipv6)
|
||||||
@ -131,6 +146,26 @@ namespace epee
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
virtual bool is_host_limit(const net_utils::network_address& na) override final
|
||||||
|
{
|
||||||
|
auto& config = m_net_server.get_config_object();
|
||||||
|
CRITICAL_REGION_LOCAL(config.m_lock);
|
||||||
|
if (config.m_max_connections <= config.m_connection_count)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const bool is_private = na.is_loopback() || na.is_local();
|
||||||
|
const auto elem = config.m_connections.find(na.host_str());
|
||||||
|
if (elem != config.m_connections.end())
|
||||||
|
{
|
||||||
|
if (is_private)
|
||||||
|
return config.m_max_private_ip_connections <= elem->second;
|
||||||
|
else
|
||||||
|
return config.m_max_public_ip_connections <= elem->second;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
|
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,10 @@
|
|||||||
|
|
||||||
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000
|
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000
|
||||||
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000
|
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000
|
||||||
|
#define DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP 3
|
||||||
|
#define DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP 25
|
||||||
|
#define DEFAULT_RPC_MAX_CONNECTIONS 100
|
||||||
|
#define DEFAULT_RPC_SOFT_LIMIT_SIZE 25 * 1024 * 1024 // 25 MiB
|
||||||
#define MAX_RPC_CONTENT_LENGTH 1048576 // 1 MB
|
#define MAX_RPC_CONTENT_LENGTH 1048576 // 1 MB
|
||||||
|
|
||||||
#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000
|
#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000
|
||||||
|
@ -169,7 +169,7 @@ namespace nodetool
|
|||||||
const command_line::arg_descriptor<bool> arg_pad_transactions = {
|
const command_line::arg_descriptor<bool> arg_pad_transactions = {
|
||||||
"pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
|
"pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
|
||||||
};
|
};
|
||||||
const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of connections allowed from the same IP address", 1};
|
const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of p2p connections allowed from the same IP address", 1};
|
||||||
|
|
||||||
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
|
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
|
||||||
{
|
{
|
||||||
|
@ -163,6 +163,10 @@ namespace cryptonote
|
|||||||
command_line::add_arg(desc, arg_rpc_payment_difficulty);
|
command_line::add_arg(desc, arg_rpc_payment_difficulty);
|
||||||
command_line::add_arg(desc, arg_rpc_payment_credits);
|
command_line::add_arg(desc, arg_rpc_payment_credits);
|
||||||
command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback);
|
command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback);
|
||||||
|
command_line::add_arg(desc, arg_rpc_max_connections_per_public_ip);
|
||||||
|
command_line::add_arg(desc, arg_rpc_max_connections_per_private_ip);
|
||||||
|
command_line::add_arg(desc, arg_rpc_max_connections);
|
||||||
|
command_line::add_arg(desc, arg_rpc_response_soft_limit);
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
core_rpc_server::core_rpc_server(
|
core_rpc_server::core_rpc_server(
|
||||||
@ -396,11 +400,28 @@ namespace cryptonote
|
|||||||
}
|
}
|
||||||
} // if (store_ssl_key)
|
} // if (store_ssl_key)
|
||||||
|
|
||||||
|
const auto max_connections_public = command_line::get_arg(vm, arg_rpc_max_connections_per_public_ip);
|
||||||
|
const auto max_connections_private = command_line::get_arg(vm, arg_rpc_max_connections_per_private_ip);
|
||||||
|
const auto max_connections = command_line::get_arg(vm, arg_rpc_max_connections);
|
||||||
|
|
||||||
|
if (max_connections < max_connections_public)
|
||||||
|
{
|
||||||
|
MFATAL(arg_rpc_max_connections_per_public_ip.name << " is bigger than " << arg_rpc_max_connections.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (max_connections < max_connections_private)
|
||||||
|
{
|
||||||
|
MFATAL(arg_rpc_max_connections_per_private_ip.name << " is bigger than " << arg_rpc_max_connections.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
|
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
|
||||||
const bool inited = epee::http_server_impl_base<core_rpc_server, connection_context>::init(
|
const bool inited = epee::http_server_impl_base<core_rpc_server, connection_context>::init(
|
||||||
rng, std::move(port), std::move(bind_ip_str),
|
rng, std::move(port), std::move(bind_ip_str),
|
||||||
std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
|
std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
|
||||||
std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
|
std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options),
|
||||||
|
max_connections_public, max_connections_private, max_connections,
|
||||||
|
command_line::get_arg(vm, arg_rpc_response_soft_limit)
|
||||||
);
|
);
|
||||||
|
|
||||||
m_net_server.get_config_object().m_max_content_length = MAX_RPC_CONTENT_LENGTH;
|
m_net_server.get_config_object().m_max_content_length = MAX_RPC_CONTENT_LENGTH;
|
||||||
@ -3885,4 +3906,28 @@ namespace cryptonote
|
|||||||
, "Allow free access from the loopback address (ie, the local host)"
|
, "Allow free access from the loopback address (ie, the local host)"
|
||||||
, false
|
, false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_max_connections_per_public_ip = {
|
||||||
|
"rpc-max-connections-per-public-ip"
|
||||||
|
, "Max RPC connections per public IP permitted"
|
||||||
|
, DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP
|
||||||
|
};
|
||||||
|
|
||||||
|
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_max_connections_per_private_ip = {
|
||||||
|
"rpc-max-connections-per-private-ip"
|
||||||
|
, "Max RPC connections per private and localhost IP permitted"
|
||||||
|
, DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP
|
||||||
|
};
|
||||||
|
|
||||||
|
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_max_connections = {
|
||||||
|
"rpc-max-connections"
|
||||||
|
, "Max RPC connections permitted"
|
||||||
|
, DEFAULT_RPC_MAX_CONNECTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_response_soft_limit = {
|
||||||
|
"rpc-response-soft-limit"
|
||||||
|
, "Max response bytes that can be queued, enforced at next response attempt"
|
||||||
|
, DEFAULT_RPC_SOFT_LIMIT_SIZE
|
||||||
|
};
|
||||||
} // namespace cryptonote
|
} // namespace cryptonote
|
||||||
|
@ -56,7 +56,6 @@ namespace cryptonote
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static const command_line::arg_descriptor<bool> arg_public_node;
|
|
||||||
static const command_line::arg_descriptor<std::string, false, true, 2> arg_rpc_bind_port;
|
static const command_line::arg_descriptor<std::string, false, true, 2> arg_rpc_bind_port;
|
||||||
static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port;
|
static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port;
|
||||||
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
|
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
|
||||||
@ -73,6 +72,10 @@ namespace cryptonote
|
|||||||
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty;
|
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty;
|
||||||
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits;
|
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits;
|
||||||
static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback;
|
static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback;
|
||||||
|
static const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_public_ip;
|
||||||
|
static const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_private_ip;
|
||||||
|
static const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections;
|
||||||
|
static const command_line::arg_descriptor<std::size_t> arg_rpc_response_soft_limit;
|
||||||
|
|
||||||
typedef epee::net_utils::connection_context_base connection_context;
|
typedef epee::net_utils::connection_context_base connection_context;
|
||||||
|
|
||||||
|
@ -129,6 +129,10 @@ namespace
|
|||||||
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
|
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
|
||||||
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
|
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
|
||||||
const command_line::arg_descriptor<bool> arg_no_initial_sync = {"no-initial-sync", "Skips the initial sync before listening for connections", false};
|
const command_line::arg_descriptor<bool> arg_no_initial_sync = {"no-initial-sync", "Skips the initial sync before listening for connections", false};
|
||||||
|
const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_public_ip = {"rpc-max-connections-per-public-ip", "Max RPC connections per public IP permitted", DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP};
|
||||||
|
const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_private_ip = {"rpc-max-connections-per-private-ip", "Max RPC connections per private and localhost IP permitted", DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP};
|
||||||
|
const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections = {"rpc-max-connections", "Max RPC connections permitted", DEFAULT_RPC_MAX_CONNECTIONS};
|
||||||
|
const command_line::arg_descriptor<std::size_t> arg_rpc_response_soft_limit = {"rpc-response-soft-limit", "Max response bytes that can be queued, enforced at next response attempt", DEFAULT_RPC_SOFT_LIMIT_SIZE};
|
||||||
|
|
||||||
constexpr const char default_rpc_username[] = "monero";
|
constexpr const char default_rpc_username[] = "monero";
|
||||||
|
|
||||||
@ -325,13 +329,30 @@ namespace tools
|
|||||||
|
|
||||||
check_background_mining();
|
check_background_mining();
|
||||||
|
|
||||||
|
const auto max_connections_public = command_line::get_arg(vm, arg_rpc_max_connections_per_public_ip);
|
||||||
|
const auto max_connections_private = command_line::get_arg(vm, arg_rpc_max_connections_per_private_ip);
|
||||||
|
const auto max_connections = command_line::get_arg(vm, arg_rpc_max_connections);
|
||||||
|
|
||||||
|
if (max_connections < max_connections_public)
|
||||||
|
{
|
||||||
|
MFATAL(arg_rpc_max_connections_per_public_ip.name << " is bigger than " << arg_rpc_max_connections.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (max_connections < max_connections_private)
|
||||||
|
{
|
||||||
|
MFATAL(arg_rpc_max_connections_per_private_ip.name << " is bigger than " << arg_rpc_max_connections.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_net_server.set_threads_prefix("RPC");
|
m_net_server.set_threads_prefix("RPC");
|
||||||
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
|
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
|
||||||
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
|
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
|
||||||
rng, std::move(bind_port), std::move(rpc_config->bind_ip),
|
rng, std::move(bind_port), std::move(rpc_config->bind_ip),
|
||||||
std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
|
std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
|
||||||
std::move(rpc_config->access_control_origins), std::move(http_login),
|
std::move(rpc_config->access_control_origins), std::move(http_login),
|
||||||
std::move(rpc_config->ssl_options)
|
std::move(rpc_config->ssl_options),
|
||||||
|
max_connections_public, max_connections_private, max_connections,
|
||||||
|
command_line::get_arg(vm, arg_rpc_response_soft_limit)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -4974,6 +4995,10 @@ int main(int argc, char** argv) {
|
|||||||
command_line::add_arg(desc_params, arg_wallet_dir);
|
command_line::add_arg(desc_params, arg_wallet_dir);
|
||||||
command_line::add_arg(desc_params, arg_prompt_for_password);
|
command_line::add_arg(desc_params, arg_prompt_for_password);
|
||||||
command_line::add_arg(desc_params, arg_no_initial_sync);
|
command_line::add_arg(desc_params, arg_no_initial_sync);
|
||||||
|
command_line::add_arg(desc_params, arg_rpc_max_connections_per_public_ip);
|
||||||
|
command_line::add_arg(desc_params, arg_rpc_max_connections_per_private_ip);
|
||||||
|
command_line::add_arg(desc_params, arg_rpc_max_connections);
|
||||||
|
command_line::add_arg(desc_params, arg_rpc_response_soft_limit);
|
||||||
command_line::add_arg(hidden_options, daemonizer::arg_non_interactive);
|
command_line::add_arg(hidden_options, daemonizer::arg_non_interactive);
|
||||||
|
|
||||||
daemonizer::init_options(hidden_options, desc_params);
|
daemonizer::init_options(hidden_options, desc_params);
|
||||||
|
@ -52,7 +52,7 @@ WALLET_DIRECTORY = builddir + "/functional-tests-directory"
|
|||||||
FUNCTIONAL_TESTS_DIRECTORY = builddir + "/tests/functional_tests"
|
FUNCTIONAL_TESTS_DIRECTORY = builddir + "/tests/functional_tests"
|
||||||
DIFFICULTY = 10
|
DIFFICULTY = 10
|
||||||
|
|
||||||
monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", str(DIFFICULTY), "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--zmq-pub", "monerod_zmq_pub", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir", "monerod_data_dir", "--log-level", "1"]
|
monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", str(DIFFICULTY), "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--zmq-pub", "monerod_zmq_pub", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir", "monerod_data_dir", "--log-level", "1", "--rpc-max-connections-per-private-ip", "100", "--rpc-max-connections", "100"]
|
||||||
monerod_extra = [
|
monerod_extra = [
|
||||||
["--offline"],
|
["--offline"],
|
||||||
["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"],
|
["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"],
|
||||||
|
@ -48,6 +48,7 @@ set(unit_tests_sources
|
|||||||
dns_resolver.cpp
|
dns_resolver.cpp
|
||||||
epee_boosted_tcp_server.cpp
|
epee_boosted_tcp_server.cpp
|
||||||
epee_levin_protocol_handler_async.cpp
|
epee_levin_protocol_handler_async.cpp
|
||||||
|
epee_http_server.cpp
|
||||||
epee_serialization.cpp
|
epee_serialization.cpp
|
||||||
epee_utils.cpp
|
epee_utils.cpp
|
||||||
expect.cpp
|
expect.cpp
|
||||||
|
200
tests/unit_tests/epee_http_server.cpp
Normal file
200
tests/unit_tests/epee_http_server.cpp
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// Copyright (c) 2014-2024, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <boost/asio/connect.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/beast/core.hpp>
|
||||||
|
#include <boost/beast/http.hpp>
|
||||||
|
#include <boost/beast/version.hpp>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "net/http_server_handlers_map2.h"
|
||||||
|
#include "net/http_server_impl_base.h"
|
||||||
|
#include "storages/portable_storage_template_helper.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr const std::size_t payload_size = 26 * 1024 * 1024;
|
||||||
|
constexpr const std::size_t max_private_ips = 25;
|
||||||
|
struct dummy
|
||||||
|
{
|
||||||
|
struct request
|
||||||
|
{
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response
|
||||||
|
{
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(payload)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
|
||||||
|
std::string payload;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string make_payload()
|
||||||
|
{
|
||||||
|
dummy::request body{};
|
||||||
|
const auto body_serialized = epee::serialization::store_t_to_binary(body);
|
||||||
|
return std::string{
|
||||||
|
reinterpret_cast<const char*>(body_serialized.data()),
|
||||||
|
body_serialized.size()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct http_server : epee::http_server_impl_base<http_server>
|
||||||
|
{
|
||||||
|
using connection_context = epee::net_utils::connection_context_base;
|
||||||
|
|
||||||
|
http_server()
|
||||||
|
: epee::http_server_impl_base<http_server>(),
|
||||||
|
dummy_size(payload_size)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
|
||||||
|
|
||||||
|
BEGIN_URI_MAP2()
|
||||||
|
MAP_URI_AUTO_BIN2("/dummy", on_dummy, dummy)
|
||||||
|
END_URI_MAP2()
|
||||||
|
|
||||||
|
bool on_dummy(const dummy::request&, dummy::response& res, const connection_context *ctx = NULL)
|
||||||
|
{
|
||||||
|
res.payload.resize(dummy_size.load(), 'f');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic<std::size_t> dummy_size;
|
||||||
|
};
|
||||||
|
} // anonymous
|
||||||
|
|
||||||
|
TEST(http_server, response_soft_limit)
|
||||||
|
{
|
||||||
|
namespace http = boost::beast::http;
|
||||||
|
|
||||||
|
http_server server{};
|
||||||
|
server.init(nullptr, "8080");
|
||||||
|
server.run(1, false);
|
||||||
|
|
||||||
|
boost::system::error_code error{};
|
||||||
|
boost::asio::io_context context{};
|
||||||
|
boost::asio::ip::tcp::socket stream{context};
|
||||||
|
stream.connect(
|
||||||
|
boost::asio::ip::tcp::endpoint{
|
||||||
|
boost::asio::ip::make_address("127.0.0.1"), 8080
|
||||||
|
},
|
||||||
|
error
|
||||||
|
);
|
||||||
|
EXPECT_FALSE(bool(error));
|
||||||
|
|
||||||
|
http::request<http::string_body> req{http::verb::get, "/dummy", 11};
|
||||||
|
req.set(http::field::host, "127.0.0.1");
|
||||||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||||
|
req.body() = make_payload();
|
||||||
|
req.prepare_payload();
|
||||||
|
http::write(stream, req, error);
|
||||||
|
EXPECT_FALSE(bool(error));
|
||||||
|
|
||||||
|
{
|
||||||
|
dummy::response payload{};
|
||||||
|
boost::beast::flat_buffer buffer;
|
||||||
|
http::response<http::basic_string_body<char>> res;
|
||||||
|
http::read(stream, buffer, res, error);
|
||||||
|
EXPECT_FALSE(bool(error));
|
||||||
|
EXPECT_EQ(200u, res.result_int());
|
||||||
|
EXPECT_TRUE(epee::serialization::load_t_from_binary(payload, res.body()));
|
||||||
|
EXPECT_EQ(payload_size, std::count(payload.payload.begin(), payload.payload.end(), 'f'));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!error)
|
||||||
|
http::write(stream, req, error);
|
||||||
|
server.send_stop_signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(http_server, private_ip_limit)
|
||||||
|
{
|
||||||
|
namespace http = boost::beast::http;
|
||||||
|
|
||||||
|
http_server server{};
|
||||||
|
server.dummy_size = 1;
|
||||||
|
server.init(nullptr, "8080");
|
||||||
|
server.run(1, false);
|
||||||
|
|
||||||
|
boost::system::error_code error{};
|
||||||
|
boost::asio::io_context context{};
|
||||||
|
|
||||||
|
http::request<http::string_body> req{http::verb::get, "/dummy", 11};
|
||||||
|
req.set(http::field::host, "127.0.0.1");
|
||||||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||||
|
req.body() = make_payload();
|
||||||
|
req.prepare_payload();
|
||||||
|
|
||||||
|
std::vector<boost::asio::ip::tcp::socket> streams{};
|
||||||
|
for (std::size_t i = 0; i < max_private_ips; ++i)
|
||||||
|
{
|
||||||
|
streams.emplace_back(context);
|
||||||
|
streams.back().connect(
|
||||||
|
boost::asio::ip::tcp::endpoint{
|
||||||
|
boost::asio::ip::make_address("127.0.0.1"), 8080
|
||||||
|
},
|
||||||
|
error
|
||||||
|
);
|
||||||
|
http::write(streams.back(), req, error);
|
||||||
|
EXPECT_FALSE(bool(error));
|
||||||
|
|
||||||
|
dummy::response payload{};
|
||||||
|
boost::beast::flat_buffer buffer;
|
||||||
|
http::response<http::basic_string_body<char>> res;
|
||||||
|
|
||||||
|
http::read(streams.back(), buffer, res, error);
|
||||||
|
EXPECT_FALSE(bool(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::socket stream{context};
|
||||||
|
stream.connect(
|
||||||
|
boost::asio::ip::tcp::endpoint{
|
||||||
|
boost::asio::ip::make_address("127.0.0.1"), 8080
|
||||||
|
},
|
||||||
|
error
|
||||||
|
);
|
||||||
|
bool failed = bool(error);
|
||||||
|
http::write(stream, req, error);
|
||||||
|
failed |= bool(error);
|
||||||
|
{
|
||||||
|
dummy::response payload{};
|
||||||
|
boost::beast::flat_buffer buffer;
|
||||||
|
http::response<http::basic_string_body<char>> res;
|
||||||
|
|
||||||
|
// make sure server ran async_accept code
|
||||||
|
http::read(stream, buffer, res, error);
|
||||||
|
}
|
||||||
|
failed |= bool(error);
|
||||||
|
EXPECT_TRUE(failed);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user