Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3023 lines
89 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
send.cxx
Abstract:
This file contains the implementation of the HttpSendRequestA API.
Contents:
HTTP_REQUEST_HANDLE_OBJECT::InitBeginSendRequest
HTTP_REQUEST_HANDLE_OBJECT::CheckClientRequestHeaders
CFsm_HttpSendRequest::RunSM
HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start
HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Finish
HTTP_REQUEST_HANDLE_OBJECT::UpdateProxyInfo
HTTP_REQUEST_HANDLE_OBJECT::FindConnCloseRequestHeader
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"
#include "autodial.h"
//
// HTTP Request Handle Object methods
//
DWORD
HTTP_REQUEST_HANDLE_OBJECT::InitBeginSendRequest(
IN LPCSTR lpszHeaders OPTIONAL,
IN DWORD dwHeadersLength,
IN LPVOID *lplpOptional,
IN LPDWORD lpdwOptionalLength,
IN DWORD dwOptionalLengthTotal,
OUT LPBOOL pfGoneOffline
)
/*++
Routine Description:
Performs Initiatization of the HTTP Request by setting up the necessary
headers and preparing the POST data.
Arguments:
lpszHeaders - Additional headers to be appended to the request.
This may be NULL if there are no additional
headers to append
dwHeadersLength - The length (in characters) of the additional
headers. If this is -1L and lpszAdditional is
non-NULL, then lpszAdditional is assumed to be
zero terminated (ASCIIZ)
lpOptionalData - Any optional data to send immediately after the
request headers. This is typically used for POST
operations. This may be NULL if there is no
optional data to send
dwOptionalDataLength - The length (in BYTEs) of the optional data. This
may be zero if there is no optional data to send
dwOptionalLengthTotal - Total Length for File Upload.
pfGoneOffline - Pointer to BOOL that is set to TRUE if durning the course
of this call, the connection was forced offline
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - One of the Win32 Error values.
Comments:
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::InitBeginSendRequest",
"%#x, %d, %d, %#x",
lplpOptional ? *lplpOptional : NULL,
lpdwOptionalLength ? *lpdwOptionalLength : NULL,
dwOptionalLengthTotal,
pfGoneOffline
));
DWORD error = ERROR_SUCCESS;
LPVOID lpOptional = *lplpOptional;
DWORD dwOptionalLength = *lpdwOptionalLength;
INET_ASSERT(pfGoneOffline);
*pfGoneOffline = FALSE;
//
// validate parameters
//
if ((lpOptional == NULL) || (dwOptionalLength == 0)) {
lpOptional = NULL;
dwOptionalLength = 0;
}
//
// the headers lengths can be -1 meaning that we should calculate the
// string lengths. We must do this before calling MakeAsyncRequest()
// which is expecting the parameters to be correct
//
__try {
if (dwHeadersLength == -1) {
dwHeadersLength = lstrlen((LPCSTR)lpszHeaders);
} else {
//
// probe headers
//
for (DWORD i = 0; i < dwHeadersLength; ++i) {
volatile char ch = lpszHeaders[i];
}
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
error = ERROR_INVALID_PARAMETER;
}
ENDEXCEPT
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// if the caller specified some additional headers, then add them before
// we make the request asynchronously
//
if (ARGUMENT_PRESENT(lpszHeaders) && (*lpszHeaders != '\0')) {
//
// we use the API here because the headers came from the app, and
// we don't trust it
//
if (!HttpAddRequestHeaders(GetPseudoHandle(),
lpszHeaders,
dwHeadersLength,
//
// if the object is being re-used then
// replace the headers to avoid
// duplicating original headers
//
IS_VALID_HTTP_STATE(this, REUSE, TRUE)
? ( HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD ): 0
//? HTTP_ADDREQ_FLAG_REPLACE : 0
)) {
error = GetLastError();
goto quit;
}
}
if (lpOptional && !GetSecondaryCacheKey()) {
//
// IE3 compat: set expiration on cache entry, if any, for this URL.
//
ExpireUrl();
}
//
// if we are in offline mode or we have transitioned to offline mode
// AND the caller didn't request RELOAD then allow only non-data GET
// requests
//
BOOL bDisconnected;
bDisconnected = IsOffline();
BOOL bOfflineMode;
bOfflineMode = (((GetInternetOpenFlags() | GetCacheFlags())
& INTERNET_FLAG_OFFLINE)
|| (bDisconnected));
if( bOfflineMode && _fIgnoreOffline )
bOfflineMode = FALSE;
if ( bOfflineMode )
{
*pfGoneOffline = TRUE;
if ( !GetSecondaryCacheKey()
&& ( (lpOptional != NULL)
|| (GetMethodType() != HTTP_METHOD_TYPE_GET))) {
error = ERROR_FILE_NOT_FOUND;
goto quit;
}
}
CheckClientRequestHeaders();
//
// if there's no data to send then if we're offline then we attempt to
// retrieve the file from cache
//
if ((lpOptional == NULL && dwOptionalLengthTotal == 0) || GetSecondaryCacheKey()) {
if (bOfflineMode) {
error = FHttpBeginCacheRetrieval(FALSE, TRUE);
if (error == ERROR_SUCCESS) {
error = ERROR_INTERNET_CACHE_SUCCESS;
goto quit;
}
else{
goto quit;
}
} else {
error = GetFromCachePreNetIO();
//
// if we succeeded in getting the file from the cache or we're
// disconnected then we're done
//
if ((error == ERROR_SUCCESS) || bDisconnected)
{
if ( error == ERROR_SUCCESS )
{
error = ERROR_INTERNET_CACHE_SUCCESS;
}
goto quit;
}
else if (error == ERROR_INTERNET_INSERT_CDROM)
{
// Check if it's ok to return this error. We do this if the
// client has indicated it allows ERROR_INTERNET_INSERT_CDROM
// via InternetSetOption and an insert cdrom dialog is not active.
if (GetErrorMask() & INTERNET_ERROR_MASK_INSERT_CDROM)
{
if (!(BOOL) InterlockedExchange((LONG*) &fCdromDialogActive, (LONG) TRUE))
goto quit;
}
}
}
}
//
// If we fall through then we are connected and a) either the thing
// is not in the cache or we did a conditional get or c) there was
// some cache error
//
error = ERROR_SUCCESS;
//
// if the app supplied a user-agent string to InternetOpen() AND hasn't
// added a "User-Agent:" header, then add it
//
LPSTR userAgent;
DWORD userAgentLength;
userAgent = GetUserAgent(&userAgentLength);
if (userAgent != NULL) {
ReplaceRequestHeader(HTTP_QUERY_USER_AGENT,
userAgent,
userAgentLength,
0, // dwIndex,
ADD_HEADER_IF_NEW
);
}
//
// do the same thing with the "Host:" header. The header-value is the host
// name supplied to InternetConnect() (or the name of the redirected host)
//
LPSTR hostName;
DWORD hostNameLength;
INTERNET_PORT hostPort;
hostName = GetHostName(&hostNameLength);
hostPort = GetHostPort();
INET_ASSERT((hostName != NULL) && (hostNameLength > 0));
char hostValue[INTERNET_MAX_HOST_NAME_LENGTH + sizeof(":4294967295")];
if ((hostPort != INTERNET_DEFAULT_HTTP_PORT)
&& (hostPort != INTERNET_DEFAULT_HTTPS_PORT)) {
if (lstrlen(hostName) > INTERNET_MAX_HOST_NAME_LENGTH)
{
error = ERROR_INVALID_PARAMETER;
goto quit;
}
hostNameLength = wsprintf(hostValue, "%s:%d", hostName, (hostPort & 0xffff));
hostName = hostValue;
}
ReplaceRequestHeader(HTTP_QUERY_HOST,
hostName,
hostNameLength,
0, // dwIndex,
ADD_HEADER_IF_NEW
);
//
// if the app requested keep-alive then add the header; if we're going via
// proxy then use the proxy-connection header
//
//if (pRequest->GetOpenFlags() & INTERNET_FLAG_KEEP_CONNECTION) {
// pRequest->SetWantKeepAlive(TRUE);
//}
//
// add the content-length header IF we are sending data OR this is a POST,
// AND ONLY if the app has not already added the header
//
if (dwOptionalLength || dwOptionalLengthTotal)
SetMethodBody();
if (((dwOptionalLength != 0) || (dwOptionalLengthTotal != 0))
//
// BUGBUG - just comparing against a method type is insufficient. We need
// a test of whether the method implies sending data (PUT, etc).
// We make the same test in other places
//
|| (GetMethodType() != HTTP_METHOD_TYPE_GET)) {
DWORD dwContentLength;
char number[sizeof("4294967295")];
//
// For File Upload we need to add the Content-Length
// header off of the Total Length, Not the current
// data size. Since we get more data via InternetWriteFile
//
if ( dwOptionalLengthTotal != 0 )
{
dwContentLength = dwOptionalLengthTotal;
}
else
{
dwContentLength = dwOptionalLength;
}
// _itoa(dwOptionalLength, number, 10);
wsprintf(number, "%d", dwContentLength);
DWORD numberLength = lstrlen(number);
/*----------------------------------------------------------------------
#62953 NOTE -- Authstate can never be in the AUTHSTATE_NEGOTIATE
state here. It is not necessary to zero out the content length
header here when omitting post data on NTLM negotiate since this
will be done later in the request. The commented-out code is not
necessary.
if ((GetMethodType() == HTTP_METHOD_TYPE_POST)
&& (GetAuthState() == AUTHSTATE_NEGOTIATE))
{
ReplaceRequestHeader(HTTP_QUERY_CONTENT_LENGTH,
"0",
1,
0, // dwIndex
ADD_HEADER
);
}
---------------------------------------------------------------------*/
// Normally we don't over-write the content-length
// header if one already exists.
DWORD dwAddHeader;
dwAddHeader = ADD_HEADER_IF_NEW;
// But if we're posting data and have an auth ctx
// over-write the content-length header which will
// have been reset to 0 to omit post data on the
// negotiate phase.
AUTHCTX *pAuthCtx;
pAuthCtx = GetAuthCtx();
if (pAuthCtx)
{
dwAddHeader = ADD_HEADER;
}
ReplaceRequestHeader(HTTP_QUERY_CONTENT_LENGTH,
(LPSTR)number,
numberLength,
0, // dwIndex
dwAddHeader
);
}
quit:
*lplpOptional = lpOptional;
*lpdwOptionalLength = dwOptionalLength;
DEBUG_LEAVE(error);
return error;
}
void
HTTP_REQUEST_HANDLE_OBJECT::CheckClientRequestHeaders(
VOID
)
/*++
Routine Description:
description-of-function.
Arguments:
None.
Return Value:
None.
--*/
{
if (IsFirstSendProcessed())
{
return; // been there, done that
}
SetFirstSendProcessed();
//
// record the initial URL for offline mapping in case of redirects
//
SetOriginalUrl(GetCacheKey());
//
// Disabling cache if OFFLINE flag not set and RELOAD flag set.
//
if ( !((GetInternetOpenFlags() | GetCacheFlags()) & INTERNET_FLAG_OFFLINE)
&& GetCacheFlags() & INTERNET_FLAG_RELOAD
)
{
SetCacheReadDisabled();
return;
}
//
// See if the client added any headers that imply caching disabled.
//
_RequestHeaders.LockHeaders();
DWORD Headers[] =
{
HTTP_QUERY_IF_MODIFIED_SINCE,
HTTP_QUERY_IF_NONE_MATCH,
HTTP_QUERY_RANGE
};
DWORD i;
for (i=0; i<ARRAY_ELEMENTS(Headers); i++)
{
if (IsRequestHeaderPresent(Headers[i]))
{
SetCacheReadDisabled();
break;
}
}
_RequestHeaders.UnlockHeaders();
}
DWORD
CFsm_HttpSendRequest::RunSM(
IN CFsm * Fsm
)
/*++
Routine Description:
description-of-function.
Arguments:
Fsm -
Return Value:
DWORD
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"CFsm_HttpSendRequest::RunSM",
"%#x",
Fsm
));
DWORD error;
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
START_SENDREQ_PERF();
CFsm_HttpSendRequest * stateMachine = (CFsm_HttpSendRequest *)Fsm;
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
switch (Fsm->GetState()) {
case FSM_STATE_INIT:
//
// Make sure appropriate connection and proxy is in place
//
FixProxySettingsForCurrentConnection(FALSE);
//
// fall through to FSM_STATE_CONTINUE
//
case FSM_STATE_CONTINUE:
//CHECK_FSM_OWNED(Fsm);
error = pRequest->HttpSendRequest_Start(stateMachine);
break;
case FSM_STATE_FINISH:
//CHECK_FSM_OWNED(Fsm);
error = pRequest->HttpSendRequest_Finish(stateMachine);
break;
case FSM_STATE_ERROR:
//CHECK_FSM_OWNED(Fsm);
error = Fsm->GetError();
//
// If we block to call GetProxyInfo async, then
// we may get unblocked during a cancel. We need to
// handle it by freeing the object in our destructor.
//
INET_ASSERT( (!stateMachine->m_fOwnsProxyInfoQueryObj) ?
( error == ERROR_INTERNET_OPERATION_CANCELLED ||
error == ERROR_INTERNET_TIMEOUT ) :
TRUE );
Fsm->SetDone();
break;
default:
//CHECK_FSM_OWNED(Fsm);
stateMachine->m_fOwnsProxyInfoQueryObj = TRUE;
error = ERROR_INTERNET_INTERNAL_ERROR;
Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR);
INET_ASSERT(FALSE);
break;
}
DEBUG_LEAVE(error);
STOP_SENDREQ_PERF();
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start(
IN CFsm_HttpSendRequest * Fsm
)
/*++
Routine Description:
Calls SendData() method in a loop, handling redirects (& authentications?)
until we have successfully started to retrieve what was originally requested
Arguments:
Fsm - HTTP send request FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Operation completed successfully
ERROR_IO_PENDING
Operation will complete asynchronously
Failure - ERROR_INTERNET_INCORRECT_HANDLE_STATE
The HTTP request handle is in the wrong state for this
request
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start",
"%#x",
Fsm
));
PERF_ENTER(HttpSendRequest_Start);
//CHECK_FSM_OWNED(Fsm);
CFsm_HttpSendRequest & fsm = *Fsm;
//
// we must loop here while the server redirects us or while we authenticate
// the user
//
FSM_STATE state = fsm.GetState();
DWORD error = fsm.GetError();
//
// We set m_fOwnsProxyInfoObj TRUE, because by virtue of being here
// we have know that we now own the pointer in our fsm, pointed to by
// fsm.m_pProxyInfoQuery.
//
// This boolean is used to know when we are allowed to FREE and ACCESS
// this pointer. If FALSE, we CANNOT touch this pointer because
// the auto-proxy thread may be accessing it. The auto-proxy thread
// will unblock us, this releasing its "un-offical" lock on this pointer.
//
fsm.m_fOwnsProxyInfoQueryObj = TRUE;
if (state == FSM_STATE_INIT)
{
if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST )
{
state = FSM_STATE_5;
fsm.SetFunctionState(FSM_STATE_5);
}
}
else
{
state = fsm.GetFunctionState();
}
retry_send_request:
do {
switch (state) {
case FSM_STATE_INIT:
case FSM_STATE_1:
//CHECK_FSM_OWNED(Fsm);
if (IsHttp1_1())
{
fsm.m_iRetries = (GlobalMaxConnectionsPerServer + 1);
}
else
{
fsm.m_iRetries = (GlobalMaxConnectionsPer1_0Server + 1);
}
fsm.m_bAuthNotFinished = FALSE;
fsm.m_dwCookieIndex = 0;
//
// Terrible bug that afflicts NS servers while doing SSL,
// they lie (those buggers), and claim they do keep-alive,
// but when we attempt to reuse their Keep-Alive sockets,
// they all fail, so we therefore must increase the retry count
// so we can empty all the bad keep-alive sockets out of the pool
//
if ( (GetOpenFlags() & INTERNET_FLAG_SECURE) )
{
CServerInfo * pServerInfo = GetServerInfo();
if ( pServerInfo && pServerInfo->IsBadNSServer() )
{
fsm.m_iRetries = 5;
}
// Also, purge all keep-alives and cached client auth cert chains
// if the user flushed the SSL state via inetcpl, including the
// one for the request object...if being reused.
if (RefreshSslState())
SetCertContextArray(NULL);
}
//
// if we're not in the right state to send, drain the socket
//
if (!IsValidHttpState(SEND)) {
#define DRAIN_SOCKET_BUFFER_LENGTH (1 K)
if (fsm.m_pBuffer == NULL) {
fsm.m_pBuffer = (LPVOID)ALLOCATE_MEMORY(
LMEM_FIXED,
DRAIN_SOCKET_BUFFER_LENGTH);
if (fsm.m_pBuffer == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
goto quit;
}
}
do {
if (!fsm.m_bSink) {
fsm.m_bSink = TRUE;
fsm.SetFunctionState(FSM_STATE_2);
error = ReadData(fsm.m_pBuffer,
DRAIN_SOCKET_BUFFER_LENGTH,
&fsm.m_dwBytesDrained,
TRUE,
0);
if (error == ERROR_IO_PENDING) {
goto quit;
}
}
//
// fall through to state 2
//
case FSM_STATE_2:
fsm.m_bSink = FALSE;
} while ((error == ERROR_SUCCESS) && (fsm.m_dwBytesDrained != 0));
if (error != ERROR_SUCCESS) {
goto quit;
}
if (fsm.m_pBuffer != NULL) {
fsm.m_pBuffer = (LPVOID)FREE_MEMORY(fsm.m_pBuffer);
INET_ASSERT(fsm.m_pBuffer == NULL);
}
ReuseObject();
INET_ASSERT(!IsData());
INET_ASSERT(IS_VALID_HTTP_STATE(this, SEND, TRUE));
//
// BUGBUG - if we're not in the right state?
//
}
//
// generate the correct request headers based
// on what types, or whether we're using
// proxies
//
fsm.SetFunctionState(FSM_STATE_3);
error = UpdateProxyInfo(Fsm, FALSE);
if (error == ERROR_IO_PENDING) {
goto done;
}
//
// set function state to not-FSM_STATE_3 to differentiate interrupted
// path in FSM_STATE_3
//
fsm.SetFunctionState(FSM_STATE_BAD);
//
// fall through
//
case FSM_STATE_3:
if (error != ERROR_SUCCESS)
{
fsm.m_bCancelRedoOfProxy = TRUE;
goto quit;
}
else if (fsm.GetFunctionState() == FSM_STATE_3)
{
BOOL fChangedFsmVariable = TRUE;
if (! fsm.m_bCancelRedoOfProxy)
{
fsm.m_bCancelRedoOfProxy = TRUE;
fChangedFsmVariable = TRUE;
}
error = UpdateProxyInfo(Fsm, TRUE);
if (error != ERROR_SUCCESS)
{
goto quit;
}
else if (fChangedFsmVariable)
{
fsm.m_bCancelRedoOfProxy = FALSE;
}
}
fsm.SetFunctionState(FSM_STATE_4);
case FSM_STATE_4:
//
// get any cookies required for this site, but only if app didn't
// tell us it will handle cookies
//
if (!(GetOpenFlags() & INTERNET_FLAG_NO_COOKIES) ) {
fsm.SetBlocking(TRUE);
int cCookie = 0;
error = CreateCookieHeaderIfNeeded(&cCookie);
if (cCookie) {
SetPerUserItem(TRUE);
}
fsm.SetBlocking(FALSE);
if(error != ERROR_SUCCESS) {
goto quit;
}
}
//
// if this URL requires authentication then add its header here, but
// only if the app didn't tell us not to
//
if (!(GetOpenFlags() & INTERNET_FLAG_NO_AUTH))
{
SetPPAbort(FALSE); // let's assume Passport is not going to abort the send.
error = AuthOnRequest(this);
if (error != ERROR_SUCCESS)
{
goto quit;
}
if (PPAbort())
{
// Passport needed to abort the send cuz the DA wanted to redirect
// the App to an different site *AND* the app wanted to handle the
// redirect itself.
error = ERROR_INTERNET_LOGIN_FAILURE;
goto quit;
}
error = HttpFiltOnRequest(this);
}
try_again:
fsm.SetFunctionState(FSM_STATE_5);
DEBUG_PRINT(HTTP, INFO, ("State_4_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
error = DoFsm(new CFsm_SendRequest(fsm.m_lpOptional,
fsm.m_dwOptionalLength,
this
));
if (error == ERROR_IO_PENDING) {
goto quit;
}
//
// fall through
//
case FSM_STATE_5:
DEBUG_PRINT(HTTP, INFO, ("State_5_start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
if (error == ERROR_INTERNET_OFFLINE) {
goto offline_check;
} else if (((GetOpenFlags() & INTERNET_FLAG_CACHE_IF_NET_FAIL)
&& ((error == ERROR_INTERNET_CANNOT_CONNECT)
|| (error == ERROR_INTERNET_CONNECTION_RESET)))
|| ((error == ERROR_INTERNET_TIMEOUT)
&& IsFromCacheTimeoutSet()
&& CanRetrieveFromCache(FALSE))) {
//CHECK_FSM_OWNED(Fsm);
//if (IsFromCacheTimeoutSet() && IsInCache()) {
// dprintf("%q timed out on connect/send - getting from cache\n", GetURL());
//}
error = GetFromCachePostNetIO(HTTP_STATUS_NOT_MODIFIED, TRUE);
if (error == ERROR_SUCCESS) {
SetNetFailed();
}
goto quit;
}
//CHECK_FSM_OWNED(Fsm);
//
// This adds CR-LF for the File Upload case
//
//if (_RequestMethod == HTTP_METHOD_TYPE_POST && _AddCRLFToPOST && fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST)
//{
// error = _Socket->Send(gszCRLF, 2, 0);
// if (error != ERROR_SUCCESS) {
// goto quit;
// }
//}
fsm.m_bWasKeepAlive = (_bKeepAliveConnection || IsKeepAlive());
if ((error != ERROR_SUCCESS)
|| ((GetStatusCode() != HTTP_STATUS_OK) && (GetStatusCode() != 0))) {
//
// must be doing proxy tunnelling request if status code set
//
INET_ASSERT(((GetStatusCode() != HTTP_STATUS_OK)
&& (GetStatusCode() != 0))
? IsTalkingToSecureServerViaProxy()
: TRUE
);
//
// server may have reset keep-alive connection
//
if (((error == ERROR_INTERNET_CONNECTION_RESET)
|| (error == ERROR_INTERNET_CONNECTION_ABORTED))
&& fsm.m_bWasKeepAlive
&& (--fsm.m_iRetries != 0)) {
DEBUG_PRINT(HTTP,
INFO,
("keep-alive connection failed after send. Retrying\n"
));
//dprintf("*** retrying k-a connection after send\n");
CloseConnection(TRUE);
goto try_again;
}
goto quit;
}
if (fsm.m_arRequest == AR_HTTP_BEGIN_SEND_REQUEST) {
goto quit;
}
fsm.SetFunctionState(FSM_STATE_6);
error = DoFsm(new CFsm_ReceiveResponse(this));
if (error == ERROR_IO_PENDING) {
goto quit;
}
//
// fall through
//
case FSM_STATE_6:
DEBUG_PRINT(HTTP, INFO, ("State_6_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
//CHECK_FSM_OWNED(Fsm);
if (error != ERROR_SUCCESS) {
//dprintf("*** post-receive: error=%d, retries=%d\n", error, fsm.m_iRetries);
//
// server may have reset keep-alive connection
//
if (((error == ERROR_INTERNET_CONNECTION_RESET)
|| (error == ERROR_INTERNET_CONNECTION_ABORTED)
|| (error == ERROR_HTTP_INVALID_SERVER_RESPONSE))
&& fsm.m_bWasKeepAlive
&& (--fsm.m_iRetries != 0)) {
DEBUG_PRINT(HTTP,
INFO,
("keep-alive connection failed after receive. Retrying\n"
));
//dprintf("*** retrying k-a connection after receive\n");
CloseConnection(TRUE);
_ResponseHeaders.FreeHeaders();
ResetResponseVariables();
_ResponseHeaders.Initialize();
_dwCurrentStreamPosition = 0;
goto try_again;
}
//
// if we reached this far and discover that the state is offline
// then we have either transitioned to this state by losing the
// net connection, or by being put into user-offline state. In
// either case, we attempt to retrieve the item from the cache
//
offline_check:
if (IsOffline()) {
// if we already have it retrived, don't do it again
if (!_hCacheStream && !_pCacheEntryInfo )
error = FHttpBeginCacheRetrieval(TRUE, TRUE, FALSE);
else
error = FHttpBeginCacheRetrieval(TRUE, TRUE, TRUE);
} else if (((GetOpenFlags() & INTERNET_FLAG_CACHE_IF_NET_FAIL)
&& ((error == ERROR_INTERNET_CANNOT_CONNECT)
|| (error == ERROR_INTERNET_CONNECTION_RESET)))
|| ((error == ERROR_INTERNET_TIMEOUT)
&& IsFromCacheTimeoutSet()
&& CanRetrieveFromCache(FALSE))) {
//if (IsFromCacheTimeoutSet() && IsInCache()) {
// dprintf("%q timed out on response - getting from cache\n", GetURL());
//}
error = GetFromCachePostNetIO(HTTP_STATUS_NOT_MODIFIED, TRUE);
if (error == ERROR_SUCCESS) {
SetNetFailed();
}
}
goto quit;
}
fsm.SetFunctionState(FSM_STATE_7);
case FSM_STATE_7:
DEBUG_PRINT(HTTP, INFO, ("State_7_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
//
// put any received cookie headers in the cookie jar, but only if the
// app didn't tell us not to
//
if (!(GetOpenFlags() & INTERNET_FLAG_NO_COOKIES)
/* && IsResponseHeaderPresent(HTTP_QUERY_SET_COOKIE) */ )
{
DWORD dwError;
fsm.SetBlocking(TRUE);
dwError = ExtractSetCookieHeaders(&fsm.m_dwCookieIndex);
fsm.SetBlocking(FALSE);
if ( dwError == ERROR_SUCCESS ) {
SetPerUserItem(TRUE);
} else if ( dwError == ERROR_IO_PENDING ) {
error = ERROR_IO_PENDING;
goto quit;
}
}
//
// we need to handle various intermediary return codes:
//
// 30x - redirection
// 40x - authentication
//
// BUT ONLY if the app didn't tell us it wanted to handle these itself
//
DWORD statusCode;
BOOL bNoAuth;
statusCode = GetStatusCode();
bNoAuth = (GetOpenFlags() & INTERNET_FLAG_NO_AUTH) ? TRUE : FALSE;
//
// if the status is 200 (most frequently return header == success)
// and we are not authenticating all responses then we're done
//
if ((statusCode == HTTP_STATUS_OK) && bNoAuth) {
goto quit;
}
//
// handle authentication before checking the cache
//
if (!bNoAuth) {
//
// call filter for cis rpa
//
if (!HttpFiltOnResponse(this)) {
switch (error = GetLastError()) {
case ERROR_INTERNET_FORCE_RETRY:
if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST )
{
goto quit;
}
fsm.m_bFinished = FALSE;
fsm.m_bAuthNotFinished = TRUE;
error = ERROR_SUCCESS;
break;
case ERROR_INTERNET_NEED_UI:
error = ERROR_SUCCESS;
break;
}
}
//
// call packages for basic, ntlm, msn, dpa etc.
//
error = AuthOnResponse(this);
// passport1.4 auth could change the status code from 302 to 401 here
statusCode = GetStatusCode();
if (error == ERROR_INTERNET_FORCE_RETRY) {
if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST )
{
goto quit;
}
//
// the object has been updated with new info - try again
//
fsm.m_bFinished = FALSE;
fsm.m_bAuthNotFinished = TRUE;
error = ERROR_SUCCESS;
//
// Reset auto-proxy info so we can retry the connection
//
if ( fsm.m_fOwnsProxyInfoQueryObj && fsm.m_pProxyInfoQuery && fsm.m_pProxyInfoQuery->IsAlloced())
{
delete fsm.m_pProxyInfoQuery;
fsm.m_pProxyInfoQuery = NULL;
}
} else if (error == ERROR_INTERNET_INCORRECT_PASSWORD) {
//
// just return success to the app which will have to check the
// headers and make the request again, with the right password
//
error = ERROR_SUCCESS;
goto quit;
} else if (error == ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY) {
// If app allows this error code return it. Otherwise return ERROR_INTERNET_LOGIN_FAILURE
if (!(GetErrorMask() & INTERNET_ERROR_MASK_LOGIN_FAILURE_DISPLAY_ENTITY_BODY))
{
error = ERROR_INTERNET_LOGIN_FAILURE;
}
else
{
goto quit;
}
}
}
//
// if we can read from the cache then let us try
//
if ((statusCode == HTTP_STATUS_OK)
|| (statusCode == HTTP_STATUS_NOT_MODIFIED)
|| (statusCode == HTTP_STATUS_PRECOND_FAILED)
|| (statusCode == HTTP_STATUS_PARTIAL_CONTENT)
|| (statusCode == 0)) {
if (GetFromCachePostNetIO(statusCode) == ERROR_SUCCESS) {
error = ERROR_SUCCESS;
goto quit;
}
}
BOOL fMustRedirect;
fMustRedirect = FALSE;
if (_pAuthCtx)
{
if (_pAuthCtx->GetSchemeType() == AUTHCTX::SCHEME_PASSPORT)
{
PASSPORT_CTX* pPPCtx = (PASSPORT_CTX*)_pAuthCtx;
if (pPPCtx->m_lpszRetUrl)
{
fMustRedirect = TRUE;
}
}
}
//
// handle redirection
//
BOOL fDoProxyRedirect;
fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_UNKNOWN;
fDoProxyRedirect = FALSE;
fsm.m_fNeedUserApproval = FALSE;
fsm.m_bRedirected = FALSE;
if (((statusCode == HTTP_STATUS_AMBIGUOUS) // 300
|| (statusCode == HTTP_STATUS_MOVED) // 301
|| (statusCode == HTTP_STATUS_REDIRECT) // 302
|| (statusCode == HTTP_STATUS_REDIRECT_METHOD) // 303
// || (statusCode == HTTP_STATUS_USE_PROXY) // 305
|| (statusCode == HTTP_STATUS_REDIRECT_KEEP_VERB)) // 307
&& (fMustRedirect || !(GetOpenFlags() & INTERNET_FLAG_NO_AUTO_REDIRECT))) {
//
// Clean out expired PROXY_STATE
//
error = ERROR_SUCCESS;
if ( fsm.m_fOwnsProxyInfoQueryObj && fsm.m_pProxyInfoQuery && fsm.m_pProxyInfoQuery->IsAlloced())
{
delete fsm.m_pProxyInfoQuery;
fsm.m_pProxyInfoQuery = NULL;
}
//fsm.m_pProxyState = NULL;
SetProxyName(NULL, 0, 0);
//
// if we've already had the max allowable redirects then quit
//
if (fsm.m_dwRedirectCount == 0) {
error = ERROR_HTTP_REDIRECT_FAILED;
fsm.m_bRedirectCountedOut = TRUE;
goto quit;
}
//
// we got 300 (ambiguous), 301 (permanent move), 302 (temporary
// move), or 303 (redirection using new method)
//
switch (statusCode) {
case HTTP_STATUS_AMBIGUOUS:
//
// 300 - multiple choice
//
//
// If there is a Location header, we do an "automatic" redirect
//
_ResponseHeaders.LockHeaders();
if (! IsResponseHeaderPresent(HTTP_QUERY_LOCATION)) {
_ResponseHeaders.UnlockHeaders();
fsm.m_bFinished = TRUE;
break;
}
_ResponseHeaders.UnlockHeaders();
//
// fall through
//
case HTTP_STATUS_MOVED:
// Table View:
//Method 301 302 303 307
// * * * GET *
//POST GET GET GET POST
//
//Put another way:
//301 & 302 - All methods are redirected to the same method but POST. POST is
// redirected to a GET.
//303 - All methods are redirected to GET
//307 - All methods are redirected to the same method.
//
// 301 - permanently moved
//
//
// fall through
//
case HTTP_STATUS_REDIRECT:
//
// 302 - temporarily moved (POST => GET, everything stays the same)
//
fsm.m_tMethodRedirect = GetMethodType();
if (fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_POST)
//
// A POST change method to a GET
//
{
fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_GET;
// force no optional data on second and subsequent sends
fsm.m_dwOptionalLength = 0;
// Don't decorate URL for a GET.
FreeSecondaryCacheKey();
}
// Other than GET and HEAD methods, we always want to
// error out to the client with ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION
if ((fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_GET) &&
(fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_HEAD))
{
AUTHCTX* pAuthCtx = GetAuthCtx();
AUTHCTX::SPMScheme eAuthScheme = AUTHCTX::SCHEME_UNKNOWN;
if (pAuthCtx != NULL)
{
eAuthScheme = pAuthCtx->GetSchemeType();
}
// If Passport1.4 authentication is underway, we relax the user approval requirement
// to make app's life easier
if (eAuthScheme != AUTHCTX::SCHEME_PASSPORT)
{
fsm.m_fNeedUserApproval = TRUE;
}
}
INET_ASSERT(((fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_GET)
|| (fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_HEAD))
? (fsm.m_dwOptionalLength == 0) : TRUE);
fsm.m_bRedirected = TRUE;
--fsm.m_dwRedirectCount;
break;
case HTTP_STATUS_REDIRECT_METHOD:
//
// 303 - see other (POST => GET)
//
fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_GET;
//
// force no optional data on second and subsequent sends
//
fsm.m_dwOptionalLength = 0;
//
// Don't decorate URL for a GET.
//
FreeSecondaryCacheKey();
fsm.m_bRedirected = TRUE;
--fsm.m_dwRedirectCount;
break;
case HTTP_STATUS_REDIRECT_KEEP_VERB:
//
// 307 - see other (POST => POST)
//
//if (IsHttp1_1()) {
fsm.m_tMethodRedirect = GetMethodType();
if ((fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_GET) &&
(fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_HEAD))
{
fsm.m_fNeedUserApproval = TRUE;
}
INET_ASSERT(((fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_GET)
|| (fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_HEAD))
? (fsm.m_dwOptionalLength == 0) : TRUE);
//} else {
// fsm.m_dwOptionalLength = 0;
// fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_GET;
//}
fsm.m_bRedirected = TRUE;
--fsm.m_dwRedirectCount;
break;
#if 0 // The http-wg will probably decide that 305 is only for proxies.
case HTTP_STATUS_USE_PROXY:
//
// 305 - use proxy, force a re-request but now use a the specifed proxy
//
if ( IsResponseHttp1_1() )
{
fsm.m_tMethodRedirect = GetMethodType();
fDoProxyRedirect = TRUE;
fsm.m_bRedirected = TRUE;
--fsm.m_dwRedirectCount;
break;
}
//
// fall through
//
#endif
default:
fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_GET;
//
// BUGBUG - force no optional data on second and subsequent
// sends
//
fsm.m_dwOptionalLength = 0;
fsm.m_bRedirected = TRUE;
--fsm.m_dwRedirectCount;
break;
}
//
// Only allow redirect to continue if we are successful.
//
if (fsm.m_bRedirected
&& ((fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_UNKNOWN)
|| (fsm.m_tMethodRedirect == GetMethodType()))) {
fsm.SetFunctionState(FSM_STATE_8);
// Remember if it was a forms submit.
fsm.m_bFormsSubmit = ((GetOpenFlags() & INTERNET_FLAG_FORMS_SUBMIT) ||
(fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_POST));
error = Redirect(fsm.m_tMethodRedirect, fDoProxyRedirect);
if (error != ERROR_SUCCESS) {
goto quit;
}
}
} else {
//
// not a status that we handle. We're done
// BUT WAIT, we're only finshed if also
// finished retrying HTTP authentication.
//
// if the app told us not to handle authentication auth_not_finished
// will be FALSE
//
if (!fsm.m_bAuthNotFinished) {
fsm.m_bFinished = TRUE;
}
}
//
// fall through
//
case FSM_STATE_8:
DEBUG_PRINT(HTTP, INFO, ("State_8_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
//CHECK_FSM_OWNED(Fsm);
if (fsm.m_bRedirected) {
BOOL bFormsSubmit = fsm.m_bFormsSubmit;
fsm.m_bFormsSubmit = FALSE;
if (error != ERROR_SUCCESS) {
goto quit;
}
//if ( IsResponseHttp1_1() &&
// fsm.m_fNeedUserApproval &&
// GetMethodType() != HTTP_METHOD_TYPE_GET &&
// GetMethodType() != HTTP_METHOD_TYPE_HEAD )
if (fsm.m_fNeedUserApproval)
{
INET_ASSERT((fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_GET) &&
(fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_HEAD));
//
// If servers force us to redirect back
// to another POST, PUT, etc. then we must notify the user.
//
error = ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION;
DEBUG_PRINT(HTTP, INFO, ("User approval needed. returning 0x%x\r\n", error));
goto quit;
}
// If we are redirecting a forms submit always urlmon a chance
// to ask the user because the submit might be going to a
// restricted zone now.
if ( bFormsSubmit )
{
error = ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION;
DEBUG_PRINT(HTTP, INFO, ("Redirected form needs user approval. returning 0x%x\r\n", error));
goto quit;
}
INET_ASSERT(error == ERROR_SUCCESS);
INET_ASSERT(!IsCacheReadInProgress());
//
// cleanup response headers from redirection
//
ReuseObject();
if (GetFromCachePreNetIO() == ERROR_SUCCESS) {
error = ERROR_SUCCESS;
goto quit;
}
//
// Allow Redirects to exit out and force the HttpEndRequestA
// caller to notice.
//
if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST &&
fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_GET &&
fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_HEAD
)
{
error = ERROR_INTERNET_FORCE_RETRY;
}
}
}
state = FSM_STATE_INIT;
} while (!fsm.m_bFinished && (error == ERROR_SUCCESS));
quit:
DEBUG_PRINT(HTTP, INFO, ("Quit1: error = 0x%x\r\n", error));
if (error == ERROR_IO_PENDING) {
goto done;
}
{
AUTHCTX* pAuthCtx = GetAuthCtx();
AUTHCTX::SPMScheme eAuthScheme = AUTHCTX::SCHEME_UNKNOWN;
if (pAuthCtx != NULL)
{
eAuthScheme = pAuthCtx->GetSchemeType();
}
if (!fsm.m_bCancelRedoOfProxy &&
//(GetStatusCode() != HTTP_STATUS_DENIED) &&
((eAuthScheme != AUTHCTX::SCHEME_PASSPORT) || (GetStatusCode() != HTTP_STATUS_DENIED)) && // this is safer
fsm.m_pInternet->RedoSendRequest(&error, fsm.m_pProxyInfoQuery, GetOriginServer(), GetServerInfo()))
{
fsm.m_bFinished = FALSE;
fsm.m_bRedirectCountedOut = FALSE;
fsm.m_dwRedirectCount = GlobalMaxHttpRedirects;
fsm.SetState(FSM_STATE_INIT);
state = FSM_STATE_INIT;
DEBUG_PRINT(HTTP, INFO, ("Quit2: error = 0x%x\r\n", error));
goto retry_send_request;
}
else
{
//SetProxyName(NULL, 0, 0);
DEBUG_PRINT(HTTP, INFO, ("Quit3: error = 0x%x\r\n", error));
}
}
//
// if ERROR_HTTP_REDIRECT_FAILED then we tried to redirect, but found that
// we couldn't do it (e.g. http:// to ftp:// or file://, etc.) We need to
// defer this to the caller to clean up & make the new request. They will
// have all the header info (plus we probably already indicated the new
// URL during the redirect callback). Just return ERROR_SUCCESS
//
// Only do this if we failed because we couldn't change to the new URL, not
// because we counted out the redirects
//
// We also change the error code to ERROR_SUCCESS if we didn't redirect
// because we were redirected to the same site
//
if (((error == ERROR_HTTP_REDIRECT_FAILED)
|| (error == ERROR_HTTP_NOT_REDIRECTED))
&& !fsm.m_bRedirectCountedOut) {
error = ERROR_SUCCESS;
}
//
// if we received a Refresh header along the way, but no longer have it (we
// were redirected) then restore it
//
if ((error == ERROR_SUCCESS) && IsRefresh()) {
DWORD err;
_ResponseHeaders.LockHeaders();
if (!IsResponseHeaderPresent(HTTP_QUERY_REFRESH))
{
DWORD len;
HEADER_STRING & refreshHeader = GetRefreshHeader();
if (refreshHeader.HaveString()) {
err = AddInternalResponseHeader(HTTP_QUERY_REFRESH,
refreshHeader.StringAddress(),
refreshHeader.StringLength()
);
INET_ASSERT(err == ERROR_SUCCESS);
}
}
_ResponseHeaders.UnlockHeaders();
}
fsm.SetNextState(FSM_STATE_FINISH);
done:
PERF_LEAVE(HttpSendRequest_Start);
DEBUG_LEAVE(error);
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Finish(
IN CFsm_HttpSendRequest * Fsm
)
/*++
Routine Description:
description-of-function.
Arguments:
Fsm -
Return Value:
DWORD
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Finish",
"%#x",
Fsm
));
PERF_ENTER(HttpSendRequest_Finish);
CFsm_HttpSendRequest & fsm = *Fsm;
DWORD error = fsm.GetError();
fsm.m_fOwnsProxyInfoQueryObj = TRUE;
if ((error == ERROR_SUCCESS)
&& !IsCacheReadInProgress()
&& !IsCacheWriteInProgress()
&& (fsm.m_arRequest != AR_HTTP_BEGIN_SEND_REQUEST)) {
INET_ASSERT(!IsCacheReadInProgress());
//
// we are not reading from the cache
// Let us ask a routine whether we should cache this
// stuff or not
//
// (only need to do this for 200/206 response code)
//
if( _StatusCode == HTTP_STATUS_OK ||
_StatusCode == HTTP_STATUS_PARTIAL_CONTENT)
{
if (FCanWriteToCache() )
{
DEBUG_PRINT(CACHE,
INFO,
("Starting cache write\n"
));
FHttpBeginCacheWrite();
} else {
//
// If the WININET client must have caching,
// Notify him that we are unable to cache his files,
// by erroring out.
//
if (GetCacheFlags() & INTERNET_FLAG_NEED_FILE) {
error = ERROR_INTERNET_UNABLE_TO_CACHE_FILE;
}
}
}
}
INET_ASSERT(fsm.m_hRequestMapped != NULL);
//if (!IsAsyncHandle() && (fsm.m_hRequestMapped != NULL)) {
// DereferenceObject((LPVOID)fsm.m_hRequestMapped);
//}
//
// we will return FALSE even if this is an async operation and the error is
// ERROR_IO_PENDING
//
fsm.SetDone(error);
//fsm.SetApiResult(error == ERROR_SUCCESS);
PERF_LEAVE(HttpSendRequest_Finish);
DEBUG_LEAVE(error);
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::BuildProxyMessage(
IN CFsm_HttpSendRequest * Fsm,
AUTO_PROXY_ASYNC_MSG * pProxyMsg,
IN OUT URL_COMPONENTS * pUrlComponents
)
/*++
Routine Description:
Calls CrackUrl to parses request URL, and
transfers the information to the AUTO_PROXY_ASYNC_MSG
Arguments:
Fsm - HTTP send request FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DWORD error = ERROR_SUCCESS;
LPSTR currentUrl;
DWORD currentUrlLength;
//
// Gather the URL off the handle
//
currentUrl = GetURL();
if (currentUrl) {
currentUrlLength = lstrlen(currentUrl);
//
// BUGBUG [arthurbi] the following can be a slow call,
// but its too risky to change the complete behavior where
// we cache it
//
//
// crack the current URL
//
memset(pUrlComponents, 0, sizeof(URL_COMPONENTS));
pUrlComponents->dwStructSize = sizeof(URL_COMPONENTS);
error = CrackUrl(currentUrl,
currentUrlLength,
FALSE, // don't escape URL-path
&(pUrlComponents->nScheme),
NULL, // don't care about Scheme Name
NULL,
&(pUrlComponents->lpszHostName),
&(pUrlComponents->dwHostNameLength),
&(pUrlComponents->nPort),
NULL, // don't care about user name
NULL,
NULL, // or password
NULL,
&(pUrlComponents->lpszUrlPath),
&(pUrlComponents->dwUrlPathLength),
NULL, // no extra
NULL,
NULL
);
pProxyMsg->SetProxyMsg(
pUrlComponents->nScheme,
currentUrl,
currentUrlLength,
pUrlComponents->lpszHostName,
pUrlComponents->dwHostNameLength,
pUrlComponents->nPort
);
} else {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INVALID_URL;
}
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::QueryProxySettings(
IN CFsm_HttpSendRequest * Fsm,
INTERNET_HANDLE_OBJECT * pInternet,
IN OUT URL_COMPONENTS * pUrlComponents
)
/*++
Routine Description:
Wrapper over GetProxyInfo call to determine proxy
settings on our given object
Arguments:
Fsm - HTTP send request FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::QueryProxySettings",
"%#x, %#x, %#x",
Fsm, pInternet, pUrlComponents
));
DWORD error = ERROR_SUCCESS;
CFsm_HttpSendRequest & fsm = *Fsm;
INET_ASSERT(fsm.m_pProxyInfoQuery);
INET_ASSERT(pInternet);
if (!IsAutoProxyChecked())
{
// Never do this more than once per request.
// QFE 353 involves supporting authentication, and
// this check prevents multiple dialogs from appearing
// due to invalid credentials.
SetAutoProxyChecked(TRUE);
error = pInternet->CheckAutoProxyDownloaded();
}
// don't touch fsm state.
if ( error == ERROR_IO_PENDING )
{
goto quit;
}
SetProxyName(NULL, 0, 0);
fsm.m_fOwnsProxyInfoQueryObj = FALSE;
error = pInternet->GetProxyInfo(
&fsm.m_pProxyInfoQuery
);
//
// If GetProxyInfo returns pending, then we no longer have
// access to the pointer that we've passed.
//
if ( error == ERROR_IO_PENDING )
{
//
// Bail out, DO NOT TOUCH any OBJECTS or FSMs
//
goto quit;
}
// then regardless we own it unless GetProxyInfo went pending with the FSM
fsm.m_fOwnsProxyInfoQueryObj = TRUE;
if ( error != ERROR_SUCCESS )
{
goto quit;
}
INET_ASSERT( error == ERROR_SUCCESS );
if ( ! ((fsm.m_pProxyInfoQuery)->IsUseProxy()) )
{
if ( pUrlComponents->nScheme == INTERNET_SCHEME_FTP ||
pUrlComponents->nScheme == INTERNET_SCHEME_GOPHER )
{
//
// we cannot go direct on these
//
error = ERROR_INTERNET_INTERNAL_ERROR;
}
SetIsTalkingToSecureServerViaProxy(FALSE);
}
quit:
DEBUG_LEAVE(error);
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::CheckForCachedProxySettings(
IN AUTO_PROXY_ASYNC_MSG *pProxyMsg,
OUT CServerInfo **ppProxyServerInfo
)
/*++
Routine Description:
Attempts to determine and then resolve if there are cached
proxy settings saved away in the CServerInfo object,
which is found in our HTTP_REQUEST_ object. This can
be very useful since calling off to an auto-proxy thread
can be quite expensive in terms of performance.
Arguments:
pProxyMsg - the object containing our current proxy message
information, that we use to scripple our proxy state for
a given request
ppProxyServerInfo - on return, may contain the resultant
cached ServerInfo object.
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DWORD error = ERROR_INTERNET_INTERNAL_ERROR;
CServerInfo * pOriginServer = GetOriginServer();
CServerInfo * pProxyServer;
INET_ASSERT(pProxyMsg);
*ppProxyServerInfo = NULL;
if (pOriginServer)
{
BOOL fCachedEntry;
pProxyServer =
pOriginServer->GetCachedProxyServerInfo(
pProxyMsg->_tUrlProtocol,
pProxyMsg->_nUrlPort,
&fCachedEntry
);
if (fCachedEntry)
{
if ( pProxyServer )
{
if (pProxyServer->CopyCachedProxyInfoToProxyMsg(pProxyMsg))
{
SetOriginServer();
*ppProxyServerInfo = pProxyServer;
error = ERROR_SUCCESS;
goto quit;
}
// nuke extra ref, sideeffect of GetCachedProxy... call
::ReleaseServerInfo(pProxyServer);
}
else
{
// DIRECT, no-proxy cached.
pProxyMsg->SetUseProxy(FALSE);
pProxyMsg->_lpszProxyHostName = NULL;
error = ERROR_SUCCESS;
goto quit;
}
}
}
pProxyMsg->SetVersion();
quit:
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::ProcessProxySettings(
IN CFsm_HttpSendRequest * Fsm,
IN INTERNET_CONNECT_HANDLE_OBJECT * pConnect,
IN OUT URL_COMPONENTS * pUrlComponents,
OUT LPSTR * lplpszRequestObject,
OUT DWORD * lpdwRequestObjectSize
)
/*++
Routine Description:
Armed with the results of the proxy query, this method takes care of
assembling the various variables and states to deal with various
types of proxies.
More specifally, this handles HTTP Cern Proxies, SOCKS proxies,
SSL-CONNECT/HTTP proxies, and special cases such as FTP URLs
with passwords through an HTTP Cern Proxy.
Arguments:
Fsm - HTTP send request FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::ProcessProxySettings",
"%#x, %#x, %#x",
Fsm, pConnect, pUrlComponents
));
DWORD error = ERROR_SUCCESS;
CFsm_HttpSendRequest & fsm = *Fsm;
LPSTR lpszUrlObject = NULL;
LPSTR lpszObject = pUrlComponents->lpszUrlPath;
DWORD dwcbObject = pUrlComponents->dwUrlPathLength;
if ((fsm.m_pProxyInfoQuery)->GetProxyScheme() == INTERNET_SCHEME_SOCKS)
{
if ( pUrlComponents->nScheme == INTERNET_SCHEME_FTP ||
pUrlComponents->nScheme == INTERNET_SCHEME_GOPHER )
{
//
// we cannot go direct on these,
// this would for example an HTTP request over SOCKS,
// redirected to an FTP/GOPHER URL
//
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
SetSocksProxyName((fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
(fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength,
(fsm.m_pProxyInfoQuery)->_nProxyHostPort
);
(fsm.m_pProxyInfoQuery)->_lpszProxyHostName = NULL;
(fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength = 0;
}
else if (pUrlComponents->nScheme == INTERNET_SCHEME_HTTPS)
{
SetIsTalkingToSecureServerViaProxy(TRUE);
}
else
{
SetIsTalkingToSecureServerViaProxy(FALSE); // default value.
//
// if this request is going via proxy then we send the entire URL as the
// request
//
DWORD urlLength;
//
// in secure proxy tunnelling case we are going to send the request
// "CONNECT <host>:<port>"
//
LPSTR lpszMBCSHostName;
DWORD dwMBCSHostNameLength;
BOOL fUsedUTF8 = FALSE;
LPSTR lpszUTF8HostName;
DWORD dwUTF8StrLen;
if (ShouldSendUTF8ServerNameToProxy())
{
INET_ASSERT (GlobalSendUTF8ServerToProxy);
if (lpszUTF8HostName = ConvertMBCSToUTF8(pUrlComponents->lpszHostName,
pUrlComponents->dwHostNameLength,
GetCodePage(),
&dwUTF8StrLen))
{
lpszMBCSHostName = pUrlComponents->lpszHostName;
dwMBCSHostNameLength = pUrlComponents->dwHostNameLength;
pUrlComponents->lpszHostName = lpszUTF8HostName;
pUrlComponents->dwHostNameLength = dwUTF8StrLen;
fUsedUTF8 = TRUE;
}
}
if (IsTunnel()) {
urlLength = pUrlComponents->dwHostNameLength + sizeof(":65535");
} else {
urlLength = INTERNET_MAX_URL_LENGTH;
}
lpszUrlObject = (LPSTR)ResizeBuffer(NULL, urlLength, FALSE);
if (lpszUrlObject == NULL)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto local_quit;
}
if (IsTunnel())
{
if (pUrlComponents->nPort == INTERNET_INVALID_PORT_NUMBER)
{
INET_ASSERT (pUrlComponents->nScheme == INTERNET_SCHEME_HTTP);
pUrlComponents->nPort = INTERNET_DEFAULT_HTTP_PORT;
}
memcpy (lpszUrlObject, pUrlComponents->lpszHostName, pUrlComponents->dwHostNameLength);
wsprintf (lpszUrlObject + pUrlComponents->dwHostNameLength, ":%d", pUrlComponents->nPort);
}
else
{
//
// there may be a user name & password (only if FTP)
//
LPSTR userName;
DWORD userNameLength;
LPSTR password;
DWORD passwordLength;
if (pUrlComponents->nScheme == INTERNET_SCHEME_FTP) {
userName = pConnect->GetUserOrPass(IS_USER, IS_SERVER);
userNameLength = userName ? lstrlen(userName) : 0;
password = pConnect->GetUserOrPass(IS_PASS, IS_SERVER);
passwordLength = password ? lstrlen(password) : 0;
} else {
userName = NULL;
userNameLength = 0;
password = NULL;
passwordLength = 0;
}
if (pUrlComponents->nPort == INTERNET_INVALID_PORT_NUMBER)
{
switch (pUrlComponents->nScheme)
{
case INTERNET_SCHEME_FTP:
pUrlComponents->nPort = INTERNET_DEFAULT_FTP_PORT;
break;
case INTERNET_SCHEME_GOPHER:
pUrlComponents->nPort = INTERNET_DEFAULT_GOPHER_PORT;
break;
case INTERNET_SCHEME_HTTP:
pUrlComponents->nPort = INTERNET_DEFAULT_HTTP_PORT;
break;
case INTERNET_SCHEME_HTTPS:
pUrlComponents->nPort = INTERNET_DEFAULT_HTTPS_PORT;
break;
default:
INET_ASSERT(FALSE);
break;
}
}
pUrlComponents->lpszUserName = userName;
pUrlComponents->dwUserNameLength = userNameLength;
pUrlComponents->lpszPassword = password;
pUrlComponents->dwPasswordLength = passwordLength;
if (!InternetCreateUrl(pUrlComponents, ICU_ESCAPE_AUTHORITY, lpszUrlObject, &urlLength))
{
error = GetLastError();
}
if (password)
{
SecureZeroMemory(password, lstrlen(password));
FREE_MEMORY(password);
password = NULL;
}
if (error != ERROR_SUCCESS)
goto local_quit;
//
// shrink the buffer to fit
//
lpszUrlObject = (LPSTR)ResizeBuffer(lpszUrlObject,
(urlLength + 1) * sizeof(TCHAR),
FALSE
);
INET_ASSERT(lpszUrlObject != NULL);
if (lpszUrlObject == NULL)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto local_quit;
}
}
SetRequestUsingProxy(TRUE);
lpszObject = lpszUrlObject;
dwcbObject = lstrlen(lpszUrlObject);
local_quit:
if (fUsedUTF8)
{
pUrlComponents->lpszHostName = lpszMBCSHostName;
pUrlComponents->dwHostNameLength = dwMBCSHostNameLength;
LPSTR hostName = lpszUTF8HostName;
DWORD hostNameLength = dwUTF8StrLen;
INTERNET_PORT hostPort = GetHostPort();
LPSTR pAllocHostName = NULL;
if ((hostPort != INTERNET_DEFAULT_HTTP_PORT)
&& (hostPort != INTERNET_DEFAULT_HTTPS_PORT))
{
pAllocHostName = new CHAR[INTERNET_MAX_HOST_NAME_LENGTH + sizeof(":4294967295")+1];
if (pAllocHostName)
{
hostNameLength = wsprintf(pAllocHostName, "%s:%d", hostName, (hostPort & 0xffff));
hostName = pAllocHostName;
}
else
{
hostName = NULL;
}
}
if (hostName)
{
ReplaceRequestHeader(HTTP_QUERY_HOST,
hostName,
hostNameLength,
0, // dwIndex,
ADD_HEADER
);
}
if (pAllocHostName)
{
delete [] pAllocHostName;
}
delete [] lpszUTF8HostName;
}
}
quit:
*lplpszRequestObject = lpszObject;
*lpdwRequestObjectSize = dwcbObject;
DEBUG_LEAVE(error);
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::UpdateRequestInfo(
IN CFsm_HttpSendRequest * Fsm,
IN LPSTR lpszObject,
IN DWORD dwcbObject,
IN OUT URL_COMPONENTS * pUrlComponents,
IN OUT CServerInfo **ppProxyServerInfo
)
/*++
Routine Description:
Based on object and URL information, for a given HTTP request,
this function assembles the "special cases" and modifes the
request headers in prepartion of making the actual request.
The "special cases" includes the handling of HTTP versioning,
HTTP 1.0/1.1 keep-alives, and Pragma headers.
This function also deals with the update the ServerInfo object
that contains the host resolution information.
Arguments:
Fsm - HTTP send request FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::UpdateRequestInfo",
"%#x, %.100q, %d, %#x",
Fsm, lpszObject, dwcbObject, pUrlComponents
));
DWORD error = ERROR_SUCCESS;
LPSTR lpszVersion = NULL;
DWORD dwVersionLen = 0;
CFsm_HttpSendRequest & fsm = *Fsm;
if ( lpszObject == NULL)
{
lpszObject = pUrlComponents->lpszUrlPath;
dwcbObject = pUrlComponents->dwUrlPathLength;
}
INET_ASSERT(dwcbObject > 0 );
_RequestHeaders.LockHeaders();
//
// if we are going via proxy and HTTP 1.1 through proxy is disabled
// then modify the request version to HTTP/1.0
//
if ((fsm.m_pProxyInfoQuery)->IsUseProxy() &&
((fsm.m_pProxyInfoQuery)->_lpszProxyHostName != NULL) &&
(!GlobalEnableProxyHttp1_1 || GetMethodType() == HTTP_METHOD_TYPE_CONNECT)) {
lpszVersion = "HTTP/1.0";
dwVersionLen = sizeof("HTTP/1.0") - 1;
RemoveGzipDeflateEncHeader();
}
ModifyRequest(GetMethodType(),
lpszObject,
dwcbObject,
lpszVersion,
dwVersionLen
);
if ((fsm.m_pProxyInfoQuery)->IsUseProxy() )
{
SetProxyName( (fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
(fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength,
(fsm.m_pProxyInfoQuery)->_nProxyHostPort
);
if ((fsm.m_pProxyInfoQuery)->_lpszProxyHostName != NULL) {
if (_ServerInfo != NULL)
{
_ServerInfo->SetProxyByPassed(FALSE);
}
//
// changing server info from origin server to proxy server. Keep
// pointer to origin server so that we can update connect and
// round-trip times
//
SetOriginServer();
if (*ppProxyServerInfo) {
// cached server info
SetServerInfo(*ppProxyServerInfo);
*ppProxyServerInfo = NULL;
}
else
{
error = SetServerInfo((fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
(fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength
);
if (error != ERROR_SUCCESS) {
goto quit;
}
}
}
}
else
{
if (_ServerInfo != NULL)
{
_ServerInfo->SetProxyByPassed(TRUE);
if ( pUrlComponents->lpszHostName )
{
error = SetServerInfo(pUrlComponents->lpszHostName,
pUrlComponents->dwHostNameLength
);
if (error != ERROR_SUCCESS) {
goto quit;
}
}
}
}
//
// determine whether we use persistent connections and ensure the correct
// type and number of keep-alive headers are present
//
//
// BUGBUG - we need to check for "Connection: keep-alive". There may be
// other types of "Connection" header, and the keep-alive header
// may contain additional information
//
DWORD bufferLength;
DWORD index;
DWORD dwHeaderNameIndex;
if (IsRequestUsingProxy()) {
RemoveAllRequestHeadersByName(HTTP_QUERY_CONNECTION);
dwHeaderNameIndex = HTTP_QUERY_PROXY_CONNECTION;
} else {
RemoveAllRequestHeadersByName(HTTP_QUERY_PROXY_CONNECTION);
dwHeaderNameIndex = HTTP_QUERY_CONNECTION;
}
if (IsRequestHeaderPresent(dwHeaderNameIndex)) {
SetWantKeepAlive(TRUE);
SetOpenFlags(
GetOpenFlags() | INTERNET_FLAG_KEEP_CONNECTION);
}
error = ERROR_SUCCESS;
//
// if the global keep-alive switch
// is off then we don't want any keep-alive headers
//
if (GlobalDisableKeepAlive)
{
RemoveAllRequestHeadersByName(HTTP_QUERY_CONNECTION);
RemoveAllRequestHeadersByName(HTTP_QUERY_PROXY_CONNECTION);
if (IsRequestHttp1_1())
{
//
// Add "Connection: Close" header because we're not doing
// keep-alive on this Request, needed for HTTP 1.1
//
(void)ReplaceRequestHeader(HTTP_QUERY_CONNECTION,
CLOSE_SZ,
CLOSE_LEN,
0,
REPLACE_HEADER
);
}
SetOpenFlags(
GetOpenFlags() & ~INTERNET_FLAG_KEEP_CONNECTION);
}
//
// if the app requested keep-alive then add the header; if we're going via
// proxy then use the proxy-connection header
//
if (GetOpenFlags() & INTERNET_FLAG_KEEP_CONNECTION)
{
SetWantKeepAlive(TRUE);
(void)ReplaceRequestHeader(dwHeaderNameIndex,
KEEP_ALIVE_SZ,
KEEP_ALIVE_LEN,
0,
ADD_HEADER_IF_NEW
);
}
//
// if app added "connection: close" then we don't want keep-alive
//
if (IsRequestHttp1_1()) {
BOOL bClose = FindConnCloseRequestHeader(dwHeaderNameIndex);
BOOL bWantKeepAlive;
DWORD dwOpenFlags = GetOpenFlags();
if (bClose || (IsTunnel() && GetAuthState() != AUTHSTATE_CHALLENGE)) {
RemoveAllRequestHeadersByName(dwHeaderNameIndex);
//
// For a Tunnel to a proxy we want to make sure that
// keep-alive is off since is does not make sense
// to do keep-alive with in a HTTP CONNECT request
//
// Note: we do not add the Connection: close header
// because of its amphorus definition in this case.
//
if (!IsTunnel()) {
(void)ReplaceRequestHeader(dwHeaderNameIndex,
CLOSE_SZ,
CLOSE_LEN,
0,
REPLACE_HEADER
);
}
bWantKeepAlive= FALSE;
dwOpenFlags &= ~INTERNET_FLAG_KEEP_CONNECTION;
} else {
bWantKeepAlive = TRUE;
dwOpenFlags |= INTERNET_FLAG_KEEP_CONNECTION;
}
SetWantKeepAlive(bWantKeepAlive);
SetOpenFlags(dwOpenFlags);
}
DWORD dwFlags;
dwFlags = GetCacheFlags();
if (GetMethodType() == HTTP_METHOD_TYPE_POST && !GetSecondaryCacheKey())
{
//
// IE30 compat: force reload on post
//
dwFlags |= INTERNET_FLAG_RELOAD;
}
if (dwFlags & (INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE))
{
//
// add "Pragma: No-Cache" header for HTTP 1.0 or
// add "Cache-Control: No-Cache" header for HTTP 1.1
// if not already present and if forced with CTRL+F5
// add "Pragma: No-Cache" header if through proxy.
//
BOOL fUseProxy = FALSE;
fUseProxy = IsRequestUsingProxy();
if ( fUseProxy || !(dwFlags & INTERNET_FLAG_RESYNCHRONIZE) )
{
if ((!fUseProxy && GlobalEnableHttp1_1))
{
ReplaceRequestHeader(HTTP_QUERY_CACHE_CONTROL,
NO_CACHE_SZ,
NO_CACHE_LEN,
0, // dwIndex
ADD_HEADER_IF_NEW
);
}
else
{
ReplaceRequestHeader(HTTP_QUERY_PRAGMA,
NO_CACHE_SZ,
NO_CACHE_LEN,
0, // dwIndex
ADD_HEADER_IF_NEW
);
}
}
}
quit:
_RequestHeaders.UnlockHeaders();
DEBUG_LEAVE(error);
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::UpdateProxyInfo(
IN CFsm_HttpSendRequest * Fsm,
IN BOOL fCallback
)
/*++
Routine Description:
Queries Proxy Information, and based on the proxy info it assembles the appropriate
HTTP request.
Arguments:
Fsm - HTTP send request FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
ERROR_NOT_ENOUGH_MEMORY
Ran out of resources
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::UpdateProxyInfo",
"%#x, %B",
Fsm,
fCallback
));
PERF_ENTER(UpdateProxyInfo);
DWORD error = ERROR_SUCCESS;
CFsm_HttpSendRequest & fsm = *Fsm;
CServerInfo *pProxyServer = NULL;
INTERNET_HANDLE_OBJECT * pInternet;
INTERNET_CONNECT_HANDLE_OBJECT * pConnect;
AUTO_PROXY_ASYNC_MSG proxyInfoQuery;
URL_COMPONENTS urlComponents;
LPSTR lpszObject = NULL;
DWORD dwcbObject = 0;
// once we're woken up, we own the obj stored in our FSM.
INET_ASSERT(fsm.m_fOwnsProxyInfoQueryObj);
//
// Get the Obj Pointers we care about
//
pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)GetParent();
INET_ASSERT(pConnect != NULL);
INET_ASSERT(pConnect->IsValid(TypeHttpConnectHandle) == ERROR_SUCCESS);
pInternet = (INTERNET_HANDLE_OBJECT *)pConnect->GetParent();
INET_ASSERT(pInternet != NULL);
INET_ASSERT(pInternet->IsValid(TypeInternetHandle) == ERROR_SUCCESS);
//
// Clear our handle state in regards to proxy settings
//
SetSocksProxyName(NULL, NULL, NULL);
SetRequestUsingProxy(FALSE);
//
// Parse URL, I have to do this every time,
// and even worse we need to do this before our caching code
// gets hit, but we can't move it because the quit code
// depends on the parsed URL. In the future we should cache this!!
//
error = BuildProxyMessage(
Fsm,
&proxyInfoQuery,
&urlComponents
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// No proxy installed on this object, bail out
//
if ( ! pInternet->IsProxy() && ! IsOverrideProxyMode() )
{
INET_ASSERT(fsm.m_pProxyInfoQuery == NULL);
fsm.m_pProxyInfoQuery = &proxyInfoQuery; // !!! put our local in the FSM
goto quit;
}
//
// If we're in the callback, just retrieve the results,
// from the orginal blocking call to proxy code
//
if ( fsm.m_pProxyInfoQuery )
{
fCallback = TRUE;
if ( ! (fsm.m_pProxyInfoQuery)->IsBackroundDetectionPending()) {
(fsm.m_pProxyInfoQuery)->SetQueryOnCallback(TRUE);
}
error = QueryProxySettings(Fsm, pInternet, &urlComponents);
if ( error != ERROR_SUCCESS || !(fsm.m_pProxyInfoQuery)->IsUseProxy())
{
goto quit;
}
}
else if ((fsm.m_pProxyInfoQuery = &proxyInfoQuery) && // !!! put our local in the FSM
CheckForCachedProxySettings(fsm.m_pProxyInfoQuery, &pProxyServer)
!= ERROR_SUCCESS )
{
//
// If there is nothing cached, then we'll need
// to do the actual proxy request to the proxy code
//
//fsm.m_pProxyInfoQuery = &proxyInfoQuery; // !!! put our local in the FSM
proxyInfoQuery.SetBlockUntilCompletetion(TRUE);
proxyInfoQuery.SetShowIndication(TRUE);
if (!IsTunnel() && !IsOverrideProxyMode())
{
error = QueryProxySettings(Fsm, pInternet, &urlComponents);
if ( error != ERROR_SUCCESS || !(fsm.m_pProxyInfoQuery)->IsUseProxy()) {
goto quit;
}
}
else // fall-back
{
//
// Get the current proxy information,
// if we're in an nested SSL tunnell
//
GetProxyName(&(fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
&(fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength,
&(fsm.m_pProxyInfoQuery)->_nProxyHostPort
);
(fsm.m_pProxyInfoQuery)->_tProxyScheme = INTERNET_SCHEME_DEFAULT;
(fsm.m_pProxyInfoQuery)->SetUseProxy(TRUE);
}
}
//
// Need to figure out whether we're actually talking
// to a Server via proxy. In this case we need to
// special case some logic in the Send so we create
// a sub-request to the proxy-server, and then do this
// request to the main SSL server.
//
if ( (fsm.m_pProxyInfoQuery)->IsUseProxy() )
{
error = ProcessProxySettings(
Fsm,
pConnect,
&urlComponents,
&lpszObject,
&dwcbObject
);
}
else
{
// Ensure this is false in case of very slim chance of
// redirect from internet https to intranet http
SetIsTalkingToSecureServerViaProxy(FALSE);
}
quit:
//
// If we didn't fail with pending,
// go ahead and process the request headers
//
if ( error != ERROR_IO_PENDING)
{
if ( error == ERROR_SUCCESS ) {
error = UpdateRequestInfo(Fsm, lpszObject, dwcbObject, &urlComponents, &pProxyServer);
}
//
// Now, Unlink the proxyinfomsg struc from the fsm,
// if its our stack based variable that we used as a temp
//
if ( fsm.m_fOwnsProxyInfoQueryObj &&
fsm.m_pProxyInfoQuery &&
! (fsm.m_pProxyInfoQuery)->IsAlloced() )
{
fsm.m_pProxyInfoQuery = NULL;
}
}
//
// Don't leak objects, Give a hoot, don't pollute !!
//
if ( pProxyServer != NULL )
{
::ReleaseServerInfo(pProxyServer);
}
if ( lpszObject != NULL &&
lpszObject != urlComponents.lpszUrlPath)
{
FREE_MEMORY(lpszObject);
}
PERF_LEAVE(UpdateProxyInfo);
DEBUG_LEAVE(error);
return error;
}
BOOL
HTTP_REQUEST_HANDLE_OBJECT::FindConnCloseRequestHeader(
IN DWORD dwIndex
)
/*++
Routine Description:
Determine if Connection: Close added to request headers
Arguments:
dwIndex - id of Connection header to search for (Connection or
Proxy-Connection)
Return Value:
BOOL
TRUE - header found
FALSE - header not found
--*/
{
DEBUG_ENTER((DBG_HTTP,
Bool,
"HTTP_REQUEST_HANDLE_OBJECT::FindConnCloseRequestHeader",
"%d [%s]",
dwIndex,
InternetMapHttpOption(dwIndex)
));
BOOL bFound = FALSE;
if (CheckedConnCloseRequest()) {
bFound = IsConnCloseRequest(dwIndex == HTTP_QUERY_PROXY_CONNECTION);
} else {
LPSTR ptr;
DWORD len;
DWORD index = 0;
while (FastQueryRequestHeader(dwIndex,
(LPVOID *)&ptr,
&len,
index) == ERROR_SUCCESS) {
if ((len == CLOSE_LEN) && (strnicmp(ptr, CLOSE_SZ, len) == 0)) {
bFound = TRUE;
break;
}
index++;
}
SetCheckedConnCloseRequest(dwIndex == HTTP_QUERY_PROXY_CONNECTION, bFound);
}
DEBUG_LEAVE(bFound);
return bFound;
}
#define ACCEPT_ENCODING_URLMON_STRING "gzip, deflate"
#define ACCEPT_ENCODING_URLMON_STRLEN (sizeof(ACCEPT_ENCODING_URLMON_STRING)-1)
VOID
HTTP_REQUEST_HANDLE_OBJECT::RemoveGzipDeflateEncHeader(
VOID
)
/*++
Routine Description:
Removes gzip, deflate encoding headers passed in from urlmon, if we finally
use Http 1.0 for the request.
Arguments:
None
Return Value:
None
--*/
{
DEBUG_ENTER((DBG_HTTP,
Bool,
"HTTP_REQUEST_HANDLE_OBJECT::RemoveGzipDeflateEncHeader",
NULL
));
LPSTR ptr;
DWORD len;
DWORD index = 0;
while (FastQueryRequestHeader((DWORD)HTTP_QUERY_ACCEPT_ENCODING,
(LPVOID *)&ptr,
&len,
index) == ERROR_SUCCESS) {
if ((len == ACCEPT_ENCODING_URLMON_STRLEN) &&
(strnicmp(ptr, ACCEPT_ENCODING_URLMON_STRING, len) == 0)) {
ReplaceRequestHeader((DWORD)HTTP_QUERY_ACCEPT_ENCODING, NULL, 0,index,0);
}
index++;
}
DEBUG_LEAVE(0);
return;
}