зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2125ecc29a
Коммит
0b2f68bfdd
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче