/* */ #include "TrackerWatcherCommand.h" #include #include "DownloadEngine.h" #include "BtAnnounce.h" #include "BtRuntime.h" #include "PieceStorage.h" #include "PeerStorage.h" #include "Peer.h" #include "prefs.h" #include "message.h" #include "ByteArrayDiskWriterFactory.h" #include "RecoverableException.h" #include "PeerInitiateConnectionCommand.h" #include "DiskAdaptor.h" #include "FileEntry.h" #include "RequestGroup.h" #include "Option.h" #include "DlAbortEx.h" #include "Logger.h" #include "A2STR.h" #include "SocketCore.h" #include "Request.h" #include "AnnounceTier.h" #include "DownloadContext.h" #include "bittorrent_helper.h" namespace aria2 { TrackerWatcherCommand::TrackerWatcherCommand (int32_t cuid, RequestGroup* requestGroup, DownloadEngine* e): Command(cuid), _requestGroup(requestGroup), e(e) { _requestGroup->increaseNumCommand(); } TrackerWatcherCommand::~TrackerWatcherCommand() { _requestGroup->decreaseNumCommand(); } bool TrackerWatcherCommand::execute() { if(_requestGroup->isForceHaltRequested()) { if(_trackerRequestGroup.isNull()) { return true; } else if(_trackerRequestGroup->getNumCommand() == 0 || _trackerRequestGroup->downloadFinished()) { return true; } else { _trackerRequestGroup->setForceHaltRequested(true); e->commands.push_back(this); return false; } } if(_btAnnounce->noMoreAnnounce()) { logger->debug("no more announce"); return true; } if(_trackerRequestGroup.isNull()) { _trackerRequestGroup = createAnnounce(); if(!_trackerRequestGroup.isNull()) { std::deque commands; _trackerRequestGroup->createInitialCommand(commands, e); e->addCommand(commands); logger->debug("added tracker request command"); } } else if(_trackerRequestGroup->downloadFinished()){ try { std::string trackerResponse = getTrackerResponse(_trackerRequestGroup); processTrackerResponse(trackerResponse); _btAnnounce->announceSuccess(); _btAnnounce->resetAnnounce(); } catch(RecoverableException& ex) { logger->error(EX_EXCEPTION_CAUGHT, ex); _btAnnounce->announceFailure(); if(_btAnnounce->isAllAnnounceFailed()) { _btAnnounce->resetAnnounce(); } } _trackerRequestGroup.reset(); } else if(_trackerRequestGroup->getNumCommand() == 0){ // handle errors here _btAnnounce->announceFailure(); // inside it, trackers = 0. _trackerRequestGroup.reset(); if(_btAnnounce->isAllAnnounceFailed()) { _btAnnounce->resetAnnounce(); } } e->commands.push_back(this); return false; } std::string TrackerWatcherCommand::getTrackerResponse (const RequestGroupHandle& requestGroup) { std::stringstream strm; unsigned char data[2048]; requestGroup->getPieceStorage()->getDiskAdaptor()->openFile(); while(1) { ssize_t dataLength = requestGroup->getPieceStorage()-> getDiskAdaptor()->readData(data, sizeof(data), strm.tellp()); if(dataLength == 0) { break; } strm.write(reinterpret_cast(data), dataLength); } return strm.str(); } // TODO we have to deal with the exception thrown By BtAnnounce void TrackerWatcherCommand::processTrackerResponse (const std::string& trackerResponse) { _btAnnounce->processAnnounceResponse (reinterpret_cast(trackerResponse.c_str()), trackerResponse.size()); while(!_btRuntime->isHalt() && _btRuntime->lessThanMinPeers()) { PeerHandle peer = _peerStorage->getUnusedPeer(); if(peer.isNull()) { break; } peer->usedBy(e->newCUID()); PeerInitiateConnectionCommand* command = new PeerInitiateConnectionCommand (peer->usedBy(), _requestGroup, peer, e, _btRuntime); command->setPeerStorage(_peerStorage); command->setPieceStorage(_pieceStorage); e->commands.push_back(command); logger->debug("CUID#%d - Adding new command CUID#%d", cuid, peer->usedBy()); } } RequestGroupHandle TrackerWatcherCommand::createAnnounce() { RequestGroupHandle rg; if(_btAnnounce->isAnnounceReady()) { rg = createRequestGroup(_btAnnounce->getAnnounceUrl()); _btAnnounce->announceStart(); // inside it, trackers++. } return rg; } static bool backupTrackerIsAvailable (const SharedHandle& context) { const BDE& announceList = context->getAttribute(bittorrent::BITTORRENT)[bittorrent::ANNOUNCE_LIST]; if(announceList.size() >= 2) { return true; } if(announceList.empty()) { return false; } if(announceList[0].size() >= 2) { return true; } else { return false; } } RequestGroupHandle TrackerWatcherCommand::createRequestGroup(const std::string& uri) { std::deque uris; uris.push_back(uri); RequestGroupHandle rg(new RequestGroup(getOption())); // If backup tracker is available, only try 2 times for each tracker // and if they all fails, then try next one. if(backupTrackerIsAvailable(_requestGroup->getDownloadContext())) { logger->debug("This is multi-tracker announce."); } else { logger->debug("This is single-tracker announce."); } rg->getOption()->put(PREF_MAX_TRIES, "2"); // TODO When dry-run mode becomes available in BitTorrent, set // PREF_DRY_RUN=false too. rg->getOption()->put(PREF_USE_HEAD, V_FALSE); static const std::string TRACKER_ANNOUNCE_FILE("[tracker.announce]"); SharedHandle dctx (new DownloadContext(getOption()->getAsInt(PREF_SEGMENT_SIZE), 0, TRACKER_ANNOUNCE_FILE)); dctx->setDir(A2STR::NIL); dctx->getFileEntries().front()->setUris(uris); rg->setDownloadContext(dctx); SharedHandle dwf(new ByteArrayDiskWriterFactory()); rg->setDiskWriterFactory(dwf); rg->setFileAllocationEnabled(false); rg->setPreLocalFileCheckEnabled(false); logger->info("Creating tracker request group GID#%d", rg->getGID()); return rg; } void TrackerWatcherCommand::setBtRuntime (const SharedHandle& btRuntime) { _btRuntime = btRuntime; } void TrackerWatcherCommand::setPeerStorage (const SharedHandle& peerStorage) { _peerStorage = peerStorage; } void TrackerWatcherCommand::setPieceStorage (const SharedHandle& pieceStorage) { _pieceStorage = pieceStorage; } void TrackerWatcherCommand::setBtAnnounce (const SharedHandle& btAnnounce) { _btAnnounce = btAnnounce; } const SharedHandle