blob: 7b80b5a4bbbdf274496e82804df2c82c12ee7b7f [file] [log] [blame]
/* ------------------------------------------------------------------
* 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);
}