2010-07-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

Added --max-connection-per-server=NUM option. The default value of
	NUM is 1. This option limits the number of connections allowed to
	one server for each download. This means when NUM is 2 and 1 URI
	is provided, even if you specified -s 5, aria2 establishes 2
	connections. Default value of -j option is changed from 5 to 2.
	* src/BtDependency.cc
	* src/CreateRequestCommand.cc
	* src/FileEntry.cc
	* src/FileEntry.h
	* src/FtpNegotiationCommand.cc
	* src/HttpResponseCommand.cc
	* src/Metalink2RequestGroup.cc
	* src/OptionHandlerFactory.cc
	* src/TrackerWatcherCommand.cc
	* src/download_helper.cc
	* src/prefs.cc
	* src/prefs.h
	* src/usage_text.h
	* test/DownloadHelperTest.cc
	* test/FileEntryTest.cc
pull/1/head
Tatsuhiro Tsujikawa 2010-07-14 11:39:05 +00:00
parent 8274432f14
commit c99960aa33
16 changed files with 260 additions and 129 deletions

View File

@ -1,3 +1,26 @@
2010-07-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Added --max-connection-per-server=NUM option. The default value of
NUM is 1. This option limits the number of connections allowed to
one server for each download. This means when NUM is 2 and 1 URI
is provided, even if you specified -s 5, aria2 establishes 2
connections. Default value of -j option is changed from 5 to 2.
* src/BtDependency.cc
* src/CreateRequestCommand.cc
* src/FileEntry.cc
* src/FileEntry.h
* src/FtpNegotiationCommand.cc
* src/HttpResponseCommand.cc
* src/Metalink2RequestGroup.cc
* src/OptionHandlerFactory.cc
* src/TrackerWatcherCommand.cc
* src/download_helper.cc
* src/prefs.cc
* src/prefs.h
* src/usage_text.h
* test/DownloadHelperTest.cc
* test/FileEntryTest.cc
2010-07-12 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> 2010-07-12 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Set end byte in Range header if start byte > 0 to get more chance Set end byte in Range header if start byte > 0 to get more chance

View File

@ -66,9 +66,8 @@ static void copyValues(const SharedHandle<FileEntry>& d,
d->setPath(s->getPath()); d->setPath(s->getPath());
d->addUris(s->getRemainingUris().begin(), d->addUris(s->getRemainingUris().begin(),
s->getRemainingUris().end()); s->getRemainingUris().end());
if(!s->isSingleHostMultiConnectionEnabled()) { d->setMaxConnectionPerServer(s->getMaxConnectionPerServer());
d->disableSingleHostMultiConnection(); d->setUniqueProtocol(s->isUniqueProtocol());
}
} }
bool BtDependency::resolve() bool BtDependency::resolve()

View File

@ -74,13 +74,9 @@ bool CreateRequestCommand::executeInternal()
setFileEntry(getDownloadContext()->findFileEntryByOffset setFileEntry(getDownloadContext()->findFileEntryByOffset
(getSegments().front()->getPositionToWrite())); (getSegments().front()->getPositionToWrite()));
} }
if(getFileEntry()->getRemainingUris().empty() &&
getOption()->getAsBool(PREF_REUSE_URI) &&
getFileEntry()->countPooledRequest() == 0) {
getFileEntry()->reuseUri(getRequestGroup()->getNumConcurrentCommand());
}
setRequest setRequest
(getFileEntry()->getRequest(getRequestGroup()->getURISelector(), (getFileEntry()->getRequest(getRequestGroup()->getURISelector(),
getOption()->getAsBool(PREF_REUSE_URI),
getOption()->get(PREF_REFERER), getOption()->get(PREF_REFERER),
// Don't use HEAD request when file // Don't use HEAD request when file
// size is known. // size is known.

View File

@ -51,13 +51,15 @@ FileEntry::FileEntry(const std::string& path,
path_(path), uris_(uris.begin(), uris.end()), length_(length), path_(path), uris_(uris.begin(), uris.end()), length_(length),
offset_(offset), offset_(offset),
requested_(true), requested_(true),
singleHostMultiConnection_(true), uniqueProtocol_(false),
maxConnectionPerServer_(1),
lastFasterReplace_(0), lastFasterReplace_(0),
logger_(LogFactory::getInstance()) {} logger_(LogFactory::getInstance()) {}
FileEntry::FileEntry(): FileEntry::FileEntry():
length_(0), offset_(0), requested_(false), length_(0), offset_(0), requested_(false),
singleHostMultiConnection_(true), uniqueProtocol_(false),
maxConnectionPerServer_(1),
logger_(LogFactory::getInstance()) {} logger_(LogFactory::getInstance()) {}
FileEntry::~FileEntry() {} FileEntry::~FileEntry() {}
@ -106,53 +108,65 @@ std::string FileEntry::selectUri(const SharedHandle<URISelector>& uriSelector)
} }
template<typename InputIterator> template<typename InputIterator>
static bool inFlightHost(InputIterator first, InputIterator last, static size_t countInFlightHost(InputIterator first, InputIterator last,
const std::string& hostname) const std::string& hostname)
{ {
// TODO redirection should be considered here. We need to parse // TODO redirection should be considered here. We need to parse
// original URI to get hostname. // original URI to get hostname.
size_t count = 0;
for(; first != last; ++first) { for(; first != last; ++first) {
if((*first)->getHost() == hostname) { if((*first)->getHost() == hostname) {
return true; ++count;
} }
} }
return false; return count;
} }
SharedHandle<Request> SharedHandle<Request>
FileEntry::getRequest FileEntry::getRequest
(const SharedHandle<URISelector>& selector, (const SharedHandle<URISelector>& selector,
bool uriReuse,
const std::string& referer, const std::string& referer,
const std::string& method) const std::string& method)
{ {
SharedHandle<Request> req; SharedHandle<Request> req;
if(requestPool_.empty()) { if(requestPool_.empty()) {
std::vector<std::string> pending; for(int g = 0; g < 2; ++g) {
while(1) { std::vector<std::string> pending;
std::string uri = selector->select(this); std::vector<std::string> ignoreHost;
if(uri.empty()) { while(1) {
return req; std::string uri = selector->select(this);
} if(uri.empty()) {
req.reset(new Request()); break;
if(req->setUri(uri)) { }
if(!singleHostMultiConnection_) { req.reset(new Request());
if(inFlightHost(inFlightRequests_.begin(), inFlightRequests_.end(), if(req->setUri(uri)) {
req->getHost())) { if(countInFlightHost(inFlightRequests_.begin(),
inFlightRequests_.end(),
req->getHost()) >= maxConnectionPerServer_) {
pending.push_back(uri); pending.push_back(uri);
ignoreHost.push_back(req->getHost());
req.reset(); req.reset();
continue; continue;
} }
req->setReferer(referer);
req->setMethod(method);
spentUris_.push_back(uri);
inFlightRequests_.push_back(req);
break;
} else {
req.reset();
} }
req->setReferer(referer); }
req->setMethod(method); uris_.insert(uris_.begin(), pending.begin(), pending.end());
spentUris_.push_back(uri); // TODO UriReuse is performed only when PREF_REUSE_URI is true.
inFlightRequests_.push_back(req); if(g == 0 && uriReuse && req.isNull() && uris_.size() == pending.size()) {
break; // Reuse URIs other than ones in pending
reuseUri(ignoreHost);
} else { } else {
req.reset(); break;
} }
} }
uris_.insert(uris_.begin(), pending.begin(), pending.end());
} else { } else {
req = requestPool_.front(); req = requestPool_.front();
requestPool_.pop_front(); requestPool_.pop_front();
@ -290,8 +304,14 @@ void FileEntry::extractURIResult
uriResults_.erase(uriResults_.begin(), i); uriResults_.erase(uriResults_.begin(), i);
} }
void FileEntry::reuseUri(size_t num) void FileEntry::reuseUri(const std::vector<std::string>& ignore)
{ {
if(logger_->debug()) {
for(std::vector<std::string>::const_iterator i = ignore.begin(),
eoi = ignore.end(); i != eoi; ++i) {
logger_->debug("ignore host=%s", (*i).c_str());
}
}
std::deque<std::string> uris = spentUris_; std::deque<std::string> uris = spentUris_;
std::sort(uris.begin(), uris.end()); std::sort(uris.begin(), uris.end());
uris.erase(std::unique(uris.begin(), uris.end()), uris.end()); uris.erase(std::unique(uris.begin(), uris.end()), uris.end());
@ -302,11 +322,29 @@ void FileEntry::reuseUri(size_t num)
std::sort(errorUris.begin(), errorUris.end()); std::sort(errorUris.begin(), errorUris.end());
errorUris.erase(std::unique(errorUris.begin(), errorUris.end()), errorUris.erase(std::unique(errorUris.begin(), errorUris.end()),
errorUris.end()); errorUris.end());
if(logger_->debug()) {
for(std::vector<std::string>::const_iterator i = errorUris.begin(),
eoi = errorUris.end(); i != eoi; ++i) {
logger_->debug("error URI=%s", (*i).c_str());
}
}
std::vector<std::string> reusableURIs; std::vector<std::string> reusableURIs;
std::set_difference(uris.begin(), uris.end(), std::set_difference(uris.begin(), uris.end(),
errorUris.begin(), errorUris.end(), errorUris.begin(), errorUris.end(),
std::back_inserter(reusableURIs)); std::back_inserter(reusableURIs));
std::vector<std::string>::iterator insertionPoint = reusableURIs.begin();
Request req;
for(std::vector<std::string>::iterator i = reusableURIs.begin(),
eoi = reusableURIs.end(); i != eoi; ++i) {
req.setUri(*i);
if(std::find(ignore.begin(), ignore.end(), req.getHost()) == ignore.end()) {
if(i != insertionPoint) {
*insertionPoint = *i;
}
++insertionPoint;
}
}
reusableURIs.erase(insertionPoint, reusableURIs.end());
size_t ininum = reusableURIs.size(); size_t ininum = reusableURIs.size();
if(logger_->debug()) { if(logger_->debug()) {
logger_->debug("Found %u reusable URIs", static_cast<unsigned int>(ininum)); logger_->debug("Found %u reusable URIs", static_cast<unsigned int>(ininum));
@ -315,19 +353,7 @@ void FileEntry::reuseUri(size_t num)
logger_->debug("URI=%s", (*i).c_str()); logger_->debug("URI=%s", (*i).c_str());
} }
} }
// Reuse at least num URIs here to avoid to uris_.insert(uris_.end(), reusableURIs.begin(), reusableURIs.end());
// run this process repeatedly.
if(ininum > 0) {
for(size_t i = 0; i < num/ininum; ++i) {
uris_.insert(uris_.end(), reusableURIs.begin(), reusableURIs.end());
}
uris_.insert(uris_.end(), reusableURIs.begin(),
reusableURIs.begin()+(num%ininum));
if(logger_->debug()) {
logger_->debug("Duplication complete: now %u URIs for reuse",
static_cast<unsigned int>(uris_.size()));
}
}
} }
void FileEntry::releaseRuntimeResource() void FileEntry::releaseRuntimeResource()

View File

@ -70,7 +70,8 @@ private:
// URIResult is stored in the ascending order of the time when its result is // URIResult is stored in the ascending order of the time when its result is
// available. // available.
std::deque<URIResult> uriResults_; std::deque<URIResult> uriResults_;
bool singleHostMultiConnection_; bool uniqueProtocol_;
size_t maxConnectionPerServer_;
std::string originalName_; std::string originalName_;
Timer lastFasterReplace_; Timer lastFasterReplace_;
Logger* logger_; Logger* logger_;
@ -162,9 +163,13 @@ public:
// newly created Request. If Request object is retrieved from the // newly created Request. If Request object is retrieved from the
// pool, referer is ignored. If method is given, it is set to newly // pool, referer is ignored. If method is given, it is set to newly
// created Request. If Request object is retrieved from the pool, // created Request. If Request object is retrieved from the pool,
// method is ignored. // method is ignored. If uriReuse is true and selector does not
// returns Request object either because uris_ is empty or all URI
// are not be usable because maxConnectionPerServer_ limit, then
// reuse used URIs and do selection again.
SharedHandle<Request> getRequest SharedHandle<Request> getRequest
(const SharedHandle<URISelector>& selector, (const SharedHandle<URISelector>& selector,
bool uriReuse = true,
const std::string& referer = A2STR::NIL, const std::string& referer = A2STR::NIL,
const std::string& method = Request::METHOD_GET); const std::string& method = Request::METHOD_GET);
@ -215,21 +220,20 @@ public:
void extractURIResult void extractURIResult
(std::deque<URIResult>& res, downloadresultcode::RESULT r); (std::deque<URIResult>& res, downloadresultcode::RESULT r);
void disableSingleHostMultiConnection() void setMaxConnectionPerServer(size_t n)
{ {
singleHostMultiConnection_ = false; maxConnectionPerServer_ = n;
} }
bool isSingleHostMultiConnectionEnabled() const size_t getMaxConnectionPerServer() const
{ {
return singleHostMultiConnection_; return maxConnectionPerServer_;
} }
// Reuse URIs which have not emitted error so far. Thie method // Reuse URIs which have not emitted error so far and whose host
// tries to reuse at least num URIs. If less than num URIs found to // component is not included in ignore. The reusable URIs are
// reuse, those URIs are used more than once so that num URIs total // appended to uris_ maxConnectionPerServer_ times.
// are available to reuse. void reuseUri(const std::vector<std::string>& ignore);
void reuseUri(size_t num);
void releaseRuntimeResource(); void releaseRuntimeResource();
@ -249,6 +253,16 @@ public:
{ {
return uris_.empty() && inFlightRequests_.empty() && requestPool_.empty(); return uris_.empty() && inFlightRequests_.empty() && requestPool_.empty();
} }
void setUniqueProtocol(bool f)
{
uniqueProtocol_ = f;
}
bool isUniqueProtocol() const
{
return uniqueProtocol_;
}
}; };
// Returns the first FileEntry which isRequested() method returns // Returns the first FileEntry which isRequested() method returns

View File

@ -114,7 +114,7 @@ bool FtpNegotiationCommand::executeInternal() {
command->setStartupIdleTime(getOption()->getAsInt(PREF_STARTUP_IDLE_TIME)); command->setStartupIdleTime(getOption()->getAsInt(PREF_STARTUP_IDLE_TIME));
command->setLowestDownloadSpeedLimit command->setLowestDownloadSpeedLimit
(getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT)); (getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
if(!getFileEntry()->isSingleHostMultiConnectionEnabled()) { if(getFileEntry()->isUniqueProtocol()) {
getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost()); getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost());
} }
getRequestGroup()->getURISelector()->tuneDownloadCommand getRequestGroup()->getURISelector()->tuneDownloadCommand

View File

@ -125,7 +125,7 @@ bool HttpResponseCommand::executeInternal()
} }
return skipResponseBody(httpResponse); return skipResponseBody(httpResponse);
} }
if(!getFileEntry()->isSingleHostMultiConnectionEnabled()) { if(getFileEntry()->isUniqueProtocol()) {
// TODO redirection should be considered here. We need to parse // TODO redirection should be considered here. We need to parse
// original URI to get hostname. // original URI to get hostname.
getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost()); getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost());

View File

@ -252,8 +252,9 @@ Metalink2RequestGroup::createRequestGroup
util::applyDir(option->get(PREF_DIR), util::applyDir(option->get(PREF_DIR),
entry->file->getPath()))); entry->file->getPath())));
dctx->getFirstFileEntry()->setUris(uris); dctx->getFirstFileEntry()->setUris(uris);
dctx->getFirstFileEntry()->setMaxConnectionPerServer(1);
if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) { if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) {
dctx->getFirstFileEntry()->disableSingleHostMultiConnection(); dctx->getFirstFileEntry()->setUniqueProtocol(true);
} }
#ifdef ENABLE_MESSAGE_DIGEST #ifdef ENABLE_MESSAGE_DIGEST
if(!entry->checksum.isNull()) { if(!entry->checksum.isNull()) {
@ -291,8 +292,9 @@ Metalink2RequestGroup::createRequestGroup
(new FileEntry (new FileEntry
(util::applyDir(option->get(PREF_DIR), (*i)->file->getPath()), (util::applyDir(option->get(PREF_DIR), (*i)->file->getPath()),
(*i)->file->getLength(), offset, uris)); (*i)->file->getLength(), offset, uris));
fe->setMaxConnectionPerServer(1);
if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) { if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) {
fe->disableSingleHostMultiConnection(); fe->setUniqueProtocol(true);
} }
fe->setOriginalName((*i)->metaurls[0]->name); fe->setOriginalName((*i)->metaurls[0]->name);
fileEntries.push_back(fe); fileEntries.push_back(fe);

View File

@ -318,12 +318,21 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
SharedHandle<OptionHandler> op(new NumberOptionHandler SharedHandle<OptionHandler> op(new NumberOptionHandler
(PREF_MAX_CONCURRENT_DOWNLOADS, (PREF_MAX_CONCURRENT_DOWNLOADS,
TEXT_MAX_CONCURRENT_DOWNLOADS, TEXT_MAX_CONCURRENT_DOWNLOADS,
"5", "2",
1, -1, 1, -1,
'j')); 'j'));
op->addTag(TAG_BASIC); op->addTag(TAG_BASIC);
handlers.push_back(op); handlers.push_back(op);
} }
{
SharedHandle<OptionHandler> op(new NumberOptionHandler
(PREF_MAX_CONNECTION_PER_SERVER,
TEXT_MAX_CONNECTION_PER_SERVER,
"1",
1, 4));
op->addTag(TAG_ADVANCED);
handlers.push_back(op);
}
{ {
SharedHandle<OptionHandler> op(new UnitNumberOptionHandler SharedHandle<OptionHandler> op(new UnitNumberOptionHandler
(PREF_MAX_DOWNLOAD_LIMIT, (PREF_MAX_DOWNLOAD_LIMIT,

View File

@ -230,6 +230,7 @@ TrackerWatcherCommand::createRequestGroup(const std::string& uri)
getLogger()->debug("This is single-tracker announce."); getLogger()->debug("This is single-tracker announce.");
} }
} }
rg->setNumConcurrentCommand(1);
// If backup tracker is available, try 2 times for each tracker // If backup tracker is available, try 2 times for each tracker
// and if they all fails, then try next one. // and if they all fails, then try next one.
rg->getOption()->put(PREF_MAX_TRIES, "2"); rg->getOption()->put(PREF_MAX_TRIES, "2");

View File

@ -183,16 +183,20 @@ template<typename InputIterator>
static void splitURI(std::vector<std::string>& result, static void splitURI(std::vector<std::string>& result,
InputIterator begin, InputIterator begin,
InputIterator end, InputIterator end,
size_t numSplit) size_t numSplit,
size_t maxIter)
{ {
size_t numURIs = std::distance(begin, end); size_t numURIs = std::distance(begin, end);
if(numURIs >= numSplit) { if(numURIs >= numSplit) {
result.insert(result.end(), begin, end); result.insert(result.end(), begin, end);
} else if(numURIs > 0) { } else if(numURIs > 0) {
for(size_t i = 0; i < numSplit/numURIs; ++i) { size_t num = std::min(numSplit/numURIs, maxIter);
for(size_t i = 0; i < num; ++i) {
result.insert(result.end(), begin, end); result.insert(result.end(), begin, end);
} }
result.insert(result.end(), begin, begin+(numSplit%numURIs)); if(num < maxIter) {
result.insert(result.end(), begin, begin+(numSplit%numURIs));
}
} }
} }
@ -209,6 +213,8 @@ static SharedHandle<RequestGroup> createRequestGroup
util::applyDir(option->get(PREF_DIR), option->get(PREF_OUT)):A2STR::NIL)); util::applyDir(option->get(PREF_DIR), option->get(PREF_OUT)):A2STR::NIL));
dctx->setDir(option->get(PREF_DIR)); dctx->setDir(option->get(PREF_DIR));
dctx->getFirstFileEntry()->setUris(uris); dctx->getFirstFileEntry()->setUris(uris);
dctx->getFirstFileEntry()->setMaxConnectionPerServer
(option->getAsInt(PREF_MAX_CONNECTION_PER_SERVER));
rg->setDownloadContext(dctx); rg->setDownloadContext(dctx);
return rg; return rg;
} }
@ -304,10 +310,8 @@ void createRequestGroupForBitTorrent
} }
// we ignore -Z option here // we ignore -Z option here
size_t numSplit = option->getAsInt(PREF_SPLIT); size_t numSplit = option->getAsInt(PREF_SPLIT);
std::vector<std::string> auxUris;
splitURI(auxUris, nargs.begin(), nargs.end(), numSplit);
SharedHandle<RequestGroup> rg = SharedHandle<RequestGroup> rg =
createBtRequestGroup(option->get(PREF_TORRENT_FILE), option, auxUris, createBtRequestGroup(option->get(PREF_TORRENT_FILE), option, nargs,
torrentData); torrentData);
rg->setNumConcurrentCommand(numSplit); rg->setNumConcurrentCommand(numSplit);
result.push_back(rg); result.push_back(rg);
@ -351,12 +355,13 @@ public:
{ {
if(detector_.isStreamProtocol(uri)) { if(detector_.isStreamProtocol(uri)) {
std::vector<std::string> streamURIs; std::vector<std::string> streamURIs;
size_t numIter = option_->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
size_t numSplit = option_->getAsInt(PREF_SPLIT); size_t numSplit = option_->getAsInt(PREF_SPLIT);
for(size_t i = 0; i < numSplit; ++i) { size_t num = std::min(numIter, numSplit);
for(size_t i = 0; i < num; ++i) {
streamURIs.push_back(uri); streamURIs.push_back(uri);
} }
SharedHandle<RequestGroup> rg = SharedHandle<RequestGroup> rg = createRequestGroup(option_, streamURIs);
createRequestGroup(option_, streamURIs);
rg->setNumConcurrentCommand(numSplit); rg->setNumConcurrentCommand(numSplit);
requestGroups_.push_back(rg); requestGroups_.push_back(rg);
} }
@ -429,10 +434,10 @@ void createRequestGroupForUri
std::stable_partition(nargs.begin(), nargs.end(), StreamProtocolFilter()); std::stable_partition(nargs.begin(), nargs.end(), StreamProtocolFilter());
// let's process http/ftp protocols first. // let's process http/ftp protocols first.
if(nargs.begin() != strmProtoEnd) { if(nargs.begin() != strmProtoEnd) {
size_t numIter = option->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
size_t numSplit = option->getAsInt(PREF_SPLIT); size_t numSplit = option->getAsInt(PREF_SPLIT);
std::vector<std::string> streamURIs; std::vector<std::string> streamURIs;
splitURI(streamURIs, nargs.begin(), strmProtoEnd, splitURI(streamURIs, nargs.begin(), strmProtoEnd, numSplit, numIter);
numSplit);
SharedHandle<RequestGroup> rg = SharedHandle<RequestGroup> rg =
createRequestGroup(option, streamURIs, true); createRequestGroup(option, streamURIs, true);
rg->setNumConcurrentCommand(numSplit); rg->setNumConcurrentCommand(numSplit);

View File

@ -190,6 +190,8 @@ const std::string PREF_ALWAYS_RESUME("always-resume");
const std::string PREF_MAX_RESUME_FAILURE_TRIES("max-resume-failure-tries"); const std::string PREF_MAX_RESUME_FAILURE_TRIES("max-resume-failure-tries");
// value: string that your file system recognizes as a file name. // value: string that your file system recognizes as a file name.
const std::string PREF_SAVE_SESSION("save-session"); const std::string PREF_SAVE_SESSION("save-session");
// value: 1*digit
const std::string PREF_MAX_CONNECTION_PER_SERVER("max-connection-per-server");
/** /**
* FTP related preferences * FTP related preferences

View File

@ -194,6 +194,8 @@ extern const std::string PREF_ALWAYS_RESUME;
extern const std::string PREF_MAX_RESUME_FAILURE_TRIES; extern const std::string PREF_MAX_RESUME_FAILURE_TRIES;
// value: string that your file system recognizes as a file name. // value: string that your file system recognizes as a file name.
extern const std::string PREF_SAVE_SESSION; extern const std::string PREF_SAVE_SESSION;
// value: 1*digit
extern const std::string PREF_MAX_CONNECTION_PER_SERVER;
/** /**
* FTP related preferences * FTP related preferences

View File

@ -683,3 +683,6 @@
" option on restart. Please note that downloads\n" \ " option on restart. Please note that downloads\n" \
" added by aria2.addTorrent and aria2.addMetalink\n" \ " added by aria2.addTorrent and aria2.addMetalink\n" \
" XML-RPC method are not saved.") " XML-RPC method are not saved.")
#define TEXT_MAX_CONNECTION_PER_SERVER \
_(" --max-connection-per-server=NUM The maximum number of connections to one server\n"\
" for each download.")

View File

@ -70,23 +70,22 @@ void DownloadHelperTest::testCreateRequestGroupForUri()
"http://charlie/file" "http://charlie/file"
}; };
std::vector<std::string> uris(vbegin(array), vend(array)); std::vector<std::string> uris(vbegin(array), vend(array));
option_->put(PREF_SPLIT, "3"); option_->put(PREF_SPLIT, "7");
option_->put(PREF_MAX_CONNECTION_PER_SERVER, "2");
option_->put(PREF_DIR, "/tmp"); option_->put(PREF_DIR, "/tmp");
option_->put(PREF_OUT, "file.out"); option_->put(PREF_OUT, "file.out");
{ {
std::vector<SharedHandle<RequestGroup> > result; std::vector<SharedHandle<RequestGroup> > result;
createRequestGroupForUri(result, option_, uris); createRequestGroupForUri(result, option_, uris);
CPPUNIT_ASSERT_EQUAL((size_t)1, result.size()); CPPUNIT_ASSERT_EQUAL((size_t)1, result.size());
SharedHandle<RequestGroup> group = result[0]; SharedHandle<RequestGroup> group = result[0];
std::vector<std::string> uris; std::vector<std::string> xuris;
group->getDownloadContext()->getFirstFileEntry()->getUris(uris); group->getDownloadContext()->getFirstFileEntry()->getUris(xuris);
CPPUNIT_ASSERT_EQUAL((size_t)3, uris.size()); CPPUNIT_ASSERT_EQUAL((size_t)6, xuris.size());
for(size_t i = 0; i < A2_ARRAY_LEN(array); ++i) { for(size_t i = 0; i < 6; ++i) {
CPPUNIT_ASSERT_EQUAL(array[i], uris[i]); CPPUNIT_ASSERT_EQUAL(array[i%3], xuris[i]);
} }
CPPUNIT_ASSERT_EQUAL((unsigned int)3, group->getNumConcurrentCommand()); CPPUNIT_ASSERT_EQUAL((unsigned int)7, group->getNumConcurrentCommand());
SharedHandle<DownloadContext> ctx = group->getDownloadContext(); SharedHandle<DownloadContext> ctx = group->getDownloadContext();
CPPUNIT_ASSERT_EQUAL(std::string("/tmp"), ctx->getDir()); CPPUNIT_ASSERT_EQUAL(std::string("/tmp"), ctx->getDir());
CPPUNIT_ASSERT_EQUAL(std::string("/tmp/file.out"), ctx->getBasePath()); CPPUNIT_ASSERT_EQUAL(std::string("/tmp/file.out"), ctx->getBasePath());
@ -94,53 +93,41 @@ void DownloadHelperTest::testCreateRequestGroupForUri()
option_->put(PREF_SPLIT, "5"); option_->put(PREF_SPLIT, "5");
{ {
std::vector<SharedHandle<RequestGroup> > result; std::vector<SharedHandle<RequestGroup> > result;
createRequestGroupForUri(result, option_, uris); createRequestGroupForUri(result, option_, uris);
CPPUNIT_ASSERT_EQUAL((size_t)1, result.size()); CPPUNIT_ASSERT_EQUAL((size_t)1, result.size());
SharedHandle<RequestGroup> group = result[0]; SharedHandle<RequestGroup> group = result[0];
std::vector<std::string> uris; std::vector<std::string> xuris;
group->getDownloadContext()->getFirstFileEntry()->getUris(uris); group->getDownloadContext()->getFirstFileEntry()->getUris(xuris);
CPPUNIT_ASSERT_EQUAL((size_t)5, uris.size()); CPPUNIT_ASSERT_EQUAL((size_t)5, xuris.size());
for(size_t i = 0; i < A2_ARRAY_LEN(array); ++i) { for(size_t i = 0; i < 5; ++i) {
CPPUNIT_ASSERT_EQUAL(array[i], uris[i]); CPPUNIT_ASSERT_EQUAL(array[i%3], xuris[i]);
} }
for(size_t i = 0; i < 5-A2_ARRAY_LEN(array); ++i) {
CPPUNIT_ASSERT_EQUAL(array[i], uris[i+A2_ARRAY_LEN(array)]);
}
CPPUNIT_ASSERT_EQUAL((unsigned int)5, group->getNumConcurrentCommand());
} }
option_->put(PREF_SPLIT, "2"); option_->put(PREF_SPLIT, "2");
{ {
std::vector<SharedHandle<RequestGroup> > result; std::vector<SharedHandle<RequestGroup> > result;
createRequestGroupForUri(result, option_, uris); createRequestGroupForUri(result, option_, uris);
CPPUNIT_ASSERT_EQUAL((size_t)1, result.size()); CPPUNIT_ASSERT_EQUAL((size_t)1, result.size());
SharedHandle<RequestGroup> group = result[0]; SharedHandle<RequestGroup> group = result[0];
std::vector<std::string> uris; std::vector<std::string> xuris;
group->getDownloadContext()->getFirstFileEntry()->getUris(uris); group->getDownloadContext()->getFirstFileEntry()->getUris(xuris);
CPPUNIT_ASSERT_EQUAL((size_t)3, uris.size()); CPPUNIT_ASSERT_EQUAL((size_t)3, xuris.size());
for(size_t i = 0; i < A2_ARRAY_LEN(array); ++i) { for(size_t i = 0; i < 3; ++i) {
CPPUNIT_ASSERT_EQUAL(array[i], uris[i]); CPPUNIT_ASSERT_EQUAL(array[i%3], xuris[i]);
} }
CPPUNIT_ASSERT_EQUAL((unsigned int)2, group->getNumConcurrentCommand());
} }
option_->put(PREF_FORCE_SEQUENTIAL, V_TRUE); option_->put(PREF_FORCE_SEQUENTIAL, V_TRUE);
{ {
std::vector<SharedHandle<RequestGroup> > result; std::vector<SharedHandle<RequestGroup> > result;
createRequestGroupForUri(result, option_, uris); createRequestGroupForUri(result, option_, uris);
CPPUNIT_ASSERT_EQUAL((size_t)3, result.size()); CPPUNIT_ASSERT_EQUAL((size_t)3, result.size());
// for alpha server // for alpha server
SharedHandle<RequestGroup> alphaGroup = result[0]; SharedHandle<RequestGroup> alphaGroup = result[0];
std::vector<std::string> alphaURIs; std::vector<std::string> alphaURIs;
alphaGroup->getDownloadContext()->getFirstFileEntry()->getUris(alphaURIs); alphaGroup->getDownloadContext()->getFirstFileEntry()->getUris(alphaURIs);
CPPUNIT_ASSERT_EQUAL((size_t)2, alphaURIs.size()); CPPUNIT_ASSERT_EQUAL((size_t)2, alphaURIs.size());
for(size_t i = 0; i < 2; ++i) { for(size_t i = 0; i < 2; ++i) {
CPPUNIT_ASSERT_EQUAL(array[0], uris[0]); CPPUNIT_ASSERT_EQUAL(array[0], alphaURIs[i]);
} }
CPPUNIT_ASSERT_EQUAL((unsigned int)2, CPPUNIT_ASSERT_EQUAL((unsigned int)2,
alphaGroup->getNumConcurrentCommand()); alphaGroup->getNumConcurrentCommand());
@ -158,6 +145,7 @@ void DownloadHelperTest::testCreateRequestGroupForUri_parameterized()
"http://charlie/file" "http://charlie/file"
}; };
std::vector<std::string> uris(vbegin(array), vend(array)); std::vector<std::string> uris(vbegin(array), vend(array));
option_->put(PREF_MAX_CONNECTION_PER_SERVER, "1");
option_->put(PREF_SPLIT, "3"); option_->put(PREF_SPLIT, "3");
option_->put(PREF_DIR, "/tmp"); option_->put(PREF_DIR, "/tmp");
option_->put(PREF_OUT, "file.out"); option_->put(PREF_OUT, "file.out");
@ -194,6 +182,7 @@ void DownloadHelperTest::testCreateRequestGroupForUri_BitTorrent()
"http://charlie/file" "http://charlie/file"
}; };
std::vector<std::string> uris(vbegin(array), vend(array)); std::vector<std::string> uris(vbegin(array), vend(array));
option_->put(PREF_MAX_CONNECTION_PER_SERVER, "1");
option_->put(PREF_SPLIT, "3"); option_->put(PREF_SPLIT, "3");
option_->put(PREF_DIR, "/tmp"); option_->put(PREF_DIR, "/tmp");
option_->put(PREF_OUT, "file.out"); option_->put(PREF_OUT, "file.out");
@ -204,13 +193,13 @@ void DownloadHelperTest::testCreateRequestGroupForUri_BitTorrent()
CPPUNIT_ASSERT_EQUAL((size_t)2, result.size()); CPPUNIT_ASSERT_EQUAL((size_t)2, result.size());
SharedHandle<RequestGroup> group = result[0]; SharedHandle<RequestGroup> group = result[0];
std::vector<std::string> uris; std::vector<std::string> xuris;
group->getDownloadContext()->getFirstFileEntry()->getUris(uris); group->getDownloadContext()->getFirstFileEntry()->getUris(xuris);
CPPUNIT_ASSERT_EQUAL((size_t)3, uris.size()); CPPUNIT_ASSERT_EQUAL((size_t)3, xuris.size());
CPPUNIT_ASSERT_EQUAL(array[0], uris[0]); CPPUNIT_ASSERT_EQUAL(array[0], xuris[0]);
CPPUNIT_ASSERT_EQUAL(array[2], uris[1]); CPPUNIT_ASSERT_EQUAL(array[2], xuris[1]);
CPPUNIT_ASSERT_EQUAL(array[3], uris[2]); CPPUNIT_ASSERT_EQUAL(array[3], xuris[2]);
CPPUNIT_ASSERT_EQUAL((unsigned int)3, group->getNumConcurrentCommand()); CPPUNIT_ASSERT_EQUAL((unsigned int)3, group->getNumConcurrentCommand());
SharedHandle<DownloadContext> ctx = group->getDownloadContext(); SharedHandle<DownloadContext> ctx = group->getDownloadContext();
@ -242,6 +231,7 @@ void DownloadHelperTest::testCreateRequestGroupForUri_Metalink()
"test.xml" "test.xml"
}; };
std::vector<std::string> uris(vbegin(array), vend(array)); std::vector<std::string> uris(vbegin(array), vend(array));
option_->put(PREF_MAX_CONNECTION_PER_SERVER, "1");
option_->put(PREF_SPLIT, "3"); option_->put(PREF_SPLIT, "3");
option_->put(PREF_METALINK_SERVERS, "2"); option_->put(PREF_METALINK_SERVERS, "2");
option_->put(PREF_DIR, "/tmp"); option_->put(PREF_DIR, "/tmp");
@ -260,11 +250,11 @@ void DownloadHelperTest::testCreateRequestGroupForUri_Metalink()
#endif // !ENABLE_BITTORRENT #endif // !ENABLE_BITTORRENT
SharedHandle<RequestGroup> group = result[0]; SharedHandle<RequestGroup> group = result[0];
std::vector<std::string> uris; std::vector<std::string> xuris;
group->getDownloadContext()->getFirstFileEntry()->getUris(uris); group->getDownloadContext()->getFirstFileEntry()->getUris(xuris);
CPPUNIT_ASSERT_EQUAL((size_t)3, uris.size()); CPPUNIT_ASSERT_EQUAL((size_t)3, xuris.size());
for(size_t i = 0; i < 3; ++i) { for(size_t i = 0; i < 3; ++i) {
CPPUNIT_ASSERT_EQUAL(array[i], uris[i]); CPPUNIT_ASSERT_EQUAL(array[i], xuris[i]);
} }
CPPUNIT_ASSERT_EQUAL((unsigned int)3, group->getNumConcurrentCommand()); CPPUNIT_ASSERT_EQUAL((unsigned int)3, group->getNumConcurrentCommand());
SharedHandle<DownloadContext> ctx = group->getDownloadContext(); SharedHandle<DownloadContext> ctx = group->getDownloadContext();
@ -290,6 +280,7 @@ void DownloadHelperTest::testCreateRequestGroupForUri_Metalink()
void DownloadHelperTest::testCreateRequestGroupForUriList() void DownloadHelperTest::testCreateRequestGroupForUriList()
{ {
option_->put(PREF_MAX_CONNECTION_PER_SERVER, "3");
option_->put(PREF_SPLIT, "3"); option_->put(PREF_SPLIT, "3");
option_->put(PREF_INPUT_FILE, "input_uris.txt"); option_->put(PREF_INPUT_FILE, "input_uris.txt");
option_->put(PREF_DIR, "/tmp"); option_->put(PREF_DIR, "/tmp");
@ -330,6 +321,7 @@ void DownloadHelperTest::testCreateRequestGroupForBitTorrent()
}; };
std::vector<std::string> auxURIs(vbegin(array), vend(array)); std::vector<std::string> auxURIs(vbegin(array), vend(array));
option_->put(PREF_MAX_CONNECTION_PER_SERVER, "2");
option_->put(PREF_SPLIT, "5"); option_->put(PREF_SPLIT, "5");
option_->put(PREF_TORRENT_FILE, "test.torrent"); option_->put(PREF_TORRENT_FILE, "test.torrent");
option_->put(PREF_DIR, "/tmp"); option_->put(PREF_DIR, "/tmp");
@ -344,7 +336,8 @@ void DownloadHelperTest::testCreateRequestGroupForBitTorrent()
SharedHandle<RequestGroup> group = result[0]; SharedHandle<RequestGroup> group = result[0];
std::vector<std::string> uris; std::vector<std::string> uris;
group->getDownloadContext()->getFirstFileEntry()->getUris(uris); group->getDownloadContext()->getFirstFileEntry()->getUris(uris);
// See -s option is ignored // See -s option is ignored. See processRootDictionary() in
// bittorrent_helper.cc
CPPUNIT_ASSERT_EQUAL((size_t)3, uris.size()); CPPUNIT_ASSERT_EQUAL((size_t)3, uris.size());
for(size_t i = 0; i < A2_ARRAY_LEN(array); ++i) { for(size_t i = 0; i < A2_ARRAY_LEN(array); ++i) {
CPPUNIT_ASSERT_EQUAL(array[i]+"/aria2-test/aria2/src/aria2c", uris[i]); CPPUNIT_ASSERT_EQUAL(array[i]+"/aria2-test/aria2/src/aria2c", uris[i]);

View File

@ -3,6 +3,7 @@
#include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/HelperMacros.h>
#include "InOrderURISelector.h" #include "InOrderURISelector.h"
#include "util.h"
namespace aria2 { namespace aria2 {
@ -13,7 +14,8 @@ class FileEntryTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testRemoveURIWhoseHostnameIs); CPPUNIT_TEST(testRemoveURIWhoseHostnameIs);
CPPUNIT_TEST(testExtractURIResult); CPPUNIT_TEST(testExtractURIResult);
CPPUNIT_TEST(testGetRequest); CPPUNIT_TEST(testGetRequest);
CPPUNIT_TEST(testGetRequest_disableSingleHostMultiConnection); CPPUNIT_TEST(testGetRequest_withoutUriReuse);
CPPUNIT_TEST(testGetRequest_withUniqueProtocol);
CPPUNIT_TEST(testReuseUri); CPPUNIT_TEST(testReuseUri);
CPPUNIT_TEST(testAddUri); CPPUNIT_TEST(testAddUri);
CPPUNIT_TEST(testAddUris); CPPUNIT_TEST(testAddUris);
@ -27,7 +29,8 @@ public:
void testRemoveURIWhoseHostnameIs(); void testRemoveURIWhoseHostnameIs();
void testExtractURIResult(); void testExtractURIResult();
void testGetRequest(); void testGetRequest();
void testGetRequest_disableSingleHostMultiConnection(); void testGetRequest_withoutUriReuse();
void testGetRequest_withUniqueProtocol();
void testReuseUri(); void testReuseUri();
void testAddUri(); void testAddUri();
void testAddUris(); void testAddUris();
@ -109,7 +112,6 @@ void FileEntryTest::testGetRequest()
SharedHandle<Request> req = fileEntry->getRequest(selector); SharedHandle<Request> req = fileEntry->getRequest(selector);
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req->getHost()); CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("http"), req->getProtocol()); CPPUNIT_ASSERT_EQUAL(std::string("http"), req->getProtocol());
fileEntry->poolRequest(req); fileEntry->poolRequest(req);
SharedHandle<Request> req2nd = fileEntry->getRequest(selector); SharedHandle<Request> req2nd = fileEntry->getRequest(selector);
@ -117,14 +119,51 @@ void FileEntryTest::testGetRequest()
CPPUNIT_ASSERT_EQUAL(std::string("http"), req2nd->getProtocol()); CPPUNIT_ASSERT_EQUAL(std::string("http"), req2nd->getProtocol());
SharedHandle<Request> req3rd = fileEntry->getRequest(selector); SharedHandle<Request> req3rd = fileEntry->getRequest(selector);
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req3rd->getHost()); CPPUNIT_ASSERT_EQUAL(std::string("mirror"), req3rd->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("ftp"), req3rd->getProtocol()); CPPUNIT_ASSERT_EQUAL(std::string("http"), req3rd->getProtocol());
SharedHandle<Request> req4th = fileEntry->getRequest(selector);
CPPUNIT_ASSERT(req4th.isNull());
fileEntry->setMaxConnectionPerServer(2);
SharedHandle<Request> req5th = fileEntry->getRequest(selector);
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req5th->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("ftp"), req5th->getProtocol());
SharedHandle<Request> req6th = fileEntry->getRequest(selector);
CPPUNIT_ASSERT_EQUAL(std::string("mirror"), req6th->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("http"), req6th->getProtocol());
SharedHandle<Request> req7th = fileEntry->getRequest(selector);
CPPUNIT_ASSERT(req7th.isNull());
} }
void FileEntryTest::testGetRequest_disableSingleHostMultiConnection() void FileEntryTest::testGetRequest_withoutUriReuse()
{ {
SharedHandle<FileEntry> fileEntry = createFileEntry(); SharedHandle<FileEntry> fileEntry = createFileEntry();
fileEntry->disableSingleHostMultiConnection(); fileEntry->setMaxConnectionPerServer(2);
SharedHandle<InOrderURISelector> selector(new InOrderURISelector());
SharedHandle<Request> req = fileEntry->getRequest(selector, false);
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("http"), req->getProtocol());
SharedHandle<Request> req2nd = fileEntry->getRequest(selector, false);
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req2nd->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("ftp"), req2nd->getProtocol());
SharedHandle<Request> req3rd = fileEntry->getRequest(selector, false);
CPPUNIT_ASSERT_EQUAL(std::string("mirror"), req3rd->getHost());
CPPUNIT_ASSERT_EQUAL(std::string("http"), req3rd->getProtocol());
SharedHandle<Request> req4th = fileEntry->getRequest(selector, false);
CPPUNIT_ASSERT(req4th.isNull());
}
void FileEntryTest::testGetRequest_withUniqueProtocol()
{
SharedHandle<FileEntry> fileEntry = createFileEntry();
fileEntry->setUniqueProtocol(true);
SharedHandle<InOrderURISelector> selector(new InOrderURISelector()); SharedHandle<InOrderURISelector> selector(new InOrderURISelector());
SharedHandle<Request> req = fileEntry->getRequest(selector); SharedHandle<Request> req = fileEntry->getRequest(selector);
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req->getHost()); CPPUNIT_ASSERT_EQUAL(std::string("localhost"), req->getHost());
@ -136,25 +175,42 @@ void FileEntryTest::testGetRequest_disableSingleHostMultiConnection()
SharedHandle<Request> req3rd = fileEntry->getRequest(selector); SharedHandle<Request> req3rd = fileEntry->getRequest(selector);
CPPUNIT_ASSERT(req3rd.isNull()); CPPUNIT_ASSERT(req3rd.isNull());
CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry->getRemainingUris().size());
CPPUNIT_ASSERT_EQUAL(std::string("ftp://localhost/aria2.zip"),
fileEntry->getRemainingUris()[0]);
CPPUNIT_ASSERT_EQUAL(std::string("http://mirror/aria2.zip"),
fileEntry->getRemainingUris()[1]);
} }
void FileEntryTest::testReuseUri() void FileEntryTest::testReuseUri()
{ {
SharedHandle<InOrderURISelector> selector(new InOrderURISelector()); SharedHandle<InOrderURISelector> selector(new InOrderURISelector());
SharedHandle<FileEntry> fileEntry = createFileEntry(); SharedHandle<FileEntry> fileEntry = createFileEntry();
fileEntry->setMaxConnectionPerServer(3);
size_t numUris = fileEntry->getRemainingUris().size(); size_t numUris = fileEntry->getRemainingUris().size();
for(size_t i = 0; i < numUris; ++i) { for(size_t i = 0; i < numUris; ++i) {
fileEntry->getRequest(selector); fileEntry->getRequest(selector, false);
} }
CPPUNIT_ASSERT_EQUAL((size_t)0, fileEntry->getRemainingUris().size()); CPPUNIT_ASSERT_EQUAL((size_t)0, fileEntry->getRemainingUris().size());
fileEntry->addURIResult("http://localhost/aria2.zip", fileEntry->addURIResult("http://localhost/aria2.zip",
downloadresultcode::UNKNOWN_ERROR); downloadresultcode::UNKNOWN_ERROR);
fileEntry->reuseUri(3); std::vector<std::string> ignore;
CPPUNIT_ASSERT_EQUAL((size_t)3, fileEntry->getRemainingUris().size()); fileEntry->reuseUri(ignore);
const std::deque<std::string>& uris = fileEntry->getRemainingUris(); CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry->getRemainingUris().size());
std::deque<std::string> uris = fileEntry->getRemainingUris();
CPPUNIT_ASSERT_EQUAL(std::string("ftp://localhost/aria2.zip"), uris[0]); CPPUNIT_ASSERT_EQUAL(std::string("ftp://localhost/aria2.zip"), uris[0]);
CPPUNIT_ASSERT_EQUAL(std::string("http://mirror/aria2.zip"), uris[1]); CPPUNIT_ASSERT_EQUAL(std::string("http://mirror/aria2.zip"), uris[1]);
CPPUNIT_ASSERT_EQUAL(std::string("ftp://localhost/aria2.zip"), uris[2]); for(size_t i = 0; i < 2; ++i) {
fileEntry->getRequest(selector, false);
}
CPPUNIT_ASSERT_EQUAL((size_t)0, fileEntry->getRemainingUris().size());
ignore.clear();
ignore.push_back("mirror");
fileEntry->reuseUri(ignore);
CPPUNIT_ASSERT_EQUAL((size_t)1, fileEntry->getRemainingUris().size());
uris = fileEntry->getRemainingUris();
CPPUNIT_ASSERT_EQUAL(std::string("ftp://localhost/aria2.zip"), uris[0]);
} }
void FileEntryTest::testAddUri() void FileEntryTest::testAddUri()