745 lines
24 KiB
C++
745 lines
24 KiB
C++
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
response.cxx
|
||
|
||
Abstract:
|
||
|
||
This file contains the HTTP Request Handle Object ReceiveResponse method
|
||
|
||
Contents:
|
||
CFsm_ReceiveResponse::RunSM
|
||
HTTP_REQUEST_HANDLE_OBJECT::ReceiveResponse_Fsm
|
||
|
||
Author:
|
||
|
||
Keith Moore (keithmo) 16-Nov-1994
|
||
|
||
Revision History:
|
||
|
||
29-Apr-97 rfirth
|
||
Conversion to FSM
|
||
|
||
--*/
|
||
|
||
#include <wininetp.h>
|
||
#include <perfdiag.hxx>
|
||
#include "httpp.h"
|
||
|
||
//
|
||
// private manifests
|
||
//
|
||
|
||
#define DEFAULT_RESPONSE_BUFFER_LENGTH (1 K)
|
||
|
||
//
|
||
// HTTP Request Handle Object methods
|
||
//
|
||
|
||
|
||
DWORD
|
||
CFsm_ReceiveResponse::RunSM(
|
||
IN CFsm * Fsm
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
Fsm -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_HTTP,
|
||
Dword,
|
||
"CFsm_ReceiveResponse::RunSM",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
CFsm_ReceiveResponse * stateMachine = (CFsm_ReceiveResponse *)Fsm;
|
||
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
|
||
DWORD error;
|
||
|
||
START_SENDREQ_PERF();
|
||
|
||
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
|
||
|
||
switch (Fsm->GetState()) {
|
||
case FSM_STATE_INIT:
|
||
case FSM_STATE_CONTINUE:
|
||
error = pRequest->ReceiveResponse_Fsm(stateMachine);
|
||
break;
|
||
|
||
default:
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
break;
|
||
}
|
||
|
||
STOP_SENDREQ_PERF();
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
HTTP_REQUEST_HANDLE_OBJECT::ReceiveResponse_Fsm(
|
||
IN CFsm_ReceiveResponse * Fsm
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
Fsm -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
#if INET_DEBUG
|
||
//#define RLF_TEST_CODE
|
||
#ifdef RLF_TEST_CODE
|
||
|
||
//
|
||
// single 100 response
|
||
//
|
||
|
||
#define TEST_HEADER_0 "HTTP/1.1 100 Continue\r\n" \
|
||
"\r\n"
|
||
|
||
//
|
||
// single 100 header
|
||
//
|
||
|
||
#define TEST_HEADER_1 "HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"\r\n"
|
||
|
||
//
|
||
// continue header with moderate amount of data
|
||
//
|
||
|
||
#define TEST_HEADER_2 "HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"Content-Length: 128\r\n" \
|
||
"Content-Type: octet/shmoctet\r\n" \
|
||
"\r\n" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef"
|
||
|
||
//
|
||
// continue header seen from apache server
|
||
//
|
||
|
||
#define TEST_HEADER_3 "HTTP/1.1 100 Continue\r\n" \
|
||
"\r\n" \
|
||
"\n\n\n\n\n"
|
||
|
||
//
|
||
// multiple continue headers, no data
|
||
//
|
||
|
||
#define TEST_HEADER_4 "HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"\r\n" \
|
||
"HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"\r\n" \
|
||
"HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"\r\n" \
|
||
"HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"\r\n"
|
||
|
||
//
|
||
// single 100 response, preceeded by preamble and containing a chunked response
|
||
//
|
||
|
||
#define TEST_HEADER_5 "!!!! this is a pre-amble, should be ignored even though it includes HTTP !!!!" \
|
||
" " \
|
||
"HTTP/1.1 100 Go ahead punk, make my day\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"Transfer-Encoding: chunked\r\n" \
|
||
"\r\n" \
|
||
"0010 this is the first chunk (16 bytes)\r\n" \
|
||
"0123456789abcdef" \
|
||
"\r\n" \
|
||
" 10; this is the second chunk (16 bytes)\r\n" \
|
||
"0123456789abcdef" \
|
||
"\r\n" \
|
||
"00F3\r\n" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"0123456789abcdef" \
|
||
"012" \
|
||
"\r\n" \
|
||
"0000; the final chunk\r\n" \
|
||
"\r\n" \
|
||
"Entity-Header: this is the chunk footer\r\n" \
|
||
"\r\n"
|
||
|
||
//
|
||
// enpty chunk encoded response with empty footer
|
||
//
|
||
|
||
#define TEST_HEADER_6 "HTTP/1.1 100 Continue\r\n" \
|
||
"Server: Richard's Test-Case Virtual Server/1.0\r\n" \
|
||
"Date: Mon, 01 Apr 2000 00:00:01 GMT\r\n" \
|
||
"Transfer-Encoding: chunked\r\n" \
|
||
"\r\n" \
|
||
"0\r\n" \
|
||
"\r\n" \
|
||
"\r\n"
|
||
|
||
const struct {LPSTR ptr; DWORD len;} test_cases[] = {
|
||
TEST_HEADER_0, sizeof(TEST_HEADER_0) - 1,
|
||
TEST_HEADER_1, sizeof(TEST_HEADER_1) - 1,
|
||
TEST_HEADER_2, sizeof(TEST_HEADER_2) - 1,
|
||
TEST_HEADER_3, sizeof(TEST_HEADER_3) - 1,
|
||
TEST_HEADER_4, sizeof(TEST_HEADER_4) - 1,
|
||
TEST_HEADER_5, sizeof(TEST_HEADER_5) - 1,
|
||
TEST_HEADER_6, sizeof(TEST_HEADER_6) - 1
|
||
};
|
||
DWORD test_index = 99;
|
||
|
||
#endif // def RLF_TEST_CODE
|
||
#endif // INET_DEBUG
|
||
|
||
DEBUG_ENTER((DBG_HTTP,
|
||
Dword,
|
||
"HTTP_REQUEST_HANDLE_OBJECT::ReceiveResponse_Fsm",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
PERF_ENTER(ReceiveResponse_Fsm);
|
||
|
||
CFsm_ReceiveResponse & fsm = *Fsm;
|
||
DWORD error = fsm.GetError();
|
||
FSM_STATE state = fsm.GetState();
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
if (error == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) {
|
||
|
||
if ((_Socket != NULL) && _Socket->IsSecure())
|
||
{
|
||
if(m_pSecurityInfo)
|
||
{
|
||
/* SCLE ref */
|
||
m_pSecurityInfo->Release();
|
||
}
|
||
/* SCLE ref */
|
||
m_pSecurityInfo = ((ICSecureSocket *)_Socket)->GetSecurityEntry();
|
||
}
|
||
|
||
SetState(HttpRequestStateOpen);
|
||
CloseConnection(TRUE);
|
||
fsm.SetDone();
|
||
goto quit2;
|
||
}
|
||
|
||
goto quit;
|
||
}
|
||
if (state != FSM_STATE_INIT) {
|
||
state = fsm.GetFunctionState();
|
||
}
|
||
do {
|
||
switch (state) {
|
||
case FSM_STATE_INIT:
|
||
if (_ResponseBuffer == NULL) {
|
||
_ResponseBufferLength = DEFAULT_RESPONSE_BUFFER_LENGTH;
|
||
_ResponseBuffer = (LPBYTE)ALLOCATE_MEMORY(LMEM_FIXED,
|
||
_ResponseBufferLength);
|
||
if (_ResponseBuffer == NULL) {
|
||
_ResponseBufferLength = 0;
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
INET_ASSERT(_BytesReceived == 0);
|
||
|
||
fsm.m_dwResponseLeft = _ResponseBufferLength;
|
||
state = FSM_STATE_2;
|
||
|
||
//
|
||
// fall through
|
||
//
|
||
|
||
#ifdef RLF_TEST_CODE
|
||
|
||
InternetGetDebugVariable("WininetTestIndex", &test_index);
|
||
if (test_index < ARRAY_ELEMENTS(test_cases)) {
|
||
_BytesReceived = test_cases[test_index].len;
|
||
memcpy(_ResponseBuffer, test_cases[test_index].ptr, _BytesReceived);
|
||
fsm.m_dwResponseLeft = _ResponseBufferLength - _BytesReceived;
|
||
}
|
||
|
||
#endif // def RLF_TEST_CODE
|
||
|
||
case FSM_STATE_2:
|
||
|
||
//
|
||
// we will allow Receive() to expand the buffer (and therefore initially
|
||
// allocate it), and to compress the buffer if we receive the end of the
|
||
// connection. It is up to UpdateResponseHeaders() to figure out when
|
||
// enough data has been read to indicate end of the headers
|
||
//
|
||
|
||
fsm.SetFunctionState(FSM_STATE_3);
|
||
|
||
INET_ASSERT(_Socket != NULL);
|
||
|
||
if (_Socket != NULL) {
|
||
error = _Socket->Receive((LPVOID *)&_ResponseBuffer,
|
||
&_ResponseBufferLength,
|
||
&fsm.m_dwResponseLeft,
|
||
&_BytesReceived,
|
||
0,
|
||
SF_EXPAND
|
||
| SF_COMPRESS
|
||
| SF_INDICATE,
|
||
&fsm.m_bEofResponseHeaders
|
||
);
|
||
if (error == ERROR_IO_PENDING) {
|
||
goto quit;
|
||
}
|
||
} else {
|
||
error = ERROR_WINHTTP_OPERATION_CANCELLED;
|
||
}
|
||
|
||
//
|
||
// fall through
|
||
//
|
||
|
||
case FSM_STATE_3:
|
||
|
||
//
|
||
// if we are using a keep-alive connection that was previously timed-out
|
||
// by the server, we may not find out about it until now
|
||
//
|
||
// Note: it seems we can get a zero length response at this point also,
|
||
// which I take to mean that the server-side socket has been closed
|
||
//
|
||
|
||
INET_ASSERT(_BytesReceived <= _ResponseBufferLength);
|
||
|
||
if ((error != ERROR_SUCCESS)
|
||
|| ((_BytesReceived == 0) && IsKeepAlive())) {
|
||
|
||
//
|
||
// We need to reset the state if we got a
|
||
// certificate request.
|
||
//
|
||
|
||
if (error == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) {
|
||
|
||
if ((_Socket != NULL) && _Socket->IsSecure())
|
||
{
|
||
if(m_pSecurityInfo)
|
||
{
|
||
/* SCLE ref */
|
||
m_pSecurityInfo->Release();
|
||
}
|
||
/* SCLE ref */
|
||
m_pSecurityInfo = ((ICSecureSocket *)_Socket)->GetSecurityEntry();
|
||
}
|
||
|
||
SetState(HttpRequestStateOpen);
|
||
}
|
||
CloseConnection(TRUE);
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// if we received no data then the server has closed the connection
|
||
// already
|
||
//
|
||
|
||
if (_BytesReceived != 0) {
|
||
|
||
BOOL bHaveFinalResponse;
|
||
|
||
do {
|
||
bHaveFinalResponse = TRUE;
|
||
error = UpdateResponseHeaders(&fsm.m_bEofResponseHeaders);
|
||
//if (!(rand() % 7)) {
|
||
// error = ERROR_HTTP_INVALID_SERVER_RESPONSE;
|
||
//}
|
||
if (error != ERROR_SUCCESS) {
|
||
//dprintf("UpdateResponseHeaders() returns %d\n", error);
|
||
break;
|
||
}
|
||
|
||
DWORD statusCode;
|
||
|
||
statusCode = GetStatusCode();
|
||
|
||
//
|
||
// receive next packet if we didn't get a status code yet
|
||
//
|
||
|
||
if (statusCode == 0) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// discard any 1xx responses and get the headers again
|
||
//
|
||
|
||
if (fsm.m_bEofResponseHeaders
|
||
&& (statusCode >= HTTP_STATUS_CONTINUE)
|
||
&& (statusCode < HTTP_STATUS_OK)) {
|
||
bHaveFinalResponse = FALSE;
|
||
fsm.SetFunctionState(FSM_STATE_4);
|
||
|
||
//
|
||
// get any data that came with the header
|
||
//
|
||
|
||
fsm.m_bDrained = FALSE;
|
||
if (IsContentLength() && (_BytesInSocket != 0)) {
|
||
error = DrainResponse(&fsm.m_bDrained);
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// fall through
|
||
//
|
||
|
||
case FSM_STATE_4:
|
||
|
||
//
|
||
// now that we have drained the socket, we can indicate
|
||
// the response to the app. This gives apps chance to
|
||
// perform progress reporting for each 100 response
|
||
// received, e.g.
|
||
//
|
||
DWORD dwStatusCode = statusCode;
|
||
InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE,
|
||
&dwStatusCode,
|
||
sizeof(dwStatusCode)
|
||
);
|
||
|
||
//
|
||
// if there is no more data left in the buffer then we
|
||
// can receive the next response at the start of the
|
||
// buffer, else continue from where the previous one
|
||
// ended
|
||
//
|
||
|
||
if (fsm.m_bDrained || !IsBufferedData()) {
|
||
fsm.m_dwResponseLeft = _ResponseBufferLength;
|
||
_BytesReceived = 0;
|
||
_DataOffset = 0;
|
||
_ResponseScanned = 0;
|
||
} else {
|
||
_ResponseScanned = _DataOffset;
|
||
if (IsContentLength()) {
|
||
_ResponseScanned += _ContentLength;
|
||
}
|
||
}
|
||
_ResponseHeaders.FreeHeaders();
|
||
_ResponseHeaders.Initialize();
|
||
ZapFlags();
|
||
_ContentLength = 0;
|
||
_BytesRemaining = 0;
|
||
_BytesInSocket = 0;
|
||
fsm.m_bEofResponseHeaders = FALSE;
|
||
if (_DataOffset == 0) {
|
||
|
||
//
|
||
// need to read next response - nothing left in
|
||
// buffer
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
// If we have a server authentication context
|
||
// and the response is anything but 401, mark
|
||
// the socket as authenticated.
|
||
AUTHCTX *pAuthCtx;
|
||
pAuthCtx = GetAuthCtx();
|
||
if (pAuthCtx && !pAuthCtx->_fIsProxy
|
||
&& (statusCode != HTTP_STATUS_DENIED))
|
||
{
|
||
|
||
#define MICROSOFT_IIS_SERVER_SZ "Microsoft-IIS/"
|
||
#define MICROSOFT_PWS_SERVER_SZ "Microsoft-PWS/"
|
||
|
||
#define MICROSOFT_IIS_SERVER_LEN (sizeof(MICROSOFT_IIS_SERVER_SZ) - 1)
|
||
#define MICROSOFT_PWS_SERVER_LEN (sizeof(MICROSOFT_PWS_SERVER_SZ) - 1)
|
||
|
||
LPSTR pszBuf;
|
||
DWORD cbBuf;
|
||
cbBuf = MAX_PATH;
|
||
if (FastQueryResponseHeader(HTTP_QUERY_SERVER,
|
||
(LPVOID*) &pszBuf, &cbBuf, 0) == ERROR_SUCCESS)
|
||
{
|
||
if (cbBuf >= MICROSOFT_IIS_SERVER_LEN
|
||
&& (!strncmp(pszBuf, MICROSOFT_IIS_SERVER_SZ, MICROSOFT_IIS_SERVER_LEN)
|
||
|| !strncmp(pszBuf, MICROSOFT_PWS_SERVER_SZ, MICROSOFT_PWS_SERVER_LEN)))
|
||
|
||
{
|
||
// Found an IIS header. Mark socket as authenticated if
|
||
// IIS 1, 2 or 3. Lengths of both strings are same.
|
||
CHAR *pVer = pszBuf + MICROSOFT_IIS_SERVER_LEN;
|
||
if (*pVer == '1'
|
||
|| *pVer == '2'
|
||
|| *pVer == '3'
|
||
)
|
||
{
|
||
// IIS 1, 2 or 3 - mark dirty.
|
||
_Socket->SetAuthenticated();
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Unknown server; may be IIS 1,2 or 3.
|
||
_Socket->SetAuthenticated();
|
||
}
|
||
}
|
||
|
||
} while (!bHaveFinalResponse);
|
||
} else {
|
||
error = ERROR_HTTP_INVALID_SERVER_RESPONSE;
|
||
}
|
||
|
||
//
|
||
// set state to perform next receive
|
||
//
|
||
|
||
state = FSM_STATE_2;
|
||
}
|
||
} while ((error == ERROR_SUCCESS) && !fsm.m_bEofResponseHeaders);
|
||
|
||
//
|
||
// we should update the RTT as soon as we get received data from
|
||
// the socket, but then we'd have to store the RTT in the socket
|
||
// object or access this one, etc. Just keep it here for now -
|
||
// its a reasonable approximation in the normal IE case: not too
|
||
// much time spent in callbacks etc.
|
||
//
|
||
|
||
UpdateRTT();
|
||
//dprintf("RTT for %s = %d\n", GetURL(), GetRTT());
|
||
//dprintf("OS = %s, PS = %s\n", ((GetOriginServer() != NULL) ? GetOriginServer()->GetHostName() : "none"),
|
||
// ((GetServerInfo() != NULL) ? GetServerInfo()->GetHostName() : "none"));
|
||
|
||
//
|
||
// we have received the headers and possibly some (or all) of the data. The
|
||
// app can now query the headers and receive the data
|
||
//
|
||
|
||
SetState(HttpRequestStateObjectData);
|
||
|
||
//
|
||
// record the amount of data immediately available to the app
|
||
//
|
||
if ( IsChunkEncoding() )
|
||
{
|
||
DWORD dwChunkBytesRead = 0;
|
||
DWORD dwChunkBytesWritten = 0;
|
||
|
||
error = _ResponseFilterList.Decode(
|
||
(LPBYTE) BufferedDataStart(),
|
||
BufferedDataLength(),
|
||
NULL,
|
||
NULL,
|
||
&dwChunkBytesRead,
|
||
&dwChunkBytesWritten
|
||
);
|
||
|
||
_ResponseBufferDataReadyToRead = dwChunkBytesWritten;
|
||
|
||
INET_ASSERT(error == ERROR_SUCCESS);
|
||
if ( error != ERROR_SUCCESS )
|
||
{
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
SetAvailableDataLength(BufferDataAvailToRead());
|
||
|
||
//
|
||
// IIS caches authentication credentials on keep-alive sockets.
|
||
//
|
||
|
||
if (_Socket) {
|
||
|
||
if (IsAuthorized()) {
|
||
_Socket->SetAuthorized();
|
||
}
|
||
}
|
||
|
||
quit:
|
||
|
||
if (error != ERROR_IO_PENDING) {
|
||
fsm.SetDone();
|
||
|
||
//
|
||
// if we got the socket from the keep-alive pool, but found no keep-
|
||
// alive header then we no longer have a keep-alive connection
|
||
//
|
||
|
||
if (_bKeepAliveConnection && !IsKeepAlive()) {
|
||
//dprintf("*** %s - NO LONGER K-A socket %#x\n", GetURL(), _Socket->GetSocket());
|
||
SetNoLongerKeepAlive();
|
||
}
|
||
|
||
//
|
||
// don't maintain the connection if there's no more data to read. UNLESS
|
||
// we are in the middle of establishing an authenticated connection
|
||
// (implies using keep-alive connection, e.g. NTLM)
|
||
// IsData() returns FALSE if there's no data at all, otherwise we
|
||
// check to see if we have read all the data already (i.e. with the
|
||
// response headers)
|
||
//
|
||
|
||
if ((error != ERROR_SUCCESS)
|
||
|| (
|
||
|
||
//
|
||
// data-less response (ignoring keep-alive & content-length)
|
||
//
|
||
|
||
(!IsData()
|
||
|
||
//
|
||
// all data body in header buffer
|
||
//
|
||
|
||
|| (IsKeepAlive()
|
||
&& IsContentLength()
|
||
&& (BufferedDataLength() == GetContentLength())
|
||
)
|
||
)
|
||
|
||
//
|
||
// but only if not in the middle of auth negotiation and if the
|
||
// connection hasn't been dropped by the server
|
||
//
|
||
|
||
&& ((GetAuthState() != AUTHSTATE_NEGOTIATE)
|
||
|| IsNoLongerKeepAlive())
|
||
)
|
||
) {
|
||
|
||
//dprintf("socket %#x [%#x/%d] error=%d, IsData()=%B, K-A=%B, C-L=%d, BDL=%d, AS=%d\n",
|
||
// _Socket,
|
||
// _Socket ? _Socket->GetSocket() : 0,
|
||
// _Socket ? _Socket->GetSourcePort() : 0,
|
||
// error,
|
||
// IsData(),
|
||
// IsKeepAlive(),
|
||
// GetContentLength(),
|
||
// BufferedDataLength(),
|
||
// GetAuthState()
|
||
// );
|
||
|
||
//
|
||
// BUGBUG - if this is a new keep-alive connection?
|
||
//
|
||
|
||
DEBUG_PRINT(HTTP,
|
||
INFO,
|
||
("closing: error = %d, IsData() = %B, K-A = %B, IsC-L = %B, BDL = %d, C-L = %d\n",
|
||
error,
|
||
IsData(),
|
||
IsKeepAlive(),
|
||
IsContentLength(),
|
||
BufferedDataLength(),
|
||
GetContentLength()
|
||
));
|
||
|
||
if(GetStatusCode() != HTTP_STATUS_REDIRECT || (HTTP_METHOD_TYPE_HEAD == GetMethodType()))
|
||
CloseConnection((error != ERROR_SUCCESS) ? TRUE : FALSE);
|
||
else
|
||
DEBUG_PRINT(HTTP, INFO, ("Not closing socket, Status code = %d \n", GetStatusCode()));
|
||
|
||
//
|
||
// set the relevant state
|
||
//
|
||
|
||
if (error != ERROR_SUCCESS &&
|
||
(error != ERROR_WINHTTP_SECURE_FAILURE ||
|
||
GetStatusFlags() & ~(WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA |
|
||
WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID |
|
||
WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID |
|
||
WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR |
|
||
WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED |
|
||
WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)) &&
|
||
error != ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED )
|
||
{
|
||
SetState(HttpRequestStateError);
|
||
}
|
||
}
|
||
|
||
PERF_LEAVE(ReceiveResponse_Fsm);
|
||
}
|
||
|
||
quit2:
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|