|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name : w3request.cxx
Abstract: Friendly wrapper for UL_HTTP_REQUEST Author: Bilal Alam (balam) 13-Dec-1999
Environment: Win32 - User Mode
Project: ULW3.DLL --*/
#include "precomp.hxx"
//
// LocalHost address used to determining whether request is local/remote
//
#define LOCAL127 0x0100007F // 127.0.0.1
#define DEFAULT_PORT 80
#define DEFAULT_PORT_SECURE 443
LPADDRINFO W3_REQUEST::sm_pHostAddrInfo = NULL;
ALLOC_CACHE_HANDLER * W3_CLONE_REQUEST::sm_pachCloneRequests;
VOID W3_REQUEST::RemoveDav( VOID ) /*++
Routine Description:
Remove DAV'ness of request
Arguments:
None
Return Value:
None
--*/ { //
// Remove translate header
//
DeleteHeader( "Translate" ); DeleteHeader( "If" ); DeleteHeader( "Lock-Token" ); }
BOOL W3_REQUEST::IsSuspectUrl( VOID ) /*++
Routine Description: Is the URL for this request look suspect? Suspect means there is a ./ pattern in it which could cause a metabase equivilency issue.
Arguments:
None
Return Value:
TRUE if the URL is suspect, else FALSE
--*/ { WCHAR * pszDotSlash; WCHAR * pszUrl; WCHAR chTemp; DWORD cchAbsPathLen; pszUrl = (PWSTR) _pUlHttpRequest->CookedUrl.pAbsPath; cchAbsPathLen = _pUlHttpRequest->CookedUrl.AbsPathLength / sizeof( WCHAR ); //
// URL UL gives us has backslashes flipped. But it is not 0 terminated
//
chTemp = pszUrl[ cchAbsPathLen ]; pszUrl[ cchAbsPathLen ] = L'\0'; pszDotSlash = wcsstr( pszUrl, L"./" ); pszUrl[ cchAbsPathLen ] = chTemp; if ( pszDotSlash != NULL ) { return TRUE; } //
// If the URL ends with a ., it is also suspect
//
if ( pszUrl[ cchAbsPathLen - 1 ] == L'.' ) { return TRUE; } return FALSE; }
BOOL W3_REQUEST::QueryClientWantsDisconnect( VOID ) /*++
Routine Description:
Returns whether the client wants to disconnect, based on its version and connection: headers
Arguments:
None
Return Value:
TRUE if client wants to disconnect
--*/ { HTTP_VERSION version; LPCSTR pszConnection; version = QueryVersion(); //
// If 0.9, then disconnect
//
if ( HTTP_EQUAL_VERSION( version, 0, 9 ) ) { return TRUE; } //
// If 1.0 and Connection: keep-alive isn't present
//
if ( HTTP_EQUAL_VERSION( version, 1, 0 ) ) { pszConnection = GetHeader( HttpHeaderConnection ); if ( pszConnection == NULL || _stricmp( pszConnection, "Keep-Alive" ) != 0 ) { return TRUE; } } //
// If 1.1 and Connection: Close is present
//
if ( HTTP_EQUAL_VERSION( version, 1, 1 ) ) { pszConnection = GetHeader( HttpHeaderConnection ); if ( pszConnection != NULL && _stricmp( pszConnection, "Close" ) == 0 ) { return TRUE; } }
return FALSE; }
HRESULT W3_REQUEST::SetHeadersByStream( CHAR * pszStream ) /*++
Routine Description:
Set request headers based on given stream
Arguments:
pszHeaderStream - Stream to parse and set headers off
Return Value:
HRESULT
--*/ { CHAR * pszCursor; CHAR * pszEnd; CHAR * pszColon; HRESULT hr = NO_ERROR; STACK_STRA( strHeaderLine, 128 ); STACK_STRA( strHeaderName, 32 ); STACK_STRA( strHeaderValue, 64 ); if ( pszStream == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// \r\n delimited
//
pszCursor = pszStream; while ( pszCursor != NULL && *pszCursor != '\0' ) { if ( *pszCursor == '\r' && *(pszCursor + 1) == '\n' ) { break; } pszEnd = strstr( pszCursor, "\r\n" ); if ( pszEnd == NULL ) { break; } //
// Split out a line
//
hr = strHeaderLine.Copy( pszCursor, (DWORD)DIFF(pszEnd - pszCursor) ); if ( FAILED( hr ) ) { goto Finished; }
//
// Advance the cursor the right after the \r\n
//
pszCursor = pszEnd + 2; //
// Split the line above into header:value
//
pszColon = strchr( strHeaderLine.QueryStr(), ':' ); if ( pszColon == NULL ) { //
// Expecting name:value. Just skip for now
//
continue; } hr = strHeaderName.Copy( strHeaderLine.QueryStr(), (DWORD)DIFF(pszColon - strHeaderLine.QueryStr()) ); if ( FAILED( hr ) ) { goto Finished; } //
// Skip the first space after the : if there is one
//
if ( pszColon[ 1 ] == ' ' ) { pszColon++; } hr = strHeaderValue.Copy( pszColon + 1 ); if ( FAILED( hr ) ) { goto Finished; }
//
// Add the header to the response
//
hr = SetHeader( strHeaderName, strHeaderValue, FALSE ); if ( FAILED( hr ) ) { goto Finished; } }
Finished: return hr; }
DWORD W3_REQUEST::QueryIPv4LocalAddress( VOID ) const /*++
Routine Description:
Get the local IP address connected to, in host order (assumes IPv4)
Arguments:
None
Return Value:
Address
--*/ { PSOCKADDR_IN pAddress;
DBG_ASSERT( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET ); pAddress = (PSOCKADDR_IN)_pUlHttpRequest->Address.pLocalAddress; return pAddress->sin_addr.s_addr; }
DWORD W3_REQUEST::QueryIPv4RemoteAddress( VOID ) const /*++
Routine Description:
Get the remote IP address connected to, in host order (assumes IPv4)
Arguments:
None
Return Value:
Address
--*/ { PSOCKADDR_IN pAddress;
DBG_ASSERT( _pUlHttpRequest->Address.pRemoteAddress->sa_family == AF_INET ); pAddress = (PSOCKADDR_IN)_pUlHttpRequest->Address.pRemoteAddress; return pAddress->sin_addr.s_addr; }
IN6_ADDR * W3_REQUEST::QueryIPv6LocalAddress( VOID ) const /*++
Routine Description:
Get the local IP address connected to, in host order (assumes IPv6)
Arguments:
None
Return Value:
Address
--*/ { PSOCKADDR_IN6 pAddress;
DBG_ASSERT( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET6 ); pAddress = (PSOCKADDR_IN6)_pUlHttpRequest->Address.pLocalAddress; return &pAddress->sin6_addr; }
IN6_ADDR * W3_REQUEST::QueryIPv6RemoteAddress( VOID ) const /*++
Routine Description:
Get the remote IP address connecting to us, in host order (assumes IPv6)
Arguments:
None
Return Value:
Address
--*/ { PSOCKADDR_IN6 pAddress;
DBG_ASSERT( _pUlHttpRequest->Address.pRemoteAddress->sa_family == AF_INET6 ); pAddress = (PSOCKADDR_IN6)_pUlHttpRequest->Address.pRemoteAddress; return &pAddress->sin6_addr; }
USHORT W3_REQUEST::QueryLocalPort( VOID ) const { USHORT port = 0; PSOCKADDR_IN6 pV6Address; PSOCKADDR_IN pV4Address; if( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET ) { pV4Address = (PSOCKADDR_IN)_pUlHttpRequest->Address.pLocalAddress; port = pV4Address->sin_port; } else if( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET6 ) { pV6Address = (PSOCKADDR_IN6) _pUlHttpRequest->Address.pLocalAddress; port = pV6Address->sin6_port; } else { DBG_ASSERT( FALSE ); }
return port; }
USHORT W3_REQUEST::QueryRemotePort( VOID ) const { USHORT port = 0; PSOCKADDR_IN6 pV6Address; PSOCKADDR_IN pV4Address; if( _pUlHttpRequest->Address.pRemoteAddress->sa_family == AF_INET ) { pV4Address = (PSOCKADDR_IN)_pUlHttpRequest->Address.pRemoteAddress; port = pV4Address->sin_port; } else if( _pUlHttpRequest->Address.pRemoteAddress->sa_family == AF_INET6 ) { pV6Address = (PSOCKADDR_IN6) _pUlHttpRequest->Address.pRemoteAddress; port = pV6Address->sin6_port; } else { DBG_ASSERT( FALSE ); }
return port; }
PSOCKADDR W3_REQUEST::QueryLocalSockAddress( VOID ) const { return _pUlHttpRequest->Address.pLocalAddress; }
PSOCKADDR W3_REQUEST::QueryRemoteSockAddress( VOID ) const { return _pUlHttpRequest->Address.pRemoteAddress; }
BOOL W3_REQUEST::IsProxyRequest( VOID ) /*++
Description:
Check if request was issued by a proxy, as determined by following rules : - "Via:" header is present (HTTP/1.1) - "Forwarded:" header is present (some HTTP/1.0 implementations) - "User-Agent:" contains "via ..." (CERN proxy)
Arguments: None
Returns: TRUE if client request was issued by proxy
--*/ { LPCSTR pUserAgent; UINT cUserAgent; LPCSTR pEnd;
if ( GetHeader( HttpHeaderVia ) || GetHeader( "Forward" ) ) { return TRUE; }
if ( (pUserAgent = GetHeader( HttpHeaderUserAgent )) != NULL ) { cUserAgent = (DWORD)strlen( pUserAgent ); pEnd = pUserAgent + cUserAgent - 3;
//
// scan for "[Vv]ia[ :]" in User-Agent: header
//
while ( pUserAgent < pEnd ) { if ( *pUserAgent == 'V' || *pUserAgent == 'v' ) { if ( pUserAgent[1] == 'i' && pUserAgent[2] == 'a' && (pUserAgent[3] == ' ' || pUserAgent[3] == ':') ) { return TRUE; } } ++pUserAgent; } }
return FALSE; }
BOOL W3_REQUEST::IsChunkedRequest( VOID ) /*++
Description:
Check if request is chunk transfer encoded.
Arguments: None
Returns: TRUE if client request has a "transfer-encoding: chunked" header.
--*/ { BOOL fRet = FALSE;
LPCSTR pTransferEncoding = GetHeader( HttpHeaderTransferEncoding ); if ( pTransferEncoding != NULL ) { fRet = ( _stricmp( pTransferEncoding, "chunked" ) == 0 ); }
return fRet; }
HRESULT W3_REQUEST::GetAuthType( STRA * pstrAuthType ) /*++
Routine Description:
Determine the auth type for this request. Auth type is the string after the authorization header (and before the space if there). Eg. Authorization: Basic FOOBAR ==> AuthType is Basic Authorization: NTLM ==> AuthType is NTLM
And of course if client cert mapping happened, type is "SSL/PCT"
Arguments:
pstrAuthType - Filled with auth type
Return Value:
HRESULT
--*/ { LPCSTR pszAuthType; LPCSTR pszSpace; HRESULT hr; if ( pstrAuthType == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// First check for client cert mapping
//
if ( QueryClientCertInfo() != NULL && QueryClientCertInfo()->Token != NULL ) { return pstrAuthType->Copy( "SSL/PCT" ); } //
// Now check for the Authorization: header
//
pszAuthType = GetHeader( HttpHeaderAuthorization ); if ( pszAuthType == NULL ) { pstrAuthType->Copy( "" ); hr = NO_ERROR; } else { pszSpace = strchr( pszAuthType, ' ' ); if ( pszSpace == NULL ) { hr = pstrAuthType->Copy( pszAuthType ); } else { hr = pstrAuthType->Copy( pszAuthType, (DWORD)DIFF( pszSpace - pszAuthType ) ); } } return hr; }
BOOL W3_REQUEST::IsLocalRequest( VOID ) const /*++
Routine Description:
Determines whether current request is a local request. This is used since certain vroot permissions can be specified for remote/local
Arguments:
None
Return Value:
TRUE if this is a local request
--*/ { ADDRINFO * p = sm_pHostAddrInfo;
if( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET ) { SOCKADDR_IN * pSockAddr;
DWORD ipv4RemoteAddress = ntohl( QueryIPv4RemoteAddress() );
if( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET ) { DWORD ipv4LocalAddress = ntohl( QueryIPv4LocalAddress() ); //
// Are the remote/local addresses the same?
//
if ( ipv4LocalAddress == ipv4RemoteAddress ) { return TRUE; } //
// Are either equal to 127.0.0.1
//
if ( ipv4LocalAddress == LOCAL127 || ipv4RemoteAddress == LOCAL127 ) { return TRUE; } }
//
// Is the remote address equal to one of the host addresses
//
while( p != NULL ) { if( p->ai_family == AF_INET ) { pSockAddr = ( SOCKADDR_IN * )p->ai_addr; if( pSockAddr->sin_addr.s_addr == ipv4RemoteAddress ) { return TRUE; } }
p = p->ai_next; } return FALSE; }
if( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET6 ) { SOCKADDR_IN6 * pSockAddr6; IN6_ADDR * pIPv6RemoteAddress = QueryIPv6RemoteAddress();
if( _pUlHttpRequest->Address.pLocalAddress->sa_family == AF_INET6 ) { IN6_ADDR * pIPv6LocalAddress = QueryIPv6LocalAddress();
//
// Are the remote/local addresses the same?
//
if( !memcmp( pIPv6LocalAddress, pIPv6RemoteAddress, sizeof( IN6_ADDR ) ) ) { return TRUE; }
//
// Are either equal to ::1
//
if( IN6_IS_ADDR_LOOPBACK( pIPv6LocalAddress ) || IN6_IS_ADDR_LOOPBACK( pIPv6RemoteAddress ) ) { return TRUE; } }
//
// Is the remote address equal to one of the host addresses
//
while( p != NULL ) { if( p->ai_family == AF_INET6 ) { pSockAddr6 = ( SOCKADDR_IN6 * )p->ai_addr; if( !memcmp( &pSockAddr6->sin6_addr, pIPv6RemoteAddress, sizeof( IN6_ADDR ) ) ) { return TRUE; } }
p = p->ai_next; } return FALSE; } DBG_ASSERT( FALSE ); return FALSE; }
HRESULT W3_REQUEST::BuildFullUrl( STRU& strPath, STRU * pstrRedirect, BOOL fIncludeParameters ) /*++
Routine Description:
Create a new URL while maintaining host/port/protocol/query-string of original request
Arguments:
strPath - New path portion of URL pstrRedirect - String filled with new full URL fIncludeParameters - TRUE if original query string should be copied too
Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; LPWSTR pszColon = NULL; LPWSTR pszRightBracket = NULL;
if ( pstrRedirect == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); }
hr = pstrRedirect->Copy( IsSecureRequest() ? L"https://" : L"http://" ); if ( FAILED( hr ) ) { return hr; }
LPCSTR pszHost = GetHeader( HttpHeaderHost ); if ( pszHost != NULL ) { hr = pstrRedirect->AppendA( pszHost ); } else { if( _pUlHttpRequest->CookedUrl.pHost[0] == L'[' ) { pszRightBracket = wcschr( _pUlHttpRequest->CookedUrl.pHost, L']' ); if( pszRightBracket != NULL ) { pszColon = pszRightBracket + 1; if( *pszColon != L':' ) { pszColon = NULL; } } else { hr = E_FAIL; } } else { pszColon = wcschr( _pUlHttpRequest->CookedUrl.pHost, L':' ); }
if ((pszColon != NULL) && ((IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT_SECURE) || (!IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT))) { hr = pstrRedirect->Append( _pUlHttpRequest->CookedUrl.pHost, (DWORD)DIFF(pszColon - _pUlHttpRequest->CookedUrl.pHost) ); } else { hr = pstrRedirect->Append( _pUlHttpRequest->CookedUrl.pHost, _pUlHttpRequest->CookedUrl.HostLength / sizeof( WCHAR ) ); } }
if ( FAILED( hr ) ) { return hr; }
hr = pstrRedirect->Append( strPath ); if ( FAILED( hr ) ) { return hr; }
if ( fIncludeParameters && _pUlHttpRequest->CookedUrl.pQueryString != NULL ) { //
// UL_HTTP_REQUEST::QueryString already contains the '?'
//
hr = pstrRedirect->Append( _pUlHttpRequest->CookedUrl.pQueryString, _pUlHttpRequest->CookedUrl.QueryStringLength / sizeof(WCHAR)); if ( FAILED( hr ) ) { return hr; } }
return S_OK; }
HRESULT W3_REQUEST::BuildFullUrl( STRA& strPath, STRA * pstrRedirect, BOOL fIncludeParameters ) /*++
Routine Description:
Create a new URL while maintaining host/port/protocol/query-string of original request
Arguments:
strPath - New path portion of URL pstrRedirect - String filled with new full URL fIncludeParameters - TRUE if original query string should be copied too
Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; LPWSTR pszColon = NULL; LPWSTR pszRightBracket = NULL;
if ( pstrRedirect == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); }
hr = pstrRedirect->Copy( IsSecureRequest() ? "https://" : "http://" ); if ( FAILED( hr ) ) { return hr; }
USHORT cchHost; LPCSTR pszHost = GetHeader( HttpHeaderHost, &cchHost ); if ( pszHost != NULL ) { hr = pstrRedirect->Append( pszHost, cchHost ); } else { if( _pUlHttpRequest->CookedUrl.pHost[0] == L'[' ) { pszRightBracket = wcschr( _pUlHttpRequest->CookedUrl.pHost, L']' ); if( pszRightBracket != NULL ) { pszColon = pszRightBracket + 1; if( *pszColon != L':' ) { pszColon = NULL; } } else { hr = E_FAIL; } } else { pszColon = wcschr( _pUlHttpRequest->CookedUrl.pHost, L':' ); }
if ((pszColon != NULL) && ((IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT_SECURE) || (!IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT))) { hr = pstrRedirect->AppendW( _pUlHttpRequest->CookedUrl.pHost, (DWORD)DIFF(pszColon - _pUlHttpRequest->CookedUrl.pHost) ); } else { hr = pstrRedirect->AppendW( _pUlHttpRequest->CookedUrl.pHost, _pUlHttpRequest->CookedUrl.HostLength / sizeof( WCHAR ) ); } }
if ( FAILED( hr ) ) { return hr; }
hr = pstrRedirect->Append( strPath ); if ( FAILED( hr ) ) { return hr; }
if ( fIncludeParameters && _pUlHttpRequest->CookedUrl.pQueryString != NULL ) { //
// UL_HTTP_REQUEST::QueryString already contains the '?'
//
hr = pstrRedirect->AppendW( _pUlHttpRequest->CookedUrl.pQueryString, _pUlHttpRequest->CookedUrl.QueryStringLength / sizeof(WCHAR)); if ( FAILED( hr ) ) { return hr; } }
return S_OK; }
CHAR * W3_REQUEST::GetHeader( CHAR * pszHeaderName ) /*++
Routine Description:
Get a request header and copy it into supplied buffer
Arguments:
pszHeaderName - Name of header to get
Return Value:
WCHAR pointer pointed to the header, NULL if no such header
--*/ { ULONG ulHeaderIndex = UNKNOWN_INDEX; HTTP_REQUEST_HEADERS * pHeaders = &(_pUlHttpRequest->Headers);
DBG_ASSERT( pszHeaderName != NULL );
//
// First check whether this header is a known header
//
ulHeaderIndex = REQUEST_HEADER_HASH::GetIndex( pszHeaderName ); if ( ulHeaderIndex == UNKNOWN_INDEX ) { //
// Need to iterate thru unknown headers
//
for ( DWORD i = 0; i < pHeaders->UnknownHeaderCount; i++ ) { if ( _stricmp( pszHeaderName, pHeaders->pUnknownHeaders[ i ].pName ) == 0 ) { return (LPSTR) pHeaders->pUnknownHeaders[ i ].pRawValue; } }
return NULL; } else { //
// Known header
//
DBG_ASSERT( ulHeaderIndex < HttpHeaderRequestMaximum ); return (LPSTR) pHeaders->KnownHeaders[ ulHeaderIndex ].pRawValue; } }
HRESULT W3_REQUEST::GetHeader( CHAR * pszHeaderName, DWORD cchHeaderName, STRA * pstrHeaderValue, BOOL fIsUnknownHeader ) /*++
Routine Description:
Get a request header and copy it into supplied buffer
Arguments:
pszHeaderName - Header name cchHeaderName - Length of header name pstrHeaderValue - Filled with header value fIsUnknownHeader - (optional) set to TRUE if header is unknown and we can avoid doing header hash lookup
Return Value:
HRESULT
--*/ { ULONG ulHeaderIndex = UNKNOWN_INDEX; HTTP_REQUEST_HEADERS * pHeaders = &(_pUlHttpRequest->Headers); HRESULT hr = HRESULT_FROM_WIN32( ERROR_INVALID_INDEX ); if ( pszHeaderName == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// First check whether this header is a known header
//
if ( !fIsUnknownHeader ) { ulHeaderIndex = REQUEST_HEADER_HASH::GetIndex( pszHeaderName ); } if ( ulHeaderIndex == UNKNOWN_INDEX ) { //
// Need to iterate thru unknown headers
//
for ( DWORD i = 0; i < pHeaders->UnknownHeaderCount; i++ ) { if ( cchHeaderName == pHeaders->pUnknownHeaders[ i ].NameLength && _stricmp( pszHeaderName, pHeaders->pUnknownHeaders[ i ].pName ) == 0 ) { hr = pstrHeaderValue->Copy( pHeaders->pUnknownHeaders[ i ].pRawValue, pHeaders->pUnknownHeaders[ i ].RawValueLength ); break; } } } else { //
// Known header
//
DBG_ASSERT( ulHeaderIndex < HttpHeaderRequestMaximum ); if ( pHeaders->KnownHeaders[ ulHeaderIndex ].pRawValue != NULL ) { hr = pstrHeaderValue->Copy( pHeaders->KnownHeaders[ ulHeaderIndex ].pRawValue, pHeaders->KnownHeaders[ ulHeaderIndex ].RawValueLength); } else { hr = HRESULT_FROM_WIN32( ERROR_INVALID_INDEX ); } } return hr; }
HRESULT W3_REQUEST::DeleteHeader( CHAR * pszHeaderName ) /*++
Routine Description: Delete a request header
Arguments:
pszHeaderName - Header to delete Return Value:
HRESULT
--*/ { ULONG ulHeaderIndex; HTTP_UNKNOWN_HEADER * pUnknownHeader; //
// Is this a known header? If so, we can just set by reference now
// since we have copied the header value
//
ulHeaderIndex = REQUEST_HEADER_HASH::GetIndex( pszHeaderName ); if ( ulHeaderIndex != UNKNOWN_INDEX && ulHeaderIndex < HttpHeaderMaximum ) { _pUlHttpRequest->Headers.KnownHeaders[ ulHeaderIndex ].pRawValue = NULL; _pUlHttpRequest->Headers.KnownHeaders[ ulHeaderIndex ].RawValueLength = 0; } else { //
// Unknown header. First check if it exists
//
for ( DWORD i = 0; i < _pUlHttpRequest->Headers.UnknownHeaderCount; i++ ) { pUnknownHeader = &(_pUlHttpRequest->Headers.pUnknownHeaders[ i ]); DBG_ASSERT( pUnknownHeader != NULL ); if ( _stricmp( pUnknownHeader->pName, pszHeaderName ) == 0 ) { break; } } if ( i < _pUlHttpRequest->Headers.UnknownHeaderCount ) { //
// Now shrink the array to remove the header
//
memmove( _pUlHttpRequest->Headers.pUnknownHeaders + i, _pUlHttpRequest->Headers.pUnknownHeaders + i + 1, ( _pUlHttpRequest->Headers.UnknownHeaderCount - i - 1 ) * sizeof( HTTP_UNKNOWN_HEADER ) ); _pUlHttpRequest->Headers.UnknownHeaderCount--; } } return NO_ERROR; }
HRESULT W3_REQUEST::DeleteKnownHeader( ULONG ulHeaderIndex ) /*++
Routine Description: Delete a request header
Arguments:
pszHeaderName - Header to delete Return Value:
HRESULT
--*/ { if ( ulHeaderIndex != UNKNOWN_INDEX && ulHeaderIndex < HttpHeaderMaximum ) { _pUlHttpRequest->Headers.KnownHeaders[ ulHeaderIndex ].pRawValue = NULL; _pUlHttpRequest->Headers.KnownHeaders[ ulHeaderIndex ].RawValueLength = 0; }
return NO_ERROR; } HRESULT W3_REQUEST::SetHeader( STRA & strHeaderName, STRA & strHeaderValue, BOOL fAppend ) /*++
Routine Description:
Set a request header
Arguments:
strHeaderName - Name of header to set strHeaderValue - New header value to set fAppend - If TRUE, the existing header value is appended to, else it is replaced
Return Value:
HRESULT
--*/ { CHAR * pszNewName = NULL; CHAR * pszNewValue = NULL; STACK_STRA( strOldHeaderValue, 256 ); STRA * pstrNewHeaderValue = NULL; HRESULT hr; ULONG index;
//
// If we're appending, then get the old header value (if any) and
// append the new value (with a comma delimiter)
//
if ( fAppend ) { hr = GetHeader( strHeaderName, &strOldHeaderValue ); if ( FAILED( hr ) ) { pstrNewHeaderValue = &strHeaderValue; hr = NO_ERROR; } else { hr = strOldHeaderValue.Append( ",", 1 ); if ( FAILED( hr ) ) { return hr; } hr = strOldHeaderValue.Append( strHeaderValue ); if ( FAILED( hr ) ) { return hr; }
pstrNewHeaderValue = &strOldHeaderValue; } } else { pstrNewHeaderValue = &strHeaderValue; } DBG_ASSERT( pstrNewHeaderValue != NULL );
//
// pstrNewHeaderValue will point to either "old,new" or "new"
//
hr = _HeaderBuffer.AllocateSpace( pstrNewHeaderValue->QueryStr(), pstrNewHeaderValue->QueryCCH(), &pszNewValue ); if ( FAILED( hr ) ) { return hr; } //
// Is this a known header?
//
index = REQUEST_HEADER_HASH::GetIndex( strHeaderName.QueryStr() ); if ( index == UNKNOWN_INDEX ) { hr = _HeaderBuffer.AllocateSpace( strHeaderName.QueryStr(), strHeaderName.QueryCCH(), &pszNewName ); if ( FAILED( hr ) ) { return hr; } //
// Find the header in the unknown list
//
for ( DWORD i = 0; i < _pUlHttpRequest->Headers.UnknownHeaderCount; i++ ) { if ( _strnicmp( strHeaderName.QueryStr(), _pUlHttpRequest->Headers.pUnknownHeaders[ i ].pName, strHeaderName.QueryCCH() ) == 0 ) { break; } } //
// If we found the unknown header, then this is much simpler
//
if ( i < _pUlHttpRequest->Headers.UnknownHeaderCount ) { _pUlHttpRequest->Headers.pUnknownHeaders[i].pRawValue = pszNewValue; _pUlHttpRequest->Headers.pUnknownHeaders[i].RawValueLength = (USHORT) pstrNewHeaderValue->QueryCB(); } else { HTTP_UNKNOWN_HEADER *pUnknownHeaders; DWORD cCount;
//
// Fun. Need to add a new unknown header
//
cCount = _pUlHttpRequest->Headers.UnknownHeaderCount;
//
// BUGBUG: are we leaking this memory?
//
pUnknownHeaders = (HTTP_UNKNOWN_HEADER *) LocalAlloc( LPTR, sizeof( HTTP_UNKNOWN_HEADER ) * ( cCount+1 ) ); if ( pUnknownHeaders == NULL ) { return HRESULT_FROM_WIN32( GetLastError() ); } memcpy( pUnknownHeaders, _pUlHttpRequest->Headers.pUnknownHeaders, sizeof( HTTP_UNKNOWN_HEADER ) * (cCount) ); pUnknownHeaders[ cCount ].pName = pszNewName; pUnknownHeaders[ cCount ].NameLength = (USHORT)strHeaderName.QueryCB();
pUnknownHeaders[ cCount ].pRawValue = pszNewValue; pUnknownHeaders[ cCount ].RawValueLength = (USHORT) pstrNewHeaderValue->QueryCB();
//
// Patch in the new array
//
if ( _pExtraUnknown != NULL ) { LocalFree( _pExtraUnknown ); } _pExtraUnknown = pUnknownHeaders; _pUlHttpRequest->Headers.pUnknownHeaders = pUnknownHeaders; _pUlHttpRequest->Headers.UnknownHeaderCount++; } } else { //
// The easy case. Known header
//
_pUlHttpRequest->Headers.KnownHeaders[ index ].pRawValue = pszNewValue; _pUlHttpRequest->Headers.KnownHeaders[ index ].RawValueLength = (USHORT) pstrNewHeaderValue->QueryCB(); } return S_OK; }
HRESULT W3_REQUEST::SetKnownHeader( ULONG ulHeaderIndex, STRA & strHeaderValue, BOOL fAppend ) /*++
Routine Description:
Set a request header
Arguments:
ulHeaderIndex - Name index of the header to set strHeaderValue - New header value to set fAppend - If TRUE, the existing header value is appended to, else it is replaced
Return Value:
HRESULT
--*/ { CHAR * pszNewValue = NULL; STACK_STRA( strOldHeaderValue, 256 ); STRA * pstrNewHeaderValue = NULL; HRESULT hr;
//
// If we're appending, then get the old header value (if any) and
// append the new value (with a comma delimiter)
//
if ( fAppend ) { hr = strOldHeaderValue.Copy( GetHeader( ulHeaderIndex ) ); if ( FAILED( hr ) ) { pstrNewHeaderValue = &strHeaderValue; hr = NO_ERROR; } else { hr = strOldHeaderValue.Append( ",", 1 ); if ( FAILED( hr ) ) { return hr; } hr = strOldHeaderValue.Append( strHeaderValue ); if ( FAILED( hr ) ) { return hr; }
pstrNewHeaderValue = &strOldHeaderValue; } } else { pstrNewHeaderValue = &strHeaderValue; } DBG_ASSERT( pstrNewHeaderValue != NULL );
//
// pstrNewHeaderValue will point to either "old,new" or "new"
//
hr = _HeaderBuffer.AllocateSpace( pstrNewHeaderValue->QueryStr(), pstrNewHeaderValue->QueryCCH(), &pszNewValue ); if ( FAILED( hr ) ) { return hr; }
//
// Set the new value
//
if ( ulHeaderIndex != UNKNOWN_INDEX && ulHeaderIndex < HttpHeaderRequestMaximum ) { _pUlHttpRequest->Headers.KnownHeaders[ ulHeaderIndex ].pRawValue = pszNewValue; _pUlHttpRequest->Headers.KnownHeaders[ ulHeaderIndex ].RawValueLength = (USHORT) pstrNewHeaderValue->QueryCB(); }
return S_OK; }
HRESULT W3_REQUEST::GetVersionString( STRA * pstrVersion ) /*++
Routine Description:
Get version string of request (like "HTTP/1.1")
Arguments:
pstrVersion - Filled in with version
Return Value:
HRESULT
--*/ { HRESULT hr; CHAR pszMajorVersion[6]; CHAR pszMinorVersion[6];
_itoa(_pUlHttpRequest->Version.MajorVersion, pszMajorVersion, 10); _itoa(_pUlHttpRequest->Version.MinorVersion, pszMinorVersion, 10);
if (FAILED(hr = pstrVersion->Copy("HTTP/", 5)) || FAILED(hr = pstrVersion->Append(pszMajorVersion)) || FAILED(hr = pstrVersion->Append(".", 1)) || FAILED(hr = pstrVersion->Append(pszMinorVersion))) { return hr; }
return S_OK; }
HRESULT W3_REQUEST::GetVerbString( STRA * pstrVerb ) /*++
Routine Description:
Get the HTTP verb from the request
Arguments:
pstrVerb - Filled in with verb
Return Value:
HRESULT
--*/ { USHORT cchVerb; CHAR *pszVerb = METHOD_HASH::GetString(_pUlHttpRequest->Verb, &cchVerb); if (pszVerb != NULL) { return pstrVerb->Copy(pszVerb, cchVerb); } else { return pstrVerb->Copy(_pUlHttpRequest->pUnknownVerb, _pUlHttpRequest->UnknownVerbLength); } }
VOID W3_REQUEST::QueryVerb( CHAR **ppszVerb, USHORT *pcchVerb) /*++
Get the HTTP verb from the request --*/ { *ppszVerb = METHOD_HASH::GetString(_pUlHttpRequest->Verb, pcchVerb); if (*ppszVerb == NULL) { *ppszVerb = (CHAR*) _pUlHttpRequest->pUnknownVerb; *pcchVerb = _pUlHttpRequest->UnknownVerbLength; } }
HRESULT W3_REQUEST::SetVerb( STRA & strVerb ) /*++
Routine Description:
Change the request verb (done in PREPROC_HEADER ISAPI filters)
Arguments:
strVerb - New verb
Return Value:
HRESULT
--*/ { HRESULT hr; CHAR *pszNewVerb;
HTTP_VERB Verb = (HTTP_VERB)METHOD_HASH::GetIndex(strVerb.QueryStr()); _pUlHttpRequest->Verb = Verb; if ( Verb == HttpVerbUnknown ) { //
// Handle unknown verbs
//
hr = _HeaderBuffer.AllocateSpace( strVerb.QueryStr(), strVerb.QueryCCH(), &pszNewVerb ); if ( FAILED( hr ) ) { return hr; } _pUlHttpRequest->pUnknownVerb = pszNewVerb; _pUlHttpRequest->UnknownVerbLength = (USHORT) strVerb.QueryCCH(); } else { _pUlHttpRequest->pUnknownVerb = NULL; _pUlHttpRequest->UnknownVerbLength = 0; } return S_OK; }
HRESULT W3_REQUEST::SetVersion( STRA& strVersion ) /*++
Routine Description:
strVersion - Set the request version (done by PREPROC_HEADER filters)
Arguments:
strVersion - Version string
Return Value:
HRESULT
--*/ { HRESULT hr = S_OK; //
// BUGBUG: Probably not the fastest way to do this
//
if ( strcmp( strVersion.QueryStr(), "HTTP/1.1" ) == 0 ) { _pUlHttpRequest->Version.MajorVersion = 1; _pUlHttpRequest->Version.MinorVersion = 1; } else if ( strcmp( strVersion.QueryStr(), "HTTP/1.0" ) == 0 ) { _pUlHttpRequest->Version.MajorVersion = 1; _pUlHttpRequest->Version.MinorVersion = 0; } else if ( strcmp( strVersion.QueryStr(), "HTTP/0.9" ) == 0 ) { _pUlHttpRequest->Version.MajorVersion = 0; _pUlHttpRequest->Version.MinorVersion = 9; } else { hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } return hr; }
HRESULT W3_REQUEST::SetNewPreloadedEntityBody( VOID * pvBuffer, DWORD cbBuffer ) /*++
Routine Description:
Change the preloaded entity body for this request
Arguments:
pvBuffer - the buffer cbBuffer - the size of the buffer
Return Value:
HRESULT
--*/ { _InsertedEntityBodyChunk.DataChunkType = HttpDataChunkFromMemory; _InsertedEntityBodyChunk.FromMemory.pBuffer = pvBuffer; _InsertedEntityBodyChunk.FromMemory.BufferLength = cbBuffer; _pUlHttpRequest->EntityChunkCount = 1; _pUlHttpRequest->pEntityChunks = &_InsertedEntityBodyChunk; return NO_ERROR; }
HRESULT W3_REQUEST::AppendEntityBody( VOID * pvBuffer, DWORD cbBuffer ) /*
Description Add the given entity to (any) entity already present
Arguments pvBuffer - the buffer cbBuffer - the size of the buffer
Return Value HRESULT */ { DWORD cbAlreadyPresent = QueryAvailableBytes();
if ( cbAlreadyPresent == 0 ) { return SetNewPreloadedEntityBody(pvBuffer, cbBuffer); } else { PVOID pbAlreadyPresent = QueryEntityBody();
if (!_buffEntityBodyPreload.Resize(cbBuffer + cbAlreadyPresent)) { return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); }
memcpy(_buffEntityBodyPreload.QueryPtr(), pbAlreadyPresent, cbAlreadyPresent);
memcpy((PBYTE)_buffEntityBodyPreload.QueryPtr() + cbAlreadyPresent, pvBuffer, cbBuffer); }
return S_OK; }
HRESULT W3_REQUEST::SetUrl( STRU & strNewUrl, BOOL fResetQueryString // = TRUE
) /*++
Routine Description:
Change the URL of the request
Arguments:
strNewUrl - New URL fResetQueryString - TRUE if we should expect query string in strNewUrl
Return Value:
HRESULT
--*/ { STACK_STRA ( straUrl, MAX_PATH ); HRESULT hr = S_OK;
DWORD lenToConvert = 0;
WCHAR * pFirstQuery = wcschr(strNewUrl.QueryStr(), L'?'); if (NULL == pFirstQuery) { lenToConvert = strNewUrl.QueryCCH(); } else { lenToConvert = (DWORD)DIFF(pFirstQuery - strNewUrl.QueryStr()); }
if (FAILED(hr = straUrl.CopyWToUTF8(strNewUrl.QueryStr(), lenToConvert))) { return hr; }
straUrl.AppendW(strNewUrl.QueryStr() + lenToConvert);
//
// SetUrlA does the canonicalization, unescaping
//
return SetUrlA( straUrl, fResetQueryString ); }
HRESULT W3_REQUEST::SetUrlA( STRA & strNewUrl, BOOL fResetQueryString // = TRUE
) /*++
Routine Description:
Change the URL of the request. Takes in an ANSI URL
Arguments:
strNewUrl - RAW version of URL which is also stored away fResetQueryString - Should we expect query string in strNewUrl
Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; ULONG cbBytesCopied; LPWSTR pszQueryString; CHAR * pszNewRawUrl; LPWSTR pszNewFullUrl; LPWSTR pszNewAbsPath; STACK_STRU ( strFullUrl, MAX_PATH); STACK_STRU ( strAbsPath, MAX_PATH);
if (strNewUrl.QueryCCH() > MAXUSHORT) { return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); }
//
// Need to process the URL ourselves. This means:
// -Unescaping, canonicalizing, and query-string-ilizing
//
//
// BUGBUG. Probably need to handle HTTP:// and HTTPS:// preceded URLs
// so that MS proxy can still work
//
hr = _HeaderBuffer.AllocateSpace( strNewUrl.QueryStr(), strNewUrl.QueryCCH(), &pszNewRawUrl ); if ( FAILED( hr ) ) { goto Finished; }
//
// Need to patch the UL_HTTP_REQUEST raw URL
//
_pUlHttpRequest->pRawUrl = pszNewRawUrl; _pUlHttpRequest->RawUrlLength = (USHORT) strNewUrl.QueryCCH();
hr = _HeaderBuffer.AllocateSpace( (strNewUrl.QueryCCH() + 1)*sizeof(WCHAR), &pszNewAbsPath ); if ( FAILED( hr ) ) { goto Finished; }
//
// Call into a UL ripped helper to do the parsing
//
hr = UlCleanAndCopyUrl( (PUCHAR)pszNewRawUrl, strNewUrl.QueryCCH(), &cbBytesCopied, pszNewAbsPath, &pszQueryString ); if ( FAILED( hr ) ) { goto Finished; }
//
// Need to patch the UL_HTTP_REQUEST AbsPath and QueryString
//
_pUlHttpRequest->CookedUrl.pAbsPath = pszNewAbsPath;
if ( pszQueryString != NULL ) { _pUlHttpRequest->CookedUrl.pQueryString = pszQueryString; _pUlHttpRequest->CookedUrl.AbsPathLength = (USHORT)DIFF(pszQueryString - pszNewAbsPath) * sizeof(WCHAR); _pUlHttpRequest->CookedUrl.QueryStringLength = (USHORT)(cbBytesCopied - _pUlHttpRequest->CookedUrl.AbsPathLength); } else { if ( fResetQueryString ) { _pUlHttpRequest->CookedUrl.pQueryString = NULL; _pUlHttpRequest->CookedUrl.QueryStringLength = 0; }
_pUlHttpRequest->CookedUrl.AbsPathLength = (USHORT) cbBytesCopied; }
hr = strAbsPath.Copy(pszNewAbsPath); if (FAILED(hr)) { goto Finished; }
//
// Need to patch up the Full Url.
//
BuildFullUrl( strAbsPath, &strFullUrl, FALSE); hr = _HeaderBuffer.AllocateSpace( strFullUrl.QueryStr(), strFullUrl.QueryCCH(), &pszNewFullUrl ); if (FAILED(hr)) { goto Finished; }
_pUlHttpRequest->CookedUrl.pFullUrl = pszNewFullUrl; _pUlHttpRequest->CookedUrl.FullUrlLength = (USHORT) strFullUrl.QueryCB();
Finished: return hr; }
HRESULT W3_REQUEST::BuildISAPIHeaderLine( LPCSTR pszHeaderName, DWORD cchHeaderName, LPCSTR pszHeaderValue, DWORD cchHeaderValue, STRA * pstrHeaderLine ) /*++
Routine Description:
Private utility to build a header line as such: pszHeaderName = "this-is-a-header", pszHeaderValue = "foobar" PRODUCES "HTTP_THIS_IS_A_HEADER:foobar\n"
Arguments:
pszHeaderName - Header name cchHeaderName - Length of header name pszHeaderValue - Header value cchHeaderValue - Length of header value pstrHeaderLine - Header line is appended
Return Value:
HRESULT
--*/ { HRESULT hr; CHAR * pszCursor; DWORD currHeaderLength = pstrHeaderLine->QueryCCH(); //
// Convert header name "a-b-c" into "HTTP_A_B_C"
//
hr = pstrHeaderLine->Append( "HTTP_", 5 ); if ( FAILED( hr ) ) { return hr; }
hr = pstrHeaderLine->Append( pszHeaderName, cchHeaderName ); if ( FAILED( hr ) ) { return hr; } //
// Convert - to _
//
pszCursor = strchr( pstrHeaderLine->QueryStr() + currHeaderLength + 5, '-' ); while ( pszCursor != NULL ) { *pszCursor++ = '_'; pszCursor = strchr( pszCursor, L'-' ); } //
// Uppercase it
//
_strupr( pstrHeaderLine->QueryStr() + currHeaderLength + 5 ); //
// Now finish the header line by adding ":<header value>\n"
//
// Note that raw HTTP looks like ": <header value>", but earlier
// versions of IIS did not include the space, and there are
// legacy ISAPI's that depend on the space after the colon
// not being there.
//
hr = pstrHeaderLine->Append( ":", 1 ); if ( FAILED( hr ) ) { return hr; }
hr = pstrHeaderLine->Append( pszHeaderValue, cchHeaderValue ); if ( FAILED( hr ) ) { return hr; }
hr = pstrHeaderLine->Append( "\n", 1 ); if ( FAILED( hr ) ) { return hr; } return NO_ERROR; }
HRESULT W3_REQUEST::BuildRawHeaderLine( LPCSTR pszHeaderName, DWORD cchHeaderName, LPCSTR pszHeaderValue, DWORD cchHeaderValue, STRA * pstrHeaderLine ) /*++
Routine Description:
Private utility to build a header line as such: pszHeaderName = "this-is-a-header", pszHeaderValue = "foobar" PRODUCES
this-is-a-header: foobar\r\n
Arguments:
pszHeaderName - Header name cchHeaderName - Length of header name pszHeaderValue - Header value cchHeaderValue - Length of header value pstrHeaderLine - Header line is appended
Return Value:
HRESULT
--*/ { HRESULT hr;
hr = pstrHeaderLine->Append( pszHeaderName, cchHeaderName ); if ( FAILED( hr ) ) { return hr; }
//
// Now finish the header line by adding ": <header value>\n"
//
hr = pstrHeaderLine->Append( ": ", 2 ); if ( FAILED( hr ) ) { return hr; }
hr = pstrHeaderLine->Append( pszHeaderValue, cchHeaderValue ); if ( FAILED( hr ) ) { return hr; }
hr = pstrHeaderLine->Append( "\r\n" ); if ( FAILED( hr ) ) { return hr; }
return NO_ERROR; } HRESULT W3_REQUEST::GetAllHeaders( STRA * pstrHeaders, BOOL fISAPIStyle ) /*++
Routine Description:
Get all headers in one string, delimited by \r\n
Arguments:
pstrHeaders - Filled with headers fISAPIStyle - If TRUE, format is: HTTP_CONTENT_LENGTH: 245\nHTTP_CONTENT_TYPE: t\n If FALSE, format is: CONTENT-LENGTH: 245\r\nCONTENT-TYPE: t\r\n
Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; DWORD cCounter; HTTP_KNOWN_HEADER * pKnownHeader; LPCSTR pszName; DWORD cchName; HTTP_UNKNOWN_HEADER * pUnknownHeader; if ( pstrHeaders == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// Copy known headers
//
for ( cCounter = 0; cCounter < HttpHeaderRequestMaximum; cCounter++ ) { pKnownHeader = &(_pUlHttpRequest->Headers.KnownHeaders[ cCounter ]); if ( pKnownHeader->RawValueLength != 0 ) { pszName = REQUEST_HEADER_HASH::GetString( cCounter, &cchName ); if ( pszName == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); }
if ( fISAPIStyle ) { hr = BuildISAPIHeaderLine( pszName, cchName, pKnownHeader->pRawValue, pKnownHeader->RawValueLength, pstrHeaders ); } else { hr = BuildRawHeaderLine( pszName, cchName, pKnownHeader->pRawValue, pKnownHeader->RawValueLength, pstrHeaders ); } if ( FAILED( hr ) ) { return hr; } } } //
// Copy unknown headers
//
for ( cCounter = 0; cCounter < _pUlHttpRequest->Headers.UnknownHeaderCount; cCounter++ ) { pUnknownHeader = &(_pUlHttpRequest->Headers.pUnknownHeaders[ cCounter ]); pszName = pUnknownHeader->pName; cchName = pUnknownHeader->NameLength;
if ( fISAPIStyle ) { hr = BuildISAPIHeaderLine( pszName, cchName, pUnknownHeader->pRawValue, pUnknownHeader->RawValueLength, pstrHeaders ); } else { hr = BuildRawHeaderLine( pszName, cchName, pUnknownHeader->pRawValue, pUnknownHeader->RawValueLength, pstrHeaders ); } if ( FAILED( hr ) ) { return hr; } } return NO_ERROR; }
HRESULT W3_REQUEST::CloneRequest( DWORD dwCloneFlags, W3_REQUEST ** ppRequest ) /*++
Routine Description:
Clone request. Used to setup a child request to execute
Arguments:
dwCloneFlags - Flags controlling how much of the current request to clone. Without any flag, we will copy only the bare minimum
W3_REQUEST_CLONE_BASICS - clone URL/querystring/Verb W3_REQUEST_CLONE_HEADERS - clone request headers W3_REQUEST_CLONE_ENTITY - clone the entity body W3_REQUEST_CLONE_NO_PRECONDITION - remove range/if-* W3_REQUEST_CLONE_NO_DAV - remove DAV requests
ppRequest - Set to point to a new W3_REQUEST on success
Return Value:
HRESULT
--*/ { W3_CLONE_REQUEST * pCloneRequest = NULL; HRESULT hr = NO_ERROR; if ( ppRequest == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppRequest = NULL; //
// Allocate a new cloned request
//
pCloneRequest = new W3_CLONE_REQUEST(); if ( pCloneRequest == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } //
// Copy the bare minimum
//
hr = pCloneRequest->CopyMinimum( _pUlHttpRequest ); if ( FAILED( hr ) ) { goto Finished; } //
// Should we copy the request basics (URL/querystring/Verb)?
//
if ( dwCloneFlags & W3_REQUEST_CLONE_BASICS ) { hr = pCloneRequest->CopyBasics( _pUlHttpRequest ); } else { hr = pCloneRequest->CopyBasics( NULL ); } if ( FAILED( hr ) ) { goto Finished; } //
// Should we copy the headers?
//
if ( dwCloneFlags & W3_REQUEST_CLONE_HEADERS ) { hr = pCloneRequest->CopyHeaders( _pUlHttpRequest ); } else { hr = pCloneRequest->CopyHeaders( NULL ); }
if ( FAILED( hr ) ) { goto Finished; } //
// Should we also reference the parent's entity body
//
if ( dwCloneFlags & W3_REQUEST_CLONE_ENTITY ) { hr = pCloneRequest->CopyEntity( _pUlHttpRequest ); } else { hr = pCloneRequest->CopyEntity( NULL ); } if ( FAILED( hr ) ) { goto Finished; } //
// Remove conditionals if requested
//
if ( dwCloneFlags & W3_REQUEST_CLONE_NO_PRECONDITION ) { pCloneRequest->RemoveConditionals(); } //
// Remove DAV'ness if requested
//
if ( dwCloneFlags & W3_REQUEST_CLONE_NO_DAV ) { pCloneRequest->RemoveDav(); } *ppRequest = pCloneRequest; Finished: if ( FAILED( hr ) ) { if ( pCloneRequest != NULL ) { delete pCloneRequest; pCloneRequest = NULL; } } return hr; } //static
HRESULT W3_REQUEST::Initialize( VOID ) /*++
Routine Description:
Global initalization for utilities used by W3_REQUEST object
Arguments:
None
Return Value:
HRESULT
--*/ { CHAR achName[ MAX_PATH + 1 ]; INT err; HRESULT hr; ADDRINFO Hints; //
// Get the host name for use in remote/local determination
//
err = gethostname( achName, sizeof( achName ) ); if ( err != 0 ) { hr = HRESULT_FROM_WIN32( WSAGetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Error getting host name. hr = %x\n", hr )); return hr; } memset( &Hints, 0, sizeof( Hints ) ); Hints.ai_family = PF_UNSPEC; Hints.ai_flags = AI_CANONNAME;
err = getaddrinfo( achName, NULL, &Hints, &sm_pHostAddrInfo ); if( err != 0 && err != WSAHOST_NOT_FOUND ) { hr = HRESULT_FROM_WIN32( WSAGetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Error getting host adress info. hr = %x\n", hr )); return hr; }
hr = REQUEST_HEADER_HASH::Initialize(); if ( FAILED( hr ) ) { return hr; } hr = METHOD_HASH::Initialize(); if ( FAILED( hr ) ) { return hr; } hr = W3_CLONE_REQUEST::Initialize(); if ( FAILED( hr ) ) { REQUEST_HEADER_HASH::Terminate(); return hr; } return NO_ERROR; }
//static
VOID W3_REQUEST::Terminate( VOID ) { if( sm_pHostAddrInfo != NULL ) { freeaddrinfo( sm_pHostAddrInfo ); sm_pHostAddrInfo = NULL; } W3_CLONE_REQUEST::Terminate(); REQUEST_HEADER_HASH::Terminate();
METHOD_HASH::Terminate(); }
HRESULT W3_CLONE_REQUEST::CopyEntity( HTTP_REQUEST * pRequestToClone ) /*++
Routine Description:
Reference the parents entity body if required
Arguments:
pRequestToClone - UL_HTTP_REQUEST to clone. NULL if we shouldn't clone
Return Value:
HRESULT
--*/ { if ( pRequestToClone == NULL ) { _ulHttpRequest.Flags = 0; _ulHttpRequest.EntityChunkCount = 0; _ulHttpRequest.pEntityChunks = NULL; } else { _ulHttpRequest.Flags = pRequestToClone->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS; _ulHttpRequest.EntityChunkCount = pRequestToClone->EntityChunkCount; _ulHttpRequest.pEntityChunks = pRequestToClone->pEntityChunks; } return S_OK; }
HRESULT W3_CLONE_REQUEST::CopyBasics( HTTP_REQUEST * pRequestToClone ) /*++
Routine Description:
Copy the URL/query-string/Verb if required
Arguments:
pRequestToClone - HTTP_REQUEST to clone. NULL if we shouldn't clone
Return Value:
HRESULT
--*/ { if ( pRequestToClone == NULL ) { _ulHttpRequest.Verb = HttpVerbUnparsed;
_ulHttpRequest.UnknownVerbLength = 0; _ulHttpRequest.pUnknownVerb = NULL; _ulHttpRequest.RawUrlLength = 0; _ulHttpRequest.pRawUrl = NULL; _ulHttpRequest.CookedUrl.FullUrlLength = 0; _ulHttpRequest.CookedUrl.pFullUrl = NULL; _ulHttpRequest.CookedUrl.HostLength = 0; _ulHttpRequest.CookedUrl.pHost = NULL; _ulHttpRequest.CookedUrl.AbsPathLength = 0; _ulHttpRequest.CookedUrl.pAbsPath = NULL; _ulHttpRequest.CookedUrl.QueryStringLength = 0; _ulHttpRequest.CookedUrl.pQueryString = NULL; } else { _ulHttpRequest.Verb = pRequestToClone->Verb;
_ulHttpRequest.UnknownVerbLength = pRequestToClone->UnknownVerbLength; _ulHttpRequest.pUnknownVerb = pRequestToClone->pUnknownVerb; _ulHttpRequest.RawUrlLength = pRequestToClone->RawUrlLength; _ulHttpRequest.pRawUrl = pRequestToClone->pRawUrl; _ulHttpRequest.CookedUrl.FullUrlLength = pRequestToClone->CookedUrl.FullUrlLength; _ulHttpRequest.CookedUrl.pFullUrl = pRequestToClone->CookedUrl.pFullUrl; _ulHttpRequest.CookedUrl.HostLength = pRequestToClone->CookedUrl.HostLength; _ulHttpRequest.CookedUrl.pHost = pRequestToClone->CookedUrl.pHost; _ulHttpRequest.CookedUrl.AbsPathLength = pRequestToClone->CookedUrl.AbsPathLength; _ulHttpRequest.CookedUrl.pAbsPath = pRequestToClone->CookedUrl.pAbsPath; _ulHttpRequest.CookedUrl.QueryStringLength = pRequestToClone->CookedUrl.QueryStringLength; _ulHttpRequest.CookedUrl.pQueryString = pRequestToClone->CookedUrl.pQueryString; } return S_OK; }
HRESULT W3_CLONE_REQUEST::CopyMinimum( HTTP_REQUEST * pRequestToClone ) /*++
Routine Description:
Copies the bare minimum from the clonee. Bare minimums includes remote/local port/address, version, etc.
Arguments:
pRequestToClone - UL_HTTP_REQUEST to clone
Return Value:
HRESULT
--*/ { if ( pRequestToClone == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// Miscellaneous UL goo
//
_ulHttpRequest.ConnectionId = pRequestToClone->ConnectionId; _ulHttpRequest.RequestId = pRequestToClone->RequestId; _ulHttpRequest.UrlContext = pRequestToClone->UrlContext; _ulHttpRequest.Version = pRequestToClone->Version;
//
// Local/Remote address
//
_ulHttpRequest.Address.pRemoteAddress = pRequestToClone->Address.pRemoteAddress; _ulHttpRequest.Address.pLocalAddress = pRequestToClone->Address.pLocalAddress;
//
// Other stuff
//
_ulHttpRequest.RawConnectionId = pRequestToClone->RawConnectionId; _ulHttpRequest.pSslInfo = pRequestToClone->pSslInfo;
return NO_ERROR; }
HRESULT W3_CLONE_REQUEST::CopyHeaders( HTTP_REQUEST * pRequestToClone ) /*++
Routine Description: Copies request headers from pRequestToClone into the current cloned request
Arguments:
pRequestToClone - HTTP_REQUEST to clone, NULL if there should be no headers
Return Value:
HRESULT
--*/ { USHORT cUnknownHeaders;
if ( pRequestToClone == NULL ) { //
// No headers.
//
ZeroMemory( &( _ulHttpRequest.Headers ), sizeof( _ulHttpRequest.Headers ) ); } else { //
// Copy all the headers
//
//
// Start with the known headers. Note that we can just copy
// the Headers member directly. The memory being referenced by
// the pointers are guaranteed to be around for the life of this
// request. (the memory is either off the UL_NATIVE_REQUEST or
// it is off a CHUNK_BUFFER from the main parent request)
//
memcpy( _ulHttpRequest.Headers.KnownHeaders, pRequestToClone->Headers.KnownHeaders, sizeof( _ulHttpRequest.Headers.KnownHeaders ) );
//
// Copy the unknown headers. For this case, we will have to
// allocate our own buffer since the unknown header array can be
// resized. But as before, the memory referenced by the
// unknown headers is OK to reference again.
//
cUnknownHeaders = pRequestToClone->Headers.UnknownHeaderCount;
if ( cUnknownHeaders > 0 ) { _pExtraUnknown = (HTTP_UNKNOWN_HEADER*) LocalAlloc( LPTR, sizeof( HTTP_UNKNOWN_HEADER)* cUnknownHeaders ); if ( _pExtraUnknown == NULL ) { return HRESULT_FROM_WIN32( GetLastError() ); } memcpy( _pExtraUnknown, pRequestToClone->Headers.pUnknownHeaders, cUnknownHeaders * sizeof( HTTP_UNKNOWN_HEADER ) ); } _ulHttpRequest.Headers.UnknownHeaderCount = cUnknownHeaders; _ulHttpRequest.Headers.pUnknownHeaders = _pExtraUnknown; } return NO_ERROR; }
VOID W3_CLONE_REQUEST::RemoveConditionals( VOID ) /*++
Routine Description:
Remove conditional/range headers from request. Used to allow a custom error URL to work correctly
Arguments:
None
Return Value:
None
--*/ { HTTP_REQUEST_HEADERS * pHeaders;
pHeaders = &(_pUlHttpRequest->Headers);
//
// Remove Range:
//
pHeaders->KnownHeaders[ HttpHeaderRange ].pRawValue = NULL; pHeaders->KnownHeaders[ HttpHeaderRange ].RawValueLength = 0;
//
// Remove If-Modified-Since:
//
pHeaders->KnownHeaders[ HttpHeaderIfModifiedSince ].pRawValue = NULL; pHeaders->KnownHeaders[ HttpHeaderIfModifiedSince ].RawValueLength = 0;
//
// Remove If-Match:
//
pHeaders->KnownHeaders[ HttpHeaderIfMatch ].pRawValue = NULL; pHeaders->KnownHeaders[ HttpHeaderIfMatch ].RawValueLength = 0;
//
// Remove If-Unmodifed-Since:
//
pHeaders->KnownHeaders[ HttpHeaderIfUnmodifiedSince ].pRawValue = NULL; pHeaders->KnownHeaders[ HttpHeaderIfUnmodifiedSince ].RawValueLength = 0;
//
// Remove If-Range:
//
pHeaders->KnownHeaders[ HttpHeaderIfRange ].pRawValue = NULL; pHeaders->KnownHeaders[ HttpHeaderIfRange ].RawValueLength = 0;
//
// Remove If-None-Match:
//
pHeaders->KnownHeaders[ HttpHeaderIfNoneMatch ].pRawValue = NULL; pHeaders->KnownHeaders[ HttpHeaderIfNoneMatch ].RawValueLength = 0; }
//static
HRESULT W3_CLONE_REQUEST::Initialize( VOID ) /*++
Routine Description:
Initialize clone request lookaside
Arguments:
None
Return Value:
HRESULT
--*/ { ALLOC_CACHE_CONFIGURATION acConfig; DBG_ASSERT( sm_pachCloneRequests == NULL );
//
// Setup allocation lookaside
//
acConfig.nConcurrency = 1; acConfig.nThreshold = 100; acConfig.cbSize = sizeof( W3_CLONE_REQUEST ); sm_pachCloneRequests = new ALLOC_CACHE_HANDLER( "W3_CLONE_REQUEST", &acConfig ); if ( sm_pachCloneRequests == NULL ) { return HRESULT_FROM_WIN32( GetLastError() ); } return NO_ERROR; }
//static
VOID W3_CLONE_REQUEST::Terminate( VOID ) /*++
Routine Description:
Terminate clone request lookaside
Arguments:
None
Return Value:
None
--*/ { if ( sm_pachCloneRequests != NULL ) { delete sm_pachCloneRequests; sm_pachCloneRequests = NULL; } }
HRESULT W3_REQUEST::PreloadEntityBody( W3_CONTEXT *pW3Context, BOOL *pfComplete ) /*++
Routine Description:
Preload entity body for this request if appropriate
Arguments:
cbConfiguredReadAhead - Amount to preload pfComplete - Set to TRUE if preload is complete
Return Value:
HRESULT
--*/ { DWORD cbConfiguredReadAhead; DWORD cbAvailableAlready = 0; PVOID pbAvailableAlready = NULL; DWORD cbAmountToPreload = 0; HRESULT hr;
W3_METADATA *pMetadata = pW3Context->QueryUrlContext()->QueryMetaData(); cbConfiguredReadAhead = pMetadata->QueryEntityReadAhead();
*pfComplete = FALSE; //
// How much entity do we already have available to us? If it is more
// than the preload size then we are finished
//
pbAvailableAlready = QueryEntityBody(); cbAvailableAlready = QueryAvailableBytes();
if ( cbAvailableAlready >= cbConfiguredReadAhead ) { *pfComplete = TRUE; return S_OK; }
//
// OK. We don't have the configured preload-size number of bytes
// currently available.
//
// Do we know how many bytes of entity are available from UL still.
//
cbAmountToPreload = pW3Context->QueryRemainingEntityFromUl(); if ( cbAmountToPreload == INFINITE ) { //
// Must be a chunked request. Cap at configured read ahead
//
cbAmountToPreload = cbConfiguredReadAhead; } else if ( cbAmountToPreload == 0 ) { //
// There is no more data available from UL.
//
*pfComplete = TRUE; return S_OK; } else { //
// There is still data to be read from UL
//
cbAmountToPreload += cbAvailableAlready; cbAmountToPreload = min( cbAmountToPreload, cbConfiguredReadAhead ); } //
// Allocate the buffer
//
if ( !_buffEntityBodyPreload.Resize( cbAmountToPreload ) ) { return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); } //
// Copy any data already available to us
//
if ( cbAvailableAlready > 0 && pbAvailableAlready != _buffEntityBodyPreload.QueryPtr()) { DBG_ASSERT( pbAvailableAlready != NULL );
memcpy( _buffEntityBodyPreload.QueryPtr(), pbAvailableAlready, cbAvailableAlready ); } //
// Now read the read from UL asychronously
//
hr = pW3Context->ReceiveEntity( W3_FLAG_ASYNC, (PBYTE) _buffEntityBodyPreload.QueryPtr() + cbAvailableAlready, cbAmountToPreload - cbAvailableAlready, NULL );
if ( FAILED( hr ) ) { //
// In the chunked case, we do not know how many bytes there were so
// we can hit EOF. However, if the client sent content-length, then
// it is an error
//
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) && pW3Context->QueryRemainingEntityFromUl() == INFINITE) { pW3Context->SetRemainingEntityFromUl(0); *pfComplete = TRUE; return S_OK; }
return hr; } *pfComplete = FALSE; return S_OK; }
HRESULT W3_REQUEST::PreloadCompletion(W3_CONTEXT *pW3Context, DWORD cbRead, DWORD dwStatus, BOOL *pfComplete) { if ( dwStatus ) { //
// In the chunked case, we do not know how many bytes there were so
// we can hit EOF. However, if the client sent content-length, then
// it is an error
//
if (dwStatus == ERROR_HANDLE_EOF && pW3Context->QueryRemainingEntityFromUl() == INFINITE) { pW3Context->SetRemainingEntityFromUl(0); *pfComplete = TRUE; return S_OK; }
return HRESULT_FROM_WIN32(dwStatus); } SetNewPreloadedEntityBody(_buffEntityBodyPreload.QueryPtr(), QueryAvailableBytes() + cbRead);
return PreloadEntityBody(pW3Context, pfComplete); }
|