Skip to content

Commit 23a11d8

Browse files
committed
Merge pull request #9775
13ff355 Set response limits on http server connections (Lee *!* Clagett) 89fa3ed epee: update 'http_server_handlers_map2.h' macros to use fully qualified names (Jeffrey Ryan)
2 parents 70afa6b + 13ff355 commit 23a11d8

16 files changed

+429
-37
lines changed

contrib/epee/include/net/abstract_tcp_server2.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#define MONERO_DEFAULT_LOG_CATEGORY "net"
6565

6666
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000
67+
#define ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT 100 * 1024 * 1024
6768

6869
namespace epee
6970
{
@@ -169,6 +170,7 @@ namespace net_utils
169170
} read;
170171
struct {
171172
std::deque<epee::byte_slice> queue;
173+
std::size_t total_bytes;
172174
bool wait_consume;
173175
} write;
174176
};
@@ -267,11 +269,17 @@ namespace net_utils
267269
struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type
268270
{
269271
shared_state()
270-
: connection_basic_shared_state(), t_protocol_handler::config_type(), pfilter(nullptr), plimit(nullptr), stop_signal_sent(false)
272+
: connection_basic_shared_state(),
273+
t_protocol_handler::config_type(),
274+
pfilter(nullptr),
275+
plimit(nullptr),
276+
response_soft_limit(ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT),
277+
stop_signal_sent(false)
271278
{}
272279

273280
i_connection_filter* pfilter;
274281
i_connection_limit* plimit;
282+
std::size_t response_soft_limit;
275283
bool stop_signal_sent;
276284
};
277285

@@ -378,6 +386,7 @@ namespace net_utils
378386

379387
void set_connection_filter(i_connection_filter* pfilter);
380388
void set_connection_limit(i_connection_limit* plimit);
389+
void set_response_soft_limit(std::size_t limit);
381390

382391
void set_default_remote(epee::net_utils::network_address remote)
383392
{

contrib/epee/include/net/abstract_tcp_server2.inl

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,12 @@ namespace net_utils
498498
if (m_state.socket.cancel_write) {
499499
m_state.socket.cancel_write = false;
500500
m_state.data.write.queue.clear();
501+
m_state.data.write.total_bytes = 0;
501502
state_status_check();
502503
}
503504
else if (ec.value()) {
504505
m_state.data.write.queue.clear();
506+
m_state.data.write.total_bytes = 0;
505507
interrupt();
506508
}
507509
else {
@@ -526,8 +528,11 @@ namespace net_utils
526528

527529
start_timer(get_default_timeout(), true);
528530
}
529-
assert(bytes_transferred == m_state.data.write.queue.back().size());
531+
const std::size_t byte_count = m_state.data.write.queue.back().size();
532+
assert(bytes_transferred == byte_count);
530533
m_state.data.write.queue.pop_back();
534+
m_state.data.write.total_bytes -=
535+
std::min(m_state.data.write.total_bytes, byte_count);
531536
m_state.condition.notify_all();
532537
start_write();
533538
}
@@ -671,8 +676,9 @@ namespace net_utils
671676
return;
672677
if (m_state.timers.throttle.out.wait_expire)
673678
return;
674-
if (m_state.socket.wait_write)
675-
return;
679+
// \NOTE See on_terminating() comments
680+
//if (m_state.socket.wait_write)
681+
// return;
676682
if (m_state.socket.wait_shutdown)
677683
return;
678684
if (m_state.protocol.wait_init)
@@ -730,8 +736,13 @@ namespace net_utils
730736
return;
731737
if (m_state.timers.throttle.out.wait_expire)
732738
return;
733-
if (m_state.socket.wait_write)
734-
return;
739+
// Writes cannot be canceled due to `async_write` being a "composed"
740+
// handler. ASIO has new cancellation routines, not available in 1.66, to
741+
// handle this situation. The problem is that if cancel is called after an
742+
// intermediate handler is queued, the op will not check the cancel flag in
743+
// our code, and will instead queue up another write.
744+
//if (m_state.socket.wait_write)
745+
// return;
735746
if (m_state.socket.wait_shutdown)
736747
return;
737748
if (m_state.protocol.wait_init)
@@ -758,6 +769,8 @@ namespace net_utils
758769
std::lock_guard<std::mutex> guard(m_state.lock);
759770
if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake)
760771
return false;
772+
if (std::numeric_limits<std::size_t>::max() - m_state.data.write.total_bytes < message.size())
773+
return false;
761774

762775
// Wait for the write queue to fall below the max. If it doesn't after a
763776
// randomized delay, drop the connection.
@@ -775,7 +788,14 @@ namespace net_utils
775788
std::uniform_int_distribution<>(5000, 6000)(rng)
776789
);
777790
};
778-
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
791+
792+
// The bytes check intentionally does not include incoming message size.
793+
// This allows for a soft overflow; a single http response will never fail
794+
// this check, but multiple responses could. Clients can avoid this case
795+
// by reading the entire response before making another request. P2P
796+
// should never hit the MAX_BYTES check (when using default values).
797+
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
798+
m_state.data.write.total_bytes <= static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit)
779799
return true;
780800
m_state.data.write.wait_consume = true;
781801
bool success = m_state.condition.wait_for(
@@ -784,14 +804,23 @@ namespace net_utils
784804
[this]{
785805
return (
786806
m_state.status != status_t::RUNNING ||
787-
m_state.data.write.queue.size() <=
788-
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT
807+
(
808+
m_state.data.write.queue.size() <=
809+
ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
810+
m_state.data.write.total_bytes <=
811+
static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit
812+
)
789813
);
790814
}
791815
);
792816
m_state.data.write.wait_consume = false;
793817
if (!success) {
794-
terminate();
818+
// synchronize with intermediate writes on `m_strand`
819+
auto self = connection<T>::shared_from_this();
820+
boost::asio::post(m_strand, [this, self] {
821+
std::lock_guard<std::mutex> guard(m_state.lock);
822+
terminate();
823+
});
795824
return false;
796825
}
797826
else
@@ -817,7 +846,9 @@ namespace net_utils
817846
) {
818847
if (!wait_consume())
819848
return false;
849+
const std::size_t byte_count = message.size();
820850
m_state.data.write.queue.emplace_front(std::move(message));
851+
m_state.data.write.total_bytes += byte_count;
821852
start_write();
822853
}
823854
else {
@@ -827,6 +858,7 @@ namespace net_utils
827858
m_state.data.write.queue.emplace_front(
828859
message.take_slice(CHUNK_SIZE)
829860
);
861+
m_state.data.write.total_bytes += m_state.data.write.queue.front().size();
830862
start_write();
831863
}
832864
}
@@ -1363,6 +1395,13 @@ namespace net_utils
13631395
}
13641396
//---------------------------------------------------------------------------------
13651397
template<class t_protocol_handler>
1398+
void boosted_tcp_server<t_protocol_handler>::set_response_soft_limit(const std::size_t limit)
1399+
{
1400+
assert(m_state != nullptr); // always set in constructor
1401+
m_state->response_soft_limit = limit;
1402+
}
1403+
//---------------------------------------------------------------------------------
1404+
template<class t_protocol_handler>
13661405
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs)
13671406
{
13681407
TRY_ENTRY();

contrib/epee/include/net/http_protocol_handler.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include <boost/optional/optional.hpp>
3434
#include <string>
35+
#include <unordered_map>
3536
#include "net_utils_base.h"
3637
#include "http_auth.h"
3738
#include "http_base.h"
@@ -54,8 +55,13 @@ namespace net_utils
5455
{
5556
std::string m_folder;
5657
std::vector<std::string> m_access_control_origins;
58+
std::unordered_map<std::string, std::size_t> m_connections;
5759
boost::optional<login> m_user;
5860
size_t m_max_content_length{std::numeric_limits<size_t>::max()};
61+
std::size_t m_connection_count{0};
62+
std::size_t m_max_public_ip_connections{3};
63+
std::size_t m_max_private_ip_connections{25};
64+
std::size_t m_max_connections{100};
5965
critical_section m_lock;
6066
};
6167

@@ -70,7 +76,7 @@ namespace net_utils
7076
typedef http_server_config config_type;
7177

7278
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
73-
virtual ~simple_http_connection_handler(){}
79+
virtual ~simple_http_connection_handler();
7480

7581
bool release_protocol()
7682
{
@@ -86,10 +92,7 @@ namespace net_utils
8692
{
8793
return true;
8894
}
89-
bool after_init_connection()
90-
{
91-
return true;
92-
}
95+
bool after_init_connection();
9396
virtual bool handle_recv(const void* ptr, size_t cb);
9497
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
9598

@@ -146,6 +149,7 @@ namespace net_utils
146149
protected:
147150
i_service_endpoint* m_psnd_hndlr;
148151
t_connection_context& m_conn_context;
152+
bool m_initialized;
149153
};
150154

151155
template<class t_connection_context>
@@ -212,10 +216,6 @@ namespace net_utils
212216
}
213217
void handle_qued_callback()
214218
{}
215-
bool after_init_connection()
216-
{
217-
return true;
218-
}
219219

220220
private:
221221
//simple_http_connection_handler::config_type m_stub_config;

contrib/epee/include/net/http_protocol_handler.inl

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,46 @@ namespace net_utils
208208
m_newlines(0),
209209
m_bytes_read(0),
210210
m_psnd_hndlr(psnd_hndlr),
211-
m_conn_context(conn_context)
211+
m_conn_context(conn_context),
212+
m_initialized(false)
212213
{
213214

214215
}
215216
//--------------------------------------------------------------------------------------------
217+
template<class t_connection_context>
218+
simple_http_connection_handler<t_connection_context>::~simple_http_connection_handler()
219+
{
220+
try
221+
{
222+
if (m_initialized)
223+
{
224+
CRITICAL_REGION_LOCAL(m_config.m_lock);
225+
if (m_config.m_connection_count)
226+
--m_config.m_connection_count;
227+
auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
228+
if (elem != m_config.m_connections.end())
229+
{
230+
if (elem->second == 1 || elem->second == 0)
231+
m_config.m_connections.erase(elem);
232+
else
233+
--(elem->second);
234+
}
235+
}
236+
}
237+
catch (...)
238+
{}
239+
}
240+
//--------------------------------------------------------------------------------------------
241+
template<class t_connection_context>
242+
bool simple_http_connection_handler<t_connection_context>::after_init_connection()
243+
{
244+
CRITICAL_REGION_LOCAL(m_config.m_lock);
245+
++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
246+
++m_config.m_connection_count;
247+
m_initialized = true;
248+
return true;
249+
}
250+
//--------------------------------------------------------------------------------------------
216251
template<class t_connection_context>
217252
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
218253
{

contrib/epee/include/net/http_server_handlers_map2.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
else if((query_info.m_URI == s_pattern) && (cond)) \
7272
{ \
7373
handled = true; \
74-
uint64_t ticks = misc_utils::get_tick_count(); \
74+
uint64_t ticks = epee::misc_utils::get_tick_count(); \
7575
boost::value_initialized<command_type::request> req; \
7676
bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
7777
if (!parse_res) \
@@ -107,7 +107,7 @@
107107
else if(query_info.m_URI == s_pattern) \
108108
{ \
109109
handled = true; \
110-
uint64_t ticks = misc_utils::get_tick_count(); \
110+
uint64_t ticks = epee::misc_utils::get_tick_count(); \
111111
boost::value_initialized<command_type::request> req; \
112112
bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), epee::strspan<uint8_t>(query_info.m_body)); \
113113
if (!parse_res) \
@@ -117,7 +117,7 @@
117117
response_info.m_response_comment = "Bad request"; \
118118
return true; \
119119
} \
120-
uint64_t ticks1 = misc_utils::get_tick_count(); \
120+
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
121121
boost::value_initialized<command_type::response> resp;\
122122
MINFO(m_conn_context << "calling " << s_pattern); \
123123
bool res = false; \
@@ -129,7 +129,7 @@
129129
response_info.m_response_comment = "Internal Server Error"; \
130130
return true; \
131131
} \
132-
uint64_t ticks2 = misc_utils::get_tick_count(); \
132+
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
133133
epee::byte_slice buffer; \
134134
epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), buffer, 64 * 1024); \
135135
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \

0 commit comments

Comments
 (0)