/*++ Copyright (c) 1994 Microsoft Corporation Module Name: thrdinfo.cxx Abstract: Functions to manipulate an INTERNET_THREAD_INFO Contents: InternetCreateThreadInfo InternetDestroyThreadInfo InternetTerminateThreadInfo InternetGetThreadInfo InternetSetThreadInfo InternetIndicateStatusAddress InternetIndicateStatusString InternetIndicateStatusNewHandle InternetIndicateStatus InternetSetLastError _InternetSetLastError InternetLockErrorText InternetUnlockErrorText InternetSetObjectHandle InternetGetObjectHandle InternetFreeThreadInfo Author: Richard L Firth (rfirth) 16-Feb-1995 Environment: Win32 user-level DLL Revision History: 16-Feb-1995 rfirth Created --*/ #include #include // // manifests // #define BAD_TLS_INDEX 0xffffffff // according to online win32 SDK documentation #ifdef SPX_SUPPORT #define GENERIC_SPX_NAME "SPX Server" #endif //SPX_SUPPORT // // macros // #ifdef ENABLE_DEBUG #define InitializeInternetThreadInfo(lpThreadInfo) \ InitializeListHead(&lpThreadInfo->List); \ lpThreadInfo->Signature = INTERNET_THREAD_INFO_SIGNATURE; \ lpThreadInfo->ThreadId = GetCurrentThreadId(); #else #define InitializeInternetThreadInfo(threadInfo) \ InitializeListHead(&lpThreadInfo->List); \ lpThreadInfo->ThreadId = GetCurrentThreadId(); #endif // ENABLE_DEBUG // // private data // PRIVATE DWORD InternetTlsIndex = BAD_TLS_INDEX; PRIVATE SERIALIZED_LIST ThreadInfoList; LPINTERNET_THREAD_INFO InternetCreateThreadInfo( IN BOOL SetTls ) /*++ Routine Description: Creates, initializes an INTERNET_THREAD_INFO. Optionally (allocates and) sets this thread's Internet TLS Assumes: 1. The first time this function is called is in the context of the process attach library call, so we allocate the TLS index once Arguments: SetTls - TRUE if we are to set the INTERNET_THREAD_INFO TLS for this thread Return Value: LPINTERNET_THREAD_INFO Success - pointer to allocated INTERNET_THREAD_INFO structure which has been set as this threads value in its InternetTlsIndex slot Failure - NULL --*/ { LPINTERNET_THREAD_INFO lpThreadInfo = NULL; BOOL ok = FALSE; if (InDllCleanup) { goto quit; } if (InternetTlsIndex == BAD_TLS_INDEX) { // // first time through, initialize serialized list // InitializeSerializedList(&ThreadInfoList); // // we assume that if we are allocating the TLS index, then this is the // one and only thread in this process that can call into this DLL // right now - i.e. this thread is loading the DLL // InternetTlsIndex = TlsAlloc(); } if (InternetTlsIndex != BAD_TLS_INDEX) { lpThreadInfo = NEW(INTERNET_THREAD_INFO); if (lpThreadInfo != NULL) { InitializeInternetThreadInfo(lpThreadInfo); if (SetTls) { ok = TlsSetValue(InternetTlsIndex, (LPVOID)lpThreadInfo); if (!ok) { DEBUG_PUT(("InternetCreateThreadInfo(): TlsSetValue(%d, %#x) returns %d\n", InternetTlsIndex, lpThreadInfo, GetLastError() )); DEBUG_BREAK(THRDINFO); } } else { ok = TRUE; } } else { DEBUG_PUT(("InternetCreateThreadInfo(): NEW(INTERNET_THREAD_INFO) returned NULL\n")); DEBUG_BREAK(THRDINFO); } } else { DEBUG_PUT(("InternetCreateThreadInfo(): TlsAlloc() returns %#x, error %d\n", BAD_TLS_INDEX, GetLastError() )); DEBUG_BREAK(THRDINFO); } if (ok) { if (!InsertAtHeadOfSerializedList(&ThreadInfoList, &lpThreadInfo->List)) { DEL(lpThreadInfo); lpThreadInfo = NULL; if (InternetTlsIndex != BAD_TLS_INDEX) { TlsFree(InternetTlsIndex); InternetTlsIndex = BAD_TLS_INDEX; } } } else { if (lpThreadInfo != NULL) { DEL(lpThreadInfo); lpThreadInfo = NULL; } if (InternetTlsIndex != BAD_TLS_INDEX) { TlsFree(InternetTlsIndex); InternetTlsIndex = BAD_TLS_INDEX; } } quit: return lpThreadInfo; } VOID InternetDestroyThreadInfo( VOID ) /*++ Routine Description: Cleans up the INTERNET_THREAD_INFO - deletes any memory it owns and deletes it Arguments: None. Return Value: None. --*/ { LPINTERNET_THREAD_INFO lpThreadInfo; IF_DEBUG(NOTHING) { DEBUG_PUT(("InternetDestroyThreadInfo(): Thread %#x: Deleting INTERNET_THREAD_INFO\n", GetCurrentThreadId() )); } // // don't call InternetGetThreadInfo() - we don't need to create the // INTERNET_THREAD_INFO if it doesn't exist in this case // lpThreadInfo = (LPINTERNET_THREAD_INFO)TlsGetValue(InternetTlsIndex); if (lpThreadInfo != NULL) { #if INET_DEBUG // // there shouldn't be anything in the debug record stack. On Win95, we // ignore this check if this is the async scheduler (nee worker) thread // AND there are entries in the debug record stack. The async thread // gets killed off before it has chance to DEBUG_LEAVE, then comes here, // causing this assert to be over-active // if (IsPlatformWin95() && lpThreadInfo->IsAsyncWorkerThread) { if (lpThreadInfo->CallDepth != 0) { DEBUG_PUT(("InternetDestroyThreadInfo(): " "Thread %#x: " "%d records in debug stack\n", lpThreadInfo->CallDepth )); } } else { INET_ASSERT(lpThreadInfo->Stack == NULL); } #endif // INET_DEBUG InternetFreeThreadInfo(lpThreadInfo); INET_ASSERT(InternetTlsIndex != BAD_TLS_INDEX); TlsSetValue(InternetTlsIndex, NULL); } else { DEBUG_PUT(("InternetDestroyThreadInfo(): Thread %#x: no INTERNET_THREAD_INFO\n", GetCurrentThreadId() )); } } VOID InternetFreeThreadInfo( IN LPINTERNET_THREAD_INFO lpThreadInfo ) /*++ Routine Description: Removes the INTERNET_THREAD_INFO from the list and frees all allocated blocks Arguments: lpThreadInfo - pointer to INTERNET_THREAD_INFO to remove and free Return Value: None. --*/ { if (RemoveFromSerializedList(&ThreadInfoList, &lpThreadInfo->List)) { if (lpThreadInfo->hErrorText != NULL) { FREE_MEMORY(lpThreadInfo->hErrorText); } //if (lpThreadInfo->lpResolverInfo != NULL) { // if (lpThreadInfo->lpResolverInfo->DnrSocketHandle != NULL) { // lpThreadInfo->lpResolverInfo->DnrSocketHandle->Dereference(); // } // DEL(lpThreadInfo->lpResolverInfo); //} DEL(lpThreadInfo); } } VOID InternetTerminateThreadInfo( VOID ) /*++ Routine Description: Destroy all INTERNET_THREAD_INFO structures and terminate the serialized list. This funciton called at process detach time. At DLL_PROCESS_DETACH time, there may be other threads in the process for which we created an INTERNET_THREAD_INFO that aren't going to get the chance to delete the structure, so we do it here. Code in this module assumes that it is impossible for a new thread to enter this DLL while we are terminating in DLL_PROCESS_DETACH Arguments: None. Return Value: None. --*/ { // // get rid of this thread's info structure. No more debug output after this! // InternetDestroyThreadInfo(); // // get rid of the thread info structures left by other threads // if (LockSerializedList(&ThreadInfoList)) { LPINTERNET_THREAD_INFO lpThreadInfo; while (lpThreadInfo = (LPINTERNET_THREAD_INFO)SlDequeueHead(&ThreadInfoList)) { // // already dequeued, no need to call InternetFreeThreadInfo() // FREE_MEMORY(lpThreadInfo); } UnlockSerializedList(&ThreadInfoList); } // // no more need for list // TerminateSerializedList(&ThreadInfoList); // // or TLS index // TlsFree(InternetTlsIndex); InternetTlsIndex = BAD_TLS_INDEX; } LPINTERNET_THREAD_INFO InternetGetThreadInfo( VOID ) /*++ Routine Description: Gets the pointer to the INTERNET_THREAD_INFO for this thread and checks that it still looks good. If this thread does not have an INTERNET_THREAD_INFO then we create one, presuming that this is a new thread Arguments: None. Return Value: LPINTERNET_THREAD_INFO Success - pointer to INTERNET_THREAD_INFO block Failure - NULL --*/ { LPINTERNET_THREAD_INFO lpThreadInfo = NULL; DWORD lastError; // // this is pretty bad - TlsGetValue() can destroy the per-thread last error // variable if it returns NULL (to indicate that NULL was actually set, and // that NULL does not indicate an error). So we have to read it before it is // potentially destroyed, and reset it before we quit. // // We do this here because typically, other functions will be completely // unsuspecting of this behaviour, and it is better to fix it once here, // than in several dozen other places, even though it is slightly // inefficient // lastError = GetLastError(); if (InternetTlsIndex != BAD_TLS_INDEX) { lpThreadInfo = (LPINTERNET_THREAD_INFO)TlsGetValue(InternetTlsIndex); } // // we may be in the process of creating the INTERNET_THREAD_INFO, in // which case its okay for this to be NULL. According to online SDK // documentation, a threads TLS value will be initialized to NULL // if (lpThreadInfo == NULL) { // // we presume this is a new thread. Create an INTERNET_THREAD_INFO // IF_DEBUG(NOTHING) { DEBUG_PUT(("InternetGetThreadInfo(): Thread %#x: Creating INTERNET_THREAD_INFO\n", GetCurrentThreadId() )); } lpThreadInfo = InternetCreateThreadInfo(TRUE); } if (lpThreadInfo != NULL) { INET_ASSERT(lpThreadInfo->Signature == INTERNET_THREAD_INFO_SIGNATURE); INET_ASSERT(lpThreadInfo->ThreadId == GetCurrentThreadId()); } else { DEBUG_PUT(("InternetGetThreadInfo(): Failed to get/create INTERNET_THREAD_INFO\n")); } // // as above - reset the last error variable in case TlsGetValue() trashed it // SetLastError(lastError); // // actual success/failure indicated by non-NULL/NULL pointer resp. // return lpThreadInfo; } VOID InternetSetThreadInfo( IN LPINTERNET_THREAD_INFO lpThreadInfo ) /*++ Routine Description: Sets lpThreadInfo as the current thread's INTERNET_THREAD_INFO. Used within fibers Arguments: lpThreadInfo - new INTERNET_THREAD_INFO to set Return Value: None. --*/ { if (InternetTlsIndex != BAD_TLS_INDEX) { if (!TlsSetValue(InternetTlsIndex, (LPVOID)lpThreadInfo)) { DEBUG_PUT(("InternetSetThreadInfo(): TlsSetValue(%d, %#x) returns %d\n", InternetTlsIndex, lpThreadInfo, GetLastError() )); INET_ASSERT(FALSE); } } else { DEBUG_PUT(("InternetSetThreadInfo(): InternetTlsIndex = %d\n", InternetTlsIndex )); INET_ASSERT(FALSE); } } DWORD InternetIndicateStatusAddress( IN DWORD dwInternetStatus, IN LPSOCKADDR lpSockAddr, IN DWORD dwSockAddrLength ) /*++ Routine Description: Make a status callback to the app. The data is a network address that we need to convert to a string Arguments: dwInternetStatus - WINHTTP_CALLBACK_STATUS_ value lpSockAddr - pointer to full socket address dwSockAddrLength - length of lpSockAddr in bytes Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_WINHTTP_OPERATION_CANCELLED The app closed the object handle during the callback --*/ { LPSTR lpAddress; INET_ASSERT(lpSockAddr != NULL); switch (lpSockAddr->sa_family) { case AF_INET: lpAddress = _I_inet_ntoa( ((struct sockaddr_in*)lpSockAddr)->sin_addr ); break; case AF_IPX: // // BUGBUG - this should be a call to WSAAddressToString, but that's not implemented yet // #ifdef SPX_SUPPORT lpAddress = GENERIC_SPX_NAME; #else lpAddress = NULL; #endif //SPX_SUPPORT break; default: lpAddress = NULL; break; } // we don't want a client to mess around with a winsock-internal buffer return InternetIndicateStatusString(dwInternetStatus, lpAddress, TRUE/*bCopyBuffer*/); } DWORD InternetIndicateStatusString( IN DWORD dwInternetStatus, IN LPSTR lpszStatusInfo OPTIONAL, IN BOOL bCopyBuffer, IN BOOL bConvertToUnicode ) /*++ Routine Description: Make a status callback to the app. The data is a string Arguments: dwInternetStatus - WINHTTP_CALLBACK_STATUS_ value lpszStatusInfo - string status data Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_WINHTTP_OPERATION_CANCELLED The app closed the object handle during the callback --*/ { DEBUG_ENTER((DBG_THRDINFO, Dword, "InternetIndicateStatusString", "%d, %q", dwInternetStatus, lpszStatusInfo )); DWORD length; if (ARGUMENT_PRESENT(lpszStatusInfo)) { length = strlen(lpszStatusInfo) + 1; } else { length = 0; } DWORD error; error = InternetIndicateStatus(dwInternetStatus, lpszStatusInfo, length, bCopyBuffer, bConvertToUnicode); DEBUG_LEAVE(error); return error; } DWORD InternetIndicateStatusNewHandle( IN LPVOID hInternetMapped ) /*++ Routine Description: Indicates to the app a new handle Arguments: hInternetMapped - mapped address of new handle being indicated Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_WINHTTP_OPERATION_CANCELLED The app closed the either the new object handle or the parent object handle during the callback --*/ { DEBUG_ENTER((DBG_THRDINFO, Dword, "InternetIndicateStatusNewHandle", "%#x", hInternetMapped )); HANDLE_OBJECT * hObject = (HANDLE_OBJECT *)hInternetMapped; // // reference the new request handle, in case the app closes it in the // callback. The new handle now has a reference count of 2 // hObject->Reference(); INET_ASSERT(hObject->ReferenceCount() == 2); // // we indicate the pseudo handle to the app // HINTERNET hInternet = hObject->GetPseudoHandle(); DWORD error = InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, (LPVOID)&hInternet, sizeof(hInternet) ); // // dereference the new request handle. If this returns TRUE then the new // handle has been deleted (the app called InternetCloseHandle() against // it which dereferenced it to 1, and now we've dereferenced it to zero) // if (hObject->Dereference()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; } else if (error == ERROR_WINHTTP_OPERATION_CANCELLED) { // // the parent handle was deleted. Kill off the new handle too // BOOL ok; ok = hObject->Dereference(); INET_ASSERT(ok); } DEBUG_LEAVE(error); return error; } DWORD InternetIndicateStatus( IN DWORD dwStatus, IN LPVOID lpBuffer, IN DWORD dwLength, IN BOOL bCopyBuffer, IN BOOL bConvertToUnicode ) /*++ Routine Description: If the app has registered a callback function for the object that this thread is operating on, call it with the arguments supplied Arguments: dwStatus - WINHTTP_CALLBACK_STATUS_ value lpBuffer - pointer to variable data buffer dwLength - length of *lpBuffer in bytes Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_WINHTTP_OPERATION_CANCELLED The app closed the object handle during the callback --*/ { DEBUG_ENTER((DBG_THRDINFO, Dword, "InternetIndicateStatus", "%s, %#x, %d", InternetMapStatus(dwStatus), lpBuffer, dwLength )); LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo(); DWORD error = ERROR_SUCCESS; // // the app can affect callback operation by specifying a zero context value // meaning no callbacks will be generated for this API // if (lpThreadInfo != NULL) { INET_ASSERT(lpThreadInfo->hObject != NULL); INET_ASSERT(lpThreadInfo->hObjectMapped != NULL); // // if the context value in the thread info block is 0 then we use the // context from the handle object // DWORD_PTR context; context = ((INTERNET_HANDLE_BASE *)lpThreadInfo->hObjectMapped)->GetContext(); WINHTTP_STATUS_CALLBACK appCallback; appCallback = ((INTERNET_HANDLE_BASE *)lpThreadInfo->hObjectMapped)->GetStatusCallback(); IF_DEBUG(THRDINFO) { switch (dwStatus) { case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: DEBUG_PRINT(THRDINFO, INFO, ("%s: dwResult = %#x, dwError = %d [%s]\n", InternetMapStatus(dwStatus), ((LPWINHTTP_ASYNC_RESULT)lpBuffer)->dwError, InternetMapError(((LPWINHTTP_ASYNC_RESULT)lpBuffer)->dwError) )); break; case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: DEBUG_PRINT(THRDINFO, INFO, ("%s: Buffer = %p, Number of bytes = %d\n", InternetMapStatus(dwStatus), lpBuffer, dwLength )); break; } } if ((appCallback != NULL) && (((INTERNET_HANDLE_BASE *)lpThreadInfo->hObjectMapped)->IsNotificationEnabled(dwStatus)) ) { LPVOID pInfo; //reported thru callback DWORD infoLength; //reported thru callback BOOL isAsyncWorkerThread; BYTE buffer[256]; // // we make a copy of the info to remove the app's opportunity to // change it. E.g. if we were about to resolve host name "foo" and // passed the pointer to our buffer containing "foo", the app could // change the name to "bar", changing the intended server // if (lpBuffer != NULL) { if (bConvertToUnicode) { INET_ASSERT( ((INTERNET_HANDLE_BASE *)lpThreadInfo->hObjectMapped)->IsUnicodeStatusCallback() ); INET_ASSERT( (dwStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) || (dwStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) || (dwStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) || (dwStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) || (dwStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) ); infoLength = MultiByteToWideChar(CP_ACP, 0, (LPSTR)lpBuffer, dwLength, NULL, 0); if (infoLength == 0) { pInfo = NULL; DEBUG_PRINT(THRDINFO, ERROR, ("MultiByteToWideChar returned 0 for a %d-length MBCS string\n", dwLength )); } else if (infoLength <= sizeof(buffer)/sizeof(WCHAR)) { pInfo = buffer; } else { pInfo = (LPVOID)ALLOCATE_FIXED_MEMORY(infoLength * sizeof(WCHAR)); } if (pInfo) { infoLength = MultiByteToWideChar(CP_ACP, 0, (LPSTR)lpBuffer, dwLength, (LPWSTR)pInfo, infoLength); if (infoLength == 0) { //MBtoWC failed if (pInfo != buffer) FREE_FIXED_MEMORY(pInfo); pInfo = NULL; DEBUG_PRINT(THRDINFO, ERROR, ("MultiByteToWideChar returned 0 for a %d-length MBCS string\n", dwLength )); } } //pInfo else { infoLength = 0; DEBUG_PRINT(THRDINFO, ERROR, ("MultiByteToWideChar() error OR Failed to allocate %d bytes for info\n", dwLength )); } //pInfo == NULL } //bConvertToUnicode else if (bCopyBuffer) { if (dwLength <= sizeof(buffer)) pInfo = buffer; else pInfo = (LPVOID)ALLOCATE_FIXED_MEMORY(dwLength); if (pInfo) { memcpy(pInfo, lpBuffer, dwLength); infoLength = dwLength; } else { infoLength = 0; DEBUG_PRINT(THRDINFO, ERROR, ("Failed to allocate %d bytes for info\n", dwLength )); } } //bCopyBuffer else { pInfo = lpBuffer; infoLength = dwLength; INET_ASSERT(dwLength); } //!bCopyBuffer && !bConvertToUnicode } //lpBuffer != NULL else { pInfo = NULL; infoLength = 0; } // // we're about to call into the app. We may be in the context of an // async worker thread, and if the callback submits an async request // then we'll execute it synchronously. To avoid this, we will reset // the async worker thread indicator in the INTERNET_THREAD_INFO and // restore it when the app returns control to us. This way, if the // app makes an API request during the callback, on a handle that // has async I/O semantics, then we will simply queue it, and not // try to execute it synchronously // isAsyncWorkerThread = lpThreadInfo->IsAsyncWorkerThread; lpThreadInfo->IsAsyncWorkerThread = FALSE; BOOL bInCallback = lpThreadInfo->InCallback; lpThreadInfo->InCallback = TRUE; INET_ASSERT(!IsBadCodePtr((FARPROC)appCallback)); DEBUG_ENTER((DBG_THRDINFO, None, "(*callback)", "%#x, %#x, %s (%d), %#x [%#x], %d", lpThreadInfo->hObject, context, InternetMapStatus(dwStatus), dwStatus, pInfo, ((dwStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) || (dwStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)) ? (DWORD_PTR)*(LPHINTERNET)pInfo : (((dwStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) || (dwStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) || (dwStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE)) ? *(LPDWORD)pInfo : 0), infoLength )); PERF_LOG(PE_APP_CALLBACK_START, dwStatus, lpThreadInfo->ThreadId, lpThreadInfo->hObject ); HINTERNET hObject = lpThreadInfo->hObject; LPVOID hObjectMapped = lpThreadInfo->hObjectMapped; appCallback(lpThreadInfo->hObject, context, dwStatus, pInfo, infoLength ); lpThreadInfo->hObject = hObject; lpThreadInfo->hObjectMapped = hObjectMapped; PERF_LOG(PE_APP_CALLBACK_END, dwStatus, lpThreadInfo->ThreadId, lpThreadInfo->hObject ); DEBUG_LEAVE(0); lpThreadInfo->InCallback = bInCallback; lpThreadInfo->IsAsyncWorkerThread = isAsyncWorkerThread; // // free the buffer // // We should free the memory only if we have done an ALLOCATE_FIXED_MEMORY in this function: if (pInfo != NULL && pInfo != lpBuffer && pInfo != buffer) { FREE_FIXED_MEMORY(pInfo); } } else { DEBUG_PRINT(THRDINFO, ERROR, ("%#x: callback = %#x, context = %#x\n", lpThreadInfo->hObject, appCallback, context )); // // if we're completing a request then we shouldn't be here - it // means we lost the context or callback address somewhere along the // way // // don't need the ASSERTS below. // It could also mean something as benign as the notification not being enabled: /* INET_ASSERT( dwStatus != WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE && dwStatus != WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE && dwStatus != WINHTTP_CALLBACK_STATUS_REQUEST_ERROR && dwStatus != WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE && dwStatus != WINHTTP_CALLBACK_STATUS_READ_COMPLETE ); */ #ifdef DEBUG if ( dwStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE || dwStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE || dwStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || dwStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE || dwStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE ) { INET_ASSERT(appCallback != NULL); /* These are not valid asserts in winhttp. Contexts don't control whether callbacks are made or not. */ //INET_ASSERT(context != NULL); //INET_ASSERT(_InternetGetContext(lpThreadInfo) != NULL); } #endif } // // if the object is now invalid then the app closed the handle in // the callback, or from an external thread and the entire operation is cancelled // propagate this error back to calling code. // if (((HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->IsInvalidated()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; } } else { // // this is catastrophic if the indication was async request completion // DEBUG_PUT(("InternetIndicateStatus(): no INTERNET_THREAD_INFO?\n")); } DEBUG_LEAVE(error); return error; } DWORD InternetSetLastError( IN DWORD ErrorNumber, IN LPSTR ErrorText, IN DWORD ErrorTextLength, IN DWORD Flags ) /*++ Routine Description: Copies the error text to the per-thread error buffer (moveable memory) Arguments: ErrorNumber - protocol-specific error code ErrorText - protocol-specific error text (from server). The buffer is NOT zero-terminated ErrorTextLength - number of characters in ErrorText Flags - Flags that control how this function operates: SLE_APPEND TRUE if ErrorText is to be appended to the text already in the buffer SLE_ZERO_TERMINATE TRUE if ErrorText must have a '\0' appended to it Return Value: DWORD Success - ERROR_SUCCESS Failure - Win32 error --*/ { DEBUG_ENTER((DBG_THRDINFO, Dword, "InternetSetLastError", "%d, %.80q, %d, %#x", ErrorNumber, ErrorText, ErrorTextLength, Flags )); DWORD error; LPINTERNET_THREAD_INFO lpThreadInfo; lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo != NULL) { error = _InternetSetLastError(lpThreadInfo, ErrorNumber, ErrorText, ErrorTextLength, Flags ); } else { DEBUG_PUT(("InternetSetLastError(): no INTERNET_THREAD_INFO\n")); error = ERROR_WINHTTP_INTERNAL_ERROR; } DEBUG_LEAVE(error); return error; } DWORD _InternetSetLastError( IN LPINTERNET_THREAD_INFO lpThreadInfo, IN DWORD ErrorNumber, IN LPSTR ErrorText, IN DWORD ErrorTextLength, IN DWORD Flags ) /*++ Routine Description: Sets or resets the last error text in an INTERNET_THREAD_INFO block Arguments: lpThreadInfo - pointer to INTERNET_THREAD_INFO ErrorNumber - protocol-specific error code ErrorText - protocol-specific error text (from server). The buffer is NOT zero-terminated ErrorTextLength - number of characters in ErrorText Flags - Flags that control how this function operates: SLE_APPEND TRUE if ErrorText is to be appended to the text already in the buffer SLE_ZERO_TERMINATE TRUE if ErrorText must have a '\0' appended to it Return Value: DWORD Success - ERROR_SUCCESS Failure - Win32 error --*/ { DEBUG_ENTER((DBG_THRDINFO, Dword, "_InternetSetLastError", "%#x, %d, %.80q, %d, %#x", lpThreadInfo, ErrorNumber, ErrorText, ErrorTextLength, Flags )); DWORD currentLength; DWORD newTextLength; DWORD error; newTextLength = ErrorTextLength; // // if we are appending text, then account for the '\0' currently at the end // of the buffer (if it exists) // if (Flags & SLE_APPEND) { currentLength = lpThreadInfo->ErrorTextLength; if (currentLength != 0) { --currentLength; } newTextLength += currentLength; } if (Flags & SLE_ZERO_TERMINATE) { ++newTextLength; } // // expect success (and why not?) // error = ERROR_SUCCESS; // // allocate, grow or shrink the buffer to fit. The buffer is moveable. If // the buffer is being shrunk to zero size then NULL will be returned as // the buffer handle from ResizeBuffer() // lpThreadInfo->hErrorText = ResizeBuffer(lpThreadInfo->hErrorText, newTextLength, FALSE ); if (lpThreadInfo->hErrorText != NULL) { LPSTR lpErrorText; lpErrorText = (LPSTR)LOCK_MEMORY(lpThreadInfo->hErrorText); INET_ASSERT(lpErrorText != NULL); if (lpErrorText != NULL) { if (Flags & SLE_APPEND) { lpErrorText += currentLength; } memcpy(lpErrorText, ErrorText, ErrorTextLength); if (Flags & SLE_ZERO_TERMINATE) { lpErrorText[ErrorTextLength++] = '\0'; } // // the text should always be zero-terminated. We expect this in // InternetGetLastResponseInfo() // INET_ASSERT(lpErrorText[ErrorTextLength - 1] == '\0'); UNLOCK_MEMORY(lpThreadInfo->hErrorText); } else { // // real error occurred - failed to lock memory? // error = GetLastError(); } } else { INET_ASSERT(newTextLength == 0); newTextLength = 0; } // // set the error code and text length // lpThreadInfo->ErrorTextLength = newTextLength; lpThreadInfo->ErrorNumber = ErrorNumber; DEBUG_LEAVE(error); return error; } LPSTR InternetLockErrorText( VOID ) /*++ Routine Description: Returns a pointer to the locked per-thread error text buffer Arguments: None. Return Value: LPSTR Success - pointer to locked buffer Failure - NULL --*/ { LPINTERNET_THREAD_INFO lpThreadInfo; lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo != NULL) { HLOCAL lpErrorText; lpErrorText = lpThreadInfo->hErrorText; if (lpErrorText != (HLOCAL)NULL) { return (LPSTR)LOCK_MEMORY(lpErrorText); } } return NULL; } // //VOID //InternetUnlockErrorText( // VOID // ) // ///*++ // //Routine Description: // // Unlocks the per-thread error text buffer locked by InternetLockErrorText() // //Arguments: // // None. // //Return Value: // // None. // //--*/ // //{ // LPINTERNET_THREAD_INFO lpThreadInfo; // // lpThreadInfo = InternetGetThreadInfo(); // // // // // assume that if we locked the error text, there must be an // // INTERNET_THREAD_INFO when we come to unlock it // // // // INET_ASSERT(lpThreadInfo != NULL); // // if (lpThreadInfo != NULL) { // // HLOCAL hErrorText; // // hErrorText = lpThreadInfo->hErrorText; // // // // // similarly, there must be a handle to the error text buffer // // // // INET_ASSERT(hErrorText != NULL); // // if (hErrorText != (HLOCAL)NULL) { // UNLOCK_MEMORY(hErrorText); // } // } //} VOID InternetSetObjectHandle( IN HINTERNET hInternet, IN HINTERNET hInternetMapped ) /*++ Routine Description: Sets the hObject field in the INTERNET_THREAD_INFO structure so we can get at the handle contents, even when we're in a function that does not take the hInternet as a parameter Arguments: hInternet - handle of object we may need info from hInternetMapped - mapped handle of object we may need info from Return Value: None. --*/ { LPINTERNET_THREAD_INFO lpThreadInfo; lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo != NULL) { _InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped); } } HINTERNET InternetGetObjectHandle( VOID ) /*++ Routine Description: Just returns the hObject value stored in our INTERNET_THREAD_INFO Arguments: None. Return Value: HINTERNET Success - non-NULL handle value Failure - NULL object handle (may not have been set) --*/ { LPINTERNET_THREAD_INFO lpThreadInfo; HINTERNET hInternet; lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo != NULL) { hInternet = lpThreadInfo->hObject; } else { hInternet = NULL; } return hInternet; } HINTERNET InternetGetMappedObjectHandle( VOID ) /*++ Routine Description: Just returns the hObjectMapped value stored in our INTERNET_THREAD_INFO Arguments: None. Return Value: HINTERNET Success - non-NULL handle value Failure - NULL object handle (may not have been set) --*/ { LPINTERNET_THREAD_INFO lpThreadInfo; HINTERNET hInternet; lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo != NULL) { hInternet = lpThreadInfo->hObjectMapped; } else { hInternet = NULL; } return hInternet; }