blob: 004df5fa6dabcaa09293833428b781d767fd430f [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 2008 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 "http_composer.h"
#include "http_parcom_internal.h"
#include "string_keyvalue_store.h"
#include "oscl_string_utils.h"
#include "oscl_string_containers.h"
////////////////////////////////////////////////////////////////////////////////////
////// HTTPComposer implementation /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void HTTPComposer::reset(const bool aKeepAllSettingsExceptURI)
{
// reset URI
iURI.setPtrLen("", 0);
iHeaderLength = 0;
iFirstLineLength = 0;
iEntityBodyLength = 0;
if (aKeepAllSettingsExceptURI) return;
// reset other stuff
iMethod = HTTP_METHOD_GET;
iVersion = HTTP_V1_0;
if (iKeyValueStore) iKeyValueStore->clear();
}
////////////////////////////////////////////////////////////////////////////////////
// factory method
OSCL_EXPORT_REF HTTPComposer* HTTPComposer::create()
{
HTTPComposer *composer = OSCL_NEW(HTTPComposer, ());
if (!composer) return NULL;
if (!composer->construct())
{
OSCL_DELETE(composer);
return NULL;
}
return composer;
}
////////////////////////////////////////////////////////////////////////////////////
bool HTTPComposer::construct()
{
reset();
if ((iKeyValueStore = StringKeyValueStore::create()) == NULL) return false;
return true;
}
////////////////////////////////////////////////////////////////////////////////////
// destructor
OSCL_EXPORT_REF HTTPComposer::~HTTPComposer()
{
reset();
// delete iKeyValueStore
if (iKeyValueStore) OSCL_DELETE(iKeyValueStore);
iKeyValueStore = NULL;
}
////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void HTTPComposer::setURI(const StrPtrLen aURI)
{
iURI = aURI;
// get relative URI
char *aServerAddPtr = oscl_strstr(((char*)aURI.c_str()), "//");
if (!aServerAddPtr) return;
aServerAddPtr += 2;
char *aRelativeUriPtr = oscl_strstr(aServerAddPtr, "/");
if (!aRelativeUriPtr) return;
iRelativeURI = StrPtrLen(aRelativeUriPtr);
}
////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF bool HTTPComposer::setField(const StrCSumPtrLen &aNewFieldName, const char *aNewFieldValue, const bool aNeedReplaceOldValue)
{
if (!iKeyValueStore) return false;
if (aNewFieldValue)
{
return (iKeyValueStore->addKeyValuePair(aNewFieldName, aNewFieldValue, aNeedReplaceOldValue) ==
StringKeyValueStore::StringKeyValueStore_Success);
}
// remove this field
return iKeyValueStore->removeKeyValuePair(aNewFieldName);
}
////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF bool HTTPComposer::setField(const StrCSumPtrLen &aNewFieldName, const StrPtrLen *aNewFieldValue, const bool aNeedReplaceOldValue)
{
if (!iKeyValueStore) return false;
if (!aNewFieldValue) return iKeyValueStore->removeKeyValuePair(aNewFieldName);
return (iKeyValueStore->addKeyValuePair(aNewFieldName, *aNewFieldValue, aNeedReplaceOldValue) ==
StringKeyValueStore::StringKeyValueStore_Success);
}
////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF int32 HTTPComposer::getCurrentRequestLength(const bool usingAbsoluteURI)
{
// sanity check
if (!usingAbsoluteURI && iRelativeURI.length() == 0) return COMPOSE_BAD_URI;
// since iHeaderLength is updated for each setField, we only need to calculate the request-line length
// Request-line: Method SP Request-URI SP HTTP-Version CRLF
iFirstLineLength = oscl_strlen(HTTPMethodString[(uint32)iMethod]); // method
StrPtrLen *uriPtr = (usingAbsoluteURI ? (&iURI) : (&iRelativeURI));
iFirstLineLength += uriPtr->length(); // uri
iFirstLineLength += 8; // "HTTP/1.1" or "HTTP/1.0"
iFirstLineLength += 4; // 2 SPs + CRLF
iHeaderLength = iFirstLineLength;
// header fields part
iHeaderLength += (iKeyValueStore->getTotalKeyValueLength() +
iKeyValueStore->getNumberOfKeyValuePairs() * 4); // 4 => key:SPvalueCRLF, 1SP + ':' + CRLT
iHeaderLength += 2; // final CRLF
return (int32)(iHeaderLength + iEntityBodyLength);
}
////////////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF int32 HTTPComposer::compose(OsclMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI, const uint32 aEntityBodyLength)
{
HTTPMemoryFragment messageBuffer(aComposedMessageBuffer);
// sanity check, all the list error codes will pop up here if there is an error
int32 status = santityCheckForCompose(messageBuffer, usingAbsoluteURI, aEntityBodyLength);
if (status != COMPOSE_SUCCESS) return status;
iEntityBodyLength = aEntityBodyLength;
// compose the first request/status line
composeFirstLine(messageBuffer, usingAbsoluteURI);
// compose the header part: general header + request header + entity header
composeHeaders(messageBuffer);
// if there is an enity body, then it should be in place,
// then add NULL terminator for a string
if (messageBuffer.getAvailableSpace() > 0)
{
*((char*)messageBuffer.getPtr() + iEntityBodyLength) = HTTP_CHAR_NULL;
messageBuffer.update(1);
}
return COMPOSE_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////
// supporting function for compose()
int32 HTTPComposer::santityCheckForCompose(HTTPMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI, const uint32 aEntityBodyLength)
{
// check URI
if (!usingAbsoluteURI && iRelativeURI.length() == 0) return COMPOSE_BAD_URI;
// check input buffer size
if (!aComposedMessageBuffer.isSpaceEnough(getCurrentRequestLength(usingAbsoluteURI) + aEntityBodyLength))
{
return COMPOSE_BUFFER_TOO_SMALL;
}
// check URI
if (iURI.length() == 0) return COMPOSE_URI_NOT_SET;
// check content type and content length for entity body
if (aEntityBodyLength)
{
StrCSumPtrLen contentType("Content-Type");
if (!iKeyValueStore->isKeyValueAvailable(contentType)) // no "Content-Type" key
return COMPOSE_CONTENT_TYPE_NOT_SET_FOR_ENTITY_BODY;
StrCSumPtrLen contentLengthKey("Content-Length");
StrPtrLen contentLengthValue;
if (!iKeyValueStore->getValueByKey(contentLengthKey, contentLengthValue)) // no "Content-Length" key
return COMPOSE_CONTENT_LENGTH_NOT_SET_FOR_ENTITY_BODY;
uint32 contentLength;
PV_atoi(contentLengthValue.c_str(), 'd', contentLengthValue.length(), contentLength);
if (contentLength != aEntityBodyLength) return COMPOSE_CONTENT_LENGTH_NOT_MATCH_ENTITY_BODY_LENGTH;
}
return COMPOSE_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////
void HTTPComposer::composeFirstLine(HTTPMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI)
{
// Request-line: Method SP Request-URI SP HTTP-Version CRLF
char *ptr = (char *)aComposedMessageBuffer.getPtr();
oscl_memcpy(ptr, HTTPMethodString[(uint32)iMethod], oscl_strlen(HTTPMethodString[(uint32)iMethod]));
ptr += oscl_strlen(HTTPMethodString[iMethod]);
*ptr++ = HTTP_CHAR_SPACE;
StrPtrLen *uriPtr = (usingAbsoluteURI ? (&iURI) : (&iRelativeURI));
oscl_memcpy(ptr, uriPtr->c_str(), uriPtr->length());
ptr += uriPtr->length();
*ptr++ = HTTP_CHAR_SPACE;
// HTTP version
OSCL_FastString versionString;
if (iVersion == HTTP_V1_1)
{
versionString.set((OSCL_String::chartype*)_STRLIT_CHAR("HTTP/1.1"), 8);
}
else
{
versionString.set((OSCL_String::chartype*)_STRLIT_CHAR("HTTP/1.0"), 8);
}
oscl_memcpy(ptr, versionString.get_cstr(), 8);
ptr += 8;
*ptr++ = HTTP_CHAR_CR;
*ptr++ = HTTP_CHAR_LF;
OSCL_ASSERT(iFirstLineLength == (uint32)(ptr - (char*)aComposedMessageBuffer.getPtr()));
aComposedMessageBuffer.update(iFirstLineLength);
}
////////////////////////////////////////////////////////////////////////////////////
class CleanupObject
{
StrPtrLen *iKeyList;
StrPtrLen *iValueList;
public:
CleanupObject(StrPtrLen *aKeyList = NULL, StrPtrLen *aValueList = NULL) :
iKeyList(aKeyList), iValueList(aValueList)
{
;
}
//! Use destructor to do all the clean up work
~CleanupObject()
{
if (iKeyList) OSCL_ARRAY_DELETE(iKeyList);
if (iValueList) OSCL_ARRAY_DELETE(iValueList);
}
void setKeyList(StrPtrLen *aKeyList)
{
iKeyList = aKeyList;
}
void setValueList(StrPtrLen *aValueList)
{
iValueList = aValueList;
}
};
bool HTTPComposer::composeHeaders(HTTPMemoryFragment &aComposedMessageBuffer)
{
char *ptr = (char *)aComposedMessageBuffer.getPtr();
CleanupObject cleanup;
uint32 numKeyValuePairs = iKeyValueStore->getNumberOfKeyValuePairs();
if (numKeyValuePairs > 0)
{
StrPtrLen *keyList = OSCL_ARRAY_NEW(StrPtrLen, numKeyValuePairs);
cleanup.setKeyList(keyList);
StrPtrLen *valueList = OSCL_ARRAY_NEW(StrPtrLen, numKeyValuePairs);
cleanup.setValueList(valueList);
if (!keyList || !valueList) return false; // let cleanup object to do automatic deallocation
// get key list
uint32 numKeys = iKeyValueStore->getCurrentKeyList(keyList);
if (numKeys == 0) return false;
uint32 i, j, index = 0;
for (i = 0, j = 0; i < numKeyValuePairs && j < numKeys; j++)
{
index = 0;
while (iKeyValueStore->getValueByKey(keyList[j], valueList[i], index))
{
// put key
oscl_memcpy(ptr, keyList[j].c_str(), keyList[j].length()); // same field key
ptr += keyList[j].length();
*ptr++ = HTTP_CHAR_COLON;
*ptr++ = HTTP_CHAR_SPACE;
// put value
oscl_memcpy(ptr, valueList[i].c_str(), valueList[i].length()); // same field key
ptr += valueList[i].length();
*ptr++ = HTTP_CHAR_CR;
*ptr++ = HTTP_CHAR_LF;
index++;
}
i += index;
}
}
// add final CRLF
*ptr++ = HTTP_CHAR_CR;
*ptr++ = HTTP_CHAR_LF;
OSCL_ASSERT(iHeaderLength == (ptr - (char*)aComposedMessageBuffer.getPtr() + aComposedMessageBuffer.getLen()));
aComposedMessageBuffer.update(ptr);
return true;
}