diff options
| -rw-r--r-- | ChangeLog | 23 | ||||
| -rw-r--r-- | src/BtDependency.cc | 62 | ||||
| -rw-r--r-- | src/FileEntry.h | 11 | ||||
| -rw-r--r-- | src/Metalink2RequestGroup.cc | 199 | ||||
| -rw-r--r-- | src/Metalink2RequestGroup.h | 2 | ||||
| -rw-r--r-- | src/MetalinkEntry.cc | 4 | ||||
| -rw-r--r-- | src/MetalinkEntry.h | 4 | ||||
| -rw-r--r-- | src/MetalinkHelper.cc | 38 | ||||
| -rw-r--r-- | src/MetalinkHelper.h | 10 | ||||
| -rw-r--r-- | src/MetalinkMetaurl.cc | 5 | ||||
| -rw-r--r-- | src/MetalinkMetaurl.h | 5 | ||||
| -rw-r--r-- | src/MetalinkParserController.cc | 1 | ||||
| -rw-r--r-- | src/RequestGroup.cc | 125 | ||||
| -rw-r--r-- | src/RequestGroup.h | 3 | ||||
| -rw-r--r-- | src/bittorrent_helper.cc | 2 | ||||
| -rw-r--r-- | test/BittorrentHelperTest.cc | 4 | ||||
| -rw-r--r-- | test/BtDependencyTest.cc | 70 | ||||
| -rw-r--r-- | test/MetalinkHelperTest.cc | 77 |
18 files changed, 494 insertions, 151 deletions
@@ -1,3 +1,26 @@ +2010-02-27 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> + + Supported name attribute of metalink::metaurl element and + multi-file torrent with Metalink4. Files with same metaurl are + grouped and downloaded in one RequestGroup. + * src/BtDependency.cc + * src/FileEntry.h + * src/Metalink2RequestGroup.cc + * src/Metalink2RequestGroup.h + * src/MetalinkEntry.cc + * src/MetalinkEntry.h + * src/MetalinkHelper.cc + * src/MetalinkHelper.h + * src/MetalinkMetaurl.cc + * src/MetalinkMetaurl.h + * src/MetalinkParserController.cc + * src/RequestGroup.cc + * src/RequestGroup.h + * src/bittorrent_helper.cc + * test/BittorrentHelperTest.cc + * test/BtDependencyTest.cc + * test/MetalinkHelperTest.cc + 2010-02-26 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> Store name attribute of metalink:metaurl element in MetalinkMetaurl. diff --git a/src/BtDependency.cc b/src/BtDependency.cc index a2009087..3c70804c 100644 --- a/src/BtDependency.cc +++ b/src/BtDependency.cc @@ -47,6 +47,7 @@ #include "File.h" #include "bittorrent_helper.h" #include "DlAbortEx.h" +#include "StringFormat.h" namespace aria2 { @@ -58,6 +59,18 @@ BtDependency::BtDependency(const RequestGroupWeakHandle& dependant, BtDependency::~BtDependency() {} +static void copyValues(const SharedHandle<FileEntry>& d, + const SharedHandle<FileEntry>& s) +{ + d->setRequested(true); + d->setPath(s->getPath()); + d->addUris(s->getRemainingUris().begin(), + s->getRemainingUris().end()); + if(!s->isSingleHostMultiConnectionEnabled()) { + d->disableSingleHostMultiConnection(); + } +} + bool BtDependency::resolve() { if(_dependee->getNumCommand() == 0 && _dependee->downloadFinished()) { @@ -80,23 +93,38 @@ bool BtDependency::resolve() bittorrent::loadFromMemory (content, context, File(dependee->getFirstFilePath()).getBasename()); } - if(context->getFileEntries().size() != - _dependant->getDownloadContext()->getFileEntries().size()) { - throw DL_ABORT_EX("The number of file in torrent doesn't match to" - " the dependent."); - } - // Copy file path in _dependant's FileEntries to newly created - // context's FileEntries to endorse the path structure of - // _dependant. URIs and singleHostMultiConnection are also copied. - for(std::vector<SharedHandle<FileEntry> >::const_iterator s = - _dependant->getDownloadContext()->getFileEntries().begin(), - d = context->getFileEntries().begin(); - d != context->getFileEntries().end(); ++s, ++d) { - (*d)->setPath((*s)->getPath()); - (*d)->addUris((*s)->getRemainingUris().begin(), - (*s)->getRemainingUris().end()); - if(!(*s)->isSingleHostMultiConnectionEnabled()) { - (*d)->disableSingleHostMultiConnection(); + const std::vector<SharedHandle<FileEntry> >& fileEntries = + context->getFileEntries(); + const std::vector<SharedHandle<FileEntry> >& dependantFileEntries = + _dependant->getDownloadContext()->getFileEntries(); + // If dependant's FileEntry::getOriginalName() is empty, we + // assume that torrent is single file. In Metalink3, this is + // always assumed. + if(fileEntries.size() == 1 && dependantFileEntries.size() == 1 && + dependantFileEntries[0]->getOriginalName().empty()) { + copyValues(fileEntries[0], dependantFileEntries[0]); + } else { + std::for_each(fileEntries.begin(), fileEntries.end(), + std::bind2nd(mem_fun_sh(&FileEntry::setRequested),false)); + // Copy file path in _dependant's FileEntries to newly created + // context's FileEntries to endorse the path structure of + // _dependant. URIs and singleHostMultiConnection are also copied. + for(std::vector<SharedHandle<FileEntry> >::const_iterator s = + dependantFileEntries.begin(); s != dependantFileEntries.end(); + ++s){ + std::vector<SharedHandle<FileEntry> >::const_iterator d = + context->getFileEntries().begin(); + for(; d != context->getFileEntries().end(); ++d) { + if((*d)->getOriginalName() == (*s)->getOriginalName()) { + break; + } + } + if(d == context->getFileEntries().end()) { + throw DL_ABORT_EX + (StringFormat("No entry %s in torrent file", + (*s)->getOriginalName().c_str()).str()); + } + copyValues(*d, *s); } } } catch(RecoverableException& e) { diff --git a/src/FileEntry.h b/src/FileEntry.h index 2dfe7eda..0199ba28 100644 --- a/src/FileEntry.h +++ b/src/FileEntry.h @@ -70,6 +70,7 @@ private: // available. std::deque<URIResult> _uriResults; bool _singleHostMultiConnection; + std::string _originalName; Logger* _logger; void storePool(const SharedHandle<Request>& request); @@ -227,6 +228,16 @@ public: void reuseUri(size_t num); void releaseRuntimeResource(); + + void setOriginalName(const std::string& originalName) + { + _originalName = originalName; + } + + const std::string& getOriginalName() const + { + return _originalName; + } }; typedef SharedHandle<FileEntry> FileEntryHandle; diff --git a/src/Metalink2RequestGroup.cc b/src/Metalink2RequestGroup.cc index 74e1b2d5..4ccd924e 100644 --- a/src/Metalink2RequestGroup.cc +++ b/src/Metalink2RequestGroup.cc @@ -136,57 +136,66 @@ void removeMetalinkContentTypes(const SharedHandle<RequestGroup>& group) void Metalink2RequestGroup::createRequestGroup (std::deque<SharedHandle<RequestGroup> >& groups, - std::deque<SharedHandle<MetalinkEntry> > entries, + const std::deque<SharedHandle<MetalinkEntry> >& entries, const SharedHandle<Option>& option) { - if(entries.size() == 0) { + if(entries.empty()) { _logger->notice(EX_NO_RESULT_WITH_YOUR_PREFS); return; } std::deque<int32_t> selectIndexes = util::parseIntRange(option->get(PREF_SELECT_FILE)).flush(); - bool useIndex; - if(selectIndexes.size()) { - useIndex = true; - } else { - useIndex = false; + std::sort(selectIndexes.begin(), selectIndexes.end()); + std::vector<SharedHandle<MetalinkEntry> > selectedEntries; + selectedEntries.reserve(entries.size()); + + std::deque<std::string> locations; + if(option->defined(PREF_METALINK_LOCATION)) { + util::split(option->get(PREF_METALINK_LOCATION), + std::back_inserter(locations), ",", true); + std::transform + (locations.begin(), locations.end(), locations.begin(), util::toLower); } - int32_t count = 0; - for(std::deque<SharedHandle<MetalinkEntry> >::iterator itr = entries.begin(); itr != entries.end(); - ++itr, ++count) { - SharedHandle<MetalinkEntry>& entry = *itr; - if(option->defined(PREF_METALINK_LOCATION)) { - std::deque<std::string> locations; - util::split(option->get(PREF_METALINK_LOCATION), - std::back_inserter(locations), ",", true); - std::transform - (locations.begin(), locations.end(), locations.begin(), util::toLower); - entry->setLocationPriority - (locations, -MetalinkResource::getLowestPriority()); - } - if(option->get(PREF_METALINK_PREFERRED_PROTOCOL) != V_NONE) { - entry->setProtocolPriority - (option->get(PREF_METALINK_PREFERRED_PROTOCOL), - -MetalinkResource::getLowestPriority()); - } - if(useIndex) { - if(std::find(selectIndexes.begin(), selectIndexes.end(), count+1) == - selectIndexes.end()) { + std::string preferredProtocol; + if(option->get(PREF_METALINK_PREFERRED_PROTOCOL) != V_NONE) { + preferredProtocol = option->get(PREF_METALINK_PREFERRED_PROTOCOL); + } + { + int32_t count = 1; + for(std::deque<SharedHandle<MetalinkEntry> >::const_iterator i = + entries.begin(); i != entries.end(); ++i, ++count) { + (*i)->dropUnsupportedResource(); + if((*i)->resources.empty() && (*i)->metaurls.empty()) { continue; } + (*i)->setLocationPriority + (locations, -MetalinkResource::getLowestPriority()); + if(!preferredProtocol.empty()) { + (*i)->setProtocolPriority + (preferredProtocol, -MetalinkResource::getLowestPriority()); + } + if(selectIndexes.empty() || + std::binary_search(selectIndexes.begin(), selectIndexes.end(), count)){ + selectedEntries.push_back(*i); + } } - entry->dropUnsupportedResource(); - if(entry->resources.empty() && entry->metaurls.empty()) { - continue; - } - _logger->info(MSG_METALINK_QUEUEING, entry->getPath().c_str()); + } + std::for_each(entries.begin(), entries.end(), + mem_fun_sh(&MetalinkEntry::reorderMetaurlsByPriority)); + std::vector<std::pair<std::string, + std::vector<SharedHandle<MetalinkEntry> > > > entryGroups; + MetalinkHelper::groupEntryByMetaurlName(entryGroups, selectedEntries); + for(std::vector<std::pair<std::string, + std::vector<SharedHandle<MetalinkEntry> > > >::const_iterator itr = + entryGroups.begin(); itr != entryGroups.end(); ++itr) { + const std::string& metaurl = (*itr).first; + const std::vector<SharedHandle<MetalinkEntry> >& mes = (*itr).second; + _logger->info("Processing metaurl group metaurl=%s", metaurl.c_str()); #ifdef ENABLE_BITTORRENT SharedHandle<RequestGroup> torrentRg; - if(!entry->metaurls.empty()) { - entry->reorderMetaurlsByPriority(); - // there is torrent entry + if(!metaurl.empty()) { std::deque<std::string> uris; - uris.push_back(entry->metaurls[0]->url); + uris.push_back(metaurl); { std::deque<SharedHandle<RequestGroup> > result; createRequestGroupForUri(result, option, uris, @@ -213,62 +222,94 @@ Metalink2RequestGroup::createRequestGroup } } #endif // ENABLE_BITTORRENT - entry->reorderResourcesByPriority(); - std::deque<std::string> uris; - std::for_each(entry->resources.begin(), entry->resources.end(), - AccumulateNonP2PUrl(uris)); SharedHandle<RequestGroup> rg(new RequestGroup(option)); - // If piece hash is specified in the metalink, - // make segment size equal to piece hash size. - size_t pieceLength; + SharedHandle<DownloadContext> dctx; + if(mes.size() == 1) { + SharedHandle<MetalinkEntry> entry = mes[0]; + _logger->info(MSG_METALINK_QUEUEING, entry->getPath().c_str()); + entry->reorderResourcesByPriority(); + std::deque<std::string> uris; + std::for_each(entry->resources.begin(), entry->resources.end(), + AccumulateNonP2PUrl(uris)); + // If piece hash is specified in the metalink, + // make segment size equal to piece hash size. + size_t pieceLength; #ifdef ENABLE_MESSAGE_DIGEST - if(entry->chunkChecksum.isNull()) { - pieceLength = option->getAsInt(PREF_SEGMENT_SIZE); - } else { - pieceLength = entry->chunkChecksum->getChecksumLength(); - } + if(entry->chunkChecksum.isNull()) { + pieceLength = option->getAsInt(PREF_SEGMENT_SIZE); + } else { + pieceLength = entry->chunkChecksum->getChecksumLength(); + } #else - pieceLength = option->getAsInt(PREF_SEGMENT_SIZE); + pieceLength = option->getAsInt(PREF_SEGMENT_SIZE); #endif // ENABLE_MESSAGE_DIGEST - SharedHandle<DownloadContext> dctx - (new DownloadContext - (pieceLength, - entry->getLength(), - util::applyDir(option->get(PREF_DIR), entry->file->getPath()))); - dctx->setDir(option->get(PREF_DIR)); - dctx->getFirstFileEntry()->setUris(uris); - if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) { - dctx->getFirstFileEntry()->disableSingleHostMultiConnection(); - } + dctx.reset(new DownloadContext + (pieceLength, + entry->getLength(), + util::applyDir(option->get(PREF_DIR), + entry->file->getPath()))); + dctx->getFirstFileEntry()->setUris(uris); + if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) { + dctx->getFirstFileEntry()->disableSingleHostMultiConnection(); + } #ifdef ENABLE_MESSAGE_DIGEST - if(entry->chunkChecksum.isNull()) { - if(!entry->checksum.isNull()) { - dctx->setChecksum(entry->checksum->getMessageDigest()); - dctx->setChecksumHashAlgo(entry->checksum->getAlgo()); + if(entry->chunkChecksum.isNull()) { + if(!entry->checksum.isNull()) { + dctx->setChecksum(entry->checksum->getMessageDigest()); + dctx->setChecksumHashAlgo(entry->checksum->getAlgo()); + } + } else { + dctx->setPieceHashes(entry->chunkChecksum->getChecksums().begin(), + entry->chunkChecksum->getChecksums().end()); + dctx->setPieceHashAlgo(entry->chunkChecksum->getAlgo()); } +#endif // ENABLE_MESSAGE_DIGEST + dctx->setSignature(entry->getSignature()); + rg->setNumConcurrentCommand + (entry->maxConnections < 0 ? + option->getAsInt(PREF_METALINK_SERVERS) : + std::min(option->getAsInt(PREF_METALINK_SERVERS), + static_cast<int32_t>(entry->maxConnections))); } else { - dctx->setPieceHashes(entry->chunkChecksum->getChecksums().begin(), - entry->chunkChecksum->getChecksums().end()); - dctx->setPieceHashAlgo(entry->chunkChecksum->getAlgo()); + dctx.reset(new DownloadContext()); + // piece length is overridden by the one in torrent file. + dctx->setPieceLength(option->getAsInt(PREF_SEGMENT_SIZE)); + std::vector<SharedHandle<FileEntry> > fileEntries; + off_t offset = 0; + for(std::deque<SharedHandle<MetalinkEntry> >::const_iterator i = + entries.begin(); i != entries.end(); ++i) { + _logger->info("Metalink: Queueing %s for download as a member.", + (*i)->getPath().c_str()); + _logger->debug("originalName = %s", (*i)->metaurls[0]->name.c_str()); + (*i)->reorderResourcesByPriority(); + std::deque<std::string> uris; + std::for_each((*i)->resources.begin(), (*i)->resources.end(), + AccumulateNonP2PUrl(uris)); + SharedHandle<FileEntry> fe + (new FileEntry + (util::applyDir(option->get(PREF_DIR), (*i)->file->getPath()), + (*i)->file->getLength(), offset, uris)); + if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) { + fe->disableSingleHostMultiConnection(); + } + fe->setOriginalName((*i)->metaurls[0]->name); + fileEntries.push_back(fe); + offset += (*i)->file->getLength(); + } + dctx->setFileEntries(fileEntries.begin(), fileEntries.end()); + rg->setNumConcurrentCommand(option->getAsInt(PREF_METALINK_SERVERS)); } -#endif // ENABLE_MESSAGE_DIGEST - dctx->setSignature(entry->getSignature()); + dctx->setDir(option->get(PREF_DIR)); rg->setDownloadContext(dctx); - rg->setNumConcurrentCommand - (entry->maxConnections < 0 ? - option->getAsInt(PREF_METALINK_SERVERS) : - std::min(option->getAsInt(PREF_METALINK_SERVERS), - static_cast<int32_t>(entry->maxConnections))); - // remove "metalink" from Accept Type list to avoid loop in tranparent - // metalink + // remove "metalink" from Accept Type list to avoid loop in + // tranparent metalink removeMetalinkContentTypes(rg); - #ifdef ENABLE_BITTORRENT - // Inject depenency between rg and torrentRg here if torrentRg.isNull() == false + // Inject depenency between rg and torrentRg here if + // torrentRg.isNull() == false if(!torrentRg.isNull()) { SharedHandle<Dependency> dep(new BtDependency(rg, torrentRg)); rg->dependsOn(dep); - torrentRg->belongsTo(rg->getGID()); } #endif // ENABLE_BITTORRENT diff --git a/src/Metalink2RequestGroup.h b/src/Metalink2RequestGroup.h index cacbb333..a3d84011 100644 --- a/src/Metalink2RequestGroup.h +++ b/src/Metalink2RequestGroup.h @@ -54,7 +54,7 @@ private: void createRequestGroup(std::deque<SharedHandle<RequestGroup> >& groups, - std::deque<SharedHandle<MetalinkEntry> > entries, + const std::deque<SharedHandle<MetalinkEntry> >& entries, const SharedHandle<Option>& option); public: Metalink2RequestGroup(); diff --git a/src/MetalinkEntry.cc b/src/MetalinkEntry.cc index 218f8ba5..75e5dc36 100644 --- a/src/MetalinkEntry.cc +++ b/src/MetalinkEntry.cc @@ -51,7 +51,7 @@ namespace aria2 { MetalinkEntry::MetalinkEntry(): - file(0), + sizeKnown(false), maxConnections(-1) {} @@ -94,7 +94,7 @@ MetalinkEntry& MetalinkEntry::operator=(const MetalinkEntry& metalinkEntry) return *this; } -std::string MetalinkEntry::getPath() const +const std::string& MetalinkEntry::getPath() const { return file->getPath(); } diff --git a/src/MetalinkEntry.h b/src/MetalinkEntry.h index 4b10ec82..2fbb6795 100644 --- a/src/MetalinkEntry.h +++ b/src/MetalinkEntry.h @@ -61,6 +61,8 @@ public: std::string version; std::vector<std::string> languages; std::vector<std::string> oses; + // True if size is specified in Metalink document. + bool sizeKnown; std::deque<SharedHandle<MetalinkResource> > resources; std::vector<SharedHandle<MetalinkMetaurl> > metaurls; int maxConnections; // Metalink3Spec @@ -77,7 +79,7 @@ public: MetalinkEntry& operator=(const MetalinkEntry& metalinkEntry); - std::string getPath() const; + const std::string& getPath() const; uint64_t getLength() const; diff --git a/src/MetalinkHelper.cc b/src/MetalinkHelper.cc index c26d1d03..abfbc99a 100644 --- a/src/MetalinkHelper.cc +++ b/src/MetalinkHelper.cc @@ -41,6 +41,7 @@ #include "prefs.h" #include "DlAbortEx.h" #include "BinaryStream.h" +#include "MetalinkMetaurl.h" namespace aria2 { @@ -79,4 +80,41 @@ void MetalinkHelper::query option->get(PREF_METALINK_OS)); } +void MetalinkHelper::groupEntryByMetaurlName +(std::vector< + std::pair<std::string, std::vector<SharedHandle<MetalinkEntry> > > >& result, + const std::vector<SharedHandle<MetalinkEntry> >& entries) +{ + for(std::vector<SharedHandle<MetalinkEntry> >::const_iterator eiter = + entries.begin(); eiter != entries.end(); ++eiter) { + if((*eiter)->metaurls.empty()) { + std::pair<std::string, std::vector<SharedHandle<MetalinkEntry> > > p; + p.second.push_back(*eiter); + result.push_back(p); + } else { + std::vector< + std::pair<std::string, + std::vector<SharedHandle<MetalinkEntry> > > >::iterator i = + result.begin(); + if((*eiter)->metaurls[0]->name.empty() || + !(*eiter)->sizeKnown) { + i = result.end(); + } + for(; i != result.end(); ++i) { + if((*i).first == (*eiter)->metaurls[0]->url && + !(*i).second[0]->metaurls[0]->name.empty()) { + (*i).second.push_back(*eiter); + break; + } + } + if(i == result.end()) { + std::pair<std::string, std::vector<SharedHandle<MetalinkEntry> > > p; + p.first = (*eiter)->metaurls[0]->url; + p.second.push_back(*eiter); + result.push_back(p); + } + } + } +} + } // namespace aria2 diff --git a/src/MetalinkHelper.h b/src/MetalinkHelper.h index 0b329576..10586fc5 100644 --- a/src/MetalinkHelper.h +++ b/src/MetalinkHelper.h @@ -36,9 +36,12 @@ #define _D_METALINK_HELPER_H_ #include "common.h" -#include "SharedHandle.h" + #include <string> #include <deque> +#include <vector> + +#include "SharedHandle.h" namespace aria2 { @@ -65,6 +68,11 @@ public: static void parseAndQuery (std::deque<SharedHandle<MetalinkEntry> >& result, const SharedHandle<BinaryStream>& binaryStream, const Option* option); + + static void groupEntryByMetaurlName + (std::vector< + std::pair<std::string, std::vector<SharedHandle<MetalinkEntry> > > >& result, + const std::vector<SharedHandle<MetalinkEntry> >& entries); }; } // namespace aria2 diff --git a/src/MetalinkMetaurl.cc b/src/MetalinkMetaurl.cc index cdd47d08..2058fcc1 100644 --- a/src/MetalinkMetaurl.cc +++ b/src/MetalinkMetaurl.cc @@ -42,4 +42,9 @@ const std::string MetalinkMetaurl::MEDIATYPE_TORRENT("torrent"); MetalinkMetaurl::MetalinkMetaurl(): priority(MetalinkResource::getLowestPriority()) {} +MetalinkMetaurl::MetalinkMetaurl +(const std::string& url, const std::string& mediatype, + const std::string& name, int priority): + url(url), mediatype(mediatype), name(name), priority(priority) {} + } // namespace aria2 diff --git a/src/MetalinkMetaurl.h b/src/MetalinkMetaurl.h index 748ee872..b237d2fc 100644 --- a/src/MetalinkMetaurl.h +++ b/src/MetalinkMetaurl.h @@ -45,11 +45,14 @@ class MetalinkMetaurl { public: std::string url; std::string mediatype; - int priority; std::string name; + int priority; MetalinkMetaurl(); + MetalinkMetaurl(const std::string& url, const std::string& mediatype, + const std::string& name, int priority); + static const std::string MEDIATYPE_TORRENT; }; diff --git a/src/MetalinkParserController.cc b/src/MetalinkParserController.cc index 5a1be233..cdb97d32 100644 --- a/src/MetalinkParserController.cc +++ b/src/MetalinkParserController.cc @@ -100,6 +100,7 @@ void MetalinkParserController::setFileLengthOfEntry(uint64_t length) } else { _tEntry->file->setLength(length); } + _tEntry->sizeKnown = true; } void MetalinkParserController::setVersionOfEntry(const std::string& version) diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index dcc8af43..05fc74dd 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -205,6 +205,8 @@ void RequestGroup::createInitialCommand SharedHandle<BtRegistry> btRegistry = e->getBtRegistry(); if(!btRegistry->getDownloadContext (torrentAttrs[bittorrent::INFO_HASH].s()).isNull()) { + // TODO If metadataGetMode == false and each FileEntry has + // URI, then go without BT. throw DOWNLOAD_FAILURE_EXCEPTION (StringFormat ("InfoHash %s is already registered.", @@ -284,14 +286,7 @@ void RequestGroup::createInitialCommand return; } - - // Remove the control file if download file doesn't exist - if(progressInfoFile->exists() && !_pieceStorage->getDiskAdaptor()->fileExists()) { - progressInfoFile->removeFile(); - _logger->notice(MSG_REMOVED_DEFUNCT_CONTROL_FILE, - progressInfoFile->getFilename().c_str(), - _downloadContext->getBasePath().c_str()); - } + removeDefunctControlFile(progressInfoFile); { uint64_t actualFileSize = _pieceStorage->getDiskAdaptor()->size(); if(actualFileSize == _downloadContext->getTotalLength()) { @@ -372,42 +367,80 @@ void RequestGroup::createInitialCommand } } #endif // ENABLE_BITTORRENT - // TODO Currently, BitTorrent+WEB-Seeding is only way to download - // multiple files in one RequestGroup. In this context, we don't - // have BitTorrent, so add assertion here. This situation will be - // changed if Metalink spec is formalized to support multi-file - // torrent. - assert(_downloadContext->getFileEntries().size() == 1); - // TODO I assume here when totallength is set to DownloadContext and it is - // not 0, then filepath is also set DownloadContext correctly.... - if(_option->getAsBool(PREF_DRY_RUN) || - _downloadContext->getTotalLength() == 0) { - createNextCommand(commands, e, 1); - }else { + if(_downloadContext->getFileEntries().size() == 1) { + // TODO I assume here when totallength is set to DownloadContext and it is + // not 0, then filepath is also set DownloadContext correctly.... + if(_option->getAsBool(PREF_DRY_RUN) || + _downloadContext->getTotalLength() == 0) { + createNextCommand(commands, e, 1); + }else { + if(e->_requestGroupMan->isSameFileBeingDownloaded(this)) { + throw DOWNLOAD_FAILURE_EXCEPTION + (StringFormat(EX_DUPLICATE_FILE_DOWNLOAD, + _downloadContext->getBasePath().c_str()).str()); + } + adjustFilename + (SharedHandle<BtProgressInfoFile>(new DefaultBtProgressInfoFile + (_downloadContext, + SharedHandle<PieceStorage>(), + _option.get()))); + initPieceStorage(); + BtProgressInfoFileHandle infoFile + (new DefaultBtProgressInfoFile(_downloadContext, _pieceStorage, + _option.get())); + if(!infoFile->exists() && downloadFinishedByFileLength()) { + _pieceStorage->markAllPiecesDone(); + _logger->notice(MSG_DOWNLOAD_ALREADY_COMPLETED, + _gid, _downloadContext->getBasePath().c_str()); + } else { + loadAndOpenFile(infoFile); + SharedHandle<CheckIntegrityEntry> checkIntegrityEntry + (new StreamCheckIntegrityEntry(this)); + processCheckIntegrityEntry(commands, checkIntegrityEntry, e); + } + } + } else { + // In this context, multiple FileEntry objects are in + // DownloadContext. if(e->_requestGroupMan->isSameFileBeingDownloaded(this)) { throw DOWNLOAD_FAILURE_EXCEPTION (StringFormat(EX_DUPLICATE_FILE_DOWNLOAD, _downloadContext->getBasePath().c_str()).str()); } - adjustFilename - (SharedHandle<BtProgressInfoFile>(new DefaultBtProgressInfoFile - (_downloadContext, - SharedHandle<PieceStorage>(), - _option.get()))); initPieceStorage(); - BtProgressInfoFileHandle infoFile - (new DefaultBtProgressInfoFile(_downloadContext, _pieceStorage, + if(_downloadContext->getFileEntries().size() > 1) { + _pieceStorage->setupFileFilter(); + } + SharedHandle<DefaultBtProgressInfoFile> progressInfoFile + (new DefaultBtProgressInfoFile(_downloadContext, + _pieceStorage, _option.get())); - if(!infoFile->exists() && downloadFinishedByFileLength()) { - _pieceStorage->markAllPiecesDone(); - _logger->notice(MSG_DOWNLOAD_ALREADY_COMPLETED, - _gid, _downloadContext->getBasePath().c_str()); + removeDefunctControlFile(progressInfoFile); + // Call Load, Save and file allocation command here + if(progressInfoFile->exists()) { + // load .aria2 file if it exists. + progressInfoFile->load(); + _pieceStorage->getDiskAdaptor()->openFile(); } else { - loadAndOpenFile(infoFile); - SharedHandle<CheckIntegrityEntry> checkIntegrityEntry - (new StreamCheckIntegrityEntry(this)); - processCheckIntegrityEntry(commands, checkIntegrityEntry, e); + if(_pieceStorage->getDiskAdaptor()->fileExists()) { + if(!_option->getAsBool(PREF_CHECK_INTEGRITY) && + !_option->getAsBool(PREF_ALLOW_OVERWRITE)) { + // TODO we need this->haltRequested = true? + throw DOWNLOAD_FAILURE_EXCEPTION + (StringFormat + (MSG_FILE_ALREADY_EXISTS, + _downloadContext->getBasePath().c_str()).str()); + } else { + _pieceStorage->getDiskAdaptor()->openFile(); + } + } else { + _pieceStorage->getDiskAdaptor()->openFile(); + } } + _progressInfoFile = progressInfoFile; + SharedHandle<CheckIntegrityEntry> checkIntegrityEntry + (new StreamCheckIntegrityEntry(this)); + processCheckIntegrityEntry(commands, checkIntegrityEntry, e); } } @@ -543,6 +576,19 @@ void RequestGroup::adjustFilename } } +void RequestGroup::removeDefunctControlFile +(const SharedHandle<BtProgressInfoFile>& progressInfoFile) +{ + // Remove the control file if download file doesn't exist + if(progressInfoFile->exists() && + !_pieceStorage->getDiskAdaptor()->fileExists()) { + progressInfoFile->removeFile(); + _logger->notice(MSG_REMOVED_DEFUNCT_CONTROL_FILE, + progressInfoFile->getFilename().c_str(), + _downloadContext->getBasePath().c_str()); + } +} + void RequestGroup::loadAndOpenFile(const BtProgressInfoFileHandle& progressInfoFile) { try { @@ -550,14 +596,7 @@ void RequestGroup::loadAndOpenFile(const BtProgressInfoFileHandle& progressInfoF _pieceStorage->getDiskAdaptor()->initAndOpenFile(); return; } - // Remove the control file if download file doesn't exist - if(progressInfoFile->exists() && !_pieceStorage->getDiskAdaptor()->fileExists()) { - progressInfoFile->removeFile(); - _logger->notice(MSG_REMOVED_DEFUNCT_CONTROL_FILE, - progressInfoFile->getFilename().c_str(), - _downloadContext->getBasePath().c_str()); - } - + removeDefunctControlFile(progressInfoFile); if(progressInfoFile->exists()) { progressInfoFile->load(); _pieceStorage->getDiskAdaptor()->openExistingFile(); diff --git a/src/RequestGroup.h b/src/RequestGroup.h index 3d33dd04..4d85ec40 100644 --- a/src/RequestGroup.h +++ b/src/RequestGroup.h @@ -180,6 +180,9 @@ private: // _uriResults, then last result code is returned. Otherwise // returns downloadresultcode::UNKNOWN_ERROR. downloadresultcode::RESULT downloadResult() const; + + void removeDefunctControlFile + (const SharedHandle<BtProgressInfoFile>& progressInfoFile); public: // The copy of option is stored in RequestGroup object. RequestGroup(const SharedHandle<Option>& option); diff --git a/src/bittorrent_helper.cc b/src/bittorrent_helper.cc index 52b9f78f..fc8a3b49 100644 --- a/src/bittorrent_helper.cc +++ b/src/bittorrent_helper.cc @@ -263,6 +263,7 @@ static void extractFileEntries (new FileEntry(util::applyDir(ctx->getDir(), path), fileLengthData.i(), offset, uris)); + fileEntry->setOriginalName(path); fileEntries.push_back(fileEntry); offset += fileEntry->getLength(); } @@ -291,6 +292,7 @@ static void extractFileEntries SharedHandle<FileEntry> fileEntry (new FileEntry(util::applyDir(ctx->getDir(), name),totalLength, 0, uris)); + fileEntry->setOriginalName(name); fileEntries.push_back(fileEntry); } ctx->setFileEntries(fileEntries.begin(), fileEntries.end()); diff --git a/test/BittorrentHelperTest.cc b/test/BittorrentHelperTest.cc index ab1a2c97..45cbd19a 100644 --- a/test/BittorrentHelperTest.cc +++ b/test/BittorrentHelperTest.cc @@ -168,6 +168,8 @@ void BittorrentHelperTest::testGetFileEntries() { SharedHandle<FileEntry> fileEntry1 = *itr; CPPUNIT_ASSERT_EQUAL(std::string("./aria2-test/aria2/src/aria2c"), fileEntry1->getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("aria2-test/aria2/src/aria2c"), + fileEntry1->getOriginalName()); itr++; SharedHandle<FileEntry> fileEntry2 = *itr; CPPUNIT_ASSERT_EQUAL(std::string("./aria2-test/aria2-0.2.2.tar.bz2"), @@ -186,6 +188,8 @@ void BittorrentHelperTest::testGetFileEntriesSingle() { SharedHandle<FileEntry> fileEntry1 = *itr; CPPUNIT_ASSERT_EQUAL(std::string("./aria2-0.8.2.tar.bz2"), fileEntry1->getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.8.2.tar.bz2"), + fileEntry1->getOriginalName()); } void BittorrentHelperTest::testGetTotalLength() { diff --git a/test/BtDependencyTest.cc b/test/BtDependencyTest.cc index 0fe912e2..312fa54b 100644 --- a/test/BtDependencyTest.cc +++ b/test/BtDependencyTest.cc @@ -23,6 +23,9 @@ class BtDependencyTest:public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(BtDependencyTest); CPPUNIT_TEST(testResolve); + CPPUNIT_TEST(testResolve_originalNameNoMatch); + CPPUNIT_TEST(testResolve_singleFileWithoutOriginalName); + CPPUNIT_TEST(testResolve_multiFile); CPPUNIT_TEST(testResolve_metadata); CPPUNIT_TEST(testResolve_loadError); CPPUNIT_TEST(testResolve_dependeeFailure); @@ -37,7 +40,9 @@ class BtDependencyTest:public CppUnit::TestFixture { dctx->setDir("/tmp"); std::deque<std::string> uris; uris.push_back("http://localhost/outfile.path"); - dctx->getFirstFileEntry()->setUris(uris); + SharedHandle<FileEntry> fileEntry = dctx->getFirstFileEntry(); + fileEntry->setUris(uris); + fileEntry->setOriginalName("aria2-0.8.2.tar.bz2"); dependant->setDownloadContext(dctx); return dependant; } @@ -65,6 +70,9 @@ public: } void testResolve(); + void testResolve_originalNameNoMatch(); + void testResolve_singleFileWithoutOriginalName(); + void testResolve_multiFile(); void testResolve_metadata(); void testResolve_loadError(); void testResolve_dependeeFailure(); @@ -93,6 +101,64 @@ void BtDependencyTest::testResolve() CPPUNIT_ASSERT_EQUAL(std::string("/tmp/outfile.path"), firstFileEntry->getPath()); CPPUNIT_ASSERT_EQUAL((size_t)1, firstFileEntry->getRemainingUris().size()); + CPPUNIT_ASSERT(firstFileEntry->isRequested()); +} + +void BtDependencyTest::testResolve_originalNameNoMatch() +{ + std::string filename = "single.torrent"; + SharedHandle<RequestGroup> dependant = createDependant(_option); + dependant->getDownloadContext()->getFirstFileEntry()->setOriginalName + ("aria2-1.1.0.tar.bz2"); + SharedHandle<RequestGroup> dependee = + createDependee(_option, filename, File(filename).size()); + dependee->getPieceStorage()->markAllPiecesDone(); + + BtDependency dep(dependant, dependee); + CPPUNIT_ASSERT(dep.resolve()); + + CPPUNIT_ASSERT(!dependant->getDownloadContext()->hasAttribute + (bittorrent::BITTORRENT)); +} + +void BtDependencyTest::testResolve_singleFileWithoutOriginalName() +{ + std::string filename = "single.torrent"; + SharedHandle<RequestGroup> dependant = createDependant(_option); + dependant->getDownloadContext()->getFirstFileEntry()->setOriginalName(""); + SharedHandle<RequestGroup> dependee = + createDependee(_option, filename, File(filename).size()); + dependee->getPieceStorage()->markAllPiecesDone(); + BtDependency dep(dependant, dependee); + CPPUNIT_ASSERT(dep.resolve()); + CPPUNIT_ASSERT(dependant->getDownloadContext()->hasAttribute + (bittorrent::BITTORRENT)); +} + +void BtDependencyTest::testResolve_multiFile() +{ + std::string filename = "test.torrent"; + SharedHandle<RequestGroup> dependant = createDependant(_option); + dependant->getDownloadContext()->getFirstFileEntry()->setOriginalName + ("aria2-test/aria2/src/aria2c"); + SharedHandle<RequestGroup> dependee = + createDependee(_option, filename, File(filename).size()); + dependee->getPieceStorage()->markAllPiecesDone(); + + BtDependency dep(dependant, dependee); + CPPUNIT_ASSERT(dep.resolve()); + + CPPUNIT_ASSERT(dependant->getDownloadContext()->hasAttribute + (bittorrent::BITTORRENT)); + + const std::vector<SharedHandle<FileEntry> >& fileEntries = + dependant->getDownloadContext()->getFileEntries(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp/outfile.path"), + fileEntries[0]->getPath()); + CPPUNIT_ASSERT(fileEntries[0]->isRequested()); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp/aria2-test/aria2-0.2.2.tar.bz2"), + fileEntries[1]->getPath()); + CPPUNIT_ASSERT(!fileEntries[1]->isRequested()); } void BtDependencyTest::testResolve_metadata() @@ -121,6 +187,8 @@ void BtDependencyTest::testResolve_metadata() CPPUNIT_ASSERT_EQUAL (std::string("cd41c7fdddfd034a15a04d7ff881216e01c4ceaf"), bittorrent::getInfoHashString(dependant->getDownloadContext())); + CPPUNIT_ASSERT + (dependant->getDownloadContext()->getFirstFileEntry()->isRequested()); } void BtDependencyTest::testResolve_loadError() diff --git a/test/MetalinkHelperTest.cc b/test/MetalinkHelperTest.cc index 902e654f..3886ac9b 100644 --- a/test/MetalinkHelperTest.cc +++ b/test/MetalinkHelperTest.cc @@ -1,8 +1,11 @@ #include "MetalinkHelper.h" + +#include <cppunit/extensions/HelperMacros.h> + #include "MetalinkEntry.h" #include "Option.h" #include "prefs.h" -#include <cppunit/extensions/HelperMacros.h> +#include "MetalinkMetaurl.h" namespace aria2 { @@ -11,16 +14,14 @@ class MetalinkHelperTest:public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MetalinkHelperTest); CPPUNIT_TEST(testParseAndQuery); CPPUNIT_TEST(testParseAndQuery_version); + CPPUNIT_TEST(testGroupEntryByMetaurlName); CPPUNIT_TEST_SUITE_END(); private: public: - void setUp() {} - - void tearDown() {} - void testParseAndQuery(); void testParseAndQuery_version(); + void testGroupEntryByMetaurlName(); }; @@ -45,4 +46,70 @@ void MetalinkHelperTest::testParseAndQuery_version() CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.5.1.tar.bz2"), entry->getPath()); } +void MetalinkHelperTest::testGroupEntryByMetaurlName() +{ + std::vector<SharedHandle<MetalinkEntry> > entries; + + SharedHandle<MetalinkEntry> e1(new MetalinkEntry()); + e1->version = "1"; + e1->sizeKnown = true; + // no name + e1->metaurls.push_back + (SharedHandle<MetalinkMetaurl> + (new MetalinkMetaurl("http://meta1", "torrent", "", 1))); + + SharedHandle<MetalinkEntry> e2(new MetalinkEntry()); + e2->version = "2"; + e2->sizeKnown = true; + + SharedHandle<MetalinkEntry> e3(new MetalinkEntry()); + e3->version = "3"; + e3->sizeKnown = true; + e3->metaurls.push_back + (SharedHandle<MetalinkMetaurl> + (new MetalinkMetaurl("http://meta2", "torrent", "f3", 1))); + + SharedHandle<MetalinkEntry> e4(new MetalinkEntry()); + e4->version = "4"; + e4->sizeKnown = true; + e4->metaurls.push_back + (SharedHandle<MetalinkMetaurl> + (new MetalinkMetaurl("http://meta1", "torrent", "f4", 1))); + + SharedHandle<MetalinkEntry> e5(new MetalinkEntry()); + e5->version = "5"; + // no size + e5->metaurls.push_back + (SharedHandle<MetalinkMetaurl> + (new MetalinkMetaurl("http://meta1", "torrent", "f5", 1))); + + SharedHandle<MetalinkEntry> e6(new MetalinkEntry()); + e6->version = "6"; + e6->sizeKnown = true; + e6->metaurls.push_back + (SharedHandle<MetalinkMetaurl> + (new MetalinkMetaurl("http://meta1", "torrent", "f6", 1))); + + entries.push_back(e1); + entries.push_back(e2); + entries.push_back(e3); + entries.push_back(e4); + entries.push_back(e5); + entries.push_back(e6); + + std::vector<std::pair<std::string, + std::vector<SharedHandle<MetalinkEntry> > > > result; + MetalinkHelper::groupEntryByMetaurlName(result, entries); + + CPPUNIT_ASSERT_EQUAL(std::string("http://meta1"), result[0].first); + CPPUNIT_ASSERT_EQUAL(std::string("1"), result[0].second[0]->version); + CPPUNIT_ASSERT_EQUAL(std::string(""), result[1].first); + CPPUNIT_ASSERT_EQUAL(std::string("2"), result[1].second[0]->version); + CPPUNIT_ASSERT_EQUAL(std::string("http://meta2"), result[2].first); + CPPUNIT_ASSERT_EQUAL(std::string("3"), result[2].second[0]->version); + CPPUNIT_ASSERT_EQUAL(std::string("http://meta1"), result[3].first); + CPPUNIT_ASSERT_EQUAL(std::string("4"), result[3].second[0]->version); + CPPUNIT_ASSERT_EQUAL(std::string("6"), result[3].second[1]->version); +} + } // namespace aria2 |
