Compare commits

...

2 Commits

Author SHA1 Message Date
efb49b8708 redundant checks 2024-03-03 12:53:59 -08:00
0aab173d94 comments 2024-03-03 12:27:36 -08:00
8 changed files with 192 additions and 67 deletions

View File

@ -10,7 +10,7 @@ Connection::Connection(boost::asio::io_context & io)
{ {
} }
auto Connection::writer_() -> void auto Connection::writer_immediate() -> void
{ {
std::vector<boost::asio::const_buffer> buffers; std::vector<boost::asio::const_buffer> buffers;
buffers.reserve(write_strings_.size()); buffers.reserve(write_strings_.size());
@ -45,14 +45,14 @@ auto Connection::writer() -> void
{ {
if (not self->write_strings_.empty()) if (not self->write_strings_.empty())
{ {
self->writer_(); self->writer_immediate();
} }
} }
}); });
} }
else else
{ {
writer_(); writer_immediate();
} }
} }

View File

@ -59,7 +59,7 @@ private:
EventDispatcher dispatcher_; EventDispatcher dispatcher_;
auto writer() -> void; auto writer() -> void;
auto writer_() -> void; auto writer_immediate() -> void;
public: public:
Connection(boost::asio::io_context & io); Connection(boost::asio::io_context & io);

View File

@ -1,48 +1,59 @@
#include "self_thread.hpp" #include "self_thread.hpp"
#include <boost/log/trivial.hpp>
#include "connection.hpp" #include "connection.hpp"
#include "ircmsg.hpp" #include "ircmsg.hpp"
#include "irc_parse_thread.hpp" #include "irc_parse_thread.hpp"
auto SelfThread::start(Connection& connection) -> std::shared_ptr<SelfThread> auto SelfThread::on_welcome(IrcMsg const& irc) -> void
{ {
auto thread = std::make_shared<SelfThread>(connection); nickname_ = irc.args[0];
}
connection.add_listener<IrcMsgEvent>([thread](IrcMsgEvent& event) auto SelfThread::on_nick(IrcMsg const& irc) -> void
{
if (is_my_mask(irc.source))
{ {
switch (event.command) nickname_ = irc.args[0];
{
// Learn nickname from 001
case IrcCommand::RPL_WELCOME:
thread->nickname_ = event.irc.args[0];
break;
// Track changes to our nickname
case IrcCommand::NICK:
{
auto const bang = event.irc.source.find('!');
if (bang != std::string::npos
&& thread->nickname_ == event.irc.source.substr(0, bang)
)
{
thread->nickname_ = event.irc.args[0];
} }
break; }
auto SelfThread::on_umodeis(IrcMsg const& irc) -> void
{
mode_ = irc.args[1];
}
auto SelfThread::on_join(IrcMsg const& irc) -> void
{
if (is_my_mask(irc.source))
{
channels_.insert(std::string{irc.args[0]});
} }
}
// Re-establish user modes auto SelfThread::on_kick(IrcMsg const& irc) -> void
case IrcCommand::RPL_UMODEIS: {
thread->mode_ = event.irc.args[1]; if (is_my_nick(irc.args[1]))
break; {
channels_.erase(std::string{irc.args[0]});
}
}
// Interpret self mode changes auto SelfThread::on_part(IrcMsg const& irc) -> void
case IrcCommand::MODE: {
if (2 == event.irc.args.size() if (is_my_mask(irc.source))
&& thread->nickname_ == event.irc.args[0] {
) channels_.erase(std::string{irc.args[0]});
}
}
auto SelfThread::on_mode(IrcMsg const& irc) -> void
{
if (is_my_nick(irc.args[0]))
{ {
auto polarity = true; auto polarity = true;
for (char const c : event.irc.args[1]) for (char const c : irc.args[1])
{ {
switch (c) switch (c)
{ {
@ -55,23 +66,91 @@ auto SelfThread::start(Connection& connection) -> std::shared_ptr<SelfThread>
default: default:
if (polarity) if (polarity)
{ {
thread->mode_ += c; mode_ += c;
} }
else else
{ {
auto const ix = thread->mode_.find(c); auto const ix = mode_.find(c);
if (ix != std::string::npos) if (ix != std::string::npos)
{ {
thread->mode_.erase(ix, 1); mode_.erase(ix, 1);
} }
} }
break; break;
} }
} }
} }
}
auto SelfThread::start(Connection& connection) -> std::shared_ptr<SelfThread>
{
auto thread = std::make_shared<SelfThread>(connection);
connection.add_listener<IrcMsgEvent>([thread](IrcMsgEvent& event)
{
switch (event.command)
{
// Learn nickname from 001
case IrcCommand::RPL_WELCOME:
thread->on_welcome(event.irc);
break;
// Track changes to our nickname
case IrcCommand::NICK:
thread->on_nick(event.irc);
break;
// Re-establish user modes
case IrcCommand::RPL_UMODEIS:
thread->on_umodeis(event.irc);
break;
case IrcCommand::JOIN:
thread->on_join(event.irc);
break;
case IrcCommand::KICK:
thread->on_kick(event.irc);
break;
case IrcCommand::PART:
thread->on_part(event.irc);
break;
// Interpret self mode changes
case IrcCommand::MODE:
thread->on_mode(event.irc);
break;
default: break; default: break;
} }
}); });
return thread; return thread;
} }
auto SelfThread::get_my_nickname() const -> std::string const&
{
return nickname_;
}
auto SelfThread::get_my_mode() const -> std::string const&
{
return mode_;
}
auto SelfThread::get_my_channels() const -> std::unordered_set<std::string> const&
{
return channels_;
}
auto SelfThread::is_my_nick(std::string_view nick) const -> bool
{
return nick == nickname_;
}
auto SelfThread::is_my_mask(std::string_view mask) const -> bool
{
auto const bang = mask.find('!');
return bang != std::string_view::npos && nickname_ == mask.substr(0, bang);
}

View File

@ -1,17 +1,39 @@
#pragma once #pragma once
#include <string>
#include <memory> #include <memory>
#include <string>
#include <unordered_set>
struct Connection; struct Connection;
struct IrcMsg;
/**
* @brief Thread to track this connection's identity, and IRC state.
*
*/
class SelfThread class SelfThread
{ {
Connection& connection_; Connection& connection_;
std::string nickname_; std::string nickname_;
std::string mode_; std::string mode_;
std::unordered_set<std::string> channels_;
auto on_welcome(IrcMsg const& irc) -> void;
auto on_nick(IrcMsg const& irc) -> void;
auto on_umodeis(IrcMsg const& irc) -> void;
auto on_join(IrcMsg const& irc) -> void;
auto on_kick(IrcMsg const& irc) -> void;
auto on_part(IrcMsg const& irc) -> void;
auto on_mode(IrcMsg const& irc) -> void;
public: public:
SelfThread(Connection& connection) : connection_{connection} {} SelfThread(Connection& connection) : connection_{connection} {}
static auto start(Connection&) -> std::shared_ptr<SelfThread>; static auto start(Connection&) -> std::shared_ptr<SelfThread>;
auto get_my_nickname() const -> std::string const&;
auto get_my_mode() const -> std::string const&;
auto get_my_channels() const -> std::unordered_set<std::string> const&;
auto is_my_nick(std::string_view nick) const -> bool;
auto is_my_mask(std::string_view nick) const -> bool;
}; };

View File

@ -31,7 +31,7 @@ struct SnotePattern
std::regex regex; std::regex regex;
}; };
SnotePattern const patterns[] = SnotePattern static const patterns[] =
{ {
{SnoteTag::ClientConnecting, {SnoteTag::ClientConnecting,
R"(^Client connecting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"}, R"(^Client connecting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"},
@ -79,7 +79,7 @@ SnotePattern const patterns[] =
"^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"}, "^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"},
}; };
auto setup_database() -> hs_database_t* static auto setup_database() -> hs_database_t*
{ {
auto const n = std::size(patterns); auto const n = std::size(patterns);
std::vector<char const*> expressions; std::vector<char const*> expressions;

View File

@ -58,7 +58,10 @@ struct SnoteThread
auto operator()(hs_scratch * scratch) const -> void; auto operator()(hs_scratch * scratch) const -> void;
}; };
/// @brief Database of server notice patterns
std::unique_ptr<hs_database, DbDeleter> db_; std::unique_ptr<hs_database, DbDeleter> db_;
/// @brief HyperScan scratch space
std::unique_ptr<hs_scratch, ScratchDeleter> scratch_; std::unique_ptr<hs_scratch, ScratchDeleter> scratch_;
static auto start(Connection& connection) -> std::shared_ptr<SnoteThread>; static auto start(Connection& connection) -> std::shared_ptr<SnoteThread>;

View File

@ -9,24 +9,22 @@
#include <chrono> #include <chrono>
#include <memory> #include <memory>
using namespace std::chrono_literals;
WatchdogThread::WatchdogThread(Connection& connection) WatchdogThread::WatchdogThread(Connection& connection)
: connection_{connection} : connection_{connection}
, timer_{connection.get_executor()} , timer_{connection.get_executor()}
, tried_ping{false} , stalled_{false}
{ {
} }
auto WatchdogThread::on_activity() -> void auto WatchdogThread::on_activity() -> void
{ {
tried_ping = false; stalled_ = false;
timer_.expires_from_now(30s); timer_.expires_from_now(WatchdogThread::TIMEOUT);
} }
auto WatchdogThread::timeout_token() auto WatchdogThread::start_timer()
{ {
return [weak = weak_from_this()](auto const& error) timer_.async_wait([weak = weak_from_this()](auto const& error)
{ {
if (not error) if (not error)
{ {
@ -35,28 +33,28 @@ auto WatchdogThread::timeout_token()
self->on_timeout(); self->on_timeout();
} }
} }
}; });
} }
auto WatchdogThread::on_timeout() -> void auto WatchdogThread::on_timeout() -> void
{ {
if (tried_ping) if (stalled_)
{ {
connection_.close(); connection_.close();
} }
else else
{ {
send_ping(connection_, "watchdog"); send_ping(connection_, "watchdog");
tried_ping = true; stalled_ = true;
timer_.expires_from_now(30s); timer_.expires_from_now(WatchdogThread::TIMEOUT);
timer_.async_wait(timeout_token()); start_timer();
} }
} }
auto WatchdogThread::on_connect() -> void auto WatchdogThread::on_connect() -> void
{ {
on_activity(); on_activity();
timer_.async_wait(timeout_token()); start_timer();
} }
auto WatchdogThread::on_disconnect() -> void auto WatchdogThread::on_disconnect() -> void

View File

@ -2,20 +2,43 @@
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <memory> #include <memory>
class Connection; class Connection;
/**
* @brief Watch for connection activity and disconnect on stall
*
* The thread will send a ping if no message is received in the
* last TIMEOUT seconds. After another period of no messages
* the thread will disconnect the connection.
*
*/
class WatchdogThread : std::enable_shared_from_this<WatchdogThread> class WatchdogThread : std::enable_shared_from_this<WatchdogThread>
{ {
Connection& connection_; Connection& connection_;
boost::asio::steady_timer timer_; boost::asio::steady_timer timer_;
bool tried_ping;
/// @brief Set true and ping sent and false when reply received
bool stalled_;
const std::chrono::steady_clock::duration TIMEOUT = std::chrono::seconds{30};
/// @brief Start the timer
/// @return
auto start_timer();
/// @brief
auto on_activity() -> void; auto on_activity() -> void;
auto timeout_token();
/// @brief
auto on_timeout() -> void; auto on_timeout() -> void;
/// @brief callback for ConnectEvent event
auto on_connect() -> void; auto on_connect() -> void;
/// @brief callback for DisconnectEvent event
auto on_disconnect() -> void; auto on_disconnect() -> void;
public: public: