void ConnectionManager::addDownloadConnection(UserConnection* uc) { dcassert(uc->isSet(UserConnection::FLAG_DOWNLOAD)); bool addConn = false; { Lock l(cs); ConnectionQueueItem::Iter i = find(downloads.begin(), downloads.end(), uc->getUser()); if(i != downloads.end()) { ConnectionQueueItem* cqi = *i; if(cqi->getState() == ConnectionQueueItem::WAITING || cqi->getState() == ConnectionQueueItem::CONNECTING) { cqi->setState(ConnectionQueueItem::ACTIVE); uc->setFlag(UserConnection::FLAG_ASSOCIATED); fire(ConnectionManagerListener::Connected(), cqi); dcdebug("ConnectionManager::addDownloadConnection, leaving to downloadmanager\n"); addConn = true; } } } if(addConn) { DownloadManager::getInstance()->addConnection(uc); } else { putConnection(uc); } }
void ConnectionManager::addUploadConnection(UserConnection* uc) { dcassert(uc->isSet(UserConnection::FLAG_UPLOAD)); bool addConn = false; { Lock l(cs); ConnectionQueueItem::Iter i = find(uploads.begin(), uploads.end(), uc->getUser()); if(i == uploads.end()) { ConnectionQueueItem* cqi = getCQI(uc->getUser(), false); cqi->setState(ConnectionQueueItem::ACTIVE); uc->setFlag(UserConnection::FLAG_ASSOCIATED); fire(ConnectionManagerListener::Connected(), cqi); dcdebug("ConnectionManager::addUploadConnection, leaving to uploadmanager\n"); addConn = true; } } if(addConn) { UploadManager::getInstance()->addConnection(uc); } else { putConnection(uc); } }
/** * Request a connection for downloading. * DownloadManager::addConnection will be called as soon as the connection is ready * for downloading. * @param aUser The user to connect to. */ void ConnectionManager::getDownloadConnection(const User::Ptr& aUser) { dcassert((bool)aUser); ConnectionQueueItem* cqi = NULL; { Lock l(cs); // Check the download pool if(find(downPool.begin(), downPool.end(), aUser) != downPool.end()) { if(find(pendingAdd.begin(), pendingAdd.end(), aUser) == pendingAdd.end()) pendingAdd.push_back(aUser); return; } // See if we're already trying to connect if(find(pendingDown.begin(), pendingDown.end(), aUser) != pendingDown.end()) return; // Check if we have an active download connection already for(ConnectionQueueItem::Iter j = active.begin(); j != active.end(); ++j) { dcassert((*j)->getConnection()); if((*j == aUser) && ((*j)->getConnection()->isSet(UserConnection::FLAG_DOWNLOAD))) return; } // Add it to the pending... cqi = new ConnectionQueueItem(aUser); cqi->setState(ConnectionQueueItem::WAITING); pendingDown.push_back(cqi); fire(ConnectionManagerListener::Added(), cqi); } }
void ConnectionManager::on(UserConnectionListener::Key, UserConnection* aSource, const string&/* aKey*/) throw() { if(aSource->getState() != UserConnection::STATE_KEY) { dcdebug("CM::onKey Bad state, ignoring"); return; } // We don't want any messages while the Up/DownloadManagers are working... aSource->removeListener(this); dcassert(aSource->getUser()); { Lock l(cs); // Only one connection / user & direction... for(ConnectionQueueItem::Iter k = active.begin(); k != active.end(); ++k) { bool sameDirection = (*k)->getConnection()->isSet(UserConnection::FLAG_UPLOAD) == aSource->isSet(UserConnection::FLAG_UPLOAD); if( sameDirection && (*k == aSource->getUser()) ) { putConnection(aSource); return; } } ConnectionQueueItem* cqi = NULL; if(aSource->isSet(UserConnection::FLAG_DOWNLOAD)) { // See if we have a matching user in the pending connections... ConnectionQueueItem::Iter i = find(pendingDown.begin(), pendingDown.end(), aSource->getUser()); if(i == pendingDown.end()) { putConnection(aSource); return; } cqi = *i; pendingDown.erase(i); cqi->setConnection(aSource); } else { dcassert(aSource->isSet(UserConnection::FLAG_UPLOAD)); cqi = new ConnectionQueueItem(aSource->getUser()); cqi->setConnection(aSource); fire(ConnectionManagerListener::Added(), cqi); } aSource->setCQI(cqi); dcassert(find(active.begin(), active.end(), cqi) == active.end()); active.push_back(cqi); fire(ConnectionManagerListener::Connected(), cqi); if(aSource->isSet(UserConnection::FLAG_DOWNLOAD)) { dcdebug("ConnectionManager::onKey, leaving to downloadmanager\n"); DownloadManager::getInstance()->addConnection(aSource); } else { dcassert(aSource->isSet(UserConnection::FLAG_UPLOAD)); dcdebug("ConnectionManager::onKey, leaving to uploadmanager\n"); UploadManager::getInstance()->addConnection(aSource); } } }
void ConnectionManager::putDownloadConnection(UserConnection* aSource, bool reuse /* = false */) { // Pool it for later usage... if(reuse) { aSource->addListener(this); { Lock l(cs); aSource->getCQI()->setState(ConnectionQueueItem::IDLE); dcassert(find(active.begin(), active.end(), aSource->getCQI()) != active.end()); active.erase(find(active.begin(), active.end(), aSource->getCQI())); downPool.push_back(aSource->getCQI()); } dcdebug("ConnectionManager::putDownloadConnection Pooling reusable connection %p to %s\n", aSource, aSource->getUser()->getNick().c_str()); } else { if(QueueManager::getInstance()->hasDownload(aSource->getCQI()->getUser())) { aSource->removeListeners(); aSource->disconnect(); Lock l(cs); ConnectionQueueItem* cqi = aSource->getCQI(); dcassert(cqi); // Remove the userconnection, don't need it any more dcassert(find(userConnections.begin(), userConnections.end(), aSource) != userConnections.end()); userConnections.erase(find(userConnections.begin(), userConnections.end(), aSource)); pendingDelete.push_back(aSource); cqi->setConnection(NULL); cqi->setState(ConnectionQueueItem::WAITING); dcassert(find(active.begin(), active.end(), aSource->getCQI()) != active.end()); active.erase(find(active.begin(), active.end(), aSource->getCQI())); cqi->setLastAttempt(GET_TICK()); pendingDown.push_back(cqi); } else { { Lock l(cs); dcassert(find(active.begin(), active.end(), aSource->getCQI()) != active.end()); active.erase(find(active.begin(), active.end(), aSource->getCQI())); } putConnection(aSource); } } }
/** * Nick received. If it's a downloader, fine, otherwise it must be an uploader. */ void ConnectionManager::on(UserConnectionListener::MyNick, UserConnection* aSource, const string& aNick) throw() { if(aSource->getState() != UserConnection::STATE_NICK) { // Already got this once, ignore... dcdebug("CM::onMyNick %p sent nick twice\n", aSource); return; } dcassert(aNick.size() > 0); dcdebug("ConnectionManager::onMyNick %p, %s\n", aSource, aNick.c_str()); dcassert(!aSource->getUser()); // First, we try looking in the pending downloads...hopefully it's one of them... { Lock l(cs); for(ConnectionQueueItem::Iter i = pendingDown.begin(); i != pendingDown.end(); ++i) { ConnectionQueueItem* cqi = *i; if(cqi->getUser()->getNick() == aNick) { aSource->setUser(cqi->getUser()); // Indicate that we're interested in this file... aSource->setFlag(UserConnection::FLAG_DOWNLOAD); } } } if(!aSource->getUser()) { // Make sure we know who it is, i e that he/she is connected... if(!ClientManager::getInstance()->isOnline(aNick)) { dcdebug("CM::onMyNick Incoming connection from unknown user %s\n", aNick.c_str()); putConnection(aSource); return; } aSource->setUser(ClientManager::getInstance()->getUser(aNick)); // We don't need this connection for downloading...make it an upload connection instead... aSource->setFlag(UserConnection::FLAG_UPLOAD); } if( aSource->isSet(UserConnection::FLAG_INCOMING) ) { aSource->myNick(aSource->getUser()->getClientNick()); aSource->lock(CryptoManager::getInstance()->getLock(), CryptoManager::getInstance()->getPk()); } aSource->setState(UserConnection::STATE_LOCK); }
void ConnectionManager::on(UserConnectionListener::Failed, UserConnection* aSource, const string& aError) throw() { Lock l(cs); if(aSource->isSet(UserConnection::FLAG_ASSOCIATED)) { if(aSource->isSet(UserConnection::FLAG_DOWNLOAD)) { ConnectionQueueItem::Iter i = find(downloads.begin(), downloads.end(), aSource->getUser()); dcassert(i != downloads.end()); ConnectionQueueItem* cqi = *i; cqi->setState(ConnectionQueueItem::WAITING); cqi->setLastAttempt(GET_TICK()); fire(ConnectionManagerListener::Failed(), cqi, aError); } else if(aSource->isSet(UserConnection::FLAG_UPLOAD)) { ConnectionQueueItem::Iter i = find(uploads.begin(), uploads.end(), aSource->getUser()); dcassert(i != uploads.end()); ConnectionQueueItem* cqi = *i; putCQI(cqi); } } putConnection(aSource); }
void ConnectionManager::on(UserConnectionListener::MyNick, UserConnection* aSource, const string& aNick) throw() { if(aSource->getState() != UserConnection::STATE_SUPNICK) { // Already got this once, ignore... dcdebug("CM::onMyNick %p sent nick twice\n", (void*)aSource); return; } dcassert(aNick.size() > 0); dcdebug("ConnectionManager::onMyNick %p, %s\n", (void*)aSource, aNick.c_str()); dcassert(!aSource->getUser()); if(aSource->isSet(UserConnection::FLAG_INCOMING)) { // Try to guess where this came from... pair<string, string> i = expectedConnections.remove(aNick); if(i.second.empty()) { dcassert(i.first.empty()); dcdebug("Unknown incoming connection from %s\n", aNick.c_str()); putConnection(aSource); return; } aSource->setToken(i.first); aSource->setHubUrl(i.second); } CID cid = ClientManager::getInstance()->makeCid(aNick, aSource->getHubUrl()); // First, we try looking in the pending downloads...hopefully it's one of them... { Lock l(cs); for(ConnectionQueueItem::Iter i = downloads.begin(); i != downloads.end(); ++i) { ConnectionQueueItem* cqi = *i; if((cqi->getState() == ConnectionQueueItem::CONNECTING || cqi->getState() == ConnectionQueueItem::WAITING) && cqi->getUser()->getCID() == cid) { aSource->setUser(cqi->getUser()); // Indicate that we're interested in this file... aSource->setFlag(UserConnection::FLAG_DOWNLOAD); break; } } } if(!aSource->getUser()) { // Make sure we know who it is, i e that he/she is connected... aSource->setUser(ClientManager::getInstance()->findUser(cid)); if(!aSource->getUser() || !ClientManager::getInstance()->isOnline(aSource->getUser())) { dcdebug("CM::onMyNick Incoming connection from unknown user %s\n", aNick.c_str()); putConnection(aSource); return; } // We don't need this connection for downloading...make it an upload connection instead... aSource->setFlag(UserConnection::FLAG_UPLOAD); } if(ClientManager::getInstance()->isOp(aSource->getUser(), aSource->getHubUrl())) aSource->setFlag(UserConnection::FLAG_OP); if( aSource->isSet(UserConnection::FLAG_INCOMING) ) { aSource->myNick(aSource->getToken()); aSource->lock(CryptoManager::getInstance()->getLock(), CryptoManager::getInstance()->getPk()); } aSource->setState(UserConnection::STATE_LOCK); }
void ConnectionManager::on(TimerManagerListener::Second, uint32_t aTick) throw() { User::List passiveUsers; ConnectionQueueItem::List removed; User::List idlers; { Lock l(cs); bool attemptDone = false; idlers = checkIdle; checkIdle.clear(); for(ConnectionQueueItem::Iter i = downloads.begin(); i != downloads.end(); ++i) { ConnectionQueueItem* cqi = *i; if(cqi->getState() != ConnectionQueueItem::ACTIVE) { if(!cqi->getUser()->isOnline()) { // Not online anymore...remove it from the pending... removed.push_back(cqi); continue; } if(cqi->getUser()->isSet(User::PASSIVE) && !ClientManager::getInstance()->isActive()) { passiveUsers.push_back(cqi->getUser()); removed.push_back(cqi); continue; } if( ((cqi->getLastAttempt() + 60*1000) < aTick) && !attemptDone ) { cqi->setLastAttempt(aTick); QueueItem::Priority prio = QueueManager::getInstance()->hasDownload(cqi->getUser()); if(prio == QueueItem::PAUSED) { removed.push_back(cqi); continue; } bool startDown = DownloadManager::getInstance()->startDownload(prio); if(cqi->getState() == ConnectionQueueItem::WAITING) { if(startDown) { cqi->setState(ConnectionQueueItem::CONNECTING); ClientManager::getInstance()->connect(cqi->getUser()); fire(ConnectionManagerListener::StatusChanged(), cqi); attemptDone = true; } else { cqi->setState(ConnectionQueueItem::NO_DOWNLOAD_SLOTS); fire(ConnectionManagerListener::Failed(), cqi, STRING(ALL_DOWNLOAD_SLOTS_TAKEN)); } } else if(cqi->getState() == ConnectionQueueItem::NO_DOWNLOAD_SLOTS && startDown) { cqi->setState(ConnectionQueueItem::WAITING); } } else if(((cqi->getLastAttempt() + 50*1000) < aTick) && (cqi->getState() == ConnectionQueueItem::CONNECTING)) { fire(ConnectionManagerListener::Failed(), cqi, STRING(CONNECTION_TIMEOUT)); cqi->setState(ConnectionQueueItem::WAITING); } } } for(ConnectionQueueItem::Iter m = removed.begin(); m != removed.end(); ++m) { putCQI(*m); } } for(User::Iter i = idlers.begin(); i != idlers.end(); ++i) { DownloadManager::getInstance()->checkIdle(*i); } for(User::Iter ui = passiveUsers.begin(); ui != passiveUsers.end(); ++ui) { QueueManager::getInstance()->removeSource(*ui, QueueItem::Source::FLAG_PASSIVE); } }
void ConnectionManager::on(TimerManagerListener::Second, u_int32_t aTick) throw() { ConnectionQueueItem::List failPassive; ConnectionQueueItem::List connecting; ConnectionQueueItem::List removed; User::List getDown; { Lock l(cs); { for(User::Iter k = pendingAdd.begin(); k != pendingAdd.end(); ++k) { ConnectionQueueItem::Iter i = find(downPool.begin(), downPool.end(), *k); if(i == downPool.end()) { // Hm, connection must have failed before it could be collected... getDown.push_back(*k); } else { ConnectionQueueItem* cqi = *i; downPool.erase(i); dcassert(find(active.begin(), active.end(), cqi) == active.end()); active.push_back(cqi); dcassert(cqi->getConnection()); dcassert(cqi->getConnection()->getCQI() == cqi); cqi->getConnection()->removeListener(this); DownloadManager::getInstance()->addConnection(cqi->getConnection()); } } pendingAdd.clear(); } bool tooMany = ((SETTING(DOWNLOAD_SLOTS) != 0) && DownloadManager::getInstance()->getDownloads() >= (size_t)SETTING(DOWNLOAD_SLOTS)); bool tooFast = ((SETTING(MAX_DOWNLOAD_SPEED) != 0 && DownloadManager::getInstance()->getAverageSpeed() >= (SETTING(MAX_DOWNLOAD_SPEED)*1024))); bool startDown = !tooMany && !tooFast; int attempts = 0; ConnectionQueueItem::Iter i = pendingDown.begin(); while(i != pendingDown.end()) { ConnectionQueueItem* cqi = *i; dcassert(cqi->getUser()); if(!cqi->getUser()->isOnline()) { // Not online anymore...remove him from the pending... i = pendingDown.erase(i); removed.push_back(cqi); continue; } if( ((cqi->getLastAttempt() + 60*1000) < aTick) && (attempts < 2) ) { cqi->setLastAttempt(aTick); if(!QueueManager::getInstance()->hasDownload(cqi->getUser())) { i = pendingDown.erase(i); removed.push_back(cqi); continue; } if(cqi->getUser()->isSet(User::PASSIVE) && (SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE)) { i = pendingDown.erase(i); failPassive.push_back(cqi); continue; } // Always start high-priority downloads unless we have 3 more than maxdownslots already... if(!startDown) { bool extraFull = (SETTING(DOWNLOAD_SLOTS) != 0) && (DownloadManager::getInstance()->getDownloads() >= (size_t)(SETTING(DOWNLOAD_SLOTS)+3)); startDown = !extraFull && QueueManager::getInstance()->hasDownload(cqi->getUser(), QueueItem::HIGHEST); } if(cqi->getState() == ConnectionQueueItem::WAITING) { if(startDown) { cqi->setState(ConnectionQueueItem::CONNECTING); cqi->getUser()->connect(); fire(ConnectionManagerListener::StatusChanged(), cqi); attempts++; } else { cqi->setState(ConnectionQueueItem::NO_DOWNLOAD_SLOTS); fire(ConnectionManagerListener::Failed(), cqi, STRING(ALL_DOWNLOAD_SLOTS_TAKEN)); } } else if(cqi->getState() == ConnectionQueueItem::NO_DOWNLOAD_SLOTS && startDown) { cqi->setState(ConnectionQueueItem::WAITING); } } else if(((cqi->getLastAttempt() + 50*1000) < aTick) && (cqi->getState() == ConnectionQueueItem::CONNECTING)) { fire(ConnectionManagerListener::Failed(), cqi, STRING(CONNECTION_TIMEOUT)); cqi->setState(ConnectionQueueItem::WAITING); } ++i; } } ConnectionQueueItem::Iter m; for(m = removed.begin(); m != removed.end(); ++m) { fire(ConnectionManagerListener::Removed(), *m); delete *m; } for(m = failPassive.begin(); m != failPassive.end(); ++m) { QueueManager::getInstance()->removeSources((*m)->getUser(), QueueItem::Source::FLAG_PASSIVE); fire(ConnectionManagerListener::Removed(), *m); delete *m; } for(User::Iter n = getDown.begin(); n != getDown.end(); ++n) { getDownloadConnection(*n); } }