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.
 
 
 
 
 
 

1064 lines
24 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
http.cxx
Abstract:
Contains methods for HTTP_REQUEST_HANDLE_OBJECT class
Contents:
RMakeHttpReqObjectHandle
HTTP_REQUEST_HANDLE_OBJECT::HTTP_REQUEST_HANDLE_OBJECT
HTTP_REQUEST_HANDLE_OBJECT::~HTTP_REQUEST_HANDLE_OBJECT
HTTP_REQUEST_HANDLE_OBJECT::SetProxyName
HTTP_REQUEST_HANDLE_OBJECT::GetProxyName
HTTP_REQUEST_HANDLE_OBJECT::ReuseObject
HTTP_REQUEST_HANDLE_OBJECT::ResetObject
HTTP_REQUEST_HANDLE_OBJECT::SetAuthenticated
HTTP_REQUEST_HANDLE_OBJECT::IsAuthenticated
Author:
Madan Appiah (madana) 16-Nov-1994
Environment:
User Mode - Win32
Revision History:
Sophia Chung (sophiac) 14-Feb-1995 (added FTP and Archie class impl.)
(code adopted from madana)
--*/
#include <wininetp.h>
//
// functions
//
DWORD
RMakeHttpReqObjectHandle(
IN HINTERNET ParentHandle,
IN OUT HINTERNET * ChildHandle,
IN CLOSE_HANDLE_FUNC wCloseFunc,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
)
/*++
Routine Description:
C-callable wrapper for creating an HTTP_REQUEST_HANDLE_OBJECT
Arguments:
ParentHandle - mapped address of parent (connect) handle
ChildHandle - IN: protocol-specific handle value associated with object
*** NOT USED FOR HTTP ***
OUT: mapped address of HTTP_REQUEST_HANDLE_OBJECT
wCloseFunc - address of protocol-specific function to be called when
object is closed
*** NOT USED FOR HTTP ***
dwFlags - app-supplied flags
dwContext - app-supplied context value
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD error;
HTTP_REQUEST_HANDLE_OBJECT * hHttp;
hHttp = New HTTP_REQUEST_HANDLE_OBJECT(
(INTERNET_CONNECT_HANDLE_OBJECT *)ParentHandle,
*ChildHandle,
wCloseFunc,
dwFlags,
dwContext
);
if (hHttp != NULL) {
error = hHttp->GetStatus();
if (error == ERROR_SUCCESS) {
//
// inform the app of the new handle
//
error = InternetIndicateStatusNewHandle((LPVOID)hHttp);
//
// ERROR_WINHTTP_OPERATION_CANCELLED is the only error that we are
// expecting here. If we get this error then the app has cancelled
// the operation. Either way, the handle we just generated will be
// already deleted
//
if (error != ERROR_SUCCESS) {
INET_ASSERT(error == ERROR_WINHTTP_OPERATION_CANCELLED);
hHttp = NULL;
}
} else {
delete hHttp;
hHttp = NULL;
}
} else {
error = ERROR_NOT_ENOUGH_MEMORY;
}
*ChildHandle = (HINTERNET)hHttp;
return error;
}
//
// HTTP_REQUEST_HANDLE_OBJECT class implementation
//
HTTP_REQUEST_HANDLE_OBJECT::HTTP_REQUEST_HANDLE_OBJECT(
INTERNET_CONNECT_HANDLE_OBJECT * Parent,
HINTERNET Child,
CLOSE_HANDLE_FUNC wCloseFunc,
DWORD dwFlags,
DWORD_PTR dwContext
) : INTERNET_CONNECT_HANDLE_OBJECT(Parent)
/*++
Routine Description:
Constructor for direct-to-net HTTP_REQUEST_HANDLE_OBJECT
Arguments:
Parent - parent object
Child - IN: HTTPREQ structure pointer
OUT: pointer to created HTTP_REQUEST_HANDLE_OBJECT
wCloseFunc - address of function that closes/destroys HTTPREQ structure
dwFlags - open flags (e.g. INTERNET_FLAG_RELOAD)
dwContext - caller-supplied request context value
Return Value:
None.
--*/
{
if (g_pAsyncCount)
{
if (Parent && Parent->IsAsyncHandle())
{
g_pAsyncCount->AddRef();
}
}
else
{
RIP(FALSE);
}
_pProxyCreds = NULL;
_pServerCreds = NULL;
_PreferredScheme = 0;
_SupportedSchemes = 0;
_AuthTarget = 0;
_SecurityLevel = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
_pszRealm = NULL;
_Context = dwContext;
_Socket = NULL;
_QueryBuffer = NULL;
_QueryBufferLength = 0;
_QueryOffset = 0;
_QueryBytesAvailable = 0;
_bKeepAliveConnection = FALSE;
_bNoLongerKeepAlive = FALSE;
_OpenFlags = dwFlags | INTERNET_FLAG_KEEP_CONNECTION;
_State = HttpRequestStateCreating;
_RequestMethod = HTTP_METHOD_TYPE_UNKNOWN;
_dwOptionalSaved = 0;
_lpOptionalSaved = NULL;
_fOptionalSaved = FALSE;
_bIsWriteRequired = FALSE;
_ResponseBuffer = NULL;
_ResponseBufferLength = 0;
ResetResponseVariables();
_RequestHeaders.SetIsRequestHeaders(TRUE);
_ResponseHeaders.SetIsRequestHeaders(FALSE);
_fTalkingToSecureServerViaProxy = FALSE;
_bViaProxy = 0;
_fRequestUsingProxy = FALSE;
_bWantKeepAlive = FALSE;
_ServerInfo = NULL;
_OriginServer = NULL;
SetServerInfoWithScheme(INTERNET_SCHEME_HTTP, FALSE);
//
// set the read/write buffer sizes to the default values (4K)
//
_ReadBufferSize = (4 K);
_WriteBufferSize = (4 K);
_CacheUrlName = NULL;
SetObjectType(TypeHttpRequestHandle);
_pAuthCtx = NULL;
_pTunnelAuthCtx = NULL;
_pCreds = NULL;
_NoResetBits.Dword = 0; // only here are we ever allowed to assign to Dword.
SetDisableNTLMPreauth(GlobalDisableNTLMPreAuth);
_ProxyHostName = NULL;
_ProxyHostNameLength = NULL;
_ProxyPort = INTERNET_INVALID_PORT_NUMBER;
_SocksProxyHostName = NULL;
_SocksProxyHostNameLength = NULL;
_SocksProxyPort = INTERNET_INVALID_PORT_NUMBER;
_HaveReadFileExData = FALSE;
memset(&_BuffersOut, 0, sizeof(_BuffersOut));
_BuffersOut.dwStructSize = sizeof(_BuffersOut);
_BuffersOut.lpvBuffer = (LPVOID)&_ReadFileExData;
m_fPPAbortSend = FALSE;
_dwEnableFlags = 0;
SetPriority(0);
#ifdef RLF_TEST_CODE
static long l = 0;
SetPriority(l++);
#endif
_RTT = 0;
if (_Status == ERROR_SUCCESS) {
_Status = _RequestHeaders.GetError();
if (_Status == ERROR_SUCCESS) {
_Status = _ResponseHeaders.GetError();
}
}
// Timeout and retry parameters
INTERNET_HANDLE_OBJECT* pRoot = GetRootHandle(Parent);
OPTIONAL_SESSION_PARAMS* pParams = pRoot->GetOptionalParams();
if (pParams)
{
_dwResolveTimeout = pParams->dwResolveTimeout;
_dwConnectTimeout = pParams->dwConnectTimeout;
_dwConnectRetries = pParams->dwConnectRetries;
_dwSendTimeout = pParams->dwSendTimeout;
_dwReceiveTimeout = pParams->dwReceiveTimeout;
}
else
{
_dwResolveTimeout = GlobalResolveTimeout;
_dwConnectTimeout = GlobalConnectTimeout;
_dwConnectRetries = GlobalConnectRetries;
_dwSendTimeout = GlobalSendTimeout;
_dwReceiveTimeout = GlobalReceiveTimeout;
}
if (_OpenFlags & WINHTTP_FLAG_SECURE)
{
m_pSecurityInfo = pRoot->GetSslSessionCache()->Find(GetHostName());
if (NULL == m_pSecurityInfo)
{
m_pSecurityInfo = New SECURITY_CACHE_LIST_ENTRY(GetHostName());
}
}
else
{
m_pSecurityInfo = NULL;
}
if (_Status == ERROR_SUCCESS && IsAsyncHandle())
{
if (!_AsyncCritSec.Init())
{
_Status = ERROR_NOT_ENOUGH_MEMORY;
}
_fAsyncFsmInProgress = FALSE;
}
}
HTTP_REQUEST_HANDLE_OBJECT::~HTTP_REQUEST_HANDLE_OBJECT(
VOID
)
/*++
Routine Description:
Destructor for HTTP_REQUEST_HANDLE_OBJECT
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
None,
"~HTTP_REQUEST_HANDLE_OBJECT",
"%#x",
this
));
//
// close the socket (or free it to the pool if keep-alive)
//
//
// Authentication Note:
// The CloseConnection parameter to force the connection closed
// is set if we received a challenge but didn't respond, otherwise
// IIS will get confused when a subsequent request recycles the
// socket from the keep-alive pool.
//
CloseConnection(GetAuthState() == AUTHSTATE_CHALLENGE);
//
// If there's an authentication context, unload the provider.
//
if (_pAuthCtx) {
delete _pAuthCtx;
}
if (_pTunnelAuthCtx) {
delete _pTunnelAuthCtx;
}
//
// free the various buffers
//
FreeResponseBuffer();
FreeQueryBuffer();
SetProxyName(NULL,NULL,0);
FreeURL();
if (m_pSecurityInfo != NULL) {
m_pSecurityInfo->Release();
}
if (_pProxyCreds)
{
delete _pProxyCreds;
}
if (_pServerCreds)
{
delete _pServerCreds;
}
if (_pszRealm)
{
FREE_MEMORY(_pszRealm);
}
if (_ServerInfo != NULL)
_ServerInfo->Dereference();
if (_OriginServer != NULL)
_OriginServer->Dereference();
if (g_pAsyncCount)
{
g_pAsyncCount->Release();
}
else
{
RIP(FALSE);
}
// There should be no work items left in the blocked queue.
INET_ASSERT(_FsmWorkItemList.GetCount() == 0);
DEBUG_LEAVE(0);
}
VOID
HTTP_REQUEST_HANDLE_OBJECT::SetProxyName(
IN LPSTR lpszProxyHostName,
IN DWORD dwProxyHostNameLength,
IN INTERNET_PORT ProxyPort
)
/*++
Routine Description:
Set proxy name in object. If already have name, free it. Don't set name if
current pointer is input
Arguments:
lpszProxyHostName - pointer to proxy name to add
dwProxyHostNameLength - length of proxy name
ProxyPort - port
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_HTTP,
None,
"HTTP_REQUEST_HANDLE_OBJECT::SetProxyName",
"{%q, %d, %d}%q, %d, %d",
_ProxyHostName,
_ProxyHostNameLength,
_ProxyPort,
lpszProxyHostName,
dwProxyHostNameLength,
ProxyPort
));
if (lpszProxyHostName != _ProxyHostName) {
if (_ProxyHostName != NULL) {
_ProxyHostName = (LPSTR)FREE_MEMORY(_ProxyHostName);
INET_ASSERT(_ProxyHostName == NULL);
SetOverrideProxyMode(FALSE);
}
if (lpszProxyHostName != NULL) {
_ProxyHostName = NEW_STRING(lpszProxyHostName);
if (_ProxyHostName == NULL) {
dwProxyHostNameLength = 0;
}
}
_ProxyHostNameLength = dwProxyHostNameLength;
_ProxyPort = ProxyPort;
} else if (lpszProxyHostName != NULL) {
DEBUG_PRINT(HTTP,
WARNING,
("!!! lpszProxyHostName == _ProxyHostName (%#x)\n",
lpszProxyHostName
));
INET_ASSERT(dwProxyHostNameLength == _ProxyHostNameLength);
INET_ASSERT(ProxyPort == _ProxyPort);
}
DEBUG_LEAVE(0);
}
VOID
HTTP_REQUEST_HANDLE_OBJECT::GetProxyName(
OUT LPSTR* lplpszProxyHostName,
OUT LPDWORD lpdwProxyHostNameLength,
OUT LPINTERNET_PORT lpProxyPort
)
/*++
Routine Description:
Return address & length of proxy name plus proxy port
Arguments:
lplpszProxyHostName - returned address of name
lpdwProxyHostNameLength - returned length of name
lpProxyPort - returned port
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_HTTP,
None,
"HTTP_REQUEST_HANDLE_OBJECT::GetProxyName",
"{%q, %d, %d}%#x, %#x, %#x",
_ProxyHostName,
_ProxyHostNameLength,
_ProxyPort,
lplpszProxyHostName,
lpdwProxyHostNameLength,
lpProxyPort
));
*lplpszProxyHostName = _ProxyHostName;
*lpdwProxyHostNameLength = _ProxyHostNameLength;
*lpProxyPort = _ProxyPort;
DEBUG_LEAVE(0);
}
VOID
HTTP_REQUEST_HANDLE_OBJECT::ReuseObject(
VOID
)
/*++
Routine Description:
Make the object re-usable: clear out any received data and headers and
reset the state to open
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_HTTP,
None,
"HTTP_REQUEST_HANDLE_OBJECT::ReuseObject",
NULL
));
_ResponseHeaders.FreeHeaders();
FreeResponseBuffer();
ResetResponseVariables();
_ResponseHeaders.Initialize();
SetState(HttpRequestStateOpen);
ResetEndOfFile();
_ResponseFilterList.ClearList();
_QueryOffset = 0;
_QueryBytesAvailable = 0;
_dwQuerySetCookieHeader = 0;
if (m_pSecurityInfo) {
m_pSecurityInfo->Release();
}
m_pSecurityInfo = NULL;
DEBUG_LEAVE(0);
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::ResetObject(
IN BOOL bForce,
IN BOOL bFreeRequestHeaders
)
/*++
Routine Description:
This method is called when we we are clearing out a partially completed
transaction, mainly for when we have determined that an if-modified-since
request, or a response that has not invalidated the cache entry can be
retrieved from cache (this is a speed issue)
Abort the connection and clear out the response headers and response
buffer; clear the response variables (all done by AbortConnection()).
If bFreeRequestHeaders, clear out the request headers.
Reinitialize the response headers. We do not reset the object state, but we
do reset the end-of-file status
Arguments:
bForce - TRUE if connection is forced closed
bFreeRequestHeaders - TRUE if request headers should be freed
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::ResetObject",
"%B, %B",
bForce,
bFreeRequestHeaders
));
DWORD error;
error = AbortConnection(bForce);
if (error == ERROR_SUCCESS) {
if (bFreeRequestHeaders) {
_RequestHeaders.FreeHeaders();
}
_ResponseHeaders.Initialize();
ResetEndOfFile();
}
DEBUG_LEAVE(error);
return error;
}
VOID
HTTP_REQUEST_HANDLE_OBJECT::SetAuthenticated(
VOID
)
/*++
Routine Description:
description-of-function.
Arguments:
SetAuthenticated -
Return Value:
None.
--*/
{
if (!_Socket)
{
INET_ASSERT(FALSE);
}
else
{
_Socket->SetAuthenticated();
}
}
BOOL
HTTP_REQUEST_HANDLE_OBJECT::IsAuthenticated(
VOID
)
/*++
Routine Description:
description-of-function.
Arguments:
IsAuthenticated -
Return Value:
BOOL
--*/
{
return (_Socket ? _Socket->IsAuthenticated() : FALSE);
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::SetObjectName(
LPSTR lpszObjectName,
LPSTR lpszExtension,
URLGEN_FUNC * procProtocolUrl
)
{
DWORD dwLen, dwError;
INTERNET_SCHEME schemeType;
//
// if there is already an object name, then free it. We are replacing it
//
//
// BUGBUG - make _CacheUrlString an ICSTRING
//
FreeURL();
//
// get protocol specific url
//
if (procProtocolUrl) {
//
// if we are going via proxy AND this is an FTP object AND the user name
// consists of <username>@<servername> then <servername> is the real
// server name, and _HostName is the name of the proxy
//
//
// BUGBUG - this is a bit of a hack(!)
//
// Note: FTP support has been removed (ssulzer, 3/2000).
//
LPSTR target = _HostName.StringAddress();
schemeType = GetSchemeType();
// make the scheme type https if necessary
schemeType = (((schemeType == INTERNET_SCHEME_DEFAULT)||
(schemeType == INTERNET_SCHEME_HTTP)) &&
(GetOpenFlags() & WINHTTP_FLAG_SECURE))?
INTERNET_SCHEME_HTTPS: schemeType;
LPSTR lpszNewUrl = NULL;
dwError = (*procProtocolUrl)(schemeType,
target,
NULL,
lpszObjectName,
lpszExtension,
_HostPort,
&lpszNewUrl,
&dwLen
);
if (dwError == ERROR_SUCCESS) {
if (!SetURLPtr (&lpszNewUrl)) {
FREE_MEMORY (lpszNewUrl);
dwError = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
else {
dwError = ERROR_INVALID_PARAMETER;
}
if (dwError == ERROR_SUCCESS) {
DEBUG_PRINT(HANDLE,
INFO,
("Url: %s\n",
_CacheUrlName
));
}
return dwError;
}
//=============================================================================
BOOL HTTP_REQUEST_HANDLE_OBJECT::GetUserAndPass
(BOOL fProxy, LPSTR *pszUser, LPSTR *pszPass)
{
DWORD dwUser, dwPass;
if (fProxy)
{
dwUser = WINHTTP_OPTION_PROXY_USERNAME & WINHTTP_OPTION_MASK;
dwPass = WINHTTP_OPTION_PROXY_PASSWORD & WINHTTP_OPTION_MASK;
}
else
{
dwUser = WINHTTP_OPTION_USERNAME & WINHTTP_OPTION_MASK;
dwPass = WINHTTP_OPTION_PASSWORD & WINHTTP_OPTION_MASK;
}
*pszUser = _xsProp[dwUser].GetPtr();
*pszPass = _xsProp[dwPass].GetPtr();
if (*pszUser && *pszPass)
return TRUE;
else
{
*pszUser = NULL;
*pszPass = NULL;
return FALSE;
}
}
//=============================================================================
BOOL HTTP_REQUEST_HANDLE_OBJECT::SetURL (LPSTR lpszUrl)
{
LPSTR lpszNew;
// Make an undecorated copy of the URL.
lpszNew = NewString(lpszUrl);
if (!lpszNew)
return FALSE;
// Clear any previous cache key and record the new one.
FreeURL();
INET_ASSERT (lpszNew);
_CacheUrlName = lpszNew;
return TRUE;
}
//=============================================================================
BOOL HTTP_REQUEST_HANDLE_OBJECT::SetURLPtr(LPSTR* ppszUrl)
{
// Swap in the new URL as the cache key.
FreeURL();
_CacheUrlName = *ppszUrl;
*ppszUrl = NULL;
return TRUE;
}
//=============================================================================
DWORD HTTP_REQUEST_HANDLE_OBJECT::SetServerInfoWithScheme(
IN INTERNET_SCHEME tScheme,
IN BOOL bDoResolution,
IN OPTIONAL BOOL fNtlm
)
/*++
Routine Description:
Associates a SERVER_INFO with this INTERNET_CONNECT_HANDLE_OBJECT based on
the host name for which this object was created and an optional scheme
type
Arguments:
tScheme - scheme type we want SERVER_INFO for
bDoResolution - TRUE if we are to resolve the host name if creating a new
SERVER_INFO object
fNtlm - TRUE if we are tunnelling for NTLM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
Dword,
"INTERNET_CONNECT_HANDLE_OBJECT::SetServerInfo",
"%s (%d), %B, %B",
InternetMapScheme(tScheme),
tScheme,
bDoResolution,
fNtlm
));
if (_ServerInfo != NULL) {
::ReleaseServerInfo(_ServerInfo);
}
//
// use the base service type to find the server info
//
//dprintf("getting server info for %q (current = %q)\n", hostName, GetHostName());
INTERNET_HANDLE_OBJECT * lpParent = GetRootHandle (this);
DWORD error = lpParent->GetServerInfo(GetHostName(),
INTERNET_SERVICE_HTTP,
bDoResolution,
&_ServerInfo
);
DEBUG_LEAVE(error);
return error;
}
//=============================================================================
DWORD HTTP_REQUEST_HANDLE_OBJECT::SetServerInfo(
IN LPSTR lpszServerName,
IN DWORD dwServerNameLength
)
/*++
Routine Description:
Associates a SERVER_INFO with this INTERNET_CONNECT_HANDLE_OBJECT based on
the host name in the parameters
Arguments:
lpszServerName - name of server
dwServerNameLength - length of lpszServerName
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
Dword,
"INTERNET_CONNECT_HANDLE_OBJECT::SetServerInfo",
"%q, %d",
lpszServerName,
dwServerNameLength
));
if (_ServerInfo != NULL) {
::ReleaseServerInfo(_ServerInfo);
}
//
// use the base service type to find the server info
//
char hostName[INTERNET_MAX_HOST_NAME_LENGTH + 1];
int copyLength = (int)min(sizeof(hostName) - 1, dwServerNameLength);
memcpy(hostName, lpszServerName, copyLength);
hostName[copyLength] = '\0';
INTERNET_HANDLE_OBJECT * lpParent = GetRootHandle (this);
DWORD error = lpParent->GetServerInfo(hostName,
INTERNET_SERVICE_HTTP,
FALSE,
&_ServerInfo
);
DEBUG_LEAVE(error);
return error;
}
//=============================================================================
VOID HTTP_REQUEST_HANDLE_OBJECT::SetOriginServer(
IN CServerInfo * pServerInfo
)
/*++
Routine Description:
description-of-function.
Arguments:
pServerInfo -
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
None,
"INTERNET_CONNECT_HANDLE_OBJECT::SetOriginServer",
"%#x{%q}",
pServerInfo,
pServerInfo ? pServerInfo->GetHostName() : ""
));
if (_OriginServer == NULL) {
_OriginServer = pServerInfo;
if (pServerInfo != NULL) {
pServerInfo->Reference();
}
}
DEBUG_LEAVE(0);
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::ScheduleWorkItem()
{
DEBUG_ENTER((DBG_ASYNC,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::ScheduleWorkItem",
NULL
));
CFsm *pFsm = NULL;
DWORD dwError = ERROR_SUCCESS;
_FsmWorkItemList.DequeueHead(&pFsm);
if (pFsm)
{
DEBUG_PRINT(ASYNC,
INFO,
("Queueing work item %#x with %d blocked async work items remaining\n",
pFsm,
_FsmWorkItemList.GetCount()
));
pFsm->SetThreadInfo(InternetGetThreadInfo());
pFsm->SetPushPop(TRUE);
pFsm->Push();
dwError = pFsm->QueueWorkItem();
}
DEBUG_LEAVE(dwError);
return dwError;
}