You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
728 lines
22 KiB
728 lines
22 KiB
/*++
|
|
|
|
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;
|
|
}
|