/* */ #include "HttpResponseCommand.h" #include "DlAbortEx.h" #include "DlRetryEx.h" #include "HttpDownloadCommand.h" #include "message.h" #include "Util.h" #include "prefs.h" #include "File.h" #include "InitiateConnectionCommandFactory.h" #include "FatalException.h" #include #include HttpResponseCommand::HttpResponseCommand(int32_t cuid, const RequestHandle& req, RequestGroup* requestGroup, const HttpConnectionHandle& httpConnection, DownloadEngine* e, const SocketHandle& s) :AbstractCommand(cuid, req, requestGroup, e, s), httpConnection(httpConnection) {} HttpResponseCommand::~HttpResponseCommand() {} bool HttpResponseCommand::executeInternal() { HttpRequestHandle httpRequest = httpConnection->getFirstHttpRequest(); if(!(httpRequest->getSegment() == segment)) { logger->info(MSG_SEGMENT_CHANGED, cuid); return prepareForRetry(0); } HttpResponseHandle httpResponse = httpConnection->receiveResponse(); if(httpResponse.isNull()) { // The server has not responded to our request yet. e->commands.push_back(this); return false; } // check HTTP status number httpResponse->validateResponse(); httpResponse->retrieveCookie(); // check whether the server supports persistent connections. /* if(Util::toLower(headers.getFirst("Connection")).find("close") != string::npos) { req->setKeepAlive(false); } */ // check whether Location header exists. If it does, update request object // with redirected URL. // then establish a connection to the new host and port if(httpResponse->isRedirect()) { httpResponse->processRedirect(); logger->info(MSG_REDIRECT, cuid, httpResponse->getRedirectURI().c_str()); e->noWait = true; return prepareForRetry(0); } if(_requestGroup->getSegmentMan()->downloadStarted) { // TODO validate totalsize _requestGroup->validateFilename(httpResponse->determinFilename()); _requestGroup->validateTotalLength(httpResponse->getEntityLength()); e->commands.push_back(createHttpDownloadCommand(httpResponse)); return true; } else { // TODO validate totalsize against hintTotalSize if it is provided. _requestGroup->validateFilenameByHint(httpResponse->determinFilename()); _requestGroup->validateTotalLengthByHint(httpResponse->getEntityLength()); _requestGroup->getSegmentMan()->filename = httpResponse->determinFilename(); if(e->_requestGroupMan->isSameFileBeingDownloaded(_requestGroup)) { throw new FatalException(EX_DUPLICATE_FILE_DOWNLOAD, _requestGroup->getFilePath().c_str()); } if(httpResponse->isTransferEncodingSpecified()) { return handleOtherEncoding(httpResponse); } else { return handleDefaultEncoding(httpResponse); } } } bool HttpResponseCommand::handleDefaultEncoding(const HttpResponseHandle& httpResponse) { HttpRequestHandle httpRequest = httpResponse->getHttpRequest(); // TODO quick and dirty way if(_requestGroup->isTorrent) { return doTorrentStuff(httpResponse); } int64_t size = httpResponse->getEntityLength(); if(size == INT64_MAX || size < 0) { throw new DlAbortEx(EX_TOO_LARGE_FILE, size); } _requestGroup->getSegmentMan()->isSplittable = !(size == 0); _requestGroup->getSegmentMan()->downloadStarted = true; _requestGroup->getSegmentMan()->totalSize = size; // quick hack for method 'head' if(httpRequest->getMethod() == Request::METHOD_HEAD) { // TODO because we don't want segment file to be saved. _requestGroup->getSegmentMan()->isSplittable = false; return true; } if(e->option->get(PREF_CHECK_INTEGRITY) != V_TRUE) { if(_requestGroup->downloadFinishedByFileLength()) { logger->notice(MSG_DOWNLOAD_ALREADY_COMPLETED, cuid, _requestGroup->getFilePath().c_str()); return true; } } DownloadCommand* command = 0; File file(_requestGroup->getFilePath()); if(_requestGroup->getRemainingUris().empty() && !file.exists()) { command = createHttpDownloadCommand(httpResponse); } _requestGroup->loadAndOpenFile(); _requestGroup->prepareForNextAction(cuid, req, e, command); e->noWait = true; return true; } bool HttpResponseCommand::handleOtherEncoding(const HttpResponseHandle& httpResponse) { HttpRequestHandle httpRequest = httpResponse->getHttpRequest(); // we ignore content-length when transfer-encoding is set _requestGroup->getSegmentMan()->downloadStarted = true; _requestGroup->getSegmentMan()->isSplittable = false; _requestGroup->getSegmentMan()->totalSize = 0; // quick hack for method 'head' if(httpRequest->getMethod() == Request::METHOD_HEAD) { return true; } // disable keep-alive req->setKeepAlive(false); segment = _requestGroup->getSegmentMan()->getSegment(cuid); _requestGroup->getSegmentMan()->diskWriter->initAndOpenFile(_requestGroup->getSegmentMan()->getFilePath()); e->commands.push_back(createHttpDownloadCommand(httpResponse)); return true; } HttpDownloadCommand* HttpResponseCommand::createHttpDownloadCommand(const HttpResponseHandle& httpResponse) { TransferEncodingHandle enc = 0; if(httpResponse->isTransferEncodingSpecified()) { enc = httpResponse->getTransferDecoder(); if(enc.isNull()) { throw new DlAbortEx(EX_TRANSFER_ENCODING_NOT_SUPPORTED, httpResponse->getTransferEncoding().c_str()); } enc->init(); } HttpDownloadCommand* command = new HttpDownloadCommand(cuid, req, _requestGroup, e, socket); command->setMaxDownloadSpeedLimit(e->option->getAsInt(PREF_MAX_DOWNLOAD_LIMIT)); command->setStartupIdleTime(e->option->getAsInt(PREF_STARTUP_IDLE_TIME)); command->setLowestDownloadSpeedLimit(e->option->getAsInt(PREF_LOWEST_SPEED_LIMIT)); command->setTransferDecoder(enc); return command; } bool HttpResponseCommand::doTorrentStuff(const HttpResponseHandle& httpResponse) { int64_t size = httpResponse->getEntityLength(); _requestGroup->getSegmentMan()->totalSize = size; if(size > 0) { _requestGroup->getSegmentMan()->initBitfield(e->option->getAsInt(PREF_SEGMENT_SIZE), _requestGroup->getSegmentMan()->totalSize); } // disable keep-alive httpResponse->getHttpRequest()->getRequest()->setKeepAlive(false); _requestGroup->getSegmentMan()->isSplittable = false; _requestGroup->getSegmentMan()->downloadStarted = true; _requestGroup->getSegmentMan()->diskWriter->initAndOpenFile("/tmp/aria2"+Util::itos((int32_t)getpid())); e->commands.push_back(createHttpDownloadCommand(httpResponse)); return true; }