Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3527 lines
86 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name :
isapi_request.cxx
Abstract:
IIS+ IIsapiCore implementation.
Author:
Wade Hilmo (wadeh) 29-Aug-2000
Project:
w3core.dll
--*/
#include "precomp.hxx"
#include "isapi_request.hxx"
#include "iisapicore_i.c"
#include "isapi_handler.h"
#include "stringau.hxx"
#define ISAPI_REQUEST_CACHE_THRESHOLD (400) // Value lifted from IIS 5
#define SZ_FAILED_OOP_REQUEST_LOG_MESSAGE \
"Out-of-process+ISAPI+extension+request+failed."
ALLOC_CACHE_HANDLER * ISAPI_REQUEST::sm_pachIsapiRequest;
PTRACE_LOG ISAPI_REQUEST::sm_pTraceLog;
HRESULT
SendEntityBodyAndLogDataHelper(
W3_CONTEXT *pW3Context,
ULATQ_CONTEXT pContext,
BOOL fAsync,
DWORD dwFlags,
USHORT cChunks,
HTTP_DATA_CHUNK * pChunks,
DWORD *pcbSent
);
BOOL
ISAPI_REQUEST::InitClass( VOID )
/*++
Routine Description:
Acache initialization function
Arguments:
None
Return Value:
TRUE on success, FALSE on error
--*/
{
ALLOC_CACHE_CONFIGURATION acConfig = { 1, ISAPI_REQUEST_CACHE_THRESHOLD,
sizeof( ISAPI_REQUEST ) };
if ( NULL != sm_pachIsapiRequest) {
// already initialized
return ( TRUE);
}
sm_pachIsapiRequest = new ALLOC_CACHE_HANDLER( "IsapiRequest",
&acConfig);
#if DBG
sm_pTraceLog = CreateRefTraceLog( 2000, 0 );
#else
sm_pTraceLog = NULL;
#endif
return ( NULL != sm_pachIsapiRequest);
}
VOID
ISAPI_REQUEST::CleanupClass( VOID )
/*++
Routine Description:
Acache cleanup function
Arguments:
None
Return Value:
None
--*/
{
if ( sm_pTraceLog != NULL )
{
DestroyRefTraceLog( sm_pTraceLog );
sm_pTraceLog = NULL;
}
if ( NULL != sm_pachIsapiRequest)
{
delete sm_pachIsapiRequest;
sm_pachIsapiRequest = NULL;
}
return;
}
HRESULT
ISAPI_REQUEST::Create(
VOID
)
/*++
Routine Description:
Creates a newly allocated ISAPI_REQUEST object. This
function does initialization tasks which wouldn't be
appropriate in the constructor due to potential failure
during initialization.
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = NOERROR;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
IF_DEBUG( ISAPI )
{
DBGPRINTF((
DBG_CONTEXT,
"Creating ISAPI_REQUEST %p, W3Context=%p, Handler=%p.\r\n",
this,
_pW3Context,
_pW3Context->QueryHandler()
));
}
if ( _fIsOop )
{
hr = CoCreateFreeThreadedMarshaler(this, &_pUnkFTM);
}
if ( FAILED( hr ) )
{
DBGPRINTF((
DBG_CONTEXT,
"ISAPI_REQUEST %p. Failed to CoCreate free threaded marshaler.\r\n",
this
));
goto ErrorExit;
}
IF_DEBUG( ISAPI )
{
DBGPRINTF((
DBG_CONTEXT,
"ISAPI_REQUEST %p created successfully.\r\n",
this
));
}
return hr;
ErrorExit:
DBG_ASSERT( FAILED( hr ) );
DBGPRINTF((
DBG_CONTEXT,
"Failed to create ISAPI_REQUEST %p. HRESULT=%08x.\r\n",
this,
hr
));
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::QueryInterface(
REFIID riid,
void __RPC_FAR *__RPC_FAR *ppvObject
)
/*++
Routine Description:
COM Goo
Arguments:
riid - Id of the interface requested
ppvObject - Upon return, points to requested interface
Return Value:
HRESULT
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( ppvObject );
*ppvObject = NULL;
/*
DBGPRINTF((
DBG_CONTEXT,
"WAM_REQUEST::QueryInterface looking for ... ( " GUID_FORMAT " )\n",
GUID_EXPAND( &riid)
));
*/
if( riid == IID_IIsapiCore )
{
*ppvObject = static_cast<IIsapiCore *>( this );
}
else if ( riid == IID_IMarshal )
{
if ( _pUnkFTM == NULL )
{
DBG_ASSERT(FALSE);
return E_NOINTERFACE;
}
else
{
return _pUnkFTM->QueryInterface(riid, ppvObject);
}
}
else if( riid == IID_IUnknown )
{
*ppvObject = static_cast<IIsapiCore *>( this );
}
else if ( _pUnkFTM != NULL )
{
return _pUnkFTM->QueryInterface(riid, ppvObject);
}
else
{
return E_NOINTERFACE;
}
DBG_ASSERT( *ppvObject );
((IUnknown *)*ppvObject)->AddRef();
/*
DBGPRINTF((
DBG_CONTEXT,
"WAM_REQUEST::QueryInterface found ( " GUID_FORMAT ", %p )\n",
GUID_EXPAND( &riid),
*ppvObject
));
*/
return NOERROR;
}
ULONG STDMETHODCALLTYPE
ISAPI_REQUEST::AddRef(
void
)
/*++
Routine Description:
COM Goo - adds a reference to the object
Arguments:
None
Return Value:
The number of references remaining at completion of this call
--*/
{
LONG cRefs;
DBG_ASSERT( CheckSignature() );
cRefs = InterlockedIncrement( &_cRefs );
//
// Log the reference ( sm_pTraceLog!=NULL if DBG=1)
//
if ( sm_pTraceLog != NULL )
{
WriteRefTraceLog( sm_pTraceLog,
cRefs,
this );
}
return cRefs;
}
ULONG STDMETHODCALLTYPE
ISAPI_REQUEST::Release(
void
)
/*++
Routine Description:
COM Goo - deletes a referece to the object, and deletes
the object upon zero references
Arguments:
None
Return Value:
The number of references remaining at completion of this call
--*/
{
LONG cRefs;
BOOL fIsOop = _fIsOop;
W3_ISAPI_HANDLER * pW3IsapiHandler = NULL;
DBG_ASSERT( CheckSignature() );
//
// WARNING - This object is always created by W3_ISAPI_HANDLER,
// and that code uses the return value from Release
// to determine if it's safe to advance the core
// state machine. It is essential that this function
// only return 0 in the case where it's called delete.
//
cRefs = InterlockedDecrement( &_cRefs );
if ( sm_pTraceLog != NULL )
{
WriteRefTraceLog( sm_pTraceLog,
cRefs,
this );
}
if ( ( cRefs == 1 ) && fIsOop )
{
_pWamProcess->RemoveIsapiRequestFromList( this );
}
if ( cRefs == 0 )
{
DBG_ASSERT( _pW3Context );
DBG_REQUIRE( ( pW3IsapiHandler = (W3_ISAPI_HANDLER*)_pW3Context->QueryHandler() ) != NULL );
delete this;
//
// Notify the W3_ISAPI_HANDLER that we are done with it
//
pW3IsapiHandler->IsapiRequestFinished();
return 0;
}
return cRefs;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::GetServerVariable(
LPSTR szVariableName,
BYTE * szBuffer,
DWORD cbBuffer,
DWORD * pcbBufferRequired
)
/*++
Routine Description:
Returns the value of a server variable
Arguments:
szVariableName - The name of the server variable
szBuffer - Upon return, contains the value of the server variable
cbBuffer - The size of szBuffer.
pcbBufferRequired - On successful return, the number of bytes copied
to the buffer. On failure, the number of bytes
required to hold szBuffer.
Return Value:
HRESULT
--*/
{
HRESULT hr = NOERROR;
//
// The only current caller for this function is w3isapi.dll, which does
// parameter validation from any outside code. So, we can get away with
// just asserting here.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_ASSERT( szVariableName );
DBG_ASSERT( pcbBufferRequired );
DBG_ASSERT( szBuffer || ( cbBuffer == 0 ) );
hr = SERVER_VARIABLE_HASH::GetServerVariable(
_pW3Context,
szVariableName,
(LPSTR)szBuffer,
&cbBuffer
);
*pcbBufferRequired = cbBuffer;
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::ReadClient(
DWORD64 IsaContext,
BYTE *pBuffer,
DWORD cbBuffer,
DWORD dwBytesToRead,
DWORD *pdwSyncBytesRead,
DWORD dwFlags
)
/*++
Routine Description:
Reads data from the client
Arguments:
IsaContext - The ISAPI_CONTEXT for this request (opaque)
pBuffer - Contains read data upon return for sync reads
cbBuffer - The size of pSyncReadBuffer
dwBytesToRead - The number of bytes to read
pdwSyncBytesRead - The number of bytes copied to pBuffer in sync case
dwFlags - HSE_IO_* flags from caller
Return Value:
HRESULT
--*/
{
DWORD dwBytesRead;
HRESULT hr = NOERROR;
BOOL fAsync = !!( dwFlags & HSE_IO_ASYNC );
//
// The only current caller for this function is w3isapi.dll, which does
// parameter validation from any outside code. So, we can get away with
// just asserting here.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_ASSERT( fAsync == !!IsaContext );
DBG_ASSERT( dwBytesToRead );
DBG_ASSERT( pdwSyncBytesRead || fAsync );
//
// Make /W4 happy...
//
if ( pBuffer == NULL && cbBuffer != 0 )
{
DBG_ASSERT( pBuffer || ( cbBuffer == 0 ) );
}
if ( fAsync )
{
DBG_ASSERT( _IsapiContext == 0 );
_IsapiContext = IsaContext;
}
//
// If this is an OOP async read, then we need to create a
// local buffer to read into.
//
if ( _fIsOop && fAsync )
{
DBG_ASSERT( _pAsyncReadBuffer == NULL );
_pAsyncReadBuffer = (LPBYTE)LocalAlloc( LPTR, dwBytesToRead );
if ( !_pAsyncReadBuffer )
{
goto ErrorExit;
}
//
// This reference insures that if the OOP host crashes and
// COM releases all the OOP-held references, that this
// object will survive until the I/O completion occurs.
//
AddRef();
hr = _pW3Context->ReceiveEntity(
fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC,
_pAsyncReadBuffer,
dwBytesToRead,
&dwBytesRead
);
}
else
{
hr = _pW3Context->ReceiveEntity(
fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC,
pBuffer,
dwBytesToRead,
fAsync ? &dwBytesRead : pdwSyncBytesRead
);
}
//
// If the request is chunked, look for ERROR_HANDLE_EOF. This
// is how http.sys signals the end of a chunked request.
//
// Since an ISAPI extension is looking for a successful, zero
// byte read, we'll need to change the result of the above call.
//
// Note that on an asynchronous call, we'll need to trigger a
// "fake" completion with zero bytes.
//
if ( FAILED( hr ) && _pW3Context->QueryRequest()->IsChunkedRequest() )
{
if ( hr == HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) )
{
hr = NOERROR;
if ( fAsync )
{
POST_MAIN_COMPLETION( _pW3Context->QueryMainContext() );
}
else
{
*pdwSyncBytesRead = 0;
}
}
}
//
// We now return you to your regular error handling program.
//
if ( FAILED( hr ) )
{
//
// If ReceiveEntity fails for some reason other than
// EOF, then consider this a non recoverable
// error and set the error to be WSAECONNRESET for
// compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
if ( _fIsOop && fAsync )
{
//
// Release the above reference, since no completion
// will be coming.
//
Release();
}
goto ErrorExit;
}
return hr;
ErrorExit:
DBG_ASSERT( FAILED( hr ) );
if ( fAsync &&
_IsapiContext != 0 )
{
_IsapiContext = 0;
}
if ( _pAsyncReadBuffer )
{
LocalFree( _pAsyncReadBuffer );
_pAsyncReadBuffer = NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::WriteClient(
DWORD64 IsaContext,
BYTE * pBuffer,
DWORD cbBuffer,
DWORD dwFlags
)
/*++
Routine Description:
Writes data to the client
Arguments:
IsaContext - The ISAPI_CONTEXT for this request (opaque)
pBuffer - Contains the data to write
cbBuffer - The amount of data to be written
dwFlags - HSE_IO_* flags from caller
Return Value:
HRESULT
--*/
{
W3_RESPONSE * pResponse;
HRESULT hr = NOERROR;
BOOL fAsync = !!( dwFlags & HSE_IO_ASYNC );
//
// The only current caller for this function is w3isapi.dll, which does
// parameter validation from any outside code. So, we can get away with
// just asserting here.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_ASSERT( fAsync == !!IsaContext );
DBG_ASSERT( pBuffer || ( cbBuffer == 0 ) );
//
// For a zero byte send, just return
//
// If the call was async, we'll need to fire off a completion.
//
// Note that this case shouldn't ever happen, as the w3isapi.dll
// code that calls this currently does a check for a zero byte
// write attempt. That code is somewhat broken in that a completion
// will never occur for a zero byte completion. That's the way all
// previous versions of ISAPI have worked, though. This code is
// here for the sole purpose that this interface could work properly
// with an API that expects a completion on a zero byte async write.
//
if ( cbBuffer == 0 )
{
if ( fAsync )
{
POST_MAIN_COMPLETION( _pW3Context->QueryMainContext() );
}
return hr;
}
if ( fAsync )
{
DBG_ASSERT( _IsapiContext == 0 );
_IsapiContext = IsaContext;
}
DBG_REQUIRE( ( pResponse = _pW3Context->QueryResponse() ) != NULL );
//
// If this as an OOP async write then we will work from a local copy
// of pBuffer.
//
if ( _fIsOop && fAsync )
{
DBG_ASSERT( _pAsyncWriteBuffer == NULL );
_pAsyncWriteBuffer = (LPBYTE)LocalAlloc( LPTR, cbBuffer );
if ( !_pAsyncWriteBuffer )
{
goto ErrorExit;
}
memcpy( _pAsyncWriteBuffer, pBuffer, cbBuffer );
pBuffer = _pAsyncWriteBuffer;
}
//
// Before sending the current data, we need to clear out
// any outstanding chunks from the response object. This can't
// ever cause a problem for a purely synchronous ISAPI. And,
// since w3isapi.dll protects against multiple outstanding
// asynchronous I/O, we shouldn't see a problem with a
// purely asynchronous I/O.
//
// If an ISAPI sends data asynchronously and then follows up
// with a second, synchronous send, then it's possible that
// the second send could clear the chunks from the first before
// they've been fully processed. This is a really, really
// dumb thing for an ISAPI to do, since the response would
// likely be scrambled at the client. So, we'll live with
// problems in that scenario.
//
pResponse->Clear();
if (!_pW3Context->QueryResponseSent())
{
_pW3Context->SetDisconnect( TRUE );
}
//
// Now setup the buffer we want to send
//
hr = pResponse->AddMemoryChunkByReference(
pBuffer,
cbBuffer
);
if ( FAILED( hr ) )
{
goto ErrorExit;
}
//
// Ok, now send what we've got.
//
if ( _fIsOop && fAsync )
{
//
// This reference insures that if the OOP host crashes and
// COM releases all the OOP-held references, that this
// object will survive until the I/O completion occurs.
//
AddRef();
}
hr = _pW3Context->SendEntity(
( fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC ) | W3_FLAG_MORE_DATA
);
if ( FAILED( hr ) )
{
//
// If SendEntity fails, consider this a non recoverable
// error and set the error to be WSAECONNRESET for
// compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
if ( _fIsOop && fAsync )
{
//
// Release the above reference, since no I/O completion
// will ever happen.
//
Release();
}
goto ErrorExit;
}
return hr;
ErrorExit:
DBG_ASSERT( FAILED( hr ) );
//
// Note that checking for async here is not an optimization.
//
// ISAPI guards against two concurrent async operations, but
// it's valid to have a synchronous and asynchronous operation
// happening currently (ie. async write, exec url, etc.,
// concurrent with a synchronous read).
//
// If we don't check for async, it's possible for a failed
// synchronous read to reset the _IsapiContext that belongs
// to some other action that's running async.
//
if ( fAsync &&
_IsapiContext != 0 )
{
_IsapiContext = 0;
}
if ( _pAsyncWriteBuffer )
{
LocalFree( _pAsyncWriteBuffer );
_pAsyncWriteBuffer = NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::SendResponseHeaders(
BOOL fDisconnect,
LPSTR szStatus,
LPSTR szHeaders,
DWORD
)
/*++
Routine Description:
Sends response headers to the client
Arguments:
fDisconnect - If FALSE, then we need to avoid closing the connection
szStatus - The status to send (ie. "200 OK")
szHeaders - The headers to send (ie. "foo: value1\r\nBar: value2\r\n")
dwFlags - HSE_IO_* flags from caller
Return Value:
HRESULT
--*/
{
W3_RESPONSE * pResponse;
STACK_STRA( strStatus,64);
STACK_STRA( strHeaders,128);
LPSTR szCookedStatus = szStatus;
LPSTR szCookedHeaders = szHeaders;
LPSTR pCursor = NULL;
HRESULT hr;
//
// The only current caller for this function is w3isapi.dll, which does
// parameter validation from any outside code. So, we can get away with
// just asserting here.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_REQUIRE( ( pResponse = _pW3Context->QueryResponse() ) != NULL );
//
// Legacy ISAPI Gymnastics:
//
// Some ISAPI extensions will try and piggy-back response
// headers onto the status string that they pass to this
// function (ie. "200 OK\r\nFoo: bar".) While this would
// work in IIS 5 - since IIS 5 didn't try and validate the
// stream - it doesn't fly with http.sys. Ultimately, this
// will cause http.sys to fail our send response call.
//
// So, to accommodate these ISAPIs, we'll look for this and
// move any such piggy-backed headers into the right argument.
//
if ( szStatus )
{
pCursor = strpbrk( szStatus, "\r\n" );
}
if ( pCursor )
{
//
// Create a local copy of the status and
// strip off the extra header data.
//
hr = strStatus.Copy( szStatus );
if ( FAILED( hr ) )
{
return ( hr );
}
strStatus.SetLen( pCursor - szStatus );
//
// Eat the \r\n preceding the header
//
while ( *pCursor == '\r' ||
*pCursor == '\n' )
{
pCursor++;
}
//
// Create a local copy of the headers
//
hr = strHeaders.Copy( pCursor );
if ( FAILED( hr ) )
{
return hr;
}
//
// We'll want to make sure that strHeaders ends
// properly with a "\r\n" at this point. Probably the
// easiest way to do this is to just eat any trailing
// '\r' or '\n' characters and then append it (some ISAPI
// developers omit the '\r' and just go with '\n'...)
//
pCursor = strHeaders.QueryStr() + strHeaders.QueryCCH() - 1;
while ( pCursor > strHeaders.QueryStr() &&
( *pCursor == '\r' ||
*pCursor == '\n' ) )
{
pCursor--;
}
strHeaders.SetLen( pCursor - strHeaders.QueryStr() + 1 );
hr = strHeaders.Append( "\r\n" );
if ( FAILED( hr ) )
{
return hr;
}
//
// If we were passed a non-NULL pointer for szHeaders, we'll
// need to append that data to strHeaders. We'll assume that
// szHeaders was correctly formed by the caller and skip the
// "\r\n" games.
//
// Else, if we were passed a NULL pointer for szHeaders, we
// still need to append the final "\r\n" before the entity
// body.
//
if ( szHeaders )
{
hr = strHeaders.Append( szHeaders );
}
else
{
hr = strHeaders.Append( "\r\n" );
}
if ( FAILED( hr ) )
{
return hr;
}
//
// Finally, set szCookedStatus and szCookedHeaders to point
// at our new data.
//
szCookedStatus = strStatus.QueryStr();
szCookedHeaders = strHeaders.QueryStr();
}
//
// Set the disconnect disposition
//
_pW3Context->SetDisconnect( fDisconnect );
//
// Need to clear any existing response
//
pResponse->Clear();
//
// Setup response from ISAPI
//
hr = pResponse->BuildResponseFromIsapi(
_pW3Context,
szCookedStatus,
szCookedHeaders,
szCookedHeaders ? (DWORD)strlen( szCookedHeaders ) : 0
);
if ( FAILED( hr ) )
{
return hr;
}
//
// Is the status is access denied, then set the sub status to
// "Denied by Application"
//
if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode )
{
pResponse->SetStatus( HttpStatusUnauthorized,
Http401Application );
}
hr = _pW3Context->SendResponse(
W3_FLAG_SYNC
| W3_FLAG_MORE_DATA
| W3_FLAG_NO_ERROR_BODY
| W3_FLAG_NO_CONTENT_LENGTH );
if ( FAILED( hr ) )
{
//
// If SendResponse fails, consider this a non recoverable
// error and set the error to be WSAECONNRESET for
// compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::MapPath(
BYTE * szPath,
DWORD cbPath,
DWORD * pcbBufferRequired,
BOOL fUnicode
)
/*++
Routine Description:
Maps a URL to a physical path
Arguments:
szPath - On entry, the URL to map
- On return, the mapped physical path
cbPath - The size of szPath
pcbBufferRequired - On successful return, the number of szPath
On error return, the number of bytes needed in szPath
fUnicode - If TRUE, szPath should be a UNICODE string on entry
and return
Return Value:
HRESULT
--*/
{
STACK_STRU( struUrl,MAX_PATH );
STACK_STRU( struPath,MAX_PATH );
HRESULT hr;
//
// The only current caller for this function is w3isapi.dll, which does
// parameter validation from any outside code. So, we can get away with
// just asserting here.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_ASSERT( szPath || ( cbPath == 0 ) );
DBG_ASSERT( pcbBufferRequired );
//
// This is kind of a weird function - the return string
// gets copied over the top of the source string.
//
if ( fUnicode )
{
hr = struUrl.Copy( (LPWSTR)szPath );
}
else
{
hr = struUrl.CopyA( (LPSTR)szPath );
}
if ( FAILED( hr ) )
{
return hr;
}
hr = W3_STATE_URLINFO::MapPath(
_pW3Context,
struUrl,
&struPath,
TRUE,
NULL,
NULL,
NULL,
NULL,
NULL
);
if ( FAILED( hr ) )
{
return hr;
}
*pcbBufferRequired = cbPath;
if ( fUnicode )
{
return struPath.CopyToBuffer( (LPWSTR)szPath, pcbBufferRequired );
}
else
{
STACK_STRA (straPath, MAX_PATH );
if (FAILED(hr = straPath.CopyW(struPath.QueryStr(),
struPath.QueryCCH())))
{
return hr;
}
return straPath.CopyToBuffer( (LPSTR)szPath, pcbBufferRequired );
}
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::MapPathEx(
BYTE * szUrl,
DWORD ,
BYTE * szPath,
DWORD cbPath,
DWORD * pcbBufferRequired,
DWORD * pcchMatchingPath,
DWORD * pcchMatchingUrl,
DWORD * pdwFlags,
BOOL fUnicode
)
/*++
Routine Description:
Does path mapping, plus a bit more
Arguments:
szUrl - The URL to map
szPath - Upon return, the physical path for the URL
cbPath - The size of szPath
pcbBufferRequired - Upon failed return, the size needed for szPath
pcchMatchingPath - Upon return, the number of characters in szPath
that correspond to the vroot in the URL
pcchMatchingUrl - Upon return, the number of characters in szUrl
that correspond to the vroot in the URL
pdwFlags - Upon return, the metadata AccessPerm flags for the URL
fUnicode - If TRUE, the caller wants to talk UNICODE
Return Value:
HRESULT
--*/
{
STACK_STRU( struUrl,MAX_PATH );
STACK_STRU( struPath,MAX_PATH );
DWORD cbPathCopied;
DWORD cchMatchingPathA;
DWORD cchMatchingUrlA;
HRESULT hr;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
if ( fUnicode )
{
hr = struUrl.Copy( (LPWSTR)szUrl );
}
else
{
hr = struUrl.CopyA( (LPSTR)szUrl );
}
if ( FAILED( hr ) )
{
return hr;
}
//
// Call W3_STATE_URLINFO::MapPath to do the work
//
hr = W3_STATE_URLINFO::MapPath(
_pW3Context,
struUrl,
&struPath,
TRUE,
pcchMatchingPath,
pcchMatchingUrl,
&cchMatchingPathA,
&cchMatchingUrlA,
pdwFlags
);
if ( FAILED( hr ) )
{
return hr;
}
//
// W3_STATE_URLINFO::MapPath works by looking for a cache entry
// for the requested szUrl. If it exists, the returned information
// will come from the cache data. This causes a potential problem
// if the cache contains a URL as "/foo/" and the caller to this
// function passes "/foo" as the URL. In this case, *pcchMatchingUrl
// will be 5, as it's derived from the cache data. It would be bad,
// though, to pass a *pcchMatchingUrl greater than the length of the
// szUrl we were given...
//
if ( fUnicode )
{
if ( *pcchMatchingUrl &&
( ( ((LPWSTR)szUrl)[*pcchMatchingUrl - 1] == L'\0' ) ||
( ((LPWSTR)szUrl)[*pcchMatchingUrl - 1] == L'/' ) ) )
{
(*pcchMatchingUrl)--;
}
}
else
{
//
// Use the ANSI matching value
//
*pcchMatchingUrl = cchMatchingUrlA;
if ( *pcchMatchingUrl &&
( ( szUrl[*pcchMatchingUrl - 1] == '\0' ) ||
( szUrl[*pcchMatchingUrl - 1] == '/' ) ) )
{
(*pcchMatchingUrl)--;
}
}
if ( pcbBufferRequired )
{
if ( fUnicode )
{
*pcbBufferRequired = (struPath.QueryCCH() + 1) * sizeof(WCHAR);
}
else
{
*pcbBufferRequired = struPath.QueryCCH() + 1;
}
}
if ( szPath )
{
if ( fUnicode )
{
cbPathCopied = cbPath;
hr = struPath.CopyToBuffer( (LPWSTR)szPath, &cbPathCopied );
if ( hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) &&
cbPath >= sizeof(WCHAR) )
{
//
// Copy what we can.
//
// Note that there are ISAPI extensions that depend on
// being able to get a truncated path in the case where
// MAX_PATH is not sufficient to store the complete data.
//
// These extensions depend on using ERROR_INSUFFICIENT_BUFFER
// as a warning that the data is truncated.
//
memset( szPath, 0, cbPath );
memcpy( szPath, struPath.QueryStr(), cbPath - sizeof(WCHAR) );
}
}
else
{
//
// Convert the path to ANSI
//
STACK_STRA( strAnsiPath, MAX_PATH+1 );
DWORD cbAnsiPath;
DWORD dwError;
//
// Get the ANSI version of the path.
//
cbAnsiPath = WideCharToMultiByte( CP_ACP,
0,
struPath.QueryStr(),
-1,
strAnsiPath.QueryStr(),
MAX_PATH,
NULL,
NULL );
if ( cbAnsiPath == 0 )
{
dwError = GetLastError();
if ( dwError == ERROR_INSUFFICIENT_BUFFER )
{
//
// Get the size of the buffer needed and
// call it again.
//
cbAnsiPath = WideCharToMultiByte( CP_ACP,
0,
struPath.QueryStr(),
-1,
NULL,
0,
NULL,
NULL );
if ( cbAnsiPath == 0 )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
hr = strAnsiPath.Resize( cbAnsiPath );
if ( FAILED( hr ) )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
cbAnsiPath = WideCharToMultiByte( CP_ACP,
0,
struPath.QueryStr(),
-1,
strAnsiPath.QueryStr(),
cbAnsiPath,
NULL,
NULL );
if ( cbAnsiPath == 0 )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
return HRESULT_FROM_WIN32( dwError );
}
}
cbPathCopied = cbPath;
strAnsiPath.SetLen( cbAnsiPath - 1 );
hr = strAnsiPath.CopyToBuffer(
(LPSTR)szPath,
&cbPathCopied
);
if ( hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) &&
cbPath >= sizeof(CHAR) )
{
//
// Copy what we can.
//
// Note that there are ISAPI extensions that depend on
// being able to get a truncated path in the case where
// MAX_PATH is not sufficient to store the complete data.
//
// These extensions depend on using ERROR_INSUFFICIENT_BUFFER
// as a warning that the data is truncated.
//
memset( szPath, 0, cbPath );
memcpy( szPath, strAnsiPath.QueryStr(), cbPath - sizeof(CHAR) );
}
}
}
if ( pcchMatchingPath &&
!fUnicode )
{
//
// Use the ANSI matching value
//
*pcchMatchingPath = cchMatchingPathA;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::TransmitFile(
DWORD64 IsaContext,
DWORD_PTR hFile,
DWORD64 cbOffset,
DWORD64 cbWrite,
LPSTR szStatusCode,
BYTE * pHead,
DWORD cbHead,
BYTE * pTail,
DWORD cbTail,
DWORD dwFlags
)
/*++
Routine Description:
Transmits a file to the client
Arguments:
IsaContext - The ISAPI_CONTEXT for this request (opaque)
hFile - Handle to file (requires FILE_FLAG_SEQUENTIAL_SCAN)
cbOffset - Offset in file to begin transmitting
cbWrite - The number of bytes to transmit
szStatusCode - HTTP status to return (ie "200 OK")
pHead - Bytes to send before file data
cbHead - The size of pHead
pTail - Bytes to send after file data
cbTail - The size of pTail
dwFlags - HSE_IO_* flags from the caller
Return Value:
HRESULT
--*/
{
W3_ISAPI_HANDLER * pW3IsapiHandler;
W3_RESPONSE * pResponse;
DWORD dwW3Flags;
BOOL fSendAsResponse;
HRESULT hr;
//
// The only current caller for this function is w3isapi.dll, which does
// parameter validation from any outside code. So, we can get away with
// just asserting here.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_ASSERT( IsaContext );
DBG_ASSERT( cbHead == 0 || pHead != NULL );
DBG_ASSERT( cbTail == 0 || pTail != NULL );
DBG_ASSERT( (HANDLE)hFile != INVALID_HANDLE_VALUE );
DBG_ASSERT( dwFlags & HSE_IO_ASYNC );
DBG_REQUIRE( ( pResponse = _pW3Context->QueryResponse() ) != NULL );
DBG_REQUIRE( ( pW3IsapiHandler = (W3_ISAPI_HANDLER*)_pW3Context->QueryHandler() ) != NULL );
DBG_ASSERT( _IsapiContext == 0 );
_IsapiContext = IsaContext;
//
// If the caller is OOP, then make copies
// of the file handle, head data and tail data. We
// don't need to make a copy of the status because
// the BuildResponseFromIsapi function does that itself.
//
if ( _fIsOop )
{
if ( hFile != NULL )
{
hr = pW3IsapiHandler->DuplicateWamProcessHandleForLocalUse(
(HANDLE)hFile, &_hTfFile
);
if ( FAILED( hr ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ErrorExit;
}
hFile = (DWORD_PTR)_hTfFile;
}
if ( pHead )
{
_pTfHead = (LPBYTE)LocalAlloc( LPTR, cbHead );
if ( !_pTfHead )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto ErrorExit;
}
memcpy( _pTfHead, pHead, cbHead );
pHead = _pTfHead;
}
if ( pTail )
{
_pTfTail = (LPBYTE)LocalAlloc( LPTR, cbTail );
if ( !_pTfTail )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto ErrorExit;
}
memcpy( _pTfTail, pTail, cbTail );
pTail = _pTfTail;
}
}
//
// Convert the HSE_IO_* flags to W3_FLAG_* flags
//
// Init the flags for sending
dwW3Flags = W3_FLAG_ASYNC | W3_FLAG_MORE_DATA;
// If disconnect flag is not set, then we'll keep the connection open
if ( !( dwFlags & HSE_IO_DISCONNECT_AFTER_SEND ) )
{
_pW3Context->SetDisconnect( FALSE );
}
else
{
_pW3Context->SetDisconnect( TRUE );
}
//
// Clear any previous chunks from the response
//
pResponse->Clear();
//
// If HSE_IO_SEND_HEADERS is specified, then we're sending
// the initial part of the response (and pHead will be an
// LPSTR containing the headers that the caller wants to
// send), else we'll be sending this data completely as
// entity data.
//
fSendAsResponse = !!( dwFlags & HSE_IO_SEND_HEADERS );
if ( fSendAsResponse )
{
//
// Set the status using data from the caller
//
hr = pResponse->BuildResponseFromIsapi( _pW3Context,
szStatusCode,
(LPSTR)pHead,
cbHead );
if ( FAILED( hr ) )
{
goto ErrorExit;
}
//
// Is the status is access denied, then set the sub status to
// "Denied by Application"
//
if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode )
{
pResponse->SetStatus( HttpStatusUnauthorized,
Http401Application );
}
}
else
{
//
// Do something with pHead if provided
//
if ( cbHead )
{
hr = pResponse->AddMemoryChunkByReference(
pHead,
cbHead
);
if ( FAILED( hr ) )
{
goto ErrorExit;
}
}
}
//
// Now add the file handle to the response. Note that it's
// allowed for the caller to pass a NULL handle. In that case,
// we won't add it, and any present pHead and pTail will still
// get sent to the client.
//
if ( hFile )
{
hr = pResponse->AddFileHandleChunk(
(HANDLE)hFile,
cbOffset,
cbWrite
);
if ( FAILED( hr ) )
{
goto ErrorExit;
}
}
//
// Add the tail if provided
//
if ( cbTail )
{
hr = pResponse->AddMemoryChunkByReference(
pTail,
cbTail
);
if ( FAILED( hr ) )
{
goto ErrorExit;
}
}
//
// Ok, now that the stuff is all set up, send it, either
// as a response or as entity
//
if ( _fIsOop )
{
//
// This reference insures that if the OOP host crashes and
// COM releases all the OOP-held references, that this
// object will survive until the I/O completion occurs.
//
AddRef();
}
if ( fSendAsResponse )
{
hr = _pW3Context->SendResponse( dwW3Flags
| W3_FLAG_NO_ERROR_BODY
| W3_FLAG_NO_CONTENT_LENGTH
| W3_FLAG_MORE_DATA );
}
else
{
hr = _pW3Context->SendEntity( dwW3Flags
| W3_FLAG_MORE_DATA );
}
if ( FAILED( hr ) )
{
//
// If SendEntity or SendResponse fail, consider this
// a non recoverable error and set the error to be
// WSAECONNRESET for compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
if ( _fIsOop )
{
//
// Release the above reference, since no I/O completion will
// occur.
//
Release();
}
goto ErrorExit;
}
return hr;
ErrorExit:
DBG_ASSERT( FAILED( hr ) );
if ( _IsapiContext != 0 )
{
_IsapiContext = 0;
}
if ( _fIsOop )
{
if ( _hTfFile != NULL && _hTfFile != INVALID_HANDLE_VALUE )
{
CloseHandle( _hTfFile );
_hTfFile = INVALID_HANDLE_VALUE;
}
if ( _pTfHead )
{
LocalFree( _pTfHead );
_pTfHead = NULL;
}
if ( _pTfTail )
{
LocalFree( _pTfTail );
_pTfTail = NULL;
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::SetConnectionClose(
BOOL fClose
)
/*++
Routine Description:
Sets the W3_CONTEXT to close (or not) connection
upon completion of the response.
Arguments:
fClose - BOOL to pass to SetDisconnect
Return Value:
HRESULT
--*/
{
DBG_ASSERT( CheckSignature() );
_pW3Context->SetDisconnect( fClose );
return NOERROR;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::SendRedirect(
LPCSTR szLocation,
BOOL fDisconnect
)
/*++
Routine Description:
Sends a 302 redirect message to the client
Arguments:
szLocation - The URL to redirect to.
fDisconnect - If TRUE, the close connection
Return Value:
HRESULT
--*/
{
STACK_STRA( strLocation, MAX_PATH );
HTTP_STATUS httpStatus = { 302, REASON("Object Moved") };
HRESULT hr;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_ASSERT( szLocation );
//
// Use W3_CONTEXT::SetupHttpRedirect to build the redirect
// response.
//
hr = strLocation.Copy( szLocation );
if ( FAILED( hr ) )
{
return hr;
}
_pW3Context->SetDisconnect( fDisconnect );
hr = _pW3Context->SetupHttpRedirect(
strLocation,
FALSE, // Don't include the original query string
httpStatus
);
if ( FAILED( hr ) )
{
return hr;
}
//
// Now send the response synchronously (the calling ISAPI might
// do something silly like send more data after this function
// returns, so we can't do it asynchronously.)
//
hr = _pW3Context->SendResponse(
W3_FLAG_SYNC |
W3_FLAG_MORE_DATA
);
if ( FAILED( hr ) )
{
//
// If SendResponse fails, consider this a non recoverable
// error and set the error to be WSAECONNRESET for
// compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::GetCertificateInfoEx(
DWORD cbAllocated,
DWORD * pdwCertEncodingType,
BYTE * pbCertEncoded,
DWORD * pcbCertEncoded,
DWORD * pdwCertificateFlags
)
/*++
Routine Description:
Gets certificate info
Arguments:
cbAllocated - The size of the pbCertEncoded buffer
pdwCertEncodingType - Upon return, the cert encoding type
pbCertEncoded - Upon return, contains the cert info
pcbCertEncoded - Upon successful return, the number of bytes
in pbCertEncoded. On failed return, the number
of bytes required to contain pbCertEncoded
pdwCertificateFlags - Upon return, the certificate flags
Return Value:
HRESULT
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
return _pW3Context->GetCertificateInfoEx( cbAllocated,
pdwCertEncodingType,
pbCertEncoded,
pcbCertEncoded,
pdwCertificateFlags );
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::AppendLog(
LPSTR szExtraParam,
USHORT StatusCode
)
/*++
Routine Description:
Append the string to the querystring logged
Arguments:
szExtraParam - the string to be appended
Return Value:
HRESULT
--*/
{
HRESULT hr;
//
// The only current caller for this function is w3isapi.dll,
// which validates the parameters.
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
if (StatusCode != 0)
{
_pW3Context->QueryResponse()->SetStatusCode(StatusCode);
}
if (szExtraParam[0] != '\0')
{
STRA &strLogParam = _pW3Context->QueryMainContext()
->QueryLogContext()->m_strLogParam;
if (strLogParam.IsEmpty())
{
STACK_STRU (strQueryString, 128);
if (FAILED(hr = _pW3Context->QueryRequest()->GetQueryStringA(&strLogParam)))
{
return hr;
}
}
return strLogParam.Append(szExtraParam);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::ExecuteUrl(
DWORD64 IsaContext,
EXEC_URL_INFO * pExecUrlInfo
)
/*++
Routine Description:
Execute a child request
Arguments:
ISaContext - The ISAPI_CONTEXT for this request (opaque)
pExecUrlInfo - Description of request to execute
Return Value:
HRESULT
--*/
{
W3_ISAPI_HANDLER * pIsapiHandler = NULL;
HRESULT hr = NO_ERROR;
BOOL fAsync;
BYTE * pbOriginalEntity = NULL;
//
// The parameters (i.e. HSE_EXEC_URL_INFO structure) was validated on
// the W3ISAPI.DLL side, so we can make assumptions about validity
//
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( pExecUrlInfo != NULL );
DBG_ASSERT( _pW3Context );
DBG_REQUIRE( ( pIsapiHandler = (W3_ISAPI_HANDLER*)_pW3Context->QueryHandler() ) != NULL );
fAsync = TRUE;
DBG_ASSERT( fAsync == !!IsaContext );
if ( fAsync )
{
DBG_ASSERT( _IsapiContext == 0 );
_IsapiContext = IsaContext;
}
//
// If we're OOP, we need to dup the user token (if there) and the
// entity body (if there and this is async request)
//
if ( _fIsOop )
{
//
// Duplicate the user token if there
//
if ( pExecUrlInfo->pUserInfo != NULL &&
pExecUrlInfo->pUserInfo->hImpersonationToken != NULL )
{
DBG_ASSERT( _hExecUrlToken == NULL );
hr = pIsapiHandler->DuplicateWamProcessHandleForLocalUse(
(HANDLE) pExecUrlInfo->pUserInfo->hImpersonationToken,
&_hExecUrlToken );
if ( FAILED( hr ) )
{
goto Finished;
}
pExecUrlInfo->pUserInfo->hImpersonationToken = reinterpret_cast<DWORD_PTR> (_hExecUrlToken);
}
//
// Duplicate the entity buffer if there and this is async request
//
if ( fAsync &&
pExecUrlInfo->pEntity != NULL &&
pExecUrlInfo->pEntity->lpbData != NULL &&
pExecUrlInfo->pEntity->cbAvailable > 0 )
{
DBG_ASSERT( _pbExecUrlEntity == NULL );
_pbExecUrlEntity = LocalAlloc( LMEM_FIXED,
pExecUrlInfo->pEntity->cbAvailable );
if ( _pbExecUrlEntity == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Finished;
}
memcpy( _pbExecUrlEntity,
pExecUrlInfo->pEntity->lpbData,
pExecUrlInfo->pEntity->cbAvailable );
//
// Remember the original pointer to entity body. We must
// maintain it on exit so that RPC unmarshalls the correct
// buffer :-(
//
pbOriginalEntity = pExecUrlInfo->pEntity->lpbData;
pExecUrlInfo->pEntity->lpbData = (LPBYTE) _pbExecUrlEntity;
}
}
//
// Execute the darn thing
//
if ( _fIsOop && fAsync )
{
//
// This reference insures that if the OOP host crashes and
// COM releases all the OOP-held references, that this
// object will survive until the I/O completion occurs.
//
AddRef();
}
hr = _pW3Context->CleanIsapiExecuteUrl( pExecUrlInfo );
if ( FAILED( hr ) )
{
if ( _fIsOop && fAsync )
{
//
// Release the above reference, since no I/O completion will
// occur.
//
Release();
}
}
//
// If nothing is pending, then we can clean up any dup'd stuff now
//
Finished:
if ( FAILED( hr ) &&
fAsync &&
_IsapiContext != 0 )
{
_IsapiContext = 0;
}
if ( FAILED( hr ) || !fAsync )
{
if ( _pbExecUrlEntity != NULL )
{
LocalFree( _pbExecUrlEntity );
_pbExecUrlEntity = NULL;
}
if ( _hExecUrlToken != NULL )
{
CloseHandle( _hExecUrlToken );
_hExecUrlToken = NULL;
}
}
//
// Regardless of return status, we need to restore the entity pointer
// if needed so RPC unmarshalls the right thing
//
if ( pbOriginalEntity != NULL )
{
DBG_ASSERT( pExecUrlInfo != NULL );
DBG_ASSERT( pExecUrlInfo->pEntity != NULL );
pExecUrlInfo->pEntity->lpbData = pbOriginalEntity;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::SendCustomError(
DWORD64 IsaContext,
CHAR * pszStatus,
USHORT uHttpSubError
)
/*++
Routine Description:
Send a custom error (if available, otherwise error out with
ERROR_FILE_NOT_FOUND)
Arguments:
IsaContext - The ISAPI_CONTEXT for this request (opaque)
pszStatus - Status line
uHttpSubError - Sub error
Return Value:
HRESULT
--*/
{
HRESULT hr;
BOOL fAsync;
HSE_CUSTOM_ERROR_INFO customErrorInfo;
DBG_ASSERT( CheckSignature() );
//
// If we have a non-NULL IsaContext, then this is an async request
//
fAsync = !!IsaContext;
if ( fAsync )
{
DBG_ASSERT( _IsapiContext == 0 );
_IsapiContext = IsaContext;
}
if ( _fIsOop && fAsync )
{
//
// This reference insures that if the OOP host crashes and
// COM releases all the OOP-held references, that this
// object will survive until the I/O completion occurs.
//
AddRef();
}
customErrorInfo.pszStatus = pszStatus;
customErrorInfo.uHttpSubError = uHttpSubError;
customErrorInfo.fAsync = fAsync;
hr = _pW3Context->CleanIsapiSendCustomError( &customErrorInfo );
if ( FAILED( hr ) )
{
if ( fAsync && _IsapiContext != 0 )
{
_IsapiContext = 0;
}
if ( _fIsOop && fAsync )
{
//
// Release the above reference, since no I/O completion
// will ever happen.
//
Release();
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::GetExecuteUrlStatus(
USHORT * pChildStatusCode,
USHORT * pChildSubErrorCode,
DWORD * pChildWin32Error
)
/*++
Routine Description:
Get the status of the last child execute
Arguments:
pChildStatusCode - Filled with status code of child execute
pChildSubErrorCode - Filled sub error if applicable
pChildWin32Error - Filled last Win32 saved for child request
Return Value:
HRESULT
--*/
{
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context != NULL );
_pW3Context->QueryChildStatusAndError( pChildStatusCode,
pChildSubErrorCode,
pChildWin32Error );
return NO_ERROR;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::VectorSend(
DWORD64 IsaContext,
BOOL fDisconnect,
LPSTR pszStatus,
LPSTR pszHeaders,
VECTOR_ELEMENT *pElements,
DWORD nElementCount,
BOOL fFinalSend,
BOOL fCacheResponse
)
/*++
Routine description
Do a vector send of multiple file handle/memory chunks
Parameters
IsaContext - The ISAPI_CONTEXT for this request (opaque)
fDisconnect - Do we disconnect after send
pszStatus - The status to be sent if any
pszHeaders - The headers to be sent if any
pElements - The file handle/memory chunks to be sent
nElementCount - The number of these chunks
fFinalSend - Should we tell http.sys on behalf of the ISAPI that this
is the final send for this response
fCacheResponse - Should we ask http.sys to cache this response
Return value
HRESULT
--*/
{
W3_ISAPI_HANDLER * pW3IsapiHandler;
HRESULT hr = S_OK;
DWORD cchHeaders = 0;
W3_RESPONSE * pResponse;
BOOL fAsync;
BOOL fFragmentCacheUsed = FALSE;
WCHAR * pszFullFragmentName;
DWORD i;
BOOL fSwitchToParsed = FALSE;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_REQUIRE( ( pResponse = _pW3Context->QueryResponse() ) != NULL );
DBG_REQUIRE( ( pW3IsapiHandler = (W3_ISAPI_HANDLER*)_pW3Context->QueryHandler() ) != NULL );
//
// A non-NULL context indicates async request
//
fAsync = !!IsaContext;
if ( fAsync )
{
DBG_ASSERT( _IsapiContext == 0 );
_IsapiContext = IsaContext;
}
if (pszHeaders != NULL)
{
cchHeaders = (DWORD)strlen(pszHeaders);
}
if ( _fIsOop )
{
//
// Need to make copies of the file handles and maybe even memory buffer
//
if ( fAsync && pszHeaders )
{
_pTfHead = (LPBYTE)LocalAlloc(LMEM_FIXED, cchHeaders + 1 );
if ( _pTfHead == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Exit;
}
memcpy( _pTfHead, pszHeaders, cchHeaders + 1 );
pszHeaders = (LPSTR)_pTfHead;
}
if (!_bufVectorElements.Resize(nElementCount * sizeof(VECTOR_ELEMENT)))
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Exit;
}
ZeroMemory(_bufVectorElements.QueryPtr(),
nElementCount * sizeof(VECTOR_ELEMENT));
VECTOR_ELEMENT *pNewElements = (VECTOR_ELEMENT *)_bufVectorElements.QueryPtr();
for (i=0; i<nElementCount; i++)
{
if (pElements[i].hFile)
{
//
// File handle chunk
//
hr = pW3IsapiHandler->DuplicateWamProcessHandleForLocalUse(
(HANDLE)pElements[i].hFile,
(HANDLE *)&pNewElements[i].hFile);
if (FAILED(hr))
{
goto Exit;
}
pNewElements[i].cbOffset = pElements[i].cbOffset;
pNewElements[i].cbFileSize = pElements[i].cbFileSize;
}
else if (pElements[i].pBuffer)
{
//
// Memory buffer chunk
//
if (fAsync)
{
//
// Need to copy the buffer too
//
pNewElements[i].pBuffer = (BYTE *)LocalAlloc(LMEM_FIXED, pElements[i].cbBufSize);
if (pNewElements[i].pBuffer == NULL)
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Exit;
}
memcpy(pNewElements[i].pBuffer,
pElements[i].pBuffer,
pElements[i].cbBufSize);
}
else
{
pNewElements[i].pBuffer = pElements[i].pBuffer;
}
pNewElements[i].cbBufSize = pElements[i].cbBufSize;
}
_nElementCount++;
}
pElements = pNewElements;
}
//
// First Clear any goo left from a previous Send
//
pResponse->Clear();
//
// We must always obey the fDisconnect flag
// assuming the SSFVectorSend always correctly sets it.
//
_pW3Context->SetDisconnect(fDisconnect);
//
// Now do the real work
//
if (pszStatus != NULL)
{
hr = pResponse->BuildResponseFromIsapi( _pW3Context,
pszStatus,
pszHeaders,
cchHeaders );
if (FAILED(hr))
{
goto Exit;
}
//
// Is the status is access denied, then set the sub status to
// "Denied by Application"
//
if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode )
{
pResponse->SetStatus( HttpStatusUnauthorized,
Http401Application );
}
}
for (i=0; i<nElementCount; i++)
{
if (pElements[i].hFile)
{
hr = pResponse->AddFileHandleChunk((HANDLE)pElements[i].hFile,
pElements[i].cbOffset,
pElements[i].cbFileSize);
}
else if (pElements[i].pBuffer)
{
hr = pResponse->AddMemoryChunkByReference(pElements[i].pBuffer,
pElements[i].cbBufSize);
}
else
{
if (FAILED(hr = _pW3Context->QueryHeaderBuffer()->AllocateSpace(
pElements[i].pszFragmentName,
(DWORD)wcslen(pElements[i].pszFragmentName),
&pszFullFragmentName)))
{
goto Exit;
}
hr = pResponse->AddFragmentChunk(pszFullFragmentName,
(USHORT)wcslen(pszFullFragmentName) * sizeof(WCHAR));
fFragmentCacheUsed = TRUE;
}
if (FAILED(hr))
{
goto Exit;
}
}
if (fFragmentCacheUsed)
{
//
// Cannot used fragment-cache if using either compression or send-raw data filters
//
W3_METADATA *pMetaData = _pW3Context->QueryUrlContext()->QueryMetaData();
if ( !_pW3Context->QueryDoneWithCompression() &&
pMetaData->QueryDoDynamicCompression() )
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto Exit;
}
if ( _pW3Context->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA ) )
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto Exit;
}
//
// We also cannot generate content-length in this case
//
_pW3Context->QueryMainContext()->SetShouldGenerateContentLength( FALSE );
}
if ( _fIsOop && fAsync )
{
//
// This reference insures that if the OOP host crashes and
// COM releases all the OOP-held references, that this
// object will survive until the I/O completion occurs.
//
AddRef();
}
if (pszStatus != NULL)
{
//
// If the response is to be cached, then we'll need to switch to
// parsed mode.
//
if (fCacheResponse)
{
_fCacheResponse = TRUE;
fSwitchToParsed = TRUE;
}
//
// If the ISAPI is doing a keep-alive single-send, is
// referencing a fragment (1+), and did not specify a
// content-length header themselves, then switch to
// parsed so that HTTP.SYS can do the work for us
//
if (fFragmentCacheUsed &&
!fSwitchToParsed &&
fFinalSend &&
!fDisconnect &&
pszHeaders != NULL &&
strstr( pszHeaders, "Content-Length: " ) == NULL &&
strstr( pszHeaders, "Transfer-Encoding: chunked" ) == NULL)
{
fSwitchToParsed = TRUE;
}
//
// Do the switch
//
if (fSwitchToParsed)
{
hr = _pW3Context->QueryResponse()->SwitchToParsedMode();
if (FAILED(hr))
{
Release();
goto Exit;
}
}
hr = _pW3Context->SendResponse( (fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC)
| (fFinalSend ? 0 : W3_FLAG_MORE_DATA)
| W3_FLAG_NO_ERROR_BODY
| W3_FLAG_NO_CONTENT_LENGTH );
}
else
{
hr = _pW3Context->SendEntity( (fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC)
| (fFinalSend ? 0 : W3_FLAG_MORE_DATA) );
}
if (FAILED(hr))
{
//
// If SendEntity or SendResponse fail, consider this
// a non recoverable error and set the error to be
// WSAECONNRESET for compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
if ( _fIsOop && fAsync )
{
//
// Release the above reference, since no I/O completion will
// occur.
//
Release();
}
}
Exit:
if ( FAILED( hr ) && fAsync && _IsapiContext != 0 )
{
_IsapiContext = 0;
}
if ( _fIsOop &&
(FAILED(hr) || !fAsync) )
{
//
// Need to destroy handles/memory buffers we copied
//
if ( _pTfHead )
{
LocalFree( _pTfHead );
_pTfHead = NULL;
}
for (i=0; i<_nElementCount; i++)
{
if (pElements[i].hFile)
{
CloseHandle((HANDLE)pElements[i].hFile);
}
else if (pElements[i].pBuffer && fAsync)
{
LocalFree(pElements[i].pBuffer);
}
}
_nElementCount = 0;
}
return hr;
}
HRESULT
ISAPI_REQUEST::GetCustomError(
DWORD dwError,
DWORD dwSubError,
DWORD dwBufferSize,
BYTE *pvBuffer,
DWORD *pdwRequiredBufferSize,
BOOL *pfIsFileError,
BOOL *pfSendErrorBody)
/*++
Routine Description:
Finds the CustomError for this error and subError. The results are returned
in pvBuffer provided there is enough buffer space. The amount of buffer space
required is returned in pdwRequestBufferSize regardless.
Arguments:
dwError - major error (e.g. 500)
dwSubError - sub error (e.g. 13)
dwBufferSize - size, in bytes, of buffer at pvBuffer
pvBuffer - pointer to buffer for result
pdwRequiredBufferSize - amount of buffer used/need
pfIsFileError - return boolean if custom error is a filename
pfSendErrorBody - upon return, TRUE if error body should be sent
Return Value:
HRESULT
--*/
{
HRESULT hr = NOERROR;
W3_METADATA *pMetadata = NULL;
LPSTR pMimeStr = "text/html";
STACK_STRA(mimeStr, 64);
STACK_STRU( strError, 64 );
DBG_ASSERT( CheckSignature() );
// first dig out the W3 Metadata pointer
pMetadata = _pW3Context->QueryUrlContext()->QueryMetaData();
DBG_ASSERT( pMetadata != NULL );
// get the custom error for this error code
hr = pMetadata->FindCustomError( (USHORT)dwError,
(USHORT)dwSubError,
pfIsFileError,
&strError );
// if successful, and the custom error is a file, we need to
// get the file's mime type
if (SUCCEEDED(hr) && *pfIsFileError) {
// lookup the MIME_ENTRY for this file.
if (SUCCEEDED(SelectMimeMappingForFileExt(strError.QueryStr(),pMetadata->QueryMimeMap(), &mimeStr))) {
pMimeStr = mimeStr.QueryStr();
}
}
// if found, convert the UNICODE string to ANSI
if (SUCCEEDED(hr)) {
int ret;
ret = WideCharToMultiByte(CP_ACP,
0,
strError.QueryStr(),
-1,
(LPSTR)pvBuffer,
dwBufferSize,
NULL,
NULL);
*pdwRequiredBufferSize = ret;
// check return. If zero, then the conversion failed.
// GetLastError() contains the error.
if (ret == 0) {
DWORD winError = GetLastError();
// if InsufBuff, then call again to get the required size
if (winError == ERROR_INSUFFICIENT_BUFFER) {
*pdwRequiredBufferSize = WideCharToMultiByte(CP_ACP,
0,
strError.QueryStr(),
-1,
NULL,
0,
NULL,
NULL);
// if the error is a filename, then include in the required
// buffer size the length of the mime string
if (*pfIsFileError) {
*pdwRequiredBufferSize += (DWORD)strlen(pMimeStr) + 1;
}
}
// in any case, make a HRESULT from the win32 error and return that
hr = HRESULT_FROM_WIN32(winError);
}
// if we continue to be successful, the next step is to put
// the mime string after the null byte of the file name
if (SUCCEEDED(hr) && *pfIsFileError) {
int fileLen = (int)strlen((char *)pvBuffer);
// make sure we have enough buffer
if ((fileLen + strlen(pMimeStr) + 2) > dwBufferSize) {
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
else {
// looks like we do. Copy the mime string in
strcpy(&((char *)pvBuffer)[fileLen+1],pMimeStr);
pdwRequiredBufferSize += strlen(pMimeStr) + 1;
}
}
}
if ( pfSendErrorBody != NULL )
{
*pfSendErrorBody = _pW3Context->QuerySendErrorBody();
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::TestConnection(
BOOL *pfIsConnected
)
/*++
Routine Description:
returns state of the connection (TRUE = opened, FALSE = closed)
Arguments:
pfIsConnected - sets to TRUE if connection is still open,
FALSE if it was closed already
Return Value:
HRESULT
--*/
{
HRESULT hr = NOERROR;
W3_CONNECTION * pConnection = NULL;
W3_MAIN_CONTEXT * pMainContext = NULL;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context != NULL );
pMainContext = _pW3Context->QueryMainContext();
DBG_ASSERT( pMainContext != NULL );
pConnection = pMainContext->QueryConnection( TRUE );
if ( pConnection == NULL )
{
//
// Issue 02/08/2001 jaroslad:
// QueryConnection currently doesn't have a way to return
// error that occured. For now assume that out of memory
// occured
//
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
//
// we will not touch pfIsConnected in the case of error
// it is caller's responsibility to check if this call
// succeeded before using pfIsConnected
//
}
else
{
*pfIsConnected = pConnection->QueryConnected();
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::GetSspiInfo(
BYTE *pCredHandle,
DWORD cbCredHandle,
BYTE *pCtxtHandle,
DWORD cbCtxtHandle)
/*++
Routine Description:
Returns SSPI info about the request
Arguments:
pCredHandle - Upon return, contains the credential handle
cbCredHandle - The size of pCredHandle
pCtxtHandle - Upon return, contains the context handle
cbCtxtHandle - The size of pCtxtHandle
Return Value:
HRESULT
--*/
{
W3_USER_CONTEXT * pUserContext;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context != NULL );
if ( _fIsOop )
{
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
}
pUserContext = _pW3Context->QueryUserContext();
DBG_ASSERT( pUserContext != NULL );
return pUserContext->GetSspiInfo( pCredHandle,
cbCredHandle,
pCtxtHandle,
cbCtxtHandle );
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::QueryToken(
BYTE *szUrl,
DWORD,
DWORD dwTokenType,
DWORD64 *pToken,
BOOL fUnicode)
/*++
Routine Description:
Returns the VR token for the request
Arguments:
szUrl - The URL for which we need to get the token
cbUrl - The size of szUrl
dwTokenType - Either TOKEN_VR_TOKEN or TOKEN_ANONYMOUS_TOKEN
pToken - Upon return, points to the resulting token
fUnicode - If TRUE, szUrl is UNICODE, else it's ANSI
Return Value:
HRESULT
--*/
{
STACK_STRU( struUrl,MAX_PATH );
W3_URL_INFO * pUrlInfo = NULL;
W3_METADATA * pMetaData = NULL;
TOKEN_CACHE_ENTRY * pTokenEntry = NULL;
HANDLE hToken = NULL;
HANDLE hTokenLocalDuplicate = NULL;
HRESULT hr;
BOOL fSuccess;
DBG_ASSERT( _pW3Context );
DBG_ASSERT( szUrl );
DBG_ASSERT( pToken );
DBG_ASSERT( dwTokenType == TOKEN_VR_TOKEN ||
dwTokenType == TOKEN_ANONYMOUS_TOKEN );
//
// Get the metadata for the specified URL
//
if ( fUnicode )
{
hr = struUrl.Copy( (LPWSTR)szUrl );
}
else
{
hr = struUrl.CopyA( (LPSTR)szUrl );
}
if ( FAILED( hr ) )
{
return hr;
}
DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL );
hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo(
_pW3Context,
struUrl,
&pUrlInfo );
if ( FAILED( hr ) )
{
return hr;
}
DBG_ASSERT( pUrlInfo != NULL );
pMetaData = pUrlInfo->QueryMetaData();
DBG_ASSERT( pMetaData != NULL );
//
// Assume that the VR token is NULL, unless we can determine otherwise
//
*pToken = NULL;
//
// Ok, so now let's get the token
//
if ( dwTokenType == TOKEN_VR_TOKEN )
{
hr = pMetaData->GetAndRefVrAccessToken( &pTokenEntry );
if( FAILED( hr ) )
{
return hr;
}
}
else if ( dwTokenType == TOKEN_ANONYMOUS_TOKEN )
{
hr = pMetaData->GetAndRefAnonymousToken( &pTokenEntry );
if( FAILED( hr ) )
{
return hr;
}
}
else
{
DBG_ASSERT( FALSE );
}
if ( pTokenEntry != NULL )
{
hToken = pTokenEntry->QueryImpersonationToken();
}
if ( hToken )
{
//
// Need to duplicate the handle. This is really only necessary for
// OOP requests, but we need to do it for both, so that the extension
// doesn't need to worry about knowing if it's inproc or not before
// deciding if it needs to close the handle.
//
fSuccess = DupTokenWithSameImpersonationLevel(
hToken,
MAXIMUM_ALLOWED,
TokenPrimary,
&hTokenLocalDuplicate
);
if( fSuccess )
{
if( _fIsOop )
{
HANDLE hTokenRemote = NULL;
fSuccess = DuplicateHandle(
GetCurrentProcess(),
hTokenLocalDuplicate,
_pWamProcess->QueryProcess(),
&hTokenRemote,
0,
FALSE,
DUPLICATE_SAME_ACCESS
);
CloseHandle(hTokenLocalDuplicate);
hTokenLocalDuplicate = NULL;
*pToken = (DWORD64)hTokenRemote;
}
else
{
*pToken = reinterpret_cast<DWORD64>(hTokenLocalDuplicate);
}
}
}
pUrlInfo->DereferenceCacheEntry();
if ( pTokenEntry != NULL )
{
pTokenEntry->DereferenceCacheEntry();
pTokenEntry = NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::ReportAsUnhealthy(
BYTE *szImage,
DWORD,
BYTE *szReason,
DWORD cbReason)
/*++
Routine Description:
Handles an ISAPI reporting itself as unhealthy
Arguments:
szImage - A UNICODE string with the ISAPI image name
cbImage - The size of szImage
szReason - A UNICODE string from the ISAPI indicating the problem
cbReason - The size of szReason
Return Value:
HRESULT
--*/
{
W3_ISAPI_HANDLER * pW3IsapiHandler;
const WCHAR * pszEventLog[2];
DBG_ASSERT( _pW3Context );
DBG_ASSERT( szImage );
DBG_ASSERT( cbReason == 0 || szReason != NULL );
DBG_REQUIRE( ( pW3IsapiHandler = (W3_ISAPI_HANDLER*)_pW3Context->QueryHandler() ) != NULL );
//
// First, log the event. After we set the unhealthy flag,
// this process could get terminated at any time.
//
// There are two possible messages to log, depending on
// whether the extension gave us a reason for being
// unhealthy.
//
pszEventLog[0] = reinterpret_cast<WCHAR*>( szImage );
if ( cbReason > 0 )
{
pszEventLog[1] = reinterpret_cast<WCHAR*>( szReason );
g_pW3Server->LogEvent(
W3_EVENT_UNHEALTHY_ISAPI,
2,
pszEventLog,
0
);
}
else
{
g_pW3Server->LogEvent(
W3_EVENT_UNHEALTHY_ISAPI_NO_REASON,
1,
pszEventLog,
0
);
}
//
// Now tell someone that we feel sick
//
UlAtqSetUnhealthy();
return NO_ERROR;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::AddFragmentToCache(
VECTOR_ELEMENT * pVectorElement,
WCHAR * pszFragmentName
)
/*++
Routine Description:
Add the fragment to cache
Arguments:
pVectorElement - The fragment to be added
pszFragmentName - name of the fragment
Return Value:
HRESULT
--*/
{
HTTP_DATA_CHUNK DataChunk;
ZeroMemory(&DataChunk, sizeof DataChunk);
if (pVectorElement->hFile)
{
DataChunk.DataChunkType = HttpDataChunkFromFileHandle;
DataChunk.FromFileHandle.FileHandle = (HANDLE)pVectorElement->hFile;
DataChunk.FromFileHandle.ByteRange.StartingOffset.QuadPart = pVectorElement->cbOffset;
DataChunk.FromFileHandle.ByteRange.Length.QuadPart = pVectorElement->cbFileSize;
}
else if (pVectorElement->pBuffer)
{
DataChunk.DataChunkType = HttpDataChunkFromMemory;
DataChunk.FromMemory.pBuffer = pVectorElement->pBuffer;
DataChunk.FromMemory.BufferLength = pVectorElement->cbBufSize;
}
return UlAtqAddFragmentToCache(&DataChunk,
pszFragmentName);
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::ReadFragmentFromCache(
WCHAR * pszFragmentName,
BYTE * pvBuffer,
DWORD cbSize,
DWORD * pcbCopied
)
/*++
Routine Description:
Read the fragment from cache
Arguments:
pszFragmentName - name of the fragment
pvBuffer - the buffer to read in
cbSize - the size of the buffer
pcbCopied - the amount copied in on return
Return Value:
HRESULT
--*/
{
return UlAtqReadFragmentFromCache(pszFragmentName,
pvBuffer,
cbSize,
pcbCopied);
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::RemoveFragmentFromCache(
WCHAR * pszFragmentName
)
/*++
Routine Description:
Remove the fragment from cache
Arguments:
pszFragmentName - name of the fragment
Return Value:
HRESULT
--*/
{
return UlAtqRemoveFragmentFromCache(pszFragmentName);
}
HRESULT
ISAPI_REQUEST::PreprocessIoCompletion(
DWORD cbIo
)
/*++
Routine Description:
Handles cleanup for any functions that use fCopiedData=TRUE.
In the case of a TransmitFile or WriteClient, this just
involves closing handles and freeing buffers. In the case
of ReadClient, we need to push the read buffer to the OOP
process.
Arguments:
cbIo - The number of bytes in a read buffer, if present
Return Value:
HRESULT
--*/
{
W3_ISAPI_HANDLER * pW3IsapiHandler;
HRESULT hr = NOERROR;
DWORD i;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_REQUIRE( ( pW3IsapiHandler = (W3_ISAPI_HANDLER*)_pW3Context->QueryHandler() ) != NULL );
//
// Cleanup any existing TF info, since we're done with it.
//
if ( _hTfFile != NULL && _hTfFile != INVALID_HANDLE_VALUE )
{
CloseHandle( _hTfFile );
_hTfFile = INVALID_HANDLE_VALUE;
}
if ( _pTfHead )
{
LocalFree( _pTfHead );
_pTfHead = NULL;
}
if ( _pTfTail )
{
LocalFree( _pTfTail );
_pTfTail = NULL;
}
//
// Cleanup any existing async write buffer
//
if ( _pAsyncWriteBuffer )
{
LocalFree( _pAsyncWriteBuffer );
_pAsyncWriteBuffer = NULL;
}
//
// If we have an async read buffer, then push the data
// to the WAM process and free it
//
if ( _pAsyncReadBuffer )
{
hr = pW3IsapiHandler->MarshalAsyncReadBuffer(
_IsapiContext,
_pAsyncReadBuffer,
cbIo
);
//
// Note that the above function could fail if, for
// example, the dllhost has crashed. There's not
// anything we can do about it here, though. We'll
// ignore it.
//
LocalFree( _pAsyncReadBuffer );
_pAsyncReadBuffer = NULL;
}
//
// Clean up HSE_EXEC_URL stuff
//
if ( _pbExecUrlEntity != NULL )
{
LocalFree( _pbExecUrlEntity );
_pbExecUrlEntity = NULL;
}
if ( _hExecUrlToken != NULL )
{
CloseHandle( _hExecUrlToken );
_hExecUrlToken = NULL;
}
//
// Need to destroy handles/memory buffers we copied for vector send
//
VECTOR_ELEMENT *pElements = (VECTOR_ELEMENT *)_bufVectorElements.QueryPtr();
for (i=0; i<_nElementCount; i++)
{
if (pElements[i].hFile)
{
CloseHandle((HANDLE)pElements[i].hFile);
}
else if (pElements[i].pBuffer)
{
LocalFree(pElements[i].pBuffer);
}
}
_nElementCount = 0;
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::GetMetadataProperty(
DWORD dwPropertyId,
BYTE * pbBuffer,
DWORD cbBuffer,
DWORD * pcbBufferRequired
)
/*++
Routine Description:
Retrieves a metabase property
Arguments:
dwPropertyId - MD_ property ID (must be of type UT_FILE)
pbBuffer - Buffer to receive serialized data
cbBuffer - Size of buffer
pcbBufferRequired - Filled with size of buffer required
Return Value:
HRESULT
--*/
{
URL_CONTEXT * pUrlContext;
W3_METADATA * pMetadata;
DBG_ASSERT( _pW3Context );
pUrlContext = _pW3Context->QueryUrlContext();
DBG_ASSERT( pUrlContext != NULL );
pMetadata = pUrlContext->QueryMetaData();
DBG_ASSERT( pMetadata != NULL );
return pMetadata->GetMetadataProperty( _pW3Context,
dwPropertyId,
pbBuffer,
cbBuffer,
pcbBufferRequired );
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::GetCacheInvalidationCallback(
DWORD64 *pfnCallback)
{
//
// BUGBUG: CODEWORK: will need another mechanism if we want to implement
// this for OOP
//
if (_fIsOop)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
*(PFN_HSE_CACHE_INVALIDATION_CALLBACK *)pfnCallback = UlAtqFlushUlCache;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::CloseConnection(
VOID
)
{
W3_RESPONSE * pResponse;
DWORD cbSent = NULL;
HRESULT hr;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
DBG_REQUIRE( ( pResponse = _pW3Context->QueryResponse() ) != NULL );
//
// Tell http.sys to close the connection. This send should not
// cause http.sys to clean up the request - and logging data
// sent later (when the ISAPI is done) should be correctly
// handled.
//
hr = SendEntityBodyAndLogDataHelper( _pW3Context,
_pW3Context->QueryUlatqContext(),
FALSE,
HTTP_SEND_RESPONSE_FLAG_DISCONNECT,
0,
NULL,
&cbSent );
if ( FAILED( hr ) )
{
//
// If SendEntity fails, consider this a non recoverable
// error and set the error to be WSAECONNRESET for
// compatibility with legacy ISAPI code.
//
hr = HRESULT_FROM_WIN32( WSAECONNRESET );
}
return hr;
}
HRESULT STDMETHODCALLTYPE
ISAPI_REQUEST::AllocateMemory(
DWORD cbSize,
DWORD64 * ppvBuffer
)
/*++
Routine Description:
Allocate some per-request memory
Arguments:
cbSize - Size to allocate
ppvBuffer - Filled with buffer
Return Value:
HRESULT
--*/
{
DWORD64 dwBuffer;
VOID * pvBuffer;
DBG_ASSERT( CheckSignature() );
DBG_ASSERT( _pW3Context );
pvBuffer = _pW3Context->ContextAlloc( cbSize );
if ( pvBuffer == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
*ppvBuffer = (DWORD64) pvBuffer;
return S_OK;
}
ISAPI_REQUEST::~ISAPI_REQUEST()
/*++
Routine Description:
Destructor
Arguments:
None
Return Value:
None
--*/
{
DBG_ASSERT( CheckSignature() );
//
// Release the free threaded marshaler
//
if ( _pUnkFTM )
{
_pUnkFTM->Release();
_pUnkFTM = NULL;
}
//
// Dissociate ourselves from the WAM_PROCESS, if present
//
if ( _pWamProcess )
{
if (_pWamProcess->QueryCrashed())
{
AppendLog(SZ_FAILED_OOP_REQUEST_LOG_MESSAGE,
500);
}
_pWamProcess->DecrementRequestCount();
_pWamProcess->Release();
_pWamProcess = NULL;
}
_dwSignature = ISAPI_REQUEST_SIGNATURE_FREE;
IF_DEBUG( ISAPI )
{
DBGPRINTF((
DBG_CONTEXT,
"ISAPI_REQUEST %p has been destroyed.\r\n",
this
));
}
}