瀏覽代碼

Implement simple Happy Eyeballs for HTTP/FTP downloads

Tatsuhiro Tsujikawa 12 年之前
父節點
當前提交
597e1a5c1b

+ 1 - 1
src/AbstractCommand.cc

@@ -225,7 +225,7 @@ bool AbstractCommand::execute() {
 #ifdef ENABLE_ASYNC_DNS
         && !asyncNameResolverMan_->resolverChecked()
 #endif // ENABLE_ASYNC_DNS
-        )) {
+        ) || noCheck()) {
       checkPoint_ = global::wallclock();
       if(getPieceStorage()) {
         if(!req_ || req_->getMaxPipelinedRequest() == 1 ||

+ 12 - 0
src/AbstractCommand.h

@@ -123,6 +123,11 @@ protected:
     return socket_;
   }
 
+  SharedHandle<SocketCore>& getSocket()
+  {
+    return socket_;
+  }
+
   void setSocket(const SharedHandle<SocketCore>& s);
 
   void createSocket();
@@ -215,6 +220,13 @@ protected:
   }
 
   void checkSocketRecvBuffer();
+
+  // Returns true if the derived class wants to execute
+  // executeInternal() unconditionally
+  virtual bool noCheck()
+  {
+    return false;
+  }
 public:
   AbstractCommand
   (cuid_t cuid, const SharedHandle<Request>& req,

+ 42 - 1
src/AbstractProxyRequestCommand.cc

@@ -44,6 +44,9 @@
 #include "SocketCore.h"
 #include "DownloadContext.h"
 #include "SocketRecvBuffer.h"
+#include "BackupIPv4ConnectCommand.h"
+#include "fmt.h"
+#include "LogFactory.h"
 
 namespace aria2 {
 
@@ -67,16 +70,48 @@ AbstractProxyRequestCommand::AbstractProxyRequestCommand
   setWriteCheckSocket(getSocket());
 }
 
-AbstractProxyRequestCommand::~AbstractProxyRequestCommand() {}
+AbstractProxyRequestCommand::~AbstractProxyRequestCommand()
+{
+  if(backupConnectionInfo_) {
+    backupConnectionInfo_->cancel = true;
+  }
+}
+
+bool AbstractProxyRequestCommand::noCheck() {
+  return backupConnectionInfo_ && !backupConnectionInfo_->ipaddr.empty();
+}
 
 bool AbstractProxyRequestCommand::executeInternal() {
   //socket->setBlockingMode();
   if(httpConnection_->sendBufferIsEmpty()) {
+    if(backupConnectionInfo_ && !backupConnectionInfo_->ipaddr.empty()) {
+      A2_LOG_INFO(fmt("CUID#%"PRId64" - Use backup connection address %s",
+                      getCuid(), backupConnectionInfo_->ipaddr.c_str()));
+      getDownloadEngine()->markBadIPAddress
+        (getRequest()->getConnectedHostname(),
+         getRequest()->getConnectedAddr(),
+         getRequest()->getConnectedPort());
+
+      getRequest()->setConnectedAddrInfo(getRequest()->getConnectedHostname(),
+                                         backupConnectionInfo_->ipaddr,
+                                         getRequest()->getConnectedPort());
+
+      Command* c = createSelf(backupConnectionInfo_->socket);
+      c->setStatus(STATUS_ONESHOT_REALTIME);
+      getDownloadEngine()->setNoWait(true);
+      getDownloadEngine()->addCommand(c);
+      backupConnectionInfo_.reset();
+      return true;
+    }
     if(!checkIfConnectionEstablished
        (getSocket(), getRequest()->getConnectedHostname(),
         getRequest()->getConnectedAddr(), getRequest()->getConnectedPort())) {
       return true;
     }
+    if(backupConnectionInfo_) {
+      backupConnectionInfo_->cancel = true;
+      backupConnectionInfo_.reset();
+    }
     SharedHandle<HttpRequest> httpRequest(new HttpRequest());
     httpRequest->setUserAgent(getOption()->get(PREF_USER_AGENT));
     httpRequest->setRequest(getRequest());
@@ -96,4 +131,10 @@ bool AbstractProxyRequestCommand::executeInternal() {
   }
 }
 
+void AbstractProxyRequestCommand::setBackupConnectInfo
+(const SharedHandle<BackupConnectInfo>& info)
+{
+  backupConnectionInfo_ = info;
+}
+
 } // namespace aria2

+ 11 - 0
src/AbstractProxyRequestCommand.h

@@ -41,19 +41,28 @@ namespace aria2 {
 
 class HttpConnection;
 class SocketCore;
+class BackupConnectInfo;
 
 class AbstractProxyRequestCommand : public AbstractCommand {
 private:
   SharedHandle<Request> proxyRequest_;
 
   SharedHandle<HttpConnection> httpConnection_;
+
+  SharedHandle<BackupConnectInfo> backupConnectionInfo_;
 protected:
   virtual bool executeInternal();
+  virtual bool noCheck();
 
   const SharedHandle<HttpConnection>& getHttpConnection() const
   {
     return httpConnection_;
   }
+
+  const SharedHandle<Request>& getProxyRequest() const
+  {
+    return proxyRequest_;
+  }
 public:
   AbstractProxyRequestCommand(cuid_t cuid,
                               const SharedHandle<Request>& req,
@@ -66,6 +75,8 @@ public:
   virtual ~AbstractProxyRequestCommand();
 
   virtual Command* getNextCommand() = 0;
+  virtual Command* createSelf(const SharedHandle<SocketCore>& socket) = 0;
+  void setBackupConnectInfo(const SharedHandle<BackupConnectInfo>& info);
 };
 
 } // namespace aria2

+ 140 - 0
src/BackupIPv4ConnectCommand.cc

@@ -0,0 +1,140 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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 "BackupIPv4ConnectCommand.h"
+#include "RequestGroup.h"
+#include "DownloadEngine.h"
+#include "SocketCore.h"
+#include "wallclock.h"
+#include "RecoverableException.h"
+#include "fmt.h"
+#include "LogFactory.h"
+#include "prefs.h"
+#include "Option.h"
+
+namespace aria2 {
+
+BackupConnectInfo::BackupConnectInfo()
+  : cancel(false)
+{}
+
+BackupIPv4ConnectCommand::BackupIPv4ConnectCommand
+(cuid_t cuid, const std::string& ipaddr, uint16_t port,
+ const SharedHandle<BackupConnectInfo>& info, Command* mainCommand,
+ RequestGroup* requestGroup, DownloadEngine* e)
+: Command(cuid),
+  ipaddr_(ipaddr),
+  port_(port),
+  info_(info),
+  mainCommand_(mainCommand),
+  requestGroup_(requestGroup),
+  e_(e),
+  startTime_(global::wallclock()),
+  timeoutCheck_(global::wallclock()),
+  timeout_(requestGroup_->getOption()->getAsInt(PREF_CONNECT_TIMEOUT))
+{
+  requestGroup_->increaseStreamCommand();
+  requestGroup_->increaseNumCommand();
+}
+
+BackupIPv4ConnectCommand::~BackupIPv4ConnectCommand()
+{
+  requestGroup_->decreaseNumCommand();
+  requestGroup_->decreaseStreamCommand();
+  if(socket_) {
+    e_->deleteSocketForWriteCheck(socket_, this);
+  }
+}
+
+bool BackupIPv4ConnectCommand::execute()
+{
+  bool retval = false;
+  if(requestGroup_->downloadFinished() || requestGroup_->isHaltRequested()) {
+    retval = true;
+  } else if(info_->cancel) {
+    A2_LOG_INFO(fmt("CUID#%"PRId64" - Backup connection canceled", getCuid()));
+    retval = true;
+  } else if(socket_) {
+    if(writeEventEnabled()) {
+      try {
+        std::string error = socket_->getSocketError();
+        if(error.empty()) {
+          A2_LOG_INFO(fmt("CUID#%"PRId64" - Backup connection to %s "
+                          "established", getCuid(), ipaddr_.c_str()));
+          info_->ipaddr = ipaddr_;
+          e_->deleteSocketForWriteCheck(socket_, this);
+          info_->socket.swap(socket_);
+          mainCommand_->setStatus(STATUS_ONESHOT_REALTIME);
+          e_->setNoWait(true);
+          retval = true;
+        } else {
+          A2_LOG_INFO(fmt("CUID#%"PRId64" - Backup connection failed: %s",
+                          getCuid(), error.c_str()));
+          retval = true;
+        }
+      } catch(RecoverableException& e) {
+        A2_LOG_INFO_EX(fmt("CUID#%"PRId64" - Backup connection failed",
+                           getCuid()), e);
+        retval = true;
+      }
+    }
+  } else if(!socket_) {
+    // TODO Although we check 300ms initial timeout as described in
+    // RFC 6555, the interval will be much longer and around 1 second
+    // due to the refresh interval mechanism in DownloadEngine.
+    if(startTime_.differenceInMillis(global::wallclock()) >= 300) {
+      socket_.reset(new SocketCore());
+      try {
+        socket_->establishConnection(ipaddr_, port_);
+        e_->addSocketForWriteCheck(socket_, this);
+        timeoutCheck_ = global::wallclock();
+      } catch(RecoverableException& e) {
+        A2_LOG_INFO_EX(fmt("CUID#%"PRId64" - Backup connection failed",
+                           getCuid()), e);
+        socket_.reset();
+        retval = true;
+      }
+    }
+  } else if(timeoutCheck_.difference(global::wallclock()) >= timeout_) {
+    A2_LOG_INFO(fmt("CUID#%"PRId64" - Backup connection command timeout",
+                    getCuid()));
+    retval = true;
+  }
+  if(!retval) {
+    e_->addCommand(this);
+  }
+  return retval;
+}
+
+} // namespace aria2

+ 89 - 0
src/BackupIPv4ConnectCommand.h

@@ -0,0 +1,89 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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 BACKUP_IPV4_CONNECT_COMMAND_H
+#define BACKUP_IPV4_CONNECT_COMMAND_H
+
+#include "Command.h"
+
+#include <string>
+
+#include "SharedHandle.h"
+#include "TimerA2.h"
+
+namespace aria2 {
+
+class RequestGroup;
+class DownloadEngine;
+class SocketCore;
+
+// Used to communicate mainCommand and backup IPv4 connection command.
+// When backup connection succeeds, ipaddr is filled with connected
+// IPv4 address and socket is a socket connected to the ipaddr.  If
+// mainCommand wants to cancel backup connection command, cancel
+// member becomes true.
+struct BackupConnectInfo {
+  std::string ipaddr;
+  SharedHandle<SocketCore> socket;
+  bool cancel;
+  BackupConnectInfo();
+};
+
+// Make backup connection to IPv4 address. This is a simplest RFC 6555
+// "Happy Eyeballs" implementation.
+class BackupIPv4ConnectCommand : public Command {
+public:
+  BackupIPv4ConnectCommand(cuid_t cuid,
+                           const std::string& ipaddr, uint16_t port,
+                           const SharedHandle<BackupConnectInfo>& info,
+                           Command* mainCommand,
+                           RequestGroup* requestGroup, DownloadEngine* e);
+  ~BackupIPv4ConnectCommand();
+  virtual bool execute();
+private:
+  std::string ipaddr_;
+  uint16_t port_;
+  SharedHandle<SocketCore> socket_;
+  SharedHandle<BackupConnectInfo> info_;
+  Command* mainCommand_;
+  RequestGroup* requestGroup_;
+  DownloadEngine* e_;
+  Timer startTime_;
+  Timer timeoutCheck_;
+  time_t timeout_;
+};
+
+} // namespace aria2
+
+#endif // BACKUP_IPV4_CONNECT_COMMAND_H

+ 17 - 1
src/FtpInitiateConnectionCommand.cc

@@ -56,6 +56,7 @@
 #include "AuthConfig.h"
 #include "fmt.h"
 #include "SocketRecvBuffer.h"
+#include "BackupIPv4ConnectCommand.h"
 
 namespace aria2 {
 
@@ -110,12 +111,22 @@ Command* FtpInitiateConnectionCommand::createNextCommand
                                  getRequestGroup(), hc, getDownloadEngine(),
                                  getSocket());
         c->setProxyRequest(proxyRequest);
+        SharedHandle<BackupConnectInfo> backupConnectInfo
+          = createBackupIPv4ConnectCommand(hostname, addr, port, c);
+        if(backupConnectInfo) {
+          c->setBackupConnectInfo(backupConnectInfo);
+        }
         command = c;
       } else if(proxyMethod == V_TUNNEL) {
         FtpTunnelRequestCommand* c =
           new FtpTunnelRequestCommand(getCuid(), getRequest(), getFileEntry(),
                                       getRequestGroup(), getDownloadEngine(),
                                       proxyRequest, getSocket());
+        SharedHandle<BackupConnectInfo> backupConnectInfo
+          = createBackupIPv4ConnectCommand(hostname, addr, port, c);
+        if(backupConnectInfo) {
+          c->setBackupConnectInfo(backupConnectInfo);
+        }
         command = c;
       } else {
         // TODO
@@ -163,11 +174,16 @@ Command* FtpInitiateConnectionCommand::createNextCommand
                       getCuid(), addr.c_str(), port));
       createSocket();
       getSocket()->establishConnection(addr, port);
+      getRequest()->setConnectedAddrInfo(hostname, addr, port);
       FtpNegotiationCommand* c =
         new FtpNegotiationCommand(getCuid(), getRequest(), getFileEntry(),
                                   getRequestGroup(), getDownloadEngine(),
                                   getSocket());
-      getRequest()->setConnectedAddrInfo(hostname, addr, port);
+      SharedHandle<BackupConnectInfo> backupConnectInfo
+        = createBackupIPv4ConnectCommand(hostname, addr, port, c);
+      if(backupConnectInfo) {
+        c->setBackupConnectInfo(backupConnectInfo);
+      }
       command = c;
     } else {
       // options contains "baseWorkingDir"

+ 43 - 1
src/FtpNegotiationCommand.cc

@@ -74,6 +74,7 @@
 #include "CheckIntegrityEntry.h"
 #include "error_code.h"
 #include "SocketRecvBuffer.h"
+#include "BackupIPv4ConnectCommand.h"
 #ifdef ENABLE_MESSAGE_DIGEST
 # include "ChecksumCheckIntegrityEntry.h"
 #endif // ENABLE_MESSAGE_DIGEST
@@ -105,7 +106,16 @@ FtpNegotiationCommand::FtpNegotiationCommand
   setWriteCheckSocket(getSocket());
 }
 
-FtpNegotiationCommand::~FtpNegotiationCommand() {}
+FtpNegotiationCommand::~FtpNegotiationCommand()
+{
+  if(backupConnectionInfo_) {
+    backupConnectionInfo_->cancel = true;
+  }
+}
+
+bool FtpNegotiationCommand::noCheck() {
+  return backupConnectionInfo_ && !backupConnectionInfo_->ipaddr.empty();
+}
 
 bool FtpNegotiationCommand::executeInternal() {
   while(processSequence(getSegments().front()));
@@ -145,12 +155,38 @@ bool FtpNegotiationCommand::executeInternal() {
 }
 
 bool FtpNegotiationCommand::recvGreeting() {
+  if(backupConnectionInfo_ && !backupConnectionInfo_->ipaddr.empty()) {
+    A2_LOG_INFO(fmt("CUID#%"PRId64" - Use backup connection address %s",
+                    getCuid(), backupConnectionInfo_->ipaddr.c_str()));
+    getDownloadEngine()->markBadIPAddress
+      (getRequest()->getConnectedHostname(),
+       getRequest()->getConnectedAddr(),
+       getRequest()->getConnectedPort());
+
+    getRequest()->setConnectedAddrInfo(getRequest()->getConnectedHostname(),
+                                       backupConnectionInfo_->ipaddr,
+                                       getRequest()->getConnectedPort());
+
+    FtpNegotiationCommand* c =
+      new FtpNegotiationCommand(getCuid(), getRequest(), getFileEntry(),
+                                getRequestGroup(), getDownloadEngine(),
+                                backupConnectionInfo_->socket);
+    c->setStatus(STATUS_ONESHOT_REALTIME);
+    getDownloadEngine()->setNoWait(true);
+    getDownloadEngine()->addCommand(c);
+    backupConnectionInfo_.reset();
+    return true;
+  }
   if(!checkIfConnectionEstablished
      (getSocket(), getRequest()->getConnectedHostname(),
       getRequest()->getConnectedAddr(), getRequest()->getConnectedPort())) {
     sequence_ = SEQ_EXIT;
     return false;
   }
+  if(backupConnectionInfo_) {
+    backupConnectionInfo_->cancel = true;
+    backupConnectionInfo_.reset();
+  }
   setTimeout(getRequestGroup()->getTimeout());
   //socket->setBlockingMode();
   disableWriteCheckSocket();
@@ -973,4 +1009,10 @@ void FtpNegotiationCommand::onDryRunFileFound()
   sequence_ = SEQ_HEAD_OK;
 }
 
+void FtpNegotiationCommand::setBackupConnectInfo
+(const SharedHandle<BackupConnectInfo>& info)
+{
+  backupConnectionInfo_ = info;
+}
+
 } // namespace aria2

+ 5 - 0
src/FtpNegotiationCommand.h

@@ -44,6 +44,7 @@ namespace aria2 {
 class FtpConnection;
 class SocketCore;
 class HttpConnection;
+class BackupConnectInfo;
 
 class FtpNegotiationCommand : public AbstractCommand {
 public:
@@ -153,8 +154,11 @@ private:
   std::string proxyAddr_;
 
   std::deque<std::string> cwdDirs_;
+
+  SharedHandle<BackupConnectInfo> backupConnectionInfo_;
 protected:
   virtual bool executeInternal();
+  virtual bool noCheck();
 public:
   FtpNegotiationCommand(cuid_t cuid,
                         const SharedHandle<Request>& req,
@@ -165,6 +169,7 @@ public:
                         Seq seq = SEQ_RECV_GREETING,
                         const std::string& baseWorkingDir = "/");
   virtual ~FtpNegotiationCommand();
+  void setBackupConnectInfo(const SharedHandle<BackupConnectInfo>& info);
 };
 
 } // namespace aria2

+ 8 - 0
src/FtpTunnelRequestCommand.cc

@@ -63,4 +63,12 @@ Command* FtpTunnelRequestCommand::getNextCommand()
      getHttpConnection(), getDownloadEngine(), getSocket());
 }
 
+Command* FtpTunnelRequestCommand::createSelf
+(const SharedHandle<SocketCore>& socket)
+{
+  return new FtpTunnelRequestCommand(getCuid(), getRequest(), getFileEntry(),
+                                     getRequestGroup(), getDownloadEngine(),
+                                     getProxyRequest(), socket);
+}
+
 } // namespace aria2

+ 2 - 0
src/FtpTunnelRequestCommand.h

@@ -53,6 +53,8 @@ public:
   virtual ~FtpTunnelRequestCommand();
 
   virtual Command* getNextCommand();
+
+  virtual Command* createSelf(const SharedHandle<SocketCore>& socket);
 };
 
 } // namespace aria2

+ 21 - 0
src/HttpInitiateConnectionCommand.cc

@@ -51,6 +51,7 @@
 #include "util.h"
 #include "fmt.h"
 #include "SocketRecvBuffer.h"
+#include "BackupIPv4ConnectCommand.h"
 
 namespace aria2 {
 
@@ -93,6 +94,11 @@ Command* HttpInitiateConnectionCommand::createNextCommand
                                       getDownloadEngine(),
                                       proxyRequest,
                                       getSocket());
+        SharedHandle<BackupConnectInfo> backupConnectionInfo
+          = createBackupIPv4ConnectCommand(hostname, addr, port, c);
+        if(backupConnectionInfo) {
+          c->setBackupConnectInfo(backupConnectionInfo);
+        }
         command = c;
       } else if(proxyMethod == V_GET) {
         SharedHandle<SocketRecvBuffer> socketRecvBuffer
@@ -107,6 +113,11 @@ Command* HttpInitiateConnectionCommand::createNextCommand
                                                        getDownloadEngine(),
                                                        getSocket());
         c->setProxyRequest(proxyRequest);
+        SharedHandle<BackupConnectInfo> backupConnectionInfo
+          = createBackupIPv4ConnectCommand(hostname, addr, port, c);
+        if(backupConnectionInfo) {
+          c->setBackupConnectInfo(backupConnectionInfo);
+        }
         command = c;
       } else {
         // TODO
@@ -131,6 +142,7 @@ Command* HttpInitiateConnectionCommand::createNextCommand
       command = c;
     }
   } else {
+    bool connectRequired = false;
     SharedHandle<SocketCore> pooledSocket =
       getDownloadEngine()->popPooledSocket
       (resolvedAddresses, getRequest()->getPort());
@@ -141,6 +153,7 @@ Command* HttpInitiateConnectionCommand::createNextCommand
       getSocket()->establishConnection(addr, port);
 
       getRequest()->setConnectedAddrInfo(hostname, addr, port);
+      connectRequired = true;
     } else {
       setSocket(pooledSocket);
       setConnectedAddrInfo(getRequest(), hostname, pooledSocket);
@@ -155,6 +168,14 @@ Command* HttpInitiateConnectionCommand::createNextCommand
                              httpConnection,
                              getDownloadEngine(),
                              getSocket());
+    if(connectRequired) {
+      SharedHandle<BackupConnectInfo> backupConnectInfo
+        = createBackupIPv4ConnectCommand(hostname, addr, port, c);
+      if(backupConnectInfo) {
+        c->setBackupConnectInfo(backupConnectInfo);
+      }
+    }
+
     command = c;
   }
   return command;

+ 12 - 0
src/HttpProxyRequestCommand.cc

@@ -62,4 +62,16 @@ Command* HttpProxyRequestCommand::getNextCommand()
      getHttpConnection(), getDownloadEngine(), getSocket());
 }
 
+Command* HttpProxyRequestCommand::createSelf
+(const SharedHandle<SocketCore>& socket)
+{
+  return new HttpProxyRequestCommand(getCuid(),
+                                     getRequest(),
+                                     getFileEntry(),
+                                     getRequestGroup(),
+                                     getDownloadEngine(),
+                                     getProxyRequest(),
+                                     socket);
+}
+
 } // namespace aria2

+ 2 - 0
src/HttpProxyRequestCommand.h

@@ -53,6 +53,8 @@ public:
   virtual ~HttpProxyRequestCommand();
 
   virtual Command* getNextCommand();
+
+  virtual Command* createSelf(const SharedHandle<SocketCore>& socket);
 };
 
 } // namespace aria2

+ 51 - 1
src/HttpRequestCommand.cc

@@ -59,6 +59,7 @@
 #include "LogFactory.h"
 #include "fmt.h"
 #include "SocketRecvBuffer.h"
+#include "BackupIPv4ConnectCommand.h"
 
 namespace aria2 {
 
@@ -79,7 +80,12 @@ HttpRequestCommand::HttpRequestCommand
   setWriteCheckSocket(getSocket());
 }
 
-HttpRequestCommand::~HttpRequestCommand() {}
+HttpRequestCommand::~HttpRequestCommand()
+{
+  if(backupConnectionInfo_) {
+    backupConnectionInfo_->cancel = true;
+  }
+}
 
 namespace {
 SharedHandle<HttpRequest>
@@ -122,14 +128,52 @@ createHttpRequest(const SharedHandle<Request>& req,
 }
 } // namespace
 
+bool HttpRequestCommand::noCheck() {
+  return backupConnectionInfo_ && !backupConnectionInfo_->ipaddr.empty();
+}
+
 bool HttpRequestCommand::executeInternal() {
   //socket->setBlockingMode();
   if(httpConnection_->sendBufferIsEmpty()) {
+    if(backupConnectionInfo_ && !backupConnectionInfo_->ipaddr.empty()) {
+      A2_LOG_INFO(fmt("CUID#%"PRId64" - Use backup connection address %s",
+                      getCuid(), backupConnectionInfo_->ipaddr.c_str()));
+      getDownloadEngine()->markBadIPAddress
+        (getRequest()->getConnectedHostname(),
+         getRequest()->getConnectedAddr(),
+         getRequest()->getConnectedPort());
+
+      getRequest()->setConnectedAddrInfo(getRequest()->getConnectedHostname(),
+                                         backupConnectionInfo_->ipaddr,
+                                         getRequest()->getConnectedPort());
+
+      SharedHandle<SocketRecvBuffer> socketRecvBuffer
+        (new SocketRecvBuffer(backupConnectionInfo_->socket));
+      SharedHandle<HttpConnection> httpConnection
+        (new HttpConnection(getCuid(), backupConnectionInfo_->socket,
+                            socketRecvBuffer));
+      HttpRequestCommand* c =
+        new HttpRequestCommand(getCuid(), getRequest(), getFileEntry(),
+                               getRequestGroup(),
+                               httpConnection,
+                               getDownloadEngine(),
+                               backupConnectionInfo_->socket);
+      c->setProxyRequest(proxyRequest_);
+      c->setStatus(STATUS_ONESHOT_REALTIME);
+      getDownloadEngine()->setNoWait(true);
+      getDownloadEngine()->addCommand(c);
+      backupConnectionInfo_.reset();
+      return true;
+    }
     if(!checkIfConnectionEstablished
        (getSocket(), getRequest()->getConnectedHostname(),
         getRequest()->getConnectedAddr(), getRequest()->getConnectedPort())) {
       return true;
     }
+    if(backupConnectionInfo_) {
+      backupConnectionInfo_->cancel = true;
+      backupConnectionInfo_.reset();
+    }
 #ifdef ENABLE_SSL
     if(getRequest()->getProtocol() == "https") {
       if(!getSocket()->tlsConnect(getRequest()->getHost())) {
@@ -235,4 +279,10 @@ void HttpRequestCommand::setProxyRequest
   proxyRequest_ = proxyRequest;
 }
 
+void HttpRequestCommand::setBackupConnectInfo
+(const SharedHandle<BackupConnectInfo>& info)
+{
+  backupConnectionInfo_ = info;
+}
+
 } // namespace aria2

+ 5 - 0
src/HttpRequestCommand.h

@@ -41,6 +41,7 @@ namespace aria2 {
 
 class HttpConnection;
 class SocketCore;
+class BackupConnectInfo;
 
 // HttpRequestCommand sends HTTP request header to remote server.
 // Because network I/O is non-blocking, execute() returns false if all
@@ -53,8 +54,11 @@ private:
   SharedHandle<Request> proxyRequest_;
 
   SharedHandle<HttpConnection> httpConnection_;
+
+  SharedHandle<BackupConnectInfo> backupConnectionInfo_;
 protected:
   virtual bool executeInternal();
+  virtual bool noCheck();
 public:
   HttpRequestCommand(cuid_t cuid,
                      const SharedHandle<Request>& req,
@@ -66,6 +70,7 @@ public:
   virtual ~HttpRequestCommand();
 
   void setProxyRequest(const SharedHandle<Request>& proxyRequest);
+  void setBackupConnectInfo(const SharedHandle<BackupConnectInfo>& info);
 };
 
 } // namespace aria2

+ 33 - 0
src/InitiateConnectionCommand.cc

@@ -51,6 +51,7 @@
 #include "RecoverableException.h"
 #include "fmt.h"
 #include "SocketRecvBuffer.h"
+#include "BackupIPv4ConnectCommand.h"
 
 namespace aria2 {
 
@@ -127,4 +128,36 @@ void InitiateConnectionCommand::setConnectedAddrInfo
   req->setConnectedAddrInfo(hostname, peerAddr.first, peerAddr.second);
 }
 
+SharedHandle<BackupConnectInfo>
+InitiateConnectionCommand::createBackupIPv4ConnectCommand
+(const std::string& hostname, const std::string& ipaddr, uint16_t port,
+ Command* mainCommand)
+{
+  // Prepare IPv4 backup connection attemp in "Happy Eyeballs"
+  // fashion.
+  SharedHandle<BackupConnectInfo> info;
+  char buf[sizeof(in6_addr)];
+  if(inetPton(AF_INET6, ipaddr.c_str(), &buf) == -1) {
+    return info;
+  }
+  A2_LOG_INFO("Searching IPv4 address for backup connection attempt");
+  std::vector<std::string> addrs;
+  getDownloadEngine()->findAllCachedIPAddresses(std::back_inserter(addrs),
+                                                hostname, port);
+  for(std::vector<std::string>::const_iterator i = addrs.begin(),
+        eoi = addrs.end(); i != eoi; ++i) {
+    if(inetPton(AF_INET, (*i).c_str(), &buf) == 0) {
+      info.reset(new BackupConnectInfo());
+      BackupIPv4ConnectCommand* command = new BackupIPv4ConnectCommand
+        (getDownloadEngine()->newCUID(), *i, port, info, mainCommand,
+         getRequestGroup(), getDownloadEngine());
+      A2_LOG_INFO(fmt("Issue backup connection command CUID#%"PRId64
+                      ", addr=%s", command->getCuid(), (*i).c_str()));
+      getDownloadEngine()->addCommand(command);
+      return info;
+    }
+  }
+  return info;
+}
+
 } // namespace aria2

+ 6 - 0
src/InitiateConnectionCommand.h

@@ -39,6 +39,8 @@
 
 namespace aria2 {
 
+class BackupConnectInfo;
+
 class InitiateConnectionCommand : public AbstractCommand {
 protected:
   /**
@@ -64,6 +66,10 @@ protected:
   (const SharedHandle<Request>& req,
    const std::string& hostname,
    const SharedHandle<SocketCore>& socket);
+
+  SharedHandle<BackupConnectInfo> createBackupIPv4ConnectCommand
+  (const std::string& hostname, const std::string& ipaddr, uint16_t port,
+   Command* mainCommand);
 public:
   InitiateConnectionCommand(cuid_t cuid, const SharedHandle<Request>& req,
                             const SharedHandle<FileEntry>& fileEntry,

+ 2 - 1
src/Makefile.am

@@ -245,7 +245,8 @@ SRCS =  SocketCore.cc SocketCore.h\
 	WrDiskCacheEntry.cc WrDiskCacheEntry.h\
 	GroupId.cc GroupId.h\
 	IndexedList.h\
-	SaveSessionCommand.h SaveSessionCommand.cc
+	SaveSessionCommand.h SaveSessionCommand.cc\
+	BackupIPv4ConnectCommand.h BackupIPv4ConnectCommand.cc
 
 if MINGW_BUILD
 SRCS += WinConsoleFile.cc WinConsoleFile.h