diff --git a/AddressBook.cpp b/AddressBook.cpp index adcd7078..a95fb3d6 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "base64.h" #include "util.h" #include "Identity.h" @@ -151,12 +152,12 @@ namespace client //--------------------------------------------------------------------- AddressBook::AddressBook (): m_IsLoaded (false), m_IsDownloading (false), - m_DefaultSubscription (nullptr) + m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) { } AddressBook::~AddressBook () - { + { if (m_IsDownloading) { LogPrint (eLogInfo, "Subscription is downloading. Waiting for temination..."); @@ -179,7 +180,7 @@ namespace client delete m_DefaultSubscription; for (auto it: m_Subscriptions) delete it; - + delete m_SubscriptionsUpdateTimer; } AddressBookStorage * AddressBook::CreateStorage () @@ -339,11 +340,71 @@ namespace client m_Subscriptions.push_back (new AddressBookSubscription (*this, s)); } } + else + LogPrint (eLogWarning, "subscriptions.txt not found"); } else LogPrint (eLogError, "Subscriptions already loaded"); } + void AddressBook::DownloadComplete (bool success) + { + m_IsDownloading = false; + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes( + success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + + void AddressBook::StartSubscriptions () + { + LoadSubscriptions (); + if (!m_Subscriptions.size ()) return; + + auto dest = i2p::client::context.GetSharedLocalDestination (); + if (dest) + { + m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ()); + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + else + LogPrint (eLogError, "Can't start subscriptions: missing shared local destination"); + } + + void AddressBook::StopSubscriptions () + { + if (m_SubscriptionsUpdateTimer) + m_SubscriptionsUpdateTimer->cancel (); + } + + void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto dest = i2p::client::context.GetSharedLocalDestination (); + if (!dest) return; + if (m_IsLoaded && !m_IsDownloading && dest->IsReady ()) + { + // pick random subscription + CryptoPP::AutoSeededRandomPool rnd; + auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1); + m_IsDownloading = true; + m_Subscriptions[ind]->CheckSubscription (); + } + else + { + if (!m_IsLoaded) + LoadHosts (); + // try it again later + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + } + } + AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link): m_Book (book), m_Link (link) { @@ -358,6 +419,7 @@ namespace client void AddressBookSubscription::Request () { // must be run in separate thread + bool success = false; i2p::util::http::url u (m_Link); i2p::data::IdentHash ident; if (m_Book.GetIdentHash (u.host_, ident)) @@ -433,6 +495,7 @@ namespace client } if (!response.eof ()) { + success = true; if (!isChunked) m_Book.LoadHostsFromStream (response); else @@ -452,7 +515,7 @@ namespace client } else LogPrint (eLogError, "Can't resolve ", u.host_); - m_Book.SetIsDownloading (false); + m_Book.DownloadComplete (success); } } } diff --git a/AddressBook.h b/AddressBook.h index 0ae7788f..a2ad9adc 100644 --- a/AddressBook.h +++ b/AddressBook.h @@ -4,9 +4,10 @@ #include #include #include -#include +#include #include #include +#include #include "base64.h" #include "util.h" #include "Identity.h" @@ -17,7 +18,11 @@ namespace i2p namespace client { const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt"; - + const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes + const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes + const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 240; // in minutes + const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes + class AddressBookStorage // interface for storage { public: @@ -44,8 +49,10 @@ namespace client void InsertAddress (const std::string& address, const std::string& base64); // for jump service void InsertAddress (const i2p::data::IdentityEx& address); + void StartSubscriptions (); + void StopSubscriptions (); void LoadHostsFromStream (std::istream& f); - void SetIsDownloading (bool isDownloading) { m_IsDownloading = isDownloading; }; + void DownloadComplete (bool success); private: @@ -53,14 +60,17 @@ namespace client void LoadHosts (); void LoadSubscriptions (); + void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); + private: std::mutex m_AddressBookMutex; std::map m_Addresses; AddressBookStorage * m_Storage; volatile bool m_IsLoaded, m_IsDownloading; - std::list m_Subscriptions; + std::vector m_Subscriptions; AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet + boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; }; class AddressBookSubscription diff --git a/ClientContext.cpp b/ClientContext.cpp index eb824338..c4700e0e 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -77,10 +77,12 @@ namespace client m_BOBCommandChannel->Start (); LogPrint("BOB command channel started"); } + m_AddressBook.StartSubscriptions (); } void ClientContext::Stop () { + m_AddressBook.StopSubscriptions (); m_HttpProxy->Stop(); delete m_HttpProxy; m_HttpProxy = nullptr;