/*++ 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::GetHandle HTTP_REQUEST_HANDLE_OBJECT::SetProxyName HTTP_REQUEST_HANDLE_OBJECT::GetProxyName HTTP_REQUEST_HANDLE_OBJECT::ReuseObject HTTP_REQUEST_HANDLE_OBJECT::ResetObject HTTP_REQUEST_HANDLE_OBJECT::UrlCacheUnlock 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_INTERNET_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_INTERNET_OPERATION_CANCELLED); hHttp = NULL; } } else { delete hHttp; hHttp = NULL; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } *ChildHandle = (HINTERNET)hHttp; if(hHttp) { hHttp->Dereference(); } 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. --*/ { Reference(); _Context = dwContext; _Socket = NULL; _QueryBuffer = NULL; _QueryBufferLength = 0; _QueryOffset = 0; _QueryBytesAvailable = 0; _bKeepAliveConnection = FALSE; _bNoLongerKeepAlive = FALSE; _OpenFlags = dwFlags; _State = HttpRequestStateCreating; _RequestMethod = HTTP_METHOD_TYPE_UNKNOWN; _dwOptionalSaved = 0; _lpOptionalSaved = NULL; _fOptionalSaved = FALSE; _ResponseBuffer = NULL; _ResponseBufferLength = 0; ResetResponseVariables(); _RequestHeaders.SetIsRequestHeaders(TRUE); _ResponseHeaders.SetIsRequestHeaders(FALSE); _fTalkingToSecureServerViaProxy = FALSE; _fRequestUsingProxy = FALSE; _bWantKeepAlive = FALSE; _bRefresh = FALSE; _RefreshHeader = NULL; _redirectCount = GlobalMaxHttpRedirects; _redirectCountedOut = FALSE; _fIgnoreOffline = FALSE; _f3rdPartyCookies = FALSE; _fBlockedOnPrompt = FALSE; SetObjectType(TypeHttpRequestHandle); _pCacheEntryInfo = NULL; _dwCacheEntryType = 0; _pAuthCtx = NULL; _pTunnelAuthCtx = NULL; _pPWC = NULL; _lpBlockingFilter = NULL; _dwCredPolicy = 0xFFFFFFFF; _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; HttpFiltOpen(); // enumerate http filters if not already active _HaveReadFileExData = FALSE; memset(&_BuffersOut, 0, sizeof(_BuffersOut)); _BuffersOut.dwStructSize = sizeof(_BuffersOut); _BuffersOut.lpvBuffer = (LPVOID)&_ReadFileExData; _fAutoProxyChecked = FALSE; m_pSecurityInfo = NULL; m_AuthFlag = 0x00000000; m_fPPAbortSend = FALSE; _fSendUTF8ServerNameToProxy = FALSE; SetPriority(0); _dwSecurityZone = 0xffffffff; /* initialize to invalid zone value */ #ifdef RLF_TEST_CODE static long l = 0; SetPriority(l++); #endif _RTT = 0; _CP = CP_ACP; if (_Status == ERROR_SUCCESS) { _Status = _RequestHeaders.GetError(); if (_Status == ERROR_SUCCESS) { _Status = _ResponseHeaders.GetError(); } } _dwSocketSendBufferLength = -1; if ((_OpenFlags & INTERNET_FLAG_SECURE) && (_Status = LoadSecurity()) == ERROR_SUCCESS) { m_pSecurityInfo = GlobalCertCache.Find(GetHostName()); if (NULL == m_pSecurityInfo) { m_pSecurityInfo = new SECURITY_CACHE_LIST_ENTRY(GetHostName()); // Force a net hit, since this hasn't been fully validated. SetCacheReadDisabled(); } } else { m_pSecurityInfo = NULL; } } 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 (IsCacheWriteInProgress()) { LocalEndCacheWrite(IsEndOfFile()); } if (IsCacheReadInProgress()) { INET_ASSERT (_pCacheEntryInfo); FREE_MEMORY (_pCacheEntryInfo); // Rest is cleaned up in INTERNET_CONNECT_HANDLE_OBJECT::EndCacheWrite } else { UrlCacheUnlock(); } // // 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); if (m_pSecurityInfo != NULL) { m_pSecurityInfo->Release(); } DEBUG_LEAVE(0); } HINTERNET HTTP_REQUEST_HANDLE_OBJECT::GetHandle( VOID ) /*++ Routine Description: Returns child handle value. NULL for HTTP Arguments: None. Return Value: HINTERNET NULL --*/ { return NULL; } 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(); _dwCurrentStreamPosition = 0; SetState(HttpRequestStateOpen); ResetEndOfFile(); _ctChunkInfo.Reset(); _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::UrlCacheUnlock( VOID ) /*++ Routine Description: description-of-function. Arguments: None. Return Value: None. --*/ { INET_ASSERT (!_CacheReadInProgress); if (_hCacheStream) { UnlockUrlCacheEntryStream (_hCacheStream, 0); _hCacheStream = NULL; } if (_pCacheEntryInfo) { if (_pCacheEntryInfo->CacheEntryType & SPARSE_CACHE_ENTRY) { // // We can't use the partial cache entry because it is // stale, so delete the data file we got from cache. // INET_ASSERT (_CacheFileHandle != INVALID_HANDLE_VALUE); CloseHandle (_CacheFileHandle); _CacheFileHandle = INVALID_HANDLE_VALUE; DeleteFile (_pCacheEntryInfo->lpszLocalFileName); } FREE_MEMORY (_pCacheEntryInfo); _pCacheEntryInfo = NULL; } } 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); }