2691 lines
60 KiB
C++
2691 lines
60 KiB
C++
|
/*++
|
||
|
|
||
|
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
|
||
|
|
||
|
PHOSTENT W3_REQUEST::sm_pHostEnt;
|
||
|
|
||
|
ALLOC_CACHE_HANDLER * W3_CLONE_REQUEST::sm_pachCloneRequests;
|
||
|
|
||
|
DWORD
|
||
|
W3_REQUEST::QueryLocalAddress(
|
||
|
VOID
|
||
|
) const
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Get the local IP address connected to (assumes IPV4)
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Address
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HTTP_NETWORK_ADDRESS_IPV4 * pAddress;
|
||
|
|
||
|
DBG_ASSERT( _pUlHttpRequest->Address.LocalAddressType == HTTP_NETWORK_ADDRESS_TYPE_IPV4 );
|
||
|
pAddress = (HTTP_NETWORK_ADDRESS_IPV4 *)_pUlHttpRequest->Address.pLocalAddress;
|
||
|
return pAddress->IpAddress;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
pszUrl = _pUlHttpRequest->CookedUrl.pAbsPath;
|
||
|
|
||
|
//
|
||
|
// URL UL gives us has backslashes flipped. But it is not 0 terminated
|
||
|
//
|
||
|
|
||
|
chTemp = pszUrl[ _pUlHttpRequest->CookedUrl.AbsPathLength / 2 ];
|
||
|
pszUrl[ _pUlHttpRequest->CookedUrl.AbsPathLength / 2 ] = L'\0';
|
||
|
|
||
|
pszDotSlash = wcsstr( pszUrl, L"./" );
|
||
|
|
||
|
pszUrl[ _pUlHttpRequest->CookedUrl.AbsPathLength / 2 ] = chTemp;
|
||
|
|
||
|
if ( pszDotSlash != NULL )
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the URL ends with a ., it is also suspect
|
||
|
//
|
||
|
|
||
|
if ( pszUrl[ _pUlHttpRequest->CookedUrl.AbsPathLength / 2 - 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;
|
||
|
CHAR * 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,
|
||
|
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(),
|
||
|
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::QueryRemoteAddress(
|
||
|
VOID
|
||
|
) const
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Get the remote IP address connecting to us (assumes IPV4)
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Address
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HTTP_NETWORK_ADDRESS_IPV4 * pAddress;
|
||
|
|
||
|
DBG_ASSERT( _pUlHttpRequest->Address.RemoteAddressType == HTTP_NETWORK_ADDRESS_TYPE_IPV4 );
|
||
|
pAddress = (HTTP_NETWORK_ADDRESS_IPV4*) _pUlHttpRequest->Address.pRemoteAddress;
|
||
|
return pAddress->IpAddress;
|
||
|
}
|
||
|
|
||
|
USHORT
|
||
|
W3_REQUEST::QueryLocalPort(
|
||
|
VOID
|
||
|
) const
|
||
|
{
|
||
|
HTTP_NETWORK_ADDRESS_IPV4 * pAddress;
|
||
|
|
||
|
DBG_ASSERT( _pUlHttpRequest->Address.LocalAddressType == HTTP_NETWORK_ADDRESS_TYPE_IPV4 );
|
||
|
pAddress = (HTTP_NETWORK_ADDRESS_IPV4*) _pUlHttpRequest->Address.pLocalAddress;
|
||
|
return pAddress->Port;
|
||
|
}
|
||
|
|
||
|
USHORT
|
||
|
W3_REQUEST::QueryRemotePort(
|
||
|
VOID
|
||
|
) const
|
||
|
{
|
||
|
HTTP_NETWORK_ADDRESS_IPV4 * pAddress;
|
||
|
|
||
|
DBG_ASSERT( _pUlHttpRequest->Address.RemoteAddressType == HTTP_NETWORK_ADDRESS_TYPE_IPV4 );
|
||
|
pAddress = (HTTP_NETWORK_ADDRESS_IPV4*) _pUlHttpRequest->Address.pRemoteAddress;
|
||
|
return pAddress->Port;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
CHAR * pUserAgent;
|
||
|
UINT cUserAgent;
|
||
|
CHAR * pEnd;
|
||
|
|
||
|
if ( GetHeader( HttpHeaderVia ) || GetHeader( "Forward" ) )
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
if ( pUserAgent = GetHeader( HttpHeaderUserAgent ) )
|
||
|
{
|
||
|
cUserAgent = 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.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
CHAR * pTransferEncoding;
|
||
|
UINT cTransferEncoding;
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
if ( pTransferEncoding = GetHeader( HttpHeaderTransferEncoding ) )
|
||
|
{
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
CHAR * pszAuthType;
|
||
|
CHAR * 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->Reset();
|
||
|
hr = NO_ERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pszSpace = strchr( pszAuthType, ' ' );
|
||
|
|
||
|
if ( pszSpace == NULL )
|
||
|
{
|
||
|
hr = pstrAuthType->Copy( pszAuthType );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = pstrAuthType->Copy( pszAuthType,
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD localAddress = QueryLocalAddress();
|
||
|
DWORD remoteAddress = QueryRemoteAddress();
|
||
|
CHAR** list;
|
||
|
PIN_ADDR p;
|
||
|
|
||
|
//
|
||
|
// Are the remote/local addresses the same?
|
||
|
//
|
||
|
|
||
|
if ( localAddress == remoteAddress )
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Are either equal to 127.0.0.1
|
||
|
//
|
||
|
|
||
|
if ( localAddress == LOCAL127 ||
|
||
|
remoteAddress == LOCAL127 )
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Is the remote address equal to one of the host addresses
|
||
|
//
|
||
|
|
||
|
list = sm_pHostEnt->h_addr_list;
|
||
|
|
||
|
while ( (p = (PIN_ADDR)*list++) != NULL )
|
||
|
{
|
||
|
if ( p->s_addr == remoteAddress )
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
LPWSTR pszColon = wcschr( _pUlHttpRequest->CookedUrl.pHost, ':' );
|
||
|
if ((pszColon != NULL) &&
|
||
|
((IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT_SECURE) ||
|
||
|
(!IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT)))
|
||
|
{
|
||
|
hr = pstrRedirect->Append( _pUlHttpRequest->CookedUrl.pHost,
|
||
|
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;
|
||
|
|
||
|
if ( pstrRedirect == NULL )
|
||
|
{
|
||
|
DBG_ASSERT( FALSE );
|
||
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
||
|
}
|
||
|
|
||
|
hr = pstrRedirect->Copy( IsSecureRequest() ? "https://" : "http://" );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
LPWSTR pszColon = wcschr( _pUlHttpRequest->CookedUrl.pHost, ':' );
|
||
|
if ((pszColon != NULL) &&
|
||
|
((IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT_SECURE) ||
|
||
|
(!IsSecureRequest() && _wtoi(pszColon + 1) == DEFAULT_PORT)))
|
||
|
{
|
||
|
hr = pstrRedirect->AppendW( _pUlHttpRequest->CookedUrl.pHost,
|
||
|
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);
|
||
|
HRESULT hr = HRESULT_FROM_WIN32( ERROR_INVALID_INDEX );
|
||
|
|
||
|
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 pHeaders->pUnknownHeaders[ i ].pRawValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Known header
|
||
|
//
|
||
|
|
||
|
DBG_ASSERT( ulHeaderIndex < HttpHeaderRequestMaximum );
|
||
|
|
||
|
return pHeaders->pKnownHeaders[ 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->pKnownHeaders[ ulHeaderIndex ].pRawValue != NULL )
|
||
|
{
|
||
|
hr = pstrHeaderValue->Copy(
|
||
|
pHeaders->pKnownHeaders[ ulHeaderIndex ].pRawValue,
|
||
|
pHeaders->pKnownHeaders[ 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;
|
||
|
HRESULT hr;
|
||
|
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 < HttpHeaderResponseMaximum )
|
||
|
{
|
||
|
_pUlHttpRequest->Headers.pKnownHeaders[ ulHeaderIndex ].pRawValue = NULL;
|
||
|
_pUlHttpRequest->Headers.pKnownHeaders[ 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::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.pKnownHeaders[ index ].pRawValue = pszNewValue;
|
||
|
_pUlHttpRequest->Headers.pKnownHeaders[ index ].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 = _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 = 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
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD cchNewUrl;
|
||
|
HRESULT hr = NO_ERROR;
|
||
|
ULONG cbBytesCopied;
|
||
|
LPWSTR pszQueryString;
|
||
|
CHAR * pszNewRawUrl;
|
||
|
LPWSTR pszNewFullUrl;
|
||
|
LPWSTR pszNewAbsPath;
|
||
|
STACK_STRU ( strFullUrl, MAX_PATH);
|
||
|
STACK_STRU ( strAbsPath, MAX_PATH);
|
||
|
|
||
|
//
|
||
|
// 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 = 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(
|
||
|
CHAR * pszHeaderName,
|
||
|
DWORD cchHeaderName,
|
||
|
CHAR * 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(
|
||
|
CHAR * pszHeaderName,
|
||
|
DWORD cchHeaderName,
|
||
|
CHAR * 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;
|
||
|
CHAR * pszCursor;
|
||
|
|
||
|
hr = pstrHeaderLine->Append( pszHeaderName );
|
||
|
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;
|
||
|
CHAR * 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.pKnownHeaders[ 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;
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
sm_pHostEnt = gethostbyname( achName );
|
||
|
if ( sm_pHostEnt == NULL )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( WSAGetLastError() );
|
||
|
DBGPRINTF(( DBG_CONTEXT,
|
||
|
"Error getting host. 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
|
||
|
)
|
||
|
{
|
||
|
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.MoreEntityBodyExists = FALSE;
|
||
|
_ulHttpRequest.EntityChunkCount = 0;
|
||
|
_ulHttpRequest.pEntityChunks = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_ulHttpRequest.MoreEntityBodyExists = pRequestToClone->MoreEntityBodyExists;
|
||
|
_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.RemoteAddressLength = pRequestToClone->Address.RemoteAddressLength;
|
||
|
_ulHttpRequest.Address.RemoteAddressType = pRequestToClone->Address.RemoteAddressType;
|
||
|
_ulHttpRequest.Address.LocalAddressLength = pRequestToClone->Address.LocalAddressLength;
|
||
|
_ulHttpRequest.Address.LocalAddressType = pRequestToClone->Address.LocalAddressType;
|
||
|
_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
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD 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.pKnownHeaders,
|
||
|
pRequestToClone->Headers.pKnownHeaders,
|
||
|
sizeof( _ulHttpRequest.Headers.pKnownHeaders ) );
|
||
|
|
||
|
//
|
||
|
// 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->pKnownHeaders[ HttpHeaderRange ].pRawValue = NULL;
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderRange ].RawValueLength = 0;
|
||
|
|
||
|
//
|
||
|
// Remove If-Modified-Since:
|
||
|
//
|
||
|
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfModifiedSince ].pRawValue = NULL;
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfModifiedSince ].RawValueLength = 0;
|
||
|
|
||
|
//
|
||
|
// Remove If-Match:
|
||
|
//
|
||
|
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfMatch ].pRawValue = NULL;
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfMatch ].RawValueLength = 0;
|
||
|
|
||
|
//
|
||
|
// Remove If-Unmodifed-Since:
|
||
|
//
|
||
|
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfUnmodifiedSince ].pRawValue = NULL;
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfUnmodifiedSince ].RawValueLength = 0;
|
||
|
|
||
|
//
|
||
|
// Remove If-Range:
|
||
|
//
|
||
|
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfRange ].pRawValue = NULL;
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfRange ].RawValueLength = 0;
|
||
|
|
||
|
//
|
||
|
// Remove If-None-Match:
|
||
|
//
|
||
|
|
||
|
pHeaders->pKnownHeaders[ HttpHeaderIfNoneMatch ].pRawValue = NULL;
|
||
|
pHeaders->pKnownHeaders[ 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( GetLastError() );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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);
|
||
|
}
|