mirror of https://github.com/aria2/aria2
2008-11-23 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Use HEAD method to get file size from HTTP server for segmented downloading. The request to the BitTorrent Tracker always uses GET method because the response of the tracker is small and it doesn't need segmented download. * src/FtpNegotiationCommand.cc * src/HttpRequest.cc * src/HttpResponseCommand.cc * src/HttpSkipResponseCommand.cc * src/RequestGroup.cc * src/RequestGroup.h * src/TrackerWatcherCommand.cc * test/HttpRequestTest.ccpull/1/head
parent
c104ac8319
commit
62105714ef
16
ChangeLog
16
ChangeLog
|
@ -1,3 +1,19 @@
|
||||||
|
2008-11-23 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||||
|
|
||||||
|
Use HEAD method to get file size from HTTP server for segmented
|
||||||
|
downloading.
|
||||||
|
The request to the BitTorrent Tracker always uses GET method because
|
||||||
|
the response of the tracker is small and it doesn't need segmented
|
||||||
|
download.
|
||||||
|
* src/FtpNegotiationCommand.cc
|
||||||
|
* src/HttpRequest.cc
|
||||||
|
* src/HttpResponseCommand.cc
|
||||||
|
* src/HttpSkipResponseCommand.cc
|
||||||
|
* src/RequestGroup.cc
|
||||||
|
* src/RequestGroup.h
|
||||||
|
* src/TrackerWatcherCommand.cc
|
||||||
|
* test/HttpRequestTest.cc
|
||||||
|
|
||||||
2008-11-23 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
2008-11-23 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||||
|
|
||||||
Fixed the bug that causes floating exception when -T option is used and
|
Fixed the bug that causes floating exception when -T option is used and
|
||||||
|
|
|
@ -341,12 +341,6 @@ bool FtpNegotiationCommand::onFileSizeDetermined(uint64_t totalLength)
|
||||||
} else {
|
} else {
|
||||||
_requestGroup->initPieceStorage();
|
_requestGroup->initPieceStorage();
|
||||||
|
|
||||||
// TODO Is this really necessary?
|
|
||||||
if(req->getMethod() == Request::METHOD_HEAD) {
|
|
||||||
sequence = SEQ_HEAD_OK;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BtProgressInfoFileHandle infoFile(new DefaultBtProgressInfoFile(_requestGroup->getDownloadContext(), _requestGroup->getPieceStorage(), e->option));
|
BtProgressInfoFileHandle infoFile(new DefaultBtProgressInfoFile(_requestGroup->getDownloadContext(), _requestGroup->getPieceStorage(), e->option));
|
||||||
if(!infoFile->exists() && _requestGroup->downloadFinishedByFileLength()) {
|
if(!infoFile->exists() && _requestGroup->downloadFinishedByFileLength()) {
|
||||||
sequence = SEQ_DOWNLOAD_ALREADY_COMPLETED;
|
sequence = SEQ_DOWNLOAD_ALREADY_COMPLETED;
|
||||||
|
|
|
@ -136,7 +136,7 @@ std::string HttpRequest::createRequest() const
|
||||||
{
|
{
|
||||||
SharedHandle<AuthConfig> authConfig =
|
SharedHandle<AuthConfig> authConfig =
|
||||||
_authConfigFactory->createAuthConfig(request);
|
_authConfigFactory->createAuthConfig(request);
|
||||||
std::string requestLine = "GET ";
|
std::string requestLine = request->getMethod()+" ";
|
||||||
if(!_proxyRequest.isNull()) {
|
if(!_proxyRequest.isNull()) {
|
||||||
if(getProtocol() == Request::PROTO_FTP &&
|
if(getProtocol() == Request::PROTO_FTP &&
|
||||||
request->getUsername().empty() && !authConfig->getUser().empty()) {
|
request->getUsername().empty() && !authConfig->getUser().empty()) {
|
||||||
|
|
|
@ -188,12 +188,6 @@ bool HttpResponseCommand::handleDefaultEncoding(const HttpResponseHandle& httpRe
|
||||||
HttpRequestHandle httpRequest = httpResponse->getHttpRequest();
|
HttpRequestHandle httpRequest = httpResponse->getHttpRequest();
|
||||||
_requestGroup->initPieceStorage();
|
_requestGroup->initPieceStorage();
|
||||||
|
|
||||||
// quick hack for method 'head',, is it necessary?
|
|
||||||
if(httpRequest->getMethod() == Request::METHOD_HEAD) {
|
|
||||||
// TODO because we don't want segment file to be saved.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
BtProgressInfoFileHandle infoFile(new DefaultBtProgressInfoFile(_requestGroup->getDownloadContext(), _requestGroup->getPieceStorage(), e->option));
|
BtProgressInfoFileHandle infoFile(new DefaultBtProgressInfoFile(_requestGroup->getDownloadContext(), _requestGroup->getPieceStorage(), e->option));
|
||||||
if(!infoFile->exists() && _requestGroup->downloadFinishedByFileLength()) {
|
if(!infoFile->exists() && _requestGroup->downloadFinishedByFileLength()) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -210,13 +204,20 @@ bool HttpResponseCommand::handleDefaultEncoding(const HttpResponseHandle& httpRe
|
||||||
// we can't continue to use this socket because server sends all entity
|
// we can't continue to use this socket because server sends all entity
|
||||||
// body instead of a segment.
|
// body instead of a segment.
|
||||||
// Therefore, we shutdown the socket here if pipelining is enabled.
|
// Therefore, we shutdown the socket here if pipelining is enabled.
|
||||||
if(!segment.isNull() && segment->getPositionToWrite() == 0 &&
|
if(req->getMethod() == Request::METHOD_GET &&
|
||||||
|
!segment.isNull() && segment->getPositionToWrite() == 0 &&
|
||||||
!req->isPipeliningEnabled()) {
|
!req->isPipeliningEnabled()) {
|
||||||
command = createHttpDownloadCommand(httpResponse);
|
command = createHttpDownloadCommand(httpResponse);
|
||||||
} else {
|
} else {
|
||||||
_requestGroup->getSegmentMan()->cancelSegment(cuid);
|
_requestGroup->getSegmentMan()->cancelSegment(cuid);
|
||||||
}
|
}
|
||||||
prepareForNextAction(command);
|
prepareForNextAction(command);
|
||||||
|
if(req->getMethod() == Request::METHOD_HEAD) {
|
||||||
|
if(req->supportsPersistentConnection()) {
|
||||||
|
e->poolSocket(req, isProxyDefined(), socket);
|
||||||
|
}
|
||||||
|
req->setMethod(Request::METHOD_GET);
|
||||||
|
}
|
||||||
} catch(Exception& e) {
|
} catch(Exception& e) {
|
||||||
delete command;
|
delete command;
|
||||||
throw;
|
throw;
|
||||||
|
@ -261,9 +262,12 @@ static SharedHandle<Decoder> getContentEncodingDecoder
|
||||||
|
|
||||||
bool HttpResponseCommand::handleOtherEncoding(const HttpResponseHandle& httpResponse) {
|
bool HttpResponseCommand::handleOtherEncoding(const HttpResponseHandle& httpResponse) {
|
||||||
HttpRequestHandle httpRequest = httpResponse->getHttpRequest();
|
HttpRequestHandle httpRequest = httpResponse->getHttpRequest();
|
||||||
// quick hack for method 'head',, is it necessary?
|
if(req->getMethod() == Request::METHOD_HEAD) {
|
||||||
if(httpRequest->getMethod() == Request::METHOD_HEAD) {
|
if(req->supportsPersistentConnection()) {
|
||||||
return true;
|
e->poolSocket(req, isProxyDefined(), socket);
|
||||||
|
}
|
||||||
|
req->setMethod(Request::METHOD_GET);
|
||||||
|
return prepareForRetry(0);
|
||||||
}
|
}
|
||||||
_requestGroup->initPieceStorage();
|
_requestGroup->initPieceStorage();
|
||||||
_requestGroup->shouldCancelDownloadForSafety();
|
_requestGroup->shouldCancelDownloadForSafety();
|
||||||
|
@ -286,10 +290,11 @@ bool HttpResponseCommand::skipResponseBody
|
||||||
(cuid, req, _requestGroup, httpConnection, httpResponse, e, socket);
|
(cuid, req, _requestGroup, httpConnection, httpResponse, e, socket);
|
||||||
command->setTransferEncodingDecoder(decoder);
|
command->setTransferEncodingDecoder(decoder);
|
||||||
|
|
||||||
// If the response body is zero-length, set command's status to real time
|
// If request method is HEAD or the response body is zero-length,
|
||||||
// so that avoid read check blocking
|
// set command's status to real time so that avoid read check blocking
|
||||||
if(httpResponse->getEntityLength() == 0 &&
|
if(req->getMethod() == Request::METHOD_HEAD ||
|
||||||
!httpResponse->isTransferEncodingSpecified()) {
|
(httpResponse->getEntityLength() == 0 &&
|
||||||
|
!httpResponse->isTransferEncodingSpecified())) {
|
||||||
command->setStatusRealtime();
|
command->setStatusRealtime();
|
||||||
// If entity length == 0, then socket read/write check must be disabled.
|
// If entity length == 0, then socket read/write check must be disabled.
|
||||||
command->disableSocketCheck();
|
command->disableSocketCheck();
|
||||||
|
|
|
@ -80,12 +80,14 @@ void HttpSkipResponseCommand::setTransferEncodingDecoder
|
||||||
|
|
||||||
bool HttpSkipResponseCommand::executeInternal()
|
bool HttpSkipResponseCommand::executeInternal()
|
||||||
{
|
{
|
||||||
if(_totalLength == 0 && _transferEncodingDecoder.isNull()) {
|
if(req->getMethod() == Request::METHOD_HEAD ||
|
||||||
// If content-length header is present and it's value is 0, then
|
(_totalLength == 0 && _transferEncodingDecoder.isNull())) {
|
||||||
// pool socket for reuse.
|
// If request method is HEAD or content-length header is present and
|
||||||
|
// it's value is 0, then pool socket for reuse.
|
||||||
// If content-length header is not present, then EOF is expected in the end.
|
// If content-length header is not present, then EOF is expected in the end.
|
||||||
// In this case, the content is thrown away and socket cannot be pooled.
|
// In this case, the content is thrown away and socket cannot be pooled.
|
||||||
if(_httpResponse->getHttpHeader()->defined(HttpHeader::CONTENT_LENGTH)) {
|
if(req->getMethod() == Request::METHOD_HEAD ||
|
||||||
|
_httpResponse->getHttpHeader()->defined(HttpHeader::CONTENT_LENGTH)) {
|
||||||
poolConnection();
|
poolConnection();
|
||||||
}
|
}
|
||||||
return processResponse();
|
return processResponse();
|
||||||
|
|
|
@ -179,7 +179,8 @@ void RequestGroup::closeFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestGroup::createInitialCommand(std::deque<Command*>& commands,
|
void RequestGroup::createInitialCommand(std::deque<Command*>& commands,
|
||||||
DownloadEngine* e)
|
DownloadEngine* e,
|
||||||
|
const std::string& method)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_BITTORRENT
|
#ifdef ENABLE_BITTORRENT
|
||||||
{
|
{
|
||||||
|
@ -306,7 +307,7 @@ void RequestGroup::createInitialCommand(std::deque<Command*>& commands,
|
||||||
// TODO I assume here when totallength is set to DownloadContext and it is
|
// TODO I assume here when totallength is set to DownloadContext and it is
|
||||||
// not 0, then filepath is also set DownloadContext correctly....
|
// not 0, then filepath is also set DownloadContext correctly....
|
||||||
if(_downloadContext->getTotalLength() == 0) {
|
if(_downloadContext->getTotalLength() == 0) {
|
||||||
createNextCommand(commands, e, 1);
|
createNextCommand(commands, e, 1, method);
|
||||||
}else {
|
}else {
|
||||||
if(e->_requestGroupMan->isSameFileBeingDownloaded(this)) {
|
if(e->_requestGroupMan->isSameFileBeingDownloaded(this)) {
|
||||||
throw DownloadFailureException
|
throw DownloadFailureException
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "SharedHandle.h"
|
#include "SharedHandle.h"
|
||||||
#include "TransferStat.h"
|
#include "TransferStat.h"
|
||||||
#include "TimeA2.h"
|
#include "TimeA2.h"
|
||||||
|
#include "Request.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -164,15 +165,23 @@ public:
|
||||||
|
|
||||||
SharedHandle<SegmentMan> getSegmentMan() const;
|
SharedHandle<SegmentMan> getSegmentMan() const;
|
||||||
|
|
||||||
|
// Returns first bootstrap commands to initiate a download.
|
||||||
|
// If this is HTTP/FTP download and file size is unknown, only 1 command
|
||||||
|
// (usually, HttpInitiateConnection or FtpInitiateConnection) will be created
|
||||||
|
// with its Request object having Requet::METHOD_HEAD in its method.
|
||||||
|
// This behavior can be changed by providing 3rd argument.
|
||||||
|
// The method has effect only for using HTTP request including FTP via HTTP
|
||||||
|
// proxy.
|
||||||
void createInitialCommand(std::deque<Command*>& commands,
|
void createInitialCommand(std::deque<Command*>& commands,
|
||||||
DownloadEngine* e);
|
DownloadEngine* e,
|
||||||
|
const std::string& method = Request::METHOD_HEAD);
|
||||||
|
|
||||||
void createNextCommandWithAdj(std::deque<Command*>& commands,
|
void createNextCommandWithAdj(std::deque<Command*>& commands,
|
||||||
DownloadEngine* e, int numAdj);
|
DownloadEngine* e, int numAdj);
|
||||||
|
|
||||||
void createNextCommand(std::deque<Command*>& commands,
|
void createNextCommand(std::deque<Command*>& commands,
|
||||||
DownloadEngine* e, unsigned int numCommand,
|
DownloadEngine* e, unsigned int numCommand,
|
||||||
const std::string& method = "GET");
|
const std::string& method = Request::METHOD_GET);
|
||||||
|
|
||||||
void addURI(const std::string& uri)
|
void addURI(const std::string& uri)
|
||||||
{
|
{
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "A2STR.h"
|
#include "A2STR.h"
|
||||||
#include "SocketCore.h"
|
#include "SocketCore.h"
|
||||||
|
#include "Request.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -92,7 +93,8 @@ bool TrackerWatcherCommand::execute() {
|
||||||
_trackerRequestGroup = createAnnounce();
|
_trackerRequestGroup = createAnnounce();
|
||||||
if(!_trackerRequestGroup.isNull()) {
|
if(!_trackerRequestGroup.isNull()) {
|
||||||
std::deque<Command*> commands;
|
std::deque<Command*> commands;
|
||||||
_trackerRequestGroup->createInitialCommand(commands, e);
|
_trackerRequestGroup->createInitialCommand(commands, e,
|
||||||
|
Request::METHOD_GET);
|
||||||
e->addCommand(commands);
|
e->addCommand(commands);
|
||||||
logger->debug("added tracker request command");
|
logger->debug("added tracker request command");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "HttpRequest.h"
|
#include "HttpRequest.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <cppunit/extensions/HelperMacros.h>
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
|
||||||
#include "prefs.h"
|
#include "prefs.h"
|
||||||
|
@ -11,6 +13,7 @@
|
||||||
#include "Option.h"
|
#include "Option.h"
|
||||||
#include "array_fun.h"
|
#include "array_fun.h"
|
||||||
#include "CookieStorage.h"
|
#include "CookieStorage.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ class HttpRequestTest : public CppUnit::TestFixture {
|
||||||
CPPUNIT_TEST(testCreateRequest_ftp);
|
CPPUNIT_TEST(testCreateRequest_ftp);
|
||||||
CPPUNIT_TEST(testCreateRequest_with_cookie);
|
CPPUNIT_TEST(testCreateRequest_with_cookie);
|
||||||
CPPUNIT_TEST(testCreateRequest_query);
|
CPPUNIT_TEST(testCreateRequest_query);
|
||||||
|
CPPUNIT_TEST(testCreateRequest_head);
|
||||||
CPPUNIT_TEST(testCreateProxyRequest);
|
CPPUNIT_TEST(testCreateProxyRequest);
|
||||||
CPPUNIT_TEST(testIsRangeSatisfied);
|
CPPUNIT_TEST(testIsRangeSatisfied);
|
||||||
CPPUNIT_TEST(testUserAgent);
|
CPPUNIT_TEST(testUserAgent);
|
||||||
|
@ -46,6 +50,7 @@ public:
|
||||||
void testCreateRequest_ftp();
|
void testCreateRequest_ftp();
|
||||||
void testCreateRequest_with_cookie();
|
void testCreateRequest_with_cookie();
|
||||||
void testCreateRequest_query();
|
void testCreateRequest_query();
|
||||||
|
void testCreateRequest_head();
|
||||||
void testCreateProxyRequest();
|
void testCreateProxyRequest();
|
||||||
void testIsRangeSatisfied();
|
void testIsRangeSatisfied();
|
||||||
void testUserAgent();
|
void testUserAgent();
|
||||||
|
@ -450,6 +455,23 @@ void HttpRequestTest::testCreateRequest_query()
|
||||||
CPPUNIT_ASSERT_EQUAL(expectedText, httpRequest.createRequest());
|
CPPUNIT_ASSERT_EQUAL(expectedText, httpRequest.createRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpRequestTest::testCreateRequest_head()
|
||||||
|
{
|
||||||
|
SharedHandle<Request> request(new Request());
|
||||||
|
request->setMethod(Request::METHOD_HEAD);
|
||||||
|
request->setUrl("http://localhost/aria2-1.0.0.tar.bz2");
|
||||||
|
|
||||||
|
HttpRequest httpRequest;
|
||||||
|
httpRequest.setRequest(request);
|
||||||
|
httpRequest.setAuthConfigFactory(_authConfigFactory);
|
||||||
|
|
||||||
|
std::stringstream result(httpRequest.createRequest());
|
||||||
|
std::string line;
|
||||||
|
CPPUNIT_ASSERT(getline(result, line));
|
||||||
|
Util::trimSelf(line);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("HEAD /aria2-1.0.0.tar.bz2 HTTP/1.1"), line);
|
||||||
|
}
|
||||||
|
|
||||||
void HttpRequestTest::testCreateProxyRequest()
|
void HttpRequestTest::testCreateProxyRequest()
|
||||||
{
|
{
|
||||||
SharedHandle<Request> request(new Request());
|
SharedHandle<Request> request(new Request());
|
||||||
|
|
Loading…
Reference in New Issue