Browse Source

2007-12-12 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	If several protocols are available for a mirror, aria2 now use 
one of
	them. --metalink-preferred-protocol option was added to specify 
the
	preference of protocol.
	* src/AbstractCommand.cc
	* src/OptionHandlerFactory.cc
	* src/ServerHost.{h, cc}
	* src/Metalink2RequestGroup.cc
	* src/RequestGroup.{h, cc}
	* test/RequestGroupTest.cc
	* src/option_processing.cc
	* src/prefs.h
	* src/HttpResponseCommand.cc
	* src/MetalinkResource.{h, cc}
	* src/FtpNegotiationCommand.cc
	* src/MetalinkEntry.{h, cc}
	* src/MetalinkEntryTest.cc
Tatsuhiro Tsujikawa 18 years ago
parent
commit
fca7b9d7e4

+ 19 - 0
ChangeLog

@@ -1,3 +1,22 @@
+2007-12-12  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	If several protocols are available for a mirror, aria2 now use one of
+	them. --metalink-preferred-protocol option was added to specify the
+	preference of protocol.
+	* src/AbstractCommand.cc
+	* src/OptionHandlerFactory.cc
+	* src/ServerHost.{h, cc}
+	* src/Metalink2RequestGroup.cc
+	* src/RequestGroup.{h, cc}
+	* test/RequestGroupTest.cc
+	* src/option_processing.cc
+	* src/prefs.h
+	* src/HttpResponseCommand.cc
+	* src/MetalinkResource.{h, cc}
+	* src/FtpNegotiationCommand.cc
+	* src/MetalinkEntry.{h, cc}
+	* src/MetalinkEntryTest.cc
+
 2007-12-12  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
 	Code cleanup and added debug log.

+ 1 - 0
src/AbstractCommand.cc

@@ -160,6 +160,7 @@ bool AbstractCommand::execute() {
 }
 
 void AbstractCommand::tryReserved() {
+  _requestGroup->removeServerHost(cuid);
   Commands commands = _requestGroup->createNextCommand(e, 1);
   e->addCommand(commands);
 }

+ 4 - 0
src/FtpNegotiationCommand.cc

@@ -46,6 +46,7 @@
 #include "DefaultBtProgressInfoFile.h"
 #include "RequestGroupMan.h"
 #include "DownloadFailureException.h"
+#include "ServerHost.h"
 
 FtpNegotiationCommand::FtpNegotiationCommand(int32_t cuid,
 					     const RequestHandle& req,
@@ -73,6 +74,9 @@ bool FtpNegotiationCommand::executeInternal() {
     command->setMaxDownloadSpeedLimit(e->option->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
     command->setStartupIdleTime(e->option->getAsInt(PREF_STARTUP_IDLE_TIME));
     command->setLowestDownloadSpeedLimit(e->option->getAsInt(PREF_LOWEST_SPEED_LIMIT));
+    if(!_requestGroup->isSingleHostMultiConnectionEnabled()) {
+      _requestGroup->removeURIWhoseHostnameIs(_requestGroup->searchServerHost(cuid)->getHostname());
+    }
     e->commands.push_back(command);
     return true;
   } else if(sequence == SEQ_HEAD_OK || sequence == SEQ_DOWNLOAD_ALREADY_COMPLETED) {

+ 4 - 0
src/HttpResponseCommand.cc

@@ -50,6 +50,7 @@
 #include "DefaultBtProgressInfoFile.h"
 #include "RequestGroupMan.h"
 #include "DownloadFailureException.h"
+#include "ServerHost.h"
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -85,6 +86,9 @@ bool HttpResponseCommand::executeInternal()
     e->noWait = true;
     return prepareForRetry(0);
   }
+  if(!_requestGroup->isSingleHostMultiConnectionEnabled()) {
+    _requestGroup->removeURIWhoseHostnameIs(_requestGroup->searchServerHost(cuid)->getHostname());
+  }
   if(_requestGroup->getPieceStorage().isNull()) {
     int64_t totalLength = httpResponse->getEntityLength();
     SingleFileDownloadContextHandle dctx = _requestGroup->getDownloadContext();

+ 2 - 1
src/Makefile.am

@@ -146,7 +146,8 @@ SRCS =  Socket.h\
 	BtRegistry.cc BtRegistry.h\
 	MultiFileAllocationIterator.cc MultiFileAllocationIterator.h\
 	PeerConnection.cc PeerConnection.h\
-	ByteArrayDiskWriter.cc ByteArrayDiskWriter.h
+	ByteArrayDiskWriter.cc ByteArrayDiskWriter.h\
+	ServerHost.cc
 #	debug_new.cpp
 
 if ENABLE_MESSAGE_DIGEST

+ 8 - 7
src/Makefile.in

@@ -291,7 +291,7 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	BtRegistry.cc BtRegistry.h MultiFileAllocationIterator.cc \
 	MultiFileAllocationIterator.h PeerConnection.cc \
 	PeerConnection.h ByteArrayDiskWriter.cc ByteArrayDiskWriter.h \
-	IteratableChunkChecksumValidator.cc \
+	ServerHost.cc IteratableChunkChecksumValidator.cc \
 	IteratableChunkChecksumValidator.h \
 	IteratableChecksumValidator.cc IteratableChecksumValidator.h \
 	CheckIntegrityCommand.cc CheckIntegrityCommand.h \
@@ -524,11 +524,11 @@ am__objects_12 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	DirectDiskAdaptor.$(OBJEXT) MultiDiskAdaptor.$(OBJEXT) \
 	Peer.$(OBJEXT) BtRegistry.$(OBJEXT) \
 	MultiFileAllocationIterator.$(OBJEXT) PeerConnection.$(OBJEXT) \
-	ByteArrayDiskWriter.$(OBJEXT) $(am__objects_1) \
-	$(am__objects_2) $(am__objects_3) $(am__objects_4) \
-	$(am__objects_5) $(am__objects_6) $(am__objects_7) \
-	$(am__objects_8) $(am__objects_9) $(am__objects_10) \
-	$(am__objects_11)
+	ByteArrayDiskWriter.$(OBJEXT) ServerHost.$(OBJEXT) \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_5) $(am__objects_6) \
+	$(am__objects_7) $(am__objects_8) $(am__objects_9) \
+	$(am__objects_10) $(am__objects_11)
 am_libaria2c_a_OBJECTS = $(am__objects_12)
 libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(bindir)"
@@ -817,7 +817,7 @@ SRCS = Socket.h SocketCore.cc SocketCore.h Command.cc Command.h \
 	BtRegistry.cc BtRegistry.h MultiFileAllocationIterator.cc \
 	MultiFileAllocationIterator.h PeerConnection.cc \
 	PeerConnection.h ByteArrayDiskWriter.cc ByteArrayDiskWriter.h \
-	$(am__append_1) $(am__append_2) $(am__append_3) \
+	ServerHost.cc $(am__append_1) $(am__append_2) $(am__append_3) \
 	$(am__append_4) $(am__append_5) $(am__append_6) \
 	$(am__append_7) $(am__append_8) $(am__append_9) \
 	$(am__append_10) $(am__append_11)
@@ -1075,6 +1075,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ResourcesMetalinkParserState.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SeedCheckCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SegmentMan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerHost.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ShaVisitor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleBtMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleLogger.Po@am__quote@

+ 5 - 0
src/Metalink2RequestGroup.cc

@@ -133,6 +133,9 @@ RequestGroups Metalink2RequestGroup::createRequestGroup(MetalinkEntries entries)
       Util::slice(locations, _option->get(PREF_METALINK_LOCATION), ',', true);
       entry->setLocationPreference(locations, 100);
     }
+    if(_option->get(PREF_METALINK_PREFERRED_PROTOCOL) != V_NONE) {
+      entry->setProtocolPreference(_option->get(PREF_METALINK_PREFERRED_PROTOCOL), 100);
+    }
     if(useIndex) {
       if(find(selectIndexes.begin(), selectIndexes.end(), count+1) == selectIndexes.end()) {
 	continue;
@@ -206,6 +209,8 @@ RequestGroups Metalink2RequestGroup::createRequestGroup(MetalinkEntries entries)
     rg->setNumConcurrentCommand(entry->maxConnections < 0 ?
 				_option->getAsInt(PREF_METALINK_SERVERS) :
 				min<int32_t>(_option->getAsInt(PREF_METALINK_SERVERS), entry->maxConnections));
+    // In metalink, multi connection to a single host is not allowed.
+    rg->setSingleHostMultiConnectionEnabled(false);
 
 #ifdef ENABLE_BITTORRENT
     // Inject depenency between rg and torrentRg here if torrentRg.isNull() == false

+ 23 - 0
src/MetalinkEntry.cc

@@ -74,6 +74,29 @@ void MetalinkEntry::setLocationPreference(const Strings& locations,
 	   AddLocationPreference(locations, preferenceToAdd));
 }
 
+class AddProtocolPreference {
+private:
+  const string& _protocol;
+  int32_t _preferenceToAdd;
+public:
+  AddProtocolPreference(const string& protocol, int32_t prefToAdd):
+    _protocol(protocol), _preferenceToAdd(prefToAdd) {}
+
+  void operator()(const MetalinkResourceHandle& res) const
+  {
+    if(_protocol == MetalinkResource::getTypeString(res->type)) {
+      res->preference += _preferenceToAdd;
+    }
+  }
+};
+
+void MetalinkEntry::setProtocolPreference(const string& protocol,
+					  int32_t preferenceToAdd)
+{
+  for_each(resources.begin(), resources.end(),
+	   AddProtocolPreference(protocol, preferenceToAdd));
+}
+
 class PrefOrder {
 public:
   bool operator()(const MetalinkResourceHandle& res1,

+ 1 - 0
src/MetalinkEntry.h

@@ -96,6 +96,7 @@ public:
   void reorderResourcesByPreference();
   
   void setLocationPreference(const Strings& locations, int32_t preferenceToAdd);
+  void setProtocolPreference(const string& protocol, int32_t preferenceToAdd);
 
   static FileEntries toFileEntry(const MetalinkEntries& metalinkEntries);
 };

+ 4 - 0
src/MetalinkResource.cc

@@ -34,6 +34,10 @@
 /* copyright --> */
 #include "MetalinkResource.h"
 
+string MetalinkResource::type2String[] = {
+  "ftp", "http", "https", "bittorrent", "not_supported"
+};
+
 MetalinkResource::MetalinkResource():
   maxConnections(-1)
 {}

+ 8 - 1
src/MetalinkResource.h

@@ -40,12 +40,14 @@
 class MetalinkResource {
 public:
   enum TYPE {
-    TYPE_FTP,
+    TYPE_FTP = 0,
     TYPE_HTTP,
     TYPE_HTTPS,
     TYPE_BITTORRENT,
     TYPE_NOT_SUPPORTED
   };
+
+  static string type2String[];
 public:
   string url;
   TYPE type;
@@ -66,6 +68,11 @@ public:
     }
     return *this;
   }
+
+  static const string& getTypeString(TYPE type)
+  {
+    return type2String[type];
+  }
 };
 
 typedef SharedHandle<MetalinkResource> MetalinkResourceHandle;

+ 6 - 0
src/OptionHandlerFactory.cc

@@ -35,6 +35,7 @@
 #include "OptionHandlerFactory.h"
 #include "prefs.h"
 #include "OptionHandlerImpl.h"
+#include "a2functional.h"
 
 OptionHandlers OptionHandlerFactory::createOptionHandlers()
 {
@@ -102,6 +103,11 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
   handlers.push_back(new UnitNumberOptionHandler(PREF_NO_FILE_ALLOCATION_LIMIT, 0));
   handlers.push_back(new BooleanOptionHandler(PREF_ENABLE_DIRECT_IO));
   handlers.push_back(new BooleanOptionHandler(PREF_ALLOW_PIECE_LENGTH_CHANGE));
+  {
+    const char* params[] = { V_HTTP, V_HTTPS, V_FTP, V_NONE };
+    handlers.push_back(new ParameterOptionHandler(PREF_METALINK_PREFERRED_PROTOCOL,
+						  Strings(&params[0], &params[arrayLength(params)])));
+  }
 
   return handlers;
 }

+ 91 - 4
src/RequestGroup.cc

@@ -63,6 +63,7 @@
 #include "DownloadHandlerFactory.h"
 #include "MemoryBufferPreDownloadHandler.h"
 #include "DownloadHandlerConstants.h"
+#include "ServerHost.h"
 #ifdef ENABLE_MESSAGE_DIGEST
 # include "CheckIntegrityCommand.h"
 #endif // ENABLE_MESSAGE_DIGEST
@@ -97,6 +98,7 @@ RequestGroup::RequestGroup(const Option* option,
   _preLocalFileCheckEnabled(true),
   _haltRequested(false),
   _forceHaltRequested(false),
+  _singleHostMultiConnectionEnabled(true),
   _option(option),
   _logger(LogFactory::getInstance())
 {
@@ -381,18 +383,28 @@ Commands RequestGroup::createNextCommandWithAdj(DownloadEngine* e, int32_t numAd
 Commands RequestGroup::createNextCommand(DownloadEngine* e, int32_t numCommand, const string& method)
 {
   Commands commands;
+  Strings pendingURIs;
   for(;!_uris.empty() && numCommand--; _uris.pop_front()) {
     string uri = _uris.front();
-    _spentUris.push_back(uri);
     RequestHandle req = new Request();
-    req->setReferer(_option->get(PREF_REFERER));
-    req->setMethod(method);
     if(req->setUrl(uri)) {
-      commands.push_back(InitiateConnectionCommandFactory::createInitiateConnectionCommand(CUIDCounterSingletonHolder::instance()->newID(), req, this, e));
+      ServerHostHandle sv = _singleHostMultiConnectionEnabled ? 0 : searchServerHost(req->getHost());
+      if(sv.isNull()) {
+	_spentUris.push_back(uri);
+	req->setReferer(_option->get(PREF_REFERER));
+	req->setMethod(method);
+	Command* command = InitiateConnectionCommandFactory::createInitiateConnectionCommand(CUIDCounterSingletonHolder::instance()->newID(), req, this, e);
+	ServerHostHandle sv = new ServerHost(command->getCuid(), req->getHost());
+	registerServerHost(sv);
+	commands.push_back(command);
+      } else {
+	pendingURIs.push_front(uri);
+      }
     } else {
       _logger->error(MSG_UNRECOGNIZED_URI, req->getUrl().c_str());
     }
   }
+  copy(pendingURIs.begin(), pendingURIs.end(), front_inserter(_uris));
   return commands;
 }
 
@@ -744,3 +756,78 @@ DownloadResultHandle RequestGroup::createDownloadResult() const
 			    DownloadResult::FINISHED :
 			    DownloadResult::NOT_YET);
 }
+
+void RequestGroup::registerServerHost(const ServerHostHandle& serverHost)
+{
+  _serverHosts.push_back(serverHost);
+}
+
+class FindServerHostByCUID
+{
+private:
+  int32_t _cuid;
+public:
+  FindServerHostByCUID(int32_t cuid):_cuid(cuid) {}
+
+  bool operator()(const ServerHostHandle& sv) const
+  {
+    return sv->getCuid() == _cuid;
+  }
+};
+
+ServerHostHandle RequestGroup::searchServerHost(int32_t cuid) const
+{
+  ServerHosts::const_iterator itr = find_if(_serverHosts.begin(),
+					    _serverHosts.end(),
+					    FindServerHostByCUID(cuid));
+  if(itr == _serverHosts.end()) {
+    return 0;
+  } else {
+    return *itr;
+  }
+}
+
+class FindServerHostByHostname
+{
+private:
+  const string& _hostname;
+public:
+  FindServerHostByHostname(const string& hostname):_hostname(hostname) {}
+
+  bool operator()(const ServerHostHandle& sv) const
+  {
+    return sv->getHostname() == _hostname;
+  }
+};
+
+ServerHostHandle RequestGroup::searchServerHost(const string& hostname) const
+{
+  ServerHosts::const_iterator itr = find_if(_serverHosts.begin(),
+					    _serverHosts.end(),
+					    FindServerHostByHostname(hostname));
+  if(itr == _serverHosts.end()) {
+    return 0;
+  } else {
+    return *itr;
+  }
+}
+
+void RequestGroup::removeServerHost(int32_t cuid)
+{
+  remove_if(_serverHosts.begin(), _serverHosts.end(), FindServerHostByCUID(cuid));
+}
+  
+void RequestGroup::removeURIWhoseHostnameIs(const string& hostname)
+{
+  Strings newURIs;
+  Request req;
+  for(Strings::const_iterator itr = _uris.begin(); itr != _uris.end(); ++itr) {
+    if((*itr).find(hostname) == string::npos ||
+       req.setUrl(*itr) && req.getHost() != hostname) {
+      newURIs.push_back(*itr);
+    }
+  }
+  _logger->debug("GUID#%d - Removed %d duplicate hostname URIs",
+		 _gid, _uris.size()-newURIs.size());
+  _uris = newURIs;
+}

+ 34 - 0
src/RequestGroup.h

@@ -71,6 +71,9 @@ class CheckIntegrityEntry;
 typedef SharedHandle<CheckIntegrityEntry> CheckIntegrityEntryHandle;
 class DownloadResult;
 typedef SharedHandle<DownloadResult> DownloadResultHandle;
+class ServerHost;
+typedef SharedHandle<ServerHost> ServerHostHandle;
+typedef deque<ServerHostHandle> ServerHosts;
 
 class RequestGroup {
 private:
@@ -103,6 +106,8 @@ private:
 
   DependencyHandle _dependency;
 
+  ServerHosts _serverHosts;
+
   bool _fileAllocationEnabled;
 
   bool _preLocalFileCheckEnabled;
@@ -111,6 +116,8 @@ private:
 
   bool _forceHaltRequested;
 
+  bool _singleHostMultiConnectionEnabled;
+
   PreDownloadHandlers _preDownloadHandlers;
 
   PostDownloadHandlers _postDownloadHandlers;
@@ -310,6 +317,33 @@ public:
   {
     return _option;
   }
+
+  bool isSingleHostMultiConnectionEnabled() const
+  {
+    return _singleHostMultiConnectionEnabled;
+  }
+
+  void setSingleHostMultiConnectionEnabled(bool f)
+  {
+    _singleHostMultiConnectionEnabled = f;
+  }
+
+  /**
+   * Registers given ServerHost.
+   */
+  void registerServerHost(const ServerHostHandle& serverHost);
+
+  /**
+   * Returns ServerHost whose cuid is given cuid. If it is not found, returns
+   * 0.
+   */
+  ServerHostHandle searchServerHost(int32_t cuid) const;
+
+  ServerHostHandle searchServerHost(const string& hostname) const;
+
+  void removeServerHost(int32_t cuid);
+  
+  void removeURIWhoseHostnameIs(const string& hostname);
 };
 
 typedef SharedHandle<RequestGroup> RequestGroupHandle;

+ 40 - 0
src/ServerHost.cc

@@ -0,0 +1,40 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "ServerHost.h"
+
+ServerHost::ServerHost(int32_t cuid, const string& hostname):
+  _cuid(cuid), _hostname(hostname) {}
+
+ServerHost::~ServerHost() {}

+ 69 - 0
src/ServerHost.h

@@ -0,0 +1,69 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_SERVER_HOST_H_
+#define _D_SERVER_HOST_H_
+
+#include "common.h"
+
+class ServerHost {
+private:
+  int32_t _cuid;
+
+  string _hostname;
+  
+public:
+  ServerHost(int32_t cuid, const string& hostname);
+
+  ~ServerHost();
+
+  int32_t getCuid() const
+  {
+    return _cuid;
+  }
+
+  const string& getHostname() const
+  {
+    return _hostname;
+  }
+
+  bool operator<(const ServerHost& server) const
+  {
+    return this->_cuid < server._cuid;
+  }
+};
+
+typedef SharedHandle<ServerHost> ServerHostHandle;
+
+#endif // _D_SERVER_HOST_H_

+ 5 - 0
src/option_processing.cc

@@ -128,6 +128,7 @@ Option* option_processing(int argc, char* const argv[])
   op->put(PREF_SEED_RATIO, "1.0");
   op->put(PREF_ENABLE_DIRECT_IO, V_FALSE);
   op->put(PREF_ALLOW_PIECE_LENGTH_CHANGE, V_FALSE);
+  op->put(PREF_METALINK_PREFERRED_PROTOCOL, V_NONE);
   while(1) {
     int optIndex = 0;
     int lopt;
@@ -205,6 +206,7 @@ Option* option_processing(int argc, char* const argv[])
       { "metalink-os", required_argument, &lopt, 102 },
       { "follow-metalink", required_argument, &lopt, 103 },
       { "metalink-location", required_argument, &lopt, 104 },
+      { "metalink-preferred-protocol", required_argument, &lopt, 105 },
 #endif // ENABLE_METALINK
       { "version", no_argument, NULL, 'v' },
       { "help", no_argument, NULL, 'h' },
@@ -300,6 +302,9 @@ Option* option_processing(int argc, char* const argv[])
       case 104:
 	cmdstream << PREF_METALINK_LOCATION << "=" << optarg << "\n";
 	break;
+      case 105:
+	cmdstream << PREF_METALINK_PREFERRED_PROTOCOL << "=" << optarg << "\n";
+	break;
       case 200:
 	cmdstream << PREF_LOWEST_SPEED_LIMIT << "=" << optarg << "\n";
 	break;

+ 5 - 0
src/prefs.h

@@ -225,5 +225,10 @@
 #define PREF_METALINK_SERVERS "metalink-servers"
 // values: true | false | mem
 #define PREF_FOLLOW_METALINK "follow-metalink"
+// values: http | https | ftp | none
+#define PREF_METALINK_PREFERRED_PROTOCOL "metalink-preferred-protocol"
+#  define V_HTTP "http"
+#  define V_HTTPS "https"
+#  define V_FTP "ftp"
 
 #endif // _D_PREFS_H_

+ 4 - 0
src/version_usage.cc

@@ -300,6 +300,10 @@ void showUsage() {
   cout << _(" --metalink-location=LOCATION[,...] The location of the preferred server.\n"
 	    "                              A comma-deliminated list of locations is\n"
 	    "                              acceptable.") << endl;
+  cout << _(" --metalink-preferred-protocol=PROTO Specify preferred protocol. The possible\n"
+	    "                              values are 'http', 'https', 'ftp' and 'none'.\n"
+	    "                              Specifiy none to disable this feature.") << "\n"
+       << DEFAULT_MSG << "none" << "\n";
   cout << _(" --follow-metalink=true|false|mem If true or mem is specified, when a file\n"
 	    "                              whose suffix is .metaink or content type is\n"
 	    "                              application/metalink+xml is downloaded, aria2\n"

+ 13 - 0
test/MetalinkEntryTest.cc

@@ -9,6 +9,7 @@ class MetalinkEntryTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testDropUnsupportedResource);
   CPPUNIT_TEST(testReorderResourcesByPreference);
   CPPUNIT_TEST(testSetLocationPreference);
+  CPPUNIT_TEST(testSetProtocolPreference);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -21,6 +22,7 @@ public:
   void testDropUnsupportedResource();
   void testReorderResourcesByPreference();
   void testSetLocationPreference();
+  void testSetProtocolPreference();
 };
 
 
@@ -122,3 +124,14 @@ void MetalinkEntryTest::testSetLocationPreference()
   CPPUNIT_ASSERT_EQUAL(string("JP"), entry->resources[4]->location);
   CPPUNIT_ASSERT_EQUAL((int32_t)190, entry->resources[4]->preference);
 }
+
+void MetalinkEntryTest::testSetProtocolPreference()
+{
+  MetalinkEntryHandle entry = createTestEntry();
+  entry->setProtocolPreference("http", 1);
+  CPPUNIT_ASSERT_EQUAL(50, entry->resources[0]->preference); // ftp
+  CPPUNIT_ASSERT_EQUAL(101, entry->resources[1]->preference); // http, +1
+  CPPUNIT_ASSERT_EQUAL(60, entry->resources[2]->preference); // bittorrent
+  CPPUNIT_ASSERT_EQUAL(10, entry->resources[3]->preference); // not supported
+  CPPUNIT_ASSERT_EQUAL(90, entry->resources[4]->preference); // https
+}

+ 53 - 1
test/RequestGroupTest.cc

@@ -1,5 +1,6 @@
 #include "RequestGroup.h"
-#include "prefs.h"
+#include "ServerHost.h"
+#include "Option.h"
 #include <cppunit/extensions/HelperMacros.h>
 
 using namespace std;
@@ -7,12 +8,63 @@ using namespace std;
 class RequestGroupTest : public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(RequestGroupTest);
+  CPPUNIT_TEST(testRegisterSearchRemove);
+  CPPUNIT_TEST(testRemoveURIWhoseHostnameIs);
   CPPUNIT_TEST_SUITE_END();
 private:
 
 public:
   void setUp() {}
+
+  void testRegisterSearchRemove();
+  void testRemoveURIWhoseHostnameIs();
 };
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION( RequestGroupTest );
+
+void RequestGroupTest::testRegisterSearchRemove()
+{
+  Option op;
+  RequestGroup rg(&op, Strings());
+  ServerHostHandle sv1 = new ServerHost(1, "localhost1");
+  ServerHostHandle sv2 = new ServerHost(2, "localhost2");
+  ServerHostHandle sv3 = new ServerHost(3, "localhost3");
+
+  rg.registerServerHost(sv3);
+  rg.registerServerHost(sv1);
+  rg.registerServerHost(sv2);
+
+  CPPUNIT_ASSERT(rg.searchServerHost(0).isNull());
+
+  {
+    ServerHostHandle sv = rg.searchServerHost(1);
+    CPPUNIT_ASSERT(!sv.isNull());
+    CPPUNIT_ASSERT_EQUAL(string("localhost1"), sv->getHostname());
+  }
+
+  rg.removeServerHost(1);
+
+  {
+    ServerHostHandle sv = rg.searchServerHost(1);
+    CPPUNIT_ASSERT(sv.isNull());
+  }
+  {
+    ServerHostHandle sv = rg.searchServerHost(2);
+    CPPUNIT_ASSERT(!sv.isNull());
+    CPPUNIT_ASSERT_EQUAL(string("localhost2"), sv->getHostname());
+  }
+}
+
+void RequestGroupTest::testRemoveURIWhoseHostnameIs()
+{
+  const char* uris[] = { "http://localhost/aria2.zip",
+			 "ftp://localhost/aria2.zip",
+			 "http://mirror/aria2.zip" };
+  Option op;
+  RequestGroup rg(&op, Strings(&uris[0], &uris[3]));
+  rg.removeURIWhoseHostnameIs("localhost");
+  CPPUNIT_ASSERT_EQUAL((size_t)1, rg.getRemainingUris().size());
+  CPPUNIT_ASSERT_EQUAL(string("http://mirror/aria2.zip"),
+		       rg.getRemainingUris()[0]);
+}