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.
 
 
 
 
 
 

1853 lines
47 KiB

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// BODY.CPP
//
// Common implementation classes from which request body and
// response body are derived.
//
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
//
#include <_davprs.h>
#include <body.h>
// ========================================================================
//
// CLASS IAcceptObserver
//
// ------------------------------------------------------------------------
//
// IAcceptObserver::~IAcceptObserver()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IAcceptObserver::~IAcceptObserver() {}
// ========================================================================
//
// CLASS IAsyncPersistObserver
//
// ------------------------------------------------------------------------
//
// IAsyncPersistObserver::~IAsyncPersistObserver()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IAsyncPersistObserver::~IAsyncPersistObserver() {}
// ========================================================================
//
// CLASS IAsyncIStreamObserver
//
// ------------------------------------------------------------------------
//
// IAsyncIStreamObserver::~IAsyncIStreamObserver()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IAsyncIStreamObserver::~IAsyncIStreamObserver() {}
// ========================================================================
//
// CLASS CIStreamAsyncStream
//
class CIStreamAsyncStream : public IAsyncStream
{
//
// The OLE IStream
//
IStream& m_stm;
// NOT IMPLEMENTED
//
CIStreamAsyncStream( const CIStreamAsyncStream& );
CIStreamAsyncStream& operator=( const CIStreamAsyncStream& );
public:
// CREATORS
//
CIStreamAsyncStream( IStream& stm ) : m_stm(stm) {}
// ACCESSORS
//
void AsyncWrite( const BYTE * pbBuf,
UINT cbToWrite,
IAsyncWriteObserver& obsAsyncWrite );
};
// ------------------------------------------------------------------------
//
// CIStreamAsyncStream::AsyncWrite()
//
void
CIStreamAsyncStream::AsyncWrite(
const BYTE * pbBuf,
UINT cbToWrite,
IAsyncWriteObserver& obsAsyncWrite )
{
ULONG cbWritten;
HRESULT hr;
hr = m_stm.Write( pbBuf,
cbToWrite,
&cbWritten );
obsAsyncWrite.WriteComplete( cbWritten, hr );
}
// ========================================================================
//
// CLASS IBodyPartVisitor
//
// ------------------------------------------------------------------------
//
// IBodyPartVisitor::~IBodyPartVisitor()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IBodyPartVisitor::~IBodyPartVisitor() {}
// ========================================================================
//
// CLASS IBodyPart
//
// ------------------------------------------------------------------------
//
// IBodyPart::~IBodyPart()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IBodyPart::~IBodyPart() {}
// ------------------------------------------------------------------------
//
// CTextBodyPart::CTextBodyPart()
//
CTextBodyPart::CTextBodyPart( UINT cbText, LPCSTR lpszText )
{
AddTextBytes( cbText, lpszText );
}
// ------------------------------------------------------------------------
//
// CTextBodyPart::CTextBodyPart()
//
VOID
CTextBodyPart::AddTextBytes( UINT cbText, LPCSTR lpszText )
{
m_bufText.Append( cbText, lpszText );
}
// ------------------------------------------------------------------------
//
// CTextBodyPart::Rewind()
//
VOID
CTextBodyPart::Rewind()
{
//
// Since a text body part is implemented as a randomly-
// accessible array there is nothing to "rewind".
//
}
// ------------------------------------------------------------------------
//
// CTextBodyPart::Accept()
//
VOID
CTextBodyPart::Accept( IBodyPartVisitor& v,
UINT64 ibPos64,
IAcceptObserver& obsAccept )
{
Assert( ibPos64 < m_bufText.CbSize() );
//
// Just visit all of the remaining text in the buffer. The visitor
// may not process it all, but that will be reflected in the next
// call to this function.
// NOTE: To be compatable with IBodyPart the position is passed
// in as 64 bit value (this is necessary to support file body parts
// that are bigger than 4GB). However we do not want anyone to create
// text body parts that are bigger than 4GB. So assert that it is not
// the case here and truncate the passed in 64 bit value to 32 bits.
//
Assert(0 == (0xFFFFFFFF00000000 & ibPos64));
v.VisitBytes(
reinterpret_cast<const BYTE *>(m_bufText.PContents()) + static_cast<UINT>(ibPos64),
m_bufText.CbSize() - static_cast<UINT>(ibPos64),
obsAccept );
}
// ========================================================================
//
// CLASS CFileBodyPart
//
// ------------------------------------------------------------------------
//
// CFileBodyPart::CFileBodyPart()
//
CFileBodyPart::CFileBodyPart( const auto_ref_handle& hf,
UINT64 ibFile64,
UINT64 cbFile64 ) :
m_hf(hf),
m_ibFile64(ibFile64),
m_cbFile64(cbFile64)
{
// We do not support byteranges on the files larger than 4GB. But due to the fact that byterange
// processing is all DWORD based in adition to the check for default file length value we do the
// check for default byterange value. If _HSE_TF_INFO will ever be fixed to take file size values
// larger than DWORD then we would be able to move our byterange processing to UINT64 base and
// the second check below would go away.
//
if ((0xFFFFFFFFFFFFFFFF == cbFile64) || // If we got the default file length value indicating that we want data up to the end of the file
(0x00000000FFFFFFFF == cbFile64)) // If we got the default byterange value indicating that we want the data up to the end of the file
{
LARGE_INTEGER cbFileSize;
if (!GetFileSizeEx(hf.get(), &cbFileSize))
{
DebugTrace( "CFileBodyPart::CFileBodyPart() - GetFileSizeEx() failed with last error (0x%08lX)\n", GetLastError() );
throw CLastErrorException();
}
m_cbFile64 = cbFileSize.QuadPart;
}
}
// ------------------------------------------------------------------------
//
// CFileBodyPart::Rewind()
//
VOID
CFileBodyPart::Rewind()
{
//
// Since the files in file body parts are opened overlapped,
// they do not have internal file pointers, hence they never
// need to be rewound.
//
}
// ------------------------------------------------------------------------
//
// CFileBodyPart::Accept()
//
VOID
CFileBodyPart::Accept( IBodyPartVisitor& v,
UINT64 ibPos64,
IAcceptObserver& obsAccept )
{
if (ibPos64 < m_cbFile64)
{
//
// Just visit the remainder of the file. The visitor
// may not process it all, but that will be reflected in the next
// call to this function.
//
v.VisitFile( m_hf,
m_ibFile64 + ibPos64,
m_cbFile64 - ibPos64,
obsAccept );
}
else
{
//
// We should always have something to accept unless we have a
// 0-length file body part. In that case, just tell the observer
// how much was visited: nothing.
//
obsAccept.AcceptComplete(0);
}
}
// ========================================================================
//
// CLASS CAsyncReadVisitor
//
// A body part visitor that asynchronously reads body parts into
// a fixed size, caller-supplied, buffer.
//
class CAsyncReadVisitor :
public IBodyPartVisitor,
private IAsyncReadObserver
{
//
// Error information
//
HRESULT m_hr;
//
// User's buffer and its size
//
LPBYTE m_pbBufUser;
UINT m_cbBufUser;
//
// Accept observer passed to VisitStream(). This observer must
// be stashed in a member variable because reading from the stream
// is asynchronous and we need to be able to notify the observer
// when the read completes.
//
IAcceptObserver * m_pobsAccept;
//
// IBodyPartVisitor
//
VOID VisitBytes( const BYTE * pbData,
UINT cbToRead,
IAcceptObserver& obsAccept );
VOID VisitFile( const auto_ref_handle& hf,
UINT64 ibOffset64,
UINT64 cbToRead64,
IAcceptObserver& obsAccept );
VOID VisitStream( IAsyncStream& stm,
UINT cbToRead,
IAcceptObserver& obsAccept );
VOID VisitComplete();
//
// IAsyncReadObserver for async streams visited via VisitStream()
//
VOID ReadComplete( UINT cbRead, HRESULT hr );
// NOT IMPLEMENTED
//
CAsyncReadVisitor( const CAsyncReadVisitor& );
CAsyncReadVisitor& operator=( const CAsyncReadVisitor& );
public:
// CREATORS
//
CAsyncReadVisitor() :
// Always start with clean member variables
m_hr(S_OK),
m_pbBufUser(NULL),
m_cbBufUser(0),
m_pobsAccept(NULL)
{
}
// ACCESSORS
//
HRESULT Hresult() const { return m_hr; }
// MANIPULATORS
//
VOID
Configure( LPBYTE pbBufUser,
UINT cbBufUser )
{
m_pbBufUser = pbBufUser;
m_cbBufUser = cbBufUser;
// Also reset our HRESULT
m_hr = S_OK;
}
};
// ------------------------------------------------------------------------
//
// CAsyncReadVisitor::VisitBytes()
//
VOID
CAsyncReadVisitor::VisitBytes( const BYTE * pbData,
UINT cbToRead,
IAcceptObserver& obsAccept )
{
cbToRead = min(cbToRead, m_cbBufUser);
memcpy(m_pbBufUser, pbData, cbToRead);
obsAccept.AcceptComplete(cbToRead);
}
// ------------------------------------------------------------------------
//
// CAsyncReadVisitor::VisitFile()
//
// Not implemented because 1) request bodies cannot have file
// body parts and 2) CAsyncReadVisitor is currently only used
// with request bodies. Should we ever need this for response
// bodies we'll need to write the code at that point.
//
// The old implementation used ReadFileEx() to read from the file
// asynchronously. In a nutshell, we couldn't use ReadFileEx()
// because it relied on APC for calling back its completion routine.
// APC in turn required the calling thread to enter an alertable
// wait state. Typically, we would only call VisitFile() from an
// I/O completion port thread pool, and those threads are never
// in an alertable wait state, thus the completion routine for
// ReadFileEx() would never be called.
//
VOID
CAsyncReadVisitor::VisitFile( const auto_ref_handle&,
UINT64,
UINT64,
IAcceptObserver& obsAccept )
{
TrapSz( "CAsyncReadVisitor::VisitFile() is not implemented!!" );
//
// If, for whatever random reason, someone actually does call
// this function, at least do something predictable: fail gracefully.
//
m_hr = E_FAIL;
obsAccept.AcceptComplete( 0 );
}
// ------------------------------------------------------------------------
//
// CAsyncReadVisitor::VisitStream()
//
VOID
CAsyncReadVisitor::VisitStream( IAsyncStream& stmSrc,
UINT cbToRead,
IAcceptObserver& obsAccept )
{
//
// Read into our user's buffer only as much of the stream as is
// immediately available -- i.e. the amount of data that can be
// read without pending the read operation. Note that on input
// cbToRead is the amount of data remaining to be read from the
// stream -- it is not all necessarily immediately available.
//
// X5 162502: This used to say min(stmSrc.CbReady(),...) here
// instead of min(cbToRead,...). This was not a problem on IIS5
// because there was always at least some data immediately available
// when our ISAPI was called. However, on the Local Store, it may
// be such that when we call the ISAPI, there is no data immediately
// available. This turned out to be a problem because we would get
// here and cbToRead would be assigned to 0, which would end up
// making it look like we'd finished (end of stream), which would
// cause XML parse errors (0-byte XML bodies don't parse well!).
//
cbToRead = min(cbToRead, m_cbBufUser);
//
// Save off the observer and start reading. Even though this is
// an AsyncRead() call, we have limited the request to what can
// be read immediately, so our ReadComplete() should be called
// before the AsyncRead() call returns. This is important because
// we are reading directly into the user's buffer. The buffer
// is valid for the duration of this visit.
//
m_pobsAccept = &obsAccept;
stmSrc.AsyncRead(m_pbBufUser, cbToRead, *this);
}
// ------------------------------------------------------------------------
//
// CAsyncReadVisitor::ReadComplete()
//
// Called when the AsyncRead() of the stream by VisitStream() completes.
//
VOID
CAsyncReadVisitor::ReadComplete( UINT cbRead, HRESULT hr )
{
//
// Latch in any error returned.
//
m_hr = hr;
//
// Notify our observer of the number of bytes read.
//
Assert(m_pobsAccept);
m_pobsAccept->AcceptComplete(cbRead);
}
// ------------------------------------------------------------------------
//
// CAsyncReadVisitor::VisitComplete()
//
VOID
CAsyncReadVisitor::VisitComplete()
{
m_hr = S_FALSE;
}
// ========================================================================
//
// CLASS CAsyncCopyToVisitor
//
// A body part visitor that asynchronously copies body parts into
// a destination async stream.
//
class CAsyncCopyToVisitor :
public IBodyPartVisitor,
private IAsyncWriteObserver,
private IAsyncCopyToObserver
{
//
// CAsyncCopyToVisitor forwards its refcounting calls to this
// parent object (settable via SetRCParent()). We are a non-refcounted
// member of another object (e.g. CAsyncPersistor below) -- so our
// lifetime must be determined by the lifetime of our parent object.
//
IRefCounted * m_prcParent;
//
// Error information
//
HRESULT m_hr;
//
// The destination stream
//
IAsyncStream * m_pstmDst;
//
// The count of bytes to copy and the count copied
//
ULONG m_cbToCopy;
ULONG m_cbCopied;
//
// The Accept() observer to notify when we're done
// visiting upon completion of an AsyncWrite()
// or AsyncCopyTo() on the destination stream.
//
IAcceptObserver * m_pobsAccept;
//
// IBodyPartVisitor
//
VOID VisitBytes( const BYTE * pbData,
UINT cbToCopy,
IAcceptObserver& obsAccept );
VOID VisitFile( const auto_ref_handle& hf,
UINT64 ibOffset64,
UINT64 cbToCopy64,
IAcceptObserver& obsAccept );
VOID VisitStream( IAsyncStream& stm,
UINT cbToCopy,
IAcceptObserver& obsAccept );
VOID VisitComplete();
//
// IAsyncWriteObserver
//
VOID WriteComplete( UINT cbWritten, HRESULT hr );
//
// IAsyncCopyToObserver
//
VOID CopyToComplete( UINT cbCopied, HRESULT hr );
// NOT IMPLEMENTED
//
CAsyncCopyToVisitor( const CAsyncCopyToVisitor& );
CAsyncCopyToVisitor& operator=( const CAsyncCopyToVisitor& );
public:
// CREATORS
//
CAsyncCopyToVisitor() :
m_prcParent(NULL),
m_hr(S_OK),
m_pstmDst(NULL),
m_cbToCopy(0),
m_cbCopied(0)
{
}
// ACCESSORS
//
HRESULT Hresult() const { return m_hr; }
UINT CbCopied() const { return m_cbCopied; }
// MANIPULATORS
//
VOID
Configure( IAsyncStream& stmDst,
ULONG cbToCopy )
{
m_pstmDst = &stmDst;
m_cbToCopy = cbToCopy;
m_cbCopied = 0;
m_hr = S_OK;
}
VOID
SetRCParent(IRefCounted * prcParent)
{
Assert(prcParent);
m_prcParent = prcParent;
}
// Refcounting for IAsyncWriteObserver. Since this is not a refcounted
// object we forward the refcouting to the object with which we
// were configured.
//
void AddRef()
{
Assert( m_prcParent );
m_prcParent->AddRef();
}
void Release()
{
Assert( m_prcParent );
m_prcParent->Release();
}
};
// ------------------------------------------------------------------------
//
// CAsyncCopyToVisitor::WriteComplete()
//
void
CAsyncCopyToVisitor::WriteComplete( UINT cbWritten, HRESULT hr )
{
ActvTrace( "DAV: TID %3d: 0x%08lX: CAsyncCopyToVisitor::WriteComplete() called. hr = 0x%08lX, cbWritten = %u\n", GetCurrentThreadId(), this, hr, cbWritten );
m_cbCopied += cbWritten;
m_hr = hr;
m_pobsAccept->AcceptComplete( cbWritten );
}
// ------------------------------------------------------------------------
//
// CAsyncCopyToVisitor::VisitBytes()
//
void
CAsyncCopyToVisitor::VisitBytes( const BYTE * pbData,
UINT cbToCopy,
IAcceptObserver& obsAccept )
{
ActvTrace( "DAV: TID %3d: 0x%08lX: CAsyncCopyToVisitor::VisitBytes() called. cbToCopy = %u\n", GetCurrentThreadId(), this, cbToCopy );
//
// Remember the accept observer so that we can notify it when
// the AsyncWrite() below completes.
//
m_pobsAccept = &obsAccept;
//
// Start writing
//
cbToCopy = min( cbToCopy, m_cbToCopy - m_cbCopied );
m_pstmDst->AsyncWrite( pbData, cbToCopy, *this );
}
// ------------------------------------------------------------------------
//
// CAsyncCopyToVisitor::VisitFile()
//
// Not implemented because 1) request bodies cannot have file
// body parts and 2) CAsyncCopyToVisitor is currently only used
// with request bodies. Should we ever need this for response
// bodies we'll need to write the code at that point.
//
// The old implementation used ReadFileEx() to read from the file
// asynchronously. In a nutshell, we couldn't use ReadFileEx()
// because it relied on APC for calling back its completion routine.
// APC in turn required the calling thread to enter an alertable
// wait state. Typically, we would only call VisitFile() from an
// I/O completion port thread pool, and those threads are never
// in an alertable wait state, thus the completion routine for
// ReadFileEx() would never be called.
//
void
CAsyncCopyToVisitor::VisitFile( const auto_ref_handle&,
UINT64,
UINT64,
IAcceptObserver& obsAccept )
{
TrapSz( "CAsyncCopyToVisitor::VisitFile() is not implemented!!" );
//
// If, for whatever random reason, someone actually does call
// this function, at least do something predictable: fail gracefully.
//
m_hr = E_FAIL;
obsAccept.AcceptComplete( 0 );
}
// ------------------------------------------------------------------------
//
// CAsyncCopyToVisitor::VisitStream()
//
void
CAsyncCopyToVisitor::VisitStream( IAsyncStream& stmSrc,
UINT cbToCopy,
IAcceptObserver& obsAccept )
{
ActvTrace( "DAV: TID %3d: 0x%08lX: CAsyncCopyToVisitor::VisitStream() called. cbToCopy = %u\n", GetCurrentThreadId(), this, cbToCopy );
//
// Remember the accept observer so that we can notify it when
// the AsyncCopyTo() below completes.
//
m_pobsAccept = &obsAccept;
//
// Start copying
//
cbToCopy = min( cbToCopy, m_cbToCopy - m_cbCopied );
stmSrc.AsyncCopyTo( *m_pstmDst, cbToCopy, *this );
}
// ------------------------------------------------------------------------
//
// CAsyncCopyToVisitor::CopyToComplete()
//
void
CAsyncCopyToVisitor::CopyToComplete( UINT cbCopied, HRESULT hr )
{
m_cbCopied += cbCopied;
m_hr = hr;
ActvTrace( "DAV: TID %3d: 0x%08lX: CAsyncCopyToVisitor::CopyToComplete() hr = 0x%08lX, cbCopied = %u, m_cbCopied = %u\n", GetCurrentThreadId(), this, hr, cbCopied, m_cbCopied );
m_pobsAccept->AcceptComplete( cbCopied );
}
// ------------------------------------------------------------------------
//
// CAsyncCopyToVisitor::VisitComplete()
//
VOID
CAsyncCopyToVisitor::VisitComplete()
{
m_hr = S_FALSE;
}
// ========================================================================
//
// CLASS CBodyAsIStream
//
// Provides once-only access to the entire body as an OLE COM IStream using
// either IStream::Read() and IStream::CopyTo().
//
class CBodyAsIStream :
public CStreamNonImpl,
private IAcceptObserver
{
//
// Iterator used to traverse the body
//
IBody::iterator * m_pitBody;
//
// The three states of the read operation started by the most recent
// call to CBodyAsIStream::Read():
//
// READ_ACTIVE
// The read is active. It may or may not complete
// synchronously. This is the initial state.
//
// READ_PENDING
// The read is pending. The read did not complete before
// we had to return to the caller. CBodyAsIStream::Read()
// returns E_PENDING and the stream observer (below) is notified
// when the read completes.
//
// READ_COMPLETE
// The read completed before we had to return to the
// caller. CBodyAsIStream::Read() does not return E_PENDING
// and the stream observer (below) is not notified.
//
// Note: m_lStatus is meaningless (and hence uninitialized/invalid) until
// CBodyAsIStream::Read() is called.
//
enum
{
READ_ACTIVE,
READ_PENDING,
READ_COMPLETE,
READ_INVALID_STATUS = -1
};
LONG m_lStatus;
//
// Status of last completed operation.
//
HRESULT m_hr;
//
// Async visitor used for Read().
//
CAsyncReadVisitor m_arv;
//
// Count of bytes read in the visit started by the most recent
// call to CBodyAsIStream::Read().
//
// Note: m_cbRead is meaningless (and hence uninitialized) until
// CBodyAsIStream::Read() is called.
//
UINT m_cbRead;
//
// Reference to the async I/O completion observer. We notify this
// observer from CBodyAsIStream::AcceptComplete() when the async
// Accept() call we make in CBodyAsIStream::Read() completes for
// a read that we have pended.
//
IAsyncIStreamObserver& m_obsStream;
// IAcceptObserver callback used when accepting async read visitor
// to asynchronoulsy refill the buffer.
//
VOID AcceptComplete( UINT64 cbRead64 );
// NOT IMPLEMENTED
//
CBodyAsIStream( const CBodyAsIStream& );
CBodyAsIStream& operator=( const CBodyAsIStream& );
public:
CBodyAsIStream( IBody& body,
IAsyncIStreamObserver& obs ) :
m_pitBody(body.GetIter()),
m_lStatus(READ_INVALID_STATUS),
m_hr(S_OK),
m_cbRead(0),
m_obsStream(obs)
{
}
// COM IStream ACCESSORS/MANIPULATORS
//
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read(
/* [length_is][size_is][out] */ void __RPC_FAR *,
/* [in] */ ULONG,
/* [out] */ ULONG __RPC_FAR *);
//$WORKAROUND: MSXML is calling our Stat() method. (X5#89140)
// Our parent (CStreamNonImpl) has a TrapSz() there, so
// avoid it by implementing our own Stat() call here,
// that just returns E_NOTIMPL.
// MSXML doesn't care if this is not implemented, just so long
// as it doesn't crash/assert/dbgbreak. If they get results
// back here, they do other security checking that we don't
// need or want!
//
virtual HRESULT STDMETHODCALLTYPE Stat(
/* [out] */ STATSTG __RPC_FAR *,
/* [in] */ DWORD)
{
return E_NOTIMPL;
}
//$WORKAROUND: end
};
// ------------------------------------------------------------------------
//
// CBodyAsIStream::Read()
//
HRESULT STDMETHODCALLTYPE
CBodyAsIStream::Read( LPVOID pv,
ULONG cbToRead,
ULONG * pcbRead )
{
HRESULT hr = S_OK;
Assert( cbToRead > 0 );
Assert( !IsBadWritePtr(pv, cbToRead) );
Assert( !pcbRead || !IsBadWritePtr(pcbRead, sizeof(ULONG)) );
BodyStreamTrace( "DAV: TID %3d: 0x%08lX: CBodyAsIStream::Read() called to read %lu bytes from stream\n", GetCurrentThreadId(), this, cbToRead );
//
// If we are called on the Read() again while the previous read is pending
// return with the same indication - that operation is still pending.
// This is done to protect ourselves from the external callers like MSXML
// parser, that tries to read data from us, we return E_PENDING, and then
// they are turning around and calling into us again without waiting for
// the previous pending read to complete. Our code does handle only one
// outstanding async IO at a time. So the check below allows us to ignore
// the IOs that are attempted to start while previous one is pending. This
// works as long as the caller does expect to be called back only once for
// any amount of async IOs issued while the original IO is still pending.
// If the last condition is not met the only thing left to us would be to
// error out so we do not crash and for the callers to fix their behaviour
// so the code would work.
//
if (READ_PENDING == InterlockedCompareExchange( &m_lStatus,
READ_PENDING,
READ_PENDING ))
{
return E_PENDING;
}
//
// As this is an STDMETHODCALLTYPE function, we need to wrap the whole thing
// in a try/catch block to keep exceptions due to memory allocation failures
// from propagating out.
//
// Note: We don't expect anything in this try/catch block to throw a "hard"
// Win32 exception so we don't need a CWin32ExceptionHandler in the block.
//
try
{
//
// Check for errors from the previous (pended) read.
//
hr = m_arv.Hresult();
if ( FAILED(hr) )
{
DebugTrace( "CBodyAsIStream::Read() - Error from previous async read 0x%08lX\n", hr );
goto ret;
}
//
// Set up our visitor to read directly into the caller's buffer.
//
m_arv.Configure(static_cast<LPBYTE>(pv), cbToRead);
//
// Clear out the count of bytes read from the previous run
//
m_cbRead = 0;
//
// Set our status to actively reading. When we call Accept(), this status will
// change in one of two possible ways: If we finish accepting before our
// Accept() call returns then the status will be set to READ_COMPLETE and
// we will complete this Read() call synchronously. If not then it will still
// be set to READ_ACTIVE at the point where we test and set it below to
// READ_PENDING.
//
m_lStatus = READ_ACTIVE;
//
// Visit the body.
//
m_pitBody->Accept( m_arv, *this );
//
// Check the visit status. If the visit has not completed at this
// point then attempt to pend the read operation and return E_PENDING
// to our caller. If we successfully pend the operation then our
// AcceptComplete() routine will notify our stream observer when the
// read completes.
//
if ( READ_ACTIVE == m_lStatus &&
READ_ACTIVE == InterlockedExchange( &m_lStatus, READ_PENDING ) )
{
BodyStreamTrace( "DAV: TID %3d: 0x%08lX: CBodyAsIStream::Read() Returning E_PENDING\n", GetCurrentThreadId(), this );
hr = E_PENDING;
goto ret;
}
//
// Check for errors from the current read.
//
hr = m_arv.Hresult();
if ( FAILED(hr) )
{
DebugTrace( "CBodyAsIStream::Read() - Error from current read 0x%08lX\n", hr );
goto ret;
}
//
// If we're at End Of Stream then return what we got.
//
if ( S_FALSE == hr )
{
//
// Don't return S_FALSE when we're also returning
// data. The IStream spec is unclear on whether
// that is allowed.
//
if ( m_cbRead > 0 )
hr = S_OK;
}
//
// Return the number of bytes read if the caller asked for
// that information.
//
if ( pcbRead )
*pcbRead = m_cbRead;
}
catch ( CDAVException& e )
{
hr = e.Hresult();
Assert( FAILED(hr) );
}
ret:
return hr;
}
// ------------------------------------------------------------------------
//
// CBodyAsIStream::AcceptComplete()
//
// Called when the Accept() call started in Read() to asynchronously
// refill the buffer completes.
//
VOID
CBodyAsIStream::AcceptComplete( UINT64 cbRead64 )
{
BodyStreamTrace( "DAV: TID %3d: 0x%08lX: CBodyAsIStream::AcceptComplete() cbRead64 = %lu\n", GetCurrentThreadId(), this, cbRead64 );
//
// Update the count of bytes that our Accept() call successfully
// read into the user's buffer. We are accepting in piecies so the
// accepted amount should be really much less than 4GB.
//
Assert(0 == (0xFFFFFFFF00000000 & cbRead64));
m_cbRead = static_cast<UINT>(cbRead64);
//
// Set status to READ_COMPLETE. If the read operation pended --
// i.e. the previous state was READ_PENDING, not READ_ACTIVE --
// then we must wake up the stream observer and tell it that
// we are done.
//
if ( READ_PENDING == InterlockedExchange( &m_lStatus, READ_COMPLETE ) )
m_obsStream.AsyncIOComplete();
}
// ========================================================================
//
// CLASS CAsyncPersistor
//
// Implements an async driven object to persist a body to an IAsyncStream.
//
class CAsyncPersistor :
public CMTRefCounted,
public IRefCounted,
private IAcceptObserver
{
//
// Body iterator
//
IBody::iterator * m_pitBody;
//
// Async driving mechanism
//
CAsyncDriver<CAsyncPersistor> m_driver;
friend class CAsyncDriver<CAsyncPersistor>;
//
// Caller-supplied observer to notify when we're done persisting.
//
auto_ref_ptr<IAsyncPersistObserver> m_pobsPersist;
//
// CopyTo visitor used to persist the body
//
CAsyncCopyToVisitor m_actv;
//
// CAsyncDriver callback
//
VOID Run();
//
// IAcceptObserver callback used when accepting async copyto visitor
// to asynchronously persist the body to the destination stream.
//
VOID AcceptComplete( UINT64 cbCopied64 );
// NOT IMPLEMENTED
//
CAsyncPersistor( const CAsyncPersistor& );
CAsyncPersistor& operator=( const CAsyncPersistor& );
public:
// CREATORS
//
CAsyncPersistor( IBody& body,
IAsyncStream& stm,
IAsyncPersistObserver& obs ) :
m_pitBody(body.GetIter()),
m_pobsPersist(&obs)
{
//
// Set the CopyTo() parameters here, once. If we ever need
// to copy request bodies larger than ULONG_MAX bytes, we'll
// need to move this call down into Run().
//
m_actv.Configure(stm, ULONG_MAX);
}
// MANIUPLATORS
//
VOID Start()
{
m_driver.Start(*this);
}
// Refcounting -- forward all refcounting requests to our refcounting
// implementation base class: CMTRefCounted.
//
void AddRef() { CMTRefCounted::AddRef(); }
void Release() { CMTRefCounted::Release(); }
};
// ------------------------------------------------------------------------
//
// CAsyncPersistor::Run()
//
VOID
CAsyncPersistor::Run()
{
PersistTrace( "DAV: TID %3d: 0x%08lX: CAsyncPersistor::Run() called\n", GetCurrentThreadId(), this );
//
// AddRef() for Accept(). Use auto_ref_ptr for exception-safety.
//
auto_ref_ptr<CAsyncPersistor> pRef(this);
m_actv.SetRCParent(this);
m_pitBody->Accept(m_actv, *this);
pRef.relinquish();
}
// ------------------------------------------------------------------------
//
// CAsyncPersistor::AcceptComplete()
//
VOID
CAsyncPersistor::AcceptComplete( UINT64 cbCopied64 )
{
//
// Take ownership of the reference added in Run().
//
auto_ref_ptr<CAsyncPersistor> pRef;
pRef.take_ownership(this);
//
// We're done when the status of the CopyTo visitor is
// S_FALSE (success) or an error.
//
HRESULT hr = m_actv.Hresult();
PersistTrace( "DAV: TID %3d: 0x%08lX: CAsyncPersistor::AcceptComplete() hr = 0x%08lX\n, cbCopied64 = %ud\n", GetCurrentThreadId(), this, hr, cbCopied64 );
if ( FAILED(hr) || S_FALSE == hr )
{
Assert( m_pobsPersist.get() );
m_pobsPersist->PersistComplete(hr);
}
else
{
Start();
}
}
// ========================================================================
//
// CLASS IBody
//
// ------------------------------------------------------------------------
//
// IBody::~IBody()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IBody::~IBody() {}
// ========================================================================
//
// CLASS IBody::iterator
//
// ------------------------------------------------------------------------
//
// IBody::iterator::~iterator()
//
// Out of line virtual destructor necessary for proper deletion
// of objects of derived classes via this class
//
IBody::iterator::~iterator() {}
// ========================================================================
//
// CLASS CList
//
// The body part list implementation uses the STL list template.
// Body parts are stored in auto_ptrs so that they are automatically
// destroyed as they are removed from the list or when the list itself is
// destroyed.
//
// This class does not by itself need to provide any sort of thread-safety.
//
typedef std::list<
auto_ptr_obsolete<IBodyPart>,
heap_allocator< auto_ptr_obsolete<IBodyPart> >
> CList;
// ========================================================================
//
// CLASS CBodyPartList
//
// Encapsulates access to the list of body parts. The reason for this
// seemingly extra level of encapsulation is that it enables us to
// change the list implementation easily without touching code which
// uses the list.
//
// !!! IMPORTANT !!!
// When accessing/modifying the raw STL list through CBodyPartList,
// we must acquire our critical section. Threads may be iterating
// over the list via our iterator, CBodyPartListIter, while we are modifying
// the list and the STL list and its iterator are not thread-safe.
// In other words, CBodyPartListIter and CBodyPartList share the same critsec.
//
class CBodyPartList
{
// The list
//
CList m_list;
// Critical section to serialize access to the above list
//
CCriticalSection m_csList;
// NOT IMPLEMENTED
//
CBodyPartList( const CBodyPartList& );
CBodyPartList& operator=( const CBodyPartList& );
friend class CBodyPartListIter;
public:
// CREATORS
//
CBodyPartList() {}
// ACCESSORS
//
const BOOL FIsEmpty() const
{
//
// Note: we don't currently acquire the critical section
// proctecting the raw list as we expect this function
// to not be called once we are accessing the list
// from multiple threads.
//
//
// Return whether there are any body parts in the list
//
return m_list.empty();
}
// MANIPULATORS
//
VOID Clear()
{
//
// Note: we don't currently acquire the critical section
// proctecting the raw list as we expect this function
// to not be called once we are accessing the list
// from multiple threads.
//
//
// Remove all body parts from the list (at which point they
// should be automatically destroyed).
//
m_list.clear();
}
VOID PushPart( IBodyPart * pBodyPart )
{
CSynchronizedBlock sb(m_csList);
//
// Our iterator (CBodyPartList iter, below) uses the STL
// list reverse_iterator to traverse the list from back to
// front, so we append body parts to the *front* of the list.
//
m_list.push_front( auto_ptr_obsolete<IBodyPart>(pBodyPart) );
}
};
// ========================================================================
//
// CLASS CBodyPartListIter
//
// Implements an iterator for CBodyPartList
//
// This implementation uses the reverse STL list iterator corresponding
// to the usage of the STL list type in CBodyPartList. STL iterators
// have some syntactic sugar that we need to note here:
//
// * (deref) of an iterator gives the thing pointed to
// ++ (increment) of an iterator goes to the "next" item
// -- (decrement) of an iterator goes to the "previous" item
//
// We use the reverse iterator because of the behavior we need at
// the end of the list w.r.t. adding new items. When an iterator
// reaches the end of the list and items are later added there,
// we want the iterator to refer to the first of the new items rather
// than the new end-of-list. The forward STL iterator has the
// latter behavior.
//
// !!! IMPORTANT !!!
// When accessing/modifying the raw STL list through our iterator
// we must acquire CBodyPartList's critical section. Threads may
// be modifying the list while we are iterating through it and
// the STL list and its iterator are not thread-safe. In other words,
// CBodyPartListIter and CBodyPartList share the same critsec.
//
class CBodyPartListIter
{
// Pointer to the list to iterate on
//
CBodyPartList * m_pBodyPartList;
// The raw STL list iterator
//
CList::reverse_iterator m_itRaw;
// CBodyPartList ACCESSORS
//
CCriticalSection& CritsecList() const
{
Assert( m_pBodyPartList );
return m_pBodyPartList->m_csList;
}
CList& RawList() const
{
Assert( m_pBodyPartList );
return m_pBodyPartList->m_list;
}
// NOT IMPLEMENTED
//
CBodyPartListIter( const CBodyPartListIter& );
CBodyPartListIter& operator=( const CBodyPartListIter& );
public:
// CREATORS
//
CBodyPartListIter() :
m_pBodyPartList(NULL)
{
}
VOID Start( CBodyPartList& m_bodyPartList )
{
m_pBodyPartList = &m_bodyPartList;
//
// Note: we don't currently acquire the critical section
// proctecting the raw list as we expect this function
// to not be called once we are accessing the list
// from multiple threads.
//
m_itRaw = RawList().rbegin();
}
// ACCESSORS
//
BOOL FDone()
{
CSynchronizedBlock sb(CritsecList());
return m_itRaw == RawList().rend();
}
IBodyPart * PItem()
{
CSynchronizedBlock sb(CritsecList());
return *m_itRaw;
}
// MANIPULATORS
//
// ------------------------------------------------------------------------
//
// CBody::Prune()
//
// Bumps the iterator to the next item in the list
//
VOID Next()
{
CSynchronizedBlock sb(CritsecList());
//
// We had better not already be at the end...
//
Assert( m_itRaw != RawList().rend() );
++m_itRaw;
}
// ------------------------------------------------------------------------
//
// CBody::Prune()
//
// Prunes the list at this iterator's current position. Removes items
// from the current position to the end of the list. Does not remove
// the current item.
//
VOID Prune()
{
CSynchronizedBlock sb(CritsecList());
//
// Unfortunately the STL only allows us to erase between two
// forward iterators. And there is no way to get a forward
// iterator directly from a reverse iterator. So we must
// start a forward iterator at the end of the list and walk
// it backward the same distance that our reverse iterator
// is from its "beginning" of the list and then erase the
// items between the forward iterator and the end of the list.
//
CList::iterator itErase = RawList().end();
for ( CList::reverse_iterator it = RawList().rbegin();
it != m_itRaw;
++it )
{
--itErase;
}
if ( itErase != RawList().end() )
RawList().erase( ++itErase, RawList().end() );
}
};
// ========================================================================
//
// CLASS CBody
//
class CBody : public IBody
{
// ========================================================================
//
// CLASS iterator
//
class iterator :
public IBody::iterator,
private IAcceptObserver
{
//
// Iterator to walk the body part list.
//
CBodyPartListIter m_itPart;
//
// Pointer to the current body part referred to by the
// above iterator.
//
IBodyPart * m_pBodyPart;
//
// Current position in the above part.
//
UINT64 m_ibPart64;
//
// Observer to call when Accept() completes -- set on each
// Accept() call.
//
IAcceptObserver * m_pobsAccept;
//
// IAcceptObserver
//
VOID AcceptComplete( UINT64 cbAccepted64 );
// NOT IMPLEMENTED
//
iterator( const iterator& );
iterator& operator=( const iterator& );
public:
iterator() {}
VOID Start( CBodyPartList& bodyPartList )
{
m_itPart.Start(bodyPartList);
m_pBodyPart = NULL;
}
VOID Accept( IBodyPartVisitor& v,
IAcceptObserver& obsAccept );
VOID Prune();
};
// Body part list and current position in that list
//
CBodyPartList m_bodyPartList;
// Our iterator
//
iterator m_it;
//
// Inline helper to add a body part
//
void _AddBodyPart( IBodyPart * pBodyPart )
{
m_bodyPartList.PushPart(pBodyPart);
}
// NOT IMPLEMENTED
//
CBody( const CBody& );
CBody& operator=( const CBody& );
public:
CBody() {}
// ACCESSORS
//
BOOL FIsEmpty() const;
BOOL FIsAtEnd() const;
UINT64 CbSize64() const;
// MANIPULATORS
//
void AddText( LPCSTR lpszText,
UINT cbText );
void AddFile( const auto_ref_handle& hf,
UINT64 ibFile,
UINT64 cbFile );
void AddStream( IStream& stm );
void AddStream( IStream& stm,
UINT ibOffset,
UINT cbSize );
void AddBodyPart( IBodyPart * pBodyPart );
void AsyncPersist( IAsyncStream& stm,
IAsyncPersistObserver& obs );
IStream * GetIStream( IAsyncIStreamObserver& obs )
{
return new CBodyAsIStream(*this, obs);
}
IBody::iterator * GetIter();
VOID Clear();
};
// ------------------------------------------------------------------------
//
// CBody::GetIter()
//
IBody::iterator *
CBody::GetIter()
{
m_it.Start(m_bodyPartList);
return &m_it;
}
// ------------------------------------------------------------------------
//
// CBody::FIsEmpty()
//
BOOL
CBody::FIsEmpty() const
{
return m_bodyPartList.FIsEmpty();
}
// ------------------------------------------------------------------------
//
// CBody::CbSize64()
//
UINT64
CBody::CbSize64() const
{
UINT64 cbSize64 = 0;
//
// Sum the sizes of all the body parts
//
CBodyPartListIter it;
for ( it.Start(const_cast<CBodyPartList&>(m_bodyPartList));
!it.FDone();
it.Next() )
{
cbSize64 += it.PItem()->CbSize64();
}
return cbSize64;
}
// ------------------------------------------------------------------------
//
// CBody::AddText()
//
// Adds static text to the body by creating a text body part with
// its own copy of the text and adding that body part to the
// body part list.
//
// !!!
// For best performance, implement your own text body part on top
// of your text data source rather than copying it via this function
// as doing so avoids making an extra copy of the data from the
// data source in memory.
//
void
CBody::AddText( LPCSTR lpszText, UINT cbText )
{
_AddBodyPart( new CTextBodyPart(cbText, lpszText) );
}
// ------------------------------------------------------------------------
//
// CBody::AddFile()
//
void
CBody::AddFile( const auto_ref_handle& hf,
UINT64 ibFile64,
UINT64 cbFile64 )
{
_AddBodyPart( new CFileBodyPart(hf, ibFile64, cbFile64) );
}
// ------------------------------------------------------------------------
//
// CBody::AddStream()
//
void
CBody::AddStream( IStream& stm )
{
TrapSz("Stream body parts no longer implemented");
}
// ------------------------------------------------------------------------
//
// CBody::AddStream()
//
void
CBody::AddStream( IStream& stm,
UINT ibOffset,
UINT cbSize )
{
TrapSz("Stream body parts no longer implemented");
}
// ------------------------------------------------------------------------
//
// CBody::AddBodyPart()
//
void
CBody::AddBodyPart( IBodyPart * pBodyPart )
{
_AddBodyPart( pBodyPart );
}
// ------------------------------------------------------------------------
//
// CBody::Clear()
//
VOID
CBody::Clear()
{
m_bodyPartList.Clear();
}
// ------------------------------------------------------------------------
//
// CBody::iterator::Accept()
//
// Accepts an asynchronous body part visitor (v) at the iterator's
// current position. The Accept() observer (obsAccept) is notified
// when the visitor finishes.
//
// Lifetimes of both the visitor and the observer are controled
// outside the scope of this function; i.e. it is assumed that
// the observer will still be valid when the visitor finishes.
//
VOID
CBody::iterator::Accept( IBodyPartVisitor& v,
IAcceptObserver& obsAccept )
{
//
// If we've reached the end of the body, then we're done.
//
if ( m_itPart.FDone() )
{
v.VisitComplete();
obsAccept.AcceptComplete(0);
return;
}
//
// We're not at the end of the body. If we are starting
// a new part then rewind the part and our current position.
//
if ( NULL == m_pBodyPart )
{
m_pBodyPart = m_itPart.PItem();
m_pBodyPart->Rewind();
m_ibPart64 = 0;
}
//
// Save off the observer so that we can call it back when
// the body part is done accepting the visitor.
//
m_pobsAccept = &obsAccept;
//
// Accept the specified visitor starting from the current
// position in the current body part.
//
m_pBodyPart->Accept( v, m_ibPart64, *this );
}
// ------------------------------------------------------------------------
//
// CBody::iterator::AcceptComplete()
//
// IBodyPart::AcceptObserver method called by the body part when it is
// done with the visitor we told it to accept in Accept() above.
//
VOID
CBody::iterator::AcceptComplete( UINT64 cbAccepted64 )
{
Assert( m_pBodyPart );
m_ibPart64 += cbAccepted64;
//
// If we reach the end of the current body part then tell
// our iterator to go to the next part. If we hit the end
// of the body, we will catch that condition in Accept() the
// next time we get called there.
//
if ( m_ibPart64 == m_pBodyPart->CbSize64() )
{
m_itPart.Next();
//
// Null out the current body part so we will know to
// fetch the next one on the next call to Accept().
//
m_pBodyPart = NULL;
}
//
// Callback our observer
//
m_pobsAccept->AcceptComplete(cbAccepted64);
}
// ------------------------------------------------------------------------
//
// CBody::iterator::Prune()
//
// Deletes items from the body part list up to, but not including,
// the part at the current list position. This minimizes the
// memory footprint for large one-pass async partwise operations
// such as request persisting or response transmission.
//
VOID
CBody::iterator::Prune()
{
m_itPart.Prune();
}
// ------------------------------------------------------------------------
//
// CBody::AsyncPersist()
//
void
CBody::AsyncPersist( IAsyncStream& stm,
IAsyncPersistObserver& obs )
{
PersistTrace( "DAV: TID %3d: 0x%08lX: CBody::AsyncPersist() called\n", GetCurrentThreadId(), this );
auto_ref_ptr<CAsyncPersistor>
pPersistor(new CAsyncPersistor(*this, stm, obs));
pPersistor->Start();
}
// ------------------------------------------------------------------------
//
// NewBody()
//
IBody * NewBody()
{
return new CBody();
}
// ------------------------------------------------------------------------
//
// CXMLBody::ScAddTextBytes
//
SCODE
CXMLBody::ScAddTextBytes ( UINT cbText, LPCSTR lpszText )
{
Assert (lpszText);
// Create the text body part if necessary
//
if (!m_ptbp.get())
m_ptbp = new CTextBodyPart(0, NULL);
// Add the piece to the body part
//
m_ptbp->AddTextBytes (cbText, lpszText);
// Add to body part list if this body part has reach a proper size
//
if (m_fChunked && (m_ptbp->CbSize64() > CB_XMLBODYPART_SIZE))
SendCurrentChunk();
return S_OK;
}