diff --git a/mozilla/build/mac/build_scripts/MozillaBuildList.pm b/mozilla/build/mac/build_scripts/MozillaBuildList.pm index a7c1a8cadde..6758e434e7f 100644 --- a/mozilla/build/mac/build_scripts/MozillaBuildList.pm +++ b/mozilla/build/mac/build_scripts/MozillaBuildList.pm @@ -557,6 +557,7 @@ sub BuildClientDist() InstallFromManifest(":mozilla:uriloader:exthandler:MANIFEST_IDL", "$distdirectory:idl:"); #NETWERK + InstallFromManifest(":mozilla:netwerk:build:MANIFEST", "$distdirectory:netwerk:"); InstallFromManifest(":mozilla:netwerk:base:public:MANIFEST", "$distdirectory:netwerk:"); InstallFromManifest(":mozilla:netwerk:base:public:MANIFEST_IDL", "$distdirectory:idl:"); InstallFromManifest(":mozilla:netwerk:socket:base:MANIFEST_IDL", "$distdirectory:idl:"); diff --git a/mozilla/chrome/src/nsChromeProtocolHandler.cpp b/mozilla/chrome/src/nsChromeProtocolHandler.cpp index 4d8b47de1dd..392fb916814 100644 --- a/mozilla/chrome/src/nsChromeProtocolHandler.cpp +++ b/mozilla/chrome/src/nsChromeProtocolHandler.cpp @@ -271,7 +271,7 @@ nsCachedChromeChannel::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } NS_IMETHODIMP -nsCachedChromeChannel::AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) +nsCachedChromeChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("don't do that"); return NS_ERROR_FAILURE; diff --git a/mozilla/content/xul/document/src/nsXULDocument.cpp b/mozilla/content/xul/document/src/nsXULDocument.cpp index d236bf70026..b5418f4dc64 100644 --- a/mozilla/content/xul/document/src/nsXULDocument.cpp +++ b/mozilla/content/xul/document/src/nsXULDocument.cpp @@ -349,7 +349,7 @@ public: NS_IMETHOD OpenOutputStream(nsIOutputStream **_retval) { *_retval = nsnull; return NS_OK; } NS_IMETHOD AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; } - NS_IMETHOD AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } + NS_IMETHOD AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes) { *aLoadAttributes = nsIChannel::LOAD_NORMAL; return NS_OK; } NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes) { return NS_OK; } NS_IMETHOD GetContentType(char * *aContentType) { *aContentType = nsnull; return NS_OK; } diff --git a/mozilla/embedding/browser/webBrowser/nsWebBrowserPersist.cpp b/mozilla/embedding/browser/webBrowser/nsWebBrowserPersist.cpp index ab255d48733..0e1953382dd 100644 --- a/mozilla/embedding/browser/webBrowser/nsWebBrowserPersist.cpp +++ b/mozilla/embedding/browser/webBrowser/nsWebBrowserPersist.cpp @@ -219,7 +219,7 @@ NS_IMETHODIMP nsWebBrowserPersist::SaveURI(nsIURI *aURI, nsIInputStream *aPostDa mInputStream = inStream; // Get the output channel ready for writing - rv = outputChannel->AsyncWrite(inStream, NS_STATIC_CAST(nsIStreamObserver *, this), nsnull); + rv = NS_AsyncWriteFromStream(outputChannel, inStream, NS_STATIC_CAST(nsIStreamObserver *, this), nsnull); if (NS_FAILED(rv)) { OnEndDownload(); diff --git a/mozilla/extensions/datetime/nsDateTimeChannel.cpp b/mozilla/extensions/datetime/nsDateTimeChannel.cpp index 613bb83ed7e..082a0a6cc27 100644 --- a/mozilla/extensions/datetime/nsDateTimeChannel.cpp +++ b/mozilla/extensions/datetime/nsDateTimeChannel.cpp @@ -202,8 +202,7 @@ nsDateTimeChannel::AsyncRead(nsIStreamListener *aListener, } NS_IMETHODIMP -nsDateTimeChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsDateTimeChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("nsDateTimeChannel::AsyncWrite"); diff --git a/mozilla/extensions/finger/nsFingerChannel.cpp b/mozilla/extensions/finger/nsFingerChannel.cpp index 8c99900fdb1..f1fd991714f 100644 --- a/mozilla/extensions/finger/nsFingerChannel.cpp +++ b/mozilla/extensions/finger/nsFingerChannel.cpp @@ -32,6 +32,7 @@ #include "nsMimeTypes.h" #include "nsIStreamConverterService.h" #include "nsITXTToHTMLConv.h" +#include "nsNetUtil.h" static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); @@ -250,8 +251,7 @@ nsFingerChannel::AsyncRead(nsIStreamListener *aListener, nsISupports *ctxt) } NS_IMETHODIMP -nsFingerChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsFingerChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("nsFingerChannel::AsyncWrite"); @@ -540,7 +540,7 @@ nsFingerChannel::SendRequest(nsIChannel* aChannel) { rv = aChannel->SetTransferCount(requestBuffer.Length()); if (NS_FAILED(rv)) return rv; - rv = aChannel->AsyncWrite(charstream, this, 0); + rv = NS_AsyncWriteFromStream(aChannel, charstream, this, nsnull); return rv; } diff --git a/mozilla/extensions/xmlextras/base/src/nsDOMParser.cpp b/mozilla/extensions/xmlextras/base/src/nsDOMParser.cpp index e320b2b9206..5dbd5ecbbc8 100644 --- a/mozilla/extensions/xmlextras/base/src/nsDOMParser.cpp +++ b/mozilla/extensions/xmlextras/base/src/nsDOMParser.cpp @@ -316,8 +316,8 @@ NS_IMETHODIMP nsDOMParserChannel::AsyncRead(nsIStreamListener *listener, nsISupp return NS_ERROR_NOT_IMPLEMENTED; } -/* void asyncWrite (in nsIInputStream fromStream, in nsIStreamObserver observer, in nsISupports ctxt); */ -NS_IMETHODIMP nsDOMParserChannel::AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) +/* void asyncWrite (in nsIStreamProvider provider, in nsISupports ctxt); */ +NS_IMETHODIMP nsDOMParserChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index 58c2abeb3b6..9e4fd957de0 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -656,9 +656,8 @@ public: NS_IMETHOD SetURI(nsIURI* aURI) { gURI = aURI; NS_ADDREF(gURI); return NS_OK; } NS_IMETHOD OpenInputStream(nsIInputStream **_retval) { *_retval = nsnull; return NS_OK; } NS_IMETHOD OpenOutputStream(nsIOutputStream **_retval) { *_retval = nsnull; return NS_OK; } - NS_IMETHOD AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; } - NS_IMETHOD AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } + NS_IMETHOD AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes) { *aLoadAttributes = nsIChannel::LOAD_NORMAL; return NS_OK; } NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes) { return NS_OK; } NS_IMETHOD GetContentType(char * *aContentType) { *aContentType = nsnull; return NS_OK; } diff --git a/mozilla/layout/html/base/src/nsPresShell.cpp b/mozilla/layout/html/base/src/nsPresShell.cpp index 58c2abeb3b6..9e4fd957de0 100644 --- a/mozilla/layout/html/base/src/nsPresShell.cpp +++ b/mozilla/layout/html/base/src/nsPresShell.cpp @@ -656,9 +656,8 @@ public: NS_IMETHOD SetURI(nsIURI* aURI) { gURI = aURI; NS_ADDREF(gURI); return NS_OK; } NS_IMETHOD OpenInputStream(nsIInputStream **_retval) { *_retval = nsnull; return NS_OK; } NS_IMETHOD OpenOutputStream(nsIOutputStream **_retval) { *_retval = nsnull; return NS_OK; } - NS_IMETHOD AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; } - NS_IMETHOD AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } + NS_IMETHOD AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes) { *aLoadAttributes = nsIChannel::LOAD_NORMAL; return NS_OK; } NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes) { return NS_OK; } NS_IMETHOD GetContentType(char * *aContentType) { *aContentType = nsnull; return NS_OK; } diff --git a/mozilla/mailnews/base/util/nsMsgProtocol.cpp b/mozilla/mailnews/base/util/nsMsgProtocol.cpp index 2b1e1c93ae1..105cbbb289b 100644 --- a/mozilla/mailnews/base/util/nsMsgProtocol.cpp +++ b/mozilla/mailnews/base/util/nsMsgProtocol.cpp @@ -416,7 +416,7 @@ NS_IMETHODIMP nsMsgProtocol::AsyncRead(nsIStreamListener *listener, nsISupports return LoadUrl(m_url, nsnull); } -NS_IMETHODIMP nsMsgProtocol::AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) +NS_IMETHODIMP nsMsgProtocol::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("AsyncWrite"); return NS_ERROR_NOT_IMPLEMENTED; diff --git a/mozilla/mailnews/compose/src/nsSmtpService.cpp b/mozilla/mailnews/compose/src/nsSmtpService.cpp index 12e4e1b0975..7065f478e1c 100644 --- a/mozilla/mailnews/compose/src/nsSmtpService.cpp +++ b/mozilla/mailnews/compose/src/nsSmtpService.cpp @@ -341,7 +341,7 @@ NS_IMETHODIMP nsMailtoChannel::AsyncRead(nsIStreamListener *listener, nsISupport return listener->OnStartRequest(this, ctxt); } -NS_IMETHODIMP nsMailtoChannel::AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) +NS_IMETHODIMP nsMailtoChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("AsyncWrite"); return NS_ERROR_NOT_IMPLEMENTED; diff --git a/mozilla/mailnews/imap/src/nsImapProtocol.cpp b/mozilla/mailnews/imap/src/nsImapProtocol.cpp index a8d0263e330..721940ca446 100644 --- a/mozilla/mailnews/imap/src/nsImapProtocol.cpp +++ b/mozilla/mailnews/imap/src/nsImapProtocol.cpp @@ -7008,7 +7008,7 @@ NS_IMETHODIMP nsImapMockChannel::AsyncRead(nsIStreamListener *listener, nsISuppo return rv; } -NS_IMETHODIMP nsImapMockChannel::AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) +NS_IMETHODIMP nsImapMockChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("nsImapMockChannel::AsyncWrite"); return NS_ERROR_NOT_IMPLEMENTED; diff --git a/mozilla/modules/libjar/nsJARChannel.cpp b/mozilla/modules/libjar/nsJARChannel.cpp index d69015f2952..5ef89b162d8 100644 --- a/mozilla/modules/libjar/nsJARChannel.cpp +++ b/mozilla/modules/libjar/nsJARChannel.cpp @@ -392,8 +392,7 @@ nsJARChannel::AsyncReadJARElement() } NS_IMETHODIMP -nsJARChannel::AsyncWrite(nsIInputStream* fromStream, - nsIStreamObserver* observer, +nsJARChannel::AsyncWrite(nsIStreamProvider* provider, nsISupports* ctxt) { NS_NOTREACHED("nsJARChannel::AsyncWrite"); diff --git a/mozilla/modules/libjar/nsJARInputStream.cpp b/mozilla/modules/libjar/nsJARInputStream.cpp index a194ae3c93a..488d18f965c 100644 --- a/mozilla/modules/libjar/nsJARInputStream.cpp +++ b/mozilla/modules/libjar/nsJARInputStream.cpp @@ -28,7 +28,7 @@ * nsISupports implementation *--------------------------------------------*/ -NS_IMPL_ISUPPORTS1(nsJARInputStream, nsIInputStream); +NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream); /*---------------------------------------------------------- * nsJARInputStream implementation diff --git a/mozilla/netwerk/base/public/MANIFEST_IDL b/mozilla/netwerk/base/public/MANIFEST_IDL index 0e57b254a61..406c7ef9c60 100644 --- a/mozilla/netwerk/base/public/MANIFEST_IDL +++ b/mozilla/netwerk/base/public/MANIFEST_IDL @@ -19,6 +19,7 @@ nsIStreamListener.idl nsIStreamLoader.idl nsIDownloader.idl nsIStreamObserver.idl +nsIStreamProvider.idl nsIURI.idl nsIURL.idl nsIURLParser.idl diff --git a/mozilla/netwerk/base/public/Makefile.in b/mozilla/netwerk/base/public/Makefile.in index 9fe467fe258..362fa359718 100644 --- a/mozilla/netwerk/base/public/Makefile.in +++ b/mozilla/netwerk/base/public/Makefile.in @@ -51,10 +51,11 @@ XPIDLSRCS = \ nsISocketTransport.idl \ nsISocketTransportService.idl \ nsIStreamIO.idl \ + nsIStreamObserver.idl \ nsIStreamListener.idl \ + nsIStreamProvider.idl \ nsIStreamLoader.idl \ nsISocketTransport.idl \ - nsIStreamObserver.idl \ nsIURI.idl \ nsIURL.idl \ nsIURLParser.idl \ diff --git a/mozilla/netwerk/base/public/makefile.win b/mozilla/netwerk/base/public/makefile.win index 31566773435..460470b1066 100644 --- a/mozilla/netwerk/base/public/makefile.win +++ b/mozilla/netwerk/base/public/makefile.win @@ -54,6 +54,7 @@ XPIDLSRCS = \ .\nsISocketTransportService.idl \ .\nsIStreamIO.idl \ .\nsIStreamListener.idl \ + .\nsIStreamProvider.idl \ .\nsIStreamLoader.idl \ .\nsIDownloader.idl \ .\nsIStreamObserver.idl \ diff --git a/mozilla/netwerk/base/public/nsIChannel.idl b/mozilla/netwerk/base/public/nsIChannel.idl index bc4ea276658..bf5c6e77b33 100644 --- a/mozilla/netwerk/base/public/nsIChannel.idl +++ b/mozilla/netwerk/base/public/nsIChannel.idl @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -27,6 +27,7 @@ interface nsIInputStream; interface nsIOutputStream; interface nsIStreamObserver; interface nsIStreamListener; +interface nsIStreamProvider; interface nsILoadGroup; interface nsIInterfaceRequestor; interface nsIFile; @@ -43,7 +44,7 @@ typedef unsigned long nsLoadFlags; * request can be issued in one of several ways: * * - AsyncRead and AsyncWrite allow for asynchronous requests, calling - * back the user's stream listener or observer, + * back the user's stream listener or provider, * - OpenInputStream and OpenOutputStream allow for synchronous reads * and writes on the underlying channel. * @@ -187,7 +188,7 @@ interface nsIChannel : nsIRequest readonly attribute nsIFile localFile; /** - * Setting pipeliningAllowed causes the load of a URL (issued via asyncOpen, + * Setting pipeliningAllowed causes the load of a URL (issued via * asyncRead or asyncWrite) to be deferred in order to allow the request to * be pipelined for greater throughput efficiency. Pipelined requests will * be forced to load when the first non-pipelined request is issued. @@ -209,13 +210,16 @@ interface nsIChannel : nsIRequest */ const unsigned long LOAD_NORMAL = 0; - /** * Don't deliver status notifications to the nsIProgressEventSink, or keep * this load from completing the nsILoadGroup it may belong to: */ const unsigned long LOAD_BACKGROUND = 1 << 0; + /** + * Used exclusively by the uriloader and docshell to indicate whether or + * not this channel corresponds to the toplevel document. + */ const unsigned long LOAD_DOCUMENT_URI = 1 << 1; /** @@ -288,45 +292,35 @@ interface nsIChannel : nsIRequest //////////////////////////////////////////////////////////////////////////// /** - * Opens a blocking input stream to the URL's specified source. - * @param startPosition - The offset from the start of the data - * from which to read. - * @param readCount - The number of bytes to read. If -1, everything - * up to the end of the data is read. If greater than the end of - * the data, the amount available is returned in the stream. + * Opens a blocking input stream to the URI's specified source. This + * call may block until the input stream is available. */ nsIInputStream openInputStream(); /** - * Opens a blocking output stream to the URL's specified destination. - * @param startPosition - The offset from the start of the data - * from which to begin writing. + * Opens a blocking output stream to the URI's specified destination. This + * call may block until the output stream is available. */ nsIOutputStream openOutputStream(); /** - * Reads asynchronously from the URL's specified source. Notifications - * are provided to the stream listener on the thread of the specified - * event queue. - * The startPosition argument designates the offset in the source where - * the data will be read. - * If the readCount == -1 then all the available data is delivered to - * the stream listener. + * Reads asynchronously from the URI's specified source. Notifications + * are provided to the stream listener on the calling thread. + * + * @param listener - notification handler. + * @param ctxt - application context passed to methods invoked on listener. */ void asyncRead(in nsIStreamListener listener, in nsISupports ctxt); /** - * Writes asynchronously to the URL's specified destination. Notifications - * are provided to the stream observer on the thread of the specified - * event queue. - * The startPosition argument designates the offset in the destination where - * the data will be written. - * If the writeCount == -1, then all the available data in the input - * stream is written. + * Writes asynchronously to the URI's specified destination. Notifications + * are provided to the stream provider on the calling thread. + * + * @param provider - notification handler. + * @param ctxt - application context passed to methods invoked on provider. */ - void asyncWrite(in nsIInputStream fromStream, - in nsIStreamObserver observer, + void asyncWrite(in nsIStreamProvider provider, in nsISupports ctxt); }; diff --git a/mozilla/netwerk/base/public/nsIStreamListener.idl b/mozilla/netwerk/base/public/nsIStreamListener.idl index 55d6c5840c5..306941161d0 100644 --- a/mozilla/netwerk/base/public/nsIStreamListener.idl +++ b/mozilla/netwerk/base/public/nsIStreamListener.idl @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -22,21 +22,85 @@ #include "nsIStreamObserver.idl" -interface nsIInputStream; +interface nsIChannel; interface nsIInputStream; interface nsIOutputStream; interface nsIEventQueue; +/** + * The nsIChannel::AsyncRead notification handler. It accepts + * data from the channel, when the channel is ready to provide it. + */ [scriptable, uuid(1a637020-1482-11d3-9333-00104ba0fd40)] interface nsIStreamListener : nsIStreamObserver { + /** + * Called when there is data to be read from the channel. + * + * @param channel - the channel being read + * @param ctxt - opaque parameter passed to AsyncRead + * @param input - temporary input stream for reading data chunk + * @param offset - current stream position (informational) + * @param count - number of bytes that can be read without blocking + * + * @return NS_OK - if successfully read something. + * @return NS_BASE_STREAM_CLOSED - if done reading data. NOTE: this is + * NOT equivalent to reading zero bytes and returning NS_OK. + * @return NS_BASE_STREAM_WOULD_BLOCK - if no data can be read at + * this time. This implicitly calls Suspend on the channel. Call + * Resume on the channel to continue the AsyncRead when more data + * becomes available. + * @return - if failure. + */ void onDataAvailable(in nsIChannel channel, in nsISupports ctxt, - in nsIInputStream inStr, - in unsigned long sourceOffset, + in nsIInputStream input, + in unsigned long offset, in unsigned long count); }; +/** + * A stream listener proxy is used to ship data over to another thread specified + * by the thread's event queue. The "true" stream listener's methods are + * invoked on the other thread. + * + * This interface only provides the initialization needed after construction. + * Otherwise, these objects may be used as a nsIStreamListener. + */ +[scriptable, uuid(e400e688-6b54-4a84-8c4e-56b40281981a)] +interface nsIStreamListenerProxy : nsIStreamListener +{ + /** + * Initializes an nsIStreamListenerProxy. + * + * @param listener - receives listener notifications on the other thread + * @param eventQ - may be NULL indicating the calling thread's event queue + * @param bufferSegmentSize - passing zero indicates the default + * @param bufferMaxSize - passing zero indicates the default + */ + void init(in nsIStreamListener listener, + in nsIEventQueue eventQ, + in unsigned long bufferSegmentSize, + in unsigned long bufferMaxSize); +}; + +/** + * A simple stream listener can be used with AsyncRead to supply data to + * a output stream. + */ +[scriptable, uuid(a9b84f6a-0824-4278-bae6-bfca0570a26e)] +interface nsISimpleStreamListener : nsIStreamListener +{ + /** + * Initialize the simple stream listener. + * + * @param sink - data will be read from the channel to this output stream + * @param observer - optional stream observer (can be NULL) + */ + void init(in nsIOutputStream sink, + in nsIStreamObserver observer); +}; + /** * An asynchronous stream listener is used to ship data over to another thread specified * by the thread's event queue. The receiver stream listener is then used to receive @@ -55,42 +119,3 @@ interface nsIAsyncStreamListener : nsIStreamListener void init(in nsIStreamListener receiver, in nsIEventQueue eventQueue); }; - -/** - * A synchronous stream listener pushes data through a pipe that ends up - * in an input stream to be read by another thread. - * - * This interface only provides the initialization needed after construction. Otherwise, - * these objects are used simply as nsIStreamListener. - */ -[scriptable, uuid(1f9fb93e-91bf-11d3-8cd9-0060b0fc14a3)] -interface nsISyncStreamListener : nsIStreamListener -{ - /** - * Initializes an nsISyncStreamListener. - */ - void init(out nsIInputStream inStream, - out nsIOutputStream outStream); -}; - -%{C++ - -// Use this CID to construct an nsIAsyncStreamListener -#define NS_ASYNCSTREAMLISTENER_CID \ -{ /* 60047bb2-91c0-11d3-8cd9-0060b0fc14a3 */ \ - 0x60047bb2, \ - 0x91c0, \ - 0x11d3, \ - {0x8c, 0xd9, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ -} - -// Use this CID to construct an nsISyncStreamListener -#define NS_SYNCSTREAMLISTENER_CID \ -{ /* 65fa5cb2-91c0-11d3-8cd9-0060b0fc14a3 */ \ - 0x65fa5cb2, \ - 0x91c0, \ - 0x11d3, \ - {0x8c, 0xd9, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ -} - -%} diff --git a/mozilla/netwerk/base/public/nsIStreamObserver.idl b/mozilla/netwerk/base/public/nsIStreamObserver.idl index bccbae7c720..f20f5365402 100644 --- a/mozilla/netwerk/base/public/nsIStreamObserver.idl +++ b/mozilla/netwerk/base/public/nsIStreamObserver.idl @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -22,27 +22,55 @@ #include "nsISupports.idl" -interface nsIEventQueue; interface nsIChannel; +interface nsIEventQueue; [scriptable, uuid(fd91e2e0-1481-11d3-9333-00104ba0fd40)] interface nsIStreamObserver : nsISupports { /** - * Called to signify the beginning of an asyncronous request. + * Called to signify the beginning of an asynchronous request. + * + * @param channel - channel being observed + * @param ctxt - user specified data passed to AsyncRead/Write */ void onStartRequest(in nsIChannel channel, in nsISupports ctxt); /** - * Called to signify the end of an asyncronous request. - * @param notif - a notification object containing any error code and error parameters - * (may be null if the notification status is NS_OK) + * Called to signify the end of an asynchronous request. This + * call is always preceded by a call to onStartRequest. + * + * @param channel - channel being observed + * @param ctxt - user specified data passed to AsyncRead/Write + * @param statusCode - reason for stopping (NS_OK if completed successfully) + * @param statusText - human readable reason for stopping (can be NULL) */ void onStopRequest(in nsIChannel channel, in nsISupports ctxt, - in nsresult status, - in wstring statusArg); + in nsresult statusCode, + in wstring statusText); +}; + +/** + * A stream observer proxy is used to ship data over to another thread specified + * by the thread's event queue. The "true" stream observer's methods are + * invoked on the other thread. + * + * This interface only provides the initialization needed after construction. Otherwise, + * these objects are used simply as nsIStreamObserver's. + */ +[scriptable, uuid(3c9b532e-db84-4ecf-aa6a-4d38a9c4c5f0)] +interface nsIStreamObserverProxy : nsIStreamObserver +{ + /** + * Initializes an nsIStreamObserverProxy. + * + * @param observer - receives observer notifications on the other thread + * @param eventQ - may be NULL indicating the calling thread's event queue + */ + void init(in nsIStreamObserver observer, + in nsIEventQueue eventQ); }; /** @@ -65,22 +93,8 @@ interface nsIAsyncStreamObserver : nsIStreamObserver }; %{C++ - -// Use this CID to construct an nsIAsyncStreamObserver -#define NS_ASYNCSTREAMOBSERVER_CID \ -{ /* fcc7c380-91b3-11d3-8cd9-0060b0fc14a3 */ \ - 0xfcc7c380, \ - 0x91b3, \ - 0x11d3, \ - {0x8c, 0xd9, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ -} - -//////////////////////////////////////////////////////////////////////////////// // Generic status codes for OnStopRequest: - -#define NS_BINDING_SUCCEEDED NS_OK -#define NS_BINDING_FAILED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 1) -#define NS_BINDING_ABORTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 2) - +#define NS_BINDING_SUCCEEDED NS_OK +#define NS_BINDING_FAILED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 1) +#define NS_BINDING_ABORTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 2) %} - diff --git a/mozilla/netwerk/base/public/nsIStreamProvider.idl b/mozilla/netwerk/base/public/nsIStreamProvider.idl new file mode 100644 index 00000000000..41c3008ea7b --- /dev/null +++ b/mozilla/netwerk/base/public/nsIStreamProvider.idl @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsIStreamObserver.idl" + +interface nsIChannel; +interface nsIInputStream; +interface nsIOutputStream; +interface nsIEventQueue; + +/** + * The nsIChannel::AsyncWrite notification handler. It provides + * data to the channel, when the channel is ready to accept it. + */ +[scriptable, uuid(d10ef7a9-b728-43d4-9c49-74172186d691)] +interface nsIStreamProvider : nsIStreamObserver +{ + /** + * Called when data may be written to the channel. + * + * @param channel - the channel being written to + * @param ctxt - opaque parameter passed to AsyncWrite + * @param output - output stream for writing data chunk + * @param offset - current stream position (informational) + * @param count - number of bytes that can be written without blocking + * + * @return NS_OK - if successfully wrote something. + * @return NS_BASE_STREAM_CLOSED - if done writing data. NOTE: this is + * NOT equivalent to writing zero bytes and returning NS_OK. + * @return NS_BASE_STREAM_WOULD_BLOCK - if no data can be written at + * this time. This implicitly calls Suspend on the channel. Call + * Resume on the channel to continue the AsyncWrite when more data + * becomes available. + * @return - if failure. + */ + void onDataWritable(in nsIChannel channel, + in nsISupports ctxt, + in nsIOutputStream output, + in unsigned long offset, + in unsigned long count); +}; + +/** + * A stream provider proxy is used to ship data over to another thread specified + * by the thread's event queue. The "true" stream provider's methods are + * invoked on the other thread. + * + * This interface only provides the initialization needed after construction. + * Otherwise, these objects may be used as a nsIStreamProvider. + */ +[scriptable, uuid(5c3b0bac-605a-49ac-880e-5c8b993f7d2b)] +interface nsIStreamProviderProxy : nsIStreamProvider +{ + /** + * Initializes an nsIStreamProviderProxy. + * + * @param provider - receives provider notifications on the other thread. + * @param eventQ - may be NULL indicating the calling thread's event queue. + */ + void init(in nsIStreamProvider provider, + in nsIEventQueue eventQ, + in unsigned long bufferSegmentSize, + in unsigned long bufferMaxSize); +}; + +/** + * A simple stream provider can be used with AsyncWrite to supply data from + * an existing input stream. + */ +[scriptable, uuid(c20bb3b9-0755-4eff-9222-3537f9e89082)] +interface nsISimpleStreamProvider : nsIStreamProvider +{ + /** + * Initialize the simple stream provider. + * + * @param - data will be read from this input stream to the channel + * @param - optional stream observer (can be NULL) + */ + void init(in nsIInputStream source, + in nsIStreamObserver observer); +}; diff --git a/mozilla/netwerk/base/public/nsNetUtil.h b/mozilla/netwerk/base/public/nsNetUtil.h index d4bb69bf74f..19ef0a78e8c 100644 --- a/mozilla/netwerk/base/public/nsNetUtil.h +++ b/mozilla/netwerk/base/public/nsNetUtil.h @@ -26,7 +26,9 @@ #include "nsIURI.h" #include "netCore.h" #include "nsIInputStream.h" +#include "nsIOutputStream.h" #include "nsIStreamListener.h" +#include "nsIStreamProvider.h" #include "nsILoadGroup.h" #include "nsIInterfaceRequestor.h" #include "nsString.h" @@ -40,9 +42,12 @@ #include "nsIDownloader.h" #include "nsIStreamLoader.h" #include "nsIStreamIO.h" +#include "nsIPipe.h" #include "nsXPIDLString.h" #include "prio.h" // for read/write flags, permissions, etc. +#include "nsNetCID.h" + // Helper, to simplify getting the I/O service. inline const nsGetServiceByCID do_GetIOService(nsresult* error = 0) @@ -368,6 +373,139 @@ NS_NewStreamLoader(nsIStreamLoader* *result, return rv; } +inline nsresult +NS_NewStreamObserverProxy(nsIStreamObserver **aResult, + nsIStreamObserver *aObserver, + nsIEventQueue *aEventQ=nsnull) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsCOMPtr proxy; + static NS_DEFINE_CID(kStreamObserverProxyCID, NS_STREAMOBSERVERPROXY_CID); + + rv = nsComponentManager::CreateInstance(kStreamObserverProxyCID, + nsnull, + NS_GET_IID(nsIStreamObserverProxy), + getter_AddRefs(proxy)); + if (NS_FAILED(rv)) return rv; + + rv = proxy->Init(aObserver, aEventQ); + if (NS_FAILED(rv)) return rv; + + *aResult = proxy; + NS_ADDREF(*aResult); + + return NS_OK; +} + +inline nsresult +NS_NewStreamListenerProxy(nsIStreamListener **aResult, + nsIStreamListener *aListener, + nsIEventQueue *aEventQ=nsnull, + PRUint32 aBufferSegmentSize=0, + PRUint32 aBufferMaxSize=0) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsCOMPtr proxy; + static NS_DEFINE_CID(kStreamListenerProxyCID, NS_STREAMLISTENERPROXY_CID); + + rv = nsComponentManager::CreateInstance(kStreamListenerProxyCID, + nsnull, + NS_GET_IID(nsIStreamListenerProxy), + getter_AddRefs(proxy)); + if (NS_FAILED(rv)) return rv; + + rv = proxy->Init(aListener, aEventQ, aBufferSegmentSize, aBufferMaxSize); + if (NS_FAILED(rv)) return rv; + + *aResult = proxy; + NS_ADDREF(*aResult); + + return NS_OK; +} + +inline nsresult +NS_NewStreamProviderProxy(nsIStreamProvider **aResult, + nsIStreamProvider *aProvider, + nsIEventQueue *aEventQ=nsnull, + PRUint32 aBufferSegmentSize=0, + PRUint32 aBufferMaxSize=0) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsCOMPtr proxy; + static NS_DEFINE_CID(kStreamProviderProxyCID, NS_STREAMPROVIDERPROXY_CID); + + rv = nsComponentManager::CreateInstance(kStreamProviderProxyCID, + nsnull, + NS_GET_IID(nsIStreamProviderProxy), + getter_AddRefs(proxy)); + if (NS_FAILED(rv)) return rv; + + rv = proxy->Init(aProvider, aEventQ, aBufferSegmentSize, aBufferMaxSize); + if (NS_FAILED(rv)) return rv; + + *aResult = proxy; + NS_ADDREF(*aResult); + + return NS_OK; +} + +inline nsresult +NS_NewSimpleStreamListener(nsIStreamListener **aResult, + nsIOutputStream *aSink, + nsIStreamObserver *aObserver=nsnull) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsCOMPtr listener; + static NS_DEFINE_CID(kSimpleStreamListenerCID, NS_SIMPLESTREAMLISTENER_CID); + rv = nsComponentManager::CreateInstance(kSimpleStreamListenerCID, + nsnull, + NS_GET_IID(nsISimpleStreamListener), + getter_AddRefs(listener)); + if (NS_FAILED(rv)) return rv; + + rv = listener->Init(aSink, aObserver); + if (NS_FAILED(rv)) return rv; + + *aResult = listener.get(); + NS_ADDREF(*aResult); + + return NS_OK; +} + +inline nsresult +NS_NewSimpleStreamProvider(nsIStreamProvider **aResult, + nsIInputStream *aSource, + nsIStreamObserver *aObserver=nsnull) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsCOMPtr provider; + static NS_DEFINE_CID(kSimpleStreamProviderCID, NS_SIMPLESTREAMPROVIDER_CID); + rv = nsComponentManager::CreateInstance(kSimpleStreamProviderCID, + nsnull, + NS_GET_IID(nsISimpleStreamProvider), + getter_AddRefs(provider)); + if (NS_FAILED(rv)) return rv; + + rv = provider->Init(aSource, aObserver); + if (NS_FAILED(rv)) return rv; + + *aResult = provider.get(); + NS_ADDREF(*aResult); + + return NS_OK; +} + +// Depracated, prefer NS_NewStreamObserverProxy inline nsresult NS_NewAsyncStreamObserver(nsIStreamObserver **result, nsIStreamObserver *receiver, @@ -389,6 +527,7 @@ NS_NewAsyncStreamObserver(nsIStreamObserver **result, return NS_OK; } +// Depracated, prefer NS_NewStreamListenerProxy inline nsresult NS_NewAsyncStreamListener(nsIStreamListener **result, nsIStreamListener *receiver, @@ -410,25 +549,79 @@ NS_NewAsyncStreamListener(nsIStreamListener **result, return NS_OK; } +// Depracated, prefer a true synchonous implementation inline nsresult -NS_NewSyncStreamListener(nsIInputStream **inStream, - nsIOutputStream **outStream, - nsIStreamListener **listener) +NS_NewSyncStreamListener(nsIInputStream **aInStream, + nsIOutputStream **aOutStream, + nsIStreamListener **aResult) { nsresult rv; - nsCOMPtr lsnr; - static NS_DEFINE_CID(kSyncStreamListenerCID, NS_SYNCSTREAMLISTENER_CID); - rv = nsComponentManager::CreateInstance(kSyncStreamListenerCID, - nsnull, - NS_GET_IID(nsISyncStreamListener), - getter_AddRefs(lsnr)); - if (NS_FAILED(rv)) return rv; - rv = lsnr->Init(inStream, outStream); + + NS_ENSURE_ARG_POINTER(aInStream); + NS_ENSURE_ARG_POINTER(aOutStream); + + nsCOMPtr pipeIn; + nsCOMPtr pipeOut; + + rv = NS_NewPipe(getter_AddRefs(pipeIn), + getter_AddRefs(pipeOut), + 4*1024, // NS_SYNC_STREAM_LISTENER_SEGMENT_SIZE + 32*1024); // NS_SYNC_STREAM_LISTENER_BUFFER_SIZE if (NS_FAILED(rv)) return rv; - *listener = lsnr; - NS_ADDREF(*listener); + rv = NS_NewSimpleStreamListener(aResult, pipeOut); + if (NS_FAILED(rv)) return rv; + + *aInStream = pipeIn; + NS_ADDREF(*aInStream); + *aOutStream = pipeOut; + NS_ADDREF(*aOutStream); + return NS_OK; } +// +// Calls AsyncWrite on the specified channel, with a stream provider that +// reads data from the specified input stream. +// +inline nsresult +NS_AsyncWriteFromStream(nsIChannel *aChannel, + nsIInputStream *aSource, + nsIStreamObserver *aObserver=NULL, + nsISupports *aContext=NULL) +{ + NS_ENSURE_ARG_POINTER(aChannel); + + nsresult rv; + nsCOMPtr provider; + rv = NS_NewSimpleStreamProvider(getter_AddRefs(provider), + aSource, + aObserver); + if (NS_FAILED(rv)) return rv; + + return aChannel->AsyncWrite(provider, aContext); +} + +// +// Calls AsyncRead on the specified channel, with a stream listener that +// writes data to the specified output stream. +// +inline nsresult +NS_AsyncReadToStream(nsIChannel *aChannel, + nsIOutputStream *aSink, + nsIStreamObserver *aObserver=NULL, + nsISupports *aContext=NULL) +{ + NS_ENSURE_ARG_POINTER(aChannel); + + nsresult rv; + nsCOMPtr listener; + rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), + aSink, + aObserver); + if (NS_FAILED(rv)) return rv; + + return aChannel->AsyncRead(listener, aContext); +} + #endif // nsNetUtil_h__ diff --git a/mozilla/netwerk/base/src/Makefile.in b/mozilla/netwerk/base/src/Makefile.in index 63e89597058..6c80807afda 100644 --- a/mozilla/netwerk/base/src/Makefile.in +++ b/mozilla/netwerk/base/src/Makefile.in @@ -34,13 +34,16 @@ CPPSRCS = \ nsURLHelper.cpp \ nsFileStreams.cpp \ nsBufferedStreams.cpp \ - nsAsyncStreamListener.cpp \ - nsSyncStreamListener.cpp \ nsIOService.cpp \ nsSocketTransport.cpp \ nsSocketTransportService.cpp \ nsFileTransport.cpp \ nsFileTransportService.cpp \ + nsSimpleStreamListener.cpp \ + nsSimpleStreamProvider.cpp \ + nsStreamObserverProxy.cpp \ + nsStreamListenerProxy.cpp \ + nsStreamProviderProxy.cpp \ nsStdURLParser.cpp \ nsAuthURLParser.cpp \ nsNoAuthURLParser.cpp \ @@ -55,6 +58,7 @@ CPPSRCS = \ nsDownloader.cpp \ nsProtocolProxyService.cpp \ nsProxyAutoConfigUtils.cpp \ + nsAsyncStreamListener.cpp \ $(NULL) # we don't want the shared lib, but we want to force the creation of a diff --git a/mozilla/netwerk/base/src/makefile.win b/mozilla/netwerk/base/src/makefile.win index dcadfa75060..7bfaf200062 100644 --- a/mozilla/netwerk/base/src/makefile.win +++ b/mozilla/netwerk/base/src/makefile.win @@ -30,13 +30,16 @@ CPP_OBJS = \ .\$(OBJDIR)\nsURLHelper.obj \ .\$(OBJDIR)\nsFileStreams.obj \ .\$(OBJDIR)\nsBufferedStreams.obj \ - .\$(OBJDIR)\nsAsyncStreamListener.obj \ - .\$(OBJDIR)\nsSyncStreamListener.obj \ .\$(OBJDIR)\nsIOService.obj \ .\$(OBJDIR)\nsSocketTransport.obj \ .\$(OBJDIR)\nsSocketTransportService.obj \ .\$(OBJDIR)\nsFileTransport.obj \ .\$(OBJDIR)\nsFileTransportService.obj \ + .\$(OBJDIR)\nsSimpleStreamListener.obj \ + .\$(OBJDIR)\nsSimpleStreamProvider.obj \ + .\$(OBJDIR)\nsStreamObserverProxy.obj \ + .\$(OBJDIR)\nsStreamListenerProxy.obj \ + .\$(OBJDIR)\nsStreamProviderProxy.obj \ .\$(OBJDIR)\nsStdURLParser.obj \ .\$(OBJDIR)\nsAuthURLParser.obj \ .\$(OBJDIR)\nsNoAuthURLParser.obj \ diff --git a/mozilla/netwerk/base/src/nsAsyncStreamListener.cpp b/mozilla/netwerk/base/src/nsAsyncStreamListener.cpp index 01878cd6b32..f5f08f4a532 100644 --- a/mozilla/netwerk/base/src/nsAsyncStreamListener.cpp +++ b/mozilla/netwerk/base/src/nsAsyncStreamListener.cpp @@ -36,6 +36,12 @@ static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID); PRLogModuleInfo* gStreamEventLog = 0; #endif +// prevent name conflicts +#define nsStreamListenerEvent nsStreamListenerEvent0 +#define nsOnStartRequestEvent nsOnStartRequestEvent0 +#define nsOnStopRequestEvent nsOnStopRequestEvent0 +#define nsOnDataAvailableEvent nsOnDataAvailableEvent0 + //////////////////////////////////////////////////////////////////////////////// class nsStreamListenerEvent diff --git a/mozilla/netwerk/base/src/nsFileTransport.cpp b/mozilla/netwerk/base/src/nsFileTransport.cpp index b313bf5db83..e54be33e277 100644 --- a/mozilla/netwerk/base/src/nsFileTransport.cpp +++ b/mozilla/netwerk/base/src/nsFileTransport.cpp @@ -35,23 +35,164 @@ static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); #define NS_OUTPUT_STREAM_BUFFER_SIZE (64 * 1024) -#ifdef PR_LOGGING -// -// Log module for nsFileTransport logging... -// -// To enable logging (see prlog.h for full details): -// -// set NSPR_LOG_MODULES=nsFileTransport:5 -// set NSPR_LOG_FILE=nspr.log -// -// this enables PR_LOG_DEBUG level information and places all output in -// the file nspr.log -// -PRLogModuleInfo* gFileTransportLog = nsnull; +////////////////////////////////////////////////////////////////////////////////// -#endif /* PR_LOGGING */ +#if defined(PR_LOGGING) +static PRLogModuleInfo *gFileTransportLog = nsnull; +#endif -//////////////////////////////////////////////////////////////////////////////// +#define LOG(args) PR_LOG(gFileTransportLog, PR_LOG_DEBUG, args) + +////////////////////////////////////////////////////////////////////////////////// + +// +// An nsFileTransportSourceWrapper captures the number of bytes read from an +// input stream. +// +class nsFileTransportSourceWrapper : public nsIInputStream +{ +public: + NS_DECL_ISUPPORTS + + nsFileTransportSourceWrapper() : mBytesRead(0) {NS_INIT_ISUPPORTS();} + virtual ~nsFileTransportSourceWrapper() {} + + // + // nsIInputStream implementation... + // + NS_IMETHOD Close() { + return mSource->Close(); + } + NS_IMETHOD Available(PRUint32 *aCount) { + return mSource->Available(aCount); + } + NS_IMETHOD Read(char *aBuf, PRUint32 aCount, PRUint32 *aBytesRead) { + nsresult rv = mSource->Read(aBuf, aCount, aBytesRead); + if (NS_SUCCEEDED(rv)) + mBytesRead += *aBytesRead; + return rv; + } + NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, + PRUint32 aCount, PRUint32 *aBytesRead) { + nsresult rv = mSource->ReadSegments(aWriter, aClosure, aCount, aBytesRead); + if (NS_SUCCEEDED(rv)) + mBytesRead += *aBytesRead; + return rv; + } + NS_IMETHOD GetNonBlocking(PRBool *aValue) { + return mSource->GetNonBlocking(aValue); + } + NS_IMETHOD GetObserver(nsIInputStreamObserver **aObserver) { + return mSource->GetObserver(aObserver); + } + NS_IMETHOD SetObserver(nsIInputStreamObserver *aObserver) { + return mSource->SetObserver(aObserver); + } + + // + // Helper functions + // + void SetSource(nsIInputStream *aSource) { + mSource = aSource; + } + PRUint32 GetBytesRead() { + return mBytesRead; + } + void ZeroBytesRead() { + mBytesRead = 0; + } + +protected: + // + // State variables + // + PRUint32 mBytesRead; + nsCOMPtr mSource; +}; + +// This must be threadsafe since different threads can run the same transport +NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileTransportSourceWrapper, nsIInputStream) + +////////////////////////////////////////////////////////////////////////////////// + +// +// An nsFileTransportSinkWrapper captures the number of bytes written to an +// output stream. +// +class nsFileTransportSinkWrapper : public nsIOutputStream +{ +public: + NS_DECL_ISUPPORTS + + nsFileTransportSinkWrapper() : mBytesWritten(0) {NS_INIT_ISUPPORTS();} + virtual ~nsFileTransportSinkWrapper() {} + + // + // nsIInputStream implementation... + // + NS_IMETHOD Close() { + return mSink->Close(); + } + NS_IMETHOD Flush() { + return mSink->Flush(); + } + NS_IMETHOD Write(const char *aBuf, PRUint32 aCount, PRUint32 *aBytesWritten) { + nsresult rv = mSink->Write(aBuf, aCount, aBytesWritten); + if (NS_SUCCEEDED(rv)) + mBytesWritten += *aBytesWritten; + return rv; + } + NS_IMETHOD WriteFrom(nsIInputStream *aSource, PRUint32 aCount, PRUint32 *aBytesWritten) { + nsresult rv = mSink->WriteFrom(aSource, aCount, aBytesWritten); + if (NS_SUCCEEDED(rv)) + mBytesWritten += *aBytesWritten; + return rv; + } + NS_IMETHOD WriteSegments(nsReadSegmentFun aReader, void *aClosure, + PRUint32 aCount, PRUint32 *aBytesWritten) { + nsresult rv = mSink->WriteSegments(aReader, aClosure, aCount, aBytesWritten); + if (NS_SUCCEEDED(rv)) + mBytesWritten += *aBytesWritten; + return rv; + } + NS_IMETHOD GetNonBlocking(PRBool *aValue) { + return mSink->GetNonBlocking(aValue); + } + NS_IMETHOD SetNonBlocking(PRBool aValue) { + return mSink->SetNonBlocking(aValue); + } + NS_IMETHOD GetObserver(nsIOutputStreamObserver **aObserver) { + return mSink->GetObserver(aObserver); + } + NS_IMETHOD SetObserver(nsIOutputStreamObserver *aObserver) { + return mSink->SetObserver(aObserver); + } + + // + // Helper functions + // + void SetSink(nsIOutputStream *aSink) { + mSink = aSink; + } + PRUint32 GetBytesWritten() { + return mBytesWritten; + } + void ZeroBytesWritten() { + mBytesWritten = 0; + } + +protected: + // + // State variables + // + PRUint32 mBytesWritten; + nsCOMPtr mSink; +}; + +// This must be threadsafe since different threads can run the same transport +NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileTransportSinkWrapper, nsIOutputStream) + +////////////////////////////////////////////////////////////////////////////////// nsFileTransport::nsFileTransport() : mContentType(nsnull), @@ -60,26 +201,24 @@ nsFileTransport::nsFileTransport() mXferState(CLOSED), mRunState(RUNNING), mCancelStatus(NS_OK), - mMonitor(nsnull), + mSuspendCount(0), + mLock(nsnull), + mActive(PR_FALSE), mStatus(NS_OK), mOffset(0), mTotalAmount(-1), mTransferAmount(-1), mLoadAttributes(LOAD_NORMAL), - mBuffer(nsnull), + mSourceWrapper(nsnull), + mSinkWrapper(nsnull), mService(nsnull) { - NS_INIT_REFCNT(); + NS_INIT_ISUPPORTS(); -#ifdef PR_LOGGING - // - // Initialize the global PRLogModule for socket transport logging - // if necessary... - // - if (nsnull == gFileTransportLog) { +#if defined(PR_LOGGING) + if (!gFileTransportLog) gFileTransportLog = PR_NewLogModule("nsFileTransport"); - } -#endif /* PR_LOGGING */ +#endif } nsresult @@ -109,9 +248,9 @@ nsresult nsFileTransport::Init(nsFileTransportService *aService, nsIStreamIO* io) { nsresult rv = NS_OK; - if (mMonitor == nsnull) { - mMonitor = nsAutoMonitor::NewMonitor("nsFileTransport"); - if (mMonitor == nsnull) + if (mLock == nsnull) { + mLock = PR_NewLock(); + if (mLock == nsnull) return NS_ERROR_OUT_OF_MEMORY; } mStreamIO = io; @@ -128,29 +267,26 @@ nsFileTransport::Init(nsFileTransportService *aService, nsIStreamIO* io) nsFileTransport::~nsFileTransport() { - if (mXferState != CLOSED) { + if (mXferState != CLOSED) DoClose(); - } + NS_ASSERTION(mSource == nsnull, "transport not closed"); - NS_ASSERTION(mInputStream == nsnull, "transport not closed"); - NS_ASSERTION(mOutputStream == nsnull, "transport not closed"); + NS_ASSERTION(mSourceWrapper == nsnull, "transport not closed"); NS_ASSERTION(mSink == nsnull, "transport not closed"); - NS_ASSERTION(mBuffer == nsnull, "transport not closed"); - if (mMonitor) - nsAutoMonitor::DestroyMonitor(mMonitor); + NS_ASSERTION(mSinkWrapper == nsnull, "transport not closed"); + + if (mLock) + PR_DestroyLock(mLock); if (mContentType) nsCRT::free(mContentType); PR_AtomicDecrement(&mService->mTotalTransports); - } -NS_IMPL_THREADSAFE_ISUPPORTS5(nsFileTransport, +NS_IMPL_THREADSAFE_ISUPPORTS3(nsFileTransport, nsIChannel, nsIRequest, - nsIRunnable, - nsIInputStreamObserver, - nsIOutputStreamObserver); + nsIRunnable) NS_METHOD nsFileTransport::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) @@ -194,53 +330,58 @@ nsFileTransport::GetStatus(nsresult *status) NS_IMETHODIMP nsFileTransport::Cancel(nsresult status) { - nsAutoMonitor mon(mMonitor); + nsresult rv = NS_OK; + + nsAutoLock lock(mLock); NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); - nsresult rv = NS_OK; - if (mRunState == SUSPENDED) { - rv = Resume(); + mCancelStatus = status; + + // Only dispatch a new thread, if there isn't one currently active. + if (!mActive) { +#ifdef TIMING + mStartTime = PR_IntervalNow(); +#endif + rv = mService->DispatchRequest(this); } - if (NS_SUCCEEDED(rv)) { - // if there's no other error pending, say that we aborted - mRunState = CANCELED; - mCancelStatus = status; - } - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: Cancel [this=%x %s]", - this, mStreamName.GetBuffer())); + + LOG(("nsFileTransport: Cancel [this=%x %s]\n", this, mStreamName.GetBuffer())); return rv; } NS_IMETHODIMP nsFileTransport::Suspend() { - nsAutoMonitor mon(mMonitor); - nsresult rv = NS_OK; - if (mRunState != SUSPENDED) { - // XXX close the stream here? - mRunState = SUSPENDED; - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: Suspend [this=%x %s]", - this, mStreamName.GetBuffer())); + nsAutoLock lock(mLock); + if (mRunState != CANCELED) { + LOG(("nsFileTransport: Suspend [this=%x %s]\n", this, mStreamName.GetBuffer())); + PR_AtomicIncrement(&mSuspendCount); } - return rv; + return NS_OK; } NS_IMETHODIMP nsFileTransport::Resume() { - nsAutoMonitor mon(mMonitor); - nsresult rv = NS_OK; - if (mRunState == SUSPENDED) { - // XXX re-open the stream and seek here? - mRunState = RUNNING; // set this first before resuming! - mStatus = mService->DispatchRequest(this); - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: Resume [this=%x %s] status=%x", - this, mStreamName.GetBuffer(), mStatus)); + nsAutoLock lock(mLock); + if (mRunState != CANCELED) { + LOG(("nsFileTransport: Resume [this=%x %s]\n", this, mStreamName.GetBuffer())); + // Allow negative suspend count + PR_AtomicDecrement(&mSuspendCount); + + // Only dispatch a new thread, if there isn't one currently active. + if (!mActive && (mSuspendCount == 0)) { + mRunState = RUNNING; +#ifdef TIMING + mStartTime = PR_IntervalNow(); +#endif + mStatus = mService->DispatchRequest(this); + } } - return rv; + else + LOG(("nsFileTransport: Resume ignored [this=%x %s] status=%x cancelstatus=%x\n", + this, mStreamName.GetBuffer(), mStatus, mCancelStatus)); + return NS_OK; } //////////////////////////////////////////////////////////////////////////////// @@ -281,29 +422,22 @@ nsFileTransport::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) return NS_ERROR_IN_PROGRESS; NS_ASSERTION(listener, "need to supply an nsIStreamListener"); - rv = NS_NewAsyncStreamListener(getter_AddRefs(mListener), - listener, nsnull); - if (NS_FAILED(rv)) return rv; - - rv = NS_NewPipe(getter_AddRefs(mInputStream), - getter_AddRefs(mOutputStream), - mBufferSegmentSize, mBufferMaxSize, - PR_TRUE, PR_TRUE); - if (NS_FAILED(rv)) return rv; - - rv = mInputStream->SetObserver(this); - if (NS_FAILED(rv)) return rv; - rv = mOutputStream->SetObserver(this); + rv = NS_NewStreamListenerProxy(getter_AddRefs(mListener), + listener, nsnull, + mBufferSegmentSize, + mBufferMaxSize); if (NS_FAILED(rv)) return rv; NS_ASSERTION(mContext == nsnull, "context not released"); mContext = ctxt; mXferState = OPEN_FOR_READ; - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: AsyncRead [this=%x %s] mOffset=%d mTransferAmount=%d", - this, mStreamName.GetBuffer(), mOffset, mTransferAmount)); + LOG(("nsFileTransport: AsyncRead [this=%x %s] mOffset=%d mTransferAmount=%d\n", + this, mStreamName.GetBuffer(), mOffset, mTransferAmount)); +#ifdef TIMING + mStartTime = PR_IntervalNow(); +#endif rv = mService->DispatchRequest(this); if (NS_FAILED(rv)) return rv; @@ -311,30 +445,31 @@ nsFileTransport::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } NS_IMETHODIMP -nsFileTransport::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsFileTransport::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { nsresult rv = NS_OK; + LOG(("nsFileTransport: AsyncWrite [this=%x, provider=%x]\n", + this, provider)); + if (mXferState != CLOSED) return NS_ERROR_IN_PROGRESS; - if (observer) { - rv = NS_NewAsyncStreamObserver(getter_AddRefs(mObserver), - observer, NS_CURRENT_EVENTQ); - if (NS_FAILED(rv)) return rv; - } + NS_ASSERTION(provider, "need to supply an nsIStreamProvider"); + rv = NS_NewStreamProviderProxy(getter_AddRefs(mProvider), provider); + if (NS_FAILED(rv)) return rv; NS_ASSERTION(mContext == nsnull, "context not released"); mContext = ctxt; mXferState = OPEN_FOR_WRITE; - mSource = fromStream; - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: AsyncWrite [this=%x %s]", - this, mStreamName.GetBuffer())); + LOG(("nsFileTransport: AsyncWrite [this=%x %s] mOffset=%d mTransferAmount=%d\n", + this, mStreamName.GetBuffer(), mOffset, mTransferAmount)); +#ifdef TIMING + mStartTime = PR_IntervalNow(); +#endif rv = mService->DispatchRequest(this); if (NS_FAILED(rv)) return rv; @@ -348,7 +483,20 @@ nsFileTransport::AsyncWrite(nsIInputStream *fromStream, NS_IMETHODIMP nsFileTransport::Run(void) { + PR_Lock(mLock); + mActive = PR_TRUE; + + LOG(("nsFileTransport: Inside Run\n")); + +#ifdef TIMING + PRIntervalTime now = PR_IntervalNow(); + printf("nsFileTransport: latency=%u ticks\n", now - mStartTime); +#endif + while (mXferState != CLOSED && mRunState != SUSPENDED) { + // + // Change transfer state if canceled. + // if (mRunState == CANCELED) { if (mXferState == READING) mXferState = END_READ; @@ -358,33 +506,44 @@ nsFileTransport::Run(void) mXferState = CLOSING; mStatus = mCancelStatus; } + // + // While processing, we allow Suspend, Resume, and Cancel. + // + PR_Unlock(mLock); Process(); - } - return NS_OK; -} + PR_Lock(mLock); -static NS_METHOD -nsWriteToFile(nsIInputStream* in, - void* closure, - const char* fromRawSegment, - PRUint32 toOffset, - PRUint32 count, - PRUint32 *writeCount) -{ - nsIOutputStream* outStr = (nsIOutputStream*)closure; - nsresult rv = outStr->Write(fromRawSegment, count, writeCount); - return rv; + // + // Were we canceled ? + // + if (NS_FAILED(mCancelStatus)) + mRunState = CANCELED; + // + // Were we suspended ? + // + else if (mSuspendCount > 0) + mRunState = SUSPENDED; + } + + LOG(("nsFileTransport: Leaving Run [xferState=%d runState=%d]\n", + mXferState, mRunState)); + + mActive = PR_FALSE; + PR_Unlock(mLock); + return NS_OK; } void nsFileTransport::Process(void) { + LOG(("nsFileTransport: Inside Process [this=%x state=%x status=%x]\n", + this, mXferState, mStatus)); + switch (mXferState) { case OPEN_FOR_READ: { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: OPEN_FOR_READ [this=%x %s]", - this, mStreamName.GetBuffer())); + LOG(("nsFileTransport: OPEN_FOR_READ [this=%x %s]\n", this, mStreamName.GetBuffer())); mStatus = mStreamIO->Open(&mContentType, &mTotalAmount); + LOG(("nsFileTransport: OPEN_FOR_READ [this=%x %s] status=%x\n", this, mStreamName.GetBuffer(), mStatus)); if (mListener) { nsresult rv = mListener->OnStartRequest(this, mContext); // always send the start notification if (NS_SUCCEEDED(mStatus)) @@ -396,27 +555,28 @@ nsFileTransport::Process(void) } case START_READ: { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: START_READ [this=%x %s]", - this, mStreamName.GetBuffer())); + LOG(("nsFileTransport: START_READ [this=%x %s]\n", this, mStreamName.GetBuffer())); PR_AtomicIncrement(&mService->mInUseTransports); mStatus = mStreamIO->GetInputStream(getter_AddRefs(mSource)); if (NS_FAILED(mStatus)) { + LOG(("nsFileTransport: mStreamIO->GetInputStream() failed [this=%x rv=%x]\n", + this, mStatus)); mXferState = END_READ; return; } if (mOffset > 0) { - // if we need to set a starting offset, QI for the nsISeekableStream and set it + // if we need to set a starting offset, QI for the nsISeekableStream + // and set it nsCOMPtr ras = do_QueryInterface(mSource, &mStatus); if (NS_FAILED(mStatus)) { mXferState = END_READ; return; } - // for now, assume the offset is always relative to the start of the file (position 0) - // so use PR_SEEK_SET + // for now, assume the offset is always relative to the start of the + // file (position 0) so use PR_SEEK_SET mStatus = ras->Seek(PR_SEEK_SET, mOffset); if (NS_FAILED(mStatus)) { mXferState = END_READ; @@ -424,76 +584,100 @@ nsFileTransport::Process(void) } } + if (!mSourceWrapper) { + // + // Allocate an input stream wrapper to capture the number of bytes + // read from mSource. + // + NS_NEWXPCOM(mSourceWrapper, nsFileTransportSourceWrapper); + if (!mSourceWrapper) { + mStatus = NS_ERROR_OUT_OF_MEMORY; + mXferState = END_READ; + return; + } + NS_ADDREF(mSourceWrapper); + mSourceWrapper->SetSource(mSource); + } + // capture the total amount for progress information if (mTransferAmount < 0) { mTransferAmount = mTotalAmount; - } - mTotalAmount = mTransferAmount; + } else + mTotalAmount = mTransferAmount; mXferState = READING; break; } case READING: { - // Use a temporary status variable here so that we don't whack mStatus with - // NS_BASE_STREAM_WOULD_BLOCK. This could cause the async stream listener to - // decide an error was happening, and not deliver necessary events. - nsresult status; + // + // Read at most mBufferMaxSize. + // + PRInt32 transferAmt = mBufferMaxSize; + if (mTransferAmount >= 0) + transferAmt = PR_MIN(transferAmt, mTransferAmount); - PRUint32 writeAmt; - // and feed the buffer to the application via the buffer stream: - status = mOutputStream->WriteFrom(mSource, mTransferAmount, &writeAmt); - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: READING [this=%x %s] amt=%d status=%x", - this, mStreamName.GetBuffer(), writeAmt, status)); + LOG(("nsFileTransport: READING [this=%x %s] transferAmt=%u mBufferMaxSize=%u\n", + this, mStreamName.GetBuffer(), transferAmt, mBufferMaxSize)); + + // Zero the number of bytes read on the source wrapper + mSourceWrapper->ZeroBytesRead(); + + // + // Give the listener a chance to read at most transferAmt bytes from + // the source input stream. + // + nsresult status = mListener->OnDataAvailable(this, mContext, + mSourceWrapper, + mOffset, transferAmt); + + // + // Handle the various return codes. + // if (status == NS_BASE_STREAM_WOULD_BLOCK) { + LOG(("nsFileTransport: READING [this=%x %s] listener would block; suspending self.\n", + this, mStreamName.GetBuffer())); mStatus = NS_OK; - return; + PR_AtomicIncrement(&mSuspendCount); } - mStatus = status; - if (NS_FAILED(mStatus) || writeAmt == 0) { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: READING [this=%x %s] %s writing to buffered output stream", - this, mStreamName.GetBuffer(), NS_SUCCEEDED(mStatus) ? "done" : "error")); + else if (status == NS_BASE_STREAM_CLOSED) { + LOG(("nsFileTransport: READING [this=%x %s] done reading file.\n", + this, mStreamName.GetBuffer())); + mStatus = NS_OK; mXferState = END_READ; - return; } - if (mTransferAmount > 0) { - mTransferAmount -= writeAmt; - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: READING [this=%x %s] %d bytes left to transfer", - this, mStreamName.GetBuffer(), mTransferAmount)); + else if (NS_FAILED(status)) { + LOG(("nsFileTransport: READING [this=%x %s] error reading file.\n", + this, mStreamName.GetBuffer())); + mStatus = status; + mXferState = END_READ; } - PRUint32 offset = mOffset; - mOffset += writeAmt; - if (mListener) { - mStatus = mListener->OnDataAvailable(this, mContext, - mInputStream, - offset, writeAmt); - if (NS_FAILED(mStatus)) { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: READING [this=%x %s] error notifying stream listener", - this, mStreamName.GetBuffer())); + else { + // + // get the number of bytes read + // + PRUint32 total = mSourceWrapper->GetBytesRead(); + mOffset += total; + if (mTransferAmount > 0) + mTransferAmount -= total; + + if (0 == total || 0 == mTransferAmount) { + LOG(("nsFileTransport: READING [this=%x %s] done reading file.\n", + this, mStreamName.GetBuffer())); mXferState = END_READ; - return; + } + else + LOG(("nsFileTransport: READING [this=%x %s] read %u bytes [offset=%u]\n", + this, mStreamName.GetBuffer(), total, mOffset)); + + if (mProgress && !(mLoadAttributes & LOAD_BACKGROUND) + && (mTransferAmount >= 0)) { + mProgress->OnProgress(this, mContext, + mTotalAmount - mTransferAmount, + mTotalAmount); } } - - if (mProgress && mTransferAmount >= 0 && - !(mLoadAttributes & LOAD_BACKGROUND)) { - nsresult rv = mProgress->OnProgress(this, mContext, - mTotalAmount - mTransferAmount, - mTotalAmount); - NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnProgress failure"); - } - - if (mTransferAmount == 0) { - mXferState = END_READ; - return; - } - - // stay in the READING state break; } @@ -501,52 +685,50 @@ nsFileTransport::Process(void) PR_AtomicDecrement(&mService->mInUseTransports); - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: END_READ [this=%x %s] status=%x", - this, mStreamName.GetBuffer(), mStatus)); + LOG(("nsFileTransport: END_READ [this=%x %s] status=%x\n", + this, mStreamName.GetBuffer(), mStatus)); #if defined (DEBUG_dougt) || defined (DEBUG_warren) NS_ASSERTION(mTransferAmount <= 0 || NS_FAILED(mStatus), "didn't transfer all the data"); #endif if (mTransferAmount > 0 && NS_SUCCEEDED(mStatus)) { + // // This happens when the requested read amount is more than the amount - // of the data in the stream/file. + // of the data in the stream/file, or if the listener returned + // NS_BASE_STREAM_CLOSED. + // mStatus = NS_BASE_STREAM_CLOSED; } - if (mOutputStream) { - mOutputStream->Flush(); - mOutputStream = null_nsCOMPtr(); - } - mInputStream = null_nsCOMPtr(); - mSource = null_nsCOMPtr(); - - nsresult rv; if (mListener) { - rv = mListener->OnStopRequest(this, mContext, mStatus, nsnull); - NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnStopRequest failure"); - mListener = null_nsCOMPtr(); + mListener->OnStopRequest(this, mContext, mStatus, nsnull); + mListener = 0; } if (mProgress && !(mLoadAttributes & LOAD_BACKGROUND)) { - nsAutoString fileName; fileName.AssignWithConversion(mStreamName); - rv = mProgress->OnStatus(this, mContext, - NS_NET_STATUS_READ_FROM, - fileName.GetUnicode()); - NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnStopRequest failure"); + nsAutoString fileName; + fileName.AssignWithConversion(mStreamName); + mProgress->OnStatus(this, mContext, + NS_NET_STATUS_READ_FROM, + fileName.GetUnicode()); } - mContext = null_nsCOMPtr(); + mContext = 0; + + // close the data source + mSource = 0; + NS_IF_RELEASE(mSourceWrapper); + mSourceWrapper = nsnull; mXferState = CLOSING; break; } case OPEN_FOR_WRITE: { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: OPEN_FOR_WRITE [this=%x %s]", - this, mStreamName.GetBuffer())); + LOG(("nsFileTransport: OPEN_FOR_WRITE [this=%x %s]\n", + this, mStreamName.GetBuffer())); mStatus = mStreamIO->Open(&mContentType, &mTotalAmount); - if (mObserver) { - nsresult rv = mObserver->OnStartRequest(this, mContext); // always send the start notification + if (mProvider) { + // always send the start notification + nsresult rv = mProvider->OnStartRequest(this, mContext); if (NS_SUCCEEDED(mStatus)) mStatus = rv; } @@ -556,9 +738,8 @@ nsFileTransport::Process(void) } case START_WRITE: { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: START_WRITE [this=%x %s]", - this, mStreamName.GetBuffer())); + LOG(("nsFileTransport: START_WRITE [this=%x %s]\n", + this, mStreamName.GetBuffer())); PR_AtomicIncrement(&mService->mInUseTransports); @@ -569,14 +750,15 @@ nsFileTransport::Process(void) } if (mOffset > 0) { - // if we need to set a starting offset, QI for the nsISeekableStream and set it + // If we need to set a starting offset, QI for the nsISeekableStream + // and set it. nsCOMPtr ras = do_QueryInterface(mSink, &mStatus); if (NS_FAILED(mStatus)) { mXferState = END_WRITE; return; } - // for now, assume the offset is always relative to the start of the file (position 0) - // so use PR_SEEK_SET + // For now, assume the offset is always relative to the start of the + // file (position 0) so use PR_SEEK_SET. mStatus = ras->Seek(PR_SEEK_SET, mOffset); if (NS_FAILED(mStatus)) { mXferState = END_WRITE; @@ -585,19 +767,19 @@ nsFileTransport::Process(void) mOffset = 0; } - mInputStream = do_QueryInterface(mSource, &mStatus); - if (NS_FAILED(mStatus)) { - // if the given input stream isn't a buffered input - // stream, then we need to have our own buffer to do the - // transfer - - mStatus = NS_OK; - mBuffer = new char[mBufferSegmentSize]; - if (mBuffer == nsnull) { + if (!mSinkWrapper) { + // + // Allocate an output stream wrapper to capture the number of bytes + // written to mSink. + // + NS_NEWXPCOM(mSinkWrapper, nsFileTransportSinkWrapper); + if (!mSinkWrapper) { mStatus = NS_ERROR_OUT_OF_MEMORY; mXferState = END_WRITE; return; } + NS_ADDREF(mSinkWrapper); + mSinkWrapper->SetSink(mSink); } mXferState = WRITING; @@ -605,62 +787,75 @@ nsFileTransport::Process(void) } case WRITING: { - // Use a temporary status variable here so that we don't whack mStatus with - // NS_BASE_STREAM_WOULD_BLOCK. This could cause the async stream listener to - // decide an error was happening, and not deliver necessary events. - nsresult status; - - PRUint32 transferAmt = mBufferSegmentSize; + // + // Write at most mBufferMaxSize + // + PRUint32 transferAmt = mBufferMaxSize; if (mTransferAmount >= 0) - transferAmt = PR_MIN(mBufferSegmentSize, (PRUint32)mTransferAmount); - PRUint32 writeAmt; - if (mInputStream) { - status = mInputStream->ReadSegments(nsWriteToFile, mSink, - transferAmt, &writeAmt); + transferAmt = PR_MIN(transferAmt, (PRUint32)mTransferAmount); + + // Zero the number of bytes written to the sink wrapper. + mSinkWrapper->ZeroBytesWritten(); + + // + // Ask the provider for data + // + nsresult status = mProvider->OnDataWritable(this, mContext, + mSinkWrapper, + mOffset, transferAmt); + + // + // Handle the various return codes. + // + if (status == NS_BASE_STREAM_WOULD_BLOCK) { + LOG(("nsFileTransport: WRITING [this=%x %s] provider would block; suspending self.\n", + this, mStreamName.GetBuffer())); + mStatus = NS_OK; + PR_AtomicIncrement(&mSuspendCount); + } + else if (status == NS_BASE_STREAM_CLOSED) { + LOG(("nsFileTransport: WRITING [this=%x %s] no more data to be written.\n", + this, mStreamName.GetBuffer())); + mStatus = NS_OK; + mXferState = END_WRITE; + } + else if (NS_FAILED(status)) { + LOG(("nsFileTransport: WRITING [this=%x %s] provider failed.\n", + this, mStreamName.GetBuffer())); + mStatus = status; + mXferState = END_WRITE; } else { - PRUint32 readAmt; - status = mSource->Read(mBuffer, transferAmt, &readAmt); - if (status == NS_BASE_STREAM_WOULD_BLOCK) { - mStatus = NS_OK; - return; - } - mStatus = status; - if (NS_FAILED(mStatus) || readAmt == 0) { + // + // Get the number of bytes written + // + PRUint32 total = mSinkWrapper->GetBytesWritten(); + mOffset += total; + + if (mTransferAmount > 0) + mTransferAmount -= total; + + if (total == 0 || mTransferAmount == 0) { + LOG(("nsFileTransport: WRITING [this=%x %s] done writing file.\n", + this, mStreamName.GetBuffer())); mXferState = END_WRITE; - return; } - status = mSink->Write(mBuffer, readAmt, &writeAmt); - } - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: WRITING [this=%x %s] amt=%d status=%x", - this, mStreamName.GetBuffer(), writeAmt, status)); - if (status == NS_BASE_STREAM_WOULD_BLOCK) { - mStatus = NS_OK; - return; - } - mStatus = status; - if (NS_FAILED(mStatus) || writeAmt == 0) { - mXferState = END_WRITE; - return; - } + else + LOG(("nsFileTransport: WRITING [this=%x %s] wrote %u bytes [offset=%u]\n", + this, mStreamName.GetBuffer(), total, mOffset)); - mTransferAmount -= writeAmt; - mOffset += writeAmt; - if (mProgress && !(mLoadAttributes & LOAD_BACKGROUND)) { - (void)mProgress->OnProgress(this, mContext, - mTotalAmount - mTransferAmount, - mTotalAmount); + if (mProgress && !(mLoadAttributes & LOAD_BACKGROUND) + && (mTransferAmount >= 0)) + mProgress->OnProgress(this, mContext, + mTotalAmount - mTransferAmount, + mTotalAmount); } - - // stay in the WRITING state break; } case END_WRITE: { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: END_WRITE [this=%x %s] status=%x", - this, mStreamName.GetBuffer(), mStatus)); + LOG(("nsFileTransport: END_WRITE [this=%x %s] status=%x\n", + this, mStreamName.GetBuffer(), mStatus)); PR_AtomicDecrement(&mService->mInUseTransports); @@ -675,34 +870,24 @@ nsFileTransport::Process(void) if (mSink) { mSink->Flush(); - mSink = null_nsCOMPtr(); - } - if (mInputStream) { - mInputStream = null_nsCOMPtr(); - } - else if (mBuffer) { - delete mBuffer; - mBuffer = nsnull; - } - if (mSource) { - (void)mSource->Close(); - mSource = null_nsCOMPtr(); + mSink = 0; } + NS_IF_RELEASE(mSinkWrapper); + mSinkWrapper = nsnull; nsresult rv; - if (mObserver) { - rv = mObserver->OnStopRequest(this, mContext, mStatus, nsnull); - NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnStopRequest failure"); - mObserver = null_nsCOMPtr(); + if (mProvider) { + mProvider->OnStopRequest(this, mContext, mStatus, nsnull); + mProvider = 0; } if (mProgress && !(mLoadAttributes & LOAD_BACKGROUND)) { nsAutoString fileName; fileName.AssignWithConversion(mStreamName); rv = mProgress->OnStatus(this, mContext, NS_NET_STATUS_WROTE_TO, fileName.GetUnicode()); - NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnStopRequest failure"); + NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnStatus failure"); } - mContext = null_nsCOMPtr(); + mContext = 0; mXferState = CLOSING; break; @@ -718,53 +903,27 @@ nsFileTransport::Process(void) break; } } + + LOG(("nsFileTransport: Leaving Process [this=%x state=%x status=%x]\n", + this, mXferState, mStatus)); } void nsFileTransport::DoClose(void) { - PR_LOG(gFileTransportLog, PR_LOG_DEBUG, - ("nsFileTransport: CLOSING [this=%x %s] status=%x", - this, mStreamName.GetBuffer(), mStatus)); + LOG(("nsFileTransport: CLOSING [this=%x %s] status=%x\n", + this, mStreamName.GetBuffer(), mStatus)); if (mStreamIO) { nsresult rv = mStreamIO->Close(mStatus); NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected Close failure"); - mStreamIO = null_nsCOMPtr(); + mStreamIO = 0; } mXferState = CLOSED; PR_AtomicDecrement(&mService->mConnectedTransports); } -//////////////////////////////////////////////////////////////////////////////// -// nsIInputStreamObserver/nsIOutputStreamObserver methods: -//////////////////////////////////////////////////////////////////////////////// - -NS_IMETHODIMP -nsFileTransport::OnFull(nsIOutputStream* out) -{ - return Suspend(); -} - -NS_IMETHODIMP -nsFileTransport::OnWrite(nsIOutputStream* out, PRUint32 aCount) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsFileTransport::OnEmpty(nsIInputStream* in) -{ - return Resume(); -} - -NS_IMETHODIMP -nsFileTransport::OnClose(nsIInputStream* in) -{ - return NS_OK; -} - //////////////////////////////////////////////////////////////////////////////// // other nsIChannel methods: //////////////////////////////////////////////////////////////////////////////// @@ -999,4 +1158,5 @@ nsFileTransport::GetSecurityInfo(nsISupports * *aSecurityInfo) *aSecurityInfo = nsnull; return NS_OK; } + //////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/netwerk/base/src/nsFileTransport.h b/mozilla/netwerk/base/src/nsFileTransport.h index d159ee4ca4f..2379cc97a3e 100644 --- a/mozilla/netwerk/base/src/nsFileTransport.h +++ b/mozilla/netwerk/base/src/nsFileTransport.h @@ -32,6 +32,7 @@ #include "nsILoadGroup.h" #include "nsCOMPtr.h" #include "nsIStreamListener.h" +#include "nsIStreamProvider.h" #include "nsIProgressEventSink.h" #include "nsIInputStream.h" #include "nsIOutputStream.h" @@ -41,19 +42,20 @@ #include "prlog.h" #include "nsFileTransportService.h" +//#define TIMING + class nsIInterfaceRequestor; +class nsFileTransportSourceWrapper; +class nsFileTransportSinkWrapper; + class nsFileTransport : public nsIChannel, - public nsIRunnable, - public nsIInputStreamObserver, - public nsIOutputStreamObserver + public nsIRunnable { public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUEST NS_DECL_NSICHANNEL - NS_DECL_NSIINPUTSTREAMOBSERVER - NS_DECL_NSIOUTPUTSTREAMOBSERVER NS_DECL_NSIRUNNABLE nsFileTransport(); @@ -99,7 +101,7 @@ protected: nsCOMPtr mCallbacks; nsCOMPtr mProgress; nsCOMPtr mStreamIO; - char* mContentType; + char *mContentType; PRUint32 mBufferSegmentSize; PRUint32 mBufferMaxSize; @@ -107,11 +109,16 @@ protected: // mXferState is only changed by the file transport thread: XferState mXferState; + // mRunState is only changed by the user's thread, but looked at by the // file transport thread: RunState mRunState; nsresult mCancelStatus; - PRMonitor* mMonitor; + PRInt32 mSuspendCount; + PRLock *mLock; + + // The transport is active if it is currently being processed by a thread. + PRBool mActive; // state variables: nsresult mStatus; @@ -120,18 +127,22 @@ protected: PRInt32 mTransferAmount; nsLoadFlags mLoadAttributes; - // reading state varialbles: + // reading state variables: nsCOMPtr mListener; nsCOMPtr mSource; - nsCOMPtr mInputStream; - nsCOMPtr mOutputStream; + nsFileTransportSourceWrapper *mSourceWrapper; // writing state variables: - nsCOMPtr mObserver; + nsCOMPtr mProvider; nsCOMPtr mSink; - char* mBuffer; + nsFileTransportSinkWrapper *mSinkWrapper; + nsCString mStreamName; - nsFileTransportService* mService; + nsFileTransportService *mService; + +#ifdef TIMING + PRIntervalTime mStartTime; +#endif }; #define NS_FILE_TRANSPORT_DEFAULT_SEGMENT_SIZE (2*1024) diff --git a/mozilla/netwerk/base/src/nsInputStreamChannel.cpp b/mozilla/netwerk/base/src/nsInputStreamChannel.cpp index bac57ab95aa..c8ddef50f25 100644 --- a/mozilla/netwerk/base/src/nsInputStreamChannel.cpp +++ b/mozilla/netwerk/base/src/nsInputStreamChannel.cpp @@ -170,12 +170,18 @@ nsStreamIOChannel::Init(nsIURI* uri, nsIStreamIO* io) return NS_OK; } -NS_IMPL_THREADSAFE_ISUPPORTS5(nsStreamIOChannel, - nsIStreamIOChannel, - nsIChannel, - nsIRequest, - nsIStreamObserver, - nsIStreamListener); +NS_IMPL_THREADSAFE_ADDREF(nsStreamIOChannel) +NS_IMPL_THREADSAFE_RELEASE(nsStreamIOChannel) + +NS_INTERFACE_MAP_BEGIN(nsStreamIOChannel) + NS_INTERFACE_MAP_ENTRY(nsIStreamIOChannel) + NS_INTERFACE_MAP_ENTRY(nsIChannel) + NS_INTERFACE_MAP_ENTRY(nsIRequest) + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIStreamProvider) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIStreamObserver, nsIStreamListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener) +NS_INTERFACE_MAP_END_THREADSAFE //////////////////////////////////////////////////////////////////////////////// // nsIRequest methods: @@ -349,13 +355,12 @@ nsStreamIOChannel::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } NS_IMETHODIMP -nsStreamIOChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, nsISupports *ctxt) +nsStreamIOChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { nsresult rv; - NS_ASSERTION(observer, "no observer"); - mUserObserver = observer; + NS_ASSERTION(provider, "no provider"); + SetProvider(provider); if (mLoadGroup) { nsCOMPtr factory; @@ -364,12 +369,15 @@ nsStreamIOChannel::AsyncWrite(nsIInputStream *fromStream, // rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory)); if (factory) { + NS_WARNING("load group proxy listener not implemented for AsyncWrite"); +#if 0 nsIStreamListener *newListener; rv = factory->CreateLoadGroupListener(GetListener(), &newListener); if (NS_SUCCEEDED(rv)) { mUserObserver = newListener; NS_RELEASE(newListener); } +#endif } rv = mLoadGroup->AddChannel(this, nsnull); @@ -398,7 +406,7 @@ nsStreamIOChannel::AsyncWrite(nsIInputStream *fromStream, if (NS_FAILED(rv)) goto done; } #endif - rv = mFileTransport->AsyncWrite(fromStream, this, ctxt); + rv = mFileTransport->AsyncWrite(this, ctxt); done: if (NS_FAILED(rv)) { @@ -600,7 +608,7 @@ nsStreamIOChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) } //////////////////////////////////////////////////////////////////////////////// -// nsIStreamListener methods: +// nsIStreamObserver implementation: //////////////////////////////////////////////////////////////////////////////// NS_IMETHODIMP @@ -635,6 +643,10 @@ nsStreamIOChannel::OnStopRequest(nsIChannel* transportChannel, nsISupports* cont return mStreamIO->Close(aStatus); } +//////////////////////////////////////////////////////////////////////////////// +// nsIStreamListener implementation: +//////////////////////////////////////////////////////////////////////////////// + NS_IMETHODIMP nsStreamIOChannel::OnDataAvailable(nsIChannel* transportChannel, nsISupports* context, nsIInputStream *aIStream, PRUint32 aSourceOffset, @@ -645,3 +657,16 @@ nsStreamIOChannel::OnDataAvailable(nsIChannel* transportChannel, nsISupports* co } //////////////////////////////////////////////////////////////////////////////// +// nsIStreamProvider implementation: +//////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsStreamIOChannel::OnDataWritable(nsIChannel* transportChannel, nsISupports* context, + nsIOutputStream *aOStream, PRUint32 aOffset, + PRUint32 aLength) +{ + return GetProvider()->OnDataWritable(this, context, aOStream, + aOffset, aLength); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/netwerk/base/src/nsInputStreamChannel.h b/mozilla/netwerk/base/src/nsInputStreamChannel.h index 860648fe7ec..8b1c5ec4c64 100644 --- a/mozilla/netwerk/base/src/nsInputStreamChannel.h +++ b/mozilla/netwerk/base/src/nsInputStreamChannel.h @@ -29,6 +29,7 @@ #include "nsCRT.h" #include "nsILoadGroup.h" #include "nsIStreamListener.h" +#include "nsIStreamProvider.h" #include "nsIInterfaceRequestor.h" #include "nsIStreamIO.h" @@ -56,7 +57,8 @@ protected: //////////////////////////////////////////////////////////////////////////////// class nsStreamIOChannel : public nsIStreamIOChannel, - public nsIStreamListener + public nsIStreamListener, + public nsIStreamProvider { public: NS_DECL_ISUPPORTS @@ -65,6 +67,7 @@ public: NS_DECL_NSISTREAMIOCHANNEL NS_DECL_NSISTREAMOBSERVER NS_DECL_NSISTREAMLISTENER + NS_DECL_NSISTREAMPROVIDER nsStreamIOChannel(); virtual ~nsStreamIOChannel(); @@ -75,6 +78,8 @@ public: protected: nsIStreamListener* GetListener() { return (nsIStreamListener*)mUserObserver.get(); } void SetListener(nsIStreamListener* listener) { mUserObserver = listener; } + nsIStreamProvider* GetProvider() { return (nsIStreamProvider*)mUserObserver.get(); } + void SetProvider(nsIStreamProvider* provider) { mUserObserver = provider; } protected: nsCOMPtr mCallbacks; diff --git a/mozilla/netwerk/base/src/nsSimpleStreamListener.cpp b/mozilla/netwerk/base/src/nsSimpleStreamListener.cpp new file mode 100644 index 00000000000..e3a184edef2 --- /dev/null +++ b/mozilla/netwerk/base/src/nsSimpleStreamListener.cpp @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsSimpleStreamListener.h" + +// +//---------------------------------------------------------------------------- +// nsISupports implementation... +//---------------------------------------------------------------------------- +// +NS_IMPL_ISUPPORTS3(nsSimpleStreamListener, + nsISimpleStreamListener, + nsIStreamListener, + nsIStreamObserver) + +// +//---------------------------------------------------------------------------- +// nsIStreamObserver implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSimpleStreamListener::OnStartRequest(nsIChannel *aChannel, + nsISupports *aContext) +{ + return mObserver ? + mObserver->OnStartRequest(aChannel, aContext) : NS_OK; +} + +NS_IMETHODIMP +nsSimpleStreamListener::OnStopRequest(nsIChannel *aChannel, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aStatusText) +{ + return mObserver ? + mObserver->OnStopRequest(aChannel, aContext, aStatus, aStatusText) : NS_OK; +} + +// +//---------------------------------------------------------------------------- +// nsIStreamListener implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSimpleStreamListener::OnDataAvailable(nsIChannel *aChannel, + nsISupports *aContext, + nsIInputStream *aSource, + PRUint32 aOffset, + PRUint32 aCount) +{ + PRUint32 writeCount; + nsresult rv = mSink->WriteFrom(aSource, aCount, &writeCount); + // + // Equate zero bytes read and NS_SUCCEEDED to stopping the read. + // + if (NS_SUCCEEDED(rv) && (writeCount == 0)) + return NS_BASE_STREAM_CLOSED; + return rv; +} + +// +//---------------------------------------------------------------------------- +// nsISimpleStreamListener implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSimpleStreamListener::Init(nsIOutputStream *aSink, + nsIStreamObserver *aObserver) +{ + NS_PRECONDITION(aSink, "null output stream"); + + mSink = aSink; + mObserver = aObserver; + + return NS_OK; +} diff --git a/mozilla/netwerk/base/src/nsSimpleStreamListener.h b/mozilla/netwerk/base/src/nsSimpleStreamListener.h new file mode 100644 index 00000000000..840a826c7ad --- /dev/null +++ b/mozilla/netwerk/base/src/nsSimpleStreamListener.h @@ -0,0 +1,19 @@ +#include "nsIStreamListener.h" +#include "nsIOutputStream.h" +#include "nsCOMPtr.h" + +class nsSimpleStreamListener : public nsISimpleStreamListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSISIMPLESTREAMLISTENER + + nsSimpleStreamListener() { NS_INIT_ISUPPORTS(); } + virtual ~nsSimpleStreamListener() {} + +protected: + nsCOMPtr mSink; + nsCOMPtr mObserver; +}; diff --git a/mozilla/netwerk/base/src/nsSimpleStreamProvider.cpp b/mozilla/netwerk/base/src/nsSimpleStreamProvider.cpp new file mode 100644 index 00000000000..90c04f42076 --- /dev/null +++ b/mozilla/netwerk/base/src/nsSimpleStreamProvider.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsSimpleStreamProvider.h" +#include "nsIOutputStream.h" + +// +//---------------------------------------------------------------------------- +// nsISupports implementation... +//---------------------------------------------------------------------------- +// +NS_IMPL_ISUPPORTS3(nsSimpleStreamProvider, + nsISimpleStreamProvider, + nsIStreamProvider, + nsIStreamObserver) + +// +//---------------------------------------------------------------------------- +// nsIStreamObserver implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSimpleStreamProvider::OnStartRequest(nsIChannel *aChannel, + nsISupports *aContext) +{ + return mObserver ? + mObserver->OnStartRequest(aChannel, aContext) : NS_OK; +} + +NS_IMETHODIMP +nsSimpleStreamProvider::OnStopRequest(nsIChannel *aChannel, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aStatusText) +{ + return mObserver ? + mObserver->OnStopRequest(aChannel, aContext, aStatus, aStatusText) : NS_OK; +} + +// +//---------------------------------------------------------------------------- +// nsIStreamProvider implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSimpleStreamProvider::OnDataWritable(nsIChannel *aChannel, + nsISupports *aContext, + nsIOutputStream *aOutput, + PRUint32 aOffset, + PRUint32 aCount) +{ + PRUint32 writeCount; + nsresult rv = aOutput->WriteFrom(mSource, aCount, &writeCount); + // + // Equate zero bytes written and NS_SUCCEEDED to EOF + // + if (NS_SUCCEEDED(rv) && (writeCount == 0)) + return NS_BASE_STREAM_CLOSED; + return rv; +} + +// +//---------------------------------------------------------------------------- +// nsISimpleStreamProvider implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSimpleStreamProvider::Init(nsIInputStream *aSource, + nsIStreamObserver *aObserver) +{ + NS_PRECONDITION(aSource, "null input stream"); + + mSource = aSource; + mObserver = aObserver; + + return NS_OK; +} diff --git a/mozilla/netwerk/base/src/nsSimpleStreamProvider.h b/mozilla/netwerk/base/src/nsSimpleStreamProvider.h new file mode 100644 index 00000000000..0d9e00ac5e8 --- /dev/null +++ b/mozilla/netwerk/base/src/nsSimpleStreamProvider.h @@ -0,0 +1,19 @@ +#include "nsIStreamProvider.h" +#include "nsIInputStream.h" +#include "nsCOMPtr.h" + +class nsSimpleStreamProvider : public nsISimpleStreamProvider +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSISTREAMPROVIDER + NS_DECL_NSISIMPLESTREAMPROVIDER + + nsSimpleStreamProvider() { NS_INIT_ISUPPORTS(); } + virtual ~nsSimpleStreamProvider() {} + +protected: + nsCOMPtr mSource; + nsCOMPtr mObserver; +}; diff --git a/mozilla/netwerk/base/src/nsSocketTransport.cpp b/mozilla/netwerk/base/src/nsSocketTransport.cpp index 3802579d2dd..b86be822c4e 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport.cpp +++ b/mozilla/netwerk/base/src/nsSocketTransport.cpp @@ -43,11 +43,189 @@ #include "nsIChannelSecurityInfo.h" #include "nsMemory.h" +#if defined(PR_LOGGING) +static PRLogModuleInfo *gSocketTransportLog = nsnull; +#endif + +#define LOG(args) PR_LOG(gSocketTransportLog, PR_LOG_DEBUG, args) + static NS_DEFINE_CID(kSocketProviderService, NS_SOCKETPROVIDERSERVICE_CID); static NS_DEFINE_CID(kDNSService, NS_DNSSERVICE_CID); static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); +// +//---------------------------------------------------------------------------- +// nsSocketInputStream +//---------------------------------------------------------------------------- +// +class nsSocketInputStream : public nsIInputStream +{ +public: + NS_DECL_ISUPPORTS + + nsSocketInputStream() + : mBytesRead(0), mSocketFD(nsnull) { NS_INIT_ISUPPORTS(); } + virtual ~nsSocketInputStream() {} + + void SetSocketFD(PRFileDesc *aSocketFD) { + mSocketFD = aSocketFD; + } + PRUint32 GetBytesRead() { + return mBytesRead; + } + void ZeroBytesRead() { + mBytesRead = 0; + } + + // + // nsIInputStream implementation... + // + NS_IMETHOD Close() { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD Available(PRUint32 *aCount) { + NS_PRECONDITION(mSocketFD, "null socket fd"); + PRInt32 avail = PR_Available(mSocketFD); + if (avail >= 0) { + *aCount = avail; + return NS_OK; + } + else + return NS_ERROR_FAILURE; + } + NS_IMETHOD Read(char *aBuf, PRUint32 aCount, PRUint32 *aBytesRead) { + NS_PRECONDITION(mSocketFD, "null socket fd"); + PRInt32 result = PR_Read(mSocketFD, aBuf, aCount); + LOG(("nsSocketTransport: PR_Read(count=%u) returned %d\n", aCount, result)); + nsresult rv = NS_OK; + if (result < 0) { + PRErrorCode code = PR_GetError(); + if (PR_WOULD_BLOCK_ERROR == code) + rv = NS_BASE_STREAM_WOULD_BLOCK; + else { + LOG(("nsSocketTransport: PR_Read() failed [error=%x, os_error=%x]\n", + code, PR_GetOSError())); + rv = NS_ERROR_FAILURE; + } + *aBytesRead = 0; + } + else { + *aBytesRead = result; + mBytesRead += result; + } + return rv; + } + NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, + PRUint32 aCount, PRUint32 *aBytesRead) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD GetNonBlocking(PRBool *aValue) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD GetObserver(nsIInputStreamObserver **aObserver) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD SetObserver(nsIInputStreamObserver *aObserver) { + return NS_ERROR_NOT_IMPLEMENTED; + } + +protected: + PRUint32 mBytesRead; + PRFileDesc *mSocketFD; +}; + +NS_IMPL_ISUPPORTS1(nsSocketInputStream, nsIInputStream) + +// +//---------------------------------------------------------------------------- +// nsSocketOutputStream +//---------------------------------------------------------------------------- +// +class nsSocketOutputStream : public nsIOutputStream +{ +public: + NS_DECL_ISUPPORTS + + nsSocketOutputStream() + : mBytesWritten(0), mSocketFD(nsnull) { NS_INIT_ISUPPORTS(); } + virtual ~nsSocketOutputStream() {} + + void SetSocketFD(PRFileDesc *aSocketFD) { + mSocketFD = aSocketFD; + } + PRUint32 GetBytesWritten() { + return mBytesWritten; + } + void ZeroBytesWritten() { + mBytesWritten = 0; + } + + // + // nsIInputStream implementation... + // + NS_IMETHOD Close() { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD Flush() { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD Write(const char *aBuf, PRUint32 aCount, PRUint32 *aBytesWritten) { + NS_PRECONDITION(mSocketFD, "null socket fd"); + PRInt32 result = PR_Write(mSocketFD, aBuf, aCount); + LOG(("nsSocketTransport: PR_Write(count=%u) returned %d\n", aCount, result)); + nsresult rv = NS_OK; + if (result < 0) { + PRErrorCode code = PR_GetError(); + if (PR_WOULD_BLOCK_ERROR == code) + rv = NS_BASE_STREAM_WOULD_BLOCK; + else { + LOG(("nsSocketTransport: PR_Write() failed [error=%x, os_error=%x]\n", + code, PR_GetOSError())); + rv = NS_ERROR_FAILURE; + } + *aBytesWritten = 0; + } + else { + *aBytesWritten = result; + mBytesWritten += result; + } + return rv; + } + NS_IMETHOD WriteFrom(nsIInputStream *aIS, PRUint32 aCount, PRUint32 *aBytesWritten) { + NS_NOTREACHED("WriteFrom"); + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD WriteSegments(nsReadSegmentFun aReader, void *aClosure, + PRUint32 aCount, PRUint32 *aBytesWritten) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD GetNonBlocking(PRBool *aValue) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD SetNonBlocking(PRBool aValue) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD GetObserver(nsIOutputStreamObserver **aObserver) { + return NS_ERROR_NOT_IMPLEMENTED; + } + NS_IMETHOD SetObserver(nsIOutputStreamObserver *aObserver) { + return NS_ERROR_NOT_IMPLEMENTED; + } + +protected: + PRUint32 mBytesWritten; + PRFileDesc *mSocketFD; +}; + +NS_IMPL_ISUPPORTS1(nsSocketOutputStream, nsIOutputStream) + +// +//---------------------------------------------------------------------------- +// nsSocketTransport +//---------------------------------------------------------------------------- +// + // // This is the State table which maps current state to next state // for each socket operation... @@ -104,29 +282,15 @@ nsSocketState gStateTable[eSocketOperation_Max][eSocketState_Max] = { static PRIntervalTime gConnectTimeout = PR_INTERVAL_NO_WAIT; #if defined(PR_LOGGING) -// -// Log module for SocketTransport logging... -// -// To enable logging (see prlog.h for full details): -// -// set NSPR_LOG_MODULES=nsSocketTransport:5 -// set NSPR_LOG_FILE=nspr.log -// -// this enables PR_LOG_DEBUG level information and places all output in -// the file nspr.log -// -PRLogModuleInfo* gSocketLog = nsnull; - -#endif /* PR_LOGGING */ - -static PRUint32 sTotalTransportsCreated = 0; -static PRUint32 sTotalTransportsDeleted = 0; +static PRUint32 sTotalTransportsCreated = 0; +static PRUint32 sTotalTransportsDeleted = 0; +#endif nsSocketTransport::nsSocketTransport(): - mSocketTimeout (PR_INTERVAL_NO_TIMEOUT), - mSocketConnectTimeout (PR_MillisecondsToInterval (DEFAULT_SOCKET_CONNECT_TIMEOUT_IN_MS)), - mOnStartWriteFired (PR_FALSE), - mOnStartReadFired (PR_FALSE), + mSocketTimeout(PR_INTERVAL_NO_TIMEOUT), + mSocketConnectTimeout(PR_MillisecondsToInterval(DEFAULT_SOCKET_CONNECT_TIMEOUT_IN_MS)), + mOnStartWriteFired(PR_FALSE), + mOnStartReadFired(PR_FALSE), mCancelStatus(NS_OK), mCloseConnectionOnceDone(PR_FALSE), mCurrentState(eSocketState_Created), @@ -139,29 +303,33 @@ nsSocketTransport::nsSocketTransport(): mProxyHost(nsnull), mProxyTransparent(PR_FALSE), mSSLProxy(PR_FALSE), + mService(nsnull), mReadWriteState(0), mSelectFlags(0), - mService(nsnull), + mStatus(NS_OK), + mSuspendCount(0), mSocketFD(nsnull), mSocketTypeCount(0), mSocketTypes(nsnull), - mReadOffset (0), + mReadOffset(0), mWriteOffset(0), - mStatus (NS_OK), - mSuspendCount(0), - mWriteCount (0), + mWriteCount(0), mBytesExpected(-1), - mReuseCount (0), - mLastReuseCount (0), - mWriteBuffer (nsnull), - mWriteBufferIndex (0), - mWriteBufferLength (0), - mBufferSegmentSize (0), - mBufferMaxSize (0), - mIdleTimeoutInSeconds (0), + mReuseCount(0), + mLastReuseCount(0), + mSocketInputStream(nsnull), + mSocketOutputStream(nsnull), + mBufferSegmentSize(0), + mBufferMaxSize(0), + mIdleTimeoutInSeconds(0), mWasConnected (PR_FALSE) { NS_INIT_REFCNT(); + +#if defined(PR_LOGGING) + if (!gSocketTransportLog) + gSocketTransportLog = PR_NewLogModule("nsSocketTransport"); +#endif PR_INIT_CLIST(&mListLink); @@ -179,75 +347,59 @@ nsSocketTransport::nsSocketTransport(): // // Initialize the global connect timeout value if necessary... // - if (PR_INTERVAL_NO_WAIT == gConnectTimeout) { + if (PR_INTERVAL_NO_WAIT == gConnectTimeout) gConnectTimeout = PR_MillisecondsToInterval(CONNECT_TIMEOUT_IN_MS); - } -#if defined(PR_LOGGING) - // - // Initialize the global PRLogModule for socket transport logging - // if necessary... - // - if (nsnull == gSocketLog) { - gSocketLog = PR_NewLogModule("nsSocketTransport"); - } -#endif /* PR_LOGGING */ - - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Creating nsSocketTransport [%x], TotalCreated=%d, TotalDeleted=%d\n", this, ++sTotalTransportsCreated, sTotalTransportsDeleted)); + LOG(("nsSocketTransport: Creating [%x], TotalCreated=%d, TotalDeleted=%d\n", + this, ++sTotalTransportsCreated, sTotalTransportsDeleted)); } nsSocketTransport::~nsSocketTransport() { - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Deleting nsSocketTransport [%s:%d %x], TotalCreated=%d, TotalDeleted=%d\n", - mHostName, mPort, this, sTotalTransportsCreated, ++sTotalTransportsDeleted)); + LOG(("nsSocketTransport: Deleting [%s:%d %x], TotalCreated=%d, TotalDeleted=%d\n", + mHostName, mPort, this, sTotalTransportsCreated, ++sTotalTransportsDeleted)); // Release the nsCOMPtrs... // // It is easier to debug problems if these are released before the // nsSocketTransport context is lost... // - mReadListener = null_nsCOMPtr(); - mReadContext = null_nsCOMPtr(); - mReadPipeIn = null_nsCOMPtr(); - mReadPipeOut = null_nsCOMPtr(); + mReadListener = 0; + mReadContext = 0; + mReadPipeIn = 0; + mReadPipeOut = 0; - mWriteObserver = null_nsCOMPtr(); - mWriteContext = null_nsCOMPtr(); - mWritePipeIn = null_nsCOMPtr(); - mWritePipeOut = null_nsCOMPtr(); + //mWriteProvider = 0; + mWriteContext = 0; + mWritePipeIn = 0; + mWritePipeOut = 0; // // Cancel any pending DNS request... // if (mDNSRequest) { mDNSRequest->Cancel(NS_BINDING_ABORTED); + mDNSRequest = 0; } - mDNSRequest = null_nsCOMPtr(); - CloseConnection (); + CloseConnection(); - NS_IF_RELEASE (mService); + NS_IF_RELEASE(mService); - CRTFREEIF (mProxyHost); - CRTFREEIF (mHostName); + CRTFREEIF(mProxyHost); + CRTFREEIF(mHostName); while (mSocketTypeCount) { mSocketTypeCount--; - CRTFREEIF (mSocketTypes[mSocketTypeCount]); + CRTFREEIF(mSocketTypes[mSocketTypeCount]); } - if (mSocketTypes != NULL) nsMemory::Free(mSocketTypes); + if (mSocketTypes) + nsMemory::Free(mSocketTypes); if (mMonitor) { nsAutoMonitor::DestroyMonitor(mMonitor); mMonitor = nsnull; } - if (mWriteBuffer) { - PR_Free(mWriteBuffer); - mWriteBuffer = nsnull; - } - if (mService) PR_AtomicDecrement(&mService->mTotalTransports); } @@ -276,45 +428,35 @@ nsresult nsSocketTransport::Init(nsSocketTransportService* aService, mPort = aPort; mProxyPort = aProxyPort; - if (aHost && *aHost) - { + if (aHost && *aHost) { mHostName = nsCRT::strdup(aHost); if (!mHostName) rv = NS_ERROR_OUT_OF_MEMORY; } else // hostname was nsnull or empty... - { rv = NS_ERROR_FAILURE; - } - if (aProxyHost && *aProxyHost) - { + if (aProxyHost && *aProxyHost) { mProxyHost = nsCRT::strdup(aProxyHost); if (!mProxyHost) rv = NS_ERROR_OUT_OF_MEMORY; } - if (NS_SUCCEEDED(rv) && aSocketTypeCount) - { - mSocketTypes = (char**) nsMemory::Alloc (aSocketTypeCount * sizeof(char*)); + if (NS_SUCCEEDED(rv) && aSocketTypeCount) { + mSocketTypes = (char**) nsMemory::Alloc(aSocketTypeCount * sizeof(char*)); if (!mSocketTypes) - { rv = NS_ERROR_OUT_OF_MEMORY; - } - else - { + else { mSocketTypeCount = 0; - for (PRUint32 type = 0; type < aSocketTypeCount; type++) - { + for (PRUint32 type = 0; type < aSocketTypeCount; type++) { const char * socketType = aSocketTypes[type]; - - if (socketType == nsnull) continue; + if (socketType == nsnull) + continue; #ifdef DEBUG - printf("pushing io layer: %s\n", socketType); + LOG(("nsSocketTransport: pushing io layer: %s\n", socketType)); #endif mSocketTypes[mSocketTypeCount] = nsCRT::strdup(socketType); - if (!mSocketTypes[mSocketTypeCount]) - { + if (!mSocketTypes[mSocketTypeCount]) { rv = NS_ERROR_OUT_OF_MEMORY; break; } @@ -322,16 +464,13 @@ nsresult nsSocketTransport::Init(nsSocketTransportService* aService, // increase the count mSocketTypeCount++; - if (nsCRT::strcmp(socketType, "socks") == 0) - { + if (nsCRT::strcmp(socketType, "socks") == 0) { // for SOCKS proxys, we want to switch some of // the default proxy behavior mProxyTransparent = PR_TRUE; } if (mProxyHost && (nsCRT::strcmp(socketType, "ssl") == 0)) - { mSSLProxy = PR_TRUE; - } } } } @@ -339,19 +478,17 @@ nsresult nsSocketTransport::Init(nsSocketTransportService* aService, // // Create the lock used for synchronizing access to the transport instance. // - if (NS_SUCCEEDED(rv)) - { + if (NS_SUCCEEDED(rv)) { mMonitor = nsAutoMonitor::NewMonitor("nsSocketTransport"); if (!mMonitor) rv = NS_ERROR_OUT_OF_MEMORY; } // Update the active time for timeout purposes... - mLastActiveTime = PR_IntervalNow (); - PR_AtomicIncrement (&mService -> mTotalTransports); + mLastActiveTime = PR_IntervalNow(); + PR_AtomicIncrement(&mService->mTotalTransports); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Initializing nsSocketTransport [%s:%d %x]. rv = %x", + LOG(("nsSocketTransport: Initializing [%s:%d %x]. rv = %x", mHostName, mPort, this, rv)); return rv; @@ -366,11 +503,10 @@ nsresult nsSocketTransport::CheckForTimeout (PRIntervalTime aCurrentTime) // Enter the socket transport lock... nsAutoMonitor mon(mMonitor); - if (aCurrentTime > mLastActiveTime) { + if (aCurrentTime > mLastActiveTime) idleInterval = aCurrentTime - mLastActiveTime; - } else { + else idleInterval = 0; - } if (mSocketConnectTimeout != PR_INTERVAL_NO_TIMEOUT && mCurrentState == eSocketState_WaitConnect && idleInterval >= mSocketConnectTimeout @@ -378,11 +514,9 @@ nsresult nsSocketTransport::CheckForTimeout (PRIntervalTime aCurrentTime) mSocketTimeout != PR_INTERVAL_NO_TIMEOUT && mCurrentState == eSocketState_WaitReadWrite && idleInterval >= mSocketTimeout) { - PR_LOG ( - gSocketLog, PR_LOG_ERROR, ("nsSocketTransport::CheckForTimeout() [%s:%d %x].\t" - "TIMED OUT... Idle interval: %d\n", - mHostName, mPort, this, idleInterval) - ); + LOG(("nsSocketTransport: CheckForTimeout() [%s:%d %x].\t" + "TIMED OUT... Idle interval: %d\n", + mHostName, mPort, this, idleInterval)); // Move the transport into the Timeout state... mCurrentState = eSocketState_Timeout; @@ -395,308 +529,312 @@ nsresult nsSocketTransport::CheckForTimeout (PRIntervalTime aCurrentTime) nsresult nsSocketTransport::Process(PRInt16 aSelectFlags) { - PRBool done = PR_FALSE; + PRBool done = PR_FALSE; - // - // Enter the socket transport lock... - // This lock protects access to socket transport member data... - // - PR_EnterMonitor(mMonitor); + // + // Enter the socket transport lock... + // This lock protects access to socket transport member data... + // + PR_EnterMonitor(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::Process() [%s:%d %x].\t" - "aSelectFlags = %x.\t" - "CurrentState = %d\n", - mHostName, mPort, this, aSelectFlags, mCurrentState)); + LOG(("nsSocketTransport: Entering Process() [host=%s:%d this=%x], " + "aSelectFlags=%x, CurrentState=%d.\n", + mHostName, mPort, this, aSelectFlags, mCurrentState)); - // - // Check for an error during PR_Poll(...) - // - if (PR_POLL_EXCEPT & aSelectFlags) { - PR_LOG(gSocketLog, PR_LOG_ERROR, - ("Operation failed via PR_POLL_EXCEPT. [%s:%d %x].\n", + if (mOperation == eSocketOperation_None) + done = PR_TRUE; // nothing to process + + // + // Check for an error during PR_Poll(...) + // + if (PR_POLL_EXCEPT & aSelectFlags) { + LOG(("nsSocketTransport: Operation failed via PR_POLL_EXCEPT. [host=%s:%d this=%x].\n", mHostName, mPort, this)); - // An error has occurred, so cancel the read and/or write operation... - if (mCurrentState == eSocketState_WaitConnect) - mStatus = NS_ERROR_CONNECTION_REFUSED; - else - mStatus = NS_BINDING_FAILED; - } - - if (PR_POLL_HUP & aSelectFlags) { - PR_LOG(gSocketLog, PR_LOG_ERROR, - ("Operation failed via PR_POLL_HUP. [%s:%d %x].\n", - mHostName, mPort, this)); - if (mCurrentState == eSocketState_WaitConnect) { - mStatus = NS_ERROR_CONNECTION_REFUSED; - } else { - mStatus = NS_OK; - } - mCurrentState = eSocketState_Error; - } - - while (!done) - { - // - // If the transport has been suspended, then return NS_OK immediately... - // This removes the transport from the select list... - // - if (mSuspendCount) { - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Transport [%s:%d %x] is suspended.\n", - mHostName, mPort, this)); - - done = PR_TRUE; - mStatus = NS_OK; - continue; - } - - // - // If the transport has been canceled the set the status code to - // NS_BINDING_ABORTED which is a failure code... This will cause the - // transport to move into the error state and end the request... - // - if (NS_FAILED(mCancelStatus)) { - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Transport [%s:%d %x] has been cancelled.\n", - mHostName, mPort, this)); - - mStatus = mCancelStatus; - mCancelStatus = NS_OK; - } - - // - // If an error has occurred then move into the error state... - // - if (NS_FAILED(mStatus) && (NS_BASE_STREAM_WOULD_BLOCK != mStatus)) { - mCurrentState = eSocketState_Error; - } - - switch (mCurrentState) { - case eSocketState_Created: - case eSocketState_Closed: - break; - - case eSocketState_Connected: - // - // A connection has been established with the server - // - PR_AtomicIncrement (&mService -> mConnectedTransports); - mWasConnected = PR_TRUE; - - mSelectFlags = PR_POLL_EXCEPT; - - if (GetReadType() != eSocketRead_None) - { - // Set the select flags for non-blocking reads... - mSelectFlags |= PR_POLL_READ; - - // Fire a notification that the read has started... - if (mReadListener && !mOnStartReadFired) - { - mOnStartReadFired = PR_TRUE; - mReadListener -> OnStartRequest (this , mReadContext); - } - } - - if (GetWriteType() != eSocketWrite_None) - { - // Set the select flags for non-blocking writes... - mSelectFlags |= PR_POLL_WRITE; - - // Fire a notification that the write has started... - if (mWriteObserver && !mOnStartWriteFired) - { - mOnStartWriteFired = PR_TRUE; - mWriteObserver -> OnStartRequest (this, mWriteContext); - } - } - break; - - case eSocketState_Error: - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Transport [%s:%d %x] is in error state.\n", - mHostName, mPort, this)); - - // Cancel any DNS requests... - if (mDNSRequest) { - mDNSRequest->Cancel(NS_BINDING_ABORTED); - mDNSRequest = null_nsCOMPtr(); - } - - // Cancel any read and/or write requests... - SetFlag(eSocketRead_Done); - SetFlag(eSocketWrite_Done); - - CloseConnection (); - - // - // Fall into the Done state... - // - case eSocketState_Done: - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Transport [%s:%d %x] is in done state.\n", - mHostName, mPort, this)); - - mBytesExpected = -1; - - if (GetFlag(eSocketRead_Done)) - { - // Fire a notification that the read has finished... - if (mReadListener) - { - if (!mOnStartReadFired) - { - mOnStartReadFired = PR_TRUE; - mReadListener -> OnStartRequest (this , mReadContext); - } - - mReadListener -> OnStopRequest(this, mReadContext, mStatus, nsnull); - mReadListener = null_nsCOMPtr (); - mReadContext = null_nsCOMPtr (); - } - - // Close the socket transport end of the pipe... - if (mReadPipeOut) - mReadPipeOut -> Close (); - - mReadPipeIn = null_nsCOMPtr (); - mReadPipeOut = null_nsCOMPtr (); - SetReadType (eSocketRead_None); - ClearFlag (eSocketRead_Done); - - // When we have finished reading from the server - // close connection if required to do so... - if (mCloseConnectionOnceDone) - CloseConnection(); - } /* eSocketRead_Done */ - - if (GetFlag(eSocketWrite_Done)) - { - // Fire a notification that the write has finished... - if (mWriteObserver) - { - if (!mOnStartWriteFired) - { - mOnStartWriteFired = PR_TRUE; - mWriteObserver -> OnStartRequest (this, mWriteContext); - } - - mWriteObserver -> OnStopRequest(this, mWriteContext, mStatus, nsnull); - mWriteObserver = null_nsCOMPtr (); - mWriteContext = null_nsCOMPtr (); - } - // Close the socket transport end of the pipe... - if (mWritePipeIn) - mWritePipeIn -> Close (); - - mWritePipeIn = null_nsCOMPtr (); - mWritePipeOut = null_nsCOMPtr (); - SetWriteType (eSocketWrite_None); - ClearFlag (eSocketWrite_Done); - - if (mCloseConnectionOnceDone) - CloseConnection (); - } /* eSocketWrite_Done */ - - // - // Are all read and write requests done? - // - if ((GetReadType() == eSocketRead_None) && - (GetWriteType() == eSocketWrite_None)) - { - mCurrentState = gStateTable[mOperation][mCurrentState]; - mOperation = eSocketOperation_None; - mStatus = NS_OK; - done = PR_TRUE; - } + // An error has occurred, so cancel the read and/or write operation... + if (mCurrentState == eSocketState_WaitConnect) + mStatus = NS_ERROR_CONNECTION_REFUSED; else - { - // Still reading or writing... - mCurrentState = eSocketState_WaitReadWrite; + mStatus = NS_BINDING_FAILED; + } + + if (PR_POLL_HUP & aSelectFlags) { + LOG(("nsSocketTransport: Operation failed via PR_POLL_HUP. [host=%s:%d this=%x].\n", + mHostName, mPort, this)); + if (mCurrentState == eSocketState_WaitConnect) + mStatus = NS_ERROR_CONNECTION_REFUSED; + else + mStatus = NS_OK; + mCurrentState = eSocketState_Error; + } + + while (!done) { + // + // If the transport has been canceled then set the cancel status as + // the transport's status. This will cause the transport to move + // into the error state and end the request. + // + if (NS_FAILED(mCancelStatus)) { + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] has been cancelled.\n", + mHostName, mPort, this)); + + mStatus = mCancelStatus; + mCancelStatus = NS_OK; } - - continue; - - case eSocketState_WaitDNS: - mStatus = doResolveHost(); - break; - - case eSocketState_WaitConnect: - mStatus = doConnection(aSelectFlags); - break; - - case eSocketState_WaitReadWrite: - // Process the read request... - if (GetReadType() != eSocketRead_None) - { - if (mBytesExpected == 0) - { - mStatus = NS_OK; - mSelectFlags &= (~PR_POLL_READ); - - } - else - mStatus = doRead (aSelectFlags); - - if (NS_SUCCEEDED(mStatus)) - { - SetFlag(eSocketRead_Done); - break; - } - } - // Process the write request... - if ((NS_SUCCEEDED(mStatus) || mStatus == NS_BASE_STREAM_WOULD_BLOCK) - && (GetWriteType() != eSocketWrite_None)) { - mStatus = doWrite(aSelectFlags); - if (NS_SUCCEEDED(mStatus)) { - SetFlag(eSocketWrite_Done); + // + // If the transport has been suspended, then return NS_OK immediately. + // This removes the transport from the select list. + // + else if (mSuspendCount) { + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is suspended.\n", + mHostName, mPort, this)); + mStatus = NS_OK; break; - } } - break; - case eSocketState_Timeout: - mStatus = NS_ERROR_NET_TIMEOUT; - break; + // + // If an error has occurred then move into the error state... + // + if (NS_FAILED(mStatus) && (NS_BASE_STREAM_WOULD_BLOCK != mStatus)) + mCurrentState = eSocketState_Error; - default: - NS_ASSERTION(0, "Unexpected state..."); - mStatus = NS_ERROR_FAILURE; - break; + switch (mCurrentState) { + case eSocketState_Created: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in Created state.\n", + mHostName, mPort, this)); + break; + case eSocketState_Closed: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in Closed state.\n", + mHostName, mPort, this)); + break; + + case eSocketState_Connected: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in Connected state.\n", + mHostName, mPort, this)); + // + // A connection has been established with the server + // + PR_AtomicIncrement(&mService->mConnectedTransports); + mWasConnected = PR_TRUE; + + mSelectFlags = PR_POLL_EXCEPT; + + if (GetReadType() != eSocketRead_None) { + // Set the select flags for non-blocking reads... + mSelectFlags |= PR_POLL_READ; + + // Fire a notification that the read has started... + if (mReadListener && !mOnStartReadFired) { + mOnStartReadFired = PR_TRUE; + mReadListener->OnStartRequest(this, mReadContext); + } + } + + if (GetWriteType() != eSocketWrite_None) { + // Set the select flags for non-blocking writes... + mSelectFlags |= PR_POLL_WRITE; + + // Fire a notification that the write has started... + if (mWriteProvider && !mOnStartWriteFired) { + mOnStartWriteFired = PR_TRUE; + mWriteProvider->OnStartRequest(this, mWriteContext); + } + } + break; + + case eSocketState_Error: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in Error state.\n", + mHostName, mPort, this)); + + // Cancel any DNS requests... + if (mDNSRequest) { + mDNSRequest->Cancel(NS_BINDING_ABORTED); + mDNSRequest = 0; + } + + // Cancel any read and/or write requests... + SetFlag(eSocketRead_Done); + SetFlag(eSocketWrite_Done); + + CloseConnection(); + + // + // Fall into the Done state... + // + case eSocketState_Done: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in Done state.\n", + mHostName, mPort, this)); + + mBytesExpected = -1; + + if (GetFlag(eSocketRead_Done)) { + // Fire a notification that the read has finished... + if (mReadListener) { + if (!mOnStartReadFired) { + mOnStartReadFired = PR_TRUE; + mReadListener->OnStartRequest(this, mReadContext); + } + mReadListener->OnStopRequest(this, mReadContext, mStatus, nsnull); + mReadListener = 0; + mReadContext = 0; + } + + if (mSocketInputStream) { + NS_RELEASE(mSocketInputStream); + mSocketInputStream = 0; + } + + // Close the socket transport end of the pipe... + if (mReadPipeOut) { + mReadPipeOut->Close(); + mReadPipeIn = 0; + } + mReadPipeOut = 0; + + SetReadType(eSocketRead_None); + ClearFlag(eSocketRead_Done); + + // When we have finished reading from the server + // close connection if required to do so... + if (mCloseConnectionOnceDone) + CloseConnection(); + } /* eSocketRead_Done */ + + if (GetFlag(eSocketWrite_Done)) { + // Fire a notification that the write has finished... + if (mWriteProvider) { + if (!mOnStartWriteFired) { + mOnStartWriteFired = PR_TRUE; + mWriteProvider->OnStartRequest(this, mWriteContext); + } + mWriteProvider->OnStopRequest(this, mWriteContext, mStatus, nsnull); + mWriteProvider = 0; + mWriteContext = 0; + } + + if (mSocketOutputStream) { + NS_RELEASE(mSocketOutputStream); + mSocketOutputStream = 0; + } + + // Close down the pipe + if (mWritePipeIn) + mWritePipeIn->Close(); + if (mWritePipeOut) + mWritePipeOut->Close(); + mWritePipeIn = 0; + mWritePipeOut = 0; + + SetWriteType(eSocketWrite_None); + ClearFlag(eSocketWrite_Done); + + // Close down the pipe + if (mCloseConnectionOnceDone) + CloseConnection (); + } /* eSocketWrite_Done */ + + // + // Are all read and write requests done? + // + if ((GetReadType() == eSocketRead_None) && + (GetWriteType() == eSocketWrite_None)) { + mCurrentState = gStateTable[mOperation][mCurrentState]; + mOperation = eSocketOperation_None; + mStatus = NS_OK; + done = PR_TRUE; + } + else { + // Still reading or writing... + mCurrentState = eSocketState_WaitReadWrite; + } + continue; + + case eSocketState_WaitDNS: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in WaitDNS state.\n", + mHostName, mPort, this)); + mStatus = doResolveHost(); + break; + + case eSocketState_WaitConnect: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in WaitConnect state.\n", + mHostName, mPort, this)); + mStatus = doConnection(aSelectFlags); + break; + + case eSocketState_WaitReadWrite: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in WaitReadWrite state.\n", + mHostName, mPort, this)); + // Process the read request... + if (GetReadType() != eSocketRead_None) { + if (mBytesExpected == 0) { + mStatus = NS_OK; + mSelectFlags &= (~PR_POLL_READ); + } + else if (GetFlag(eSocketRead_Async)) + mStatus = doReadAsync(aSelectFlags); + else + mStatus = doRead(aSelectFlags); + + if (NS_SUCCEEDED(mStatus)) { + SetFlag(eSocketRead_Done); + break; + } + } + + if (NS_FAILED(mStatus) && (mStatus != NS_BASE_STREAM_WOULD_BLOCK)) + break; + + // Process the write request... + if (GetWriteType() != eSocketWrite_None) { + if (GetFlag(eSocketWrite_Async)) + mStatus = doWriteAsync(aSelectFlags); + else + mStatus = doWrite(aSelectFlags); + + if (NS_SUCCEEDED(mStatus)) + SetFlag(eSocketWrite_Done); + } + break; + + case eSocketState_Timeout: + LOG(("nsSocketTransport: Transport [host=%s:%d this=%x] is in Timeout state.\n", + mHostName, mPort, this)); + mStatus = NS_ERROR_NET_TIMEOUT; + break; + + default: + NS_ASSERTION(0, "Unexpected state..."); + mStatus = NS_ERROR_FAILURE; + break; + } + + // Notify the nsIProgressEventSink of the progress... + if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes)) + fireStatus(mCurrentState); + + // + // If the current state has successfully completed, then move to the + // next state for the current operation... + // + if (NS_SUCCEEDED(mStatus)) + mCurrentState = gStateTable[mOperation][mCurrentState]; + else if (NS_BASE_STREAM_WOULD_BLOCK == mStatus) + done = PR_TRUE; + + // + // Any select flags are *only* valid the first time through the loop... + // + aSelectFlags = 0; } - // Notify the nsIProgressEventSink of the progress... - if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes)) { - fireStatus(mCurrentState); - } - // - // If the current state has successfully completed, then move to the - // next state for the current operation... - // - if (NS_SUCCEEDED(mStatus)) { - mCurrentState = gStateTable[mOperation][mCurrentState]; - } - else if (NS_BASE_STREAM_WOULD_BLOCK == mStatus) { - done = PR_TRUE; - } + // Update the active time for timeout purposes... + mLastActiveTime = PR_IntervalNow(); - // - // Any select flags are *only* valid the first time through the loop... - // - aSelectFlags = 0; - } + LOG(("nsSocketTransport: Leaving Process() [host=%s:%d this=%x], mStatus = %x, " + "CurrentState=%d\n\n", + mHostName, mPort, this, mStatus, mCurrentState)); - // Update the active time for timeout purposes... - mLastActiveTime = PR_IntervalNow(); - - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::Process() [%s:%d %x]. mStatus = %x.\t" - "CurrentState = %d\n\n", - mHostName, mPort, this, mStatus, mCurrentState)); - - PR_ExitMonitor(mMonitor); - return mStatus; + PR_ExitMonitor(mMonitor); + return mStatus; } @@ -715,72 +853,70 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags) //----- nsresult nsSocketTransport::doResolveHost(void) { - nsresult rv = NS_OK; + nsresult rv = NS_OK; - NS_ASSERTION(eSocketState_WaitDNS == mCurrentState, "Wrong state."); + NS_ASSERTION(eSocketState_WaitDNS == mCurrentState, "Wrong state."); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::doResolveHost() [%s:%d %x].\n", - mHostName, mPort, this)); - - // - // The hostname has not been resolved yet... - // - if (PR_IsNetAddrType(&mNetAddress, PR_IpAddrAny)) { - // - // Initialize the port used for the connection... - // - // XXX: The list of ports must be restricted - see net_bad_ports_table[] in - // mozilla/network/main/mkconect.c - // - mNetAddress.ipv6.port = PR_htons(((mProxyPort != -1 && !mProxyTransparent) ? mProxyPort : mPort)); - - NS_WITH_SERVICE(nsIDNSService, - pDNSService, - kDNSService, - &rv); - if (NS_FAILED(rv)) return rv; + LOG(("nsSocketTransport: Entering doResolveHost() [host=%s:%d this=%x].\n", + mHostName, mPort, this)); // - // Give up the SocketTransport lock. This allows the DNS thread to call the - // nsIDNSListener notifications without blocking... + // The hostname has not been resolved yet... // - PR_ExitMonitor(mMonitor); + if (PR_IsNetAddrType(&mNetAddress, PR_IpAddrAny)) { + // + // Initialize the port used for the connection... + // + // XXX: The list of ports must be restricted - see net_bad_ports_table[] in + // mozilla/network/main/mkconect.c + // + mNetAddress.ipv6.port = PR_htons(((mProxyPort != -1 && !mProxyTransparent) ? mProxyPort : mPort)); - rv = pDNSService->Lookup((mProxyHost && !mProxyTransparent) ? mProxyHost : mHostName, - this, - nsnull, - getter_AddRefs(mDNSRequest)); - // - // Aquire the SocketTransport lock again... - // - PR_EnterMonitor(mMonitor); + NS_WITH_SERVICE(nsIDNSService, + pDNSService, + kDNSService, + &rv); + if (NS_FAILED(rv)) return rv; - if (NS_SUCCEEDED(rv)) { - // - // The DNS lookup has finished... It has either failed or succeeded. - // - if (NS_FAILED(mStatus) || !PR_IsNetAddrType(&mNetAddress, PR_IpAddrAny)) { - mDNSRequest = null_nsCOMPtr(); - rv = mStatus; - } - // - // The DNS lookup is being processed... Mark the transport as waiting - // until the result is available... - // - else { - SetFlag(eSocketDNS_Wait); - rv = NS_BASE_STREAM_WOULD_BLOCK; - } + // + // Give up the SocketTransport lock. This allows the DNS thread to call the + // nsIDNSListener notifications without blocking... + // + PR_ExitMonitor(mMonitor); + + rv = pDNSService->Lookup((mProxyHost && !mProxyTransparent) ? mProxyHost : mHostName, + this, + nsnull, + getter_AddRefs(mDNSRequest)); + // + // Aquire the SocketTransport lock again... + // + PR_EnterMonitor(mMonitor); + + if (NS_SUCCEEDED(rv)) { + // + // The DNS lookup has finished... It has either failed or succeeded. + // + if (NS_FAILED(mStatus) || !PR_IsNetAddrType(&mNetAddress, PR_IpAddrAny)) { + mDNSRequest = 0; + rv = mStatus; + } + // + // The DNS lookup is being processed... Mark the transport as waiting + // until the result is available... + // + else { + SetFlag(eSocketDNS_Wait); + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + } } - } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::doResolveHost() [%s:%d %x].\t" - "rv = %x.\n\n", - mHostName, mPort, this, rv)); + LOG(("nsSocketTransport: Leaving doResolveHost() [%s:%d %x].\t" + "rv = %x.\n\n", + mHostName, mPort, this, rv)); - return rv; + return rv; } //----- @@ -801,209 +937,190 @@ nsresult nsSocketTransport::doResolveHost(void) //----- nsresult nsSocketTransport::doConnection(PRInt16 aSelectFlags) { - PRStatus status; - nsresult rv = NS_OK; - PRBool proxyTransparent = PR_FALSE; + PRStatus status; + nsresult rv = NS_OK; + PRBool proxyTransparent = PR_FALSE; - NS_ASSERTION(eSocketState_WaitConnect == mCurrentState, "Wrong state."); + NS_ASSERTION(eSocketState_WaitConnect == mCurrentState, "Wrong state."); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::doConnection() [%s:%d %x].\t" - "aSelectFlags = %x.\n", - mHostName, mPort, this, aSelectFlags)); + LOG(("nsSocketTransport: Entering doConnection() [host=%s:%d this=%x], " + "aSelectFlags=%x.\n", + mHostName, mPort, this, aSelectFlags)); - if (!mSocketFD) { - // - // Step 1: - // Create a new TCP socket structure... - // - if (!mSocketTypeCount) - { - mSocketFD = PR_OpenTCPSocket(PR_AF_INET6); - } - else - { - NS_WITH_SERVICE(nsISocketProviderService, - pProviderService, - kSocketProviderService, - &rv); - - char * destHost = mHostName; - PRInt32 destPort = mPort; - char * proxyHost = mProxyHost; - PRInt32 proxyPort = mProxyPort; - - for (PRUint32 type = 0; type < mSocketTypeCount; type++) - { + if (!mSocketFD) { + // + // Step 1: + // Create a new TCP socket structure... + // + if (!mSocketTypeCount) + mSocketFD = PR_OpenTCPSocket(PR_AF_INET6); + else { + NS_WITH_SERVICE(nsISocketProviderService, + pProviderService, + kSocketProviderService, + &rv); + + char * destHost = mHostName; + PRInt32 destPort = mPort; + char * proxyHost = mProxyHost; + PRInt32 proxyPort = mProxyPort; + + for (PRUint32 type = 0; type < mSocketTypeCount; type++) { + nsCOMPtr pProvider; - nsCOMPtr pProvider; - - if (NS_SUCCEEDED(rv)) - rv = pProviderService->GetSocketProvider(mSocketTypes[type], + if (NS_SUCCEEDED(rv)) + rv = pProviderService->GetSocketProvider(mSocketTypes[type], getter_AddRefs(pProvider)); - if (!NS_SUCCEEDED(rv)) break; + if (!NS_SUCCEEDED(rv)) break; - nsCOMPtr socketInfo; + nsCOMPtr socketInfo; - if (!type) - { - // if this is the first type, we'll want the - // service to allocate a new socket - rv = pProvider->NewSocket(destHost, - destPort, - proxyHost, - proxyPort, - &mSocketFD, - getter_AddRefs(socketInfo)); - } - else - { - // the socket has already been allocated, - // so we just want the service to add itself - // to the stack (such as pushing an io layer) - rv = pProvider->AddToSocket(destHost, + if (!type) { + // if this is the first type, we'll want the + // service to allocate a new socket + rv = pProvider->NewSocket(destHost, destPort, proxyHost, proxyPort, - mSocketFD, + &mSocketFD, getter_AddRefs(socketInfo)); - } + } + else { + // the socket has already been allocated, + // so we just want the service to add itself + // to the stack (such as pushing an io layer) + rv = pProvider->AddToSocket(destHost, + destPort, + proxyHost, + proxyPort, + mSocketFD, + getter_AddRefs(socketInfo)); + } - if (!NS_SUCCEEDED(rv) || !mSocketFD) break; + if (!NS_SUCCEEDED(rv) || !mSocketFD) break; - // if the service was ssl, we want to hold onto the socket info - if (nsCRT::strcmp(mSocketTypes[type], "ssl") == 0 || - nsCRT::strcmp(mSocketTypes[type], "tls") == 0) { - mSecurityInfo = socketInfo; - } - else if (nsCRT::strcmp(mSocketTypes[type], "ssl-forcehandshake") == 0) { - mSecurityInfo = socketInfo; - nsCOMPtr securityInfo(do_QueryInterface(mSecurityInfo, &rv)); - if (NS_SUCCEEDED(rv) && securityInfo) - securityInfo->SetForceHandshake(PR_TRUE); - } - else if (nsCRT::strcmp(mSocketTypes[type], "socks") == 0) { - // since socks is transparent, any layers above - // it do not have to worry about proxy stuff - proxyHost = nsnull; - proxyPort = -1; - proxyTransparent = PR_TRUE; - } - } - } + // if the service was ssl, we want to hold onto the socket info + if (nsCRT::strcmp(mSocketTypes[type], "ssl") == 0 || + nsCRT::strcmp(mSocketTypes[type], "tls") == 0) { + mSecurityInfo = socketInfo; + } + else if (nsCRT::strcmp(mSocketTypes[type], "ssl-forcehandshake") == 0) { + mSecurityInfo = socketInfo; + nsCOMPtr securityInfo = do_QueryInterface(mSecurityInfo, &rv); + if (NS_SUCCEEDED(rv) && securityInfo) + securityInfo->SetForceHandshake(PR_TRUE); + } + else if (nsCRT::strcmp(mSocketTypes[type], "socks") == 0) { + // since socks is transparent, any layers above + // it do not have to worry about proxy stuff + proxyHost = nsnull; + proxyPort = -1; + proxyTransparent = PR_TRUE; + } + } + } - if (mSocketFD) { - PRSocketOptionData opt; - - // Make the socket non-blocking... - opt.option = PR_SockOpt_Nonblocking; - opt.value.non_blocking = PR_TRUE; - status = PR_SetSocketOption(mSocketFD, &opt); - if (PR_SUCCESS != status) { - rv = NS_ERROR_FAILURE; - } - - // XXX: Is this still necessary? + if (mSocketFD) { + PRSocketOptionData opt; + + // Make the socket non-blocking... + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_TRUE; + status = PR_SetSocketOption(mSocketFD, &opt); + if (PR_SUCCESS != status) + rv = NS_ERROR_FAILURE; + + // XXX: Is this still necessary? #if defined(XP_WIN16) // || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK)) - opt.option = PR_SockOpt_Linger; - opt.value.linger.polarity = PR_TRUE; - opt.value.linger.linger = PR_INTERVAL_NO_WAIT; + opt.option = PR_SockOpt_Linger; + opt.value.linger.polarity = PR_TRUE; + opt.value.linger.linger = PR_INTERVAL_NO_WAIT; #ifdef XP_OS2 - PR_SetSocketOption(mSocketFD, &opt); + PR_SetSocketOption(mSocketFD, &opt); #else - PR_SetSocketOption(*sock, &opt); + PR_SetSocketOption(*sock, &opt); #endif #endif /* XP_WIN16 || XP_OS2*/ - } - else { - rv = NS_ERROR_OUT_OF_MEMORY; - } - - // - // Step 2: - // Initiate the connect() to the host... - // - // This is only done the first time doConnection(...) is called. - // - if (NS_SUCCEEDED(rv)) { - status = PR_Connect(mSocketFD, &mNetAddress, gConnectTimeout); - if (PR_SUCCESS != status) { - PRErrorCode code = PR_GetError(); - // - // If the PR_Connect(...) would block, then return WOULD_BLOCK... - // It is the callers responsibility to place the transport on the - // select list of the transport thread... - // - if ((PR_WOULD_BLOCK_ERROR == code) || - (PR_IN_PROGRESS_ERROR == code)) { - - // Set up the select flags for connect... - mSelectFlags = (PR_POLL_READ | PR_POLL_EXCEPT | PR_POLL_WRITE); - rv = NS_BASE_STREAM_WOULD_BLOCK; - } - // - // If the socket is already connected, then return success... - // - else if (PR_IS_CONNECTED_ERROR == code) { - rv = NS_OK; - } - // - // The connection was refused... - // - else { - // Connection refused... - PR_LOG(gSocketLog, PR_LOG_ERROR, - ("Connection Refused [%s:%d %x]. PRErrorCode = %x\n", - mHostName, mPort, this, code)); - - rv = NS_ERROR_CONNECTION_REFUSED; - } - } - } - } - // - // Step 3: - // Process the flags returned by PR_Poll() if any... - // - else if (aSelectFlags) { - if (PR_POLL_EXCEPT & aSelectFlags) { - PR_LOG(gSocketLog, PR_LOG_ERROR, - ("Connection Refused via PR_POLL_EXCEPT. [%s:%d %x].\n", - mHostName, mPort, this)); - - rv = NS_ERROR_CONNECTION_REFUSED; - } - // - // The connection was successful... - // - // PR_Poll(...) returns PR_POLL_WRITE to indicate that the connection is - // established... - // - else if (PR_POLL_WRITE & aSelectFlags) { - rv = NS_OK; - } - } else { - rv = NS_BASE_STREAM_WOULD_BLOCK; - } - - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::doConnection() [%s:%d %x].\t" - "rv = %x.\n\n", - mHostName, mPort, this, rv)); - - if (rv == NS_OK && mSecurityInfo && mProxyHost && mProxyHost && proxyTransparent) { - // if the connection phase is finished, and the ssl layer - // has been pushed, and we were proxying (transparently; ie. nothing - // has to happen in the protocol layer above us), it's time - // for the ssl to "step up" and start doing it's thing. - nsCOMPtr sslControl(do_QueryInterface(mSecurityInfo, &rv)); - if (NS_SUCCEEDED(rv) && sslControl) { - sslControl->ProxyStepUp(); - } - } + } + else + rv = NS_ERROR_OUT_OF_MEMORY; - return rv; + // + // Step 2: + // Initiate the connect() to the host... + // + // This is only done the first time doConnection(...) is called. + // + if (NS_SUCCEEDED(rv)) { + status = PR_Connect(mSocketFD, &mNetAddress, gConnectTimeout); + if (PR_SUCCESS != status) { + PRErrorCode code = PR_GetError(); + // + // If the PR_Connect(...) would block, then return WOULD_BLOCK... + // It is the callers responsibility to place the transport on the + // select list of the transport thread... + // + if ((PR_WOULD_BLOCK_ERROR == code) || + (PR_IN_PROGRESS_ERROR == code)) { + + // Set up the select flags for connect... + mSelectFlags = (PR_POLL_READ | PR_POLL_EXCEPT | PR_POLL_WRITE); + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + // + // If the socket is already connected, then return success... + // + else if (PR_IS_CONNECTED_ERROR == code) + rv = NS_OK; + // + // The connection was refused... + // + else { + // Connection refused... + LOG(("nsSocketTransport: Connection Refused [%s:%d %x]. PRErrorCode = %x\n", + mHostName, mPort, this, code)); + rv = NS_ERROR_CONNECTION_REFUSED; + } + } + } + } + // + // Step 3: + // Process the flags returned by PR_Poll() if any... + // + else if (aSelectFlags) { + if (PR_POLL_EXCEPT & aSelectFlags) { + LOG(("nsSocketTransport: Connection Refused via PR_POLL_EXCEPT. [%s:%d %x].\n", + mHostName, mPort, this)); + + rv = NS_ERROR_CONNECTION_REFUSED; + } + // + // The connection was successful... + // + // PR_Poll(...) returns PR_POLL_WRITE to indicate that the connection is + // established... + // + else if (PR_POLL_WRITE & aSelectFlags) + rv = NS_OK; + } else + rv = NS_BASE_STREAM_WOULD_BLOCK; + + LOG(("nsSocketTransport: Leaving doConnection() [%s:%d %x].\t" + "rv = %x.\n\n", + mHostName, mPort, this, rv)); + + if (rv == NS_OK && mSecurityInfo && mProxyHost && mProxyHost && proxyTransparent) { + // if the connection phase is finished, and the ssl layer + // has been pushed, and we were proxying (transparently; ie. nothing + // has to happen in the protocol layer above us), it's time + // for the ssl to "step up" and start doing it's thing. + nsCOMPtr sslControl = do_QueryInterface(mSecurityInfo, &rv); + if (NS_SUCCEEDED(rv) && sslControl) + sslControl->ProxyStepUp(); + } + return rv; } @@ -1021,50 +1138,39 @@ nsReadFromSocket(nsIOutputStream* out, PRUint32 count, PRUint32 *readCount) { - nsresult rv = NS_OK; - PRInt32 len; - nsReadFromSocketClosure *info = (nsReadFromSocketClosure*)closure; + nsresult rv = NS_OK; + PRInt32 len; + nsReadFromSocketClosure *info = (nsReadFromSocketClosure*)closure; - info->bEOF = PR_FALSE; - *readCount = 0; - if (count > 0) - { - len = PR_Read (info -> fd, toRawSegment, count); - - if (len >= 0) - { - *readCount = (PRUint32)len; - info -> bEOF = (0 == len); - } - // - // Error... - // - else - { - PRErrorCode code = PR_GetError (); - - if (PR_WOULD_BLOCK_ERROR == code) - { - rv = NS_BASE_STREAM_WOULD_BLOCK; + info->bEOF = PR_FALSE; + *readCount = 0; + if (count > 0) { + len = PR_Read(info->fd, toRawSegment, count); + if (len >= 0) { + *readCount = (PRUint32)len; + info->bEOF = (0 == len); + } + // + // Error... + // + else { + PRErrorCode code = PR_GetError(); + if (PR_WOULD_BLOCK_ERROR == code) + rv = NS_BASE_STREAM_WOULD_BLOCK; + else { + PRInt32 osCode = PR_GetOSError (); + LOG(("nsSocketTransport: PR_Read() failed. PRErrorCode = %x, os_error=%d\n", code, osCode)); + info->bEOF = PR_TRUE; + // XXX: What should this error code be? + rv = NS_ERROR_FAILURE; + } + } } - else - { - PRInt32 osCode = PR_GetOSError (); - PR_LOG (gSocketLog, PR_LOG_ERROR, ("PR_Read() failed. PRErrorCode = %x, os_error=%d\n", code, osCode)); - info -> bEOF = PR_TRUE; + LOG(("nsSocketTransport: nsReadFromSocket [fd=%x]. rv = %x. Buffer space = %d. Bytes read =%d\n", + info->fd, rv, count, *readCount)); - // XXX: What should this error code be? - rv = NS_ERROR_FAILURE; - } - } - } - - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsReadFromSocket [fd=%x]. rv = %x. Buffer space = %d. Bytes read =%d\n", - info->fd, rv, count, *readCount)); - - return rv; + return rv; } static NS_METHOD @@ -1075,41 +1181,120 @@ nsWriteToSocket(nsIInputStream* in, PRUint32 count, PRUint32 *writeCount) { - nsresult rv = NS_OK; - PRInt32 len; - PRFileDesc* fd = (PRFileDesc*)closure; + nsresult rv = NS_OK; + PRInt32 len; + PRFileDesc* fd = (PRFileDesc*)closure; - *writeCount = 0; - if (count > 0) { - len = PR_Write(fd, fromRawSegment, count); - if (len > 0) { - *writeCount = (PRUint32)len; + *writeCount = 0; + if (count > 0) { + len = PR_Write(fd, fromRawSegment, count); + if (len > 0) + *writeCount = (PRUint32)len; + // + // Error... + // + else { + PRErrorCode code = PR_GetError(); + if (PR_WOULD_BLOCK_ERROR == code) + rv = NS_BASE_STREAM_WOULD_BLOCK; + else { + LOG(("nsSocketTransport: PR_Write() failed. PRErrorCode = %x\n", code)); + // XXX: What should this error code be? + rv = NS_ERROR_FAILURE; + } + } } + + LOG(("nsSocketTransport: nsWriteToSocket [fd=%x]. rv = %x. Buffer space = %d. Bytes written =%d\n", + fd, rv, count, *writeCount)); + + return rv; +} + +//----- +// +// doReadAsync: +// +// Return values: +// NS_OK +// NS_BASE_STREAM_WOULD_BLOCK +// failure +// +//----- +nsresult nsSocketTransport::doReadAsync(PRInt16 aSelectFlags) +{ + if (!(aSelectFlags & PR_POLL_READ)) { + // wait for the proper select flags + return NS_BASE_STREAM_WOULD_BLOCK; + } + + if (!mSocketInputStream) { + NS_NEWXPCOM(mSocketInputStream, nsSocketInputStream); + if (!mSocketInputStream) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mSocketInputStream); + mSocketInputStream->SetSocketFD(mSocketFD); + } + + mSocketInputStream->ZeroBytesRead(); + + PRUint32 transferAmt = PR_MIN(mBufferMaxSize, MAX_IO_TRANSFER_SIZE); + + LOG(("nsSocketTransport: READING [this=%x] calling listener [offset=%u, count=%u]\n", + this, mReadOffset, transferAmt)); + + nsresult rv = mReadListener->OnDataAvailable(this, + mReadContext, + mSocketInputStream, + mReadOffset, + transferAmt); + // - // Error... + // Handle the error conditions // + if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + // + // Don't suspend the transport if we also writing to it. + // XXX interface does not provide for overlapped i/o (bug 65220). + // + if (mSelectFlags & PR_POLL_WRITE) { + LOG(("nsSocketTransport: READING [this=%x] busy waiting on read!\n", this)); + PR_Sleep(50); // Don't starve the other threads either!! + } else { + LOG(("nsSocketTransport: READING [this=%x] listener would block; suspending self.\n", this)); + mSuspendCount++; + } + } + else if (NS_BASE_STREAM_CLOSED == rv) { + LOG(("nsSocketTransport: READING [this=%x] done reading socket.\n", this)); + rv = NS_OK; // go to done state + } + else if (NS_FAILED(rv)) { + LOG(("nsSocketTransport: READING [this=%x] error reading socket.\n", this)); + } else { - PRErrorCode code = PR_GetError(); + PRUint32 total = mSocketInputStream->GetBytesRead(); + mReadOffset += total; - if (PR_WOULD_BLOCK_ERROR == code) { - rv = NS_BASE_STREAM_WOULD_BLOCK; - } - else { - PR_LOG(gSocketLog, PR_LOG_ERROR, - ("PR_Write() failed. PRErrorCode = %x\n", code)); + if (0 == total) { + LOG(("nsSocketTransport: READING [this=%x] done reading socket.\n", this)); + mSelectFlags &= ~PR_POLL_READ; + } + else { + LOG(("nsSocketTransport: READING [this=%x] read %u bytes [offset=%u]\n", + this, total, mReadOffset)); + // + // Stay in the read state + // + rv = NS_BASE_STREAM_WOULD_BLOCK; + } - // XXX: What should this error code be? - rv = NS_ERROR_FAILURE; - } + if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes) && mEventSink) + // we don't have content length info at the socket level + // just pass 0 through. + mEventSink->OnProgress(this, mReadContext, mReadOffset, 0); } - } - - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsWriteToSocket [fd=%x]. rv = %x. Buffer space = %d. Bytes written =%d\n", - fd, rv, count, *writeCount)); - - return rv; - + return rv; } //----- @@ -1128,90 +1313,172 @@ nsWriteToSocket(nsIInputStream* in, //----- nsresult nsSocketTransport::doRead(PRInt16 aSelectFlags) { - nsReadFromSocketClosure info; - PRUint32 totalBytesWritten; - nsresult rv = NS_OK; + nsReadFromSocketClosure info; + PRUint32 totalBytesWritten; + nsresult rv = NS_OK; - NS_ASSERTION(eSocketState_WaitReadWrite == mCurrentState, "Wrong state."); - NS_ASSERTION(GetReadType() != eSocketRead_None, "Bad Read Type!"); + NS_ASSERTION(eSocketState_WaitReadWrite == mCurrentState, "Wrong state."); + NS_ASSERTION(GetReadType() != eSocketRead_None, "Bad Read Type!"); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::doRead() [%s:%d %x].\t" - "aSelectFlags = %x.\t", - mHostName, mPort, this, aSelectFlags)); + LOG(("nsSocketTransport: Entering doRead() [host=%s:%d this=%x], " + "aSelectFlags=%x.\n", + mHostName, mPort, this, aSelectFlags)); - // - // Fill the stream with as much data from the network as possible... - // - // - totalBytesWritten = 0; - info.fd = mSocketFD; + // + // Fill the stream with as much data from the network as possible... + // + // + totalBytesWritten = 0; + info.fd = mSocketFD; - // Release the transport lock... WriteSegments(...) aquires the nsBuffer - // lock which could cause a deadlock by blocking the socket transport - // thread - // - PR_ExitMonitor(mMonitor); - rv = mReadPipeOut->WriteSegments(nsReadFromSocket, (void*)&info, - MAX_IO_TRANSFER_SIZE, &totalBytesWritten); - PR_EnterMonitor(mMonitor); + // Release the transport lock... WriteSegments(...) aquires the nsPipe + // lock which could cause a deadlock by blocking the socket transport + // thread + // + PR_ExitMonitor(mMonitor); + rv = mReadPipeOut->WriteSegments(nsReadFromSocket, (void*)&info, + MAX_IO_TRANSFER_SIZE, &totalBytesWritten); + PR_EnterMonitor(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("WriteSegments [fd=%x]. rv = %x. Bytes read =%d\n", - mSocketFD, rv, totalBytesWritten)); + LOG(("nsSocketTransport: WriteSegments [fd=%x]. rv = %x. Bytes read =%d\n", + mSocketFD, rv, totalBytesWritten)); - // - // Fire a single OnDataAvaliable(...) notification once as much data has - // been filled into the stream as possible... - // - if (totalBytesWritten) - { - if (mReadListener) - { - nsresult rv1; - - rv1 = mReadListener->OnDataAvailable(this, mReadContext, mReadPipeIn, - mReadOffset, - totalBytesWritten); - - // - // If the consumer returns failure, then cancel the operation... - // - if (NS_FAILED(rv1)) - rv = rv1; + // + // Fire a single OnDataAvaliable(...) notification once as much data has + // been filled into the stream as possible... + // + if (totalBytesWritten) { + if (mReadListener) { + nsresult rv1; + rv1 = mReadListener->OnDataAvailable(this, mReadContext, mReadPipeIn, + mReadOffset, + totalBytesWritten); + // + // If the consumer returns failure, then cancel the operation... + // + if (NS_FAILED(rv1)) + rv = rv1; + } + mReadOffset += totalBytesWritten; } - mReadOffset += totalBytesWritten; - } - // - // Deal with the possible return values... - // - if (NS_SUCCEEDED(rv)) { - if (info.bEOF || mBytesExpected == 0) - { - // EOF condition - mSelectFlags &= (~PR_POLL_READ); - rv = NS_OK; - } - else { // continue to return WOULD_BLOCK until we've completely finished this read - rv = NS_BASE_STREAM_WOULD_BLOCK; + // + // Deal with the possible return values... + // + if (NS_SUCCEEDED(rv)) { + if (info.bEOF || mBytesExpected == 0) { + // EOF condition + mSelectFlags &= (~PR_POLL_READ); + rv = NS_OK; + } + else // continue to return WOULD_BLOCK until we've completely finished this read + rv = NS_BASE_STREAM_WOULD_BLOCK; } - } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::doRead() [%s:%d %x]. rv = %x.\t" - "Total bytes read: %d\n\n", - mHostName, mPort, this, rv, totalBytesWritten)); + LOG(("nsSocketTransport: Leaving doRead() [%s:%d %x]. rv = %x.\t" + "Total bytes read: %d\n\n", + mHostName, mPort, this, rv, totalBytesWritten)); - if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes) && mEventSink) - // we don't have content length info at the socket level - // just pass 0 through. - (void)mEventSink->OnProgress(this, mReadContext, mReadOffset, 0); + if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes) && mEventSink) + // we don't have content length info at the socket level + // just pass 0 through. + (void)mEventSink->OnProgress(this, mReadContext, mReadOffset, 0); - return rv; + return rv; } +//----- +// +// doWriteAsync: +// +// This method is called while holding the SocketTransport lock. It is +// always called on the socket transport thread... +// +// Return values: +// NS_OK - keep on truckin +// NS_BASE_STREAM_WOULD_BLOCK - no data was written at this time +// NS_BASE_STREAM_CLOSED - no more data will ever be written +// +// FAILURE +// +//----- +nsresult nsSocketTransport::doWriteAsync(PRInt16 aSelectFlags) +{ + LOG(("nsSocketTransport: Entering doWriteAsync() [host=%s:%d this=%x], " + "aSelectFlags=%x.\n", + mHostName, mPort, this, aSelectFlags)); + + if (!(aSelectFlags & PR_POLL_WRITE)) { + // wait for the proper select flags + return NS_BASE_STREAM_WOULD_BLOCK; + } + + if (!mSocketOutputStream) { + NS_NEWXPCOM(mSocketOutputStream, nsSocketOutputStream); + if (!mSocketOutputStream) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mSocketOutputStream); + mSocketOutputStream->SetSocketFD(mSocketFD); + } + + mSocketOutputStream->ZeroBytesWritten(); + + PRUint32 transferAmt = PR_MIN(mBufferMaxSize, MAX_IO_TRANSFER_SIZE); + + nsresult rv = mWriteProvider->OnDataWritable(this, + mWriteContext, + mSocketOutputStream, + mWriteOffset, + transferAmt); + + // + // Handle the error conditions + // + if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + // + // Don't suspend the transport if we also reading from it. + // XXX interface does not provide for overlapped i/o (bug 65220). + // + if (mSelectFlags & PR_POLL_READ) { + LOG(("nsSocketTransport: WRITING [this=%x] busy waiting on write!\n", this)); + PR_Sleep(50); // Don't starve the other threads either!! + } else { + LOG(("nsSocketTransport: WRITING [this=%x] provider would block; suspending self.\n", this)); + mSuspendCount++; + } + } + else if (NS_BASE_STREAM_CLOSED == rv) { + LOG(("nsSocketTransport: WRITING [this=%x] provider has finished.\n", this)); + rv = NS_OK; // go to done state + } + else if (NS_FAILED(rv)) { + LOG(("nsSocketTransport: WRITING [this=%x] provider failed, rv=%x.\n", this, rv)); + } + else { + PRUint32 total = mSocketOutputStream->GetBytesWritten(); + mWriteOffset += total; + + if (0 == total) { + LOG(("nsSocketTransport: READING [this=%x] done writing to socket.\n", this)); + mSelectFlags &= ~PR_POLL_WRITE; + } + else { + LOG(("nsSocketTransport: READING [this=%x] wrote %u bytes [offset=%u]\n", + this, total, mWriteOffset)); + // + // Stay in the write state + // + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + + if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes) && mEventSink) + // we don't have content length info at the socket level + // just pass 0 through. + mEventSink->OnProgress(this, mWriteContext, mWriteOffset, 0); + } + return rv; +} //----- // @@ -1221,188 +1488,140 @@ nsresult nsSocketTransport::doRead(PRInt16 aSelectFlags) // always called on the socket transport thread... // // Return values: -// NS_OK -// NS_BASE_STREAM_WOULD_BLOCK +// NS_OK - keep on truckin +// NS_BASE_STREAM_WOULD_BLOCK - no data was written to the pipe at this time +// NS_BASE_STREAM_CLOSED - no more data will ever be written to the pipe // -// NS_ERROR_FAILURE +// FAILURE // //----- nsresult nsSocketTransport::doWrite(PRInt16 aSelectFlags) { - PRUint32 totalBytesWritten; - nsresult rv = NS_OK; + PRUint32 totalBytesWritten = 0; + nsresult rv = NS_OK; - NS_ASSERTION(eSocketState_WaitReadWrite == mCurrentState, "Wrong state."); - NS_ASSERTION(GetWriteType() != eSocketWrite_None, "Bad Write Type!"); + NS_ASSERTION(eSocketState_WaitReadWrite == mCurrentState, "Wrong state."); + NS_ASSERTION(GetWriteType() != eSocketWrite_None, "Bad Write Type!"); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::doWrite() [%s:%d %x].\t" - "aSelectFlags = %x.\t" - "mWriteCount = %d\n", - mHostName, mPort, this, aSelectFlags, mWriteCount)); + LOG(("nsSocketTransport: Entering doWrite() [host=%s:%d this=%x], " + "aSelectFlags=%x.\n", + mHostName, mPort, this, aSelectFlags)); + + rv = doWriteFromBuffer(&totalBytesWritten); + do { + totalBytesWritten = 0; - do - { - totalBytesWritten = 0; - if (mWritePipeIn) { // Writing from a nsIInputStream... rv = doWriteFromBuffer(&totalBytesWritten); + + // Update the counters... + if (mWriteCount > 0) { + NS_ASSERTION(mWriteCount >= (PRInt32)totalBytesWritten, + "wrote more than humanly possible"); + mWriteCount -= totalBytesWritten; + } + mWriteOffset += totalBytesWritten; } - else { - // Writing from a generic nsIInputStream... - rv = doWriteFromStream(&totalBytesWritten); - } + while (NS_SUCCEEDED (rv) && totalBytesWritten); - // Update the counters... - if (mWriteCount > 0) { - NS_ASSERTION(mWriteCount >= (PRInt32)totalBytesWritten, - "wrote more than humanly possible"); - mWriteCount -= totalBytesWritten; - } - - mWriteOffset += totalBytesWritten; - - } - while (NS_SUCCEEDED (rv) && totalBytesWritten); - - // - // The write operation has completed... - // - if (NS_SUCCEEDED(rv) && (0 == totalBytesWritten || // eof, or - GetFlag(eSocketWrite_Async) && 0 == mWriteCount) ) { // wrote everything - mSelectFlags &= (~PR_POLL_WRITE); - rv = NS_OK; - } - else if (NS_SUCCEEDED(rv)) { - // We wrote something, so loop and try again. - // return WOULD_BLOCK to keep the transport on the select list... - rv = NS_BASE_STREAM_WOULD_BLOCK; - } - else if (NS_BASE_STREAM_WOULD_BLOCK == rv) { // - // If the buffer is empty, then notify the reader and stop polling - // for write until there is data in the buffer. See the OnWrite() - // notification... + // The write operation has completed... // - NS_ASSERTION((0 == totalBytesWritten), "returned NS_BASE_STREAM_WOULD_BLOCK and a writeCount"); - if (GetFlag(eSocketWrite_Sync)) { - // We can only wait if we created the stream (a pipe -- created in the synchronous - // OpenOutputStream case). If we didn't create it, we couldn't have been the buffer - // observer, so we won't get any notification when more data becomes available. - SetFlag(eSocketWrite_Wait); - mSelectFlags &= (~PR_POLL_WRITE); + if (NS_SUCCEEDED(rv) && (0 == totalBytesWritten || // eof, or + GetFlag(eSocketWrite_Async) && 0 == mWriteCount) ) { // wrote everything + mSelectFlags &= (~PR_POLL_WRITE); + rv = NS_OK; + } + else if (NS_SUCCEEDED(rv)) { + // + // We wrote something, so loop and try again. + // return WOULD_BLOCK to keep the transport on the select list... + // + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + else if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + // + // If the buffer is empty, then notify the reader and stop polling + // for write until there is data in the buffer. See the OnWrite() + // notification... + // + NS_ASSERTION((0 == totalBytesWritten), + "returned NS_BASE_STREAM_WOULD_BLOCK and a writeCount"); + // + // We can only wait if we created the stream (a pipe -- created + // in the synchronous OpenOutputStream case). If we didn't create + // it, we couldn't have been the buffer observer, so we won't get + // any notification when more data becomes available. + // + SetFlag(eSocketWrite_Wait); + mSelectFlags &= (~PR_POLL_WRITE); } - } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::doWrite() [%s:%d %x]. rv = %x.\t" - "Total bytes written: %d\n\n", - mHostName, mPort, this, rv, totalBytesWritten)); + LOG(("nsSocketTransport: Leaving doWrite() [%s:%d %x]. rv = %x.\t" + "Total bytes written: %d\n\n", + mHostName, mPort, this, rv, totalBytesWritten)); - if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes) && mEventSink) - // we don't have content length info at the socket level - // just pass 0 through. - (void)mEventSink->OnProgress(this, mWriteContext, mWriteOffset, 0); + if (!(nsIChannel::LOAD_BACKGROUND & mLoadAttributes) && mEventSink) + // we don't have content length info at the socket level + // just pass 0 through. + mEventSink->OnProgress(this, mWriteContext, mWriteOffset, 0); - return rv; + return rv; } +// +// Try to write out everything in the pipe. Return NS_OK unless +// there is an error. This should never return WOULD_BLOCK! +// nsresult nsSocketTransport::doWriteFromBuffer(PRUint32 *aCount) { - nsresult rv; - PRUint32 transferCount; + nsresult rv; + PRUint32 transferCount; - // Figure out how much data to write... - if (mWriteCount > 0) { - transferCount = PR_MIN(mWriteCount, MAX_IO_TRANSFER_SIZE); - } else { - transferCount = MAX_IO_TRANSFER_SIZE; - } + // Figure out how much data to write... + if (mWriteCount > 0) + transferCount = PR_MIN(mWriteCount, MAX_IO_TRANSFER_SIZE); + else + transferCount = MAX_IO_TRANSFER_SIZE; - // - // Release the transport lock... ReadSegments(...) aquires the nsBuffer - // lock which could cause a deadlock by blocking the socket transport - // thread - // - PR_ExitMonitor(mMonitor); - - // - // Write the data to the network... - // - // return values: - // NS_BASE_STREAM_WOULD_BLOCK - The stream is empty. - // NS_OK - The write succeeded. - // FAILURE - Something bad happened. - // - rv = mWritePipeIn->ReadSegments(nsWriteToSocket, (void*)mSocketFD, - transferCount, aCount); - PR_EnterMonitor(mMonitor); + // + // Release the transport lock... ReadSegments(...) aquires the nsBuffer + // lock which could cause a deadlock by blocking the socket transport + // thread + // + PR_ExitMonitor(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("ReadSegments [fd=%x]. rv = %x. Bytes written =%d\n", - mSocketFD, rv, *aCount)); + // + // Write the data to the network... + // + // return values: + // NS_BASE_STREAM_WOULD_BLOCK - The stream is empty. + // NS_OK - The write succeeded. + // FAILURE - Something bad happened. + // + rv = mWritePipeIn->ReadSegments(nsWriteToSocket, (void *) mSocketFD, + transferCount, aCount); + PR_EnterMonitor(mMonitor); - return rv; + LOG(("nsSocketTransport: ReadSegments [fd=%x]. rv = %x. Bytes written =%d\n", + mSocketFD, rv, *aCount)); + + //NS_POSTCONDITION(rv != NS_BASE_STREAM_WOULD_BLOCK, "ReadSegments returned WOULD_BLOCK!"); + return rv; } - -nsresult nsSocketTransport::doWriteFromStream(PRUint32 *aCount) -{ - nsresult rv = NS_OK; - PRUint32 transferCount; - - // Figure out how much data to write... - if (mWriteCount > 0) { - transferCount = PR_MIN(mWriteCount, MAX_IO_TRANSFER_SIZE); - } else { - transferCount = MAX_IO_TRANSFER_SIZE; - } - - // - // Read some data from the stream... - // - // return values: - // NS_BASE_STREAM_WOULD_BLOCK - The stream is empty. - // NS_OK - The write succeeded. - // FAILURE - Something bad happened. - // - if (0 == mWriteBufferLength) { - // When the buffer is empty, read the next chunk of data into - // the buffer... - rv = mWriteFromStream->Read(mWriteBuffer, transferCount, - &mWriteBufferLength); - mWriteBufferIndex = 0; - } - - *aCount = 0; - if (NS_SUCCEEDED(rv)) { - // Try to send the data to the network. - rv = nsWriteToSocket(nsnull, (void*)mSocketFD, mWriteBuffer + mWriteBufferIndex, 0, - mWriteBufferLength, aCount); - // Update the buffer index and length with the actual amount of data - // that was sent... - mWriteBufferIndex += *aCount; - mWriteBufferLength -= *aCount; - } - - return rv; -} - - -nsresult nsSocketTransport::CloseConnection (PRBool bNow) +nsresult nsSocketTransport::CloseConnection(PRBool bNow) { PRStatus status; nsresult rv = NS_OK; - if (!bNow) // close connection once done. - { + if (!bNow) { // close connection once done. mCloseConnectionOnceDone = PR_TRUE; return NS_OK; } - if (!mSocketFD) - { + if (!mSocketFD) { mCurrentState = eSocketState_Closed; return NS_OK; } @@ -1413,11 +1632,9 @@ nsresult nsSocketTransport::CloseConnection (PRBool bNow) mSocketFD = nsnull; - if (mWasConnected) - { + if (mWasConnected) { if (mService) - PR_AtomicDecrement (&mService -> mConnectedTransports); - + PR_AtomicDecrement(&mService->mConnectedTransports); mWasConnected = PR_FALSE; } @@ -1488,8 +1705,7 @@ nsSocketTransport::SetReuseCount (PRUint32 count) NS_IMETHODIMP nsSocketTransport::GetBytesExpected (PRInt32 * bytes) { - if (bytes != NULL) - { + if (bytes != NULL) { *bytes = mBytesExpected; return NS_OK; } @@ -1501,12 +1717,10 @@ nsSocketTransport::SetBytesExpected (PRInt32 bytes) { nsAutoMonitor mon(mMonitor); - if (mCurrentState == eSocketState_WaitReadWrite) - { + if (mCurrentState == eSocketState_WaitReadWrite) { mBytesExpected = bytes; - if (mBytesExpected == 0) - mService -> Wakeup (this); + mService->Wakeup(this); } return NS_OK; } @@ -1540,105 +1754,114 @@ nsSocketTransport::GetName(PRUnichar* *result) NS_IMETHODIMP nsSocketTransport::IsPending(PRBool *result) { - *result = mCurrentState != eSocketState_Created - && mCurrentState != eSocketState_Closed; - return NS_OK; + *result = mCurrentState != eSocketState_Created + && mCurrentState != eSocketState_Closed; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::GetStatus(nsresult *status) { - if (NS_FAILED(mCancelStatus)) - *status = mCancelStatus; - else if (mStatus == NS_BASE_STREAM_WOULD_BLOCK) - *status = NS_OK; - else - *status = mStatus; - return NS_OK; + if (NS_FAILED(mCancelStatus)) + *status = mCancelStatus; + else if (mStatus == NS_BASE_STREAM_WOULD_BLOCK) + *status = NS_OK; + else + *status = mStatus; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::Cancel(nsresult status) { - NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); - nsresult rv = NS_OK; + NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); + nsresult rv = NS_OK; - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); - mCancelStatus = status; - // - // Wake up the transport on the socket transport thread so it can - // be removed from the select list... - // - mLastActiveTime = PR_IntervalNow (); - rv = mService->AddToWorkQ(this); + mCancelStatus = status; - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Canceling nsSocketTransport [%s:%d %x]. rv = %x\n", - mHostName, mPort, this, rv)); + // A cancelled transport cannot be suspended. + mSuspendCount = 0; - return rv; + // + // Wake up the transport on the socket transport thread so it can + // be removed from the select list... + // + mLastActiveTime = PR_IntervalNow (); + rv = mService->AddToWorkQ(this); + + LOG(("nsSocketTransport: Canceling [%s:%d %x]. rv = %x\n", + mHostName, mPort, this, rv)); + + return rv; } NS_IMETHODIMP nsSocketTransport::Suspend(void) { - nsresult rv = NS_OK; + // Silently ignore calls to suspend a cancelled transport + if (NS_FAILED(mCancelStatus)) + return NS_OK; - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + nsresult rv = NS_OK; - mSuspendCount += 1; - // - // Wake up the transport on the socket transport thread so it can - // be removed from the select list... - // - // Only do this the first time a transport is suspended... - // - if (1 == mSuspendCount) { - mLastActiveTime = PR_IntervalNow (); - rv = mService->AddToWorkQ(this); - } + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Suspending nsSocketTransport [%s:%d %x]. rv = %x\t" - "mSuspendCount = %d.\n", - mHostName, mPort, this, rv, mSuspendCount)); + mSuspendCount += 1; + // + // Wake up the transport on the socket transport thread so it can + // be removed from the select list... + // + // Only do this the first time a transport is suspended... + // + if (1 == mSuspendCount) { + mLastActiveTime = PR_IntervalNow (); + rv = mService->AddToWorkQ(this); + } - return rv; + LOG(("nsSocketTransport: Suspending [%s:%d %x]. rv = %x\t" + "mSuspendCount = %d.\n", + mHostName, mPort, this, rv, mSuspendCount)); + + return rv; } NS_IMETHODIMP nsSocketTransport::Resume(void) { - nsresult rv = NS_OK; + // Silently ignore calls to resume a cancelled transport + if (NS_FAILED(mCancelStatus)) + return NS_OK; - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + nsresult rv = NS_OK; - if (mSuspendCount) { - mSuspendCount -= 1; - // - // Wake up the transport on the socket transport thread so it can - // be resumed... - // - if (0 == mSuspendCount) { - mLastActiveTime = PR_IntervalNow (); - rv = mService->AddToWorkQ(this); + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); + + if (mSuspendCount) { + mSuspendCount -= 1; + // + // Wake up the transport on the socket transport thread so it can + // be resumed... + // + if (0 == mSuspendCount) { + mLastActiveTime = PR_IntervalNow (); + rv = mService->AddToWorkQ(this); + } + } else { + // Only a suspended transport can be resumed... + rv = NS_ERROR_FAILURE; } - } else { - // Only a suspended transport can be resumed... - rv = NS_ERROR_FAILURE; - } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("Resuming nsSocketTransport [%s:%d %x]. rv = %x\t" - "mSuspendCount = %d.\n", - mHostName, mPort, this, rv, mSuspendCount)); + LOG(("nsSocketTransport: Resuming [%s:%d %x]. rv = %x\t" + "mSuspendCount = %d.\n", + mHostName, mPort, this, rv, mSuspendCount)); - return rv; + return rv; } // @@ -1647,15 +1870,13 @@ nsSocketTransport::Resume(void) // -------------------------------------------------------------------------- // // The pipe observer is used by the following methods: -// AsyncRead(...) // OpenInputStream(...) // OpenOutputStream(...). // NS_IMETHODIMP nsSocketTransport::OnFull(nsIOutputStream* out) { - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnFull() [%s:%d %x] nsIOutputStream=%x.\n", + LOG(("nsSocketTransport: OnFull() [%s:%d %x] nsIOutputStream=%x.\n", mHostName, mPort, this, out)); // @@ -1663,13 +1884,11 @@ nsSocketTransport::OnFull(nsIOutputStream* out) // transport from the select list until the consumer can // make room... // - if (out == mReadPipeOut.get()) - { + if (out == mReadPipeOut.get()) { // Enter the socket transport lock... nsAutoMonitor mon(mMonitor); - if (!GetFlag(eSocketRead_Wait)) - { + if (!GetFlag(eSocketRead_Wait)) { SetFlag(eSocketRead_Wait); mSelectFlags &= (~PR_POLL_READ); } @@ -1685,68 +1904,66 @@ nsSocketTransport::OnFull(nsIOutputStream* out) NS_IMETHODIMP nsSocketTransport::OnWrite(nsIOutputStream* out, PRUint32 aCount) { - nsresult rv = NS_OK; + nsresult rv = NS_OK; - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnWrite() [%s:%d %x]. nsIOutputStream=%x Count=%d\n", - mHostName, mPort, this, out, aCount)); + LOG(("nsSocketTransport: OnWrite() [%s:%d %x]. nsIOutputStream=%x Count=%d\n", + mHostName, mPort, this, out, aCount)); - // - // The consumer has written some data into the pipe... If the transport - // was waiting to write some data to the network, then add it to the - // select list... - // - if (out == mWritePipeOut.get()) { - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + // + // The consumer has written some data into the pipe... If the transport + // was waiting to write some data to the network, then add it to the + // select list... + // + if (out == mWritePipeOut.get()) { + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); - if (GetFlag(eSocketWrite_Wait)) { - ClearFlag(eSocketWrite_Wait); - mSelectFlags |= PR_POLL_WRITE; + if (GetFlag(eSocketWrite_Wait)) { + ClearFlag(eSocketWrite_Wait); + mSelectFlags |= PR_POLL_WRITE; - // Start the crank. - mOperation = eSocketOperation_ReadWrite; - mLastActiveTime = PR_IntervalNow (); - rv = mService->AddToWorkQ(this); + // Start the crank. + mOperation = eSocketOperation_ReadWrite; + mLastActiveTime = PR_IntervalNow (); + rv = mService->AddToWorkQ(this); + } } - } - return NS_OK; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::OnEmpty(nsIInputStream* in) { - nsresult rv = NS_OK; + nsresult rv = NS_OK; - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnEmpty() [%s:%d %x] nsIInputStream=%x.\n", - mHostName, mPort, this, in)); + LOG(("nsSocketTransport: OnEmpty() [%s:%d %x] nsIInputStream=%x.\n", + mHostName, mPort, this, in)); - // - // The consumer has emptied the pipe... If the transport was waiting - // for room in the pipe, then put it back on the select list... - // - if (in == mReadPipeIn.get()) { - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); - - if (GetFlag(eSocketRead_Wait)) { - ClearFlag(eSocketRead_Wait); - mSelectFlags |= PR_POLL_READ; - mLastActiveTime = PR_IntervalNow (); - rv = mService->AddToWorkQ(this); + // + // The consumer has emptied the pipe... If the transport was waiting + // for room in the pipe, then put it back on the select list... + // + if (in == mReadPipeIn.get()) { + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); + + if (GetFlag(eSocketRead_Wait)) { + ClearFlag(eSocketRead_Wait); + mSelectFlags |= PR_POLL_READ; + mLastActiveTime = PR_IntervalNow (); + rv = mService->AddToWorkQ(this); + } } - } - return rv; + return rv; } NS_IMETHODIMP nsSocketTransport::OnClose(nsIInputStream* inStr) { - return NS_OK; + return NS_OK; } // @@ -1757,11 +1974,10 @@ nsSocketTransport::OnClose(nsIInputStream* inStr) NS_IMETHODIMP nsSocketTransport::OnStartLookup(nsISupports *aContext, const char *aHostName) { - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnStartLookup(...) [%s:%d %x]. Host is %s\n", - mHostName, mPort, this, aHostName)); + LOG(("nsSocketTransport: OnStartLookup(...) [%s:%d %x]. Host is %s\n", + mHostName, mPort, this, aHostName)); - return NS_OK; + return NS_OK; } NS_IMETHODIMP @@ -1769,42 +1985,38 @@ nsSocketTransport::OnFound(nsISupports *aContext, const char* aHostName, nsHostEnt *aHostEnt) { - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); - nsresult rv = NS_OK; -#ifdef PR_LOGGING - char addrbuf[50]; + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); + nsresult rv = NS_OK; +#if defined(PR_LOGGING) + char addrbuf[50]; #endif - if (aHostEnt->hostEnt.h_addr_list - && aHostEnt->hostEnt.h_addr_list[0]) { - if (aHostEnt->hostEnt.h_addrtype == PR_AF_INET6) { - memcpy(&mNetAddress.ipv6.ip, aHostEnt->hostEnt.h_addr_list[0], sizeof(mNetAddress.ipv6.ip)); - } else { - PR_ConvertIPv4AddrToIPv6(*(PRUint32*)aHostEnt->hostEnt.h_addr_list[0], &mNetAddress.ipv6.ip); - } -#ifdef PR_LOGGING - PR_NetAddrToString(&mNetAddress, addrbuf, sizeof(addrbuf)); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnFound(...) [%s:%d %x]." + if (aHostEnt->hostEnt.h_addr_list && aHostEnt->hostEnt.h_addr_list[0]) { + if (aHostEnt->hostEnt.h_addrtype == PR_AF_INET6) + memcpy(&mNetAddress.ipv6.ip, aHostEnt->hostEnt.h_addr_list[0], sizeof(mNetAddress.ipv6.ip)); + else + PR_ConvertIPv4AddrToIPv6(*(PRUint32*)aHostEnt->hostEnt.h_addr_list[0], &mNetAddress.ipv6.ip); +#if defined(PR_LOGGING) + PR_NetAddrToString(&mNetAddress, addrbuf, sizeof(addrbuf)); + LOG(("nsSocketTransport: OnFound(...) [%s:%d %x]." " DNS lookup succeeded => %s (%s)\n", mHostName, mPort, this, aHostEnt->hostEnt.h_name, addrbuf)); #endif - } else { - // XXX: What should happen here? The GetHostByName(...) succeeded but - // there are *no* A records... - rv = NS_ERROR_FAILURE; + } else { + // XXX: What should happen here? The GetHostByName(...) succeeded but + // there are *no* A records... + rv = NS_ERROR_FAILURE; - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnFound(...) [%s:%d %x]." + LOG(("nsSocketTransport: OnFound(...) [%s:%d %x]." " DNS lookup succeeded (%s) but no address returned!", mHostName, mPort, this, aHostEnt->hostEnt.h_name)); - } + } - return rv; + return rv; } NS_IMETHODIMP @@ -1812,30 +2024,28 @@ nsSocketTransport::OnStopLookup(nsISupports *aContext, const char *aHostName, nsresult aStatus) { - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("nsSocketTransport::OnStopLookup(...) [%s:%d %x]." - " Status = %x Host is %s\n", - mHostName, mPort, this, aStatus, aHostName)); + LOG(("nsSocketTransport: OnStopLookup(...) [%s:%d %x]." + " Status = %x Host is %s\n", + mHostName, mPort, this, aStatus, aHostName)); - // Release our reference to the DNS Request... - mDNSRequest = null_nsCOMPtr(); + // Release our reference to the DNS Request... + mDNSRequest = 0; - // If the lookup failed, set the status... - if (NS_FAILED(aStatus)) { - mStatus = aStatus; - } + // If the lookup failed, set the status... + if (NS_FAILED(aStatus)) + mStatus = aStatus; - // Start processing the transport again - if necessary... - if (GetFlag(eSocketDNS_Wait)) { - ClearFlag(eSocketDNS_Wait); - mLastActiveTime = PR_IntervalNow (); - mService->AddToWorkQ(this); - } + // Start processing the transport again - if necessary... + if (GetFlag(eSocketDNS_Wait)) { + ClearFlag(eSocketDNS_Wait); + mLastActiveTime = PR_IntervalNow(); + mService->AddToWorkQ(this); + } - return NS_OK; + return NS_OK; } @@ -1847,46 +2057,43 @@ nsSocketTransport::OnStopLookup(nsISupports *aContext, NS_IMETHODIMP nsSocketTransport::GetOriginalURI(nsIURI* *aURL) { - nsStdURL *url; - url = new nsStdURL(nsnull); - // XXX: not sure this is correct behavior, but we should somehow - // prevent reusing the same nsSocketTransport for different SSL hosts - // in proxied case. - if (mProxyHost && !(mProxyTransparent || mSSLProxy)) - { - url->SetHost(mProxyHost); - url->SetPort(mProxyPort); - } - else - { - url->SetHost(mHostName); - url->SetPort(mPort); - } + nsStdURL *url; + url = new nsStdURL(nsnull); + // XXX: not sure this is correct behavior, but we should somehow + // prevent reusing the same nsSocketTransport for different SSL hosts + // in proxied case. + if (mProxyHost && !(mProxyTransparent || mSSLProxy)) { + url->SetHost(mProxyHost); + url->SetPort(mProxyPort); + } + else { + url->SetHost(mHostName); + url->SetPort(mPort); + } + nsresult rv; + rv = CallQueryInterface((nsIURL*)url, aURL); - nsresult rv; - rv = CallQueryInterface((nsIURL*)url, aURL); - - return rv; + return rv; } NS_IMETHODIMP nsSocketTransport::SetOriginalURI(nsIURI* aURL) { - NS_NOTREACHED("SetOriginalURI"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetOriginalURI"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::GetURI(nsIURI* *aURL) { - return GetOriginalURI(aURL); + return GetOriginalURI(aURL); } NS_IMETHODIMP nsSocketTransport::SetURI(nsIURI* aURL) { - return SetOriginalURI(aURL); + return SetOriginalURI(aURL); } NS_IMETHODIMP @@ -1898,42 +2105,24 @@ nsSocketTransport::AsyncRead(nsIStreamListener* aListener, // Enter the socket transport lock... nsAutoMonitor mon(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::AsyncRead() [%s:%d %x]\n", + LOG(("nsSocketTransport: Entering AsyncRead() [host=%s:%d this=%x]\n", mHostName, mPort, this)); // If a read is already in progress then fail... if (GetReadType() != eSocketRead_None) rv = NS_ERROR_IN_PROGRESS; - // Create a new non-blocking input stream for reading data into... - if (NS_SUCCEEDED(rv) && !mReadPipeIn) - { - // XXXbe calling out of module with a lock held... - rv = NS_NewPipe(getter_AddRefs(mReadPipeIn), - getter_AddRefs(mReadPipeOut), - mBufferSegmentSize, mBufferMaxSize, - PR_TRUE, PR_TRUE); - if (NS_SUCCEEDED(rv)) - rv = mReadPipeIn->SetObserver(this); - - if (NS_SUCCEEDED(rv)) - rv = mReadPipeOut->SetObserver(this); - } - - // Create a marshalling stream listener to receive notifications... - if (NS_SUCCEEDED(rv)) - { - mOnStartReadFired = PR_FALSE; - rv = NS_NewAsyncStreamListener(getter_AddRefs(mReadListener), - aListener, NS_CURRENT_EVENTQ); - } - - if (NS_SUCCEEDED(rv)) - { - // Store the context used for this read... + if (NS_SUCCEEDED(rv)) { mReadContext = aContext; - + mOnStartReadFired = PR_FALSE; + // Create a stream listener proxy to receive notifications... + rv = NS_NewStreamListenerProxy(getter_AddRefs(mReadListener), + aListener, nsnull, + mBufferSegmentSize, + mBufferMaxSize); + } + + if (NS_SUCCEEDED(rv)) { mOperation = eSocketOperation_ReadWrite; SetReadType(eSocketRead_Async); @@ -1941,8 +2130,7 @@ nsSocketTransport::AsyncRead(nsIStreamListener* aListener, rv = mService->AddToWorkQ(this); } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::AsyncRead() [%s:%d %x]. rv = %x.\n", + LOG(("nsSocketTransport: Leaving AsyncRead() [host=%s:%d this=%x], rv=%x.\n", mHostName, mPort, this, rv)); return rv; @@ -1950,73 +2138,42 @@ nsSocketTransport::AsyncRead(nsIStreamListener* aListener, NS_IMETHODIMP -nsSocketTransport::AsyncWrite(nsIInputStream* aFromStream, - nsIStreamObserver* aObserver, +nsSocketTransport::AsyncWrite(nsIStreamProvider* aProvider, nsISupports* aContext) { nsresult rv = NS_OK; - + // Enter the socket transport lock... nsAutoMonitor mon(mMonitor); - - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::AsyncWrite() [%s:%d %x]\n", + + LOG(("nsSocketTransport: Entering AsyncWrite() [host=%s:%d this=%x]\n", mHostName, mPort, this)); // If a write is already in progress then fail... if (GetWriteType() != eSocketWrite_None) rv = NS_ERROR_IN_PROGRESS; - if (NS_SUCCEEDED(rv)) - { -// mWritePipeIn = do_QueryInterface(aFromStream, &rv); -// if (NS_FAILED(rv)) -// { - // If the input stream does not support nsIInputStream, then - // an intermediate buffer is necessary to move the data from the - // stream to the network... - mWriteFromStream = aFromStream; - - rv = NS_OK; - // Create the intermediate buffer if necessary... - if (!mWriteBuffer) - { - mWriteBuffer = (char *)PR_Malloc(MAX_IO_TRANSFER_SIZE+1); - if (!mWriteBuffer) - rv = NS_ERROR_OUT_OF_MEMORY; - } - // Reset to buffer index and length. - mWriteBufferLength = 0; - mWriteBufferIndex = 0; -// } - } - - if (NS_SUCCEEDED(rv)) - { + if (NS_SUCCEEDED(rv)) { mWriteContext = aContext; - - // Create a marshalling stream observer to receive notifications... - mOnStartWriteFired = PR_FALSE; - if (aObserver) { - mWriteObserver = null_nsCOMPtr(); - rv = NS_NewAsyncStreamObserver(getter_AddRefs(mWriteObserver), - aObserver, NS_CURRENT_EVENTQ); + if (aProvider) { + mWriteProvider = 0; + // Create a stream provider proxy to handle notifications... + rv = NS_NewStreamProviderProxy(getter_AddRefs(mWriteProvider), + aProvider, NS_CURRENT_EVENTQ); } } - if (NS_SUCCEEDED(rv)) - { + if (NS_SUCCEEDED(rv)) { mOperation = eSocketOperation_ReadWrite; SetWriteType(eSocketWrite_Async); - mLastActiveTime = PR_IntervalNow (); + mLastActiveTime = PR_IntervalNow (); rv = mService->AddToWorkQ(this); } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::AsyncWrite() [%s:%d %x]. rv = %x.\n", + LOG(("nsSocketTransport: Leaving AsyncWrite() [host=%s:%d this=%x], rv=%x.\n", mHostName, mPort, this, rv)); return rv; @@ -2026,178 +2183,166 @@ nsSocketTransport::AsyncWrite(nsIInputStream* aFromStream, NS_IMETHODIMP nsSocketTransport::OpenInputStream(nsIInputStream* *result) { - nsresult rv = NS_OK; + nsresult rv = NS_OK; - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::OpenInputStream() [%s:%d %x].\n", - mHostName, mPort, this)); + LOG(("nsSocketTransport: Entering OpenInputStream() [host=%s:%d this=%x].\n", + mHostName, mPort, this)); - // If a read is already in progress then fail... - if (GetReadType() != eSocketRead_None) { - rv = NS_ERROR_IN_PROGRESS; - } + // If a read is already in progress then fail... + if (GetReadType() != eSocketRead_None) + rv = NS_ERROR_IN_PROGRESS; - // Create a new blocking input stream for reading data into... - if (NS_SUCCEEDED(rv)) { - mReadListener = null_nsCOMPtr(); - mReadContext = null_nsCOMPtr(); - - // XXXbe calling out of module with a lock held... - rv = NS_NewPipe(getter_AddRefs(mReadPipeIn), - getter_AddRefs(mReadPipeOut), - mBufferSegmentSize, mBufferMaxSize); + // Create a new blocking input stream for reading data into... if (NS_SUCCEEDED(rv)) { - if (NS_SUCCEEDED(rv)) - rv = mReadPipeIn->SetObserver(this); - if (NS_SUCCEEDED(rv)) - rv = mReadPipeOut->SetObserver(this); + mReadListener = 0; + mReadContext = 0; + + // XXXbe calling out of module with a lock held... + rv = NS_NewPipe(getter_AddRefs(mReadPipeIn), + getter_AddRefs(mReadPipeOut), + mBufferSegmentSize, mBufferMaxSize); if (NS_SUCCEEDED(rv)) { - rv = mReadPipeOut->SetNonBlocking(PR_TRUE); - *result = mReadPipeIn; - NS_IF_ADDREF(*result); + if (NS_SUCCEEDED(rv)) + rv = mReadPipeIn->SetObserver(this); + if (NS_SUCCEEDED(rv)) + rv = mReadPipeOut->SetObserver(this); + if (NS_SUCCEEDED(rv)) { + rv = mReadPipeOut->SetNonBlocking(PR_TRUE); + *result = mReadPipeIn; + NS_IF_ADDREF(*result); + } } } - } - if (NS_SUCCEEDED(rv)) { - mOperation = eSocketOperation_ReadWrite; - SetReadType(eSocketRead_Sync); + if (NS_SUCCEEDED(rv)) { + mOperation = eSocketOperation_ReadWrite; + SetReadType(eSocketRead_Sync); - mLastActiveTime = PR_IntervalNow (); - rv = mService->AddToWorkQ(this); - } + mLastActiveTime = PR_IntervalNow (); + rv = mService->AddToWorkQ(this); + } - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::OpenInputStream() [%s:%d %x].\t" - "rv = %x.\n", - mHostName, mPort, this, rv)); + LOG(("nsSocketTransport: Leaving OpenInputStream() [host=%s:%d this=%x], " + "rv=%x.\n", + mHostName, mPort, this, rv)); - return rv; + return rv; } NS_IMETHODIMP nsSocketTransport::OpenOutputStream(nsIOutputStream* *result) { - nsresult rv = NS_OK; + nsresult rv = NS_OK; - // Enter the socket transport lock... - nsAutoMonitor mon(mMonitor); + // Enter the socket transport lock... + nsAutoMonitor mon(mMonitor); - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("+++ Entering nsSocketTransport::OpenOutputStream() [%s:%d %x].\n", - mHostName, mPort, this)); + LOG(("nsSocketTransport: Entering OpenOutputStream() [host=%s:%d this=%x].\n", + mHostName, mPort, this)); - // If a write is already in progress then fail... - if (GetWriteType() != eSocketWrite_None) { - rv = NS_ERROR_IN_PROGRESS; - } + // If a write is already in progress then fail... + if (GetWriteType() != eSocketWrite_None) + rv = NS_ERROR_IN_PROGRESS; - if (NS_SUCCEEDED(rv)) { - // No observer or write context is available... - mWriteCount = 0; - mWriteObserver = null_nsCOMPtr(); - mWriteContext = null_nsCOMPtr(); - - // We want a pipe here so the caller can "write" into one end - // and the other end (aWriteStream) gets the data. This data - // is then written to the underlying socket when nsSocketTransport::doWrite() - // is called. - - nsCOMPtr out; - nsCOMPtr in; - // XXXbe calling out of module with a lock held... - rv = NS_NewPipe(getter_AddRefs(in), getter_AddRefs(out), - mBufferSegmentSize, mBufferMaxSize, - PR_TRUE, PR_FALSE); - if (NS_SUCCEEDED(rv)) - rv = in->SetObserver(this); - - if (NS_SUCCEEDED(rv)) - rv = out->SetObserver(this); - if (NS_SUCCEEDED(rv)) { - mWritePipeIn = in; - *result = out; - NS_IF_ADDREF(*result); + // No provider or write context is available... + mWriteCount = 0; + mWriteProvider = 0; + mWriteContext = 0; - mWritePipeOut = out; + // We want a pipe here so the caller can "write" into one end + // and the other end (aWriteStream) gets the data. This data + // is then written to the underlying socket when nsSocketTransport::doWrite() + // is called. + + nsCOMPtr out; + nsCOMPtr in; + // XXXbe calling out of module with a lock held... + rv = NS_NewPipe(getter_AddRefs(in), getter_AddRefs(out), + mBufferSegmentSize, mBufferMaxSize, + PR_TRUE, PR_FALSE); + if (NS_SUCCEEDED(rv)) + rv = in->SetObserver(this); + + if (NS_SUCCEEDED(rv)) + rv = out->SetObserver(this); + + if (NS_SUCCEEDED(rv)) { + mWritePipeIn = in; + *result = out; + NS_IF_ADDREF(*result); + + mWritePipeOut = out; + } + + SetWriteType(eSocketWrite_Sync); } - SetWriteType(eSocketWrite_Sync); - } -/* - if (NS_SUCCEEDED(rv)) { - mOperation = eSocketOperation_ReadWrite; - // Start the crank. - rv = mService->AddToWorkQ(this); - } -*/ - PR_LOG(gSocketLog, PR_LOG_DEBUG, - ("--- Leaving nsSocketTransport::OpenOutputStream() [%s:%d %x].\t" - "rv = %x.\n", - mHostName, mPort, this, rv)); + LOG(("nsSocketTransport: Leaving OpenOutputStream() [host=%s:%d this=%x], " + "rv = %x.\n", + mHostName, mPort, this, rv)); - return rv; + return rv; } NS_IMETHODIMP nsSocketTransport::GetLoadAttributes(PRUint32 *aLoadAttributes) { - *aLoadAttributes = mLoadAttributes; - return NS_OK; + *aLoadAttributes = mLoadAttributes; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::SetLoadAttributes(PRUint32 aLoadAttributes) { - mLoadAttributes = aLoadAttributes; - return NS_OK; + mLoadAttributes = aLoadAttributes; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::GetContentType(char * *aContentType) { - *aContentType = nsnull; - return NS_ERROR_FAILURE; // XXX doesn't make sense for transports + *aContentType = nsnull; + return NS_ERROR_FAILURE; // XXX doesn't make sense for transports } NS_IMETHODIMP nsSocketTransport::SetContentType(const char *aContentType) { - return NS_ERROR_FAILURE; // XXX doesn't make sense for transports + return NS_ERROR_FAILURE; // XXX doesn't make sense for transports } NS_IMETHODIMP nsSocketTransport::GetContentLength(PRInt32 *aContentLength) { - // The content length is always unknown for transports... - *aContentLength = -1; - return NS_OK; + // The content length is always unknown for transports... + *aContentLength = -1; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::SetContentLength(PRInt32 aContentLength) { - NS_NOTREACHED("SetContentLength"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetContentLength"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::GetTransferOffset(PRUint32 *aTransferOffset) { - NS_NOTREACHED("GetTransferOffset"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("GetTransferOffset"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::SetTransferOffset(PRUint32 aTransferOffset) { - NS_NOTREACHED("SetTransferOffset"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetTransferOffset"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP @@ -2217,115 +2362,115 @@ nsSocketTransport::SetTransferCount(PRInt32 aTransferCount) NS_IMETHODIMP nsSocketTransport::GetBufferSegmentSize(PRUint32 *aBufferSegmentSize) { - NS_NOTREACHED("GetBufferSegmentSize"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("GetBufferSegmentSize"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::SetBufferSegmentSize(PRUint32 aBufferSegmentSize) { - NS_NOTREACHED("SetBufferSegmentSize"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetBufferSegmentSize"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::GetBufferMaxSize(PRUint32 *aBufferMaxSize) { - NS_NOTREACHED("GetBufferMaxSize"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("GetBufferMaxSize"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::SetBufferMaxSize(PRUint32 aBufferMaxSize) { - NS_NOTREACHED("SetBufferMaxSize"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetBufferMaxSize"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::GetLocalFile(nsIFile* *file) { - *file = nsnull; - return NS_OK; + *file = nsnull; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::GetPipeliningAllowed(PRBool *aPipeliningAllowed) { - *aPipeliningAllowed = PR_FALSE; - return NS_OK; + *aPipeliningAllowed = PR_FALSE; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::SetPipeliningAllowed(PRBool aPipeliningAllowed) { - NS_NOTREACHED("SetPipeliningAllowed"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetPipeliningAllowed"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::GetLoadGroup(nsILoadGroup * *aLoadGroup) { - NS_ASSERTION(0, "transports shouldn't end up in groups"); - return NS_ERROR_FAILURE; + NS_ASSERTION(0, "transports shouldn't end up in groups"); + return NS_ERROR_FAILURE; } NS_IMETHODIMP nsSocketTransport::SetLoadGroup(nsILoadGroup* aLoadGroup) { - NS_NOTREACHED("SetLoadGroup"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_NOTREACHED("SetLoadGroup"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSocketTransport::GetOwner(nsISupports * *aOwner) { - *aOwner = mOwner.get(); - NS_IF_ADDREF(*aOwner); - return NS_OK; + *aOwner = mOwner.get(); + NS_IF_ADDREF(*aOwner); + return NS_OK; } NS_IMETHODIMP nsSocketTransport::SetOwner(nsISupports * aOwner) { - mOwner = aOwner; - return NS_OK; + mOwner = aOwner; + return NS_OK; } NS_IMETHODIMP nsSocketTransport::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks) { - *aNotificationCallbacks = mCallbacks.get(); - NS_IF_ADDREF(*aNotificationCallbacks); - return NS_OK; + *aNotificationCallbacks = mCallbacks.get(); + NS_IF_ADDREF(*aNotificationCallbacks); + return NS_OK; } NS_IMETHODIMP nsSocketTransport::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) { - mCallbacks = aNotificationCallbacks; - mEventSink = 0; - - // Get a nsIProgressEventSink so that we can fire status/progress on it- - if (mCallbacks) { - nsCOMPtr sink; - nsresult rv = mCallbacks->GetInterface(NS_GET_IID(nsIProgressEventSink), - getter_AddRefs(sink)); - if (NS_SUCCEEDED(rv)) { - // Now generate a proxied event sink- - NS_WITH_SERVICE(nsIProxyObjectManager, - proxyMgr, kProxyObjectManagerCID, &rv); - if (NS_SUCCEEDED(rv)) { - rv = proxyMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ, // primordial thread - should change? - NS_GET_IID(nsIProgressEventSink), - sink, - PROXY_ASYNC | PROXY_ALWAYS, - getter_AddRefs(mEventSink)); - } + mCallbacks = aNotificationCallbacks; + mEventSink = 0; + + // Get a nsIProgressEventSink so that we can fire status/progress on it- + if (mCallbacks) { + nsCOMPtr sink; + nsresult rv = mCallbacks->GetInterface(NS_GET_IID(nsIProgressEventSink), + getter_AddRefs(sink)); + if (NS_SUCCEEDED(rv)) { + // Now generate a proxied event sink- + NS_WITH_SERVICE(nsIProxyObjectManager, + proxyMgr, kProxyObjectManagerCID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = proxyMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ, // primordial thread - should change? + NS_GET_IID(nsIProgressEventSink), + sink, + PROXY_ASYNC | PROXY_ALWAYS, + getter_AddRefs(mEventSink)); + } + } } - } - return NS_OK; + return NS_OK; } NS_IMETHODIMP @@ -2333,10 +2478,8 @@ nsSocketTransport::IsAlive (PRUint32 seconds, PRBool *alive) { *alive = PR_TRUE; - if (mSocketFD) - { - if (mLastActiveTime != PR_INTERVAL_NO_WAIT) - { + if (mSocketFD) { + if (mLastActiveTime != PR_INTERVAL_NO_WAIT) { PRUint32 now = PR_IntervalToSeconds (PR_IntervalNow ()); PRUint32 last = PR_IntervalToSeconds ( mLastActiveTime ); PRUint32 diff = now - last; @@ -2348,8 +2491,7 @@ nsSocketTransport::IsAlive (PRUint32 seconds, PRBool *alive) static char c; PRInt32 rval = PR_Read (mSocketFD, &c, 0); - if (rval < 0) - { + if (rval < 0) { PRErrorCode code = PR_GetError (); if (code != PR_WOULD_BLOCK_ERROR) @@ -2380,7 +2522,8 @@ nsSocketTransport::SetIdleTimeout (PRUint32 seconds) } NS_IMETHODIMP -nsSocketTransport::GetIPStr(PRUint32 aLen, char **_retval) { +nsSocketTransport::GetIPStr(PRUint32 aLen, char **_retval) +{ NS_ASSERTION(aLen > 0, "caller must pass in str len"); *_retval = (char*)nsMemory::Alloc(aLen); if (!*_retval) return NS_ERROR_FAILURE; @@ -2450,27 +2593,27 @@ nsSocketTransport::fireStatus(PRUint32 aCode) nsresult status; switch (aCode) { - case eSocketState_Created: - case eSocketState_WaitDNS: + case eSocketState_Created: + case eSocketState_WaitDNS: status = NS_NET_STATUS_RESOLVING_HOST; break; - case eSocketState_Connected: + case eSocketState_Connected: status = NS_NET_STATUS_CONNECTED_TO; break; - case eSocketState_WaitReadWrite: + case eSocketState_WaitReadWrite: status = mWriteContext ? NS_NET_STATUS_RECEIVING_FROM : NS_NET_STATUS_SENDING_TO; break; - case eSocketState_WaitConnect: + case eSocketState_WaitConnect: status = NS_NET_STATUS_CONNECTING_TO; break; - case eSocketState_Closed: - case eSocketState_Done: - case eSocketState_Timeout: - case eSocketState_Error: - case eSocketState_Max: - default: + case eSocketState_Closed: + case eSocketState_Done: + case eSocketState_Timeout: + case eSocketState_Error: + case eSocketState_Max: + default: status = NS_OK; break; } diff --git a/mozilla/netwerk/base/src/nsSocketTransport.h b/mozilla/netwerk/base/src/nsSocketTransport.h index a80c464733e..4456f305641 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport.h +++ b/mozilla/netwerk/base/src/nsSocketTransport.h @@ -35,6 +35,7 @@ #include "nsIOutputStream.h" #include "nsIEventQueueService.h" #include "nsIStreamListener.h" +#include "nsIStreamProvider.h" #include "nsIDNSListener.h" #include "nsIPipe.h" #include "nsIProgressEventSink.h" @@ -114,6 +115,8 @@ enum nsSocketReadWriteInfo { // Forward declarations... class nsSocketTransportService; +class nsSocketInputStream; +class nsSocketOutputStream; class nsIInterfaceRequestor; class nsSocketTransport : public nsISocketTransport, @@ -166,7 +169,9 @@ protected: nsresult doConnection(PRInt16 aSelectFlags); nsresult doResolveHost(void); nsresult doRead(PRInt16 aSelectFlags); + nsresult doReadAsync(PRInt16 aSelectFlags); nsresult doWrite(PRInt16 aSelectFlags); + nsresult doWriteAsync(PRInt16 aSelectFlags); nsresult doWriteFromBuffer(PRUint32 *aCount); nsresult doWriteFromStream(PRUint32 *aCount); @@ -222,40 +227,41 @@ protected: nsSocketOperation mOperation; nsCOMPtr mOwner; nsCOMPtr mSecurityInfo; + PRInt32 mProxyPort; char* mProxyHost; PRBool mProxyTransparent; PRBool mSSLProxy; - nsCOMPtr mReadContext; - nsCOMPtr mReadListener; - nsCOMPtr mReadPipeIn; - nsCOMPtr mReadPipeOut; + + nsSocketTransportService* mService; + PRUint32 mReadWriteState; PRInt16 mSelectFlags; - nsSocketTransportService* mService; + nsresult mStatus; + PRInt32 mSuspendCount; + PRFileDesc* mSocketFD; PRUint32 mSocketTypeCount; char* *mSocketTypes; + + nsCOMPtr mReadListener; + nsCOMPtr mReadContext; PRUint32 mReadOffset; - PRUint32 mWriteOffset; - nsresult mStatus; - PRInt32 mSuspendCount; - PRInt32 mWriteCount; + + nsCOMPtr mWriteProvider; nsCOMPtr mWriteContext; - PRInt32 mBytesExpected; + PRUint32 mWriteOffset; + PRInt32 mWriteCount; + + PRInt32 mBytesExpected; PRUint32 mReuseCount; PRUint32 mLastReuseCount; + + nsSocketInputStream *mSocketInputStream; + nsSocketOutputStream *mSocketOutputStream; - // The following four members are used when AsyncWrite(...) is called - // with an nsIInputStream which does not also support the - // nsIBufferedInputStream interface... - // - nsCOMPtr mWriteFromStream; - char * mWriteBuffer; - PRUint32 mWriteBufferIndex; - PRUint32 mWriteBufferLength; - - nsCOMPtr mWriteObserver; + nsCOMPtr mReadPipeIn; + nsCOMPtr mReadPipeOut; nsCOMPtr mWritePipeIn; nsCOMPtr mWritePipeOut; PRUint32 mBufferSegmentSize; diff --git a/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp b/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp new file mode 100644 index 00000000000..edc244b35dc --- /dev/null +++ b/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsStreamListenerProxy.h" +#include "nsIGenericFactory.h" +#include "nsIInputStream.h" +#include "nsIPipe.h" +#include "nsAutoLock.h" + +#define LOG(args) PR_LOG(gStreamProxyLog, PR_LOG_DEBUG, args) + +#define DEFAULT_BUFFER_SEGMENT_SIZE 2048 +#define DEFAULT_BUFFER_MAX_SIZE (4*2048) + +// +//---------------------------------------------------------------------------- +// Design Overview +// +// A stream listener proxy maintains a pipe. When the channel makes data +// available, the proxy copies as much of that data as possible into the pipe. +// If data was written to the pipe, then the proxy posts an asynchronous event +// corresponding to the amount of data written. If no data could be written, +// because the pipe was full, then WOULD_BLOCK is returned to the channel, +// indicating that the channel should suspend itself. +// +// Once suspended in this manner, the channel is only resumed when the pipe is +// emptied. +// +// XXX The current design does NOT allow the channel to be "externally" +// suspended!! For the moment this is not a problem, but it should be fixed. +//---------------------------------------------------------------------------- +// + +// +//---------------------------------------------------------------------------- +// nsStreamListenerProxy implementation... +//---------------------------------------------------------------------------- +// + +nsStreamListenerProxy::nsStreamListenerProxy() + : mLock(nsnull) + , mPendingCount(0) + , mListenerStatus(NS_OK) +{ } + +nsStreamListenerProxy::~nsStreamListenerProxy() +{ + if (mLock) { + PR_DestroyLock(mLock); + mLock = nsnull; + } +} + +PRUint32 +nsStreamListenerProxy::GetPendingCount() +{ + return PR_AtomicSet((PRInt32 *) &mPendingCount, 0); +} + +// +//---------------------------------------------------------------------------- +// nsOnDataAvailableEvent internal class... +//---------------------------------------------------------------------------- +// +class nsOnDataAvailableEvent : public nsStreamObserverEvent +{ +public: + nsOnDataAvailableEvent(nsStreamProxyBase *aProxy, + nsIChannel *aChannel, + nsISupports *aContext, + nsIInputStream *aSource, + PRUint32 aOffset) + : nsStreamObserverEvent(aProxy, aChannel, aContext) + , mSource(aSource) + , mOffset(aOffset) + { + MOZ_COUNT_CTOR(nsOnDataAvailableEvent); + } + + ~nsOnDataAvailableEvent() + { + MOZ_COUNT_DTOR(nsOnDataAvailableEvent); + } + + NS_IMETHOD HandleEvent(); + +protected: + nsCOMPtr mSource; + PRUint32 mOffset; +}; + +NS_IMETHODIMP +nsOnDataAvailableEvent::HandleEvent() +{ + LOG(("nsOnDataAvailableEvent: HandleEvent [event=%x, chan=%x]", this, mChannel.get())); + + nsStreamListenerProxy *listenerProxy = + NS_STATIC_CAST(nsStreamListenerProxy *, mProxy); + + if (NS_FAILED(listenerProxy->GetListenerStatus())) { + LOG(("nsOnDataAvailableEvent: Discarding event [listener_status=%x, chan=%x]\n", + listenerProxy->GetListenerStatus(), mChannel.get())); + return NS_ERROR_FAILURE; + } + + nsCOMPtr listener = listenerProxy->GetListener(); + if (!listener) { + LOG(("nsOnDataAvailableEvent: Already called OnStopRequest (listener is NULL), [chan=%x]\n", + mChannel.get())); + return NS_ERROR_FAILURE; + } + + nsresult status = NS_OK; + nsresult rv = mChannel->GetStatus(&status); + NS_ASSERTION(NS_SUCCEEDED(rv), "GetStatus failed"); + + // + // We should only forward this event to the listener if the channel is + // still in a "good" state. Because these events are being processed + // asynchronously, there is a very real chance that the listener might + // have cancelled the channel after _this_ event was triggered. + // + if (NS_SUCCEEDED(status)) { + // + // Find out from the listener proxy how many bytes to report. + // + PRUint32 count = listenerProxy->GetPendingCount(); + +#if defined(PR_LOGGING) + { + PRUint32 avail; + mSource->Available(&avail); + LOG(("nsOnDataAvailableEvent: Calling the consumer's OnDataAvailable " + "[offset=%u count=%u avail=%u chan=%x]\n", + mOffset, count, avail, mChannel.get())); + } +#endif + + // Forward call to listener + rv = listener->OnDataAvailable(mChannel, mContext, mSource, mOffset, count); + + LOG(("nsOnDataAvailableEvent: Done with the consumer's OnDataAvailable " + "[rv=%x, chan=%x]\n", + rv, mChannel.get())); + + // + // XXX Need to suspend the underlying channel... must consider + // other pending events (such as OnStopRequest). These + // should not be forwarded to the listener if the channel + // is suspended. Also, handling the Resume could be tricky. + // + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { + NS_NOTREACHED("listener returned NS_BASE_STREAM_WOULD_BLOCK" + " -- support for this is not implemented"); + rv = NS_ERROR_NOT_IMPLEMENTED; + } + + // + // Update the listener status + // + listenerProxy->SetListenerStatus(rv); + } + else + LOG(("nsOnDataAvailableEvent: Not calling OnDataAvailable [chan=%x]", + mChannel.get())); + return NS_OK; +} + +// +//---------------------------------------------------------------------------- +// nsISupports implementation... +//---------------------------------------------------------------------------- +// +NS_IMPL_ISUPPORTS_INHERITED3(nsStreamListenerProxy, + nsStreamProxyBase, + nsIStreamListenerProxy, + nsIStreamListener, + nsIInputStreamObserver) + +// +//---------------------------------------------------------------------------- +// nsIStreamObserver implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamListenerProxy::OnStartRequest(nsIChannel *aChannel, + nsISupports *aContext) +{ + + return nsStreamProxyBase::OnStartRequest(aChannel, aContext); +} + +NS_IMETHODIMP +nsStreamListenerProxy::OnStopRequest(nsIChannel *aChannel, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aStatusText) +{ + // + // We are done with the pipe. + // + mPipeIn = 0; + mPipeOut = 0; + + return nsStreamProxyBase::OnStopRequest(aChannel, aContext, + aStatus, aStatusText); +} + +// +//---------------------------------------------------------------------------- +// nsIStreamListener implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamListenerProxy::OnDataAvailable(nsIChannel *aChannel, + nsISupports *aContext, + nsIInputStream *aSource, + PRUint32 aOffset, + PRUint32 aCount) +{ + nsresult rv; + PRUint32 bytesWritten=0; + + LOG(("nsStreamListenerProxy: OnDataAvailable [offset=%u count=%u chan=%x]\n", + aOffset, aCount, aChannel)); + + NS_PRECONDITION(mChannelToResume == 0, "Unexpected call to OnDataAvailable"); + NS_PRECONDITION(mPipeIn, "Pipe not initialized"); + NS_PRECONDITION(mPipeOut, "Pipe not initialized"); + + // + // Any non-successful listener status gets passed back to the caller + // + { + nsresult status = mListenerStatus; + if (NS_FAILED(status)) { + LOG(("nsStreamListenerProxy: Listener failed [status=%x chan=%x]\n", status, aChannel)); + return status; + } + } + + // + // Enter the ChannelToResume lock + // + { + nsAutoLock lock(mLock); + + // + // Try to copy data into the pipe. + // + // If the pipe is full, then suspend the calling channel. It + // will be resumed when the pipe is emptied. Being inside the + // ChannelToResume lock ensures that the resume will follow + // the suspend. + // + rv = mPipeOut->WriteFrom(aSource, aCount, &bytesWritten); + + LOG(("nsStreamListenerProxy: Wrote data to pipe [rv=%x count=%u bytesWritten=%u chan=%x]\n", + rv, aCount, bytesWritten, aChannel)); + + if (NS_FAILED(rv)) { + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { + LOG(("nsStreamListenerProxy: Setting channel to resume [chan=%x]\n", aChannel)); + mChannelToResume = aChannel; + } + return rv; + } + else if (bytesWritten == 0) { + LOG(("nsStreamListenerProxy: Copied zero bytes; not posting an event [chan=%x]\n", aChannel)); + return NS_BASE_STREAM_CLOSED; // there was no more data to read! + } + } + + // + // Update the pending count; return if able to piggy-back on a pending event. + // + PRUint32 total = PR_AtomicAdd((PRInt32 *) &mPendingCount, bytesWritten); + if (total > bytesWritten) { + LOG(("nsStreamListenerProxy: Piggy-backing pending event [total=%u, chan=%x]\n", + total, aChannel)); + return NS_OK; + } + + // + // Post an event for the number of bytes actually written. + // + nsOnDataAvailableEvent *ev = + new nsOnDataAvailableEvent(this, aChannel, aContext, mPipeIn, aOffset); + if (!ev) return NS_ERROR_OUT_OF_MEMORY; + + rv = ev->FireEvent(GetEventQueue()); + if (NS_FAILED(rv)) { + delete ev; + return rv; + } + return NS_OK; +} + +// +//---------------------------------------------------------------------------- +// nsIStreamListenerProxy implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamListenerProxy::Init(nsIStreamListener *aListener, + nsIEventQueue *aEventQ, + PRUint32 aBufferSegmentSize, + PRUint32 aBufferMaxSize) +{ + NS_PRECONDITION(GetReceiver() == nsnull, "Listener already set"); + NS_PRECONDITION(GetEventQueue() == nsnull, "Event queue already set"); + + // + // Create the ChannelToResume lock + // + mLock = PR_NewLock(); + if (!mLock) return NS_ERROR_OUT_OF_MEMORY; + + // + // Create the pipe + // + if (aBufferSegmentSize == 0) + aBufferSegmentSize = DEFAULT_BUFFER_SEGMENT_SIZE; + if (aBufferMaxSize == 0) + aBufferMaxSize = DEFAULT_BUFFER_MAX_SIZE; + // The segment size must not exceed the maximum + aBufferSegmentSize = PR_MIN(aBufferMaxSize, aBufferSegmentSize); + + nsresult rv = NS_NewPipe(getter_AddRefs(mPipeIn), + getter_AddRefs(mPipeOut), + aBufferSegmentSize, + aBufferMaxSize, + PR_TRUE, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + rv = mPipeIn->SetObserver(this); + if (NS_FAILED(rv)) return rv; + + SetReceiver(aListener); + return SetEventQueue(aEventQ); +} + +// +//---------------------------------------------------------------------------- +// nsIInputStreamObserver implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamListenerProxy::OnEmpty(nsIInputStream *aInputStream) +{ + LOG(("nsStreamListenerProxy: OnEmpty\n")); + // + // The pipe has been emptied by the listener. If the channel + // has been suspended (waiting for the pipe to be emptied), then + // go ahead and resume it. But take care not to resume while + // holding the "ChannelToResume" lock. + // + nsCOMPtr chan; + { + nsAutoLock lock(mLock); + chan = mChannelToResume; + mChannelToResume = 0; + } + if (chan) { + LOG(("nsStreamListenerProxy: Resuming channel\n")); + chan->Resume(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsStreamListenerProxy::OnClose(nsIInputStream *aInputStream) +{ + LOG(("nsStreamListenerProxy: OnClose\n")); + return NS_OK; +} diff --git a/mozilla/netwerk/base/src/nsStreamListenerProxy.h b/mozilla/netwerk/base/src/nsStreamListenerProxy.h new file mode 100644 index 00000000000..85172516bdb --- /dev/null +++ b/mozilla/netwerk/base/src/nsStreamListenerProxy.h @@ -0,0 +1,51 @@ +#ifndef nsStreamListenerProxy_h__ +#define nsStreamListenerProxy_h__ + +#include "nsStreamObserverProxy.h" +#include "nsIStreamListener.h" +#include "nsIChannel.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsCOMPtr.h" + +class nsStreamListenerProxy : public nsStreamProxyBase + , public nsIStreamListenerProxy + , public nsIInputStreamObserver +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSISTREAMLISTENERPROXY + NS_DECL_NSIINPUTSTREAMOBSERVER + + nsStreamListenerProxy(); + virtual ~nsStreamListenerProxy(); + + nsIStreamListener *GetListener() + { + return NS_STATIC_CAST(nsIStreamListener *, GetReceiver()); + } + + void SetListenerStatus(nsresult status) + { + mListenerStatus = status; + } + + nsresult GetListenerStatus() + { + return mListenerStatus; + } + + PRUint32 GetPendingCount(); + +protected: + nsCOMPtr mPipeIn; + nsCOMPtr mPipeOut; + nsCOMPtr mChannelToResume; + PRLock *mLock; + PRUint32 mPendingCount; + nsresult mListenerStatus; +}; + +#endif /* !nsStreamListenerProxy_h__ */ diff --git a/mozilla/netwerk/base/src/nsStreamObserverProxy.cpp b/mozilla/netwerk/base/src/nsStreamObserverProxy.cpp new file mode 100644 index 00000000000..3df29809b2d --- /dev/null +++ b/mozilla/netwerk/base/src/nsStreamObserverProxy.cpp @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsStreamObserverProxy.h" +#include "nsIGenericFactory.h" +#include "nsIServiceManager.h" +#include "nsIEventQueueService.h" +#include "nsString.h" + +#if defined(PR_LOGGING) +PRLogModuleInfo *gStreamProxyLog; +#endif +#define LOG(args) PR_LOG(gStreamProxyLog, PR_LOG_DEBUG, args) + +static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID); + +// +//---------------------------------------------------------------------------- +// nsStreamObserverEvent implementation... +//---------------------------------------------------------------------------- +// +nsStreamObserverEvent::nsStreamObserverEvent(nsStreamProxyBase *aProxy, + nsIChannel *aChannel, + nsISupports *aContext) + : mProxy(aProxy) + , mChannel(aChannel) + , mContext(aContext) +{ + NS_IF_ADDREF(mProxy); +} + +nsStreamObserverEvent::~nsStreamObserverEvent() +{ + NS_IF_RELEASE(mProxy); +} + +nsresult +nsStreamObserverEvent::FireEvent(nsIEventQueue *aEventQ) +{ + NS_PRECONDITION(aEventQ, "null event queue"); + + PL_InitEvent(&mEvent, nsnull, + (PLHandleEventProc) nsStreamObserverEvent::HandlePLEvent, + (PLDestroyEventProc) nsStreamObserverEvent::DestroyPLEvent); + + PRStatus status = aEventQ->PostEvent(&mEvent); + return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE; +} + +void PR_CALLBACK +nsStreamObserverEvent::HandlePLEvent(PLEvent *aEvent) +{ + nsStreamObserverEvent *ev = GET_STREAM_OBSERVER_EVENT(aEvent); + NS_ASSERTION(ev, "null event"); + + // Pass control the real event handler + if (ev) + ev->HandleEvent(); +} + +void PR_CALLBACK +nsStreamObserverEvent::DestroyPLEvent(PLEvent *aEvent) +{ + nsStreamObserverEvent *ev = GET_STREAM_OBSERVER_EVENT(aEvent); + NS_ASSERTION(ev, "null event"); + delete ev; +} + +// +//---------------------------------------------------------------------------- +// nsOnStartRequestEvent internal class... +//---------------------------------------------------------------------------- +// +class nsOnStartRequestEvent : public nsStreamObserverEvent +{ +public: + nsOnStartRequestEvent(nsStreamProxyBase *aProxy, + nsIChannel *aChannel, + nsISupports *aContext) + : nsStreamObserverEvent(aProxy, aChannel, aContext) + { + MOZ_COUNT_CTOR(nsOnStartRequestEvent); + } + + ~nsOnStartRequestEvent() + { + MOZ_COUNT_DTOR(nsOnStartRequestEvent); + } + + NS_IMETHOD HandleEvent(); +}; + +NS_IMETHODIMP +nsOnStartRequestEvent::HandleEvent() +{ + LOG(("nsOnStartRequestEvent: HandleEvent [event=%x chan=%x]\n", + this, mChannel.get())); + + nsCOMPtr observer = mProxy->GetReceiver(); + if (!observer) { + LOG(("nsOnStartRequestEvent: Already called OnStopRequest (observer is NULL)\n")); + return NS_ERROR_FAILURE; + } + + return observer->OnStartRequest(mChannel, mContext); +} + +// +//---------------------------------------------------------------------------- +// nsOnStopRequestEvent internal class... +//---------------------------------------------------------------------------- +// +class nsOnStopRequestEvent : public nsStreamObserverEvent +{ +public: + nsOnStopRequestEvent(nsStreamProxyBase *aProxy, + nsIChannel *aChannel, nsISupports *aContext, + nsresult aStatus, const PRUnichar *aStatusText) + : nsStreamObserverEvent(aProxy, aChannel, aContext) + , mStatus(aStatus) + , mStatusText(aStatusText) + { + MOZ_COUNT_CTOR(nsOnStopRequestEvent); + } + + ~nsOnStopRequestEvent() + { + MOZ_COUNT_DTOR(nsOnStopRequestEvent); + } + + NS_IMETHOD HandleEvent(); + +protected: + nsresult mStatus; + nsString mStatusText; +}; + +NS_IMETHODIMP +nsOnStopRequestEvent::HandleEvent() +{ + LOG(("nsOnStopRequestEvent: HandleEvent [event=%x chan=%x]\n", + this, mChannel.get())); + + nsCOMPtr observer = mProxy->GetReceiver(); + if (!observer) { + LOG(("nsOnStopRequestEvent: Already called OnStopRequest (observer is NULL)\n")); + return NS_ERROR_FAILURE; + } + + // + // Do not allow any more events to be handled after OnStopRequest + // + mProxy->SetReceiver(nsnull); + + return observer->OnStopRequest(mChannel, + mContext, + mStatus, + mStatusText.GetUnicode()); +} + +// +//---------------------------------------------------------------------------- +// nsStreamProxyBase: nsISupports implementation... +//---------------------------------------------------------------------------- +// +NS_IMPL_THREADSAFE_ISUPPORTS1(nsStreamProxyBase, + nsIStreamObserver) + +// +//---------------------------------------------------------------------------- +// nsStreamProxyBase: nsIStreamObserver implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamProxyBase::OnStartRequest(nsIChannel *aChannel, + nsISupports *aContext) +{ + LOG(("nsStreamProxyBase: OnStartRequest [this=%x chan=%x]\n", this, aChannel)); + nsOnStartRequestEvent *ev = + new nsOnStartRequestEvent(this, aChannel, aContext); + if (!ev) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = ev->FireEvent(GetEventQueue()); + if (NS_FAILED(rv)) + delete ev; + return rv; +} + +NS_IMETHODIMP +nsStreamProxyBase::OnStopRequest(nsIChannel *aChannel, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aStatusText) +{ + LOG(("nsStreamProxyBase: OnStopRequest [this=%x chan=%x status=%x]\n", + this, aChannel, aStatus)); + nsOnStopRequestEvent *ev = + new nsOnStopRequestEvent(this, aChannel, aContext, aStatus, aStatusText); + if (!ev) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = ev->FireEvent(GetEventQueue()); + if (NS_FAILED(rv)) + delete ev; + return rv; +} + +// +//---------------------------------------------------------------------------- +// nsStreamProxyBase: implementation... +//---------------------------------------------------------------------------- +// +nsresult +nsStreamProxyBase::SetEventQueue(nsIEventQueue *aEventQ) +{ +#if defined(PR_LOGGING) + if (!gStreamProxyLog) + gStreamProxyLog = PR_NewLogModule("nsStreamProxy"); +#endif + nsresult rv = NS_OK; + if ((aEventQ == NS_CURRENT_EVENTQ) || (aEventQ == NS_UI_THREAD_EVENTQ)) { + nsCOMPtr serv = + do_GetService(kEventQueueService, &rv); + if (NS_FAILED(rv)) + return rv; + rv = serv->GetSpecialEventQueue((PRInt32) aEventQ, + getter_AddRefs(mEventQ)); + } else + mEventQ = aEventQ; + return rv; +} + +// +//---------------------------------------------------------------------------- +// nsStreamObserverProxy: nsISupports implementation... +//---------------------------------------------------------------------------- +// +NS_IMPL_ISUPPORTS_INHERITED1(nsStreamObserverProxy, + nsStreamProxyBase, + nsIStreamObserverProxy) + +// +//---------------------------------------------------------------------------- +// nsStreamObserverProxy: nsIStreamObserverProxy implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamObserverProxy::Init(nsIStreamObserver *aObserver, + nsIEventQueue *aEventQ) +{ + NS_PRECONDITION(aObserver, "null observer"); + SetReceiver(aObserver); + return SetEventQueue(aEventQ); +} diff --git a/mozilla/netwerk/base/src/nsStreamObserverProxy.h b/mozilla/netwerk/base/src/nsStreamObserverProxy.h new file mode 100644 index 00000000000..6466f1fffe2 --- /dev/null +++ b/mozilla/netwerk/base/src/nsStreamObserverProxy.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsStreamObserverProxy_h__ +#define nsStreamObserverProxy_h__ + +#include "nsIStreamObserver.h" +#include "nsIEventQueue.h" +#include "nsIChannel.h" +#include "nsCOMPtr.h" +#include "prlog.h" + +#if defined(PR_LOGGING) +extern PRLogModuleInfo *gStreamProxyLog; +#endif + +class nsStreamProxyBase : public nsIStreamObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMOBSERVER + + nsStreamProxyBase() { NS_INIT_ISUPPORTS(); } + virtual ~nsStreamProxyBase() {} + + nsIEventQueue *GetEventQueue() { return mEventQ.get(); } + nsIStreamObserver *GetReceiver() { return mReceiver.get(); } + + nsresult SetEventQueue(nsIEventQueue *); + + nsresult SetReceiver(nsIStreamObserver *aReceiver) { + mReceiver = aReceiver; + return NS_OK; + } + +private: + nsCOMPtr mEventQ; + nsCOMPtr mReceiver; +}; + +class nsStreamObserverProxy : public nsStreamProxyBase + , public nsIStreamObserverProxy +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_NSISTREAMOBSERVER(nsStreamProxyBase::) + NS_DECL_NSISTREAMOBSERVERPROXY +}; + +class nsStreamObserverEvent +{ +public: + nsStreamObserverEvent(nsStreamProxyBase *proxy, + nsIChannel *channel, nsISupports *context); + virtual ~nsStreamObserverEvent(); + + nsresult FireEvent(nsIEventQueue *); + NS_IMETHOD HandleEvent() = 0; + +protected: + static void PR_CALLBACK HandlePLEvent(PLEvent *); + static void PR_CALLBACK DestroyPLEvent(PLEvent *); + + PLEvent mEvent; + nsStreamProxyBase *mProxy; + nsCOMPtr mChannel; + nsCOMPtr mContext; +}; + +#define GET_STREAM_OBSERVER_EVENT(_mEvent_ptr) \ + ((nsStreamObserverEvent *) \ + ((char *)(_mEvent_ptr) - offsetof(nsStreamObserverEvent, mEvent))) + +#endif /* !nsStreamObserverProxy_h__ */ diff --git a/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp b/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp new file mode 100644 index 00000000000..c931b187962 --- /dev/null +++ b/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsStreamProviderProxy.h" +#include "nsIPipe.h" + +#define LOG(args) PR_LOG(gStreamProxyLog, PR_LOG_DEBUG, args) + +#define DEFAULT_BUFFER_SEGMENT_SIZE 2048 +#define DEFAULT_BUFFER_MAX_SIZE (4*2048) + +// +//---------------------------------------------------------------------------- +// Design Overview +// +// A stream provider proxy maintains a pipe. When requested to provide data +// to the channel, whatever data is in the pipe (up to the amount requested) +// is provided to the channel. If there is no data in the pipe, then the +// proxy posts an asynchronous event for more data, and returns WOULD_BLOCK +// indicating that the channel should suspend itself. +// +// The channel is only resumed once the event has been successfully handled; +// meaning that data has been written to the pipe. +//---------------------------------------------------------------------------- +// + +nsStreamProviderProxy::nsStreamProviderProxy() + : mProviderStatus(NS_OK) +{ } + +nsStreamProviderProxy::~nsStreamProviderProxy() +{ } + +// +//---------------------------------------------------------------------------- +// nsOnDataWritableEvent internal class... +//---------------------------------------------------------------------------- +// +class nsOnDataWritableEvent : public nsStreamObserverEvent +{ +public: + nsOnDataWritableEvent(nsStreamProxyBase *aProxy, + nsIChannel *aChannel, + nsISupports *aContext, + nsIOutputStream *aSink, + PRUint32 aOffset, + PRUint32 aCount) + : nsStreamObserverEvent(aProxy, aChannel, aContext) + , mSink(aSink) + , mOffset(aOffset) + , mCount(aCount) + { + MOZ_COUNT_CTOR(nsOnDataWritableEvent); + } + + ~nsOnDataWritableEvent() + { + MOZ_COUNT_DTOR(nsOnDataWritableEvent); + } + + NS_IMETHOD HandleEvent(); + +protected: + nsCOMPtr mSink; + PRUint32 mOffset; + PRUint32 mCount; +}; + +NS_IMETHODIMP +nsOnDataWritableEvent::HandleEvent() +{ + LOG(("nsOnDataWritableEvent: HandleEvent [event=%x chan=%x]", + this, mChannel.get())); + + nsStreamProviderProxy *providerProxy = + NS_STATIC_CAST(nsStreamProviderProxy *, mProxy); + + nsCOMPtr provider = providerProxy->GetProvider(); + if (!provider) { + LOG(("nsOnDataWritableEvent: Already called OnStopRequest (provider is NULL)\n")); + return NS_ERROR_FAILURE; + } + + nsresult status = NS_OK; + nsresult rv = mChannel->GetStatus(&status); + NS_ASSERTION(NS_SUCCEEDED(rv), "GetStatus failed"); + + // + // We should only forward this event to the provider if the channel is + // still in a "good" state. Because these events are being processed + // asynchronously, there is a very real chance that the provider might + // have cancelled the channel after _this_ event was triggered. + // + if (NS_SUCCEEDED(status)) { + LOG(("nsOnDataWritableEvent: Calling the consumer's OnDataWritable\n")); + rv = provider->OnDataWritable(mChannel, mContext, mSink, mOffset, mCount); + LOG(("nsOnDataWritableEvent: Done with the consumer's OnDataWritable [rv=%x]\n", rv)); + + // + // Mask NS_BASE_STREAM_WOULD_BLOCK return values. + // + providerProxy->SetProviderStatus( + rv != NS_BASE_STREAM_WOULD_BLOCK ? rv : NS_OK); + + // + // The channel is already suspended, so unless the provider returned + // NS_BASE_STREAM_WOULD_BLOCK, we should wake up the channel. + // + if (rv != NS_BASE_STREAM_WOULD_BLOCK) + mChannel->Resume(); + } + else + LOG(("nsOnDataWritableEvent: Not calling OnDataWritable")); + return NS_OK; +} + +// +//---------------------------------------------------------------------------- +// nsISupports implementation... +//---------------------------------------------------------------------------- +// +NS_IMPL_ISUPPORTS_INHERITED2(nsStreamProviderProxy, + nsStreamProxyBase, + nsIStreamProviderProxy, + nsIStreamProvider) + +// +//---------------------------------------------------------------------------- +// nsIStreamObserver implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamProviderProxy::OnStartRequest(nsIChannel *aChannel, + nsISupports *aContext) +{ + return nsStreamProxyBase::OnStartRequest(aChannel, aContext); +} + +NS_IMETHODIMP +nsStreamProviderProxy::OnStopRequest(nsIChannel *aChannel, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aStatusText) +{ + // + // Close the pipe + // + mPipeIn = 0; + mPipeOut = 0; + + return nsStreamProxyBase::OnStopRequest(aChannel, aContext, + aStatus, aStatusText); +} + +// +//---------------------------------------------------------------------------- +// nsIStreamProvider implementation... +//---------------------------------------------------------------------------- +// +static NS_METHOD +nsWriteToSink(nsIInputStream *source, + void *closure, + const char *fromRawSegment, + PRUint32 offset, + PRUint32 count, + PRUint32 *writeCount) +{ + nsIOutputStream *sink = (nsIOutputStream *) closure; + return sink->Write(fromRawSegment, count, writeCount); +} + +NS_IMETHODIMP +nsStreamProviderProxy::OnDataWritable(nsIChannel *aChannel, + nsISupports *aContext, + nsIOutputStream *aSink, + PRUint32 aOffset, + PRUint32 aCount) +{ + nsresult rv; + + LOG(("nsStreamProviderProxy: OnDataWritable [offset=%u, count=%u]\n", + aOffset, aCount)); + + NS_PRECONDITION(aCount > 0, "Invalid parameter"); + NS_PRECONDITION(mPipeIn, "Pipe not initialized"); + NS_PRECONDITION(mPipeOut, "Pipe not initialized"); + NS_PRECONDITION(mProviderStatus != NS_BASE_STREAM_WOULD_BLOCK, + "Invalid provider status"); + + // + // Any non-successful provider status gets passed back to the caller + // + if (NS_FAILED(mProviderStatus)) { + LOG(("nsStreamProviderProxy: Provider failed [status=%x]\n", mProviderStatus)); + return mProviderStatus; + } + + // + // Provide the channel with whatever data is already in the pipe (not + // exceeding aCount). + // + PRUint32 count; + rv = mPipeIn->Available(&count); + if (NS_FAILED(rv)) return rv; + + if (count > 0) { + count = PR_MIN(count, aCount); + + PRUint32 bytesWritten; + rv = mPipeIn->ReadSegments(nsWriteToSink, aSink, count, &bytesWritten); + if (NS_FAILED(rv)) return rv; + + return NS_OK; + } + + // + // Post an event requesting the provider for more data. + // + nsOnDataWritableEvent *ev = + new nsOnDataWritableEvent(this, aChannel, aContext, + mPipeOut, aOffset, aCount); + if (!ev) + return NS_ERROR_OUT_OF_MEMORY; + + rv = ev->FireEvent(GetEventQueue()); + if (NS_FAILED(rv)) { + delete ev; + return rv; + } + return NS_BASE_STREAM_WOULD_BLOCK; +} + +// +//---------------------------------------------------------------------------- +// nsIStreamProviderProxy implementation... +//---------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsStreamProviderProxy::Init(nsIStreamProvider *aProvider, + nsIEventQueue *aEventQ, + PRUint32 aBufferSegmentSize, + PRUint32 aBufferMaxSize) +{ + NS_PRECONDITION(GetReceiver() == nsnull, "Listener already set"); + NS_PRECONDITION(GetEventQueue() == nsnull, "Event queue already set"); + + // + // Create the pipe + // + if (aBufferSegmentSize == 0) + aBufferSegmentSize = DEFAULT_BUFFER_SEGMENT_SIZE; + if (aBufferMaxSize == 0) + aBufferMaxSize = DEFAULT_BUFFER_MAX_SIZE; + // The segment size must not exceed the maximum + aBufferSegmentSize = PR_MIN(aBufferMaxSize, aBufferSegmentSize); + + nsresult rv = NS_NewPipe(getter_AddRefs(mPipeIn), + getter_AddRefs(mPipeOut), + aBufferSegmentSize, + aBufferMaxSize, + PR_TRUE, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + SetReceiver(aProvider); + return SetEventQueue(aEventQ); +} diff --git a/mozilla/netwerk/base/src/nsStreamProviderProxy.h b/mozilla/netwerk/base/src/nsStreamProviderProxy.h new file mode 100644 index 00000000000..6aa19bfd6d4 --- /dev/null +++ b/mozilla/netwerk/base/src/nsStreamProviderProxy.h @@ -0,0 +1,37 @@ +#ifndef nsStreamProviderProxy_h__ +#define nsStreamProviderProxy_h__ + +#include "nsStreamObserverProxy.h" +#include "nsIStreamProvider.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" + +class nsStreamProviderProxy : public nsStreamProxyBase + , public nsIStreamProviderProxy +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSISTREAMPROVIDER + NS_DECL_NSISTREAMPROVIDERPROXY + + nsStreamProviderProxy(); + virtual ~nsStreamProviderProxy(); + + nsIStreamProvider *GetProvider() + { + return NS_STATIC_CAST(nsIStreamProvider *, GetReceiver()); + } + + void SetProviderStatus(nsresult status) + { + mProviderStatus = status; + } + +protected: + nsCOMPtr mPipeIn; + nsCOMPtr mPipeOut; + nsresult mProviderStatus; +}; + +#endif /* !nsStreamProviderProxy_h__ */ diff --git a/mozilla/netwerk/build/MANIFEST b/mozilla/netwerk/build/MANIFEST new file mode 100644 index 00000000000..70e4b3c4f24 --- /dev/null +++ b/mozilla/netwerk/build/MANIFEST @@ -0,0 +1,4 @@ +# +# This is a list of local files which get copied to the mozilla:dist directory +# +nsNetCID.h diff --git a/mozilla/netwerk/build/Makefile.in b/mozilla/netwerk/build/Makefile.in index dc677c39b5b..3b8a383c374 100644 --- a/mozilla/netwerk/build/Makefile.in +++ b/mozilla/netwerk/build/Makefile.in @@ -32,6 +32,7 @@ IS_COMPONENT = 1 REQUIRES = xpcom nkcache pref js mime dbm util jar locale intl CPPSRCS = nsNetModule.cpp +EXPORTS = nsNetCID.h SHARED_LIBRARY_LIBS = \ $(DIST)/lib/libneckobase_s.$(LIB_SUFFIX) \ diff --git a/mozilla/netwerk/build/makefile.win b/mozilla/netwerk/build/makefile.win index b2dd21162b4..2b9790ea032 100644 --- a/mozilla/netwerk/build/makefile.win +++ b/mozilla/netwerk/build/makefile.win @@ -28,6 +28,10 @@ DLL=.\$(OBJDIR)\$(DLLNAME).dll LCFLAGS = -DWIN32_LEAN_AND_MEAN -D_IMPL_NS_NET +EXPORTS = \ + nsNetCID.h \ + $(NULL) + CPP_OBJS= \ .\$(OBJDIR)\nsNetModule.obj \ $(NULL) diff --git a/mozilla/netwerk/build/nsNetCID.h b/mozilla/netwerk/build/nsNetCID.h new file mode 100644 index 00000000000..49eeeea8e2f --- /dev/null +++ b/mozilla/netwerk/build/nsNetCID.h @@ -0,0 +1,102 @@ +#ifndef nsNetCID_h__ +#define nsNetCID_h__ + +// +// nsStreamObserverProxy +// +#define NS_STREAMOBSERVERPROXY_CLASSNAME \ + "Stream Observer Proxy" +#define NS_STREAMOBSERVERPROXY_CONTRACTID \ + "@mozilla.org/network/stream-observer-proxy;1" +#define NS_STREAMOBSERVERPROXY_CID \ +{ /* 51fa28c7-74c0-4b85-9c46-d03faa7b696b */ \ + 0x51fa28c7, \ + 0x74c0, \ + 0x4b85, \ + {0x9c, 0x46, 0xd0, 0x3f, 0xaa, 0x7b, 0x69, 0x6b} \ +} + +// +// nsStreamListenerProxy +// +#define NS_STREAMLISTENERPROXY_CLASSNAME \ + "Stream Listener Proxy" +#define NS_STREAMLISTENERPROXY_CONTRACTID \ + "@mozilla.org/network/stream-listener-proxy;1" +#define NS_STREAMLISTENERPROXY_CID \ +{ /* 96c48f15-aa8a-4da7-a9d5-e842bd76f015 */ \ + 0x96c48f15, \ + 0xaa8a, \ + 0x4da7, \ + {0xa9, 0xd5, 0xe8, 0x42, 0xbd, 0x76, 0xf0, 0x15} \ +} + +// +// nsStreamProviderProxy +// +#define NS_STREAMPROVIDERPROXY_CLASSNAME \ + "Stream Provider Proxy" +#define NS_STREAMPROVIDERPROXY_CONTRACTID \ + "@mozilla.org/network/stream-provider-proxy;1" +#define NS_STREAMPROVIDERPROXY_CID \ +{ /* ae964fcf-9c27-40f7-9bbd-78894bfc1f31 */ \ + 0xae964fcf, \ + 0x9c27, \ + 0x40f7, \ + {0x9b, 0xbd, 0x78, 0x89, 0x4b, 0xfc, 0x1f, 0x31} \ +} + +// +// nsSimpleStreamListener +// +#define NS_SIMPLESTREAMLISTENER_CLASSNAME \ + "Simple Stream Listener" +#define NS_SIMPLESTREAMLISTENER_CONTRACTID \ + "@mozilla.org/network/simple-stream-listener;1" +#define NS_SIMPLESTREAMLISTENER_CID \ +{ /* fb8cbf4e-4701-4ba1-b1d6-5388e041fb67 */ \ + 0xfb8cbf4e, \ + 0x4701, \ + 0x4ba1, \ + {0xb1, 0xd6, 0x53, 0x88, 0xe0, 0x41, 0xfb, 0x67} \ +} + +// +// nsSimpleStreamProvider +// +#define NS_SIMPLESTREAMPROVIDER_CLASSNAME \ + "Simple Stream Provider" +#define NS_SIMPLESTREAMPROVIDER_CONTRACTID \ + "@mozilla.org/network/simple-stream-provider;1" +#define NS_SIMPLESTREAMPROVIDER_CID \ +{ /* f9f6a519-4efb-4f36-af40-2a5ec3992710 */ \ + 0xf9f6a519, \ + 0x4efb, \ + 0x4f36, \ + {0xaf, 0x40, 0x2a, 0x5e, 0xc3, 0x99, 0x27, 0x10} \ +} + +// +// nsAsyncStreamObserver +// +#define NS_ASYNCSTREAMOBSERVER_CID \ +{ /* fcc7c380-91b3-11d3-8cd9-0060b0fc14a3 */ \ + 0xfcc7c380, \ + 0x91b3, \ + 0x11d3, \ + {0x8c, 0xd9, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ +} + +// +// nsAsyncStreamListener +// +#define NS_ASYNCSTREAMLISTENER_CID \ +{ /* 60047bb2-91c0-11d3-8cd9-0060b0fc14a3 */ \ + 0x60047bb2, \ + 0x91c0, \ + 0x11d3, \ + {0x8c, 0xd9, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ +} + + +#endif /* !nsNetCID_h__ */ diff --git a/mozilla/netwerk/build/nsNetModule.cpp b/mozilla/netwerk/build/nsNetModule.cpp index 8963662db9d..6df0c8ff089 100644 --- a/mozilla/netwerk/build/nsNetModule.cpp +++ b/mozilla/netwerk/build/nsNetModule.cpp @@ -42,12 +42,14 @@ #include "nsStreamLoader.h" #include "nsDownloader.h" #include "nsAsyncStreamListener.h" -#include "nsSyncStreamListener.h" +//#include "nsSyncStreamListener.h" #include "nsFileStreams.h" #include "nsBufferedStreams.h" #include "nsProtocolProxyService.h" #include "nsSOCKSSocketProvider.h" +#include "nsNetCID.h" + /////////////////////////////////////////////////////////////////////////////// #include "nsStreamConverterService.h" @@ -74,7 +76,21 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCacheManager, Init) #include "nsXMLMIMEDataSource.h" #include "nsMIMEInfoImpl.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsMIMEInfoImpl); +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMIMEInfoImpl) + +/////////////////////////////////////////////////////////////////////////////// + +#include "nsStreamObserverProxy.h" +#include "nsStreamListenerProxy.h" +#include "nsStreamProviderProxy.h" +#include "nsSimpleStreamListener.h" +#include "nsSimpleStreamProvider.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsStreamObserverProxy) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsStreamListenerProxy) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsStreamProviderProxy) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSimpleStreamListener) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsSimpleStreamProvider) /////////////////////////////////////////////////////////////////////////////// @@ -400,6 +416,26 @@ static nsModuleComponentInfo gNetModuleInfo[] = { NS_DOWNLOADER_CID, "@mozilla.org/network/downloader;1", nsDownloader::Create }, + { NS_STREAMOBSERVERPROXY_CLASSNAME, + NS_STREAMOBSERVERPROXY_CID, + NS_STREAMOBSERVERPROXY_CONTRACTID, + nsStreamObserverProxyConstructor }, + { NS_STREAMLISTENERPROXY_CLASSNAME, + NS_STREAMLISTENERPROXY_CID, + NS_STREAMLISTENERPROXY_CONTRACTID, + nsStreamListenerProxyConstructor }, + { NS_STREAMPROVIDERPROXY_CLASSNAME, + NS_STREAMPROVIDERPROXY_CID, + NS_STREAMPROVIDERPROXY_CONTRACTID, + nsStreamProviderProxyConstructor }, + { NS_SIMPLESTREAMLISTENER_CLASSNAME, + NS_SIMPLESTREAMLISTENER_CID, + NS_SIMPLESTREAMLISTENER_CONTRACTID, + nsSimpleStreamListenerConstructor }, + { NS_SIMPLESTREAMPROVIDER_CLASSNAME, + NS_SIMPLESTREAMPROVIDER_CID, + NS_SIMPLESTREAMPROVIDER_CONTRACTID, + nsSimpleStreamProviderConstructor }, { "Async Stream Observer", NS_ASYNCSTREAMOBSERVER_CID, "@mozilla.org/network/async-stream-observer;1", @@ -408,10 +444,12 @@ static nsModuleComponentInfo gNetModuleInfo[] = { NS_ASYNCSTREAMLISTENER_CID, "@mozilla.org/network/async-stream-listener;1", nsAsyncStreamListener::Create }, + /* { "Sync Stream Listener", NS_SYNCSTREAMLISTENER_CID, "@mozilla.org/network/sync-stream-listener;1", nsSyncStreamListener::Create }, + */ { "Load Group", NS_LOADGROUP_CID, "@mozilla.org/network/load-group;1", diff --git a/mozilla/netwerk/cache/filecache/nsDiskCacheRecordChannel.cpp b/mozilla/netwerk/cache/filecache/nsDiskCacheRecordChannel.cpp index 419fb8e3ee0..797a1477f6a 100644 --- a/mozilla/netwerk/cache/filecache/nsDiskCacheRecordChannel.cpp +++ b/mozilla/netwerk/cache/filecache/nsDiskCacheRecordChannel.cpp @@ -434,8 +434,7 @@ nsDiskCacheRecordChannel::AsyncRead(nsIStreamListener *aListener, } NS_IMETHODIMP -nsDiskCacheRecordChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsDiskCacheRecordChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { diff --git a/mozilla/netwerk/cache/memcache/nsMemCacheChannel.cpp b/mozilla/netwerk/cache/memcache/nsMemCacheChannel.cpp index 94c9828ba8a..a900bfc1fab 100644 --- a/mozilla/netwerk/cache/memcache/nsMemCacheChannel.cpp +++ b/mozilla/netwerk/cache/memcache/nsMemCacheChannel.cpp @@ -525,14 +525,15 @@ nsMemCacheChannel::AsyncRead(nsIStreamListener *aListener, nsISupports *aContext mAsyncReadStream = asyncReadStreamAdaptor; rv = asyncReadStreamAdaptor->AsyncRead(aListener, aContext); - if (NS_FAILED(rv)) - delete asyncReadStreamAdaptor; + if (NS_FAILED(rv)) { + mAsyncReadStream = nsnull; + NS_RELEASE(asyncReadStreamAdaptor); + } return rv; } NS_IMETHODIMP -nsMemCacheChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, nsISupports *ctxt) +nsMemCacheChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { // Not required to be implemented NS_NOTREACHED("nsMemCacheChannel::AsyncWrite"); diff --git a/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.cpp b/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.cpp index dc205876452..d1450f419c1 100644 --- a/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.cpp +++ b/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.cpp @@ -203,8 +203,7 @@ nsCacheEntryChannel::AsyncRead(nsIStreamListener *aListener, nsISupports *aConte // No async writes allowed to the cache yet NS_IMETHODIMP -nsCacheEntryChannel::AsyncWrite(nsIInputStream *aFromStream, - nsIStreamObserver *aObserver, +nsCacheEntryChannel::AsyncWrite(nsIStreamProvider *aProvider, nsISupports *aContext) { NS_NOTREACHED("nsCacheEntryChannel::AsyncWrite"); diff --git a/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.h b/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.h index 4643628ce4f..9f38336c53e 100644 --- a/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.h +++ b/mozilla/netwerk/cache/mgr/nsCacheEntryChannel.h @@ -59,8 +59,7 @@ public: NS_IMETHOD OpenOutputStream(nsIOutputStream* *aOutputStream); NS_IMETHOD OpenInputStream(nsIInputStream* *aInputStream); NS_IMETHOD AsyncRead(nsIStreamListener *aListener, nsISupports *aContext); - NS_IMETHOD AsyncWrite(nsIInputStream *aFromStream, - nsIStreamObserver *aObserver, nsISupports *aContext); + NS_IMETHOD AsyncWrite(nsIStreamProvider *aProvider, nsISupports *aContext); NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes); NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes); NS_IMETHOD GetLoadGroup(nsILoadGroup* *aLoadGroup); diff --git a/mozilla/netwerk/protocol/data/src/nsDataChannel.cpp b/mozilla/netwerk/protocol/data/src/nsDataChannel.cpp index 0ba09516c0e..bb6882e5542 100644 --- a/mozilla/netwerk/protocol/data/src/nsDataChannel.cpp +++ b/mozilla/netwerk/protocol/data/src/nsDataChannel.cpp @@ -357,8 +357,7 @@ nsDataChannel::AsyncRead(nsIStreamListener *aListener, nsISupports *ctxt) } NS_IMETHODIMP -nsDataChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsDataChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { // you can't write to a data url diff --git a/mozilla/netwerk/protocol/datetime/src/nsDateTimeChannel.cpp b/mozilla/netwerk/protocol/datetime/src/nsDateTimeChannel.cpp index 613bb83ed7e..082a0a6cc27 100644 --- a/mozilla/netwerk/protocol/datetime/src/nsDateTimeChannel.cpp +++ b/mozilla/netwerk/protocol/datetime/src/nsDateTimeChannel.cpp @@ -202,8 +202,7 @@ nsDateTimeChannel::AsyncRead(nsIStreamListener *aListener, } NS_IMETHODIMP -nsDateTimeChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsDateTimeChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("nsDateTimeChannel::AsyncWrite"); diff --git a/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp b/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp index 3aebf536586..77be1784ed1 100644 --- a/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp +++ b/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp @@ -353,8 +353,7 @@ nsFileChannel::AsyncRead(nsIStreamListener *listener, } NS_IMETHODIMP -nsFileChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsFileChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { nsresult rv; @@ -392,7 +391,7 @@ nsFileChannel::AsyncWrite(nsIInputStream *fromStream, rv = EnsureTransport(); if (NS_FAILED(rv)) goto done; - rv = mFileTransport->AsyncWrite(fromStream, observer, ctxt); + rv = mFileTransport->AsyncWrite(provider, ctxt); done: if (NS_FAILED(rv)) { diff --git a/mozilla/netwerk/protocol/finger/src/nsFingerChannel.cpp b/mozilla/netwerk/protocol/finger/src/nsFingerChannel.cpp index 8c99900fdb1..f1fd991714f 100644 --- a/mozilla/netwerk/protocol/finger/src/nsFingerChannel.cpp +++ b/mozilla/netwerk/protocol/finger/src/nsFingerChannel.cpp @@ -32,6 +32,7 @@ #include "nsMimeTypes.h" #include "nsIStreamConverterService.h" #include "nsITXTToHTMLConv.h" +#include "nsNetUtil.h" static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); @@ -250,8 +251,7 @@ nsFingerChannel::AsyncRead(nsIStreamListener *aListener, nsISupports *ctxt) } NS_IMETHODIMP -nsFingerChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsFingerChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("nsFingerChannel::AsyncWrite"); @@ -540,7 +540,7 @@ nsFingerChannel::SendRequest(nsIChannel* aChannel) { rv = aChannel->SetTransferCount(requestBuffer.Length()); if (NS_FAILED(rv)) return rv; - rv = aChannel->AsyncWrite(charstream, this, 0); + rv = NS_AsyncWriteFromStream(aChannel, charstream, this, nsnull); return rv; } diff --git a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp index 4ae903a73a1..6e69cc6984f 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp @@ -293,13 +293,12 @@ nsFTPChannel::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } NS_IMETHODIMP -nsFTPChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsFTPChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { nsresult rv = NS_OK; - mObserver = observer; + mObserver = provider; mUserContext = ctxt; if (mProxyChannel) { @@ -307,9 +306,11 @@ nsFTPChannel::AsyncWrite(nsIInputStream *fromStream, if (NS_FAILED(rv)) return rv; rv = mProxyChannel->SetTransferCount(mAmount); if (NS_FAILED(rv)) return rv; - return mProxyChannel->AsyncWrite(fromStream, observer, ctxt); + return mProxyChannel->AsyncWrite(provider, ctxt); } + return NS_ERROR_NOT_IMPLEMENTED; +#if 0 NS_ASSERTION(mAmount > 0, "FTP requires stream len info"); if (mAmount < 1) return NS_ERROR_NOT_INITIALIZED; @@ -334,6 +335,7 @@ nsFTPChannel::AsyncWrite(nsIInputStream *fromStream, if (NS_FAILED(rv)) return rv; } return mFTPState->Connect(); +#endif } NS_IMETHODIMP diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp index 464f1f755cf..9d4a327a0ce 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp @@ -1328,7 +1328,7 @@ nsFtpState::S_stor() { mFireCallbacks = PR_FALSE; // observer callbacks will be handled by the transport. PR_LOG(gFTPLog, PR_LOG_DEBUG, ("(%x) writing on Data Transport\n", this)); - return mDPipe->AsyncWrite(mWriteStream, mObserver, mObserverContext); + return NS_AsyncWriteFromStream(mDPipe, mWriteStream, mObserver, mObserverContext); } FTP_STATE diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp index a13fa01a8f6..2540e29aece 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp @@ -25,6 +25,7 @@ #include "prlog.h" #include "nsIPipe.h" #include "nsIInputStream.h" +#include "nsNetUtil.h" #if defined(PR_LOGGING) extern PRLogModuleInfo* gFTPLog; @@ -102,7 +103,7 @@ nsFtpControlConnection::Connect() // to indicate between the read and the write transport. We will be passing the a non-null // context for the write, and a null context for the read. - rv = mCPipe->AsyncWrite(inStream, NS_STATIC_CAST(nsIStreamObserver*, this), NS_STATIC_CAST(nsISupports*, this)); + rv = NS_AsyncWriteFromStream(mCPipe, inStream, NS_STATIC_CAST(nsIStreamObserver*, this), NS_STATIC_CAST(nsISupports*, this)); if (NS_FAILED(rv)) return rv; // get the ball rolling by reading on the control socket. diff --git a/mozilla/netwerk/protocol/http/src/nsHTTPChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHTTPChannel.cpp index 1f93cea2255..a7dc8c78490 100644 --- a/mozilla/netwerk/protocol/http/src/nsHTTPChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHTTPChannel.cpp @@ -344,10 +344,12 @@ nsHTTPChannel::AsyncRead(nsIStreamListener *listener, nsISupports *aContext) } NS_IMETHODIMP -nsHTTPChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsHTTPChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { + NS_NOTREACHED("nsHTTPChannel::AsyncWrite"); + return NS_ERROR_NOT_IMPLEMENTED; +#if 0 nsresult rv = NS_OK; // parameter validation @@ -365,6 +367,7 @@ nsHTTPChannel::AsyncWrite(nsIInputStream *fromStream, Open(); return rv; +#endif } NS_IMETHODIMP diff --git a/mozilla/netwerk/protocol/http/src/nsHTTPRequest.cpp b/mozilla/netwerk/protocol/http/src/nsHTTPRequest.cpp index f5801995fd7..f1bfe18e837 100644 --- a/mozilla/netwerk/protocol/http/src/nsHTTPRequest.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHTTPRequest.cpp @@ -43,6 +43,7 @@ #include "nsISocketTransport.h" #include "nsISSLSocketControl.h" #include "plstr.h" +#include "nsNetUtil.h" #if defined(PR_LOGGING) extern PRLogModuleInfo* gHTTPLog; @@ -732,7 +733,7 @@ nsHTTPPipelinedRequest::WriteRequest(nsIInputStream* iRequestStream) } mOnStopDone = PR_FALSE; - rv = mTransport->AsyncWrite(stream, this,(nsISupports*)(nsIRequest*)req->mConnection); + rv = NS_AsyncWriteFromStream(mTransport, stream, this, (nsISupports*)(nsIRequest*)req->mConnection); NS_RELEASE(req); return rv; @@ -782,7 +783,7 @@ nsHTTPPipelinedRequest::OnStopRequest(nsIChannel* channel, nsISupports* i_Contex ("nsHTTPRequest [this=%x]. " "Writing PUT/POST data to the server.\n", this)); - rv = mTransport->AsyncWrite(mInputStream, this, + rv = NS_AsyncWriteFromStream(mTransport, mInputStream, this, (nsISupports*)(nsIRequest*)req->mConnection); /* the mInputStream is released below... */ diff --git a/mozilla/netwerk/protocol/http/src/nsHTTPResponseListener.cpp b/mozilla/netwerk/protocol/http/src/nsHTTPResponseListener.cpp index e54e9a7b1bd..1b2ec2a7a3e 100644 --- a/mozilla/netwerk/protocol/http/src/nsHTTPResponseListener.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHTTPResponseListener.cpp @@ -648,7 +648,8 @@ nsHTTPServerListener::OnStartRequest(nsIChannel* channel, nsISupports* i_pContex mChunkHeaderCtx.SetEOF(PR_FALSE) ; nsHTTPRequest * req; - mPipelinedRequest->GetCurrentRequest(&req) ; + if (mPipelinedRequest) + mPipelinedRequest->GetCurrentRequest(&req) ; if (req) { @@ -851,6 +852,14 @@ nsresult nsHTTPServerListener::FireSingleOnData(nsIStreamListener *aListener, return rv; } +struct nsWriteLineInfo +{ + nsCString &buf; + PRBool haveLF; + + nsWriteLineInfo(nsCString &b) : buf(b), haveLF(0) {} +}; + static NS_METHOD nsWriteLineToString(nsIInputStream* in, void* closure, @@ -859,29 +868,29 @@ nsWriteLineToString(nsIInputStream* in, PRUint32 count, PRUint32 *writeCount) { - nsCString* str = (nsCString*)closure; + nsWriteLineInfo *info = (nsWriteLineInfo *) closure; *writeCount = 0; + if (info->haveLF) + // Stop reading + return NS_BASE_STREAM_WOULD_BLOCK; + else + { char c = 0; - - const char *buf = fromRawSegment; + const char *p = fromRawSegment; for (; count>0; --count) { - c = *buf++; + c = *p++; (*writeCount)++; if (c == LF) { + info->haveLF = PR_TRUE; break; } } - str->Append(fromRawSegment, *writeCount); - // - // If the entire segment has been read, and no LF has been found, - // then return OK to continue reading the next segment (if there - // is one). Else, return WOULD_BLOCK to cause ReadSegments to exit - // and allow us to parse this header. - // - return (c != LF) ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; + info->buf.Append(fromRawSegment, *writeCount); + // Successfully read something + return NS_OK; +} } - nsresult nsHTTPServerListener::ParseStatusLine(nsIInputStream* in, PRUint32 aLength, @@ -901,8 +910,9 @@ nsresult nsHTTPServerListener::ParseStatusLine(nsIInputStream* in, return NS_ERROR_FAILURE; } + nsWriteLineInfo info(mHeaderBuffer); rv = in->ReadSegments(nsWriteLineToString, - (void*) &mHeaderBuffer, + (void*) &info, aLength, &actualBytesRead) ; if (NS_FAILED(rv)) return rv; @@ -987,8 +997,9 @@ nsresult nsHTTPServerListener::ParseHTTPHeader(nsIInputStream* in, PRInt32 newlineOffset = 0; do { // Append the buffer into the header string... + nsWriteLineInfo info(mHeaderBuffer); rv = in->ReadSegments(nsWriteLineToString, - (void*) &mHeaderBuffer, + (void*) &info, totalBytesToRead, &actualBytesRead); if (NS_FAILED(rv)) return rv; diff --git a/mozilla/netwerk/protocol/jar/src/nsJARChannel.cpp b/mozilla/netwerk/protocol/jar/src/nsJARChannel.cpp index d69015f2952..5ef89b162d8 100644 --- a/mozilla/netwerk/protocol/jar/src/nsJARChannel.cpp +++ b/mozilla/netwerk/protocol/jar/src/nsJARChannel.cpp @@ -392,8 +392,7 @@ nsJARChannel::AsyncReadJARElement() } NS_IMETHODIMP -nsJARChannel::AsyncWrite(nsIInputStream* fromStream, - nsIStreamObserver* observer, +nsJARChannel::AsyncWrite(nsIStreamProvider* provider, nsISupports* ctxt) { NS_NOTREACHED("nsJARChannel::AsyncWrite"); diff --git a/mozilla/netwerk/protocol/res/src/nsResChannel.cpp b/mozilla/netwerk/protocol/res/src/nsResChannel.cpp index 99594ecf99f..558fde17734 100644 --- a/mozilla/netwerk/protocol/res/src/nsResChannel.cpp +++ b/mozilla/netwerk/protocol/res/src/nsResChannel.cpp @@ -92,7 +92,7 @@ NS_IMPL_ISUPPORTS5(nsResChannel, nsIChannel, nsIRequest, nsIStreamListener, - nsIStreamObserver) + nsIStreamProvider) NS_METHOD nsResChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) @@ -428,8 +428,7 @@ nsResChannel::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } NS_IMETHODIMP -nsResChannel::AsyncWrite(nsIInputStream *fromStream, - nsIStreamObserver *observer, +nsResChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { nsresult rv; @@ -459,16 +458,15 @@ nsResChannel::AsyncWrite(nsIInputStream *fromStream, } NS_ASSERTION(mState == ASYNC_READ, "wrong state"); - mFromStream = fromStream; mUserContext = ctxt; - mUserObserver = observer; + mUserObserver = provider; do { rv = EnsureNextResolvedChannel(); if (NS_FAILED(rv)) break; if (mResolvedChannel) - rv = mResolvedChannel->AsyncWrite(fromStream, this, nsnull); + rv = mResolvedChannel->AsyncWrite(this, nsnull); // Later, this AsyncWrite will call back our OnStopRequest // method. The action resumes there... } while (NS_FAILED(rv)); @@ -704,7 +702,7 @@ nsResChannel::OnStopRequest(nsIChannel* transportChannel, nsISupports* context, case ASYNC_READ: return AsyncRead(GetUserListener(), mUserContext); case ASYNC_WRITE: - return AsyncWrite(mFromStream, mUserObserver, mUserContext); + return AsyncWrite(GetUserProvider(), mUserContext); default: break; } @@ -725,10 +723,9 @@ nsResChannel::EndRequest(nsresult aStatus, const PRUnichar* aStatusArg) } #endif // Release the reference to the consumer stream listener... - mUserObserver = null_nsCOMPtr(); - mUserContext = null_nsCOMPtr(); - mResolvedChannel = null_nsCOMPtr(); - mFromStream = null_nsCOMPtr(); + mUserObserver = 0; + mUserContext = 0; + mResolvedChannel = 0; return rv; } @@ -745,4 +742,16 @@ nsResChannel::OnDataAvailable(nsIChannel* transportChannel, nsISupports* context aSourceOffset, aLength); } +NS_IMETHODIMP +nsResChannel::OnDataWritable(nsIChannel* transportChannel, nsISupports* context, + nsIOutputStream *aOStream, PRUint32 aOffset, PRUint32 aLength) +{ +#ifdef DEBUG + NS_ASSERTION(mInitiator == PR_CurrentThread(), + "wrong thread calling this routine"); +#endif + return GetUserProvider()->OnDataWritable(this, mUserContext, aOStream, + aOffset, aLength); +} + //////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/netwerk/protocol/res/src/nsResChannel.h b/mozilla/netwerk/protocol/res/src/nsResChannel.h index 82ee436226f..20736d8b06c 100644 --- a/mozilla/netwerk/protocol/res/src/nsResChannel.h +++ b/mozilla/netwerk/protocol/res/src/nsResChannel.h @@ -25,6 +25,7 @@ #include "nsIResChannel.h" #include "nsIStreamListener.h" +#include "nsIStreamProvider.h" #include "nsIResProtocolHandler.h" #include "nsIURI.h" #include "nsIInterfaceRequestor.h" @@ -38,7 +39,8 @@ #endif class nsResChannel : public nsIResChannel, - public nsIStreamListener + public nsIStreamListener, + public nsIStreamProvider { public: NS_DECL_ISUPPORTS @@ -47,6 +49,7 @@ public: NS_DECL_NSIRESCHANNEL NS_DECL_NSISTREAMOBSERVER NS_DECL_NSISTREAMLISTENER + NS_DECL_NSISTREAMPROVIDER nsResChannel(); virtual ~nsResChannel(); @@ -90,6 +93,15 @@ protected: return listener; } + nsIStreamProvider* GetUserProvider() { + // this method doesn't addref the provider + NS_ASSERTION(mState == ASYNC_WRITE, "wrong state"); + // this cast is safe because we set mUserObserver in AsyncWrite + nsIStreamObserver* obs = mUserObserver; + nsIStreamProvider* provider = NS_STATIC_CAST(nsIStreamProvider*, obs); + return provider; + } + nsresult EnsureNextResolvedChannel(); nsresult EndRequest(nsresult aStatus, const PRUnichar* aStatusArg); @@ -107,7 +119,6 @@ protected: Substitutions mSubstitutions; nsCOMPtr mUserObserver; nsCOMPtr mUserContext; - nsCOMPtr mFromStream; PRUint32 mStartPosition; PRInt32 mCount; PRUint32 mBufferSegmentSize; diff --git a/mozilla/netwerk/test/Makefile.in b/mozilla/netwerk/test/Makefile.in index 39eecb05807..6bd334c56a1 100644 --- a/mozilla/netwerk/test/Makefile.in +++ b/mozilla/netwerk/test/Makefile.in @@ -29,6 +29,8 @@ include $(DEPTH)/config/autoconf.mk MODULE = test_necko REQUIRES = xpcom necko timer cookie layout widget dom +SHARED_LIBRARY_LIBS = $(DIST)/lib/libmozdbm_s.$(LIB_SUFFIX) + CPPSRCS = \ TestSocketIO.cpp \ TestSocketInput.cpp \ @@ -37,9 +39,11 @@ CPPSRCS = \ TestProtocols.cpp \ urltest.cpp \ TestFileInput2.cpp \ + TestFileTransport.cpp \ TestRes.cpp \ TestCallbacks.cpp \ TestSyncHTTP.cpp \ + TestDBMAccess.cpp \ $(NULL) SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) @@ -59,6 +63,7 @@ LIBS = \ $(XPCOM_LIBS) \ $(LOST_SYM_LIBS) \ $(NSPR_LIBS) \ + $(SHARED_LIBRARY_LIBS) \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mozilla/netwerk/test/TestFileTransport.cpp b/mozilla/netwerk/test/TestFileTransport.cpp index 388feacfe12..015e3b175cc 100644 --- a/mozilla/netwerk/test/TestFileTransport.cpp +++ b/mozilla/netwerk/test/TestFileTransport.cpp @@ -31,6 +31,19 @@ #include "nsIStreamListener.h" #include "nsIEventQueueService.h" #include "nsIEventQueue.h" +#include "nsNetUtil.h" +#include "prlog.h" + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(PR_LOGGING) +static PRLogModuleInfo *gTestFileTransportLog = nsnull; +#define PRINTF(args) PR_LOG(gTestFileTransportLog, PR_LOG_ALWAYS, args) +#else +#define PRINTF(args) +#endif + +//////////////////////////////////////////////////////////////////////////////// static NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); @@ -46,13 +59,13 @@ public: NS_DECL_ISUPPORTS NS_IMETHOD OnStartRequest(nsIChannel *channel, nsISupports *ctxt) { - printf("starting\n"); + PRINTF(("starting\n")); return NS_OK; } NS_IMETHOD OnStopRequest(nsIChannel *channel, nsISupports *ctxt, nsresult aStatus, const PRUnichar* aStatusArg) { - printf("ending status=%0x total=%d\n", aStatus, mTotal); + PRINTF(("ending status=%0x total=%d\n", aStatus, mTotal)); if (--mStopCount == 0) gDone = PR_TRUE; return NS_OK; @@ -61,9 +74,9 @@ public: NS_IMETHOD OnDataAvailable(nsIChannel *channel, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) { - printf("receiving %d bytes\n", count); + PRINTF(("receiving %d bytes\n", count)); char buf[256]; - PRUint32 writeCount; + PRUint32 writeCount, givenCount=count; nsresult rv; while (count > 0) { PRUint32 amt = PR_MIN(count, 256); @@ -71,12 +84,15 @@ public: rv = inStr->Read(buf, amt, &readCount); if (NS_FAILED(rv)) return rv; NS_ASSERTION(readCount != 0, "premature EOF"); - nsresult rv = mOut->Write(buf, readCount, &writeCount); + rv = mOut->Write(buf, readCount, &writeCount); if (NS_FAILED(rv)) return rv; NS_ASSERTION(writeCount == readCount, "failed to write all the data"); count -= readCount; mTotal += readCount; } + PRINTF(("done reading data [read %u bytes]\n", givenCount - count)); + //PRINTF("sleeping for 100 ticks\n");FLUSH(); + //PR_Sleep(100); return NS_OK; } @@ -124,6 +140,8 @@ TestAsyncRead(const char* fileName, PRUint32 offset, PRInt32 length) { nsresult rv; + PRINTF(("TestAsyncRead\n")); + NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; @@ -151,7 +169,7 @@ TestAsyncRead(const char* fileName, PRUint32 offset, PRInt32 length) while (!gDone) { PLEvent* event; - rv = gEventQ->GetEvent(&event); + rv = gEventQ->WaitForEvent(&event); if (NS_FAILED(rv)) return rv; rv = gEventQ->HandleEvent(event); if (NS_FAILED(rv)) return rv; @@ -169,6 +187,8 @@ TestAsyncWrite(const char* fileName, PRUint32 offset, PRInt32 length) { nsresult rv; + PRINTF(("TestAsyncWrite\n")); + NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; @@ -202,12 +222,12 @@ TestAsyncWrite(const char* fileName, PRUint32 offset, PRInt32 length) if (NS_FAILED(rv)) return rv; rv = fileTrans->SetTransferCount(length); if (NS_FAILED(rv)) return rv; - rv = fileTrans->AsyncWrite(inStr, listener, nsnull); + rv = NS_AsyncWriteFromStream(fileTrans, inStr, listener, nsnull); if (NS_FAILED(rv)) return rv; while (!gDone) { PLEvent* event; - rv = gEventQ->GetEvent(&event); + rv = gEventQ->WaitForEvent(&event); if (NS_FAILED(rv)) return rv; rv = gEventQ->HandleEvent(event); if (NS_FAILED(rv)) return rv; @@ -233,15 +253,15 @@ public: PRInt32 length; rv = channel->GetContentLength(&length); if (NS_FAILED(rv)) return rv; - printf("stream opened: content type = %s, length = %d\n", - contentType, length); + PRINTF(("stream opened: content type = %s, length = %d\n", + contentType, length)); nsCRT::free(contentType); return NS_OK; } NS_IMETHOD OnStopRequest(nsIChannel *channel, nsISupports *ctxt, nsresult aStatus, const PRUnichar* aStatusArg) { - printf("stream closed: status %x\n", aStatus); + PRINTF(("stream closed: status %x\n", aStatus)); return NS_OK; } @@ -265,6 +285,10 @@ main(int argc, char* argv[]) { nsresult rv; +#if defined(PR_LOGGING) + gTestFileTransportLog = PR_NewLogModule("TestFileTransport"); +#endif + if (argc < 2) { printf("usage: %s \n", argv[0]); return -1; diff --git a/mozilla/netwerk/test/TestProtocols.cpp b/mozilla/netwerk/test/TestProtocols.cpp index 875dd87546b..2fa12cd6bc3 100644 --- a/mozilla/netwerk/test/TestProtocols.cpp +++ b/mozilla/netwerk/test/TestProtocols.cpp @@ -651,7 +651,7 @@ main(int argc, char* argv[]) /* Mac stuff is missing here! */ #else PLEvent *gEvent; - rv = gEventQ->GetEvent(&gEvent); + rv = gEventQ->WaitForEvent(&gEvent); rv = gEventQ->HandleEvent(gEvent); #endif /* XP_UNIX */ #endif /* !WIN32 */ diff --git a/mozilla/netwerk/test/TestSocketIO.cpp b/mozilla/netwerk/test/TestSocketIO.cpp index 1ea2ffb6cc7..9a9afced9c9 100644 --- a/mozilla/netwerk/test/TestSocketIO.cpp +++ b/mozilla/netwerk/test/TestSocketIO.cpp @@ -20,6 +20,7 @@ * Contributor(s): */ #include +#include #ifdef WIN32 #include @@ -34,13 +35,19 @@ #include "nsIEventQueueService.h" #include "nsIServiceManager.h" #include "nsIChannel.h" -#include "nsIStreamObserver.h" +#include "nsIStreamProvider.h" #include "nsIStreamListener.h" #include "nsIPipe.h" #include "nsIOutputStream.h" #include "nsIInputStream.h" #include "nsCRT.h" #include "nsCOMPtr.h" +#include "nsIByteArrayInputStream.h" + +#if defined(PR_LOGGING) +static PRLogModuleInfo *gTestSocketIOLog; +#endif +#define LOG(args) PR_LOG(gTestSocketIOLog, PR_LOG_DEBUG, args) static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); @@ -50,250 +57,310 @@ static PRTime gElapsedTime; static int gKeepRunning = 1; static nsIEventQueue* gEventQ = nsnull; -class InputTestConsumer : public nsIStreamListener +// +//---------------------------------------------------------------------------- +// Test Listener +//---------------------------------------------------------------------------- +// + +class TestListener : public nsIStreamListener { public: - InputTestConsumer(); - virtual ~InputTestConsumer(); + TestListener() { NS_INIT_ISUPPORTS(); } + virtual ~TestListener() {} NS_DECL_ISUPPORTS NS_DECL_NSISTREAMOBSERVER NS_DECL_NSISTREAMLISTENER }; - -InputTestConsumer::InputTestConsumer() -{ - NS_INIT_REFCNT(); -} - -InputTestConsumer::~InputTestConsumer() -{ -} - - -NS_IMPL_ADDREF(InputTestConsumer); -NS_IMPL_RELEASE(InputTestConsumer); -NS_IMPL_QUERY_INTERFACE2(InputTestConsumer, +NS_IMPL_ISUPPORTS2(TestListener, nsIStreamObserver, nsIStreamListener); - - NS_IMETHODIMP -InputTestConsumer::OnStartRequest(nsIChannel* channel, nsISupports* context) +TestListener::OnStartRequest(nsIChannel* channel, nsISupports* context) { - printf("\n+++ InputTestConsumer::OnStartRequest +++\n"); + LOG(("TestListener::OnStartRequest\n")); return NS_OK; } - NS_IMETHODIMP -InputTestConsumer::OnDataAvailable(nsIChannel* channel, +TestListener::OnDataAvailable(nsIChannel* channel, nsISupports* context, nsIInputStream *aIStream, PRUint32 aSourceOffset, PRUint32 aLength) { + LOG(("TestListener::OnDataAvailable [offset=%u length=%u]\n", + aSourceOffset, aLength)); char buf[1025]; PRUint32 amt; - do { + while (1) { aIStream->Read(buf, 1024, &amt); - if (amt == 0) break; + if (amt == 0) + break; buf[amt] = '\0'; puts(buf); - } while (amt != 0); - + } return NS_OK; } - NS_IMETHODIMP -InputTestConsumer::OnStopRequest(nsIChannel* channel, nsISupports* context, +TestListener::OnStopRequest(nsIChannel* channel, nsISupports* context, nsresult aStatus, const PRUnichar* aStatusArg) { + LOG(("TestListener::OnStopRequest [aStatus=%x]\n", aStatus)); gKeepRunning = 0; - printf("\n+++ InputTestConsumer::OnStopRequest (status = %x) +++\n", aStatus); return NS_OK; } +// +//---------------------------------------------------------------------------- +// Test Provider +//---------------------------------------------------------------------------- +// - -class TestWriteObserver : public nsIStreamObserver +class TestProvider : public nsIStreamProvider { public: - TestWriteObserver(nsIChannel* aChannel); - virtual ~TestWriteObserver(); + TestProvider(char *data); + virtual ~TestProvider(); NS_DECL_ISUPPORTS NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSISTREAMPROVIDER protected: - nsIChannel* mTransport; + nsCOMPtr mData; }; +NS_IMPL_ISUPPORTS2(TestProvider, + nsIStreamProvider, + nsIStreamObserver) -TestWriteObserver::TestWriteObserver(nsIChannel* aChannel) +TestProvider::TestProvider(char *data) { - NS_INIT_REFCNT(); - mTransport = aChannel; - NS_ADDREF(mTransport); + NS_INIT_ISUPPORTS(); + NS_NewByteArrayInputStream(getter_AddRefs(mData), data, strlen(data)); + LOG(("Constructing TestProvider [this=%x]\n", this)); } -TestWriteObserver::~TestWriteObserver() +TestProvider::~TestProvider() { - NS_RELEASE(mTransport); + LOG(("Destroying TestProvider [this=%x]\n", this)); } - -NS_IMPL_ISUPPORTS(TestWriteObserver,NS_GET_IID(nsIStreamObserver)); - - NS_IMETHODIMP -TestWriteObserver::OnStartRequest(nsIChannel* channel, nsISupports* context) +TestProvider::OnStartRequest(nsIChannel* channel, nsISupports* context) { - printf("\n+++ TestWriteObserver::OnStartRequest +++\n"); + LOG(("TestProvider::OnStartRequest [this=%x]\n", this)); return NS_OK; } - NS_IMETHODIMP -TestWriteObserver::OnStopRequest(nsIChannel* channel, nsISupports* context, +TestProvider::OnStopRequest(nsIChannel* channel, nsISupports* context, nsresult aStatus, const PRUnichar* aStatusArg) { - printf("\n+++ TestWriteObserver::OnStopRequest (status = %x) +++\n", aStatus); + LOG(("TestProvider::OnStopRequest [status=%x]\n", aStatus)); - if (NS_SUCCEEDED(aStatus)) { - mTransport->AsyncRead(nsnull, new InputTestConsumer); - } else { + nsCOMPtr listener = do_QueryInterface(new TestListener()); + + if (NS_SUCCEEDED(aStatus)) + channel->AsyncRead(listener, nsnull); + else gKeepRunning = 0; - } return NS_OK; } -nsresult NS_AutoregisterComponents() +NS_IMETHODIMP +TestProvider::OnDataWritable(nsIChannel *channel, nsISupports *context, + nsIOutputStream *output, PRUint32 offset, PRUint32 count) { - nsresult rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup, NULL /* default */); + LOG(("TestProvider::OnDataWritable [offset=%u, count=%u]\n", offset, count)); + PRUint32 writeCount; + nsresult rv = output->WriteFrom(mData, count, &writeCount); + // Zero bytes written on success indicates EOF + if (NS_SUCCEEDED(rv) && (writeCount == 0)) + return NS_BASE_STREAM_CLOSED; return rv; } +// +//---------------------------------------------------------------------------- +// Synchronous IO +//---------------------------------------------------------------------------- +// +nsresult +WriteRequest(nsIOutputStream *os, const char *request) +{ + PRUint32 n; + return os->Write(request, strlen(request), &n); +} + +nsresult +ReadResponse(nsIInputStream *is) +{ + PRUint32 bytesRead; + char buf[2048]; + do { + is->Read(buf, sizeof(buf), &bytesRead); + if (bytesRead > 0) + fwrite(buf, 1, bytesRead, stdout); + } while (bytesRead > 0); + return NS_OK; +} + +// +//---------------------------------------------------------------------------- +// Startup... +//---------------------------------------------------------------------------- +// + +void +sighandler(int sig) +{ + LOG(("got signal: %d\n", sig)); + NS_BREAK(); +} + +void +usage(char **argv) +{ + printf("usage: %s [-sync] \n", argv[0]); + exit(1); +} + int main(int argc, char* argv[]) { nsresult rv; - if (argc < 3) { - printf("usage: %s \n", argv[0]); - return -1; - } + signal(SIGSEGV, sighandler); - char* hostName = argv[1]; - char* fileName = argv[2]; - int port = 80; + gTestSocketIOLog = PR_NewLogModule("TestSocketIO"); - rv = NS_AutoregisterComponents(); + if (argc < 3) + usage(argv); - if (NS_FAILED(rv)) return rv; + PRIntn i=0; + PRBool sync = PR_FALSE; + if (nsCRT::strcasecmp(argv[1], "-sync") == 0) { + if (argc < 4) + usage(argv); + sync = PR_TRUE; + i = 1; + } + + char *hostName = argv[1+i]; + char *fileName = argv[2+i]; + int port = 80; // Create the Event Queue for this thread... NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv); - if (NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) { + NS_WARNING("failed to create: event queue service!"); + return rv; + } - rv = eventQService->CreateThreadEventQueue(); - if (NS_FAILED(rv)) return rv; + rv = eventQService->CreateMonitoredThreadEventQueue(); + if (NS_FAILED(rv)) { + NS_WARNING("failed to create: thread event queue!"); + return rv; + } eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, &gEventQ); // Create the Socket transport service... NS_WITH_SERVICE(nsISocketTransportService, sts, kSocketTransportServiceCID, &rv); - if (NS_FAILED(rv)) return rv; - - // Create a stream for the data being written to the server... - nsIInputStream* stream; - PRUint32 bytesWritten; - - nsCOMPtr out; - rv = NS_NewPipe(&stream, getter_AddRefs(out), 1024, 4096); - if (NS_FAILED(rv)) return rv; - - char *buffer = PR_smprintf("GET %s HTML/1.0%s%s", fileName, CRLF, CRLF); - out->Write(buffer, strlen(buffer), &bytesWritten); - printf("\n+++ Request is: %s\n", buffer); - - // Create the socket transport... - nsIChannel* transport; - rv = sts->CreateTransport(hostName, port, nsnull, -1, 0, 0, &transport); - -// This stuff is used to test the output stream -#if 0 - nsIOutputStream* outStr = nsnull; - rv = transport->OpenOutputStream(&outStr); - - if (NS_SUCCEEDED(rv)) { - PRUint32 bytes; - rv = outStr->Write("test", 4, &bytes); - - - if (NS_FAILED(rv)) return rv; - } - - rv = outStr->Close(); -#else - if (NS_SUCCEEDED(rv)) { - TestWriteObserver* observer = new TestWriteObserver(transport); - - gElapsedTime = PR_Now(); - rv = transport->SetTransferCount(bytesWritten); - if (NS_SUCCEEDED(rv)) { - rv = transport->AsyncWrite(stream, nsnull, observer); + if (NS_FAILED(rv)) { + NS_WARNING("failed to create: socket transport service!"); + return rv; } - NS_RELEASE(transport); - } -#endif + char *buffer = PR_smprintf("GET %s HTTP/1.1" CRLF + "host: %s" CRLF + "user-agent: Mozilla/5.0 (X11; N; Linux 2.2.16-22smp i686; en-US; m18) Gecko/20001220" CRLF + "accept: */*" CRLF + "accept-language: en" CRLF + "accept-encoding: gzip,deflate,compress,identity" CRLF + "keep-alive: 300" CRLF + "connection: keep-alive" CRLF + CRLF, + fileName, hostName); + LOG(("Request [\n%s]\n", buffer)); - if (NS_FAILED(rv)) return rv; + // Create the socket transport... + nsCOMPtr transport; + rv = sts->CreateTransport(hostName, port, nsnull, -1, 0, 0, getter_AddRefs(transport)); + if (NS_FAILED(rv)) { + NS_WARNING("failed to create: socket transport!"); + return rv; + } + + gElapsedTime = PR_Now(); + + if (!sync) { + rv = transport->AsyncWrite(new TestProvider(buffer), nsnull); + if (NS_FAILED(rv)) { + NS_WARNING("failed calling: AsyncWrite!"); + return rv; + } // Enter the message pump to allow the URL load to proceed. while ( gKeepRunning ) { - #ifdef WIN32 MSG msg; - if (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); - } else { + } else gKeepRunning = FALSE; - } #else #ifdef XP_MAC /* Mac stuff is missing here! */ #else #ifdef XP_OS2 QMSG qmsg; - if (WinGetMsg(0, &qmsg, 0, 0, 0)) WinDispatchMsg(0, &qmsg); else gKeepRunning = FALSE; #else PLEvent *gEvent; - rv = gEventQ->GetEvent(&gEvent); + rv = gEventQ->WaitForEvent(&gEvent); rv = gEventQ->HandleEvent(gEvent); #endif #endif #endif } + } + else { + // synchronous write + { + nsCOMPtr os; + rv = transport->OpenOutputStream(getter_AddRefs(os)); + if (NS_FAILED(rv)) return rv; + rv = WriteRequest(os, buffer); + if (NS_FAILED(rv)) return rv; + } + // synchronous read + { + nsCOMPtr is; + rv = transport->OpenInputStream(getter_AddRefs(is)); + if (NS_FAILED(rv)) return rv; + rv = ReadResponse(is); + if (NS_FAILED(rv)) return rv; + } + } PRTime endTime; endTime = PR_Now(); - printf("Elapsed time: %d\n", (PRInt32)(endTime/1000UL-gElapsedTime/1000UL)); + LOG(("Elapsed time: %d\n", (PRInt32)(endTime/1000UL - gElapsedTime/1000UL))); sts->Shutdown(); - return 0; } diff --git a/mozilla/netwerk/test/TestSocketTransport.cpp b/mozilla/netwerk/test/TestSocketTransport.cpp index df02fff4ccb..8b0ed4d7e98 100644 --- a/mozilla/netwerk/test/TestSocketTransport.cpp +++ b/mozilla/netwerk/test/TestSocketTransport.cpp @@ -49,6 +49,7 @@ #include "nsIInterfaceRequestor.h" #include "nsIProgressEventSink.h" #include "nsCRT.h" +#include "nsNetUtil.h" #if defined(XP_MAC) #include "macstdlibextras.h" @@ -431,7 +432,7 @@ nsresult TestConnection::WriteBuffer(void) if (NS_SUCCEEDED(rv)) { rv = mTransport->SetTransferCount(bytesWritten); if (NS_SUCCEEDED(rv)) { - rv = mTransport->AsyncWrite(mStream, /* mOutputObserver */ nsnull, mTransport); + rv = NS_AsyncWriteFromStream(mTransport, mStream, nsnull, mTransport); } } // Wait for the write to complete... diff --git a/mozilla/rdf/chrome/src/nsChromeProtocolHandler.cpp b/mozilla/rdf/chrome/src/nsChromeProtocolHandler.cpp index 4d8b47de1dd..392fb916814 100644 --- a/mozilla/rdf/chrome/src/nsChromeProtocolHandler.cpp +++ b/mozilla/rdf/chrome/src/nsChromeProtocolHandler.cpp @@ -271,7 +271,7 @@ nsCachedChromeChannel::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } NS_IMETHODIMP -nsCachedChromeChannel::AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) +nsCachedChromeChannel::AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { NS_NOTREACHED("don't do that"); return NS_ERROR_FAILURE; diff --git a/mozilla/rdf/content/src/nsXULDocument.cpp b/mozilla/rdf/content/src/nsXULDocument.cpp index d236bf70026..b5418f4dc64 100644 --- a/mozilla/rdf/content/src/nsXULDocument.cpp +++ b/mozilla/rdf/content/src/nsXULDocument.cpp @@ -349,7 +349,7 @@ public: NS_IMETHOD OpenOutputStream(nsIOutputStream **_retval) { *_retval = nsnull; return NS_OK; } NS_IMETHOD AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; } - NS_IMETHOD AsyncWrite(nsIInputStream *fromStream, nsIStreamObserver *observer, nsISupports *ctxt) { return NS_OK; } + NS_IMETHOD AsyncWrite(nsIStreamProvider *provider, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes) { *aLoadAttributes = nsIChannel::LOAD_NORMAL; return NS_OK; } NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes) { return NS_OK; } NS_IMETHOD GetContentType(char * *aContentType) { *aContentType = nsnull; return NS_OK; } diff --git a/mozilla/xpcom/io/nsPipe2.cpp b/mozilla/xpcom/io/nsPipe2.cpp index 8c15c01ff6a..f29e920cdcd 100644 --- a/mozilla/xpcom/io/nsPipe2.cpp +++ b/mozilla/xpcom/io/nsPipe2.cpp @@ -403,6 +403,10 @@ nsPipe::nsPipeInputStream::ReadSegments(nsWriteSegmentFun writer, if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) goto done; NS_ASSERTION(writeCount <= readBufferLen, "writer returned bad writeCount"); +#ifdef DEBUG + if (writeCount > 0 && rv == NS_BASE_STREAM_WOULD_BLOCK) + NS_WARNING("Invalid writer implementation: cannot write data and return WOULD_BLOCK"); +#endif readBuffer += writeCount; readBufferLen -= writeCount; *readCount += writeCount; @@ -432,6 +436,15 @@ nsPipe::nsPipeInputStream::ReadSegments(nsWriteSegmentFun writer, goto done; } } + else if (pipe->mReadCursor == pipe->mWriteCursor) { + if (mObserver) { + mon.Exit(); // XXXbe avoid deadlock better + rv = mObserver->OnEmpty(this); + mon.Enter(); + if (NS_FAILED(rv)) + goto done; + } + } } done: diff --git a/mozilla/xpcom/tests/TestPipes.cpp b/mozilla/xpcom/tests/TestPipes.cpp index 8d375780ebe..d09c9ef1f2c 100644 --- a/mozilla/xpcom/tests/TestPipes.cpp +++ b/mozilla/xpcom/tests/TestPipes.cpp @@ -152,6 +152,8 @@ public: PRUint32 count; PRUint32 total = 0; while (PR_TRUE) { + //if (gTrace) + // printf("calling Read\n"); rv = mIn->Read(buf, 100, &count); if (NS_FAILED(rv)) { printf("read failed\n"); @@ -231,7 +233,9 @@ TestShortWrites(nsIInputStream* in, nsIOutputStream* out) if (gTrace) printf("wrote %d bytes: %s\n", writeCount, buf); PR_smprintf_free(buf); + //printf("calling Flush\n"); out->Flush(); + //printf("calling WaitForReceipt\n"); PRUint32 received = receiver->WaitForReceipt(); NS_ASSERTION(received == writeCount, "received wrong amount"); } @@ -292,7 +296,7 @@ TestPipeObserver() printf("TestPipeObserver: OnWrite and OnFull should be called once, OnEmpty should be called twice.\n"); nsIInputStream* in; nsIOutputStream* out; - rv = NS_NewPipe(&in, &out, 20, 20, PR_TRUE, PR_TRUE); + rv = NS_NewPipe(&in, &out, 18, 36, PR_TRUE, PR_TRUE); if (NS_FAILED(rv)) return rv; rv = in->SetObserver(obs); @@ -302,27 +306,55 @@ TestPipeObserver() char buf[] = "puirt a beul: a style of Gaelic vocal music intended for dancing."; PRUint32 cnt; - // this should print OnWrite message: + printf("> should see OnWrite message:\n"); rv = out->Write(buf, 20, &cnt); if (NS_FAILED(rv)) return rv; NS_ASSERTION(cnt == 20, "Write failed"); - // this should print OnFull message: - rv = out->Write(buf + 20, 1, &cnt); - if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) return rv; - NS_ASSERTION(cnt == 0 && rv == NS_BASE_STREAM_WOULD_BLOCK, "Write failed"); - - char buf2[20]; - rv = in->Read(buf2, 20, &cnt); + printf("> should see OnWrite message followed by OnFull message:\n"); + rv = out->Write(buf + 20, 20, &cnt); if (NS_FAILED(rv)) return rv; - NS_ASSERTION(cnt == 20, "Read failed"); - NS_ASSERTION(nsCRT::strncmp(buf, buf2, 20) == 0, "Read wrong stuff"); + NS_ASSERTION(cnt == 16, "Write failed"); - // this should print OnEmpty message: - rv = in->Read(buf2, 1, &cnt); + rv = in->Available(&cnt); + if (NS_FAILED(rv)) return rv; + printf("available = %u\n", cnt); + NS_ASSERTION(cnt == 36, "Available failed"); + + char buf2[40]; + printf("> should see OnEmpty message:\n"); + rv = in->Read(buf2, 40, &cnt); + if (NS_FAILED(rv)) return rv; + printf("cnt = %u\n", cnt); + NS_ASSERTION(cnt == 36, "Read failed"); + NS_ASSERTION(nsCRT::strncmp(buf, buf2, 36) == 0, "Read wrong stuff"); + + rv = in->Available(&cnt); + if (NS_FAILED(rv)) return rv; + printf("available = %u\n", cnt); + NS_ASSERTION(cnt == 0, "Available failed"); + + printf("> should see OnEmpty message:\n"); + rv = in->Read(buf2, 2, &cnt); if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) return rv; NS_ASSERTION(cnt == 0 && rv == NS_BASE_STREAM_WOULD_BLOCK, "Read failed"); + printf("> should see OnWrite message:\n"); + rv = out->Write(buf, 20, &cnt); + if (NS_FAILED(rv)) return rv; + NS_ASSERTION(cnt == 20, "Write failed"); + + rv = in->Available(&cnt); + if (NS_FAILED(rv)) return rv; + printf("available = %u\n", cnt); + NS_ASSERTION(cnt == 20, "Available failed"); + + printf("> should see OnEmpty message:\n"); + rv = in->Read(buf2, 20, &cnt); + if (NS_FAILED(rv)) return rv; + NS_ASSERTION(cnt == 20, "Read failed"); + + printf("> should see OnClose message:\n"); NS_RELEASE(obs); NS_RELEASE(in); NS_RELEASE(out); diff --git a/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp b/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp index ee9f2e36ba5..9af8b60a665 100644 --- a/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp +++ b/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp @@ -202,7 +202,7 @@ nsStreamXferOp::Start( void ) { // hand the output channel our input stream. it will take care // of reading data from the stream and writing it to disk. - rv = mOutputChannel->AsyncWrite(inStream, NS_STATIC_CAST(nsIStreamObserver*,this), nsnull); + rv = NS_AsyncWriteFromStream(mOutputChannel, inStream, NS_STATIC_CAST(nsIStreamObserver*,this), nsnull); if ( NS_FAILED( rv ) ) { this->OnError( kOpAsyncWrite, rv ); }