コード例 #1
0
XBOX::VError VHTTPMessage::ReadFromStream (XBOX::VStream& inStream, const XBOX::VString& inBoundary)
{
#define	MAX_BUFFER_LENGTH	32768

	const char			HTTP_CR = '\r';
	const char			HTTP_LF = '\n';
	const char			HTTP_CRLF [] = { HTTP_CR, HTTP_LF, 0 };
	const char			HTTP_CRLFCRLF [] = { HTTP_CR, HTTP_LF, HTTP_CR, HTTP_LF, 0 };

	XBOX::VError		streamError = XBOX::VE_OK;
	HTTPParsingState	parsingState;
	XBOX::VError		parsingError = XBOX::VE_OK;
	XBOX::VSize			bufferSize = MAX_BUFFER_LENGTH;
	char *				buffer = (char *)XBOX::vMalloc (bufferSize, 0);
	XBOX::VSize			bufferOffset = 0;
	XBOX::VSize			unreadBytes = 0;
	sLONG				lineLen = 0;
	XBOX::VString		header;
	XBOX::VString		value;
	char *				startLinePtr = NULL;
	char *				endLinePtr = NULL;
	char *				endHeaderPtr = NULL;
	sLONG				endLineSize = sizeof (HTTP_CRLF) - 1;
	sLONG				contentLength = 0;
	void *				bodyContentBuffer = NULL;
	XBOX::VSize			bodyContentSize = 0;
	const sLONG			MAX_REQUEST_ENTITY_SIZE = XBOX::MaxLongInt;
	XBOX::VString		boundaryEnd;
	char *				boundary = NULL;
	bool				stopReadingStream = false;

	if (!inBoundary.IsEmpty())
	{
		boundaryEnd.AppendString ("--");
		boundaryEnd.AppendString (inBoundary);
		boundary = new char[boundaryEnd.GetLength() + 1];
		if (NULL != boundary)
			boundaryEnd.ToCString (boundary, boundaryEnd.GetLength() + 1);
	}

	if (NULL == buffer)
		return XBOX::VE_MEMORY_FULL;

	parsingState = PS_ReadingHeaders;

	XBOX::StErrorContextInstaller errorContext (XBOX::VE_STREAM_EOF, XBOX::VE_OK);

	bool isAlreadyReading = inStream.IsReading();

	if (!isAlreadyReading)
		streamError = inStream.OpenReading();

	while ((XBOX::VE_OK == streamError) && !stopReadingStream)
	{
		if (0 == unreadBytes)
			bufferOffset = 0;

		bufferSize = MAX_BUFFER_LENGTH - bufferOffset;

		streamError = inStream.GetData (buffer + bufferOffset, &bufferSize);

		unreadBytes = (bufferSize + bufferOffset);
		bufferOffset = 0;

		while ((unreadBytes > 0) && (XBOX::VE_OK == parsingError))
		{
			if (parsingState <= PS_ReadingHeaders)
			{
				startLinePtr = buffer + bufferOffset;
				endLinePtr = strstr (startLinePtr, HTTP_CRLF);

				if ((NULL != endLinePtr) && (NULL == endHeaderPtr))
					endHeaderPtr = strstr (startLinePtr, HTTP_CRLFCRLF);
			}

			/* Start to parse the Status-Line */
			switch (parsingState)
			{
			case PS_ReadingHeaders:
				{
					if (NULL != endLinePtr)
					{
						if (startLinePtr != (endHeaderPtr + endLineSize))
						{
							if (_ExtractHeaderValuePair (startLinePtr, endLinePtr, header, value))
							{
								GetHeaders().SetHeaderValue (header, value, false);
							}
						}

						else /*if (startLinePtr == endHeaderPtr)*/
						{
							parsingState = PS_ReadingBody;
							XBOX::VString contentLengthString;
							if (GetHeaders().GetHeaderValue (STRING_HEADER_CONTENT_LENGTH, contentLengthString))
								contentLength = HTTPTools::GetLongFromString (contentLengthString);
						}
					}
					break;
				}

			case PS_ReadingBody:
				{
					if (!boundaryEnd.IsEmpty())
					{
						if (NULL != boundary)
						{
							char *endBoundaryPtr = memstr (buffer + bufferOffset, unreadBytes, boundary, strlen (boundary));
							if (NULL != endBoundaryPtr)
							{
								XBOX::VSize nbBytesToCopy  = (endBoundaryPtr - (buffer + bufferOffset));
								inStream.UngetData (endBoundaryPtr, unreadBytes - nbBytesToCopy);
								unreadBytes = nbBytesToCopy;
								if (NULL != memstr (endBoundaryPtr - 2, 2, HTTP_CRLF, 2))
									unreadBytes -= 2;	// Skip CRLF after boundary part
								stopReadingStream = true;
							}
						}
					}

					if (NULL == bodyContentBuffer)
					{
						// There's no Content-Length field in header
						if (0 == contentLength)
						{
							bodyContentBuffer = XBOX::vMalloc (bufferSize, 0);
							bodyContentSize = 0;
						}
						// There's one Content-Length, just check it match limits
						else if ((contentLength > 0) && (contentLength < MAX_REQUEST_ENTITY_SIZE))
						{
							bodyContentBuffer = XBOX::vMalloc (contentLength, 0);
							bodyContentSize = 0;
						}
					}

					if ((NULL != bodyContentBuffer) && (bodyContentSize + unreadBytes < MAX_REQUEST_ENTITY_SIZE))
					{
						XBOX::VSize nbBytesToCopy = unreadBytes;
						if (bodyContentSize + nbBytesToCopy > contentLength)
							bodyContentBuffer = XBOX::vRealloc (bodyContentBuffer, bodyContentSize + nbBytesToCopy);

						memcpy ((char *)(bodyContentBuffer) + bodyContentSize, buffer + bufferOffset, unreadBytes);
						bodyContentSize += unreadBytes;
						bufferOffset = unreadBytes = 0;
					}
					else
					{
						parsingError = XBOX::VE_MEMORY_FULL;

						if (NULL != bodyContentBuffer)
						{
							XBOX::vFree (bodyContentBuffer);
							bodyContentBuffer = NULL;
						}
					}
					break;
				}
			}

			if (XBOX::VE_OK != parsingError)
				break;

			if (NULL != endLinePtr)
			{
				lineLen = (endLinePtr - startLinePtr) + endLineSize; // to skip CRLF;
				bufferOffset += lineLen;
				unreadBytes -= lineLen;
				endLinePtr = NULL;
			}
			else
			{
				if (bufferOffset > 0)
				{
					memmove (buffer, buffer + bufferOffset, unreadBytes);
					buffer[unreadBytes] = 0;
				}
				bufferOffset = unreadBytes;
				break;
			}
		}

		if (XBOX::VE_OK != parsingError)
			break;
	}

	if (!isAlreadyReading)
		inStream.CloseReading();

	if (XBOX::VE_STREAM_EOF == streamError)
		streamError = XBOX::VE_OK;

	if (!parsingError && !streamError)
	{
		if (NULL != bodyContentBuffer)
		{
#if VERSIONDEBUG
			if ((contentLength > 0) && (bodyContentSize != contentLength))
				assert (false);
#endif
			GetBody().SetDataPtr (bodyContentBuffer, bodyContentSize);

			XBOX::VString	contentType;
			XBOX::CharSet	charSet = XBOX::VTC_UNKNOWN;

			/* Set VStream charset according to content-type header other else set default charset to UTF-8 */
			if ((!GetHeaders().GetContentType (contentType, &charSet)) || (XBOX::VTC_UNKNOWN == charSet))
				charSet = XBOX::VTC_UTF_8;
			GetBody().SetCharSet (charSet);
		}

		parsingState = PS_ParsingFinished;
	}
	else
	{
		if (NULL != bodyContentBuffer)
			XBOX::vFree (bodyContentBuffer);
	}

	delete [] boundary;
	boundary = NULL;

	XBOX::vFree (buffer);
	buffer = NULL;

	return streamError;

#undef MAX_BUFFER_LENGTH
}