/*++ 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 // // 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 @ then 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; }