M src/bridge/bridge.cpp => src/bridge/bridge.cpp +2 -2
@@ 52,7 52,7 @@ void Bridge::clean()
while (it != this->irc_clients.end())
{
IrcClient* client = it->second.get();
- if (!client->is_connected())
+ if (!client->is_connected() && !client->is_connecting())
it = this->irc_clients.erase(it);
else
++it;
@@ 249,7 249,7 @@ std::string Bridge::get_own_nick(const Iid& iid)
return "";
}
-size_t Bridge::connected_clients() const
+size_t Bridge::active_clients() const
{
return this->irc_clients.size();
}
M src/bridge/bridge.hpp => src/bridge/bridge.hpp +3 -3
@@ 105,9 105,9 @@ public:
*/
std::string get_own_nick(const Iid& iid);
/**
- * Get the number of server to which this bridge is connected.
+ * Get the number of server to which this bridge is connected or connecting.
*/
- size_t connected_clients() const;
+ size_t active_clients() const;
private:
/**
@@ 125,7 125,7 @@ private:
* The JID of the user associated with this bridge. Messages from/to this
* JID are only managed by this bridge.
*/
- std::string user_jid;
+ const std::string user_jid;
/**
* One IrcClient for each IRC server we need to be connected to.
* The pointer is shared by the bridge and the poller.
M src/irc/irc_client.cpp => src/irc/irc_client.cpp +7 -4
@@ 29,10 29,13 @@ void IrcClient::start()
{
this->bridge->send_xmpp_message(this->hostname, "", std::string("Connecting to ") +
this->hostname + ":" + "6667");
- std::pair<bool, std::string> res = this->connect(this->hostname, "6667");
- if (!res.first)
- this->bridge->send_xmpp_message(this->hostname, "",
- std::string("Connection failed: ") + res.second);
+ this->connect(this->hostname, "6667");
+}
+
+void IrcClient::on_connection_failed(const std::string& reason)
+{
+ this->bridge->send_xmpp_message(this->hostname, "",
+ std::string("Connection failed: ") + reason);
}
void IrcClient::on_connected()
M src/irc/irc_client.hpp => src/irc/irc_client.hpp +4 -0
@@ 30,6 30,10 @@ public:
*/
void start();
/**
+ * Called when the connection to the server cannot be established
+ */
+ void on_connection_failed(const std::string& reason) override final;
+ /**
* Called when successfully connected to the server
*/
void on_connected() override final;
M src/main.cpp => src/main.cpp +5 -6
@@ 66,11 66,6 @@ int main(int ac, char** av)
Poller p;
p.add_socket_handler(xmpp_component);
- if (!xmpp_component->start())
- {
- log_info("Exiting");
- return -1;
- }
// Install the signals used to exit the process cleanly, or reload the
// config
@@ 93,8 88,10 @@ int main(int ac, char** av)
sigaction(SIGUSR1, &on_sigusr, nullptr);
sigaction(SIGUSR2, &on_sigusr, nullptr);
+ xmpp_component->start();
+
const std::chrono::milliseconds timeout(-1);
- while (p.poll(timeout) != -1 || !exiting)
+ while (p.poll(timeout) != -1)
{
// Check for empty irc_clients (not connected, or with no joined
// channel) and remove them
@@ 119,6 116,8 @@ int main(int ac, char** av)
}
// If the only existing connection is the one to the XMPP component:
// close the XMPP stream.
+ if (exiting && xmpp_component->is_connecting())
+ xmpp_component->close();
if (exiting && p.size() == 1 && xmpp_component->is_document_open())
xmpp_component->close_document();
}
M src/network/poller.cpp => src/network/poller.cpp +9 -3
@@ 155,8 155,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
else if (this->fds[i].revents & POLLIN)
{
auto socket_handler = this->socket_handlers.at(this->fds[i].fd);
- if (socket_handler->is_connected())
- socket_handler->on_recv();
+ socket_handler->on_recv();
nb_events--;
}
else if (this->fds[i].revents & POLLOUT)
@@ 164,6 163,8 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
auto socket_handler = this->socket_handlers.at(this->fds[i].fd);
if (socket_handler->is_connected())
socket_handler->on_send();
+ else
+ socket_handler->connect();
nb_events--;
}
}
@@ 185,7 186,12 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
if (revents[i].events & EPOLLIN)
socket_handler->on_recv();
if (revents[i].events & EPOLLOUT)
- socket_handler->on_send();
+ {
+ if (socket_handler->is_connected())
+ socket_handler->on_send();
+ else
+ socket_handler->connect();
+ }
}
return nb_events;
#endif
M src/network/socket_handler.cpp => src/network/socket_handler.cpp +49 -8
@@ 10,6 10,7 @@
#include <unistd.h>
#include <errno.h>
#include <cstring>
+#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
@@ 17,18 18,38 @@
SocketHandler::SocketHandler():
poller(nullptr),
- connected(false)
+ connected(false),
+ connecting(false)
+{
+ this->init_socket();
+}
+
+void SocketHandler::init_socket()
{
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
throw std::runtime_error("Could not create socket");
int optval = 1;
if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1)
log_warning("Failed to enable TCP keepalive on socket: " << strerror(errno));
+ // Set the socket on non-blocking mode. This is useful to receive a EAGAIN
+ // error when connect() would block, to not block the whole process if a
+ // remote is not responsive.
+ const int existing_flags = ::fcntl(this->socket, F_GETFL, 0);
+ if ((existing_flags == -1) ||
+ (::fcntl(this->socket, F_SETFL, existing_flags | O_NONBLOCK) == -1))
+ throw std::runtime_error(std::string("Could not initialize socket: ") + strerror(errno));
}
-std::pair<bool, std::string> SocketHandler::connect(const std::string& address, const std::string& port)
+void SocketHandler::connect(const std::string& address, const std::string& port)
{
- log_info("Trying to connect to " << address << ":" << port);
+ if (!this->connecting)
+ {
+ log_info("Trying to connect to " << address << ":" << port);
+ }
+ this->connecting = true;
+ this->address = address;
+ this->port = port;
+
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = 0;
@@ 43,7 64,8 @@ std::pair<bool, std::string> SocketHandler::connect(const std::string& address,
{
log_warning(std::string("getaddrinfo failed: ") + gai_strerror(res));
this->close();
- return std::make_pair(false, gai_strerror(res));
+ this->on_connection_failed(gai_strerror(res));
+ return ;
}
// Make sure the alloced structure is always freed at the end of the
@@ 56,14 78,28 @@ std::pair<bool, std::string> SocketHandler::connect(const std::string& address,
{
log_info("Connection success.");
this->connected = true;
+ this->connecting = false;
this->on_connected();
- return std::make_pair(true, "");
+ return ;
+ }
+ else if (errno == EINPROGRESS || errno == EALREADY)
+ { // retry this process later, when the socket
+ // is ready to be written on.
+ log_debug("Need to retry connecting later..." << strerror(errno));
+ this->poller->watch_send_events(this);
+ return ;
}
log_info("Connection failed:" << strerror(errno));
}
log_error("All connection attempts failed.");
this->close();
- return std::make_pair(false, "");
+ this->on_connection_failed(strerror(errno));
+ return ;
+}
+
+void SocketHandler::connect()
+{
+ this->connect(this->address, this->port);
}
void SocketHandler::set_poller(Poller* poller)
@@ 114,11 150,11 @@ void SocketHandler::on_send()
void SocketHandler::close()
{
this->connected = false;
+ this->connecting = false;
this->poller->remove_socket_handler(this->get_socket());
::close(this->socket);
// recreate the socket for a potential future usage
- if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
- throw std::runtime_error("Could not create socket");
+ this->init_socket();
}
socket_t SocketHandler::get_socket() const
@@ 139,3 175,8 @@ bool SocketHandler::is_connected() const
{
return this->connected;
}
+
+bool SocketHandler::is_connecting() const
+{
+ return this->connecting;
+}
M src/network/socket_handler.hpp => src/network/socket_handler.hpp +22 -1
@@ 20,9 20,14 @@ public:
explicit SocketHandler();
virtual ~SocketHandler() {}
/**
+ * (re-)Initialize the socket
+ */
+ void init_socket();
+ /**
* Connect to the remote server, and call on_connected() if this succeeds
*/
- std::pair<bool, std::string> connect(const std::string& address, const std::string& port);
+ void connect(const std::string& address, const std::string& port);
+ void connect();
/**
* Set the pointer to the given Poller, to communicate with it.
*/
@@ 54,6 59,11 @@ public:
*/
virtual void on_connected() = 0;
/**
+ * Called when the connection fails. Not when it is closed later, just at
+ * the connect() call.
+ */
+ virtual void on_connection_failed(const std::string& reason) = 0;
+ /**
* Called when we detect a disconnection from the remote host.
*/
virtual void on_connection_close() = 0;
@@ 63,6 73,7 @@ public:
*/
virtual void parse_in_buffer() = 0;
bool is_connected() const;
+ bool is_connecting() const;
protected:
socket_t socket;
@@ 86,7 97,17 @@ protected:
* (actually it is sharing our ownership with a Bridge).
*/
Poller* poller;
+ /**
+ * Hostname we are connected/connecting to
+ */
+ std::string address;
+ /**
+ * Port we are connected/connecting to
+ */
+ std::string port;
+
bool connected;
+ bool connecting;
private:
SocketHandler(const SocketHandler&) = delete;
M src/xmpp/xmpp_component.cpp => src/xmpp/xmpp_component.cpp +8 -3
@@ 50,9 50,9 @@ XmppComponent::~XmppComponent()
{
}
-bool XmppComponent::start()
+void XmppComponent::start()
{
- return this->connect("127.0.0.1", "5347").first;
+ this->connect("127.0.0.1", "5347");
}
bool XmppComponent::is_document_open() const
@@ 67,6 67,11 @@ void XmppComponent::send_stanza(const Stanza& stanza)
this->send_data(std::move(str));
}
+void XmppComponent::on_connection_failed(const std::string& reason)
+{
+ log_error("Failed to connect to the XMPP server: " << reason);
+}
+
void XmppComponent::on_connected()
{
log_info("connected to XMPP server");
@@ 103,7 108,7 @@ void XmppComponent::clean()
while (it != this->bridges.end())
{
it->second->clean();
- if (it->second->connected_clients() == 0)
+ if (it->second->active_clients() == 0)
it = this->bridges.erase(it);
else
++it;
M src/xmpp/xmpp_component.hpp => src/xmpp/xmpp_component.hpp +2 -2
@@ 20,6 20,7 @@ class XmppComponent: public SocketHandler
public:
explicit XmppComponent(const std::string& hostname, const std::string& secret);
~XmppComponent();
+ void on_connection_failed(const std::string& reason) override final;
void on_connected() override final;
void on_connection_close() override final;
void parse_in_buffer() override final;
@@ 38,9 39,8 @@ public:
void clean();
/**
* Connect to the XMPP server.
- * Returns false if we failed to connect
*/
- bool start();
+ void start();
/**
* Serialize the stanza and add it to the out_buf to be sent to the
* server.