summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2013-04-01 00:12:27 +0900
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2013-04-01 00:12:27 +0900
commit597e1a5c1b10aa3668f91bcae02a864dcd0c045b (patch)
tree4437c2896b410b441013452b7385dfa303441772
parentd671d8bf3672cbefc06f3727f82b77872abe6120 (diff)
Implement simple Happy Eyeballs for HTTP/FTP downloads
-rw-r--r--src/AbstractCommand.cc2
-rw-r--r--src/AbstractCommand.h12
-rw-r--r--src/AbstractProxyRequestCommand.cc43
-rw-r--r--src/AbstractProxyRequestCommand.h11
-rw-r--r--src/BackupIPv4ConnectCommand.cc140
-rw-r--r--src/BackupIPv4ConnectCommand.h89
-rw-r--r--src/FtpInitiateConnectionCommand.cc18
-rw-r--r--src/FtpNegotiationCommand.cc44
-rw-r--r--src/FtpNegotiationCommand.h5
-rw-r--r--src/FtpTunnelRequestCommand.cc8
-rw-r--r--src/FtpTunnelRequestCommand.h2
-rw-r--r--src/HttpInitiateConnectionCommand.cc21
-rw-r--r--src/HttpProxyRequestCommand.cc12
-rw-r--r--src/HttpProxyRequestCommand.h2
-rw-r--r--src/HttpRequestCommand.cc52
-rw-r--r--src/HttpRequestCommand.h5
-rw-r--r--src/InitiateConnectionCommand.cc33
-rw-r--r--src/InitiateConnectionCommand.h6
-rw-r--r--src/Makefile.am3
19 files changed, 502 insertions, 6 deletions
diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc
index 916e9fe8..8b311fcb 100644
--- a/src/AbstractCommand.cc
+++ b/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 ||
diff --git a/src/AbstractCommand.h b/src/AbstractCommand.h
index 53676077..d0749aa7 100644
--- a/src/AbstractCommand.h
+++ b/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,
diff --git a/src/AbstractProxyRequestCommand.cc b/src/AbstractProxyRequestCommand.cc
index 22bce921..1f9fe1ba 100644
--- a/src/AbstractProxyRequestCommand.cc
+++ b/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
diff --git a/src/AbstractProxyRequestCommand.h b/src/AbstractProxyRequestCommand.h
index 841ebf85..ed863e14 100644
--- a/src/AbstractProxyRequestCommand.h
+++ b/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
diff --git a/src/BackupIPv4ConnectCommand.cc b/src/BackupIPv4ConnectCommand.cc
new file mode 100644
index 00000000..9ca2081b
--- /dev/null
+++ b/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
diff --git a/src/BackupIPv4ConnectCommand.h b/src/BackupIPv4ConnectCommand.h
new file mode 100644
index 00000000..de207bc1
--- /dev/null
+++ b/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
diff --git a/src/FtpInitiateConnectionCommand.cc b/src/FtpInitiateConnectionCommand.cc
index 374bfccb..1daf5e61 100644
--- a/src/FtpInitiateConnectionCommand.cc
+++ b/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"
diff --git a/src/FtpNegotiationCommand.cc b/src/FtpNegotiationCommand.cc
index 03827b54..b21ff71f 100644
--- a/src/FtpNegotiationCommand.cc
+++ b/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
diff --git a/src/FtpNegotiationCommand.h b/src/FtpNegotiationCommand.h
index 07e4c228..ebd79544 100644
--- a/src/FtpNegotiationCommand.h
+++ b/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
diff --git a/src/FtpTunnelRequestCommand.cc b/src/FtpTunnelRequestCommand.cc
index 2ca92895..c74a1665 100644
--- a/src/FtpTunnelRequestCommand.cc
+++ b/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
diff --git a/src/FtpTunnelRequestCommand.h b/src/FtpTunnelRequestCommand.h
index c22ff381..97b585cd 100644
--- a/src/FtpTunnelRequestCommand.h
+++ b/src/FtpTunnelRequestCommand.h
@@ -53,6 +53,8 @@ public:
virtual ~FtpTunnelRequestCommand();
virtual Command* getNextCommand();
+
+ virtual Command* createSelf(const SharedHandle<SocketCore>& socket);
};
} // namespace aria2
diff --git a/src/HttpInitiateConnectionCommand.cc b/src/HttpInitiateConnectionCommand.cc
index 794b1886..deb9c62d 100644
--- a/src/HttpInitiateConnectionCommand.cc
+++ b/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;
diff --git a/src/HttpProxyRequestCommand.cc b/src/HttpProxyRequestCommand.cc
index 410345ce..9be2a230 100644
--- a/src/HttpProxyRequestCommand.cc
+++ b/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
diff --git a/src/HttpProxyRequestCommand.h b/src/HttpProxyRequestCommand.h
index 60fd6648..15b50c31 100644
--- a/src/HttpProxyRequestCommand.h
+++ b/src/HttpProxyRequestCommand.h
@@ -53,6 +53,8 @@ public:
virtual ~HttpProxyRequestCommand();
virtual Command* getNextCommand();
+
+ virtual Command* createSelf(const SharedHandle<SocketCore>& socket);
};
} // namespace aria2
diff --git a/src/HttpRequestCommand.cc b/src/HttpRequestCommand.cc
index f393125f..6fc083c6 100644
--- a/src/HttpRequestCommand.cc
+++ b/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
diff --git a/src/HttpRequestCommand.h b/src/HttpRequestCommand.h
index 8f6d37f9..6ca2278b 100644
--- a/src/HttpRequestCommand.h
+++ b/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
diff --git a/src/InitiateConnectionCommand.cc b/src/InitiateConnectionCommand.cc
index baee1f74..c4d084fa 100644
--- a/src/InitiateConnectionCommand.cc
+++ b/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
diff --git a/src/InitiateConnectionCommand.h b/src/InitiateConnectionCommand.h
index 7b52b353..374de527 100644
--- a/src/InitiateConnectionCommand.h
+++ b/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,
diff --git a/src/Makefile.am b/src/Makefile.am
index 2cc7ba21..6eeee32f 100644
--- a/src/Makefile.am
+++ b/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