Bug #32222 --> Implement a subclass of nsMsgprotocol which performs asynch writes. Re-write how we send

post data to use this subclass. We'll now read in the post file asynchronously and write out the post
data asynchronously to the server. When necessary, we'll pause and resume the file request for the incoming
post data since that comes in faster than data goes out.
sr=bienvenu
r=ducarroz/varada
This commit is contained in:
mscott%netscape.com 2001-04-08 00:40:30 +00:00
parent 2125ecc29a
commit 0b2f68bfdd
2 changed files with 523 additions and 52 deletions

View File

@ -35,15 +35,23 @@
#include "nsIDNSService.h"
#include "nsIMsgWindow.h"
#include "nsIMsgStatusFeedback.h"
#include "nsIPipe.h"
#include "nsIPrompt.h"
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
NS_IMPL_ISUPPORTS4(nsMsgProtocol,
nsIStreamListener,
nsIStreamObserver,
nsIChannel,
nsIRequest)
static NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID);
NS_IMPL_THREADSAFE_ADDREF(nsMsgProtocol)
NS_IMPL_THREADSAFE_RELEASE(nsMsgProtocol)
NS_INTERFACE_MAP_BEGIN(nsMsgProtocol)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIStreamObserver)
NS_INTERFACE_MAP_ENTRY(nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_END_THREADSAFE
nsMsgProtocol::nsMsgProtocol(nsIURI * aURL)
{
@ -138,27 +146,25 @@ nsresult nsMsgProtocol::OpenFileSocket(nsIURI * aURL, const nsFileSpec * aFileSp
char * urlSpec = PR_smprintf("file://%s", (const char *) filePath);
// dougt - there should be an easier way!
nsCOMPtr<nsIURI> aIURI;
if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(aIURI), urlSpec)))
return(PR_FALSE);
if (!aIURI) return(PR_FALSE);
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(uri), urlSpec)))
return rv;
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aIURI);
if (!fileURL) return(PR_FALSE);
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
if (!fileURL) return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> file;
rv = fileURL->GetFile(getter_AddRefs(file));
if (NS_FAILED(rv)) return(PR_FALSE);
nsCOMPtr<nsIFile> file;
rv = fileURL->GetFile(getter_AddRefs(file));
if (NS_FAILED(rv)) return rv;
// dougt
NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID);
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) return PR_FALSE;
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = fts->CreateTransport(file, PR_RDWR | PR_CREATE_FILE,
0664, getter_AddRefs(m_transport));
rv = fts->CreateTransport(file, PR_RDWR | PR_CREATE_FILE,
0664, getter_AddRefs(m_transport));
PR_FREEIF(urlSpec);
m_socketIsOpen = PR_FALSE;
m_socketIsOpen = PR_FALSE;
}
return rv;
@ -240,7 +246,7 @@ NS_IMETHODIMP nsMsgProtocol::OnStartRequest(nsIRequest *request, nsISupports *ct
m_loadGroup->AddRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull /* context isupports */);
}
// if we are set up as a channel, we should notify our channel listener that we are starting...
// if we are set up as a channel, we should notify our channel listener that we are starting...
// so pass in ourself as the channel and not the underlying socket or file channel the protocol
// happens to be using
if (!mSuppressListenerNotifications && m_channelListener)
@ -285,20 +291,20 @@ NS_IMETHODIMP nsMsgProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctx
switch (aStatus)
{
case NS_ERROR_UNKNOWN_HOST:
// todo, put this into a string bundle
alertMsg.AssignWithConversion("Failed to connect to the server.");
break;
case NS_ERROR_CONNECTION_REFUSED:
// todo, put this into a string bundle
alertMsg.AssignWithConversion("Connection refused to the server.");
break;
case NS_ERROR_NET_TIMEOUT:
// todo, put this into a string bundle
alertMsg.AssignWithConversion("Connection to the server timed out.");
break;
default:
alertMsg.AppendInt(aStatus, 16);
break;
// todo, put this into a string bundle
alertMsg.AssignWithConversion("Failed to connect to the server.");
break;
case NS_ERROR_CONNECTION_REFUSED:
// todo, put this into a string bundle
alertMsg.AssignWithConversion("Connection refused to the server.");
break;
case NS_ERROR_NET_TIMEOUT:
// todo, put this into a string bundle
alertMsg.AssignWithConversion("Connection to the server timed out.");
break;
default:
alertMsg.AppendInt(aStatus, 16);
break;
}
rv = msgPrompt->Alert(nsnull, alertMsg.GetUnicode());
@ -406,7 +412,7 @@ NS_IMETHODIMP nsMsgProtocol::SetURI(nsIURI* aURI)
NS_IMETHODIMP nsMsgProtocol::Open(nsIInputStream **_retval)
{
NS_NOTREACHED("Open");
NS_NOTREACHED("Open");
return NS_ERROR_NOT_IMPLEMENTED;
}
@ -477,23 +483,22 @@ NS_IMETHODIMP nsMsgProtocol::GetContentLength(PRInt32 * aContentLength)
NS_IMETHODIMP nsMsgProtocol::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
*aSecurityInfo = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsMsgProtocol::GetName(PRUnichar * *aName)
{
return NS_ERROR_NOT_IMPLEMENTED;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMsgProtocol::SetContentLength(PRInt32 aContentLength)
{
NS_NOTREACHED("SetContentLength");
return NS_ERROR_NOT_IMPLEMENTED;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsMsgProtocol::GetOwner(nsISupports * *aPrincipal)
{
*aPrincipal = mOwner;
@ -509,8 +514,8 @@ NS_IMETHODIMP nsMsgProtocol::SetOwner(nsISupports * aPrincipal)
NS_IMETHODIMP nsMsgProtocol::GetLoadGroup(nsILoadGroup * *aLoadGroup)
{
*aLoadGroup = m_loadGroup;
NS_IF_ADDREF(*aLoadGroup);
*aLoadGroup = m_loadGroup;
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
@ -562,23 +567,22 @@ NS_IMETHODIMP nsMsgProtocol::GetStatus(nsresult *status)
NS_IMETHODIMP nsMsgProtocol::Cancel(nsresult status)
{
NS_ASSERTION(m_request,"no channel");
if (!m_request) {
if (!m_request)
return NS_ERROR_FAILURE;
}
return m_request->Cancel(status);
}
NS_IMETHODIMP nsMsgProtocol::Suspend()
{
NS_NOTREACHED("Suspend");
return NS_ERROR_NOT_IMPLEMENTED;
NS_NOTREACHED("Suspend");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsMsgProtocol::Resume()
{
NS_NOTREACHED("Resume");
return NS_ERROR_NOT_IMPLEMENTED;
NS_NOTREACHED("Resume");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult nsMsgProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec)
@ -690,3 +694,413 @@ nsresult nsMsgProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec)
}
return NS_OK;
}
/////////////////////////////////////////////////////////////////////
// nsMsgAsyncWriteProtocol subclass and related helper classes
/////////////////////////////////////////////////////////////////////
class nsMsgProtocolStreamProvider : public nsIStreamProvider
{
public:
NS_DECL_ISUPPORTS
nsMsgProtocolStreamProvider() { NS_INIT_REFCNT(); }
virtual ~nsMsgProtocolStreamProvider() {}
void Init(nsMsgAsyncWriteProtocol *aProtInstance, nsIInputStream *aInputStream) { mMsgProtocol = aProtInstance; mInStream = aInputStream;}
//
// nsIStreamObserver implementation ...
//
NS_IMETHODIMP OnStartRequest(nsIRequest *chan, nsISupports *ctxt) { return NS_OK; }
NS_IMETHODIMP OnStopRequest(nsIRequest *chan, nsISupports *ctxt, nsresult status, const PRUnichar *statusText) { return NS_OK; }
//
// nsIStreamProvider implementation ...
//
NS_IMETHODIMP OnDataWritable(nsIRequest *aChannel, nsISupports *aContext,
nsIOutputStream *aOutStream,
PRUint32 aOffset, PRUint32 aCount)
{
NS_ASSERTION(mInStream, "not initialized");
nsresult rv;
PRUint32 avail;
// Write whatever is available in the pipe. If the pipe is empty, then
// return NS_BASE_STREAM_WOULD_BLOCK; we will resume the write when there
// is more data.
rv = mInStream->Available(&avail);
if (NS_FAILED(rv)) return rv;
if (avail == 0)
{
mMsgProtocol->mSuspendedWrite = PR_TRUE;
return NS_BASE_STREAM_WOULD_BLOCK;
}
PRUint32 bytesWritten;
rv = aOutStream->WriteFrom(mInStream, PR_MIN(avail, aCount), &bytesWritten);
// if were full at the time, the input stream may be backed up and we need to read any remains from the last ODA call
// before we'll get more ODA calls
if (mMsgProtocol->mSuspendedRead)
mMsgProtocol->UnblockPostReader();
return rv;
}
protected:
nsMsgAsyncWriteProtocol * mMsgProtocol;
nsCOMPtr<nsIInputStream> mInStream;
};
NS_IMPL_THREADSAFE_ISUPPORTS2(nsMsgProtocolStreamProvider,
nsIStreamProvider,
nsIStreamObserver)
class nsMsgFilePostHelper : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMOBSERVER
NS_DECL_NSISTREAMLISTENER
nsMsgFilePostHelper() { NS_INIT_REFCNT(); mSuspendedPostFileRead = PR_FALSE;}
nsresult Init(nsIOutputStream * aOutStream, nsMsgAsyncWriteProtocol * aProtInstance, nsIFile *aFileToPost);
virtual ~nsMsgFilePostHelper() {}
nsCOMPtr<nsIRequest> mPostFileRequest;
PRBool mSuspendedPostFileRead;
protected:
nsCOMPtr<nsIOutputStream> mOutStream;
nsMsgAsyncWriteProtocol * mProtInstance;
};
NS_IMPL_THREADSAFE_ADDREF(nsMsgFilePostHelper)
NS_IMPL_THREADSAFE_RELEASE(nsMsgFilePostHelper)
NS_INTERFACE_MAP_BEGIN(nsMsgFilePostHelper)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIStreamObserver)
NS_INTERFACE_MAP_END_THREADSAFE
nsresult nsMsgFilePostHelper::Init(nsIOutputStream * aOutStream, nsMsgAsyncWriteProtocol * aProtInstance, nsIFile *aFileToPost)
{
nsresult rv = NS_OK;
mOutStream = aOutStream;
mProtInstance = aProtInstance; // mscott work out ref counting issue
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsITransport> transport;
rv = fts->CreateTransport(aFileToPost, PR_RDONLY, 0664, getter_AddRefs(transport));
if (transport)
{
rv = transport->AsyncRead(this, nsnull, 0, -1, 0, getter_AddRefs(mPostFileRequest));
}
return rv;
}
NS_IMETHODIMP nsMsgFilePostHelper::OnStartRequest(nsIRequest * aChannel, nsISupports *ctxt)
{
return NS_OK;
}
NS_IMETHODIMP nsMsgFilePostHelper::OnStopRequest(nsIRequest * aChannel, nsISupports *ctxt, nsresult aStatus, const PRUnichar* aMsg)
{
if (!mSuspendedPostFileRead)
mProtInstance->PostDataFinished();
mSuspendedPostFileRead = PR_FALSE;
mProtInstance->mFilePostHelper = nsnull;
return NS_OK;
}
NS_IMETHODIMP nsMsgFilePostHelper::OnDataAvailable(nsIRequest * /* aChannel */, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
{
if (mSuspendedPostFileRead)
{
mProtInstance->UpdateSuspendedReadBytes(count, mProtInstance->mInsertPeriodRequired);
return NS_OK;
}
mProtInstance->ProcessIncomingPostData(inStr, count);
if (mProtInstance->mSuspendedWrite)
{
// if we got here then we had suspended the write 'cause we didn't have anymore
// data to write (i.e. the pipe went empty). So resume the channel to kick
// things off again.
mProtInstance->mSuspendedWrite = PR_FALSE;
mProtInstance->m_WriteRequest->Resume();
}
return NS_OK;
}
NS_IMPL_ADDREF_INHERITED(nsMsgAsyncWriteProtocol, nsMsgProtocol)
NS_IMPL_RELEASE_INHERITED(nsMsgAsyncWriteProtocol, nsMsgProtocol)
NS_INTERFACE_MAP_BEGIN(nsMsgAsyncWriteProtocol)
NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
nsMsgAsyncWriteProtocol::nsMsgAsyncWriteProtocol(nsIURI * aURL) : nsMsgProtocol(aURL)
{
mSuspendedWrite = PR_FALSE;
mSuspendedReadBytes = 0;
mSuspendedRead = PR_FALSE;
mInsertPeriodRequired = PR_FALSE;
mSuspendedReadBytesPostPeriod = 0;
mFilePostHelper = nsnull;
}
nsMsgAsyncWriteProtocol::~nsMsgAsyncWriteProtocol()
{}
nsresult nsMsgAsyncWriteProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec)
{
// convert the file spec into a nsIFile....
nsFileSpec * spec = new nsFileSpec();
fileSpec->GetFileSpec(spec);
nsCOMPtr<nsILocalFile> file;
NS_FileSpecToIFile(spec, getter_AddRefs(file));
nsCOMPtr<nsIStreamListener> listener;
NS_NEWXPCOM(listener, nsMsgFilePostHelper);
if (!listener) return NS_ERROR_OUT_OF_MEMORY;
// be sure to initialize some state before posting
mSuspendedReadBytes = 0;
mSuspendedRead = PR_FALSE;
mInsertPeriodRequired = PR_FALSE;
mSuspendedReadBytesPostPeriod = 0;
mFilePostHelper = NS_STATIC_CAST(nsMsgFilePostHelper*,NS_STATIC_CAST(nsIStreamListener*, listener));
NS_STATIC_CAST(nsMsgFilePostHelper*,NS_STATIC_CAST(nsIStreamListener*, listener))->Init(m_outputStream, this, file);
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::SuspendPostFileRead()
{
#ifdef DEBUG_mscott
printf("suspending post read during send\n");
#endif
if (mFilePostHelper)
{
// uhoh we need to pause reading in the file until we get unblocked...
mFilePostHelper->mPostFileRequest->Suspend();
mFilePostHelper->mSuspendedPostFileRead = PR_TRUE;
}
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::ResumePostFileRead()
{
#ifdef DEBUG_mscott
printf("resuming post read during send\n");
#endif
if (mFilePostHelper)
{
if (mFilePostHelper->mSuspendedPostFileRead)
{
mFilePostHelper->mPostFileRequest->Resume();
mFilePostHelper->mSuspendedPostFileRead = PR_FALSE;
}
}
else // we must be done with the download so send the '.'
{
PostDataFinished();
}
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::UpdateSuspendedReadBytes(PRUint32 aNewBytes, PRBool aAddToPostPeriodByteCount)
{
// depending on our current state, we'll either add aNewBytes to mSuspendedReadBytes
// or mSuspendedReadBytesAfterPeriod.
mSuspendedRead = PR_TRUE;
if (aAddToPostPeriodByteCount)
mSuspendedReadBytesPostPeriod += aNewBytes;
else
mSuspendedReadBytes += aNewBytes;
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::PostDataFinished()
{
SendData(nsnull, CRLF "." CRLF);
mPostDataStream = nsnull;
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::ProcessIncomingPostData(nsIInputStream *inStr, PRUint32 count)
{
// We need to quote any '.' that occur at the beginning of a line.
// but I don't want to waste time reading out the data into a buffer and searching
// let's try to leverage nsIBufferedInputStream and see if we can "peek" into the
// current contents for this particular case.
nsCOMPtr<nsISearchableInputStream> bufferInputStr = do_QueryInterface(inStr);
NS_ASSERTION(bufferInputStr, "i made a wrong assumption about the type of stream we are getting");
NS_ASSERTION(mSuspendedReadBytes == 0, "oops, I missed something");
if (!mPostDataStream) mPostDataStream = inStr;
if (bufferInputStr)
{
PRUint32 amountWritten;
while (count > 0)
{
PRBool found = PR_FALSE;
PRUint32 offset = 0;
bufferInputStr->Search("\012.", PR_TRUE, &found, &offset); // LF.
if (!found || offset > count)
{
// push this data into the output stream
m_outputStream->WriteFrom(inStr, count, &amountWritten);
// store any remains which need read out at a later date
if (count > amountWritten) // stream will block
{
UpdateSuspendedReadBytes(count - amountWritten, PR_FALSE);
SuspendPostFileRead();
}
break;
}
else
{
// count points to the LF in a LF followed by a '.'
// go ahead and write up to offset..
m_outputStream->WriteFrom(inStr, offset + 1, &amountWritten);
count -= amountWritten;
if (offset+1 > amountWritten)
{
UpdateSuspendedReadBytes(offset+1 - amountWritten, PR_FALSE);
mInsertPeriodRequired = PR_TRUE;
UpdateSuspendedReadBytes(count, mInsertPeriodRequired);
SuspendPostFileRead();
break;
}
// write out the extra '.'
m_outputStream->Write(".", 1, &amountWritten);
if (amountWritten != 1)
{
mInsertPeriodRequired = PR_TRUE;
// once we do write out the '.', if we are now blocked we need to remember the remaining count that comes
// after the '.' so we can perform processing on that once we become unblocked.
UpdateSuspendedReadBytes(count, mInsertPeriodRequired);
SuspendPostFileRead();
}
}
} // while count > 0
}
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::UnblockPostReader()
{
PRUint32 amountWritten = 0;
if (mSuspendedRead)
{
// (1) attempt to write out any remaining read bytes we need in order to unblock the reader
if (mSuspendedReadBytes > 0 && mPostDataStream)
{
m_outputStream->WriteFrom(mPostDataStream, mSuspendedReadBytes, &amountWritten);
if (mSuspendedReadBytes > amountWritten)
mSuspendedReadBytes -= amountWritten;
else
mSuspendedReadBytes = 0;
}
// (2) if we are now unblocked, and we need to insert a '.' then do so now...
if (mInsertPeriodRequired && mSuspendedReadBytes == 0)
{
amountWritten = 0;
m_outputStream->Write(".", 1, &amountWritten);
if (amountWritten == 1) // if we succeeded then clear pending '.' flag
mInsertPeriodRequired = PR_FALSE;
}
// (3) if we inserted a '.' and we still have bytes after the '.' which need processed before the stream is unblocked
// then fake an ODA call to handle this now...
if (!mInsertPeriodRequired && mSuspendedReadBytesPostPeriod > 0)
{
// these bytes actually need processed for extra '.''s.....
PRUint32 postbytes = mSuspendedReadBytesPostPeriod;
mSuspendedReadBytesPostPeriod = 0;
ProcessIncomingPostData(mPostDataStream, postbytes);
}
// (4) determine if we are out of the suspended read state...
if (mSuspendedReadBytes == 0 && !mInsertPeriodRequired && mSuspendedReadBytesPostPeriod == 0)
{
mSuspendedRead = PR_FALSE;
ResumePostFileRead();
}
} // if we are in the suspended read state
return NS_OK;
}
nsresult nsMsgAsyncWriteProtocol::SetupTransportState()
{
nsresult rv = NS_OK;
if (!m_outputStream && m_transport)
{
// first create a pipe which we'll use to write the data we want to send
// into.
rv = NS_NewPipe(getter_AddRefs(mInStream), getter_AddRefs(m_outputStream),
1024, // segmentSize
1024*8, // maxSize
PR_TRUE,
PR_TRUE);
nsCOMPtr<nsIStreamProvider> provider;
NS_NEWXPCOM(provider, nsMsgProtocolStreamProvider);
if (!provider) return NS_ERROR_OUT_OF_MEMORY;
NS_STATIC_CAST(nsMsgProtocolStreamProvider*,
NS_STATIC_CAST(nsIStreamProvider*, provider))->Init(this, mInStream);
rv = m_transport->AsyncWrite(provider, nsnull, 0, 0, 0, getter_AddRefs(m_WriteRequest));
if (NS_FAILED(rv)) return rv;
} // if m_transport
return rv;
}
PRInt32 nsMsgAsyncWriteProtocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging)
{
PRUint32 len = nsCRT::strlen(dataBuffer);
PRUint32 cnt;
nsresult rv = m_outputStream->Write(dataBuffer, len, &cnt);
if (NS_SUCCEEDED(rv) && len==cnt)
{
if (mSuspendedWrite)
{
// if we got here then we had suspended the write 'cause we didn't have anymore
// data to write (i.e. the pipe went empty). So resume the channel to kick
// things off again.
mSuspendedWrite = PR_FALSE;
m_WriteRequest->Resume();
}
return NS_OK;
}
else
return NS_ERROR_FAILURE;
}

View File

@ -32,10 +32,12 @@
#include "nsCOMPtr.h"
#include "nsIFileSpec.h"
#include "nsIInterfaceRequestor.h"
#include "nsIStreamProvider.h"
#include "nsIProgressEventSink.h"
#include "nsITransport.h"
class nsIPrompt;
class nsIMsgMailNewsUrl;
class nsMsgFilePostHelper;
// This is a helper class used to encapsulate code shared between all of the
// mailnews protocol objects (imap, news, pop, smtp, etc.) In particular,
@ -112,12 +114,13 @@ protected:
virtual nsresult InitFromURI(nsIURI *aUrl);
// Ouput stream for writing commands to the socket
nsCOMPtr<nsIOutputStream> m_outputStream; // this will be obtained from the transport interface
// Ouput stream for writing commands to the socket
nsCOMPtr<nsITransport> m_transport;
nsCOMPtr<nsIRequest> m_request;
nsCOMPtr<nsIOutputStream> m_outputStream; // this will be obtained from the transport interface
PRBool m_socketIsOpen; // mscott: we should look into keeping this state in the nsSocketTransport...
// I'm using it to make sure I open the socket the first time a URL is loaded into the connection
PRUint32 m_flags; // used to store flag information
@ -147,4 +150,58 @@ protected:
PRBool mSuppressListenerNotifications;
};
// This is is a subclass of nsMsgProtocol extends the parent class with AsyncWrite support. Protocols like smtp
// and news want to leverage aysnc write. We don't want everyone who inherits from nsMsgProtocol to have to
// pick up the extra overhead.
class NS_MSG_BASE nsMsgAsyncWriteProtocol : public nsMsgProtocol
{
public:
NS_DECL_ISUPPORTS_INHERITED
nsMsgAsyncWriteProtocol(nsIURI * aURL);
virtual ~nsMsgAsyncWriteProtocol();
// temporary over ride...
virtual nsresult PostMessage(nsIURI* url, nsIFileSpec *fileSpec);
// over ride the following methods from the base class
virtual nsresult SetupTransportState();
virtual PRInt32 SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging = PR_FALSE);
// if we suspended the asynch write while waiting for more data to write then this will be TRUE
PRBool mSuspendedWrite;
nsCOMPtr<nsIRequest> m_WriteRequest;
// because we are reading the post data in asychronously, it's possible that we aren't sending it
// out fast enough and the reading gets blocked. The following set of state variables are used to
// track this.
PRBool mSuspendedRead;
PRBool mInsertPeriodRequired; // do we need to insert a '.' as part of the unblocking process
nsresult ProcessIncomingPostData(nsIInputStream *inStr, PRUint32 count);
nsresult UnblockPostReader();
nsresult UpdateSuspendedReadBytes(PRUint32 aNewBytes, PRBool aAddToPostPeriodByteCount);
nsresult PostDataFinished(); // this is so we'll send out a closing '.' and release any state related to the post
// these two routines are used to pause and resume our loading of the file containing the contents
// we are trying to post. We call these routines when we aren't sending the bits out fast enough
// to keep up with the file read.
nsresult SuspendPostFileRead();
nsresult ResumePostFileRead();
nsresult UpdateSuspendedReadBytes(PRUint32 aNewBytes);
nsMsgFilePostHelper * mFilePostHelper; // needs to be a weak reference
protected:
// the streams for the pipe used to queue up data for the async write calls to the server.
// we actually re-use the same mOutStream variable in our parent class for the output
// stream to the socket channel. So no need for a new variable here.
nsCOMPtr<nsIInputStream> mInStream;
nsCOMPtr<nsIInputStream> mPostDataStream;
PRUint32 mSuspendedReadBytes; // remaining # of bytes we need to read before
// the input stream becomes unblocked
PRUint32 mSuspendedReadBytesPostPeriod; // # of bytes which need processed after we insert a '.' before
// the input stream becomes unblocked.
};
#endif /* nsMsgProtocol_h__ */