77#include < fmt/ranges.h>
88#include < boost/algorithm/string.hpp>
99
10+ #include < userver/engine/task/cancel.hpp>
1011#include < userver/utest/assert_macros.hpp>
1112#include < userver/utils/numeric_cast.hpp>
1213#include < userver/utils/text.hpp>
@@ -20,15 +21,16 @@ const std::string kCrlf = "\r\n";
2021} // namespace
2122
2223MockRedisServerBase::MockRedisServerBase (int port)
23- : acceptor_(io_service_ )
24+ : client_tasks_( )
2425{
25- acceptor_.open (io::ip::tcp::v4 ());
26- const boost::asio::ip::tcp::acceptor::reuse_address option (true );
27- acceptor_.set_option (option);
28- acceptor_.bind (io::ip::tcp::endpoint (io::ip::tcp::v4 (), port));
29- acceptor_.listen ();
26+ auto addr = engine::io::Sockaddr::MakeIPv4LoopbackAddress ();
27+ addr.SetPort (port);
28+ listener_ = engine::io::Socket{engine::io::AddrDomain::kInet , engine::io::SocketType::kStream };
29+ listener_.Bind (addr);
30+ port_ = listener_.Getsockname ().Port ();
31+ listener_.Listen ();
3032
31- thread_ = std::thread (&MockRedisServerBase::Work, this );
33+ listener_task_ = engine::AsyncNoTracing ([ this ] { AcceptLoop (); } );
3234}
3335
3436MockRedisServerBase::~MockRedisServerBase () { Stop (); }
@@ -45,19 +47,14 @@ void MockRedisServerBase::SendReplyData(ConnectionPtr connection, const storages
4547 SendReply (connection, ReplyDataToRedisProto (reply_data));
4648}
4749
48- int MockRedisServerBase::GetPort () const { return acceptor_. local_endpoint (). port () ; }
50+ int MockRedisServerBase::GetPort () const { return port_ ; }
4951
50- void MockRedisServerBase::Stop () {
51- io_service_.stop ();
52- if (thread_.joinable ()) {
53- thread_.join ();
54- }
55- }
52+ void MockRedisServerBase::Stop () { listener_task_.SyncCancel (); }
5653
5754void MockRedisServerBase::SendReply (ConnectionPtr connection, const std::string& reply) {
5855 LOG_DEBUG () << " reply: " << reply;
59- // TODO: async?
60- io::write (connection-> socket , io::buffer (reply. c_str (), reply.size () ));
56+ const auto size = connection-> socket . SendAll (reply. data (), reply. size (), {});
57+ UASSERT (size == reply.size ());
6158}
6259
6360std::string MockRedisServerBase::ReplyDataToRedisProto (const storages::redis::ReplyData& reply_data) {
@@ -84,62 +81,48 @@ std::string MockRedisServerBase::ReplyDataToRedisProto(const storages::redis::Re
8481 }
8582}
8683
87- void MockRedisServerBase::Accept () {
88- auto connection = std::make_shared<Connection>(io_service_);
89- connection
90- ->reader = std::unique_ptr<redisReader, decltype (&redisReaderFree)>(redisReaderCreate (), &redisReaderFree);
91- acceptor_.async_accept (connection->socket , [connection, this ](auto item) {
92- OnAccept (connection, std::move (item));
93- Accept ();
94- });
95- }
96-
97- void MockRedisServerBase::Work () {
98- Accept ();
99- UEXPECT_NO_THROW (io_service_.run ());
100- }
101-
102- void MockRedisServerBase::OnAccept (ConnectionPtr connection, boost::system::error_code ec) {
103- LOG_DEBUG () << " accept(2): " << ec;
104- OnConnected (connection);
105- DoRead (connection);
106- }
107-
108- void MockRedisServerBase::OnRead (ConnectionPtr connection, boost::system::error_code ec, size_t count) {
109- LOG_DEBUG () << " read " << ec << " count=" << count;
110- if (ec) {
111- LOG_DEBUG () << " read(2) error: " << ec;
112- OnDisconnected (connection);
113- connection->socket .close ();
114- return ;
115- }
84+ void MockRedisServerBase::AcceptLoop () {
85+ while (!engine::current_task::ShouldCancel ()) {
86+ engine::io::Socket client_socket = listener_.Accept ({});
87+ if (engine::current_task::ShouldCancel ()) {
88+ return ;
89+ }
11690
117- auto ret = redisReaderFeed (connection->reader .get (), connection->data .data (), count);
118- if (ret != REDIS_OK ) {
119- throw std::runtime_error (" redisReaderFeed() returned error: " + std::string (connection->reader ->errstr ));
91+ auto connection = std::make_shared<Connection>(std::move (client_socket));
92+ connection
93+ ->reader = std::unique_ptr<redisReader, decltype (&redisReaderFree)>(redisReaderCreate (), &redisReaderFree);
94+ OnConnected (connection);
95+ client_tasks_.AsyncDetach (" mock-redis-client" , [this , connection] { HandleConnection (connection); });
12096 }
97+ }
12198
122- void * hiredis_reply = nullptr ;
123- while (redisReaderGetReply (connection->reader .get (), &hiredis_reply) == REDIS_OK && hiredis_reply) {
124- auto reply = std::make_shared<
125- storages::redis::Reply>(" " , static_cast <redisReply*>(hiredis_reply), storages::redis::ReplyStatus::kOk );
126- LOG_DEBUG () << " command: " << reply->data .ToDebugString ();
99+ void MockRedisServerBase::HandleConnection (ConnectionPtr connection) {
100+ while (!engine::current_task::ShouldCancel ()) {
101+ const auto count = connection->socket .RecvSome (connection->data .data (), connection->data .size (), {});
102+ LOG_DEBUG () << " read count=" << count;
103+ if (count == 0 ) {
104+ LOG_DEBUG () << " read: connection closed" ;
105+ OnDisconnected (connection);
106+ connection->socket .Close ();
107+ return ;
108+ }
127109
128- OnCommand (connection, reply );
129- freeReplyObject (hiredis_reply);
130- hiredis_reply = nullptr ;
131- }
110+ auto ret = redisReaderFeed (connection-> reader . get (), connection-> data . data (), count );
111+ if (ret != REDIS_OK ) {
112+ throw std::runtime_error ( " redisReaderFeed() returned error: " + std::string (connection-> reader -> errstr )) ;
113+ }
132114
133- DoRead (connection);
134- }
115+ void * hiredis_reply = nullptr ;
116+ while (redisReaderGetReply (connection->reader .get (), &hiredis_reply) == REDIS_OK && hiredis_reply) {
117+ auto reply = std::make_shared<
118+ storages::redis::Reply>(" " , static_cast <redisReply*>(hiredis_reply), storages::redis::ReplyStatus::kOk );
119+ LOG_DEBUG () << " command: " << reply->data .ToDebugString ();
135120
136- void MockRedisServerBase::DoRead (ConnectionPtr connection) {
137- connection->socket .async_read_some (
138- io::buffer (connection->data ),
139- [connection, this ](boost::system::error_code error_code, size_t count) {
140- OnRead (connection, error_code, count);
121+ OnCommand (connection, reply);
122+ freeReplyObject (hiredis_reply);
123+ hiredis_reply = nullptr ;
141124 }
142- );
125+ }
143126}
144127
145128MockRedisServer::~MockRedisServer () { Stop (); }
@@ -361,7 +344,7 @@ void MockRedisServer::RegisterHandlerFunc(
361344 const std::vector<std::string>& args_prefix,
362345 HandlerFunc handler
363346) {
364- const std::lock_guard<std::mutex> lock ( mutex_) ;
347+ const std::lock_guard lock{ mutex_} ;
365348 AddHandlerFunc (handlers_[boost::algorithm::to_lower_copy (cmd)], args_prefix, std::move (handler));
366349}
367350
@@ -410,16 +393,16 @@ MockRedisServer::HandlerPtr MockRedisServer::DoRegisterTimeoutHandler(
410393}
411394
412395size_t MockRedisServer::Handler::GetReplyCount () const {
413- const std::lock_guard<std::mutex> lock ( mutex_) ;
396+ const std::lock_guard lock{ mutex_} ;
414397 return reply_count_;
415398}
416399
417400void MockRedisServer::Handler::AccountReply () {
418401 {
419- const std::lock_guard<std::mutex> lock ( mutex_) ;
402+ const std::lock_guard lock{ mutex_} ;
420403 ++reply_count_;
421404 }
422- cv_.notify_one ();
405+ cv_.NotifyOne ();
423406}
424407
425408MockRedisServer::CommonMasterSlaveInfo::CommonMasterSlaveInfo (
0 commit comments