Skip to content

Commit

Permalink
fix #121: parsing of chunked transfer encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
tjanczuk committed Dec 16, 2011
1 parent 337a0f2 commit a304bda
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 83 deletions.
107 changes: 107 additions & 0 deletions src/iisnode/chttpprotocol.cpp
Expand Up @@ -270,6 +270,113 @@ HRESULT CHttpProtocol::ParseResponseStatusLine(CNodeHttpStoredContext* context)
return hr;
}

HRESULT CHttpProtocol::ParseChunkHeader(CNodeHttpStoredContext* context)
{
HRESULT hr;

char* data = (char*)context->GetBuffer() + context->GetParsingOffset();
char* current;
char* chunkHeaderStart;
DWORD dataSize = context->GetDataSize() - context->GetParsingOffset();
ULONG chunkLength = 0;
ULONG totalChunkLength = 0;

// attempt to parse as many response body chunks as there are buffered in memory

current = data;
do
{
// parse chunk length

chunkHeaderStart = current;
chunkLength = 0;
while (true)
{
ErrorIf((current - data) >= dataSize, ERROR_MORE_DATA);
if (*current >= 'A' && *current <= 'F')
{
chunkLength <<= 4;
chunkLength += *current - 'A' + 10;
}
else if (*current >= 'a' && *current <= 'f')
{
chunkLength <<= 4;
chunkLength += *current - 'a' + 10;
}
else if (*current >= '0' && *current <= '9')
{
chunkLength <<= 4;
chunkLength += *current - '0';
}
else
{
ErrorIf(current == chunkHeaderStart, ERROR_BAD_FORMAT); // no hex digits found
break;
}

current++;
}

// skip optional extensions

while (true)
{
ErrorIf((current - data) >= dataSize, ERROR_MORE_DATA);
if (*current == 0x0D)
{
break;
}

current++;
}

// LF

current++;
ErrorIf((current - data) >= dataSize, ERROR_MORE_DATA);
ErrorIf(*current != 0x0A, ERROR_BAD_FORMAT);
current++;

// remember total length of all parsed chunks before attempting to parse subsequent chunk header

// set total chunk length to include current chunk content length, previously parsed chunks (with headers),
// plus the CRLF following the current chunk content
totalChunkLength = chunkLength + (ULONG)(current - data) + 2;
current += chunkLength + 2; // chunk content length + CRLF

} while (chunkLength != 0); // exit when last chunk has been detected

// if we are here, current buffer contains the header of the last chunk of the response

context->SetChunkLength(totalChunkLength);
context->SetIsLastChunk(TRUE);
context->SetChunkTransmitted(0);

return S_OK;

Error:

if (ERROR_MORE_DATA != hr)
{
context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(
L"iisnode failed to parse response body chunk header", WINEVENT_LEVEL_ERROR, context->GetActivityId());

return hr;
}
else if (0 < totalChunkLength)
{
// at least one response chunk has been successfuly parsed, but more chunks remain

context->SetChunkLength(totalChunkLength);
context->SetIsLastChunk(FALSE);
context->SetChunkTransmitted(0);

return S_OK;
}

return hr;
}

HRESULT CHttpProtocol::ParseResponseHeaders(CNodeHttpStoredContext* context)
{
HRESULT hr;
Expand Down
1 change: 1 addition & 0 deletions src/iisnode/chttpprotocol.h
Expand Up @@ -15,6 +15,7 @@ class CHttpProtocol
static HRESULT SerializeRequestHeaders(CNodeHttpStoredContext* ctx, void** result, DWORD* resultSize, DWORD* resultLength);
static HRESULT ParseResponseStatusLine(CNodeHttpStoredContext* context);
static HRESULT ParseResponseHeaders(CNodeHttpStoredContext* context);
static HRESULT ParseChunkHeader(CNodeHttpStoredContext* context);
};

#endif
38 changes: 29 additions & 9 deletions src/iisnode/cnodehttpstoredcontext.cpp
Expand Up @@ -2,7 +2,7 @@

CNodeHttpStoredContext::CNodeHttpStoredContext(CNodeApplication* nodeApplication, IHttpContext* context)
: nodeApplication(nodeApplication), context(context), process(NULL), buffer(NULL), bufferSize(0), dataSize(0), parsingOffset(0),
responseContentLength(0), responseContentTransmitted(0), pipe(INVALID_HANDLE_VALUE), result(S_OK),
chunkLength(0), chunkTransmitted(0), isChunked(FALSE), pipe(INVALID_HANDLE_VALUE), result(S_OK), isLastChunk(FALSE),
requestNotificationStatus(RQ_NOTIFICATION_PENDING), connectionRetryCount(0), pendingAsyncOperationCount(1),
targetUrl(NULL), targetUrlLength(0), childContext(NULL)
{
Expand Down Expand Up @@ -153,24 +153,44 @@ void CNodeHttpStoredContext::SetParsingOffset(DWORD parsingOffset)
this->parsingOffset = parsingOffset;
}

LONGLONG CNodeHttpStoredContext::GetResponseContentTransmitted()
LONGLONG CNodeHttpStoredContext::GetChunkTransmitted()
{
return this->responseContentTransmitted;
return this->chunkTransmitted;
}

LONGLONG CNodeHttpStoredContext::GetResponseContentLength()
LONGLONG CNodeHttpStoredContext::GetChunkLength()
{
return this->responseContentLength;
return this->chunkLength;
}

void CNodeHttpStoredContext::SetResponseContentTransmitted(LONGLONG length)
void CNodeHttpStoredContext::SetChunkTransmitted(LONGLONG length)
{
this->responseContentTransmitted = length;
this->chunkTransmitted = length;
}

void CNodeHttpStoredContext::SetResponseContentLength(LONGLONG length)
void CNodeHttpStoredContext::SetChunkLength(LONGLONG length)
{
this->responseContentLength = length;
this->chunkLength = length;
}

BOOL CNodeHttpStoredContext::GetIsChunked()
{
return this->isChunked;
}

void CNodeHttpStoredContext::SetIsChunked(BOOL chunked)
{
this->isChunked = chunked;
}

void CNodeHttpStoredContext::SetIsLastChunk(BOOL lastChunk)
{
this->isLastChunk = lastChunk;
}

BOOL CNodeHttpStoredContext::GetIsLastChunk()
{
return this->isLastChunk;
}

HRESULT CNodeHttpStoredContext::GetHresult()
Expand Down
18 changes: 12 additions & 6 deletions src/iisnode/cnodehttpstoredcontext.h
Expand Up @@ -19,14 +19,16 @@ class CNodeHttpStoredContext : public IHttpStoredContext
DWORD bufferSize;
DWORD dataSize;
DWORD parsingOffset;
LONGLONG responseContentTransmitted;
LONGLONG responseContentLength;
LONGLONG chunkTransmitted;
LONGLONG chunkLength;
BOOL isChunked;
HRESULT result;
REQUEST_NOTIFICATION_STATUS requestNotificationStatus;
long pendingAsyncOperationCount;
PCSTR targetUrl;
DWORD targetUrlLength;
IHttpContext* childContext;
BOOL isLastChunk;

public:

Expand All @@ -47,8 +49,11 @@ class CNodeHttpStoredContext : public IHttpStoredContext
DWORD* GetBufferSizeRef();
DWORD GetDataSize();
DWORD GetParsingOffset();
LONGLONG GetResponseContentTransmitted();
LONGLONG GetResponseContentLength();
LONGLONG GetChunkTransmitted();
LONGLONG GetChunkLength();
BOOL GetIsChunked();
void SetIsLastChunk(BOOL lastChunk);
BOOL GetIsLastChunk();
HRESULT GetHresult();
REQUEST_NOTIFICATION_STATUS GetRequestNotificationStatus();
GUID* GetActivityId();
Expand All @@ -66,8 +71,9 @@ class CNodeHttpStoredContext : public IHttpStoredContext
void SetBufferSize(DWORD bufferSize);
void SetDataSize(DWORD dataSize);
void SetParsingOffset(DWORD parsingOffet);
void SetResponseContentTransmitted(LONGLONG length);
void SetResponseContentLength(LONGLONG length);
void SetChunkTransmitted(LONGLONG length);
void SetChunkLength(LONGLONG length);
void SetIsChunked(BOOL chunked);
void SetHresult(HRESULT result);
void SetRequestNotificationStatus(REQUEST_NOTIFICATION_STATUS status);
LPOVERLAPPED InitializeOverlapped();
Expand Down

0 comments on commit a304bda

Please sign in to comment.