2006-02-17 13:35:04 +00:00
|
|
|
/* <!-- copyright */
|
|
|
|
/*
|
2006-09-21 15:31:24 +00:00
|
|
|
* aria2 - The high speed download utility
|
2006-02-17 13:35:04 +00:00
|
|
|
*
|
|
|
|
* 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
|
2006-09-21 15:31:24 +00:00
|
|
|
* 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.
|
2006-02-17 13:35:04 +00:00
|
|
|
*/
|
|
|
|
/* copyright --> */
|
|
|
|
#include "HttpResponseCommand.h"
|
|
|
|
#include "DlAbortEx.h"
|
2006-02-21 14:00:58 +00:00
|
|
|
#include "DlRetryEx.h"
|
2006-02-17 13:35:04 +00:00
|
|
|
#include "HttpDownloadCommand.h"
|
|
|
|
#include "HttpInitiateConnectionCommand.h"
|
|
|
|
#include "message.h"
|
|
|
|
#include "Util.h"
|
2006-09-19 14:52:59 +00:00
|
|
|
#include "prefs.h"
|
2006-05-24 16:08:00 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
2006-02-17 13:35:04 +00:00
|
|
|
|
2006-07-19 17:07:45 +00:00
|
|
|
HttpResponseCommand::HttpResponseCommand(int cuid, Request* req,
|
|
|
|
DownloadEngine* e,
|
|
|
|
const SocketHandle& s)
|
|
|
|
:AbstractCommand(cuid, req, e, s) {
|
2006-04-17 16:17:20 +00:00
|
|
|
http = new HttpConnection(cuid, socket, req, e->option);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
|
2006-02-21 12:27:17 +00:00
|
|
|
HttpResponseCommand::~HttpResponseCommand() {
|
|
|
|
delete http;
|
|
|
|
}
|
2006-02-17 13:35:04 +00:00
|
|
|
|
2006-09-19 14:52:59 +00:00
|
|
|
bool HttpResponseCommand::executeInternal(Segment& segment) {
|
|
|
|
if(req->segment != segment) {
|
2006-04-17 16:17:20 +00:00
|
|
|
logger->info(MSG_SEGMENT_CHANGED, cuid);
|
2006-02-18 05:13:21 +00:00
|
|
|
return prepareForRetry(0);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
HttpHeader headers;
|
2006-02-21 12:27:17 +00:00
|
|
|
int status = http->receiveResponse(headers);
|
|
|
|
if(status == 0) {
|
|
|
|
// didn't receive header fully
|
2006-05-09 15:54:14 +00:00
|
|
|
e->commands.push_back(this);
|
2006-02-21 12:27:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
2006-02-17 13:35:04 +00:00
|
|
|
// check HTTP status number
|
2006-09-19 14:52:59 +00:00
|
|
|
checkResponse(status, segment);
|
2006-02-17 13:35:04 +00:00
|
|
|
retrieveCookie(headers);
|
2006-09-19 14:52:59 +00:00
|
|
|
// check whether the server supports persistent connections.
|
|
|
|
if(Util::toLower(headers.getFirst("Connection")).find("close") != string::npos) {
|
|
|
|
req->setKeepAlive(false);
|
|
|
|
}
|
2006-02-17 13:35:04 +00:00
|
|
|
// check whether Location header exists. If it does, update request object
|
|
|
|
// with redirected URL.
|
|
|
|
// then establish a connection to the new host and port
|
2006-02-21 12:27:17 +00:00
|
|
|
if(headers.defined("Location")) {
|
|
|
|
return handleRedirect(headers.getFirst("Location"), headers);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
if(!e->segmentMan->downloadStarted) {
|
|
|
|
string transferEncoding;
|
2006-02-21 12:27:17 +00:00
|
|
|
if(headers.defined("Transfer-Encoding")) {
|
2006-09-19 14:52:59 +00:00
|
|
|
return handleOtherEncoding(headers.getFirst("Transfer-Encoding"),
|
|
|
|
headers);
|
2006-02-17 13:35:04 +00:00
|
|
|
} else {
|
|
|
|
return handleDefaultEncoding(headers);
|
|
|
|
}
|
|
|
|
} else {
|
2006-05-09 15:54:14 +00:00
|
|
|
if(determinFilename(headers) != e->segmentMan->filename) {
|
2006-02-17 13:35:04 +00:00
|
|
|
throw new DlAbortEx(EX_FILENAME_MISMATCH, req->getFile().c_str(), e->segmentMan->filename.c_str());
|
|
|
|
}
|
|
|
|
createHttpDownloadCommand();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HttpResponseCommand::checkResponse(int status, const Segment& segment) {
|
2006-04-19 17:23:58 +00:00
|
|
|
if(status == 401) {
|
|
|
|
throw new DlAbortEx(EX_AUTH_FAILED);
|
|
|
|
}
|
|
|
|
if(!(300 <= status && status < 400 ||
|
2006-09-19 14:52:59 +00:00
|
|
|
(segment.getPosition()+segment.writtenLength == 0 && (status == 200 || status == 206)) ||
|
|
|
|
(segment.getPosition()+segment.writtenLength > 0 && status == 206))) {
|
2006-04-19 17:23:58 +00:00
|
|
|
throw new DlRetryEx(EX_BAD_STATUS, status);
|
|
|
|
}
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
|
2006-04-19 17:49:03 +00:00
|
|
|
bool HttpResponseCommand::handleRedirect(const string& url, const HttpHeader& headers) {
|
2006-02-17 13:35:04 +00:00
|
|
|
req->redirectUrl(url);
|
2006-04-17 16:17:20 +00:00
|
|
|
logger->info(MSG_REDIRECT, cuid, url.c_str());
|
2006-02-17 13:35:04 +00:00
|
|
|
e->noWait = true;
|
2006-02-18 05:13:21 +00:00
|
|
|
return prepareForRetry(0);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
|
2006-05-09 15:54:14 +00:00
|
|
|
string HttpResponseCommand::determinFilename(const HttpHeader& headers) {
|
|
|
|
string contentDisposition =
|
|
|
|
Util::getContentDispositionFilename(headers.getFirst("Content-Disposition"));
|
|
|
|
if(contentDisposition.empty()) {
|
2006-08-28 12:40:41 +00:00
|
|
|
return Util::urldecode(req->getFile());
|
2006-05-09 15:54:14 +00:00
|
|
|
} else {
|
|
|
|
logger->info("CUID#%d - Content-Disposition Detected. Use %s as filename",
|
|
|
|
cuid, contentDisposition.c_str());
|
2006-08-28 12:40:41 +00:00
|
|
|
return Util::urldecode(contentDisposition);
|
2006-05-09 15:54:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-17 13:35:04 +00:00
|
|
|
bool HttpResponseCommand::handleDefaultEncoding(const HttpHeader& headers) {
|
2006-03-21 14:12:51 +00:00
|
|
|
// TODO quick and dirty way
|
|
|
|
if(req->isTorrent) {
|
|
|
|
long long int size = headers.getFirstAsLLInt("Content-Length");
|
|
|
|
e->segmentMan->totalSize = size;
|
* src/PeerChokeCommand.cc
(optUnchokingPeer): Updated according to the changes in Peer.
(ResetDelta): Removed.
(UploadFaster): Updated according to the changes in Peer.
(DownloadFaster): Updated according to the changes in Peer.
(execute): I clarify the meaning of "upload" and "download"
here.
"upload" means the transfer from localhost to remote host.
"download" means the transfer from remote host to localhost.
Based on this rule, I swapped orderByUploadRate and
orderByDownloadRate.
* src/PeerInteractionCommand.cc
(PeerInteraction): Removed peerInteraction->setUploadLImit().
(executeInternal): Removed the argument of
peerInteraction->sendMessages().
(receiveMessages): Rewritten download speed limit.
(sendKeepAlive): Removed peerInteraction->sendMessages().
* src/HttpResponseCommand.cc
(handleDefaultEncoding): If file size is unknown in torrent
request,
do not call segmentMan->initBitfield() here.
Disabled persistent connection feature in torrent request.
* src/UrlRequestInfo.h
(UrlRequestInfo): Removed const qualifier from option.
* src/TorrentMan.h
(TransferStat): New class.
(deltaDownloadLength): Removed.
(deltaUploadLength): Removed.
(addDeltaDownloadLength): Removed.
(getDeltaDownloadLength): Removed.
(resetDeltaDownloadLength): Removed.
(addDeltaUploadLength): Removed.
(getDeltaUploadLength): Removed.
(resetDeltaUploadLength): Removed.
(addActivePeer): Added peer->activate().
(deleteActivePeer): Added peer->deactivate().
(calculateStat): New function.
* src/TorrentMan.cc
(TorrentMan): Removed deltaDownloadLength and deltaUploadLength.
(calculateStat): New function.
* src/PeerInteraction.h
(uploadLimit): Removed.
(option): New variable.
(setUploadLimit): Removed.
(getUploadSpeed): Removed.
(sendMessages): Removed the argument "currentUploadSpeed".
* src/PeerInteraction.cc
(prefs.h): Included.
(PeerInteraction): Removed uploadLimit. Added option.
(sendMessages): Rewritten upload speed limit.
(sendHandshake): Removed the argument from sendMessages().
(sendBitfield): Removed the argument from sendMessages().
* src/PeerAbstractCommand.cc
(execute): Commented out the portion of upload limit.
(onAbort): Removed peer->resetStatus().
* src/TorrentRequestInfo.cc
(timeoutSpecified): Declared extern.
(execute): Set timeout to 180 if timeout is not specified by.
command-line.
* src/PieceMessage.cc
(receivedAction): Added peer->updateDownloadLength().
Removed peer->addPeerUpload().
Removed torrentMan->addDeltaDownloadLength().
(send): Added peer->updateUploadLength().
Removed peer->addPeerDownload().
Removed torrentMan->addDeltaUploadLength().
* src/main.cc
(timeoutSpecified): New variable.
(main): Set timeoutSpecified to false.
If the command-line option "--upload-limit" is specified, then
timeoutSpecified is set to true. This option will remain in the
next
release, but be deprecated in the future release.
* src/TorrentRequestInfo.h
(TorrentRequestInfo): Removed const qualifier from op.
* src/PeerStat.h
(uploadSpeed): New variable.
(PeerStat): Added default value to cuid.
(calculateUploadSpeed): New function.
(updateUploadLength): New function.
(getMaxUploadSpeed): New function.
(getAvgUploadSpeed): New function.
(reset): Added uploadSpeed. Set status to IDLE.
* src/TorrentDownloadEngine.h
(cp): Declared as Time.
(sessionDownloadLengthArray): Removed.
(sessionUploadLengthArray): Removed.
(currentCp): Removed.
(lastCalcStat): New variable
(lastElapsed): Removed.
(sessionDownloadLength): Removed.
(calculateStat): New function.
* src/TorrentDownloadEngine.cc
(initStatistics): Removed lastElapsed, cp[],
sessionDownloadLengthArray[], sessionUploadLengthArray[],
currentCp, sessionDownloadLength.
Added cp.reset() and lastCalcStat.reset().
(calculateSpeed): Changed the name of the argument.
(calculateStatistics): Rewritten.
(calculateStat): New function.
* src/Peer.h
(PeerStat.h): Included.
(peerUpload): Removed.
(peerDownload): Removed.
(peerStat): New variable.
(sessionUploadLength): New variable.
(sessionDownloadLength): New variable.
(deltaUpload): Removed.
(deltaDownload): Removed.
(resetStatus): Made private.
(Peer): Added sessionUploadLength, sessionDownloadLength.
Removed peerUpload, peerDownload.
(updateUploadLength): New function.
(addDeltaUpload): Removed.
(updateDownloadLength): New function.
(resetDeltaUpload): Removed.
(getDeltaUpload): Removed.
(addDeltaDownload): Removed.
(calculateUploadSpeed): New function.
(resetDeltaDownload): Removed.
(getDeltaDownload): Removed.
(calculateDownloadSpeed): New function.
(getSessionUploadLength): New function.
(getSessionDownloadLength): New function.
(activate): New function.
(deactivate): New function.
(addPeerUpload): Removed.
(setPeerUpload): Removed.
(getPeerUpload): Removed.
(addPeerDownload): Removed.
(setPeerDownload): Removed.
(getPeerDownload): Removed.
* src/Peer.cc
(resetStatus): Removed resetDeltaUpload() and
resetDeltaDownload().
* src/MetalinkRequestInfo.h
(MetalinkRequestInfo): Removed const qualifier from op.
* src/RequestInfo.h
(op): Removed const qualifier.
(RequestInfo): Removed const qualifier from op.
2006-09-21 13:49:06 +00:00
|
|
|
if(size > 0) {
|
|
|
|
e->segmentMan->initBitfield(e->option->getAsInt(PREF_SEGMENT_SIZE),
|
|
|
|
e->segmentMan->totalSize);
|
|
|
|
}
|
|
|
|
// disable keep-alive
|
|
|
|
req->setKeepAlive(false);
|
2006-03-21 14:12:51 +00:00
|
|
|
e->segmentMan->isSplittable = false;
|
2006-04-18 17:06:17 +00:00
|
|
|
e->segmentMan->downloadStarted = true;
|
2006-04-19 17:23:58 +00:00
|
|
|
e->segmentMan->diskWriter->initAndOpenFile("/tmp/aria2"+Util::itos(getpid()));
|
2006-03-21 14:12:51 +00:00
|
|
|
createHttpDownloadCommand();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-02-21 12:27:17 +00:00
|
|
|
long long int size = headers.getFirstAsLLInt("Content-Length");
|
2006-02-21 14:00:58 +00:00
|
|
|
if(size == LONG_LONG_MAX || size < 0) {
|
2006-02-21 12:27:17 +00:00
|
|
|
throw new DlAbortEx(EX_TOO_LARGE_FILE, size);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
e->segmentMan->isSplittable = !(size == 0);
|
2006-05-09 15:54:14 +00:00
|
|
|
e->segmentMan->filename = determinFilename(headers);
|
2006-02-17 13:35:04 +00:00
|
|
|
bool segFileExists = e->segmentMan->segmentFileExists();
|
|
|
|
e->segmentMan->downloadStarted = true;
|
|
|
|
if(segFileExists) {
|
2006-02-22 12:16:10 +00:00
|
|
|
e->segmentMan->load();
|
2006-04-19 17:23:58 +00:00
|
|
|
e->segmentMan->diskWriter->openExistingFile(e->segmentMan->getFilePath());
|
2006-02-17 13:35:04 +00:00
|
|
|
// send request again to the server with Range header
|
2006-02-18 05:13:21 +00:00
|
|
|
return prepareForRetry(0);
|
2006-02-17 13:35:04 +00:00
|
|
|
} else {
|
2006-02-21 12:27:17 +00:00
|
|
|
e->segmentMan->totalSize = size;
|
2006-09-19 14:52:59 +00:00
|
|
|
e->segmentMan->initBitfield(e->option->getAsInt(PREF_SEGMENT_SIZE),
|
|
|
|
e->segmentMan->totalSize);
|
2006-04-19 17:23:58 +00:00
|
|
|
e->segmentMan->diskWriter->initAndOpenFile(e->segmentMan->getFilePath());
|
2006-09-19 14:52:59 +00:00
|
|
|
return prepareForRetry(0);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-19 17:49:03 +00:00
|
|
|
bool HttpResponseCommand::handleOtherEncoding(const string& transferEncoding, const HttpHeader& headers) {
|
2006-02-17 13:35:04 +00:00
|
|
|
// we ignore content-length when transfer-encoding is set
|
|
|
|
e->segmentMan->downloadStarted = true;
|
|
|
|
e->segmentMan->isSplittable = false;
|
2006-05-09 15:54:14 +00:00
|
|
|
e->segmentMan->filename = determinFilename(headers);
|
2006-02-17 13:35:04 +00:00
|
|
|
e->segmentMan->totalSize = 0;
|
2006-09-19 14:52:59 +00:00
|
|
|
// disable keep-alive
|
|
|
|
req->setKeepAlive(false);
|
|
|
|
Segment segment;
|
|
|
|
e->segmentMan->getSegment(segment, cuid);
|
2006-04-19 17:23:58 +00:00
|
|
|
e->segmentMan->diskWriter->initAndOpenFile(e->segmentMan->getFilePath());
|
2006-02-17 13:35:04 +00:00
|
|
|
createHttpDownloadCommand(transferEncoding);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-04-19 17:49:03 +00:00
|
|
|
void HttpResponseCommand::createHttpDownloadCommand(const string& transferEncoding) {
|
2006-04-18 17:06:17 +00:00
|
|
|
HttpDownloadCommand* command = new HttpDownloadCommand(cuid, req, e, socket);
|
|
|
|
TransferEncoding* enc = NULL;
|
|
|
|
if(transferEncoding.size() && (enc = command->getTransferEncoding(transferEncoding)) == NULL) {
|
|
|
|
delete(command);
|
|
|
|
throw new DlAbortEx(EX_TRANSFER_ENCODING_NOT_SUPPORTED, transferEncoding.c_str());
|
2006-03-21 14:12:51 +00:00
|
|
|
} else {
|
2006-04-18 17:06:17 +00:00
|
|
|
if(enc != NULL) {
|
|
|
|
command->transferEncoding = transferEncoding;
|
|
|
|
enc->init();
|
|
|
|
}
|
2006-05-09 15:54:14 +00:00
|
|
|
e->commands.push_back(command);
|
2006-02-17 13:35:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HttpResponseCommand::retrieveCookie(const HttpHeader& headers) {
|
2006-03-22 16:21:11 +00:00
|
|
|
Strings v = headers.get("Set-Cookie");
|
|
|
|
for(Strings::const_iterator itr = v.begin(); itr != v.end(); itr++) {
|
2006-02-17 13:35:04 +00:00
|
|
|
Cookie c;
|
2006-02-21 12:27:17 +00:00
|
|
|
req->cookieBox->parse(c, *itr);
|
2006-02-17 13:35:04 +00:00
|
|
|
req->cookieBox->add(c);
|
|
|
|
}
|
|
|
|
}
|
2006-02-18 05:13:21 +00:00
|
|
|
|