blob: 0a062709111d6127847da53079b9ee55937cd4bf [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_common.h"
#include "pvmf_media_msg_format_ids.h" // for PVMF_MEDIA_CMD_EOS_FORMAT_ID
#include "oscl_utf8conv.h" // for oscl_UnicodeToUTF8
////////////////////////////////////////////////////////////////////////////////////
////// pvHttpDownloadInput implementation
////////////////////////////////////////////////////////////////////////////////////
bool pvHttpDownloadInput::getValidMediaData(INPUT_DATA_QUEUE &aDataInQueue, PVMFSharedMediaDataPtr &aMediaData, bool &isEOS)
{
isEOS = false; //aMediaData.Bind(iCurrentInputMediaData);
do
{
// There should be at least one media msg in the queue
if (aDataInQueue.empty()) return false;
// Check if the next incoming media msg is an EOS or not
// Introducing a boolean variable aEOSMsg is for simulating connection shutdown cases
bool aEOSMsg = aDataInQueue[0]->getFormatID() == PVMF_MEDIA_CMD_EOS_FORMAT_ID;
if (aEOSMsg)
{
isEOS = true;
aDataInQueue.erase(aDataInQueue.begin());
return true;
}
convertToPVMFMediaData(iCurrentInputMediaData, aDataInQueue[0]);
aDataInQueue.erase(aDataInQueue.begin());
}
while (isValidInput() == false);
aMediaData.Bind(iCurrentInputMediaData);
return true;
}
bool pvHttpDownloadInput::isValidInput()
{
if (iCurrentInputMediaData->getTimestamp() == 0xFFFFFFFF ||
iCurrentInputMediaData->getFilledSize() == 0)
{
// Cannot use this input media data since TS is not available
// Discard and return to retrieve another input media data from the queue
unbind();
return false;
// ANOTHER OPTION TO DEFAULT THE TS TO 0 IN THIS CASE AND CONTINUE ON
}
return true;
}
////// INetURI implementation
////////////////////////////////////////////////////////////////////////////////////
bool INetURI::setURI(OSCL_wString &aUri, const bool aRedirectURI)
{
if (aUri.get_size() == 0) return false;
OsclMemAllocator alloc;
char *buf = (char*)alloc.allocate(aUri.get_size() + 1);
if (!buf) return false;
uint32 size = oscl_UnicodeToUTF8(aUri.get_cstr(), aUri.get_size(), buf, aUri.get_size() + 1);
if (size == 0)
{
alloc.deallocate(buf);
return false;
}
iURI = OSCL_HeapString<OsclMemAllocator> (buf, size);
alloc.deallocate(buf);
// clear iHost
iHostName.set(NULL, 0);
iRedirectURI = aRedirectURI;
return true;
}
bool INetURI::getHostAndPort(OSCL_String &aSerAdd, int32 &aSerPort)
{
if (iURI.get_size() == 0) return false;
if (iHostName.get_size() == 0)
{
if (!parseURL(iURI, iHostName, iHostPort)) return false;
}
aSerAdd = iHostName;
aSerPort = iHostPort;
return true;
}
bool INetURI::parseURL(OSCL_String &aUrl8, OSCL_String &aSerAdd, int32 &aSerPort)
{
OSCL_HeapString<OsclMemAllocator> tmpUrl8(aUrl8);
typedef char mbchar;
mbchar* aUrl = tmpUrl8.get_str();
mbchar *server_ip_ptr = OSCL_CONST_CAST(mbchar*, oscl_strstr(((mbchar*)aUrl), "//"));
if (server_ip_ptr == NULL) return false;
server_ip_ptr += 2;
/* Locate the IP address. */
mbchar *server_port_ptr = OSCL_CONST_CAST(mbchar*, oscl_strstr(server_ip_ptr, ":"));
mbchar *tmp_ptr = server_port_ptr;
if (tmp_ptr == NULL) tmp_ptr = server_ip_ptr;
mbchar *clip_name = OSCL_CONST_CAST(mbchar*, oscl_strstr(tmp_ptr, "/"));
if (clip_name != NULL) *clip_name++ = '\0';
/* Locate the port number if provided. */
aSerPort = DEFAULT_HTTP_PORT_NUMBER;
if ((server_port_ptr != NULL) && (*(server_port_ptr + 1) != '/'))
{
*(server_port_ptr++) = '\0';
uint32 atoi_tmp;
if (PV_atoi(server_port_ptr, 'd', atoi_tmp)) aSerPort = atoi_tmp;
else return false;
}
/* relocate the server IP address, either stop at ':' or '/' */
mbchar *server_end_ptr = OSCL_CONST_CAST(mbchar*, oscl_strstr(server_ip_ptr, "/"));
if (server_end_ptr) *server_end_ptr = '\0';
OSCL_HeapString<OsclMemAllocator> tmpServerName(server_ip_ptr, oscl_strlen(server_ip_ptr));
aSerAdd = tmpServerName;
return true;
}
////////////////////////////////////////////////////////////////////////////////////
////// HttpParsingBasicObject implementation
////////////////////////////////////////////////////////////////////////////////////
int32 HttpParsingBasicObject::parseResponse(INPUT_DATA_QUEUE &aDataQueue)
{
PVMFSharedMediaDataPtr mediaData;
int32 status = getNextMediaData(aDataQueue, mediaData);
if (status != PARSE_SUCCESS)
{
if (status == PARSE_EOS_INPUT_DATA)
{
return validateEOSInput(status);
}
return status; // no input data or eos
}
OsclRefCounterMemFrag fragIn;
mediaData->getMediaFragment(0, fragIn);
HttpParsingBasicObjectAutoCleanup cleanup(this);
while (status == PARSE_SUCCESS)
{
RefCountHTTPEntityUnit entityUnit;
int32 parsingStatus = iParser->parse(fragIn, entityUnit);
if (parsingStatus < 0)
{
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0,
"HttpParsingBasicObject::parseResponse(), iParser->parse() retval=%d(iHttpHeaderParsed=%d)",
parsingStatus, (int32)iHttpHeaderParsed));
}
else
{
// save output data if there is
iOutputQueue->clear();
uint32 size = 0;
if (!saveOutputData(entityUnit, *iOutputQueue, size))
{
return PARSE_GENERAL_ERROR;
}
if (parsingStatus == HTTPParser::PARSE_HEADER_AVAILABLE)
{
iHttpHeaderParsed = true;
iParser->getContentInfo(iContentInfo);
extractServerVersionNum();
// update BandWidthEstimationInfo
iBWEstInfo.update(mediaData, iHttpHeaderParsed);
// do sanity check for HTTP header
int32 sanityCheckStatus = iParser->doSanityCheckForResponseHeader();
if (sanityCheckStatus == HTTPParser::PARSE_TRANSFER_ENCODING_NOT_SUPPORTED)
{
parsingStatus = sanityCheckStatus;
}
else
{
// output data
status = iObserver->OutputDataAvailable(iOutputQueue, true);
if (status < 0) return status;
}
}
else if (iHttpHeaderParsed && size > 0)
{
iTotalDLHttpBodySize += size;
if (iLatestMediaDataTimestamp < mediaData->getTimestamp()) iLatestMediaDataTimestamp = mediaData->getTimestamp();
// update BandWidthEstimationInfo
iBWEstInfo.update(mediaData, iHttpHeaderParsed);
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0, "HttpParsingBasicObject::parseResponse() file size = %d, download size = %d, curr_size = %d, new download size = %d",
iContentInfo.iContentLength, iTotalDLHttpBodySize, size, iBWEstInfo.iTotalSizePerRequest));
}
}
// check the condition of whether parsing the current input is done or not
// may send out callback for end of message or end of input cases
if ((status = checkParsingDone(parsingStatus)) != PARSE_SUCCESS)
{
if (status != PROCESS_WAIT_FOR_INCOMING_DATA)
{
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0, "HttpParsingBasicObject::parseResponse() status=checkParsingDone(parsingStatus)); parsingStatus = %d , status = %d",
parsingStatus, status));
}
return status;
}
}
return PARSE_SUCCESS;
}
int32 HttpParsingBasicObject::getNextMediaData(INPUT_DATA_QUEUE &aDataInQueue, PVMFSharedMediaDataPtr &aMediaData)
{
bool isEOS = false;
if (!iInput.getValidMediaData(aDataInQueue, aMediaData, isEOS)) return PARSE_NO_INPUT_DATA;
if (isEOS)
{
if (!isRedirectResponse())
{
iNumEOSMessagesAfterRequest++;
iTotalDLSizeForPrevEOS = iTotalDLSizeAtCurrEOS;
iTotalDLSizeAtCurrEOS = iTotalDLHttpBodySize;
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0, "HttpParsingBasicObject::getNextMediaData() isEOS=%d, iNumEOSMessagesAfterRequest=%d, iTotalDLHttpBodySize=%d, iTotalDLSizeAtCurrEOS=%d, iTotalDLSizeForPrevEOS=%d",
(uint32)isEOS, iNumEOSMessagesAfterRequest, iTotalDLHttpBodySize, iTotalDLSizeAtCurrEOS, iTotalDLSizeForPrevEOS));
}
iInput.unbind();
return PARSE_EOS_INPUT_DATA;
}
//if(iTotalDLSizeAtCurrEOS!=iTotalDLHttpBodySize || !isEOS) {
if (iTotalDLSizeAtCurrEOS > iTotalDLSizeForPrevEOS || iTotalDLHttpBodySize > iTotalDLSizeAtCurrEOS)
{
iNumEOSMessagesAfterRequest = 0;
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0, "HttpParsingBasicObject::getNextMediaData(), non-EOS case iNumEOSMessagesAfterRequest is reset to 0, iTotalDLHttpBodySize=%d, iTotalDLSizeAtCurrEOS=%d, iTotalDLSizeForPrevEOS=%d",
iTotalDLHttpBodySize, iTotalDLSizeAtCurrEOS, iTotalDLSizeForPrevEOS));
}
if (iNumEOSMessagesAfterRequest != 0)
{
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0, "HttpParsingBasicObject::getNextMediaData(), should be non-EOS case and iNumEOSMessagesAfterRequest is NOT reset to 0! isEOS=%d, iNumEOSMessagesAfterRequest=%d, iTotalDLHttpBodySize=%d, iTotalDLSizeAtCurrEOS=%d, iTotalDLSizeForPrevEOS=%d",
(uint32)isEOS, iNumEOSMessagesAfterRequest, iTotalDLHttpBodySize, iTotalDLSizeAtCurrEOS, iTotalDLSizeForPrevEOS));
}
return PARSE_SUCCESS;
}
int32 HttpParsingBasicObject::validateEOSInput(int32 parsingStatus)
{
//if(!iHttpHeaderParsed && iNumEOSMessagesAfterRequest>=MAX_NUM_EOS_MESSAGES_FOR_SAME_REQUEST) { // MAX_NUM_EOS_MESSAGES_FOR_SAME_REQUEST=2
if (iNumEOSMessagesAfterRequest >= iNumRetry) // iNumRetry = MAX_NUM_EOS_MESSAGES_FOR_SAME_REQUEST=2
{
// if we recieve EOS message as the first response after sending request, and we do socket reconnect and send the request again, if we still
// get EOS message, that means server always shuts down the connection for this request, and we don't want to try reconnect any more. That
// indicates the url is probably bad.
// In general, we treat download size unchange between two adjacent EOSs (even if there is some header parsed) as bad url. Otherwise we'll
// run into infinite reconnect/disconnect loop
return PARSE_BAD_URL;
}
return parsingStatus;
}
void HttpParsingBasicObject::extractServerVersionNum()
{
StrCSumPtrLen serverKey = "Server";
StrPtrLen serverValue;
if (!iParser->getField(serverKey, serverValue)) return;
if (serverValue.length() == 0) return;
// Has Sever header
char *ptr = (char*)serverValue.c_str();
for (int32 i = 0; i < serverValue.length(); i++)
{
if (!PE_isDigit(*ptr))
{
ptr++;
continue;
}
iServerVersionNumber = *ptr++ - '0';
if (PE_isDigit(*ptr) && ++i < serverValue.length())
{
iServerVersionNumber = iServerVersionNumber * 10 + (*ptr - '0');
}
break;
}
}
bool HttpParsingBasicObject::saveOutputData(RefCountHTTPEntityUnit &entityUnit, OUTPUT_DATA_QUEUE &aOutputData, uint32 &aTotalEntityDataSize)
{
aTotalEntityDataSize = 0;
int32 err = 0;
OSCL_TRY(err,
for (uint32 i = 0; i < entityUnit.getEntityUnit().getNumFragments();i++)
{
OsclRefCounterMemFrag memfrag;
entityUnit.getEntityUnit().getMemFrag(i, memfrag);
aOutputData.push_back(memfrag);
aTotalEntityDataSize += memfrag.getMemFragSize();
}
);
return (err == 0);
}
int32 HttpParsingBasicObject::checkParsingDone(const int32 parsingStatus)
{
// check error case
if (parsingStatus < 0)
{
if (parsingStatus == HTTPParser::PARSE_SYNTAX_ERROR) return PARSE_SYNTAX_ERROR;
if (parsingStatus == HTTPParser::PARSE_HTTP_VERSION_NOT_SUPPORTED) return PARSE_HTTP_VERSION_NOT_SUPPORTED;
if (parsingStatus == HTTPParser::PARSE_TRANSFER_ENCODING_NOT_SUPPORTED) return PARSE_TRANSFER_ENCODING_NOT_SUPPORTED;
return PARSE_GENERAL_ERROR; // error happens;
}
if (parsingStatus == HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE ||
parsingStatus == HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA ||
parsingStatus == HTTPParser::PARSE_SUCCESS_END_OF_INPUT ||
parsingStatus == HTTPParser::PARSE_SUCCESS)
{
// send output data
if (iHttpHeaderParsed && !iOutputQueue->empty())
{
//if(!iObserver->OutputDataAvailable(iOutputQueue, false)) return PARSE_GENERAL_ERROR;
int32 status = iObserver->OutputDataAvailable(iOutputQueue, false);
if (status < 0) return status;
if (status == PROCESS_SUCCESS_END_OF_MESSAGE) return PARSE_SUCCESS_END_OF_MESSAGE;
if (status == PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED) return status;
}
}
if (parsingStatus == HTTPParser::PARSE_STATUS_LINE_SHOW_NOT_SUCCESSFUL) return PARSE_STATUS_LINE_SHOW_NOT_SUCCESSFUL; // status code >= 300
if (parsingStatus == HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE) return PARSE_SUCCESS_END_OF_MESSAGE;
if (parsingStatus == HTTPParser::PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA) return PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA;
if (parsingStatus == HTTPParser::PARSE_NEED_MORE_DATA) return PARSE_NEED_MORE_DATA;
if (parsingStatus == HTTPParser::PARSE_SUCCESS_END_OF_INPUT) return PARSE_SUCCESS_END_OF_INPUT;
// HTTPParser::PARSE_SUCCESS or HTTPParser::PARSE_HEADER_AVAILABLE
return PARSE_SUCCESS;
}
bool HttpParsingBasicObject::isRedirectResponse()
{
bool aPartOfRedirectResponse = false;
uint32 httpStatusCode = getStatusCode();
if (PROTOCOLENGINE_REDIRECT_STATUS_CODE_START <= httpStatusCode && httpStatusCode <= PROTOCOLENGINE_REDIRECT_STATUS_CODE_END)
{
aPartOfRedirectResponse = true;
}
return aPartOfRedirectResponse;
}
// factory method
HttpParsingBasicObject* HttpParsingBasicObject::create()
{
HttpParsingBasicObject *object = OSCL_NEW(HttpParsingBasicObject, ());
if (!object) return NULL;
if (!object->construct())
{
OSCL_DELETE(object);
return NULL;
}
return object;
}
bool HttpParsingBasicObject::construct()
{
reset();
resetForBadConnectionDetection();
iServerVersionNumber = 0;
if ((iParser = HTTPParser::create()) == NULL) return false;
return true;
}
void HttpParsingBasicObject::setDownloadSize(const uint32 aInitialSize)
{
if (aInitialSize > 0)
iTotalDLHttpBodySize = aInitialSize;
else
{
// sync up with content-range_left
iTotalDLHttpBodySize = iContentInfo.iContentRangeLeft;
}
PVMF_PROTOCOL_ENGINE_LOGERRINFODATAPATH((0, "HttpParsingBasicObject::setDownloadSize %d", iTotalDLHttpBodySize));
iTotalDLSizeForPrevEOS = iTotalDLSizeAtCurrEOS = iTotalDLHttpBodySize;
}
bool HttpParsingBasicObject::getRedirectURI(OSCL_String &aRedirectUri)
{
StrCSumPtrLen aLocation = "Location";
StrPtrLen url;
if (iParser->getField(aLocation, url))
{
if (url.length() > MIN_URL_LENGTH) // MIN_URL_LENGTH = 1
{
// http_parcom views empty header value as ASCII space character 0x20
// so remove the case of empty header
aRedirectUri = OSCL_HeapString<OsclMemAllocator> (url.c_str(), url.length());
return true;
}
}
return false;
}
bool HttpParsingBasicObject::getContentType(OSCL_String &aContentType)
{
StrCSumPtrLen aContentTypeKey = "Content-Type";
StrPtrLen aContentTypeValue;
if (iParser->getField(aContentTypeKey, aContentTypeValue))
{
if (aContentTypeValue.length() > 0)
{
aContentType = OSCL_HeapString<OsclMemAllocator> (aContentTypeValue.c_str(), aContentTypeValue.length());
return true;
}
}
return false;
}
bool HttpParsingBasicObject::isServerSupportBasicAuthentication()
{
StrCSumPtrLen aAuthenKey = "WWW-Authenticate";
uint32 numFieldsByKey = iParser->getNumberOfFieldsByKey(aAuthenKey);
uint32 i = 0;
for (i = 0; i < numFieldsByKey; i++)
{
StrPtrLen aAuthenValue;
iParser->getField(aAuthenKey, aAuthenValue, i);
const char *ptrRealm = aAuthenValue.c_str();
uint32 len = aAuthenValue.length();
uint32 length = 0;
getRealmPtr(ptrRealm, len, length);
getBasicPtr(aAuthenValue, length);
if (length >= 6) return true;
}
return false;
}
bool HttpParsingBasicObject::getAuthenInfo(OSCL_String &aRealm)
{
StrCSumPtrLen aAuthenKey = "WWW-Authenticate";
uint32 numFieldsByKey = iParser->getNumberOfFieldsByKey(aAuthenKey);
uint32 i = 0;
for (i = 0; i < numFieldsByKey; i++)
{
StrPtrLen aAuthenValue;
iParser->getField(aAuthenKey, aAuthenValue, i);
const char *ptrRealm = aAuthenValue.c_str();
uint32 len = aAuthenValue.length();
uint32 length = 0;
getRealmPtr(ptrRealm, len, length);
if (len < 6) continue;
getBasicPtr(aAuthenValue, length);
if (length < 6) continue;
ptrRealm += 6;
len -= 6;
aRealm = OSCL_HeapString<OsclMemAllocator> (ptrRealm, len);
return true;
}
return false;
}
void HttpParsingBasicObject::getRealmPtr(const char *&ptrRealm, uint32 &len, uint32 &length)
{
while (!(((ptrRealm[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'r') &&
((ptrRealm[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'e') &&
((ptrRealm[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 'a') &&
((ptrRealm[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'l') &&
((ptrRealm[4] | OSCL_ASCII_CASE_MAGIC_BIT) == 'm') &&
((ptrRealm[5] | OSCL_ASCII_CASE_MAGIC_BIT) == '=')) &&
len >= 6)
{
ptrRealm++;
len--;
length++;
}
}
void HttpParsingBasicObject::getBasicPtr(const StrPtrLen aAuthenValue, uint32 &length)
{
const char *ptrBasic = aAuthenValue.c_str();
while (!(((ptrBasic[0] | OSCL_ASCII_CASE_MAGIC_BIT) == 'b') &&
((ptrBasic[1] | OSCL_ASCII_CASE_MAGIC_BIT) == 'a') &&
((ptrBasic[2] | OSCL_ASCII_CASE_MAGIC_BIT) == 's') &&
((ptrBasic[3] | OSCL_ASCII_CASE_MAGIC_BIT) == 'i') &&
((ptrBasic[4] | OSCL_ASCII_CASE_MAGIC_BIT) == 'c') &&
((ptrBasic[5] | OSCL_ASCII_CASE_MAGIC_BIT) == ' ')) &&
length >= 6)
{
ptrBasic++;
length--;
}
}
bool HttpParsingBasicObject::isServerSendAuthenticationHeader()
{
StrCSumPtrLen aAuthenKey = "WWW-Authenticate";
StrPtrLen aAuthenValue;
if (iParser->getField(aAuthenKey, aAuthenValue))
{
if (aAuthenValue.length() > 0)
{
return true;
}
}
return false;
}
int32 HttpParsingBasicObject::isNewContentRangeInfoMatchingCurrentOne(const uint32 aPrevContentLength)
{
// First, consider content-length match
if (aPrevContentLength != iContentInfo.iContentLength) return PARSE_CONTENT_LENGTH_NOT_MATCH;
// if range doesn't support, return false
if (iContentInfo.iContentRangeRight == 0) return PARSE_CONTENT_RANGE_INFO_NOT_MATCH;
// Second, consider this case where content-range exists: compare iTotalDLHttpBodySize with content-range_left
if (iTotalDLHttpBodySize > 0 && iContentInfo.iContentRangeRight > 0)
{
if (iTotalDLHttpBodySize != iContentInfo.iContentRangeLeft) return PARSE_CONTENT_RANGE_INFO_NOT_MATCH;
}
// for other cases, return true
return PARSE_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////
////// ProtocolState implementation
////////////////////////////////////////////////////////////////////////////////////
int32 ProtocolState::processMicroState(INPUT_DATA_QUEUE &aDataQueue)
{
if (iProcessingState == EHttpProcessingMicroState_SendRequest)
{
int32 status = doProcessMicroStateSendRequestPreCheck();
if (status != PROCESS_SUCCESS) return status;
return doProcessMicroStateSendRequest();
}
else if (iProcessingState == EHttpProcessingMicroState_GetResponse)
{
int32 status = doProcessMicroStateGetResponsePreCheck();
if (status != PROCESS_SUCCESS) return status;
return doProcessMicroStateGetResponse(aDataQueue);
}
return PROCESS_SUCCESS;
}
int32 ProtocolState::doProcessMicroStateSendRequestPreCheck()
{
int32 status = processMicroStateSendRequestPreCheck();
if (status < 0)
{
LOGINFODATAPATH((0, "ProtocolState::processMicroState() processMicroStateSendRequestPreCheck(), error status, errCode=%d", status));
iObserver->ProtocolStateError(status);
return status;
}
return PROCESS_SUCCESS;
}
int32 ProtocolState::doProcessMicroStateSendRequest()
{
int32 status = processMicroStateSendRequest();
if (status >= 0)
{
// SendRequest -> GetResponse automatically
iProcessingState = EHttpProcessingMicroState_GetResponse;
// No need to set iNeedGetResponsePreCheck as true.
}
if (status != PROCESS_SUCCESS)
{
LOGINFODATAPATH((0, "ProtocolState::processMicroState() send request error, errCode=%d", status));
if (status < 0) iObserver->ProtocolStateError(status);
}
return status;
}
int32 ProtocolState::doProcessMicroStateGetResponsePreCheck()
{
if (iNeedGetResponsePreCheck)
{
int32 status = processMicroStateGetResponsePreCheck();
if (status != PROCESS_SUCCESS)
{
LOGINFODATAPATH((0, "ProtocolState::processMicroState() processMicroStateGetResponsePreCheck(), error status, errCode=%d", status));
iObserver->ProtocolStateError(status);
return status;
}
iNeedGetResponsePreCheck = false;
}
return PROCESS_SUCCESS;
}
int32 ProtocolState::doProcessMicroStateGetResponse(INPUT_DATA_QUEUE &aDataQueue)
{
int32 status = processMicroStateGetResponse(aDataQueue);
if (status == PROCESS_SUCCESS_END_OF_MESSAGE ||
status == PROCESS_SUCCESS_END_OF_MESSAGE_TRUNCATED ||
status == PROCESS_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA ||
status == PROCESS_SUCCESS_END_OF_MESSAGE_BY_SERVER_DISCONNECT ||
status == PROCESS_SUCCESS_GOT_EOS)
{
// notify user that data processing at the current state is completely done.
// user may choose to change to next protocol state
if (status != PROCESS_SUCCESS_GOT_EOS)
{
ProtocolStateCompleteInfo aInfo(isDownloadStreamingDoneState(),
isLastState(),
isDownloadStreamingDoneState());
iObserver->ProtocolStateComplete(aInfo);
}
iNeedGetResponsePreCheck = true;
}
else if (status == PROCESS_SERVER_RESPONSE_ERROR || status < 0)
{
int32 errorCode = status;
if (status >= 0) errorCode = iParser->getStatusCode();
LOGINFODATAPATH((0, "ProtocolState::processMicroState() error status, errCode=%d", errorCode));
iObserver->ProtocolStateError(errorCode);
}
return status;
}
int32 ProtocolState::processMicroStateSendRequestPreCheck()
{
if (!iComposer || !iObserver) return PROCESS_INPUT_OUTPUT_NOT_READY;
// check good url in terms of parsing correctly to get server address and port
if (!iURI.isGoodUri()) return PROCESS_BAD_URL;
// reset composer
iComposer->reset();
return PROCESS_SUCCESS;
}
int32 ProtocolState::processMicroStateSendRequest()
{
// create output media data to be sent to socket node through port
PVMFSharedMediaDataPtr mediaData;
if (!iObserver->GetBufferForRequest(mediaData)) return PROCESS_MEDIA_DATA_CREATE_FAILURE;
// compose http request
OsclRefCounterMemFrag fragOut;
mediaData->getMediaFragment(0, fragOut);
OsclMemoryFragment memFrag = fragOut.getMemFrag();
memFrag.len = fragOut.getCapacity();
int32 status = composeRequest(memFrag);
if (status != PROCESS_SUCCESS) return status;
mediaData->setMediaFragFilledLen(0, iComposer->getCurrentRequestLength(iURI.isUseAbsoluteURI())); // don't count NULL
// send to port
iObserver->ProtocolRequestAvailable(getProtocolRequestType());
// set start time for download rate estimation
iStartTime.set_to_current_time();
// reset the band width estimation info structure
// needed for repositioning in progressive streaming
BandwidthEstimationInfo *pBWEstInfo = iParser->getBandwidthEstimationInfo();
pBWEstInfo->clear();
// move to the next state, GetResponse
iProcessingState = EHttpProcessingMicroState_GetResponse;
return PROCESS_SUCCESS;
}
int32 ProtocolState::composeRequest(OsclMemoryFragment &aFrag)
{
// reset composer to compose a new request
iComposer->reset();
// set three basic elements: method, url and http version
setRequestBasics();
// set fields
if (!setHeaderFields()) return PROCESS_COMPOSE_HTTP_REQUEST_FAILURE;
// compose
return doCompose(aFrag);
}
int32 ProtocolState::doCompose(OsclMemoryFragment &aFrag)
{
// compose
uint32 requestLen = iComposer->getCurrentRequestLength(iURI.isUseAbsoluteURI());
if (requestLen + 1 > aFrag.len) return PROCESS_COMPOSE_HTTP_REQUEST_BUFFER_SIZE_NOT_MATCH_REQUEST_SIZE;
if (iComposer->compose(aFrag, iURI.isUseAbsoluteURI())) return PROCESS_COMPOSE_HTTP_REQUEST_FAILURE;
return PROCESS_SUCCESS;
}
int32 ProtocolState::processMicroStateGetResponsePreCheck()
{
if (!iParser || !iObserver) return PROCESS_INPUT_OUTPUT_NOT_READY;
iParser->reset();
return PROCESS_SUCCESS;
}
int32 ProtocolState::processMicroStateGetResponse(INPUT_DATA_QUEUE &aDataQueue)
{
int32 status = iParser->parseResponse(aDataQueue);
return checkParsingStatus(status);
}
// shared routine for all the download protocols
int32 ProtocolState::checkParsingStatus(int32 parsingStatus)
{
// error part
if (parsingStatus == HttpParsingBasicObject::PARSE_SYNTAX_ERROR) return handleParsingSyntaxError();
if (parsingStatus == HttpParsingBasicObject::PARSE_GENERAL_ERROR) return PROCESS_GENERAL_ERROR;
if (parsingStatus == HttpParsingBasicObject::PARSE_BAD_URL) return PROCESS_BAD_URL;
if (parsingStatus == HttpParsingBasicObject::PARSE_HTTP_VERSION_NOT_SUPPORTED) return PROCESS_HTTP_VERSION_NOT_SUPPORTED;
if (parsingStatus == HttpParsingBasicObject::PARSE_TRANSFER_ENCODING_NOT_SUPPORTED) return PROCESS_CHUNKED_TRANSFER_ENCODING_NOT_SUPPORT;
if (parsingStatus < 0)
{
return PROCESS_PARSE_HTTP_RESPONSE_FAILURE;
}
if (parsingStatus == HttpParsingBasicObject::PARSE_NO_INPUT_DATA) return PROCESS_WAIT_FOR_INCOMING_DATA;
if (parsingStatus == HttpParsingBasicObject::PARSE_STATUS_LINE_SHOW_NOT_SUCCESSFUL) return PROCESS_SERVER_RESPONSE_ERROR;
if (parsingStatus == HttpParsingBasicObject::PARSE_SUCCESS ||
parsingStatus == HttpParsingBasicObject::PARSE_NEED_MORE_DATA ||
parsingStatus == HttpParsingBasicObject::PARSE_SUCCESS_END_OF_INPUT) return PROCESS_SUCCESS;
if (parsingStatus == HttpParsingBasicObject::PARSE_SUCCESS_END_OF_MESSAGE) return PROCESS_SUCCESS_END_OF_MESSAGE;
if (parsingStatus == HttpParsingBasicObject::PARSE_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA) return PROCESS_SUCCESS_END_OF_MESSAGE_WITH_EXTRA_DATA;
if (parsingStatus == HttpParsingBasicObject::PARSE_EOS_INPUT_DATA) return PROCESS_SUCCESS_GOT_EOS;
return PROCESS_SUCCESS;
}
int32 ProtocolState::handleParsingSyntaxError()
{
// If we run into syntax error -2 and the new http header is not available,
// then ignore the current packet and search the new response packet with http response header
if (!iParser->isHttpHeaderParsed())
{
LOGINFODATAPATH((0, "ProtocolState::handleParsingSyntaxError(), parsing error(-2) + no Http header parsed, IGNORE THE PACKET AND CONTINUE SEARCH!!"));
// reset the parser
uint32 currDownloadSize = iParser->getDownloadSize();
iParser->reset();
iParser->setDownloadSize(currDownloadSize);
return PROCESS_SUCCESS;
}
return PROCESS_PARSE_HTTP_RESPONSE_FAILURE;
}
uint32 ProtocolState::getDownloadRate()
{
TimeValue currentTime;
currentTime.set_to_current_time();
TimeValue deltaTimeVal = currentTime - iStartTime;
int32 deltaMilliSec0 = deltaTimeVal.to_msec();
int32 deltaMilliSec = iParser->getLatestMediaDataTimestamp() - (uint32)iStartTime.to_msec();
if (deltaMilliSec <= 0) return 0;
BandwidthEstimationInfo *pBWEstInfo = iParser->getBandwidthEstimationInfo();
int32 deltaMilliSec1 = pBWEstInfo->iLatestMediaDataTimestamp - pBWEstInfo->iFirstMediaDataTsPerRequest;
if (deltaMilliSec1 <= 0) return 0;
OsclFloat downloadRate0 = ((OsclFloat)getDownloadSize() / (OsclFloat)deltaMilliSec0) * (OsclFloat)1000.0; // try to avoid overflow problem for 32-bit interger multiplication
OsclFloat downloadRate = ((OsclFloat)getDownloadSize() / (OsclFloat)deltaMilliSec) * (OsclFloat)1000.0; // try to avoid overflow problem for 32-bit interger multiplication
OsclFloat downloadRate1 = ((OsclFloat)(pBWEstInfo->iTotalSizePerRequest) /
(OsclFloat)deltaMilliSec1) * (OsclFloat)1000.0; // try to avoid overflow problem for 32-bit interger multiplication
LOGINFODATAPATH((0, "ProtocolState::getDownloadRate(), deltaMilliSec0=%d, downloadSize=%d, downloadRate0=%d",
deltaMilliSec0, getDownloadSize(), (uint32)downloadRate0));
LOGINFODATAPATH((0, "ProtocolState::getDownloadRate(), deltaMilliSec=%d, downloadSize=%d, downloadRate=%d, ",
deltaMilliSec, getDownloadSize(), (uint32)downloadRate));
LOGINFODATAPATH((0, "ProtocolState::getDownloadRate(), deltaMilliSec1=%d, downloadSize1=%d, downloadRate1=%d",
deltaMilliSec1, pBWEstInfo->iTotalSizePerRequest, (uint32)downloadRate1));
OSCL_UNUSED_ARG(downloadRate0);
OSCL_UNUSED_ARG(downloadRate); // remove warnings for unused variable
return (uint32)downloadRate1;
}
uint32 ProtocolState::getDownloadTimeForEstimation()
{
TimeValue currentTime;
currentTime.set_to_current_time();
TimeValue deltaTimeVal = currentTime - iStartTime;
return (uint32)deltaTimeVal.to_msec();
}
bool ProtocolState::setExtensionFields(Oscl_Vector<OSCL_HeapString<OsclMemAllocator>, OsclMemAllocator> &aExtensionHeaderKeys,
Oscl_Vector<OSCL_HeapString<OsclMemAllocator>, OsclMemAllocator> &aExtensionHeaderValues,
Oscl_Vector<uint32, OsclMemAllocator> &aMaskBitForHTTPMethod,
Oscl_Vector<bool, OsclMemAllocator> &aExtensionHeadersPurgeOnRedirect,
const HTTPMethod aMethod)
{
// no extension headers
if (aExtensionHeaderKeys.empty() || aExtensionHeaderValues.empty()) return true;
// number of extension field keys and values doesn't match
if (aExtensionHeaderKeys.size() != aExtensionHeaderValues.size()) return false;
if (aMaskBitForHTTPMethod.size() > 0 && (aMaskBitForHTTPMethod.size() != aExtensionHeaderKeys.size())) return false;
// mask bits for Http method
uint32 bitMask = getBitMaskForHttpMethod(aMaskBitForHTTPMethod, aMethod);
for (uint32 i = 0; i < aExtensionHeaderKeys.size(); i++)
{
StrCSumPtrLen fieldKey(aExtensionHeaderKeys[i].get_cstr(),
aExtensionHeaderKeys[i].get_size());
StrPtrLen fieldValue(aExtensionHeaderValues[i].get_cstr(),
aExtensionHeaderValues[i].get_size());
bool addExtensionHeader = true;
if (bitMask > 0) addExtensionHeader = ((aMaskBitForHTTPMethod[i] & bitMask) != 0);
if (iURI.isRedirectURI() && aExtensionHeadersPurgeOnRedirect[i]) addExtensionHeader = false; // for purge on redirect
if (addExtensionHeader && !iComposer->setField(fieldKey, &fieldValue)) return false;
}
return true;
}
uint32 ProtocolState::getBitMaskForHttpMethod(Oscl_Vector<uint32, OsclMemAllocator> &aMaskBitForHTTPMethod,
const HTTPMethod aMethod)
{
uint32 bitMask = 0;
if (!aMaskBitForHTTPMethod.empty())
{
if (aMethod == HTTP_METHOD_GET) bitMask = MASK_HTTPGET_EXTENSIONHEADER;
if (aMethod == HTTP_METHOD_POST) bitMask = MASK_HTTPPOST_EXTENSIONHEADER;
if (aMethod == HTTP_METHOD_HEAD) bitMask = MASK_HTTPHEAD_EXTENSIONHEADER;
}
return bitMask;
}
bool ProtocolState::constructAuthenHeader(OSCL_String &aUserID, OSCL_String &aPasswd)
{
if (aUserID.get_size() == 0 && aPasswd.get_size() == 0) return true; // empty user and authentication strings
// set user and authentication for HTTP basic authentication
const uint32 SIZE = 512;
char buf[SIZE + 1];
char *userID = (char*)aUserID.get_cstr();
char *passwd = (char*)aPasswd.get_cstr();
oscl_snprintf(buf, SIZE, "%s:%s", userID != NULL ? userID : "", passwd != NULL ? passwd : "");
char base64buf[(SIZE<<1)+1], *ptr = (char*)base64buf;
OSCL_FastString basicString(_STRLIT_CHAR("Basic "));
oscl_memcpy(ptr, basicString.get_cstr(), basicString.get_size());
ptr += basicString.get_size();
base64enc(buf, ptr);
StrCSumPtrLen auth = "Authorization";
return iComposer->setField(auth, base64buf);
}
// subroutine for base64 encoding (used in HTTP Basic authentification)
const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
int32 ProtocolState::base64enc(char *data, char *out)
{
int32 len = oscl_strlen(data);
int32 i, index;
int32 val;
for (i = 0, index = 0; i < len; i += 3, index += 4)
{
int32 quad = 0, trip = 0;
val = (0xff & data[i]);
val <<= 8;
if ((i + 1) < len)
{
val |= (0xff & data[i+1]);
trip = 1;
}
val <<= 8;
if ((i + 2) < len)
{
val |= (0xff & data[i+2]);
quad = 1;
}
out[index+3] = alphabet[(quad ? (val & 0x3f) : 64)];
val >>= 6;
out[index+2] = alphabet[(trip ? (val & 0x3f) : 64)];
val >>= 6;
out[index+1] = alphabet[val & 0x3f];
val >>= 6;
out[index+0] = alphabet[val & 0x3f];
}
//out[++index] = 0;
out[index] = 0;
return index;
}