mirror of https://github.com/aria2/aria2
2008-12-16 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Fixed the bug that causes corrupted downloads if HTTP pipelining is enabled and the server doesn't support keep-alive. * src/AbstractCommand.cc * src/DownloadCommand.cc * src/HttpDownloadCommand.cc * src/HttpDownloadCommand.h * src/HttpResponseCommand.cc * src/Request.cc * src/Request.h * test/RequestTest.ccpull/1/head
parent
110749df84
commit
5a639a3d1e
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
||||||
|
2008-12-16 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||||
|
|
||||||
|
Fixed the bug that causes corrupted downloads if HTTP pipelining
|
||||||
|
is enabled and the server doesn't support keep-alive.
|
||||||
|
* src/AbstractCommand.cc
|
||||||
|
* src/DownloadCommand.cc
|
||||||
|
* src/HttpDownloadCommand.cc
|
||||||
|
* src/HttpDownloadCommand.h
|
||||||
|
* src/HttpResponseCommand.cc
|
||||||
|
* src/Request.cc
|
||||||
|
* src/Request.h
|
||||||
|
* test/RequestTest.cc
|
||||||
|
|
||||||
2008-12-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
2008-12-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||||
|
|
||||||
Removed Dictionary/List/Data and its related classes.
|
Removed Dictionary/List/Data and its related classes.
|
||||||
|
|
|
@ -123,13 +123,7 @@ bool AbstractCommand::execute() {
|
||||||
if(!_requestGroup->getPieceStorage().isNull()) {
|
if(!_requestGroup->getPieceStorage().isNull()) {
|
||||||
_segments.clear();
|
_segments.clear();
|
||||||
_requestGroup->getSegmentMan()->getInFlightSegment(_segments, cuid);
|
_requestGroup->getSegmentMan()->getInFlightSegment(_segments, cuid);
|
||||||
size_t maxSegments;
|
while(_segments.size() < req->getMaxPipelinedRequest()) {
|
||||||
if(req->isPipeliningEnabled()) {
|
|
||||||
maxSegments = e->option->getAsInt(PREF_MAX_HTTP_PIPELINING);
|
|
||||||
} else {
|
|
||||||
maxSegments = 1;
|
|
||||||
}
|
|
||||||
while(_segments.size() < maxSegments) {
|
|
||||||
SegmentHandle segment = _requestGroup->getSegmentMan()->getSegment(cuid);
|
SegmentHandle segment = _requestGroup->getSegmentMan()->getSegment(cuid);
|
||||||
if(segment.isNull()) {
|
if(segment.isNull()) {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -263,7 +263,9 @@ bool DownloadCommand::prepareForNextSegment() {
|
||||||
#endif // ENABLE_MESSAGE_DIGEST
|
#endif // ENABLE_MESSAGE_DIGEST
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if(_segments.size()) {
|
// The number of segments should be 1 in order to pass through the next
|
||||||
|
// segment.
|
||||||
|
if(_segments.size() == 1) {
|
||||||
SegmentHandle tempSegment = _segments.front();
|
SegmentHandle tempSegment = _segments.front();
|
||||||
SegmentHandle nextSegment =
|
SegmentHandle nextSegment =
|
||||||
_requestGroup->getSegmentMan()->getSegment(cuid,
|
_requestGroup->getSegmentMan()->getSegment(cuid,
|
||||||
|
|
|
@ -43,23 +43,37 @@
|
||||||
#include "Socket.h"
|
#include "Socket.h"
|
||||||
#include "prefs.h"
|
#include "prefs.h"
|
||||||
#include "Option.h"
|
#include "Option.h"
|
||||||
|
#include "HttpResponse.h"
|
||||||
|
#include "HttpHeader.h"
|
||||||
|
#include "Range.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
HttpDownloadCommand::HttpDownloadCommand(int cuid,
|
HttpDownloadCommand::HttpDownloadCommand
|
||||||
|
(int cuid,
|
||||||
const RequestHandle& req,
|
const RequestHandle& req,
|
||||||
RequestGroup* requestGroup,
|
RequestGroup* requestGroup,
|
||||||
|
const SharedHandle<HttpResponse>& httpResponse,
|
||||||
const HttpConnectionHandle& httpConnection,
|
const HttpConnectionHandle& httpConnection,
|
||||||
DownloadEngine* e,
|
DownloadEngine* e,
|
||||||
const SocketHandle& socket)
|
const SocketHandle& socket)
|
||||||
:DownloadCommand(cuid, req, requestGroup, e, socket),
|
:DownloadCommand(cuid, req, requestGroup, e, socket),
|
||||||
|
_httpResponse(httpResponse),
|
||||||
_httpConnection(httpConnection) {}
|
_httpConnection(httpConnection) {}
|
||||||
|
|
||||||
HttpDownloadCommand::~HttpDownloadCommand() {}
|
HttpDownloadCommand::~HttpDownloadCommand() {}
|
||||||
|
|
||||||
bool HttpDownloadCommand::prepareForNextSegment() {
|
bool HttpDownloadCommand::prepareForNextSegment() {
|
||||||
if(req->isPipeliningEnabled() && !_requestGroup->downloadFinished()) {
|
bool downloadFinished = _requestGroup->downloadFinished();
|
||||||
Command* command = new HttpRequestCommand(cuid, req, _requestGroup, _httpConnection, e, socket);
|
if(req->isPipeliningEnabled() && !downloadFinished) {
|
||||||
|
HttpRequestCommand* command =
|
||||||
|
new HttpRequestCommand(cuid, req, _requestGroup, _httpConnection, e,
|
||||||
|
socket);
|
||||||
|
// Set proxy request here. aria2 sends the HTTP request specialized for
|
||||||
|
// proxy.
|
||||||
|
if(e->option->get(PREF_PROXY_METHOD) == V_GET) {
|
||||||
|
command->setProxyRequest(createProxyRequest());
|
||||||
|
}
|
||||||
e->commands.push_back(command);
|
e->commands.push_back(command);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -71,7 +85,20 @@ bool HttpDownloadCommand::prepareForNextSegment() {
|
||||||
_requestGroup->getTotalLength()))) {
|
_requestGroup->getTotalLength()))) {
|
||||||
e->poolSocket(req, isProxyDefined(), socket);
|
e->poolSocket(req, isProxyDefined(), socket);
|
||||||
}
|
}
|
||||||
|
// The request was sent assuming that server supported pipelining, but
|
||||||
|
// it turned out that server didn't support it.
|
||||||
|
// We detect this situation by comparing the end byte in range header
|
||||||
|
// of the response with the end byte of segment.
|
||||||
|
// If it is the same, HTTP negotiation is necessary for the next request.
|
||||||
|
if(!req->isPipeliningEnabled() && req->isPipeliningHint() &&
|
||||||
|
!_segments.empty() && !downloadFinished) {
|
||||||
|
const SharedHandle<Segment>& segment = _segments.front();
|
||||||
|
if(segment->getPosition()+segment->getLength() ==
|
||||||
|
static_cast<uint64_t>(_httpResponse->getHttpHeader()->
|
||||||
|
getRange()->getEndByte()+1)) {
|
||||||
|
return prepareForRetry(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
return DownloadCommand::prepareForNextSegment();
|
return DownloadCommand::prepareForNextSegment();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,12 @@
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
class HttpResponse;
|
||||||
class HttpConnection;
|
class HttpConnection;
|
||||||
|
|
||||||
class HttpDownloadCommand : public DownloadCommand {
|
class HttpDownloadCommand : public DownloadCommand {
|
||||||
private:
|
private:
|
||||||
|
SharedHandle<HttpResponse> _httpResponse;
|
||||||
SharedHandle<HttpConnection> _httpConnection;
|
SharedHandle<HttpConnection> _httpConnection;
|
||||||
protected:
|
protected:
|
||||||
virtual bool prepareForNextSegment();
|
virtual bool prepareForNextSegment();
|
||||||
|
@ -50,6 +52,7 @@ public:
|
||||||
HttpDownloadCommand(int cuid,
|
HttpDownloadCommand(int cuid,
|
||||||
const SharedHandle<Request>& req,
|
const SharedHandle<Request>& req,
|
||||||
RequestGroup* requestGroup,
|
RequestGroup* requestGroup,
|
||||||
|
const SharedHandle<HttpResponse>& httpResponse,
|
||||||
const SharedHandle<HttpConnection>& httpConnection,
|
const SharedHandle<HttpConnection>& httpConnection,
|
||||||
DownloadEngine* e,
|
DownloadEngine* e,
|
||||||
const SharedHandle<SocketCore>& s);
|
const SharedHandle<SocketCore>& s);
|
||||||
|
|
|
@ -100,6 +100,9 @@ bool HttpResponseCommand::executeInternal()
|
||||||
// We don't care whether non-HTTP/1.1 server returns Connection: keep-alive.
|
// We don't care whether non-HTTP/1.1 server returns Connection: keep-alive.
|
||||||
req->supportsPersistentConnection
|
req->supportsPersistentConnection
|
||||||
(httpResponse->supportsPersistentConnection());
|
(httpResponse->supportsPersistentConnection());
|
||||||
|
if(req->isPipeliningEnabled()) {
|
||||||
|
req->setMaxPipelinedRequest(e->option->getAsInt(PREF_MAX_HTTP_PIPELINING));
|
||||||
|
}
|
||||||
|
|
||||||
if(httpResponse->getResponseStatus() >= HttpHeader::S300) {
|
if(httpResponse->getResponseStatus() >= HttpHeader::S300) {
|
||||||
if(httpResponse->getResponseStatus() == HttpHeader::S404) {
|
if(httpResponse->getResponseStatus() == HttpHeader::S404) {
|
||||||
|
@ -328,8 +331,8 @@ HttpDownloadCommand* HttpResponseCommand::createHttpDownloadCommand
|
||||||
{
|
{
|
||||||
|
|
||||||
HttpDownloadCommand* command =
|
HttpDownloadCommand* command =
|
||||||
new HttpDownloadCommand(cuid, req, _requestGroup, httpConnection, e,
|
new HttpDownloadCommand(cuid, req, _requestGroup,
|
||||||
socket);
|
httpResponse, httpConnection, e, socket);
|
||||||
command->setMaxDownloadSpeedLimit
|
command->setMaxDownloadSpeedLimit
|
||||||
(e->option->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
|
(e->option->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
|
||||||
command->setStartupIdleTime(e->option->getAsInt(PREF_STARTUP_IDLE_TIME));
|
command->setStartupIdleTime(e->option->getAsInt(PREF_STARTUP_IDLE_TIME));
|
||||||
|
|
|
@ -69,6 +69,7 @@ Request::Request():
|
||||||
_supportsPersistentConnection(true),
|
_supportsPersistentConnection(true),
|
||||||
_keepAliveHint(false),
|
_keepAliveHint(false),
|
||||||
_pipeliningHint(false),
|
_pipeliningHint(false),
|
||||||
|
_maxPipelinedRequest(1),
|
||||||
method(METHOD_GET)
|
method(METHOD_GET)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -115,12 +116,14 @@ static std::string urlencode(const std::string& src)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::setUrl(const std::string& url) {
|
bool Request::setUrl(const std::string& url) {
|
||||||
|
_supportsPersistentConnection = true;
|
||||||
this->url = url;
|
this->url = url;
|
||||||
return parseUrl(urlencode(removeFragment(url)));
|
return parseUrl(urlencode(removeFragment(url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::resetUrl() {
|
bool Request::resetUrl() {
|
||||||
previousUrl = referer;
|
previousUrl = referer;
|
||||||
|
_supportsPersistentConnection = true;
|
||||||
return parseUrl(urlencode(removeFragment(url)));
|
return parseUrl(urlencode(removeFragment(url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,4 +246,19 @@ unsigned int Request::getRedirectCount() const
|
||||||
return _redirectCount;
|
return _redirectCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Request::isPipeliningHint() const
|
||||||
|
{
|
||||||
|
return _pipeliningHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setMaxPipelinedRequest(unsigned int num)
|
||||||
|
{
|
||||||
|
_maxPipelinedRequest = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Request::getMaxPipelinedRequest() const
|
||||||
|
{
|
||||||
|
return _maxPipelinedRequest;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -72,6 +72,8 @@ private:
|
||||||
bool _keepAliveHint;
|
bool _keepAliveHint;
|
||||||
// enable pipelining if possible.
|
// enable pipelining if possible.
|
||||||
bool _pipeliningHint;
|
bool _pipeliningHint;
|
||||||
|
// maximum number of pipelined requests
|
||||||
|
unsigned int _maxPipelinedRequest;
|
||||||
|
|
||||||
std::string method;
|
std::string method;
|
||||||
|
|
||||||
|
@ -144,6 +146,12 @@ public:
|
||||||
_pipeliningHint = pipeliningHint;
|
_pipeliningHint = pipeliningHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPipeliningHint() const;
|
||||||
|
|
||||||
|
void setMaxPipelinedRequest(unsigned int num);
|
||||||
|
|
||||||
|
unsigned int getMaxPipelinedRequest() const;
|
||||||
|
|
||||||
void setMethod(const std::string& method) {
|
void setMethod(const std::string& method) {
|
||||||
this->method = method;
|
this->method = method;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,12 @@ class RequestTest:public CppUnit::TestFixture {
|
||||||
CPPUNIT_TEST(testSetUrl_username);
|
CPPUNIT_TEST(testSetUrl_username);
|
||||||
CPPUNIT_TEST(testSetUrl_usernamePassword);
|
CPPUNIT_TEST(testSetUrl_usernamePassword);
|
||||||
CPPUNIT_TEST(testSetUrl_zeroUsername);
|
CPPUNIT_TEST(testSetUrl_zeroUsername);
|
||||||
|
CPPUNIT_TEST(testSetUrl_supportsPersistentConnection);
|
||||||
CPPUNIT_TEST(testRedirectUrl);
|
CPPUNIT_TEST(testRedirectUrl);
|
||||||
CPPUNIT_TEST(testRedirectUrl2);
|
CPPUNIT_TEST(testRedirectUrl2);
|
||||||
|
CPPUNIT_TEST(testRedirectUrl_supportsPersistentConnection);
|
||||||
CPPUNIT_TEST(testResetUrl);
|
CPPUNIT_TEST(testResetUrl);
|
||||||
|
CPPUNIT_TEST(testResetUrl_supportsPersistentConnection);
|
||||||
CPPUNIT_TEST(testInnerLink);
|
CPPUNIT_TEST(testInnerLink);
|
||||||
CPPUNIT_TEST(testInnerLinkInReferer);
|
CPPUNIT_TEST(testInnerLinkInReferer);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
@ -59,9 +62,12 @@ public:
|
||||||
void testSetUrl_username();
|
void testSetUrl_username();
|
||||||
void testSetUrl_usernamePassword();
|
void testSetUrl_usernamePassword();
|
||||||
void testSetUrl_zeroUsername();
|
void testSetUrl_zeroUsername();
|
||||||
|
void testSetUrl_supportsPersistentConnection();
|
||||||
void testRedirectUrl();
|
void testRedirectUrl();
|
||||||
void testRedirectUrl2();
|
void testRedirectUrl2();
|
||||||
|
void testRedirectUrl_supportsPersistentConnection();
|
||||||
void testResetUrl();
|
void testResetUrl();
|
||||||
|
void testResetUrl_supportsPersistentConnection();
|
||||||
void testInnerLink();
|
void testInnerLink();
|
||||||
void testInnerLinkInReferer();
|
void testInnerLinkInReferer();
|
||||||
};
|
};
|
||||||
|
@ -447,4 +453,34 @@ void RequestTest::testSetUrl_usernamePassword()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestTest::testSetUrl_supportsPersistentConnection()
|
||||||
|
{
|
||||||
|
Request req;
|
||||||
|
CPPUNIT_ASSERT(req.setUrl("http://host/file"));
|
||||||
|
req.supportsPersistentConnection(false);
|
||||||
|
CPPUNIT_ASSERT(!req.supportsPersistentConnection());
|
||||||
|
req.setUrl("http://host/file");
|
||||||
|
CPPUNIT_ASSERT(req.supportsPersistentConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestTest::testResetUrl_supportsPersistentConnection()
|
||||||
|
{
|
||||||
|
Request req;
|
||||||
|
CPPUNIT_ASSERT(req.setUrl("http://host/file"));
|
||||||
|
req.supportsPersistentConnection(false);
|
||||||
|
CPPUNIT_ASSERT(!req.supportsPersistentConnection());
|
||||||
|
req.resetUrl();
|
||||||
|
CPPUNIT_ASSERT(req.supportsPersistentConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestTest::testRedirectUrl_supportsPersistentConnection()
|
||||||
|
{
|
||||||
|
Request req;
|
||||||
|
CPPUNIT_ASSERT(req.setUrl("http://host/file"));
|
||||||
|
req.supportsPersistentConnection(false);
|
||||||
|
CPPUNIT_ASSERT(!req.supportsPersistentConnection());
|
||||||
|
req.redirectUrl("http://host/file");
|
||||||
|
CPPUNIT_ASSERT(req.supportsPersistentConnection());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
Loading…
Reference in New Issue