Windows2003-3790/inetcore/wininet/http/chunk.cxx
2020-09-30 16:53:55 +02:00

728 lines
22 KiB
C++

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
chunk.cxx
Abstract:
Contains a generic chunked transfer implimentation
Author:
Arthur L Bierer (arthurbi) 03-May-1997
Revision History:
03-May-1997 arthurbi
Created
--*/
#include <wininetp.h>
inline
CHUNK_TOKEN
CHUNK_TRANSFER::GetToken(
IN OUT LPSTR *lplpInputBuffer,
IN LPSTR lpEndOfInputBuffer,
OUT LPDWORD lpdwValue,
IN DWORD dwExpectedTokenSize,
OUT LPDWORD lpdwBytesTokenized
)
/*++
Routine Description:
Lexes through a byte stream, seperating data into tokens. Data is special cased for efficency.
Arguments:
lplpInputBuffer - Pointer to Pointer of Buffer that should be lexed, on return contains
an offset where the next character to lex is.
lpEndofInputBuffer - Pointer to last character to passed in Buffer.
lpdwValue - On return, MAY contain numerical conversion of a text number (digit) token
dwExpectedTokenSize - Expected size of token, in all cases except for data should be 1
lpdwBytesTokenized - On return, contains size of token
Return Value:
CHUNK_TOKEN
Success - The Correct token.
Failure - CHUNK_TOKEN_INVALID
--*/
{
BOOL fFirstIteration = TRUE;
CHUNK_TOKEN ctToken = CHUNK_TOKEN_INVALID;
DEBUG_ENTER((DBG_HTTP,
Dword,
"CHUNK_TRANSFER::GetToken",
"%x [%x, %.10q], %x, %x, %u, %x",
lplpInputBuffer,
*lplpInputBuffer,
*lplpInputBuffer,
lpEndOfInputBuffer,
lpdwValue,
dwExpectedTokenSize,
lpdwBytesTokenized
));
*lpdwBytesTokenized = 0;
while ( *lplpInputBuffer < lpEndOfInputBuffer
&& *lpdwBytesTokenized < dwExpectedTokenSize )
{
//
// Set Default Token type
//
ctToken = CHUNK_TOKEN_DATA;
//
// Handle Other, "special" tokens, only if asked for by the parser.
//
if ( dwExpectedTokenSize == 1 )
{
if ( **lplpInputBuffer == '\r' )
{
ctToken = CHUNK_TOKEN_CR;
goto quit;
}
if ( **lplpInputBuffer == '\n' )
{
ctToken = CHUNK_TOKEN_LF;
goto quit;
}
if ( **lplpInputBuffer == ':' )
{
ctToken = CHUNK_TOKEN_COLON;
goto quit;
}
if ( **lplpInputBuffer >= '0' && **lplpInputBuffer <= '9' )
{
*lpdwValue = (DWORD) (**lplpInputBuffer - '0');
ctToken = CHUNK_TOKEN_DIGIT;
goto quit;
}
if ( **lplpInputBuffer >= 'A' && **lplpInputBuffer <= 'F' )
{
*lpdwValue = (DWORD) (**lplpInputBuffer - 'A') + 10;
ctToken = CHUNK_TOKEN_DIGIT;
goto quit;
}
if ( **lplpInputBuffer >= 'a' && **lplpInputBuffer <= 'f' )
{
*lpdwValue = (DWORD) (**lplpInputBuffer - 'a') + 10;
ctToken = CHUNK_TOKEN_DIGIT;
goto quit;
}
}
fFirstIteration = FALSE;
(*lplpInputBuffer)++;
(*lpdwBytesTokenized)++;
}
quit:
if (ctToken != CHUNK_TOKEN_DATA && ctToken != CHUNK_TOKEN_INVALID)
{
if ( !fFirstIteration)
{
ctToken = CHUNK_TOKEN_DATA;
}
else
{
//
// Advance past this token, since we've only
// lexed one token
//
(*lplpInputBuffer)++;
(*lpdwBytesTokenized)++;
}
}
DEBUG_PRINT(HTTP,
INFO,
("GetToken: %q, expected=%u, actual=%u\n",
InternetMapChunkToken(ctToken),
dwExpectedTokenSize,
*lpdwBytesTokenized
));
DEBUG_LEAVE((DWORD)ctToken);
return ctToken;
}
DWORD
CHUNK_TRANSFER::ParseChunkInput(
LPSTR lpInputBuffer,
DWORD dwInputBufferSize,
LPSTR *lplpInputBufferNew,
LPDWORD lpdwInputBufferNewSize
)
/*++
Routine Description:
Parses a buffer of an assumed chunked encoding byte stream. Seperates out data from header information.
Arguments:
lpInputBuffer - Pointer to buffer containing a stream of bytes to parse
dwInputBufferSize - size of byte lpInputBuffer
lplpInputBufferNew - Offset into passed in lpInputBuffer, (not used, yet)
lpdwInputBufferNewSize - On Return, cotains the size of lpInputBuffer ( data is compressed )
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
CHUNK_TOKEN ctToken;
DWORD dwValue;
DWORD dwExpectedTokenSize = 1;
DWORD dwActualTokenSize = 0;
LPSTR lpszEndOfInputBuffer = (LPSTR) (lpInputBuffer + dwInputBufferSize);
LPSTR lpszStartOfFirstDataBuffer = NULL;
DWORD dwFirstDataBufferSize = 0;
LPSTR lpszStartOfNextDataBuffer = NULL;
DWORD error = ERROR_SUCCESS;
DEBUG_ENTER((DBG_HTTP,
Dword,
"CHUNK_TRANSFER::ParseChunkInput",
"%x [%.10q], %u, %x, %x",
lpInputBuffer,
lpInputBuffer,
dwInputBufferSize,
lplpInputBufferNew,
lpdwInputBufferNewSize
));
*lplpInputBufferNew = lpInputBuffer;
while ( *lplpInputBufferNew < lpszEndOfInputBuffer)
{
//
// Calculate the max size of the token can be, only
// relevant for data, since all other tokens are assumed
// to be size of 1.
//
if (_csState == CHUNK_STATE_DATA_PARSE)
{
dwExpectedTokenSize = (_dwChunkDataSize - _dwChunkDataRead);
lpszStartOfNextDataBuffer = *lplpInputBufferNew;
}
else
{
dwExpectedTokenSize = 1;
}
DEBUG_PRINT(HTTP,
INFO,
("ParseChunk: %q, %u/%u\n",
InternetMapChunkState(_csState),
_dwChunkDataRead,
_dwChunkDataSize
));
//
// Lex through the byte stream looking for our next token.
//
ctToken = GetToken( lplpInputBufferNew,
lpszEndOfInputBuffer,
&dwValue,
dwExpectedTokenSize,
&dwActualTokenSize
);
if ( ctToken == CHUNK_TOKEN_INVALID )
{
//
// Need more data to parse...
//
error = ERROR_SUCCESS;
goto quit;
}
//
// Based on our current state, evalulate the token,
// and figure out what to do next.
//
switch ( _csState )
{
case CHUNK_STATE_START:
ResetSubStateInfo();
if ( ctToken != CHUNK_TOKEN_DIGIT )
{
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for NOT %q\n",
InternetMapChunkToken(ctToken),
InternetMapChunkToken(CHUNK_TOKEN_DIGIT)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
SetState(CHUNK_STATE_SIZE_PARSE);
// otherwise fall through
case CHUNK_STATE_SIZE_PARSE:
switch ( ctToken )
{
case CHUNK_TOKEN_DIGIT:
_dwCalculatedChunkSize *= BASE_HEX;
_dwCalculatedChunkSize += dwValue;
break;
case CHUNK_TOKEN_CR:
_dwCr++;
// fall through
case CHUNK_TOKEN_DATA:
case CHUNK_TOKEN_COLON:
_dwChunkDataSize = _dwCalculatedChunkSize;
_dwChunkDataRead = 0;
DEBUG_PRINT(HTTP,
INFO,
("ChunkParse-GotChunksize: size=%u, %x\n",
_dwCalculatedChunkSize,
_dwCalculatedChunkSize
));
if (ctToken == CHUNK_TOKEN_CR)
{
SetState(CHUNK_STATE_SIZE_CRLF);
}
else
{
SetState(CHUNK_STATE_EXT_PARSE);
}
break;
default:
// ERROR
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_EXT_PARSE:
switch ( ctToken )
{
case CHUNK_TOKEN_CR:
_dwCr++;
SetState(CHUNK_STATE_SIZE_CRLF);
break;
case CHUNK_TOKEN_DIGIT:
case CHUNK_TOKEN_DATA:
case CHUNK_TOKEN_COLON:
break;
default:
// ERROR
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_SIZE_CRLF:
switch ( ctToken )
{
case CHUNK_TOKEN_LF:
_dwLf++;
if ( IsCrLf() )
{
ClearCrLf();
if ( _dwCalculatedChunkSize == 0 )
{
SetState(CHUNK_STATE_ZERO_FOOTER);
}
else
{
SetState(CHUNK_STATE_DATA_PARSE);
}
}
else
{
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, But CRLF not matched, CR=%u, LF=%u\n",
InternetMapChunkToken(ctToken),
_dwCr,
_dwLf
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for %q\n",
InternetMapChunkToken(ctToken),
InternetMapChunkToken(CHUNK_TOKEN_LF)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_DATA_PARSE:
switch ( ctToken )
{
case CHUNK_TOKEN_CR:
case CHUNK_TOKEN_LF:
case CHUNK_TOKEN_DATA:
case CHUNK_TOKEN_DIGIT:
case CHUNK_TOKEN_COLON:
//
// If this is the first piece of data we receive
// than save it off, so we know the start of the
// buffer we are returning as data.
//
if ( lpszStartOfFirstDataBuffer == NULL )
{
lpszStartOfFirstDataBuffer = lpInputBuffer;
}
//
// If this is not the first block of data in the passed in buffer,
// we must move the block of data OVER any Chunked-tranfer header
// information.
//
if ( (lpszStartOfFirstDataBuffer+dwFirstDataBufferSize) != lpszStartOfNextDataBuffer )
{
MoveMemory((LPVOID) (lpszStartOfFirstDataBuffer+dwFirstDataBufferSize), // Dest
(LPVOID) lpszStartOfNextDataBuffer, // Source
dwActualTokenSize // size
);
}
//
// Update the size of data we've parsed out, and
// check to see if we've completely received all data.
//
dwFirstDataBufferSize += dwActualTokenSize;
_dwChunkDataRead += dwActualTokenSize;
if ( _dwChunkDataRead == _dwChunkDataSize )
{
SetState(CHUNK_STATE_DATA_CRLF);
}
INET_ASSERT(_dwChunkDataRead <= _dwChunkDataSize);
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for CR,LF,DATA,DIGIT,COLON\n",
InternetMapChunkToken(ctToken)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_DATA_CRLF:
switch (ctToken)
{
case CHUNK_TOKEN_CR:
_dwCr++;
break;
case CHUNK_TOKEN_LF:
_dwLf++;
if ( IsCrLf() )
{
ClearCrLf();
SetState(CHUNK_STATE_START);
}
else
{
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, BUT CRLF not matched, CR=%u, LF=%u\n",
InternetMapChunkToken(ctToken),
_dwCr,
_dwLf
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for CR or LF (CHUNK DATA SIZE INCORRECT??)\n",
InternetMapChunkToken(ctToken)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_ZERO_FOOTER:
switch (ctToken)
{
case CHUNK_TOKEN_CR:
_dwCr++;
SetState(CHUNK_STATE_ZERO_FOOTER_FINAL_CRLF);
break;
case CHUNK_TOKEN_DATA:
case CHUNK_TOKEN_DIGIT:
case CHUNK_TOKEN_COLON:
SetState(CHUNK_STATE_ZERO_FOOTER_NAME);
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for DATA or CR\n",
InternetMapChunkToken(ctToken)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_ZERO_FOOTER_NAME:
switch (ctToken)
{
case CHUNK_TOKEN_DATA:
case CHUNK_TOKEN_DIGIT:
break;
case CHUNK_TOKEN_COLON:
SetState(CHUNK_STATE_ZERO_FOOTER_VALUE);
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for DATA, DIGIT, COLON\n",
InternetMapChunkToken(ctToken)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_ZERO_FOOTER_VALUE:
switch (ctToken)
{
case CHUNK_TOKEN_DATA:
case CHUNK_TOKEN_DIGIT:
break;
case CHUNK_TOKEN_CR:
_dwCr++;
SetState(CHUNK_STATE_ZERO_FOOTER_CRLF);
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for DATA, DIGIT, CR\n",
InternetMapChunkToken(ctToken)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_ZERO_FOOTER_CRLF:
case CHUNK_STATE_ZERO_FOOTER_FINAL_CRLF:
switch ( ctToken )
{
case CHUNK_TOKEN_LF:
_dwLf++;
if ( IsCrLf() )
{
ClearCrLf();
if ( _csState == CHUNK_STATE_ZERO_FOOTER_CRLF)
{
SetState(CHUNK_STATE_ZERO_FOOTER);
}
else
{
INET_ASSERT( _csState == CHUNK_STATE_ZERO_FOOTER_FINAL_CRLF);
//Done?
SetState(CHUNK_STATE_FINISHED);
DEBUG_PRINT(HTTP,
INFO,
("EOF chunk\n"
));
error = ERROR_SUCCESS;
goto quit;
}
}
else
{
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, But CRLF not matched, CR=%u, LF=%u\n",
InternetMapChunkToken(ctToken),
_dwCr,
_dwLf
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
default:
DEBUG_PRINT(HTTP,
INFO,
("-->CHUNK err: Got %q, while looking for LF\n",
InternetMapChunkToken(ctToken)
));
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
break;
case CHUNK_STATE_FINISHED:
INET_ASSERT(FALSE);
error = ERROR_SUCCESS;
goto quit;
default:
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
}
quit:
if ( error == ERROR_SUCCESS)
{
if ( dwFirstDataBufferSize > 0 )
{
INET_ASSERT(lpInputBuffer == lpszStartOfFirstDataBuffer);
}
*lplpInputBufferNew = lpInputBuffer;
*lpdwInputBufferNewSize = dwFirstDataBufferSize;
}
DEBUG_LEAVE(error);
return error;
}