| /* ------------------------------------------------------------------ |
| * Copyright (C) 1998-2009 PacketVideo |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| * express or implied. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * ------------------------------------------------------------------- |
| */ |
| #include "pvmf_protocol_engine_progressive_download.h" |
| |
| #define LOGINFODATAPATH(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iLogger,PVLOGMSG_INFO,m); |
| #define PVMF_PROTOCOL_ENGINE_LOGINFODATAPATH(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iDataPathLogger,PVLOGMSG_INFO,m); |
| #define PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iDataPathErrLogger,PVLOGMSG_INFO,m); |
| |
| |
| ////// ProgressiveDownloadState_HEAD implementation //////////////////////////// |
| void ProgressiveDownloadState_HEAD::setRequestBasics() |
| { |
| iComposer->setMethod(HTTP_METHOD_HEAD); |
| //iComposer->setVersion(HTTP_V1_1); |
| iComposer->setVersion((HTTPVersion)iCfgFile->getHttpVersion()); |
| StrPtrLen uri((iURI.getURI()).get_cstr(), (iURI.getURI()).get_size()); |
| iComposer->setURI(uri); |
| } |
| |
| bool ProgressiveDownloadState_HEAD::setHeaderFields() |
| { |
| if (!DownloadState::setHeaderFields()) return false; |
| if (!ProtocolState::constructAuthenHeader(iCfgFile->GetUserId(), iCfgFile->GetUserAuth())) return false; |
| |
| return setExtensionFields(iCfgFile->getExtensionHeaderKeys(), |
| iCfgFile->getExtensionHeaderValues(), |
| iCfgFile->getHTTPMethodMasksForExtensionHeader(), |
| iCfgFile->getExtensionHeadersPurgeOnRedirect(), |
| HTTP_METHOD_HEAD); |
| } |
| |
| int32 ProgressiveDownloadState_HEAD::checkParsingStatus(int32 parsingStatus) |
| { |
| if (parsingStatus == HttpParsingBasicObject::PARSE_SUCCESS_END_OF_INPUT && iParser->isHttpHeaderParsed()) |
| return PROCESS_SUCCESS_END_OF_MESSAGE; |
| |
| return ProtocolState::checkParsingStatus(parsingStatus); |
| } |
| |
| int32 ProgressiveDownloadState_HEAD::OutputDataAvailable(OUTPUT_DATA_QUEUE *aOutputQueue, const bool isHttpHeader) |
| { |
| if (isHttpHeader) |
| { |
| iDataSideInfo.set(ProtocolEngineOutputDataType_HttpHeader); |
| iObserver->OutputDataAvailable(*aOutputQueue, iDataSideInfo); |
| } |
| return HttpParsingBasicObject::PARSE_SUCCESS; |
| } |
| |
| ////// ProgressiveDownloadState_GET implementation //////////////////////////// |
| int32 ProgressiveDownloadState_GET::processMicroStateGetResponsePreCheck() |
| { |
| int32 status = DownloadState::processMicroStateGetResponsePreCheck(); |
| if (status != PROCESS_SUCCESS) return status; |
| |
| // set the existing download size if this is resume download |
| iParser->setDownloadSize(iCfgFile->GetCurrentFileSize()); |
| |
| return PROCESS_SUCCESS; |
| } |
| |
| bool ProgressiveDownloadState_GET::setHeaderFields() |
| { |
| // only send Range header for previous non-zero bytes position. |
| // Some server may not like this, Range: bytes=0- |
| if (iCfgFile->GetCurrentFileSize() > 0 && iCfgFile->GetOverallFileSize() > 0) |
| { |
| StrCSumPtrLen rangeKey = "Range"; |
| char buffer[64]; |
| oscl_snprintf(buffer, 64, "bytes=%d-%d", iCfgFile->GetCurrentFileSize(), iCfgFile->GetOverallFileSize()); |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::setHeaderFields(), Range: bytes=%d-", iCfgFile->GetCurrentFileSize())); |
| if (!iComposer->setField(rangeKey, buffer)) return false; |
| } |
| |
| // set authentication header and common headers |
| if (!ProtocolState::constructAuthenHeader(iCfgFile->GetUserId(), iCfgFile->GetUserAuth())) return false; |
| if (!DownloadState::setHeaderFields()) return false; |
| |
| // change "Connection" field |
| StrCSumPtrLen connectionKey = "Connection"; |
| char *nullPtr = NULL; // remove "Connection" field |
| if (!iComposer->setField(connectionKey, nullPtr)) return false; |
| // reset "Connection: Close" |
| StrPtrLen connectionValue = "Close"; |
| if (!iComposer->setField(connectionKey, &connectionValue)) return false; |
| |
| |
| return setExtensionFields(iCfgFile->getExtensionHeaderKeys(), |
| iCfgFile->getExtensionHeaderValues(), |
| iCfgFile->getHTTPMethodMasksForExtensionHeader(), |
| iCfgFile->getExtensionHeadersPurgeOnRedirect()); |
| } |
| |
| int32 ProgressiveDownloadState_GET::updateDownloadStatistics() |
| { |
| int32 status = DownloadState::updateDownloadStatistics(); |
| if (status == PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED) |
| { |
| iSendEndOfMessageTruncate = true; |
| } |
| return status; |
| } |
| |
| |
| int32 ProgressiveDownloadState_GET::checkParsingStatus(int32 parsingStatus) |
| { |
| // EOS means connection is down, and can be treated as download complete |
| if (parsingStatus == HttpParsingBasicObject::PARSE_EOS_INPUT_DATA) |
| { |
| if (iParser->getDownloadSize() > 0 && iParser->isDownloadReallyHappen()) |
| { |
| iCfgFile->SetCurrentFileSize(iParser->getDownloadSize()); |
| if (iParser->getContentLength() == 0) iCfgFile->SetOverallFileSize(iParser->getDownloadSize()); |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::checkParsingStatus(), GOT EOS and COMPLETE DOWNLOAD, downloadSize=%d, contentLength=%d, isDownloadHappen=%d", |
| iParser->getDownloadSize(), iParser->getContentLength(), (int32)iParser->isDownloadReallyHappen())); |
| return PROCESS_SUCCESS_END_OF_MESSAGE_BY_SERVER_DISCONNECT; |
| } |
| } |
| |
| // download complete with truncation |
| if (iSendEndOfMessageTruncate) |
| { |
| iSendEndOfMessageTruncate = false; |
| return PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED; |
| } |
| |
| return DownloadState::checkParsingStatus(parsingStatus); |
| } |
| |
| |
| // From HttpParsingBasicObjectObserver |
| int32 ProgressiveDownloadState_GET::OutputDataAvailable(OUTPUT_DATA_QUEUE *aOutputQueue, const bool isHttpHeader) |
| { |
| if (isHttpHeader) |
| { |
| int32 status = checkContentInfoMatchingForResumeDownload(); |
| if (status != HttpParsingBasicObject::PARSE_SUCCESS) return status; |
| |
| iDataSideInfo.set(ProtocolEngineOutputDataType_HttpHeader); |
| iObserver->OutputDataAvailable(*aOutputQueue, iDataSideInfo); |
| } |
| else // output data to data stream object |
| { |
| if (iParser->getDownloadSize() > iCfgFile->GetCurrentFileSize()) |
| { |
| updateOutputDataQueue(aOutputQueue); // aOutputQueue could have the partial valid data for resume download and trucated content case |
| iDataSideInfo.set(ProtocolEngineOutputDataType_NormalData); |
| iObserver->OutputDataAvailable(*aOutputQueue, iDataSideInfo); |
| return updateDownloadStatistics(); // could return PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED |
| } |
| } |
| return HttpParsingBasicObject::PARSE_SUCCESS; |
| } |
| |
| int32 ProgressiveDownloadState_GET::checkContentInfoMatchingForResumeDownload() |
| { |
| if (iCfgFile->IsNewSession()) return HttpParsingBasicObject::PARSE_SUCCESS; |
| uint32 prevOverallFileSize = iCfgFile->GetOverallFileSize(); |
| if (iCfgFile->GetOverallFileSize() == iCfgFile->GetMaxAllowedFileSize() && !iCfgFile->HasContentLength()) |
| { |
| prevOverallFileSize = 0; // no content-length for the previous download |
| } |
| int32 status = iParser->isNewContentRangeInfoMatchingCurrentOne(prevOverallFileSize); |
| // Get internal download size synced up with new content-range info |
| iParser->setDownloadSize(); |
| return status; |
| } |
| |
| void ProgressiveDownloadState_GET::updateOutputDataQueue(OUTPUT_DATA_QUEUE *aOutputQueue) |
| { |
| // get start fragment especially for resume download case |
| bool aUseAllNewDownloadData; |
| uint32 aStartFragNo = 0, aStartFragOffset = 0; |
| getStartFragmentInNewDownloadData(*aOutputQueue, aUseAllNewDownloadData, aStartFragNo, aStartFragOffset); |
| if (aUseAllNewDownloadData) return; |
| |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getStartFragmentInNewDownloadData() : aOutputQueue->size=%d, aStartFragNo=%d, aStartFragOffset=%d", |
| aOutputQueue->size(), aStartFragNo, aStartFragOffset)); |
| |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getStartFragmentInNewDownloadData() : downloadSize=%d, currFileSize=%d", |
| iParser->getDownloadSize(), iCfgFile->GetCurrentFileSize())); |
| |
| // process start fragment |
| if (!(aStartFragNo == 0 && aStartFragOffset == 0)) // exist offset |
| { |
| OsclMemoryFragment memFrag; |
| uint8 *startPtr = (uint8*)((*aOutputQueue)[aStartFragNo].getMemFragPtr()) + aStartFragOffset; |
| memFrag.ptr = (OsclAny*)startPtr; |
| memFrag.len = (*aOutputQueue)[aStartFragNo].getMemFragSize() - aStartFragOffset; |
| OsclRefCounter *refcnt = (*aOutputQueue)[aStartFragNo].getRefCounter(); |
| OsclRefCounterMemFrag refCountMemFrag = OsclRefCounterMemFrag(memFrag, refcnt, memFrag.len); |
| refcnt->addRef(); // manually add reference counter since there will be vector push_back happens. |
| |
| for (uint32 i = 0; i <= aStartFragNo; i++) |
| { |
| aOutputQueue->erase(aOutputQueue->begin()); |
| } |
| if (memFrag.len > 0) aOutputQueue->push_front(refCountMemFrag); |
| |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue() after processing start fragment: aOutputQueue->size=%d", aOutputQueue->size())); |
| } |
| |
| // get end fragment especially for truncated content case |
| uint32 aEndFragNo = 0, aEndFragValidLen = 0; |
| getEndFragmentInNewDownloadData(*aOutputQueue, aEndFragNo, aEndFragValidLen); |
| |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getEndFragmentInNewDownloadData() : aOutputQueue->size=%d, aEndFragNo=%d, aEndFragValidLen=%d", |
| aOutputQueue->size(), aEndFragNo, aEndFragValidLen)); |
| |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue()->getStartFragmentInNewDownloadData() : downloadSize=%d, overallFileSize=%d", |
| iParser->getDownloadSize(), iCfgFile->GetOverallFileSize())); |
| |
| // process end fragment |
| if (!(aEndFragNo == aOutputQueue->size() - 1 && |
| aEndFragValidLen == (*aOutputQueue)[aEndFragNo].getMemFragSize())) |
| { |
| OsclMemoryFragment memFrag; |
| memFrag.ptr = (*aOutputQueue)[aEndFragNo].getMemFragPtr(); |
| memFrag.len = aEndFragValidLen; |
| OsclRefCounter *refcnt = (*aOutputQueue)[aEndFragNo].getRefCounter(); |
| OsclRefCounterMemFrag refCountMemFrag = OsclRefCounterMemFrag(memFrag, refcnt, memFrag.len); |
| refcnt->addRef(); // manually add reference counter since there will be vector push_back happens. |
| |
| for (int32 j = (int32)aOutputQueue->size() - 1; j >= (int32)aEndFragNo; j--) |
| { |
| aOutputQueue->erase(&(aOutputQueue->back())); |
| } |
| |
| aOutputQueue->push_back(refCountMemFrag); |
| |
| LOGINFODATAPATH((0, "ProgressiveDownloadState_GET::updateOutputDataQueue() after processing end fragment: aOutputQueue->size=%d", aOutputQueue->size())); |
| } |
| } |
| |
| void ProgressiveDownloadState_GET::getStartFragmentInNewDownloadData(OUTPUT_DATA_QUEUE &aOutputQueue, |
| bool &aUseAllNewDownloadData, |
| uint32 &aStartFragNo, |
| uint32 &aStartFragOffset) |
| { |
| aUseAllNewDownloadData = false; |
| aStartFragNo = aStartFragOffset = 0; |
| |
| uint32 validSize = iParser->getDownloadSize() - iCfgFile->GetCurrentFileSize(); |
| |
| uint32 totalSize = 0, prevTotalSize = 0; |
| for (uint32 i = 0; i < aOutputQueue.size(); i++) |
| { |
| prevTotalSize = totalSize; |
| totalSize += aOutputQueue[i].getMemFragSize(); |
| if (prevTotalSize <= validSize && validSize < totalSize) |
| { |
| if (validSize < totalSize && i < aOutputQueue.size() - 1) |
| { |
| aStartFragNo = i; |
| aStartFragOffset = validSize - prevTotalSize; |
| return; |
| } |
| } |
| } |
| |
| aUseAllNewDownloadData = (validSize == totalSize) & |
| (iParser->getDownloadSize() <= iCfgFile->GetOverallFileSize()); |
| } |
| |
| void ProgressiveDownloadState_GET::getEndFragmentInNewDownloadData(OUTPUT_DATA_QUEUE &aOutputQueue, |
| uint32 &aEndFragNo, |
| uint32 &aEndFragValidLen) |
| { |
| aEndFragNo = aOutputQueue.size() - 1; |
| aEndFragValidLen = aOutputQueue[aEndFragNo].getMemFragSize(); |
| |
| if (iParser->getDownloadSize() > iCfgFile->GetOverallFileSize()) |
| { |
| uint32 extraSize = iParser->getDownloadSize() - iCfgFile->GetOverallFileSize(); |
| uint32 reduceSize = 0, prevReduceSize = 0; |
| for (int32 i = aOutputQueue.size() - 1; i >= 0; i--) |
| { |
| prevReduceSize = reduceSize; |
| reduceSize += aOutputQueue[i].getMemFragSize(); |
| if (prevReduceSize <= extraSize && extraSize < reduceSize) |
| { |
| aEndFragNo = i; |
| aEndFragValidLen = reduceSize - extraSize; |
| return; |
| } |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| ////// ProgressiveStreamingState implementation |
| //////////////////////////////////////////////////////////////////////////////////// |
| |
| int32 ProgressiveStreamingState_GET::checkParsingStatus(int32 parsingStatus) |
| { |
| // download complete with truncation |
| if (iSendEndOfMessageTruncate) |
| { |
| iSendEndOfMessageTruncate = false; |
| return PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED; |
| } |
| |
| return DownloadState::checkParsingStatus(parsingStatus); |
| } |
| |