#include "connect.hpp" #include "logging.hpp" #include #include using namespace boost::asio::ip; using boost::asio::io_service; boost::asio::ip::tcp::socket connect(std::string host, std::string port, io_service& io_service) { boost::asio::ip::basic_resolver resolver(io_service); boost::asio::ip::basic_resolver_query query(host, port); for(auto it = resolver.resolve(query); it != boost::asio::ip::tcp::resolver::iterator(); ++it) { boost::system::error_code ec; boost::asio::ip::tcp::socket socket(io_service); socket.connect(*it, ec); if(ec) { continue; } else { return socket; } } throw std::runtime_error("None of the supplied endpoints responded"); } /*! * \brief The IterationInfo struct */ struct IterationInfo { int iteration; ///< the iteration we are currently in starting with 0; boost::asio::ip::tcp::resolver::iterator it; ///< The endpoint iterator. boost::asio::deadline_timer retry_timer; ///< A timer to retry in 3, 6, 12, 24, 48, 96 seconds after subsequent connections failures. /*! * \brief IterationInfo * \param iteration * \param it * \param io_service Used to create the deadline_timer */ IterationInfo(int iteration, boost::asio::ip::tcp::resolver::iterator it, boost::asio::io_service& io_service) : iteration(iteration) , it(it) , retry_timer(io_service) {} }; void async_connect_iteration(tcp::socket& socket, std::shared_ptr info, std::function on_connect) { if(info->it == boost::asio::ip::tcp::resolver::iterator()) { throw std::runtime_error("None of the supplied endpoints responded"); } auto handler = [&socket, info, on_connect](boost::system::error_code const& ec) { if(ec == boost::system::errc::connection_refused) { socket.close(); if(info->iteration <= 5) { int seconds_to_wait = std::pow(2, info->iteration) * 3; info->iteration++; BOOST_LOG_TRIVIAL(info) << "Connection refused, retrying in " << seconds_to_wait << " seconds."; info->retry_timer.expires_from_now(boost::posix_time::seconds(seconds_to_wait)); info->retry_timer.async_wait([&socket, info, on_connect](boost::system::error_code const& ec){ if(ec) { BOOST_LOG_TRIVIAL(error) << "Something went wrong with the retry timer: " << ec; } else { async_connect_iteration(socket, info, on_connect); } }); } else { BOOST_LOG_TRIVIAL(info) << "Connection was refused 5 times over the course of 189 seconds, moving to next resolved query"; info->it++; info->iteration = 0; async_connect_iteration(socket, info, on_connect); } } else if(ec) { BOOST_LOG_TRIVIAL(info) << ec << std::endl; info->it++; async_connect_iteration(socket, info, on_connect); } else { on_connect(); } }; socket.async_connect(*(info->it), handler); } void async_connect(tcp::socket& socket, std::string host, std::string next_port, std::function on_connect) { boost::asio::ip::basic_resolver resolver(socket.get_io_service()); boost::asio::ip::basic_resolver_query query(host, next_port); auto it = resolver.resolve(query); std::shared_ptr info = std::make_shared(0, it, socket.get_io_service()); async_connect_iteration(socket, info, on_connect); }