/*++ Copyright (c) 2000-2002 Microsoft Corporation Module Name: ClientApi.c Abstract: User-mode interface to HTTP.SYS: Client-side APIs Author: Rajesh Sundaram (rajeshsu) 1-Aug-2000 Revision History: --*/ #include "precomp.h" #include // // Private macros. // #define HTTP_PREFIX_W L"HTTP://" #define HTTP_PREFIX_LENGTH (sizeof(HTTP_PREFIX_W) - sizeof(WCHAR)) #define HTTPS_PREFIX_W L"HTTPS://" #define HTTPS_PREFIX_LENGTH (sizeof(HTTPS_PREFIX_W) - sizeof(WCHAR)) #define HTTP_DEFAULT_PORT 80 #define HTTPS_DEFAULT_PORT 443 // // Ssl stream filter stuff. // WCHAR g_StrmFilt[] = L"strmfilt.dll"; LONG g_bStrmFiltLoaded = 0; HMODULE g_hStrmFilt = NULL; FARPROC g_pStrmFiltInitialize = NULL; FARPROC g_pStrmFiltStart = NULL; FARPROC g_pStrmFiltStop = NULL; FARPROC g_pStrmFiltTerminate = NULL; extern CRITICAL_SECTION g_InitCritSec; #ifndef DBG #define DbgCriticalSectionOwned(pcs) (TRUE) #else /***************************************************************************++ Routine Description: This routine determines if the calling thread owns a critical section. Arguments: pcs - Pointer to CRITICAL_SECTION. Return Value: BOOLEAN --***************************************************************************/ BOOLEAN DbgCriticalSectionOwned( PCRITICAL_SECTION pcs ) { #define HANDLE_TO_DWORD(Handle) ((DWORD)PtrToUlong(Handle)) if (pcs->LockCount >= 0 && HANDLE_TO_DWORD(pcs->OwningThread) == GetCurrentThreadId()) { return TRUE; } return FALSE; } #endif /***************************************************************************++ Routine Description: This function resolves a name to an IP. Arguments: pServerName - The name to be resolved. ServerNameLength - Length of the name (in WCHAR). Return Value: DWORD - Completion status. --***************************************************************************/ DWORD ResolveName( IN PWCHAR pServerName, IN USHORT ServerNameLength, IN USHORT PortNumber, OUT PTRANSPORT_ADDRESS *pTransportAddress, OUT PUSHORT TransportAddressLength ) { LPSTR pBuffer; ULONG BufferLen; struct addrinfo *pAi, *pTempAi; ULONG AiLen, AiCount; DWORD dwResult; PTA_ADDRESS CurrentAddress; // // We need space to store the ANSI version of the name. // BufferLen = WideCharToMultiByte( CP_ACP, 0, pServerName, ServerNameLength, NULL, 0, NULL, NULL); if(!BufferLen) { return ERROR_INVALID_PARAMETER; } // // Account for '\0' // BufferLen = BufferLen + 1; pBuffer = RtlAllocateHeap(RtlProcessHeap(), 0, BufferLen ); if(!pBuffer) { return ERROR_NOT_ENOUGH_MEMORY; } // // Convert the name to ANSI. // if(WideCharToMultiByte(CP_ACP, 0, pServerName, ServerNameLength, pBuffer, BufferLen-1, NULL, NULL) == 0) { dwResult = GetLastError(); RtlFreeHeap(RtlProcessHeap(), 0, pBuffer); return dwResult; } // // NULL terminate it. // *(pBuffer + BufferLen - 1) = 0; // // Resolve it. // if((dwResult = getaddrinfo(pBuffer, NULL, 0, &pAi)) != 0) { RtlFreeHeap(RtlProcessHeap(), 0, pBuffer); return dwResult; } else { // // Compute size of all entries returned by getaddrinfo // pTempAi = pAi; AiLen = 0; AiCount = 0; while(pAi != NULL) { if(pAi->ai_family == PF_INET || pAi->ai_family == AF_INET6) { AiCount ++; // // ai_addrlength includes the size of the AddressType, // but TA_ADDRESS expects AddressLength to exclude this. // AiLen = AiLen + ((ULONG)pAi->ai_addrlen - RTL_FIELD_SIZE(TA_ADDRESS, AddressType)); } pAi = pAi->ai_next; } if(AiCount == 0) { // No addresses found. return ERROR_INVALID_PARAMETER; } AiLen += ((AiCount * FIELD_OFFSET(TA_ADDRESS, Address)) + FIELD_OFFSET(TRANSPORT_ADDRESS, Address)); if(BufferLen >= AiLen) { *pTransportAddress = (PTRANSPORT_ADDRESS) pBuffer; } else { RtlFreeHeap(RtlProcessHeap(), 0, pBuffer ); *pTransportAddress = (PTRANSPORT_ADDRESS) RtlAllocateHeap(RtlProcessHeap(), 0, AiLen ); if(!*pTransportAddress) { freeaddrinfo(pAi); return ERROR_NOT_ENOUGH_MEMORY; } } // // Convert this to a Transport Address. // pAi = pTempAi; (*pTransportAddress)->TAAddressCount = AiCount; CurrentAddress = (*pTransportAddress)->Address; while(pAi != NULL) { switch(pAi->ai_family) { case PF_INET: case PF_INET6: // // ai_addrlength includes the size of the AddressType, // but TA_ADDRESS expects AddressLength to exclude this. // CurrentAddress->AddressLength = (USHORT) pAi->ai_addrlen - RTL_FIELD_SIZE(TA_ADDRESS, AddressType); CurrentAddress->AddressType = pAi->ai_addr->sa_family; RtlCopyMemory( &CurrentAddress->Address, pAi->ai_addr->sa_data, CurrentAddress->AddressLength ); if(PF_INET == pAi->ai_family) { ((TDI_ADDRESS_IP *) CurrentAddress->Address)->sin_port = htons(PortNumber); } else { ((TDI_ADDRESS_IP6 *) CurrentAddress->Address)->sin6_port = htons(PortNumber); } CurrentAddress = (PTA_ADDRESS) (CurrentAddress->Address + CurrentAddress->AddressLength); break; default: break; } pAi = pAi->ai_next; } *TransportAddressLength = (USHORT) AiLen; freeaddrinfo(pTempAi); } return NO_ERROR; } /***************************************************************************++ Routine Description: This function takes in a string (of the form "Hostname[:PortNumber]"), performs DNS lookup on "Hostname". Returns a TRANSPORT_ADDRESS containing the resolved Host address and port number. Hostname can be: hostname (e.g. foo.bar.com) or IPv4 address (e.g. 128.101.35.201) or IPv6 address (e.g. [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]) This Function is a HACK and will go away when we do DNS in the kernel. Arguments: pServerLocationStr - (Input) Hostname:PortNumber string ServerLocationStrLength - (Input) Server Location string length pTransportAddress - (Output) Transport address (and port) of Server TransportAddressLength - (Output) Length of the structure pointed to by pTransportAddress Return Value: DWORD - Completion status. --***************************************************************************/ DWORD ProcessHostAndPort( IN PWCHAR pServerLocationStr, IN USHORT ServerLocationStrLength, IN USHORT DefaultPort, OUT PTRANSPORT_ADDRESS *pTransportAddress, OUT PUSHORT pTransportAddressLength ) { DWORD Status; ULONG PortNumber = 0; PWSTR ptr; PWSTR pEndStr = pServerLocationStr + (ServerLocationStrLength/sizeof(WCHAR)); PWSTR pStartHostname = pServerLocationStr; PWSTR pEndHostname = pEndStr; // may change due to presence of port # // // Empty Host String? // if (pEndStr == pServerLocationStr) return ERROR_INVALID_PARAMETER; // // check if the HostStr contains IPv6 address (RFC 2732) // if (*pServerLocationStr == L'[') { // skip '[' pStartHostname = pServerLocationStr + 1; // find the matching ']' for (ptr = pServerLocationStr+1; ptr != pEndStr && *ptr != L']'; ptr++) /* do nothing */; // missing ']'? if (ptr == pEndStr) return ERROR_INVALID_PARAMETER; // IPv6 host address ends here pEndHostname = ptr; // skip ']' ptr++; if (ptr != pEndStr && *ptr != L':') return ERROR_INVALID_PARAMETER; } else // Host name or IPv4 address is present { pStartHostname = pServerLocationStr; // Check if a port number is present for (ptr = pServerLocationStr; ptr != pEndStr && *ptr != L':'; ptr++) /* do nothing */; pEndHostname = ptr; } // If a port number is present...grab it. if (ptr != pEndStr) { ASSERT(*ptr == L':'); for (ptr++; ptr != pEndStr; ptr++) { if (!iswdigit(*ptr)) { // Junk instead of digits return ERROR_INVALID_PARAMETER; } PortNumber = 10*PortNumber + (ULONG)(*ptr - L'0'); // Port numbers are only 16 bit wide if (PortNumber >= (ULONG)(1<<16)) { return ERROR_INVALID_PARAMETER; } } } if(PortNumber == 0) { PortNumber = DefaultPort; } Status = ResolveName(pStartHostname, (USHORT) (pEndHostname-pStartHostname), (USHORT)PortNumber, pTransportAddress, pTransportAddressLength ); return Status; } /****************************************************************************++ Routine Description: Loads dymanically linked library strmfilt.dll. If the library is already loaded, it returns NO_ERROR Arguments: None. Return Value: NO_ERROR - Library was loaded (now or previously) successfully Other errors as encountered. --****************************************************************************/ DWORD LoadStrmFilt( VOID ) { LONG OldValue; HRESULT hr; DWORD Error; // // Quick check outside the lock to see if the library is already loaded. // if (g_bStrmFiltLoaded) { return NO_ERROR; } // // Make sure there is no other thread trying to load the library. // EnterCriticalSection(&g_InitCritSec); if (g_bStrmFiltLoaded == 0) { // // Library is not loaded. Proceed to load StrmFilt.dll. // g_hStrmFilt = LoadLibrary(g_StrmFilt); if (g_hStrmFilt == NULL) { Error = GetLastError(); goto Quit; } // // Get addresses of the following procedures: // StreamFilterClientInitialize, StreamFilterClientTerminate // StreamFilterClientStart, StreamFilterClientStop // g_pStrmFiltInitialize = GetProcAddress(g_hStrmFilt, "StreamFilterClientInitialize"); if (g_pStrmFiltInitialize == NULL) { Error = GetLastError(); goto Unload; } g_pStrmFiltStart = GetProcAddress(g_hStrmFilt, "StreamFilterClientStart"); if (g_pStrmFiltStart == NULL) { Error = GetLastError(); goto Unload; } g_pStrmFiltStop = GetProcAddress(g_hStrmFilt, "StreamFilterClientStop"); if (g_pStrmFiltStop == NULL) { Error = GetLastError(); goto Unload; } g_pStrmFiltTerminate = GetProcAddress(g_hStrmFilt, "StreamFilterClientTerminate"); if (g_pStrmFiltTerminate == NULL) { Error = GetLastError(); goto Unload; } // // Try to initialize StrmFilt. // hr = (HRESULT)g_pStrmFiltInitialize(); if (SUCCEEDED(hr)) { // // StrmFilt initialized suceessfully. Now try to start it. // hr = (HRESULT)g_pStrmFiltStart(); if (FAILED(hr)) { // // Could not start StrmFilt. Terminate it! // g_pStrmFiltTerminate(); } } if (FAILED(hr)) { Error = (DWORD)hr; goto Unload; } // // Remember that StrmFilt has been loaded and initialized. // Atomically set bStrmFilt to 1. The reason for atomic operation // is that g_bStrmFiltLoaded can be read without a lock. // OldValue = InterlockedExchange(&g_bStrmFiltLoaded, 1); // // g_bStrmFiltLoaded is always set to 1 under g_InitCritSec. // ASSERT(OldValue == 0); } // // This is the normal case, when everything went OK. // LeaveCriticalSection(&g_InitCritSec); return NO_ERROR; // // Erroneous cases come here. // Unload: // // Set these function pointers so that they aren't be used. // g_pStrmFiltInitialize = NULL; g_pStrmFiltStart = NULL; g_pStrmFiltStop = NULL; g_pStrmFiltTerminate = NULL; // // Unload strmfilt.dll. // ASSERT(g_hStrmFilt); FreeLibrary(g_hStrmFilt); g_hStrmFilt = NULL; Quit: LeaveCriticalSection(&g_InitCritSec); return Error; } /****************************************************************************++ Routine Description: Unloads strmfilt.dll, if loaded previously. This routine is called inside g_InitCritSec critical section. Arguments: None. Return Value: NO_ERROR if strmfilt.dll successfully unloaded. Other errors as encountered. --****************************************************************************/ DWORD UnloadStrmFilt( VOID ) { LONG OldValue; ASSERT(DbgCriticalSectionOwned(&g_InitCritSec)); OldValue = InterlockedExchange(&g_bStrmFiltLoaded, 0); // // Has strmfilt been initialized before? // if (OldValue == 0) { return ERROR_NOT_FOUND; } ASSERT(OldValue == 1); // // Sanity checks. // ASSERT(g_pStrmFiltInitialize); ASSERT(g_pStrmFiltStart); ASSERT(g_pStrmFiltStop); ASSERT(g_pStrmFiltTerminate); // // Stop StreamFilter and terminate it. // g_pStrmFiltStop(); g_pStrmFiltTerminate(); // // Set these function pointers so that they aren't used. // g_pStrmFiltInitialize = NULL; g_pStrmFiltStart = NULL; g_pStrmFiltStop = NULL; g_pStrmFiltTerminate = NULL; // // Unload strmfilt.dll. // ASSERT(g_hStrmFilt); FreeLibrary(g_hStrmFilt); g_hStrmFilt = NULL; return NO_ERROR; } /***************************************************************************++ Routine Description: This is the first api that an application uses before it can talk to a server. This call creates a NT FileHandle for the origin server Arguments: ServerNameLength - The Length of server name pServerName - The Full URI (starts with http[s]://servername/...) dwServerFlags - Flags pConfigInfo - Array of config objects pReserved - Must be NULL pServerHandle - The File Handle Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpInitializeServerContext( IN USHORT ServerNameLength, IN PWCHAR pServerName, IN USHORT ProxyLength OPTIONAL, IN PWCHAR pProxy OPTIONAL, IN ULONG ServerFlags OPTIONAL, IN PVOID pReserved, OUT PHANDLE pServerHandle ) { NTSTATUS Status; ULONG Win32Status; PTRANSPORT_ADDRESS pTransportAddress; USHORT TransportAddressLength = 0; DWORD CreateDisposition; PWCHAR pServerNameStart = NULL; USHORT DefaultPort; if(ServerFlags != 0 || pReserved != NULL) { return ERROR_INVALID_PARAMETER; } CreateDisposition = FILE_CREATE; // // Process the scheme name. We need at least one character after the // http:// or https://, so the comparision is > instead of >= // if(ServerNameLength > HTTP_PREFIX_LENGTH) { if (_wcsnicmp(pServerName, HTTP_PREFIX_W, HTTP_PREFIX_LENGTH/sizeof(WCHAR)) == 0) { pServerNameStart = pServerName + (HTTP_PREFIX_LENGTH/sizeof(WCHAR)); DefaultPort = HTTP_DEFAULT_PORT; } else if(ServerNameLength > HTTPS_PREFIX_LENGTH) { if (_wcsnicmp(pServerName, HTTPS_PREFIX_W, HTTPS_PREFIX_LENGTH/sizeof(WCHAR)) == 0) { pServerNameStart = pServerName + (HTTPS_PREFIX_LENGTH/sizeof(WCHAR)); // // If an HTTPS server is being initialized, load strmfilt.dll // DefaultPort = HTTPS_DEFAULT_PORT; Win32Status = LoadStrmFilt(); if (Win32Status != NO_ERROR) { return Win32Status; } } else { // neither http:// nor https:// return ERROR_INVALID_PARAMETER; } } else { // Not enough space to compare https:// return ERROR_INVALID_PARAMETER; } } else { // Not enough space to compare http:// return ERROR_INVALID_PARAMETER; } ASSERT(pServerNameStart != NULL); // // We don't do DNS in the kernel yet, so for now, we'll do DNS resolution // in user mode and pass the IP address across the boundary. This hack // has to be removed when we get DNS support in the kernel. // if(ProxyLength) { // The user has supplied a proxy, we don't have to resolve the // server name. // DefaultPort = HTTP_DEFAULT_PORT; if((Win32Status = ProcessHostAndPort(pProxy, ProxyLength, DefaultPort, &pTransportAddress, &TransportAddressLength )) != NO_ERROR) { return Win32Status; } } else { PWCHAR pServerNameEnd; PWCHAR pUriEnd = pServerName + (ServerNameLength / sizeof(WCHAR)); // // At this point pUri points to the first thing after the scheme. Walk // through the URI until either we hit the end or find a terminating /. // // By the comparision above, we are guaranteed to have at least one // character ASSERT(pUriEnd != pServerNameStart); pServerNameEnd = pServerNameStart; while(*pServerNameEnd != L'/') { pServerNameEnd ++; // See if we still have URI left to examine. if (pServerNameEnd == pUriEnd) { break; } } // Check for a zero server name - if(pServerNameStart == pServerNameEnd) { return ERROR_INVALID_PARAMETER; } if((Win32Status = ProcessHostAndPort( pServerNameStart, (USHORT) (pServerNameEnd - pServerNameStart) * sizeof(WCHAR), DefaultPort, &pTransportAddress, &TransportAddressLength)) != NO_ERROR) { return Win32Status; } } Status = HttpApiOpenDriverHelper( pServerHandle, pServerName, // URI ServerNameLength, pProxy, // Proxy ProxyLength, pTransportAddress, TransportAddressLength, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, // Acces HttpApiServerHandleType, 0, // Object Name 0, // Options CreateDisposition, // createDisposition NULL ); // // If we couldn't open the driver because it's not running, then try // to start the driver & retry the open. // if (Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND) { if (HttpApiTryToStartDriver(HTTP_SERVICE_NAME)) { Status = HttpApiOpenDriverHelper( pServerHandle, pServerName, // URI ServerNameLength, pProxy, // Proxy ProxyLength, pTransportAddress, TransportAddressLength, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, // Desired Acces HttpApiServerHandleType, 0, // Object Name 0, // Options CreateDisposition, // createDisposition NULL ); } } // // Need to free the pTransportAddress // RtlFreeHeap(RtlProcessHeap(), 0, pTransportAddress ); return HttpApiNtStatusToWin32Status( Status ); } /***************************************************************************++ Routine Description: Sends an HTTP request. Arguments: ServerHandle - Supplies the handle corresponding to a particular server. This is the handle as returned by HttpInitializeServerContext. pHttpRequest - The HTTP request. HttpRequestFlags - The Request Flags. pConfig - Config information for this request. pOverlapped - Overlapped structure for Overlapped I/O. ResponseBufferLength - Contains the response buffer length. ResponseBuffer - A pointer to a buffer to return the response. pBytesReceived - The # of bytes that actually got written. pRequestID - A pointer to a request identifier - This will return an ID that can be used in subsequent calls. Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpSendHttpRequest( IN HANDLE ServerHandle, IN PHTTP_REQUEST pHttpRequest, IN ULONG HttpRequestFlags, IN USHORT RequestConfigCount OPTIONAL, IN PHTTP_REQUEST_CONFIG pRequestConfig OPTIONAL, IN LPOVERLAPPED pOverlapped OPTIONAL, IN ULONG ResponseBufferLength OPTIONAL, OUT PHTTP_RESPONSE pResponseBuffer OPTIONAL, IN ULONG Reserved, // must be 0 OUT PVOID pReserved, // must be NULL OUT PULONG pBytesReceived OPTIONAL, OUT PHTTP_REQUEST_ID pRequestID ) { HTTP_SEND_REQUEST_INPUT_INFO HttpSendRequestInput; if(Reserved != 0 || pReserved != NULL) { return ERROR_INVALID_PARAMETER; } RtlZeroMemory(&HttpSendRequestInput, sizeof(HTTP_SEND_REQUEST_INPUT_INFO)); HTTP_SET_NULL_ID(pRequestID); HttpSendRequestInput.pHttpRequestId = pRequestID; HttpSendRequestInput.pHttpRequest = pHttpRequest; HttpSendRequestInput.HttpRequestFlags = HttpRequestFlags; HttpSendRequestInput.pRequestConfig = pRequestConfig; HttpSendRequestInput.RequestConfigCount = RequestConfigCount; HttpSendRequestInput.pBytesTaken = pBytesReceived; return HttpApiDeviceControl( ServerHandle, // FileHandle pOverlapped, // Overlapped IOCTL_HTTP_SEND_REQUEST, // IO Control code &HttpSendRequestInput, // InputBuffer sizeof(HttpSendRequestInput), // InputBufferLength pResponseBuffer, // Output Buffer ResponseBufferLength, // Output Buffer length pBytesReceived ); } /***************************************************************************++ Routine Description: Sends additional entity bodies. Arguments: ServerHandle - Supplies the handle corresponding to a particular server. This is the handle as returned by HttpInitializeServerContext. RequestID - The request ID that was returned by HttpSendRequest. Flags - The Request Flags. pOverlapped - Overlapped structure for Overlapped I/O. EntityBodyLength - The count of entity bodies. pHttpEntityBody - The pointer to the entity bodies. ID that can be used in subsequent calls. Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpSendRequestEntityBody( IN HANDLE ServerHandle, IN HTTP_REQUEST_ID RequestID, IN ULONG Flags, IN USHORT EntityBodyCount, IN PHTTP_DATA_CHUNK pHttpEntityBody, IN LPOVERLAPPED pOverlapped OPTIONAL ) { HTTP_SEND_REQUEST_ENTITY_BODY_INFO HttpEntity; RtlZeroMemory(&HttpEntity, sizeof(HTTP_SEND_REQUEST_ENTITY_BODY_INFO)); HttpEntity.EntityChunkCount = EntityBodyCount; HttpEntity.Flags = Flags; HttpEntity.RequestID = RequestID; HttpEntity.pHttpEntityChunk = pHttpEntityBody; return HttpApiDeviceControl( ServerHandle, // FileHandle pOverlapped, // Overlapped IOCTL_HTTP_SEND_REQUEST_ENTITY_BODY, // IO Control code &HttpEntity, // InputBuffer sizeof(HttpEntity), // InputBufferLength NULL, // ResponseBuffer 0, // Output Buffer length NULL // Bytes received. ); } /***************************************************************************++ Routine Description: Receives the response Arguments: ServerHandle - Supplies the handle corresponding to a particular server.This is the handle as returned by HttpInitializeServerContext. RequestID - The request ID that was returned by HttpSendRequest. Flags - The Request Flags. pOverlapped - Overlapped structure for Overlapped I/O. EntityBodyLength - The count of entity bodies. pHttpEntityBody - The pointer to the entity bodies. ID that can be used in subsequent calls. Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpReceiveHttpResponse( IN HANDLE ServerHandle, IN HTTP_REQUEST_ID RequestID, IN ULONG Flags, IN ULONG ResponseBufferLength, OUT PHTTP_RESPONSE pResponseBuffer, IN ULONG Reserved, // must be 0 OUT PVOID pReserved, // must be NULL OUT PULONG pBytesReceived OPTIONAL, IN LPOVERLAPPED pOverlapped OPTIONAL ) { HTTP_RECEIVE_RESPONSE_INFO HttpResponse; if(Reserved != 0 || pReserved != NULL) { return ERROR_INVALID_PARAMETER; } RtlZeroMemory(&HttpResponse, sizeof(HTTP_RECEIVE_RESPONSE_INFO)); HttpResponse.RequestID = RequestID; HttpResponse.Flags = Flags; HttpResponse.pBytesTaken = pBytesReceived; return HttpApiDeviceControl( ServerHandle, // FileHandle pOverlapped, // Overlapped IOCTL_HTTP_RECEIVE_RESPONSE, // IO Control code &HttpResponse, // InputBuffer sizeof(HttpResponse), // InputBufferLength pResponseBuffer, // Output Buffer ResponseBufferLength, // Output Buffer length pBytesReceived // Bytes received. ); } /***************************************************************************++ Routine Description: Sets server config Arguments: ServerHandle - Supplies the handle corresponding to a particular server. This is the handle as returned by HttpInitializeServerContext. pConfig - The Config object Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpSetServerContextInformation( IN HANDLE ServerHandle, IN HTTP_SERVER_CONFIG_ID ConfigId, IN PVOID pInputBuffer, IN ULONG InputBufferLength, IN LPOVERLAPPED pOverlapped ) { HTTP_SERVER_CONTEXT_INFORMATION Info; Info.ConfigID = ConfigId; Info.pInputBuffer = pInputBuffer; Info.InputBufferLength = InputBufferLength; Info.pBytesTaken = NULL; return HttpApiDeviceControl( ServerHandle, pOverlapped, IOCTL_HTTP_SET_SERVER_CONTEXT_INFORMATION, &Info, sizeof(Info), NULL, 0, NULL ); } /***************************************************************************++ Routine Description: Query server config Arguments: ServerHandle - Supplies the handle corresponding to a particular server. This is the handle as returned by HttpInitializeServerContext. pConfig - The Config object Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpQueryServerContextInformation( IN HANDLE ServerHandle, IN HTTP_SERVER_CONFIG_ID ConfigId, IN PVOID pReserved1, IN ULONG Reserved2, OUT PVOID pOutputBuffer, IN ULONG OutputBufferLength, OUT PULONG pReturnLength, IN LPOVERLAPPED pOverlapped ) { HTTP_SERVER_CONTEXT_INFORMATION Info; if(pReserved1 != NULL || Reserved2 != 0) { return ERROR_INVALID_PARAMETER; } Info.ConfigID = ConfigId; Info.pInputBuffer = NULL; Info.InputBufferLength = 0; Info.pBytesTaken = pReturnLength; return HttpApiDeviceControl( ServerHandle, pOverlapped, IOCTL_HTTP_QUERY_SERVER_CONTEXT_INFORMATION, &Info, sizeof(Info), pOutputBuffer, OutputBufferLength, pReturnLength ); } /***************************************************************************++ Routine Description: Cancels a request Arguments: ServerHandle - Supplies the handle corresponding to a particular server.This is the handle as returned by HttpInitializeServerContext. RequestID - The request ID that was returned by HttpSendRequest. pOverlapped - Overlapped structure. Return Value: ULONG - Completion status. --***************************************************************************/ ULONG WINAPI HttpCancelHttpRequest( IN HANDLE ServerHandle, IN HTTP_REQUEST_ID RequestID, IN ULONG Flags, IN LPOVERLAPPED pOverlapped OPTIONAL ) { HTTP_RECEIVE_RESPONSE_INFO HttpResponse; RtlZeroMemory(&HttpResponse, sizeof(HTTP_RECEIVE_RESPONSE_INFO)); HttpResponse.RequestID = RequestID; HttpResponse.Flags = Flags; return HttpApiDeviceControl( ServerHandle, // FileHandle pOverlapped, // Overlapped IOCTL_HTTP_CANCEL_REQUEST, // IO Control code &HttpResponse, // InputBuffer sizeof(HttpResponse), // InputBufferLength NULL, 0, NULL ); }