/* */ #include "FeedbackURISelector.h" #include #include #include "ServerStatMan.h" #include "ServerStat.h" #include "A2STR.h" #include "FileEntry.h" #include "Logger.h" #include "LogFactory.h" #include "a2algo.h" #include "uri.h" #include "fmt.h" namespace aria2 { FeedbackURISelector::FeedbackURISelector( const std::shared_ptr& serverStatMan) : serverStatMan_(serverStatMan) { } FeedbackURISelector::~FeedbackURISelector() = default; std::string FeedbackURISelector::select( FileEntry* fileEntry, const std::vector>& usedHosts) { if (A2_LOG_DEBUG_ENABLED) { for (const auto& h : usedHosts) { A2_LOG_DEBUG(fmt("UsedHost=%lu, %s", static_cast(h.first), h.second.c_str())); } } if (fileEntry->getRemainingUris().empty()) { return A2STR::NIL; } // Select URI with usedHosts first. If no URI is selected, then do // it again without usedHosts. std::string uri = selectFaster(fileEntry->getRemainingUris(), usedHosts); if (uri.empty()) { A2_LOG_DEBUG("No URI returned from selectFaster()"); uri = selectRarer(fileEntry->getRemainingUris(), usedHosts); } if (!uri.empty()) { std::deque& uris = fileEntry->getRemainingUris(); uris.erase(std::find(uris.begin(), uris.end(), uri)); } A2_LOG_DEBUG(fmt("FeedbackURISelector selected %s", uri.c_str())); return uri; } std::string FeedbackURISelector::selectRarer( const std::deque& uris, const std::vector>& usedHosts) { // pair of host and URI std::vector> cands; for (const auto& u : uris) { uri_split_result us; if (uri_split(&us, u.c_str()) == -1) { continue; } auto host = uri::getFieldString(us, USR_HOST, u.c_str()); auto protocol = uri::getFieldString(us, USR_SCHEME, u.c_str()); auto ss = serverStatMan_->find(host, protocol); if (ss && ss->isError()) { A2_LOG_DEBUG(fmt("Error not considered: %s", u.c_str())); continue; } cands.push_back(std::make_pair(host, u)); } for (const auto& h : usedHosts) { for (const auto& c : cands) { if (h.second == c.first) { return c.second; } } } assert(!uris.empty()); return uris.front(); } std::string FeedbackURISelector::selectFaster( const std::deque& uris, const std::vector>& usedHosts) { // Use first 10 good URIs to introduce some randomness. constexpr size_t NUM_URI = 10; // Ignore low speed server constexpr int SPEED_THRESHOLD = 20_k; std::vector, std::string>> fastCands; std::vector normCands; for (const auto& u : uris) { if (fastCands.size() >= NUM_URI) { break; } uri_split_result us; if (uri_split(&us, u.c_str()) == -1) { continue; } auto host = uri::getFieldString(us, USR_HOST, u.c_str()); if (findSecond(usedHosts.begin(), usedHosts.end(), host) != usedHosts.end()) { A2_LOG_DEBUG(fmt("%s is in usedHosts, not considered", u.c_str())); continue; } auto protocol = uri::getFieldString(us, USR_SCHEME, u.c_str()); auto ss = serverStatMan_->find(host, protocol); if (!ss) { normCands.push_back(u); } else if (ss->isOK()) { if (ss->getDownloadSpeed() > SPEED_THRESHOLD) { fastCands.push_back(std::make_pair(ss, u)); } else { normCands.push_back(u); } } } if (fastCands.empty()) { if (normCands.empty()) { return A2STR::NIL; } else { A2_LOG_DEBUG("Selected from normCands"); return normCands.front(); } } else { A2_LOG_DEBUG("Selected from fastCands"); std::sort(fastCands.begin(), fastCands.end(), ServerStatFaster()); return fastCands.front().second; } } } // namespace aria2