зеркало из https://github.com/mozilla/gecko-dev.git
new files for bug 176919 "async streams" r=dougt,gordon sr=sspitzer a=valeski,asa
This commit is contained in:
Родитель
135161218a
Коммит
2347ef8985
|
@ -0,0 +1,80 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIRequest.idl"
|
||||
|
||||
interface nsIInputStream;
|
||||
interface nsIOutputStream;
|
||||
interface nsIRequestObserver;
|
||||
|
||||
[scriptable, uuid(eaa49141-c21c-4fe8-a79b-77860a3910aa)]
|
||||
interface nsIAsyncStreamCopier : nsIRequest
|
||||
{
|
||||
/**
|
||||
* Initialize the input stream copier.
|
||||
*
|
||||
* @param aSource
|
||||
* contains the data to be copied.
|
||||
* @param aSink
|
||||
* specifies the destination for the data.
|
||||
* @param aSourceBuffered
|
||||
* true if aSource implements ReadSegments.
|
||||
* @param aSinkBuffered
|
||||
* true if aSink implements WriteSegments.
|
||||
* @param aChunkSize
|
||||
* specifies how many bytes to read/write at a time. this controls
|
||||
* the granularity of the copying. it should match the segment size
|
||||
* of the "buffered" streams involved.
|
||||
*/
|
||||
void init(in nsIInputStream aSource,
|
||||
in nsIOutputStream aSink,
|
||||
in boolean aSourceBuffered,
|
||||
in boolean aSinkBuffered,
|
||||
in unsigned long aChunkSize);
|
||||
|
||||
/**
|
||||
* asyncRead causes the input stream to be read in chunks and delivered
|
||||
* asynchronously to the listener via OnDataAvailable.
|
||||
*
|
||||
* @param aListener
|
||||
* receives notifications.
|
||||
* @param aListenerContext
|
||||
* passed to listener methods.
|
||||
*/
|
||||
void asyncCopy(in nsIRequestObserver aObserver,
|
||||
in nsISupports aObserverContext);
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
#include "nsIInputStream.idl"
|
||||
#include "nsIOutputStream.idl"
|
||||
|
||||
/**
|
||||
* An input stream that reads ahead and keeps a buffer coming from another input
|
||||
* stream so that fewer accesses to the underlying stream are necessary.
|
||||
*/
|
||||
[scriptable, uuid(616f5b48-da09-11d3-8cda-0060b0fc14a3)]
|
||||
interface nsIBufferedInputStream : nsIInputStream
|
||||
{
|
||||
/**
|
||||
* @param fillFromStream - add buffering to this stream
|
||||
* @param bufferSize - specifies the maximum buffer size
|
||||
*/
|
||||
void init(in nsIInputStream fillFromStream,
|
||||
in unsigned long bufferSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* An output stream that stores up data to write out to another output stream
|
||||
* and does the entire write only when the buffer is full, so that fewer writes
|
||||
* to the underlying output stream are necessary.
|
||||
*/
|
||||
[scriptable, uuid(6476378a-da09-11d3-8cda-0060b0fc14a3)]
|
||||
interface nsIBufferedOutputStream : nsIOutputStream
|
||||
{
|
||||
/**
|
||||
* @param sinkToStream - add buffering to this stream
|
||||
* @param bufferSize - specifies the maximum buffer size
|
||||
*/
|
||||
void init(in nsIOutputStream sinkToStream,
|
||||
in unsigned long bufferSize);
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIChannel.idl"
|
||||
|
||||
interface nsIInputStream;
|
||||
|
||||
/**
|
||||
* nsIInputStreamChannel
|
||||
*
|
||||
* This interface provides methods to initialize an input stream channel.
|
||||
* The input stream channel serves as a data pump for an input stream.
|
||||
*/
|
||||
[scriptable, uuid(560a64ce-6d66-44db-b38e-864469c52d03)]
|
||||
interface nsIInputStreamChannel : nsIChannel
|
||||
{
|
||||
/**
|
||||
* Sets the URI for this channel.
|
||||
*/
|
||||
void setURI(in nsIURI aURI);
|
||||
|
||||
/**
|
||||
* Get/set the content stream
|
||||
*
|
||||
* This stream contains the data that will be pushed to the channel's
|
||||
* stream listener. If the stream is non-blocking and supports the
|
||||
* nsIAsyncInputStream interface, then the stream will be read directly.
|
||||
* Otherwise, the stream will be read on a background thread.
|
||||
*
|
||||
* This attribute can only be changed before the channel is opened.
|
||||
*/
|
||||
attribute nsIInputStream contentStream;
|
||||
};
|
|
@ -0,0 +1,91 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIRequest.idl"
|
||||
|
||||
interface nsIInputStream;
|
||||
interface nsIStreamListener;
|
||||
|
||||
[scriptable, uuid(f7dd8d87-efa7-48cc-9d94-df488df0b3f9)]
|
||||
interface nsIInputStreamPump : nsIRequest
|
||||
{
|
||||
/**
|
||||
* Initialize the input stream pump.
|
||||
*
|
||||
* @param aStream
|
||||
* contains the data to be read. if the input stream is non-blocking,
|
||||
* then it will be QI'd to nsIAsyncInputStream. if the QI succeeds
|
||||
* then the stream will be read directly. otherwise, it will be read
|
||||
* on a background thread using the stream transport service.
|
||||
* @param aStreamPos
|
||||
* specifies the stream offset from which to start reading. the
|
||||
* offset value is absolute. pass -1 to specify the current offset.
|
||||
* NOTE: this parameter is ignored if the underlying stream does not
|
||||
* implement nsISeekableStream.
|
||||
* @param aStreamLen
|
||||
* specifies how much data to read from the stream. pass -1 to read
|
||||
* all data available in the stream.
|
||||
* @param aSegmentSize
|
||||
* if the stream transport service is used, then this parameter
|
||||
* specifies the segment size for the stream transport's buffer.
|
||||
* pass 0 to specify the default value.
|
||||
* @param aSegmentCount
|
||||
* if the stream transport service is used, then this parameter
|
||||
* specifies the segment count for the stream transport's buffer.
|
||||
* pass 0 to specify the default value.
|
||||
* @param aCloseWhenDone
|
||||
* if true, the input stream will be closed after it has been read.
|
||||
*/
|
||||
void init(in nsIInputStream aStream,
|
||||
in long aStreamPos,
|
||||
in long aStreamLen,
|
||||
in unsigned long aSegmentSize,
|
||||
in unsigned long aSegmentCount,
|
||||
in boolean aCloseWhenDone);
|
||||
|
||||
/**
|
||||
* asyncRead causes the input stream to be read in chunks and delivered
|
||||
* asynchronously to the listener via OnDataAvailable.
|
||||
*
|
||||
* @param aListener
|
||||
* receives notifications.
|
||||
* @param aListenerContext
|
||||
* passed to listener methods.
|
||||
*/
|
||||
void asyncRead(in nsIStreamListener aListener,
|
||||
in nsISupports aListenerContext);
|
||||
};
|
|
@ -0,0 +1,109 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsITransport;
|
||||
interface nsIInputStream;
|
||||
interface nsIOutputStream;
|
||||
|
||||
/**
|
||||
* This service read/writes a stream on a background thread.
|
||||
*
|
||||
* Use this service to transform any blocking stream (e.g., file stream)
|
||||
* into a fully asynchronous stream that can be read/written without
|
||||
* blocking the main thread.
|
||||
*/
|
||||
[scriptable, uuid(3e0f7a3b-65fd-4e10-92f3-56a8f4622f55)]
|
||||
interface nsIStreamTransportService : nsISupports
|
||||
{
|
||||
/**
|
||||
* CreateInputTransport
|
||||
*
|
||||
* @param aStream
|
||||
* The input stream that will be read on a background thread.
|
||||
* This stream must implement "blocking" stream semantics.
|
||||
* @param aStartOffset
|
||||
* The input stream will be read starting from this offset. Pass
|
||||
* -1 to read from the current stream offset. NOTE: this parameter
|
||||
* is ignored if the stream does not support nsISeekableStream.
|
||||
* @param aReadLimit
|
||||
* This parameter limits the number of bytes that will be read from
|
||||
* the input stream. Pass -1 to read everything.
|
||||
* @param aCloseWhenDone
|
||||
* Specify this flag to have the input stream closed once its
|
||||
* contents have been completely read.
|
||||
*
|
||||
* @return nsITransport instance.
|
||||
*/
|
||||
nsITransport createInputTransport(in nsIInputStream aStream,
|
||||
in long aStartOffset,
|
||||
in long aReadLimit,
|
||||
in boolean aCloseWhenDone);
|
||||
|
||||
/**
|
||||
* CreateOutputTransport
|
||||
*
|
||||
* @param aStream
|
||||
* The output stream that will be written to on a background thread.
|
||||
* This stream must implement "blocking" stream semantics.
|
||||
* @param aStartOffset
|
||||
* The output stream will be written starting at this offset. Pass
|
||||
* -1 to write to the current stream offset. NOTE: this parameter
|
||||
* is ignored if the stream does not support nsISeekableStream.
|
||||
* @param aWriteLimit
|
||||
* This parameter limits the number of bytes that will be written to
|
||||
* the output stream. Pass -1 for unlimited writing.
|
||||
* @param aCloseWhenDone
|
||||
* Specify this flag to have the output stream closed once its
|
||||
* contents have been completely written.
|
||||
*
|
||||
* @return nsITransport instance.
|
||||
*/
|
||||
nsITransport createOutputTransport(in nsIOutputStream aStream,
|
||||
in long aStartOffset,
|
||||
in long aWriteLimit,
|
||||
in boolean aCloseWhenDone);
|
||||
|
||||
/**
|
||||
* init/shutdown routines.
|
||||
*
|
||||
* XXX these should move to some necko internal interface instead.
|
||||
*/
|
||||
void init();
|
||||
void shutdown();
|
||||
};
|
|
@ -0,0 +1,574 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsAsyncStreamCopier.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsNetSegmentUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// NSPR_LOG_MODULES=nsStreamCopier:5
|
||||
//
|
||||
static PRLogModuleInfo *gStreamCopierLog = nsnull;
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(gStreamCopierLog, PR_LOG_DEBUG, args)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsAsyncStreamCopier::nsAsyncStreamCopier()
|
||||
: mInput(this)
|
||||
, mOutput(this)
|
||||
, mLock(PR_NewLock())
|
||||
, mChunkSize(NET_DEFAULT_SEGMENT_SIZE)
|
||||
, mStatus(NS_OK)
|
||||
, mIsPending(PR_FALSE)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gStreamCopierLog)
|
||||
gStreamCopierLog = PR_NewLogModule("nsStreamCopier");
|
||||
#endif
|
||||
}
|
||||
|
||||
nsAsyncStreamCopier::~nsAsyncStreamCopier()
|
||||
{
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsAsyncStreamCopier::IsComplete(nsresult *status)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (status)
|
||||
*status = mStatus;
|
||||
return !mIsPending;
|
||||
}
|
||||
|
||||
void
|
||||
nsAsyncStreamCopier::Complete(nsresult reason)
|
||||
{
|
||||
LOG(("nsAsyncStreamCopier::Complete [this=%x reason=%x]\n", this, reason));
|
||||
|
||||
nsCOMPtr<nsIRequestObserver> observer;
|
||||
nsCOMPtr<nsISupports> ctx;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (mIsPending) {
|
||||
mIsPending = PR_FALSE;
|
||||
mStatus = reason;
|
||||
|
||||
// setup OnStopRequest callback and release references...
|
||||
observer = mObserver;
|
||||
ctx = mObserverContext;
|
||||
mObserver = nsnull;
|
||||
mObserverContext = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
if (observer) {
|
||||
if (reason == NS_BASE_STREAM_CLOSED)
|
||||
reason = NS_OK;
|
||||
LOG((" calling OnStopRequest [status=%x]\n", reason));
|
||||
observer->OnStopRequest(this, ctx, reason);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsISupports
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsAsyncStreamCopier,
|
||||
nsIRequest,
|
||||
nsIAsyncStreamCopier)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIRequest
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::GetName(nsACString &name)
|
||||
{
|
||||
name = NS_LITERAL_CSTRING("nsAsyncStreamCopier");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::IsPending(PRBool *result)
|
||||
{
|
||||
*result = !IsComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::GetStatus(nsresult *status)
|
||||
{
|
||||
IsComplete(status);
|
||||
|
||||
// mask successful "error" code.
|
||||
if (*status == NS_BASE_STREAM_CLOSED)
|
||||
*status = NS_OK;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::Cancel(nsresult status)
|
||||
{
|
||||
if (IsComplete())
|
||||
return NS_OK;
|
||||
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
NS_WARNING("cancel with non-failure status code");
|
||||
status = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
mInput.CloseEx(status);
|
||||
mOutput.CloseEx(status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::Suspend()
|
||||
{
|
||||
// this could be fairly easily implemented by making Read/ReadSegments
|
||||
// return NS_BASE_STREAM_WOULD_BLOCK.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::Resume()
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
||||
{
|
||||
*aLoadFlags = LOAD_NORMAL;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::SetLoadFlags(nsLoadFlags aLoadFlags)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::GetLoadGroup(nsILoadGroup **aLoadGroup)
|
||||
{
|
||||
*aLoadGroup = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIAsyncStreamCopier
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::Init(nsIInputStream *source,
|
||||
nsIOutputStream *sink,
|
||||
PRBool sourceBuffered,
|
||||
PRBool sinkBuffered,
|
||||
PRUint32 chunkSize)
|
||||
{
|
||||
if (chunkSize == 0)
|
||||
chunkSize = NET_DEFAULT_SEGMENT_SIZE;
|
||||
mChunkSize = chunkSize;
|
||||
|
||||
mInput.mSource = source;
|
||||
mInput.mAsyncSource = do_QueryInterface(source);
|
||||
mInput.mBuffered = sourceBuffered;
|
||||
|
||||
mOutput.mSink = sink;
|
||||
mOutput.mAsyncSink = do_QueryInterface(sink);
|
||||
mOutput.mBuffered = sinkBuffered;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx)
|
||||
{
|
||||
LOG(("nsAsyncStreamCopier::AsyncCopy [this=%x observer=%x]\n", this, observer));
|
||||
|
||||
NS_ENSURE_ARG_POINTER(observer);
|
||||
NS_ENSURE_TRUE(mInput.mSource, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_TRUE(mOutput.mSink, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// we could perhaps work around this requirement by automatically using
|
||||
// the stream transport service, but then we are left having to make a
|
||||
// rather arbitrary selection between opening an asynchronous input
|
||||
// stream or an asynchronous output stream. that choice is probably
|
||||
// best left up to the consumer of this async stream copier.
|
||||
if (!mInput.mAsyncSource && !mOutput.mAsyncSink) {
|
||||
NS_ERROR("at least one stream must be asynchronous");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// build proxy for observer events
|
||||
rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), observer);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// from this point forward, AsyncCopy is going to return NS_OK. any errors
|
||||
// will be reported via OnStopRequest.
|
||||
mIsPending = PR_TRUE;
|
||||
|
||||
mObserverContext = ctx;
|
||||
rv = mObserver->OnStartRequest(this, mObserverContext);
|
||||
if (NS_FAILED(rv))
|
||||
Cancel(rv);
|
||||
|
||||
rv = NS_AsyncCopy(&mInput, &mOutput, mInput.mBuffered, mOutput.mBuffered, mChunkSize);
|
||||
if (NS_FAILED(rv))
|
||||
Cancel(rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputWrapper
|
||||
//
|
||||
// NOTE: the input stream methods may be accessed on any thread; however, we
|
||||
// assume that Read/ReadSegments will not be called simultaneously on
|
||||
// more than one thread.
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier::
|
||||
nsInputWrapper::AddRef()
|
||||
{
|
||||
return mCopier->AddRef();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier::
|
||||
nsInputWrapper::Release()
|
||||
{
|
||||
return mCopier->Release();
|
||||
}
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE3(nsAsyncStreamCopier::nsInputWrapper,
|
||||
nsIAsyncInputStream,
|
||||
nsIInputStream,
|
||||
nsIInputStreamNotify)
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::Close()
|
||||
{
|
||||
return CloseEx(NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::Available(PRUint32 *avail)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status))
|
||||
return status;
|
||||
|
||||
NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv = mSource->Available(avail);
|
||||
if (NS_FAILED(rv))
|
||||
CloseEx(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::Read(char *buf, PRUint32 count, PRUint32 *countRead)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status)) {
|
||||
*countRead = 0;
|
||||
return status == NS_BASE_STREAM_CLOSED ? NS_OK : status;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv = mSource->Read(buf, count, countRead);
|
||||
/*
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv != NS_BASE_STREAM_WOULD_BLOCK)
|
||||
CloseEx(rv);
|
||||
// else, not a real error
|
||||
}
|
||||
else if (*countRead == 0)
|
||||
CloseEx(NS_BASE_STREAM_CLOSED);
|
||||
*/
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_METHOD nsAsyncStreamCopier::
|
||||
nsInputWrapper::ReadSegmentsThunk(nsIInputStream *stream,
|
||||
void *closure,
|
||||
const char *segment,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countRead)
|
||||
{
|
||||
nsInputWrapper *self = (nsInputWrapper *) closure;
|
||||
return self->mWriter(self, self->mClosure, segment, offset, count, countRead);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure,
|
||||
PRUint32 count, PRUint32 *countRead)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status)) {
|
||||
*countRead = 0;
|
||||
return status == NS_BASE_STREAM_CLOSED ? NS_OK : status;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!mBuffered)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
mWriter = writer;
|
||||
mClosure = closure;
|
||||
|
||||
nsresult rv = mSource->ReadSegments(ReadSegmentsThunk, this, count, countRead);
|
||||
/*
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv != NS_BASE_STREAM_WOULD_BLOCK)
|
||||
CloseEx(rv);
|
||||
// else, not a real error
|
||||
}
|
||||
else if (*countRead == 0)
|
||||
CloseEx(NS_BASE_STREAM_CLOSED);
|
||||
*/
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::IsNonBlocking(PRBool *result)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status))
|
||||
return status;
|
||||
|
||||
NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
|
||||
return mSource->IsNonBlocking(result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::CloseEx(nsresult reason)
|
||||
{
|
||||
mCopier->Complete(reason);
|
||||
|
||||
if (mAsyncSource)
|
||||
mAsyncSource->CloseEx(reason);
|
||||
else
|
||||
mSource->Close();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::AsyncWait(nsIInputStreamNotify *notify, PRUint32 amount, nsIEventQueue *eventQ)
|
||||
{
|
||||
// we'll cheat a little bit here since we know that NS_AsyncCopy does pass
|
||||
// an event queue.
|
||||
NS_ASSERTION(eventQ == nsnull, "unexpected");
|
||||
|
||||
if (mAsyncSource) {
|
||||
mNotify = notify;
|
||||
return mAsyncSource->AsyncWait(this, amount, eventQ);
|
||||
}
|
||||
|
||||
// else, stream is ready
|
||||
notify->OnInputStreamReady(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsInputWrapper::OnInputStreamReady(nsIAsyncInputStream *stream)
|
||||
{
|
||||
// simple thunk
|
||||
return mNotify->OnInputStreamReady(this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOutputWrapper
|
||||
//
|
||||
// NOTE: the output stream methods may be accessed on any thread; however, we
|
||||
// assume that Write/WriteSegments will not be called simultaneously on
|
||||
// more than one thread.
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier::
|
||||
nsOutputWrapper::AddRef()
|
||||
{
|
||||
return mCopier->AddRef();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier::
|
||||
nsOutputWrapper::Release()
|
||||
{
|
||||
return mCopier->Release();
|
||||
}
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE3(nsAsyncStreamCopier::nsOutputWrapper,
|
||||
nsIAsyncOutputStream,
|
||||
nsIOutputStream,
|
||||
nsIOutputStreamNotify)
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::Close()
|
||||
{
|
||||
return CloseEx(NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::Flush()
|
||||
{
|
||||
NS_NOTREACHED("nsOutputWrapper::Flush");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::Write(const char *buf, PRUint32 count, PRUint32 *countWritten)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status)) {
|
||||
*countWritten = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv = mSink->Write(buf, count, countWritten);
|
||||
/*
|
||||
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK)
|
||||
CloseEx(rv);
|
||||
*/
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_METHOD nsAsyncStreamCopier::
|
||||
nsOutputWrapper::WriteSegmentsThunk(nsIOutputStream *stream,
|
||||
void *closure,
|
||||
char *segment,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countWritten)
|
||||
{
|
||||
nsOutputWrapper *self = (nsOutputWrapper *) closure;
|
||||
return self->mReader(self, self->mClosure, segment, offset, count, countWritten);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::WriteSegments(nsReadSegmentFun reader, void *closure,
|
||||
PRUint32 count, PRUint32 *countWritten)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status)) {
|
||||
*countWritten = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!mBuffered)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
mReader = reader;
|
||||
mClosure = closure;
|
||||
|
||||
nsresult rv = mSink->WriteSegments(WriteSegmentsThunk, this, count, countWritten);
|
||||
/*
|
||||
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK)
|
||||
CloseEx(rv);
|
||||
*/
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::WriteFrom(nsIInputStream *stream, PRUint32 count, PRUint32 *countWritten)
|
||||
{
|
||||
NS_NOTREACHED("nsOutputWrapper::WriteFrom");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::IsNonBlocking(PRBool *result)
|
||||
{
|
||||
nsresult status;
|
||||
if (mCopier->IsComplete(&status))
|
||||
return status;
|
||||
|
||||
NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED);
|
||||
return mSink->IsNonBlocking(result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::CloseEx(nsresult reason)
|
||||
{
|
||||
mCopier->Complete(reason);
|
||||
|
||||
if (mAsyncSink)
|
||||
mAsyncSink->CloseEx(reason);
|
||||
else
|
||||
mSink->Close();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::AsyncWait(nsIOutputStreamNotify *notify, PRUint32 amount, nsIEventQueue *eventQ)
|
||||
{
|
||||
// we'll cheat a little bit here since we know that NS_AsyncCopy does pass
|
||||
// an event queue.
|
||||
NS_ASSERTION(eventQ == nsnull, "unexpected");
|
||||
|
||||
if (mAsyncSink) {
|
||||
mNotify = notify;
|
||||
return mAsyncSink->AsyncWait(this, amount, eventQ);
|
||||
}
|
||||
|
||||
// else, stream is ready
|
||||
notify->OnOutputStreamReady(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAsyncStreamCopier::
|
||||
nsOutputWrapper::OnOutputStreamReady(nsIAsyncOutputStream *stream)
|
||||
{
|
||||
// simple thunk
|
||||
return mNotify->OnOutputStreamReady(this);
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsAsyncStreamCopier_h__
|
||||
#define nsAsyncStreamCopier_h__
|
||||
|
||||
#include "nsIAsyncStreamCopier.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsIRequestObserver.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "prlock.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsAsyncStreamCopier : public nsIAsyncStreamCopier
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSIASYNCSTREAMCOPIER
|
||||
|
||||
nsAsyncStreamCopier();
|
||||
virtual ~nsAsyncStreamCopier();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// these methods may be called on any thread
|
||||
|
||||
PRBool IsComplete(nsresult *status = nsnull);
|
||||
void Complete(nsresult status);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// nsInputWrapper
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class nsInputWrapper : public nsIAsyncInputStream
|
||||
, public nsIInputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
NS_DECL_NSIINPUTSTREAMNOTIFY
|
||||
|
||||
nsInputWrapper(nsAsyncStreamCopier *copier)
|
||||
: mCopier(copier)
|
||||
, mBuffered(PR_FALSE)
|
||||
{ }
|
||||
virtual ~nsInputWrapper() {}
|
||||
|
||||
nsAsyncStreamCopier *mCopier;
|
||||
nsCOMPtr<nsIInputStream> mSource;
|
||||
nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
|
||||
PRBool mBuffered;
|
||||
|
||||
private:
|
||||
// ReadSegments thunk impl
|
||||
nsWriteSegmentFun mWriter;
|
||||
void *mClosure;
|
||||
static NS_METHOD ReadSegmentsThunk(nsIInputStream *, void *, const char *,
|
||||
PRUint32, PRUint32, PRUint32 *);
|
||||
|
||||
// AsyncWait thunk impl
|
||||
nsCOMPtr<nsIInputStreamNotify> mNotify;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// nsInputWrapper
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class nsOutputWrapper : public nsIAsyncOutputStream
|
||||
, public nsIOutputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
NS_DECL_NSIASYNCOUTPUTSTREAM
|
||||
NS_DECL_NSIOUTPUTSTREAMNOTIFY
|
||||
|
||||
nsOutputWrapper(nsAsyncStreamCopier *copier)
|
||||
: mCopier(copier)
|
||||
, mBuffered(PR_FALSE)
|
||||
{ }
|
||||
virtual ~nsOutputWrapper() {}
|
||||
|
||||
nsAsyncStreamCopier *mCopier;
|
||||
nsCOMPtr<nsIOutputStream> mSink;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
|
||||
PRBool mBuffered;
|
||||
|
||||
private:
|
||||
// WriteSegments thunk impl
|
||||
nsReadSegmentFun mReader;
|
||||
void *mClosure;
|
||||
static NS_METHOD WriteSegmentsThunk(nsIOutputStream *, void *, char *,
|
||||
PRUint32, PRUint32, PRUint32 *);
|
||||
|
||||
// AsyncWait thunk impl
|
||||
nsCOMPtr<nsIOutputStreamNotify> mNotify;
|
||||
};
|
||||
|
||||
private:
|
||||
nsInputWrapper mInput;
|
||||
nsOutputWrapper mOutput;
|
||||
|
||||
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||
nsCOMPtr<nsISupports> mObserverContext;
|
||||
|
||||
PRLock *mLock;
|
||||
|
||||
PRUint32 mChunkSize;
|
||||
nsresult mStatus;
|
||||
PRBool mIsPending;
|
||||
};
|
||||
|
||||
#endif // !nsAsyncStreamCopier_h__
|
|
@ -0,0 +1,425 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the NPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the NPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsInputStreamPump.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "prlog.h"
|
||||
|
||||
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
|
||||
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// NSPR_LOG_MODULES=nsStreamPump:5
|
||||
//
|
||||
static PRLogModuleInfo *gStreamPumpLog = nsnull;
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(gStreamPumpLog, PR_LOG_DEBUG, args)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputStreamPump methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsInputStreamPump::nsInputStreamPump()
|
||||
: mState(STATE_IDLE)
|
||||
, mStreamOffset(0)
|
||||
, mStreamLength(~0U)
|
||||
, mStatus(NS_OK)
|
||||
, mSuspendCount(0)
|
||||
, mLoadFlags(LOAD_NORMAL)
|
||||
, mWaiting(PR_FALSE)
|
||||
, mCloseWhenDone(PR_FALSE)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gStreamPumpLog)
|
||||
gStreamPumpLog = PR_NewLogModule("nsStreamPump");
|
||||
#endif
|
||||
}
|
||||
|
||||
nsInputStreamPump::~nsInputStreamPump()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsInputStreamPump::EnsureWaiting()
|
||||
{
|
||||
if (!mWaiting) {
|
||||
nsresult rv = mAsyncStream->AsyncWait(this, 0, mEventQ);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("AsyncWait failed");
|
||||
return rv;
|
||||
}
|
||||
mWaiting = PR_TRUE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputStreamPump::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// although this class can only be accessed from one thread at a time, we do
|
||||
// allow its ownership to move from thread to thread, assuming the consumer
|
||||
// understands the limitations of this.
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsInputStreamPump,
|
||||
nsIRequest,
|
||||
nsIInputStreamNotify,
|
||||
nsIInputStreamPump)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputStreamPump::nsIRequest
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::GetName(nsACString &result)
|
||||
{
|
||||
result.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::IsPending(PRBool *result)
|
||||
{
|
||||
*result = (mState != STATE_IDLE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::GetStatus(nsresult *status)
|
||||
{
|
||||
*status = mStatus;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::Cancel(nsresult status)
|
||||
{
|
||||
LOG(("nsInputStreamPump::Cancel [this=%x status=%x]\n",
|
||||
this, status));
|
||||
|
||||
if (NS_FAILED(mStatus)) {
|
||||
LOG((" already canceled\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(NS_FAILED(status), "cancel with non-failure status code");
|
||||
mStatus = status;
|
||||
|
||||
// close input stream
|
||||
if (mAsyncStream) {
|
||||
mAsyncStream->CloseEx(status);
|
||||
mSuspendCount = 0; // un-suspend
|
||||
EnsureWaiting();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::Suspend()
|
||||
{
|
||||
LOG(("nsInputStreamPump::Suspend [this=%x]\n", this));
|
||||
NS_ENSURE_TRUE(mState != STATE_IDLE, NS_ERROR_UNEXPECTED);
|
||||
++mSuspendCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::Resume()
|
||||
{
|
||||
LOG(("nsInputStreamPump::Resume [this=%x]\n", this));
|
||||
NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_TRUE(mState != STATE_IDLE, NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (--mSuspendCount == 0)
|
||||
EnsureWaiting();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
||||
{
|
||||
*aLoadFlags = mLoadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::SetLoadFlags(nsLoadFlags aLoadFlags)
|
||||
{
|
||||
mLoadFlags = aLoadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::GetLoadGroup(nsILoadGroup **aLoadGroup)
|
||||
{
|
||||
NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
mLoadGroup = aLoadGroup;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputStreamPump::nsIInputStreamPump implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::Init(nsIInputStream *stream,
|
||||
PRInt32 streamPos, PRInt32 streamLen,
|
||||
PRUint32 segsize, PRUint32 segcount,
|
||||
PRBool closeWhenDone)
|
||||
{
|
||||
NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);
|
||||
|
||||
mStreamOffset = (PRUint32) streamPos;
|
||||
mStreamLength = (PRUint32) streamLen;
|
||||
mStream = stream;
|
||||
mSegSize = segsize;
|
||||
mSegCount = segcount;
|
||||
mCloseWhenDone = closeWhenDone;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt)
|
||||
{
|
||||
NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
//
|
||||
// OK, we need to use the stream transport service if
|
||||
//
|
||||
// (1) the stream is blocking
|
||||
// (2) the stream does not support nsIAsyncInputStream
|
||||
//
|
||||
|
||||
PRBool nonBlocking;
|
||||
rv = mStream->IsNonBlocking(&nonBlocking);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (nonBlocking)
|
||||
mAsyncStream = do_QueryInterface(mStream);
|
||||
|
||||
if (!mAsyncStream) {
|
||||
// ok, let's use the stream transport service to read this stream.
|
||||
nsCOMPtr<nsIStreamTransportService> sts =
|
||||
do_GetService(kStreamTransportServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsITransport> transport;
|
||||
rv = sts->CreateInputTransport(mStream, mStreamOffset, mStreamLength,
|
||||
mCloseWhenDone, getter_AddRefs(transport));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> wrapper;
|
||||
rv = transport->OpenInputStream(0, mSegSize, mSegCount, getter_AddRefs(wrapper));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mAsyncStream = do_QueryInterface(wrapper, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
// mStreamOffset now holds the number of bytes currently read. we use this
|
||||
// to enforce the mStreamLength restriction.
|
||||
mStreamOffset = 0;
|
||||
|
||||
// grab event queue (we must do this here by contract, since all notifications
|
||||
// must go to the thread which called AsyncRead)
|
||||
nsCOMPtr<nsIEventQueueService> eqs = do_GetService(kEventQueueServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(mEventQ));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = EnsureWaiting();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->AddRequest(this, nsnull);
|
||||
|
||||
mState = STATE_START;
|
||||
mListener = listener;
|
||||
mListenerContext = ctxt;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputStreamPump::nsIInputStreamNotify implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream)
|
||||
{
|
||||
LOG(("nsInputStreamPump::OnInputStreamReady [this=%x]\n", this));
|
||||
|
||||
// this function has been called from a PLEvent, so we can safely call
|
||||
// any listener or progress sink methods directly from here.
|
||||
|
||||
for (;;) {
|
||||
if (mSuspendCount || mState == STATE_IDLE) {
|
||||
mWaiting = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
PRUint32 nextState;
|
||||
switch (mState) {
|
||||
case STATE_START:
|
||||
nextState = OnStateStart();
|
||||
break;
|
||||
case STATE_TRANSFER:
|
||||
nextState = OnStateTransfer();
|
||||
break;
|
||||
case STATE_STOP:
|
||||
nextState = OnStateStop();
|
||||
break;
|
||||
}
|
||||
|
||||
if (mState == nextState && !mSuspendCount) {
|
||||
NS_ASSERTION(mState == STATE_TRANSFER, "unexpected state");
|
||||
NS_ASSERTION(NS_SUCCEEDED(mStatus), "unexpected status");
|
||||
|
||||
mWaiting = PR_FALSE;
|
||||
mStatus = EnsureWaiting();
|
||||
if (NS_SUCCEEDED(mStatus))
|
||||
break;
|
||||
|
||||
nextState = STATE_STOP;
|
||||
}
|
||||
|
||||
mState = nextState;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsInputStreamPump::OnStateStart()
|
||||
{
|
||||
LOG((" OnStateStart [this=%x]\n", this));
|
||||
|
||||
nsresult rv = mListener->OnStartRequest(this, mListenerContext);
|
||||
|
||||
// an error returned from OnStartRequest should cause us to abort; however,
|
||||
// we must not stomp on mStatus if already canceled.
|
||||
if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus))
|
||||
mStatus = rv;
|
||||
|
||||
return NS_SUCCEEDED(mStatus) ? STATE_TRANSFER : STATE_STOP;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsInputStreamPump::OnStateTransfer()
|
||||
{
|
||||
LOG((" OnStateTransfer [this=%x]\n", this));
|
||||
|
||||
// if canceled, go directly to STATE_STOP...
|
||||
if (NS_FAILED(mStatus))
|
||||
return STATE_STOP;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
PRUint32 avail;
|
||||
rv = mAsyncStream->Available(&avail);
|
||||
LOG((" Available returned [stream=%x rv=%x avail=%u]\n", mAsyncStream.get(), rv, avail));
|
||||
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
rv = NS_OK;
|
||||
avail = 0;
|
||||
}
|
||||
else if (NS_SUCCEEDED(rv) && avail) {
|
||||
// figure out how much data to report (XXX detect overflow??)
|
||||
if (avail + mStreamOffset > mStreamLength) {
|
||||
avail = mStreamLength - mStreamOffset;
|
||||
if (avail > mSegSize)
|
||||
avail = mSegSize;
|
||||
}
|
||||
|
||||
if (avail) {
|
||||
LOG((" calling OnDataAvailable [offset=%u count=%u]\n", mStreamOffset, avail));
|
||||
|
||||
rv = mListener->OnDataAvailable(this, mListenerContext, mAsyncStream, mStreamOffset, avail);
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mStreamOffset += avail;
|
||||
}
|
||||
}
|
||||
|
||||
// an error returned from Available or OnDataAvailable should cause us to
|
||||
// abort; however, we must not stomp on mStatus if already canceled.
|
||||
|
||||
if (NS_SUCCEEDED(mStatus)) {
|
||||
if (NS_FAILED(rv))
|
||||
mStatus = rv;
|
||||
else if (avail)
|
||||
return STATE_TRANSFER;
|
||||
}
|
||||
return STATE_STOP;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsInputStreamPump::OnStateStop()
|
||||
{
|
||||
LOG((" OnStateStop [this=%x status=%x]\n", this, mStatus));
|
||||
|
||||
if (mCloseWhenDone)
|
||||
mStream->Close();
|
||||
mStream = 0;
|
||||
mAsyncStream = 0;
|
||||
|
||||
mEventQ = 0;
|
||||
mIsPending = PR_FALSE;
|
||||
|
||||
mListener->OnStopRequest(this, mListenerContext, mStatus);
|
||||
mListener = 0;
|
||||
mListenerContext = 0;
|
||||
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
|
||||
|
||||
return STATE_IDLE;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the NPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the NPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsInputStreamPump_h__
|
||||
#define nsInputStreamPump_h__
|
||||
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsILoadGroup.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIEventQueue.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsInputStreamPump : public nsIInputStreamPump
|
||||
, public nsIInputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSIINPUTSTREAMPUMP
|
||||
NS_DECL_NSIINPUTSTREAMNOTIFY
|
||||
|
||||
nsInputStreamPump();
|
||||
virtual ~nsInputStreamPump();
|
||||
|
||||
protected:
|
||||
|
||||
enum {
|
||||
STATE_IDLE,
|
||||
STATE_START,
|
||||
STATE_TRANSFER,
|
||||
STATE_STOP
|
||||
};
|
||||
|
||||
nsresult EnsureWaiting();
|
||||
PRUint32 OnStateStart();
|
||||
PRUint32 OnStateTransfer();
|
||||
PRUint32 OnStateStop();
|
||||
|
||||
PRUint32 mState;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsIStreamListener> mListener;
|
||||
nsCOMPtr<nsISupports> mListenerContext;
|
||||
nsCOMPtr<nsIEventQueue> mEventQ;
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
|
||||
PRUint32 mStreamOffset;
|
||||
PRUint32 mStreamLength;
|
||||
PRUint32 mSegSize;
|
||||
PRUint32 mSegCount;
|
||||
nsresult mStatus;
|
||||
PRUint32 mSuspendCount;
|
||||
PRUint32 mLoadFlags;
|
||||
PRPackedBool mIsPending;
|
||||
PRPackedBool mWaiting; // true if waiting on async source
|
||||
PRPackedBool mCloseWhenDone;
|
||||
};
|
||||
|
||||
#endif // !nsInputStreamChannel_h__
|
|
@ -0,0 +1,70 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsNetSegmentUtils_h__
|
||||
#define nsNetSegmentUtils_h__
|
||||
|
||||
#include "nsIOService.h"
|
||||
|
||||
#define NET_DEFAULT_SEGMENT_SIZE 4096
|
||||
#define NET_DEFAULT_SEGMENT_COUNT 16
|
||||
|
||||
/**
|
||||
* returns preferred allocator for given segment size. NULL implies
|
||||
* system allocator. this result can be used when allocating a pipe.
|
||||
*/
|
||||
static inline nsIMemory *
|
||||
net_GetSegmentAlloc(PRUint32 segsize)
|
||||
{
|
||||
return (segsize == NET_DEFAULT_SEGMENT_SIZE)
|
||||
? nsIOService::gBufferCache
|
||||
: nsnull;
|
||||
}
|
||||
|
||||
/**
|
||||
* applies defaults to segment params in a consistent way.
|
||||
*/
|
||||
static inline void
|
||||
net_ResolveSegmentParams(PRUint32 &segsize, PRUint32 &segcount)
|
||||
{
|
||||
if (!segsize)
|
||||
segsize = NET_DEFAULT_SEGMENT_SIZE;
|
||||
if (!segcount)
|
||||
segcount = NET_DEFAULT_SEGMENT_COUNT;
|
||||
}
|
||||
|
||||
#endif // !nsNetSegmentUtils_h__
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,308 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsSocketTransport2_h__
|
||||
#define nsSocketTransport2_h__
|
||||
|
||||
#include "nsSocketTransportService2.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIRequest.h"
|
||||
|
||||
class nsSocketTransport;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// after this short interval, we will return to PR_Poll
|
||||
#define NS_SOCKET_CONNECT_TIMEOUT PR_MillisecondsToInterval(20)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsSocketInputStream : public nsIAsyncInputStream
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
|
||||
nsSocketInputStream(nsSocketTransport *);
|
||||
virtual ~nsSocketInputStream();
|
||||
|
||||
PRBool IsReferenced() { return mReaderRefCnt > 0; }
|
||||
nsresult Condition() { return mCondition; }
|
||||
PRUint32 ByteCount() { return mByteCount; }
|
||||
|
||||
// called by the socket transport on the socket thread...
|
||||
void OnSocketReady(nsresult condition);
|
||||
|
||||
private:
|
||||
nsSocketTransport *mTransport;
|
||||
nsrefcnt mReaderRefCnt;
|
||||
|
||||
// access to these is protected by mTransport->mLock
|
||||
nsresult mCondition;
|
||||
nsIInputStreamNotify *mNotify;
|
||||
PRUint32 mByteCount;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsSocketOutputStream : public nsIAsyncOutputStream
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
NS_DECL_NSIASYNCOUTPUTSTREAM
|
||||
|
||||
nsSocketOutputStream(nsSocketTransport *);
|
||||
virtual ~nsSocketOutputStream();
|
||||
|
||||
PRBool IsReferenced() { return mWriterRefCnt > 0; }
|
||||
nsresult Condition() { return mCondition; }
|
||||
PRUint32 ByteCount() { return mByteCount; }
|
||||
|
||||
// called by the socket transport on the socket thread...
|
||||
void OnSocketReady(nsresult condition);
|
||||
|
||||
private:
|
||||
static NS_METHOD WriteFromSegments(nsIInputStream *, void *,
|
||||
const char *, PRUint32 offset,
|
||||
PRUint32 count, PRUint32 *countRead);
|
||||
|
||||
nsSocketTransport *mTransport;
|
||||
nsrefcnt mWriterRefCnt;
|
||||
|
||||
// access to these is protected by mTransport->mLock
|
||||
nsresult mCondition;
|
||||
nsIOutputStreamNotify *mNotify;
|
||||
PRUint32 mByteCount;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsSocketTransport : public nsASocketHandler
|
||||
, public nsISocketEventHandler
|
||||
, public nsISocketTransport
|
||||
, public nsIDNSListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISOCKETEVENTHANDLER
|
||||
NS_DECL_NSITRANSPORT
|
||||
NS_DECL_NSISOCKETTRANSPORT
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
|
||||
nsSocketTransport();
|
||||
|
||||
nsresult Init(const char **socketTypes, PRUint32 typeCount,
|
||||
const nsACString &host, PRUint16 port,
|
||||
nsIProxyInfo *proxyInfo);
|
||||
|
||||
// nsASocketHandler methods:
|
||||
void OnSocketReady(PRFileDesc *, PRInt16 pollFlags);
|
||||
void OnSocketDetached(PRFileDesc *);
|
||||
|
||||
private:
|
||||
|
||||
virtual ~nsSocketTransport();
|
||||
|
||||
enum {
|
||||
MSG_ENSURE_CONNECT, // no args
|
||||
MSG_DNS_LOOKUP_COMPLETE, // uparam holds "status"
|
||||
MSG_INPUT_CLOSED, // uparam holds "reason"
|
||||
MSG_INPUT_PENDING, // no args
|
||||
MSG_OUTPUT_CLOSED, // uparam holds "reason"
|
||||
MSG_OUTPUT_PENDING // no args
|
||||
};
|
||||
|
||||
enum {
|
||||
STATE_CLOSED,
|
||||
STATE_IDLE,
|
||||
STATE_RESOLVING,
|
||||
STATE_CONNECTING,
|
||||
STATE_TRANSFERRING
|
||||
};
|
||||
|
||||
class NetAddrList {
|
||||
public:
|
||||
NetAddrList() : mList(nsnull), mLen(0) {}
|
||||
~NetAddrList() { delete[] mList; }
|
||||
|
||||
// allocate space for the address list
|
||||
nsresult Init(PRUint32 len);
|
||||
|
||||
// given a net addr in the list, return the next addr.
|
||||
// if given NULL, then return the first addr in the list.
|
||||
// returns NULL if given addr is the last addr.
|
||||
PRNetAddr *GetNext(PRNetAddr *currentAddr);
|
||||
|
||||
private:
|
||||
PRNetAddr *mList;
|
||||
PRUint32 mLen;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// these members are "set" at initialization time and are never modified
|
||||
// afterwards. this allows them to be safely accessed from any thread.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// socket type info:
|
||||
char **mTypes;
|
||||
PRUint32 mTypeCount;
|
||||
nsCString mHost;
|
||||
nsCString mProxyHost;
|
||||
PRUint16 mPort;
|
||||
PRUint16 mProxyPort;
|
||||
PRBool mProxyTransparent;
|
||||
|
||||
PRUint16 SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }
|
||||
const nsCString &SocketHost() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; }
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// members accessible only on the socket transport thread:
|
||||
// (the exception being initialization/shutdown time)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// socket state vars:
|
||||
PRUint32 mState; // STATE_??? flags
|
||||
PRPackedBool mAttached;
|
||||
PRPackedBool mInputClosed;
|
||||
PRPackedBool mOutputClosed;
|
||||
|
||||
nsCOMPtr<nsIRequest> mDNSRequest;
|
||||
|
||||
// socket methods (these can only be called on the socket thread):
|
||||
|
||||
void SendStatus(nsresult status);
|
||||
nsresult ResolveHost();
|
||||
nsresult BuildSocket(PRFileDesc *&, PRBool &, PRBool &);
|
||||
nsresult InitiateSocket();
|
||||
PRBool RecoverFromError();
|
||||
|
||||
void OnMsgInputPending()
|
||||
{
|
||||
if (mState == STATE_TRANSFERRING)
|
||||
mPollFlags |= (PR_POLL_READ | PR_POLL_EXCEPT);
|
||||
}
|
||||
void OnMsgOutputPending()
|
||||
{
|
||||
if (mState == STATE_TRANSFERRING)
|
||||
mPollFlags |= (PR_POLL_WRITE | PR_POLL_EXCEPT);
|
||||
}
|
||||
void OnMsgInputClosed(nsresult reason);
|
||||
void OnMsgOutputClosed(nsresult reason);
|
||||
|
||||
// called when the socket is connected
|
||||
void OnSocketConnected(PRFileDesc *fd);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// socket input/output objects. these may be accessed on any thread with
|
||||
// the exception of some specific methods (XXX).
|
||||
|
||||
PRLock *mLock; // protects members in this section
|
||||
PRFileDesc *mFD;
|
||||
nsrefcnt mFDref; // mFD is closed when mFDref goes to zero.
|
||||
PRBool mFDconnected; // mFD is available to consumer when TRUE.
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsITransportEventSink> mEventSink;
|
||||
nsCOMPtr<nsISupports> mSecInfo;
|
||||
|
||||
nsSocketInputStream mInput;
|
||||
nsSocketOutputStream mOutput;
|
||||
|
||||
friend class nsSocketInputStream;
|
||||
friend class nsSocketOutputStream;
|
||||
|
||||
//
|
||||
// mFD access methods: called with mLock held.
|
||||
//
|
||||
PRFileDesc *GetFD_Locked();
|
||||
void ReleaseFD_Locked(PRFileDesc *fd);
|
||||
|
||||
//
|
||||
// stream state changes (called outside mLock):
|
||||
//
|
||||
void OnInputClosed(nsresult reason)
|
||||
{
|
||||
// no need to post an event if called on the socket thread
|
||||
if (PR_GetCurrentThread() == gSocketThread)
|
||||
OnMsgInputClosed(reason);
|
||||
else
|
||||
gSocketTransportService->PostEvent(this, MSG_INPUT_CLOSED, reason, nsnull);
|
||||
}
|
||||
void OnInputPending()
|
||||
{
|
||||
// no need to post an event if called on the socket thread
|
||||
if (PR_GetCurrentThread() == gSocketThread)
|
||||
OnMsgInputPending();
|
||||
else
|
||||
gSocketTransportService->PostEvent(this, MSG_INPUT_PENDING, 0, nsnull);
|
||||
}
|
||||
void OnOutputClosed(nsresult reason)
|
||||
{
|
||||
// no need to post an event if called on the socket thread
|
||||
if (PR_GetCurrentThread() == gSocketThread)
|
||||
OnMsgOutputClosed(reason); // XXX need to not be inside lock!
|
||||
else
|
||||
gSocketTransportService->PostEvent(this, MSG_OUTPUT_CLOSED, reason, nsnull);
|
||||
}
|
||||
void OnOutputPending()
|
||||
{
|
||||
// no need to post an event if called on the socket thread
|
||||
if (PR_GetCurrentThread() == gSocketThread)
|
||||
OnMsgOutputPending();
|
||||
else
|
||||
gSocketTransportService->PostEvent(this, MSG_OUTPUT_PENDING, 0, nsnull);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// we have to be careful with these. they are modified on the DNS thread,
|
||||
// while we are in the resolving state. once we've received the event
|
||||
// MSG_DNS_LOOKUP_COMPLETE, these can only be accessed on the socket thread.
|
||||
//
|
||||
NetAddrList mNetAddrList;
|
||||
PRNetAddr *mNetAddr;
|
||||
};
|
||||
|
||||
#endif // !nsSocketTransport_h__
|
|
@ -0,0 +1,566 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsSocketTransportService2.h"
|
||||
#include "nsSocketTransport2.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsNetError.h"
|
||||
#include "prlock.h"
|
||||
#include "prlog.h"
|
||||
#include "prerror.h"
|
||||
#include "plstr.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
PRLogModuleInfo *gSocketTransportLog = nsnull;
|
||||
#endif
|
||||
|
||||
nsSocketTransportService *gSocketTransportService = nsnull;
|
||||
PRThread *gSocketThread = nsnull;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ctor/dtor (called on the main/UI thread by the service manager)
|
||||
|
||||
nsSocketTransportService::nsSocketTransportService()
|
||||
: mInitialized(PR_FALSE)
|
||||
, mThread(nsnull)
|
||||
, mThreadEvent(nsnull)
|
||||
, mEventQLock(PR_NewLock())
|
||||
, mActiveCount(0)
|
||||
, mIdleCount(0)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||||
|
||||
gSocketTransportService = this;
|
||||
}
|
||||
|
||||
nsSocketTransportService::~nsSocketTransportService()
|
||||
{
|
||||
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||||
NS_ASSERTION(!mInitialized, "not shutdown properly");
|
||||
|
||||
PR_DestroyLock(mEventQLock);
|
||||
|
||||
if (mThreadEvent)
|
||||
PR_DestroyPollableEvent(mThreadEvent);
|
||||
|
||||
gSocketTransportService = nsnull;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// event queue (any thread)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::PostEvent(nsISocketEventHandler *handler,
|
||||
PRUint32 type, PRUint32 uparam,
|
||||
void *vparam)
|
||||
{
|
||||
LOG(("nsSocketTransportService::PostEvent [handler=%x type=%u u=%x v=%x]\n",
|
||||
handler, type, uparam, vparam));
|
||||
|
||||
NS_ASSERTION(handler, "null handler");
|
||||
|
||||
nsAutoLock lock(mEventQLock);
|
||||
NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
|
||||
|
||||
SocketEvent *event = new SocketEvent(handler, type, uparam, vparam);
|
||||
if (!event)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (mEventQ.mTail)
|
||||
mEventQ.mTail->mNext = event;
|
||||
mEventQ.mTail = event;
|
||||
if (!mEventQ.mHead)
|
||||
mEventQ.mHead = event;
|
||||
|
||||
PR_SetPollableEvent(mThreadEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// socket api (socket thread only)
|
||||
|
||||
nsresult
|
||||
nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
|
||||
{
|
||||
LOG(("nsSocketTransportService::AttachSocket [handler=%x]\n", handler));
|
||||
|
||||
NS_ENSURE_TRUE(mServicingEventQ, NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_TRUE(mIdleCount < NS_SOCKET_MAX_COUNT, NS_ERROR_UNEXPECTED);
|
||||
|
||||
SocketContext *sock = &mIdleList[mIdleCount];
|
||||
sock->mFD = fd;
|
||||
sock->mHandler = handler;
|
||||
NS_ADDREF(handler);
|
||||
|
||||
mIdleCount++;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransportService::DetachSocket_Internal(SocketContext *sock)
|
||||
{
|
||||
LOG(("nsSocketTransportService::DetachSocket_Internal [handler=%x]\n", sock->mHandler));
|
||||
|
||||
// inform the handler that this socket is going away
|
||||
sock->mHandler->OnSocketDetached(sock->mFD);
|
||||
|
||||
// cleanup
|
||||
sock->mFD = nsnull;
|
||||
NS_RELEASE(sock->mHandler);
|
||||
|
||||
// find out what list this is on...
|
||||
PRInt32 index = sock - mActiveList;
|
||||
if (index > 0 && index <= NS_SOCKET_MAX_COUNT)
|
||||
RemoveFromPollList(sock);
|
||||
else
|
||||
RemoveFromIdleList(sock);
|
||||
|
||||
// NOTE: sock is now an invalid pointer
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransportService::AddToPollList(SocketContext *sock)
|
||||
{
|
||||
LOG(("nsSocketTransportService::AddToPollList [handler=%x]\n", sock->mHandler));
|
||||
|
||||
NS_ASSERTION(mActiveCount < NS_SOCKET_MAX_COUNT, "too many active sockets");
|
||||
|
||||
memcpy(&mActiveList[++mActiveCount], sock, sizeof(SocketContext));
|
||||
mPollList[mActiveCount].fd = sock->mFD;
|
||||
mPollList[mActiveCount].in_flags = sock->mHandler->mPollFlags;
|
||||
mPollList[mActiveCount].out_flags = 0;
|
||||
|
||||
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
|
||||
{
|
||||
LOG(("nsSocketTransportService::RemoveFromPollList [handler=%x]\n", sock->mHandler));
|
||||
|
||||
PRUint32 index = sock - mActiveList;
|
||||
NS_ASSERTION(index > 0 && index <= NS_SOCKET_MAX_COUNT, "invalid index");
|
||||
|
||||
LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
|
||||
|
||||
if (index != mActiveCount) {
|
||||
memcpy(&mActiveList[index], &mActiveList[mActiveCount], sizeof(SocketContext));
|
||||
memcpy(&mPollList[index], &mPollList[mActiveCount], sizeof(PRPollDesc));
|
||||
}
|
||||
mActiveCount--;
|
||||
|
||||
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransportService::AddToIdleList(SocketContext *sock)
|
||||
{
|
||||
LOG(("nsSocketTransportService::AddToIdleList [handler=%x]\n", sock->mHandler));
|
||||
|
||||
NS_ASSERTION(mIdleCount < NS_SOCKET_MAX_COUNT, "too many idle sockets");
|
||||
memcpy(&mIdleList[mIdleCount], sock, sizeof(SocketContext));
|
||||
mIdleCount++;
|
||||
|
||||
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
|
||||
{
|
||||
LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%x]\n", sock->mHandler));
|
||||
|
||||
PRUint32 index = sock - &mIdleList[0];
|
||||
NS_ASSERTION(index >= 0 && index < NS_SOCKET_MAX_COUNT, "invalid index");
|
||||
|
||||
if (index != mIdleCount - 1)
|
||||
memcpy(&mActiveList[index], &mActiveList[mIdleCount - 1], sizeof(SocketContext));
|
||||
mIdleCount--;
|
||||
|
||||
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// host:port -> ipaddr cache
|
||||
|
||||
PLDHashTableOps nsSocketTransportService::ops =
|
||||
{
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
PL_DHashGetKeyStub,
|
||||
PL_DHashStringKey,
|
||||
nsSocketTransportService::MatchEntry,
|
||||
PL_DHashMoveEntryStub,
|
||||
nsSocketTransportService::ClearEntry,
|
||||
PL_DHashFinalizeStub,
|
||||
nsnull
|
||||
};
|
||||
|
||||
PRBool PR_CALLBACK
|
||||
nsSocketTransportService::MatchEntry(PLDHashTable *table,
|
||||
const PLDHashEntryHdr *entry,
|
||||
const void *key)
|
||||
{
|
||||
const nsSocketTransportService::nsHostEntry *he =
|
||||
NS_REINTERPRET_CAST(const nsSocketTransportService::nsHostEntry *, entry);
|
||||
|
||||
return !strcmp(he->hostport(), (const char *) key);
|
||||
}
|
||||
|
||||
void PR_CALLBACK
|
||||
nsSocketTransportService::ClearEntry(PLDHashTable *table,
|
||||
PLDHashEntryHdr *entry)
|
||||
{
|
||||
nsSocketTransportService::nsHostEntry *he =
|
||||
NS_REINTERPRET_CAST(nsSocketTransportService::nsHostEntry *, entry);
|
||||
|
||||
PL_strfree((char *) he->key);
|
||||
he->key = nsnull;
|
||||
memset(&he->addr, 0, sizeof(he->addr));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransportService::LookupHost(const nsACString &host, PRUint16 port, PRIPv6Addr *addr)
|
||||
{
|
||||
NS_ASSERTION(!host.IsEmpty(), "empty host");
|
||||
NS_ASSERTION(addr, "null addr");
|
||||
|
||||
PLDHashEntryHdr *hdr;
|
||||
nsCAutoString hostport(host + nsPrintfCString(":%d", port));
|
||||
|
||||
hdr = PL_DHashTableOperate(&mHostDB, hostport.get(), PL_DHASH_LOOKUP);
|
||||
if (PL_DHASH_ENTRY_IS_BUSY(hdr)) {
|
||||
// found match
|
||||
nsHostEntry *ent = NS_REINTERPRET_CAST(nsHostEntry *, hdr);
|
||||
memcpy(addr, &ent->addr, sizeof(ent->addr));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_UNKNOWN_HOST;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransportService::RememberHost(const nsACString &host, PRUint16 port, PRIPv6Addr *addr)
|
||||
{
|
||||
// remember hostname
|
||||
|
||||
PLDHashEntryHdr *hdr;
|
||||
nsCAutoString hostport(host + nsPrintfCString(":%d", port));
|
||||
|
||||
hdr = PL_DHashTableOperate(&mHostDB, hostport.get(), PL_DHASH_ADD);
|
||||
if (!hdr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(hdr), "entry not busy");
|
||||
|
||||
nsHostEntry *ent = NS_REINTERPRET_CAST(nsHostEntry *, hdr);
|
||||
if (ent->key == nsnull) {
|
||||
ent->key = (const void *) ToNewCString(hostport);
|
||||
memcpy(&ent->addr, addr, sizeof(ent->addr));
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else {
|
||||
// verify that the existing entry is in fact a perfect match
|
||||
NS_ASSERTION(PL_strcmp(ent->hostport(), hostport.get()) == 0, "bad match");
|
||||
NS_ASSERTION(memcmp(&ent->addr, addr, sizeof(ent->addr)) == 0, "bad match");
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// xpcom api
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsSocketTransportService,
|
||||
nsISocketTransportService,
|
||||
nsIRunnable)
|
||||
|
||||
// called from main thread only
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::Init()
|
||||
{
|
||||
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||||
|
||||
if (mInitialized)
|
||||
return NS_OK;
|
||||
|
||||
if (!mThreadEvent)
|
||||
mThreadEvent = PR_NewPollableEvent();
|
||||
|
||||
nsresult rv = NS_NewThread(&mThread, this, 0, PR_JOINABLE_THREAD);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mInitialized = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// called from main thread only
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::Shutdown()
|
||||
{
|
||||
LOG(("nsSocketTransportService::Shutdown\n"));
|
||||
|
||||
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||||
|
||||
if (!mInitialized)
|
||||
return NS_OK;
|
||||
|
||||
{
|
||||
nsAutoLock lock(mEventQLock);
|
||||
|
||||
// signal uninitialized to block further events
|
||||
mInitialized = PR_FALSE;
|
||||
|
||||
PR_SetPollableEvent(mThreadEvent);
|
||||
}
|
||||
|
||||
// join with thread
|
||||
mThread->Join();
|
||||
NS_RELEASE(mThread);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::CreateTransport(const char **types,
|
||||
PRUint32 typeCount,
|
||||
const nsACString &host,
|
||||
PRInt32 port,
|
||||
nsIProxyInfo *proxyInfo,
|
||||
nsISocketTransport **result)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
|
||||
NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
nsSocketTransport *trans = new nsSocketTransport();
|
||||
if (!trans)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(trans);
|
||||
|
||||
nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(trans);
|
||||
return rv;
|
||||
}
|
||||
|
||||
*result = trans;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::GetAutodialEnabled(PRBool *value)
|
||||
{
|
||||
*value = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::SetAutodialEnabled(PRBool value)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::Run()
|
||||
{
|
||||
LOG(("nsSocketTransportService::Run"));
|
||||
|
||||
gSocketThread = PR_GetCurrentThread();
|
||||
|
||||
//
|
||||
// Initialize hostname database
|
||||
//
|
||||
PL_DHashTableInit(&mHostDB, &ops, nsnull, sizeof(nsHostEntry), 0);
|
||||
|
||||
//
|
||||
// setup thread
|
||||
//
|
||||
NS_ASSERTION(mThreadEvent, "no thread event");
|
||||
|
||||
mPollList[0].fd = mThreadEvent;
|
||||
mPollList[0].in_flags = PR_POLL_READ;
|
||||
|
||||
PRInt32 i, count;
|
||||
|
||||
//
|
||||
// poll loop
|
||||
//
|
||||
PRBool active = PR_TRUE;
|
||||
while (active) {
|
||||
// clear out flags for pollable event
|
||||
mPollList[0].out_flags = 0;
|
||||
|
||||
//
|
||||
// walk active list backwards to see if any sockets should actually be
|
||||
// idle, then walk the idle list backwards to see if any idle sockets
|
||||
// should become active. take care to only check idle sockets that
|
||||
// were idle to begin with ;-)
|
||||
//
|
||||
count = mIdleCount;
|
||||
if (mActiveCount) {
|
||||
for (i=mActiveCount; i>=1; --i) {
|
||||
//---
|
||||
LOG((" active [%d] { handler=%x condition=%x pollflags=%hu }\n", i,
|
||||
mActiveList[i].mHandler,
|
||||
mActiveList[i].mHandler->mCondition,
|
||||
mActiveList[i].mHandler->mPollFlags));
|
||||
//---
|
||||
if (NS_FAILED(mActiveList[i].mHandler->mCondition))
|
||||
DetachSocket_Internal(&mActiveList[i]);
|
||||
else {
|
||||
PRUint16 in_flags = mActiveList[i].mHandler->mPollFlags;
|
||||
if (in_flags == 0)
|
||||
MoveToIdleList(&mActiveList[i]);
|
||||
else {
|
||||
// update poll flags
|
||||
mPollList[i].in_flags = in_flags;
|
||||
mPollList[i].out_flags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count) {
|
||||
for (i=count-1; i>=0; --i) {
|
||||
//---
|
||||
LOG((" idle [%d] { handler=%x condition=%x pollflags=%hu }\n", i,
|
||||
mIdleList[i].mHandler,
|
||||
mIdleList[i].mHandler->mCondition,
|
||||
mIdleList[i].mHandler->mPollFlags));
|
||||
//---
|
||||
if (NS_FAILED(mIdleList[i].mHandler->mCondition))
|
||||
DetachSocket_Internal(&mIdleList[i]);
|
||||
else if (mIdleList[i].mHandler->mPollFlags != 0)
|
||||
MoveToPollList(&mIdleList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
|
||||
|
||||
PRInt32 n = PR_Poll(mPollList, PollCount(), NS_SOCKET_POLL_TIMEOUT);
|
||||
if (n < 0) {
|
||||
LOG((" PR_Poll error [%d]\n", PR_GetError()));
|
||||
active = PR_FALSE;
|
||||
}
|
||||
else if (n == 0) {
|
||||
LOG((" PR_Poll timeout expired\n"));
|
||||
// loop around again
|
||||
}
|
||||
else {
|
||||
count = PollCount();
|
||||
|
||||
//
|
||||
// service "active" sockets...
|
||||
//
|
||||
for (i=1; i<count; ++i) {
|
||||
if (mPollList[i].out_flags != 0) {
|
||||
nsASocketHandler *handler = mActiveList[i].mHandler;
|
||||
handler->OnSocketReady(mPollList[i].fd,
|
||||
mPollList[i].out_flags);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// check for "dead" sockets and remove them (need to do this in
|
||||
// reverse order obviously).
|
||||
//
|
||||
for (i=count-1; i>=1; --i) {
|
||||
if (NS_FAILED(mActiveList[i].mHandler->mCondition))
|
||||
DetachSocket_Internal(&mActiveList[i]);
|
||||
}
|
||||
|
||||
//
|
||||
// service the event queue
|
||||
//
|
||||
if (mPollList[0].out_flags == PR_POLL_READ) {
|
||||
|
||||
// grab the event queue
|
||||
SocketEvent *head = nsnull, *event;
|
||||
{
|
||||
nsAutoLock lock(mEventQLock);
|
||||
|
||||
// wait should not block
|
||||
PR_WaitForPollableEvent(mPollList[0].fd);
|
||||
|
||||
head = mEventQ.mHead;
|
||||
mEventQ.mHead = nsnull;
|
||||
mEventQ.mTail = nsnull;
|
||||
|
||||
// check to see if we're supposed to shutdown
|
||||
active = mInitialized;
|
||||
}
|
||||
// service the event queue
|
||||
mServicingEventQ = PR_TRUE;
|
||||
while (head) {
|
||||
head->mHandler->OnSocketEvent(head->mType,
|
||||
head->mUparam,
|
||||
head->mVparam);
|
||||
// delete head of queue
|
||||
event = head->mNext;
|
||||
delete head;
|
||||
head = event;
|
||||
}
|
||||
mServicingEventQ = PR_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// shutdown thread
|
||||
//
|
||||
|
||||
LOG(("shutting down socket transport thread...\n"));
|
||||
|
||||
// detach any sockets
|
||||
for (i=mActiveCount; i>=1; --i)
|
||||
DetachSocket_Internal(&mActiveList[i]);
|
||||
for (i=mIdleCount-1; i>=0; --i)
|
||||
DetachSocket_Internal(&mIdleList[i]);
|
||||
|
||||
// clear the hostname database
|
||||
PL_DHashTableFinish(&mHostDB);
|
||||
|
||||
gSocketThread = nsnull;
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsSocketTransportService2_h__
|
||||
#define nsSocketTransportService2_h__
|
||||
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThread.h"
|
||||
#include "pldhash.h"
|
||||
#include "prinrval.h"
|
||||
#include "prlog.h"
|
||||
#include "prio.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// set NSPR_LOG_MODULES=nsSocketTransport:5
|
||||
//
|
||||
extern PRLogModuleInfo *gSocketTransportLog;
|
||||
#define LOG(args) PR_LOG(gSocketTransportLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define NS_SOCKET_MAX_COUNT 50
|
||||
#define NS_SOCKET_POLL_TIMEOUT PR_INTERVAL_NO_TIMEOUT
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// socket handler: methods are only called on the socket thread.
|
||||
|
||||
class nsASocketHandler : public nsISupports
|
||||
{
|
||||
public:
|
||||
nsASocketHandler() : mCondition(NS_OK), mPollFlags(0) {}
|
||||
|
||||
//
|
||||
// this condition variable will be checked to determine if the socket
|
||||
// handler should be detached. it must only be accessed on the socket
|
||||
// thread.
|
||||
//
|
||||
nsresult mCondition;
|
||||
|
||||
//
|
||||
// these flags can only be modified on the socket transport thread.
|
||||
// the socket transport service will check these flags before calling
|
||||
// PR_Poll.
|
||||
//
|
||||
PRUint16 mPollFlags;
|
||||
|
||||
//
|
||||
// called to service a socket
|
||||
//
|
||||
// params:
|
||||
// socketRef - socket identifier
|
||||
// fd - socket file descriptor
|
||||
// pollFlags - poll "in-flags"
|
||||
//
|
||||
virtual void OnSocketReady(PRFileDesc *fd,
|
||||
PRInt16 pollFlags) = 0;
|
||||
|
||||
//
|
||||
// called when a socket is no longer under the control of the socket
|
||||
// transport service. the socket handler may close the socket at this
|
||||
// point. after this call returns, the handler will no longer be owned
|
||||
// by the socket transport service.
|
||||
//
|
||||
virtual void OnSocketDetached(PRFileDesc *fd) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsSocketTransportService : public nsISocketTransportService
|
||||
, public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISOCKETTRANSPORTSERVICE
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
nsSocketTransportService();
|
||||
|
||||
//
|
||||
// add a new socket to the list of controlled sockets. returns a socket
|
||||
// reference for the newly attached socket that can be used with other
|
||||
// methods to control the socket.
|
||||
//
|
||||
// NOTE: this function may only be called from an event dispatch on the
|
||||
// socket thread.
|
||||
//
|
||||
nsresult AttachSocket(PRFileDesc *fd, nsASocketHandler *);
|
||||
|
||||
//
|
||||
// LookupHost checks to see if we've previously resolved the hostname
|
||||
// during this session. We remember all successful connections to prevent
|
||||
// ip-address spoofing. See bug 149943.
|
||||
//
|
||||
// Returns TRUE if found, and sets |addr| to the cached value.
|
||||
//
|
||||
nsresult LookupHost(const nsACString &host, PRUint16 port, PRIPv6Addr *addr);
|
||||
|
||||
//
|
||||
// Remember host:port -> IP address mapping.
|
||||
//
|
||||
nsresult RememberHost(const nsACString &host, PRUint16 port, PRIPv6Addr *addr);
|
||||
|
||||
private:
|
||||
|
||||
virtual ~nsSocketTransportService();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// misc (any thread)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
PRBool mInitialized;
|
||||
nsIThread *mThread;
|
||||
PRFileDesc *mThreadEvent;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// event queue (any thread)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct SocketEvent
|
||||
{
|
||||
SocketEvent(nsISocketEventHandler *handler,
|
||||
PRUint32 type, PRUint32 uparam, void *vparam)
|
||||
: mType(type)
|
||||
, mUparam(uparam)
|
||||
, mVparam(vparam)
|
||||
, mNext(nsnull)
|
||||
{ NS_ADDREF(mHandler = handler); }
|
||||
|
||||
~SocketEvent() { NS_RELEASE(mHandler); }
|
||||
|
||||
nsISocketEventHandler *mHandler;
|
||||
PRUint32 mType;
|
||||
PRUint32 mUparam;
|
||||
void *mVparam;
|
||||
|
||||
struct SocketEvent *mNext;
|
||||
};
|
||||
|
||||
struct SocketEventQ
|
||||
{
|
||||
SocketEventQ() : mHead(nsnull), mTail(nsnull) {}
|
||||
|
||||
SocketEvent *mHead;
|
||||
SocketEvent *mTail;
|
||||
};
|
||||
|
||||
SocketEventQ mEventQ;
|
||||
PRLock *mEventQLock;
|
||||
PRBool mServicingEventQ;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// socket lists (socket thread only)
|
||||
//
|
||||
// only "active" sockets are on the poll list. the active list is kept
|
||||
// in sync with the poll list such that the index of a socket on the poll
|
||||
// list matches its index on the active socket list. as a result the first
|
||||
// element of the active socket list is always null.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct SocketContext
|
||||
{
|
||||
PRFileDesc *mFD;
|
||||
nsASocketHandler *mHandler;
|
||||
};
|
||||
|
||||
SocketContext mActiveList [ NS_SOCKET_MAX_COUNT + 1 ];
|
||||
SocketContext mIdleList [ NS_SOCKET_MAX_COUNT ];
|
||||
|
||||
PRUint32 mActiveCount;
|
||||
PRUint32 mIdleCount;
|
||||
|
||||
nsresult DetachSocket_Internal(SocketContext *);
|
||||
nsresult AddToIdleList(SocketContext *);
|
||||
nsresult AddToPollList(SocketContext *);
|
||||
void RemoveFromIdleList(SocketContext *);
|
||||
void RemoveFromPollList(SocketContext *);
|
||||
|
||||
nsresult MoveToIdleList(SocketContext *sock)
|
||||
{
|
||||
RemoveFromPollList(sock);
|
||||
return AddToIdleList(sock);
|
||||
}
|
||||
nsresult MoveToPollList(SocketContext *sock)
|
||||
{
|
||||
RemoveFromIdleList(sock);
|
||||
return AddToPollList(sock);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// poll list (socket thread only)
|
||||
//
|
||||
// first element of the poll list is the "NSPR pollable event"
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
PRPollDesc mPollList[ NS_SOCKET_MAX_COUNT + 1 ];
|
||||
|
||||
PRUint32 PollCount() { return mActiveCount + 1; }
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// mHostDB maps host:port -> nsHostEntry
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct nsHostEntry : PLDHashEntryStub
|
||||
{
|
||||
PRIPv6Addr addr;
|
||||
const char *hostport() const { return (const char *) key; }
|
||||
};
|
||||
|
||||
static PLDHashTableOps ops;
|
||||
|
||||
static PRBool PR_CALLBACK MatchEntry(PLDHashTable *, const PLDHashEntryHdr *, const void *);
|
||||
static void PR_CALLBACK ClearEntry(PLDHashTable *, PLDHashEntryHdr *);
|
||||
|
||||
PLDHashTable mHostDB;
|
||||
};
|
||||
|
||||
extern nsSocketTransportService *gSocketTransportService;
|
||||
extern PRThread *gSocketThread;
|
||||
|
||||
#endif // !nsSocketTransportService_h__
|
|
@ -0,0 +1,651 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsStreamTransportService.h"
|
||||
#include "nsNetSegmentUtils.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "netCore.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIProxyObjectManager.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// set NSPR_LOG_MODULES=nsStreamTransport:5
|
||||
//
|
||||
static PRLogModuleInfo *gSTSLog;
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(gSTSLog, PR_LOG_DEBUG, args)
|
||||
|
||||
#define MIN_THREADS 1
|
||||
#define MAX_THREADS 4
|
||||
#define DEFAULT_SEGMENT_SIZE 4096
|
||||
#define DEFAULT_SEGMENT_COUNT 16
|
||||
|
||||
static nsStreamTransportService *gSTS = nsnull;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline static nsresult
|
||||
NewEventSinkProxy(nsITransportEventSink *sink, nsIEventQueue *eventQ,
|
||||
nsITransportEventSink **result)
|
||||
{
|
||||
return NS_GetProxyForObject(eventQ,
|
||||
NS_GET_IID(nsITransportEventSink),
|
||||
sink,
|
||||
PROXY_ASYNC | PROXY_ALWAYS,
|
||||
(void **) result);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsInputStreamTransport
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsInputStreamTransport : public nsIRunnable
|
||||
, public nsITransport
|
||||
, public nsIOutputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSITRANSPORT
|
||||
NS_DECL_NSIOUTPUTSTREAMNOTIFY
|
||||
|
||||
nsInputStreamTransport(nsIInputStream *source,
|
||||
PRUint32 offset,
|
||||
PRUint32 limit,
|
||||
PRBool closeWhenDone)
|
||||
: mSource(source)
|
||||
, mSourceCondition(NS_OK)
|
||||
, mOffset(offset)
|
||||
, mLimit(limit)
|
||||
, mSegSize(0)
|
||||
, mInProgress(PR_FALSE)
|
||||
, mCloseWhenDone(closeWhenDone)
|
||||
, mFirstTime(PR_TRUE)
|
||||
{
|
||||
NS_ADDREF(gSTS);
|
||||
}
|
||||
|
||||
virtual ~nsInputStreamTransport()
|
||||
{
|
||||
nsStreamTransportService *serv = gSTS;
|
||||
NS_RELEASE(serv);
|
||||
}
|
||||
|
||||
static NS_METHOD FillPipeSegment(nsIOutputStream *, void *, char *,
|
||||
PRUint32, PRUint32, PRUint32 *);
|
||||
|
||||
private:
|
||||
// the pipe input end is never accessed from Run
|
||||
nsCOMPtr<nsIAsyncInputStream> mPipeIn;
|
||||
|
||||
nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
|
||||
nsCOMPtr<nsITransportEventSink> mEventSink;
|
||||
nsCOMPtr<nsIInputStream> mSource;
|
||||
nsresult mSourceCondition;
|
||||
PRUint32 mOffset;
|
||||
PRUint32 mLimit;
|
||||
PRUint32 mSegSize;
|
||||
PRPackedBool mInProgress;
|
||||
PRPackedBool mCloseWhenDone;
|
||||
PRPackedBool mFirstTime;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsInputStreamTransport,
|
||||
nsIRunnable,
|
||||
nsITransport,
|
||||
nsIOutputStreamNotify)
|
||||
|
||||
NS_METHOD
|
||||
nsInputStreamTransport::FillPipeSegment(nsIOutputStream *stream,
|
||||
void *closure,
|
||||
char *segment,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countRead)
|
||||
{
|
||||
nsInputStreamTransport *trans = (nsInputStreamTransport *) closure;
|
||||
|
||||
// apply read limit
|
||||
PRUint32 limit = trans->mLimit - trans->mOffset;
|
||||
if (count > limit) {
|
||||
count = limit;
|
||||
if (count == 0) {
|
||||
*countRead = 0;
|
||||
return trans->mSourceCondition = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = trans->mSource->Read(segment, count, countRead);
|
||||
if (NS_FAILED(rv))
|
||||
trans->mSourceCondition = rv;
|
||||
else if (*countRead == 0)
|
||||
trans->mSourceCondition = NS_BASE_STREAM_CLOSED;
|
||||
else {
|
||||
trans->mOffset += *countRead;
|
||||
if (trans->mEventSink)
|
||||
trans->mEventSink->OnTransportStatus(trans,
|
||||
nsITransport::STATUS_READING,
|
||||
trans->mOffset, trans->mLimit);
|
||||
}
|
||||
|
||||
return trans->mSourceCondition;
|
||||
}
|
||||
|
||||
/** nsIRunnable **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamTransport::Run()
|
||||
{
|
||||
LOG(("nsInputStreamTransport::Run\n"));
|
||||
|
||||
nsresult rv;
|
||||
PRUint32 n;
|
||||
|
||||
if (mFirstTime) {
|
||||
mFirstTime = PR_FALSE;
|
||||
|
||||
if (mOffset != ~0U) {
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
|
||||
if (seekable)
|
||||
seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
|
||||
}
|
||||
|
||||
// mOffset now holds the number of bytes read, which we will use to
|
||||
// enforce mLimit.
|
||||
mOffset = 0;
|
||||
}
|
||||
|
||||
// do as much work as we can before yielding this thread.
|
||||
for (;;) {
|
||||
rv = mPipeOut->WriteSegments(FillPipeSegment, this, mSegSize, &n);
|
||||
|
||||
LOG(("nsInputStreamTransport: WriteSegments returned [this=%x rv=%x n=%u]\n",
|
||||
this, rv, n));
|
||||
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
||||
// wait for pipe to become writable
|
||||
mPipeOut->AsyncWait(this, 0, nsnull);
|
||||
break;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (n == 0)
|
||||
rv = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
else if (NS_FAILED(mSourceCondition))
|
||||
rv = mSourceCondition;
|
||||
|
||||
// check for source error/eof
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsInputStreamTransport: got write error [this=%x error=%x]\n",
|
||||
this, rv));
|
||||
// close mPipeOut, propogating error condition...
|
||||
mPipeOut->CloseEx(rv);
|
||||
mPipeOut = 0;
|
||||
if (mCloseWhenDone)
|
||||
mSource->Close();
|
||||
mSource = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsIOutputStreamNotify **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamTransport::OnOutputStreamReady(nsIAsyncOutputStream *stream)
|
||||
{
|
||||
LOG(("nsInputStreamTransport::OnOutputStreamReady\n"));
|
||||
|
||||
// called on whatever thread removed data from the pipe or closed it.
|
||||
NS_ASSERTION(mPipeOut == stream, "wrong stream");
|
||||
nsresult rv = gSTS->Dispatch(this);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsITransport **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamTransport::OpenInputStream(PRUint32 flags,
|
||||
PRUint32 segsize,
|
||||
PRUint32 segcount,
|
||||
nsIInputStream **result)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
|
||||
|
||||
// XXX if the caller requests an unbuffered stream, then perhaps
|
||||
// we'd want to simply return mSource; however, then we would
|
||||
// not be reading mSource on a background thread. is this ok?
|
||||
|
||||
PRBool nonblocking = !(flags & OPEN_BLOCKING);
|
||||
|
||||
net_ResolveSegmentParams(segsize, segcount);
|
||||
nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
|
||||
|
||||
nsresult rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
|
||||
getter_AddRefs(mPipeOut),
|
||||
nonblocking, PR_TRUE,
|
||||
segsize, segcount, segalloc);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mSegSize = segsize;
|
||||
mInProgress = PR_TRUE;
|
||||
|
||||
rv = gSTS->Dispatch(this); // now writing to pipe
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_ADDREF(*result = mPipeIn);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamTransport::OpenOutputStream(PRUint32 flags,
|
||||
PRUint32 segsize,
|
||||
PRUint32 segcount,
|
||||
nsIOutputStream **result)
|
||||
{
|
||||
// this transport only supports reading!
|
||||
NS_NOTREACHED("nsInputStreamTransport::OpenOutputStream");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamTransport::Close(nsresult reason)
|
||||
{
|
||||
if (NS_SUCCEEDED(reason))
|
||||
reason = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
return mPipeIn->CloseEx(reason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsInputStreamTransport::SetEventSink(nsITransportEventSink *sink,
|
||||
nsIEventQueue *eventQ)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
|
||||
|
||||
if (eventQ)
|
||||
return NewEventSinkProxy(sink, eventQ, getter_AddRefs(mEventSink));
|
||||
|
||||
mEventSink = sink;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOutputStreamTransport
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsOutputStreamTransport : public nsIRunnable
|
||||
, public nsITransport
|
||||
, public nsIInputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSITRANSPORT
|
||||
NS_DECL_NSIINPUTSTREAMNOTIFY
|
||||
|
||||
nsOutputStreamTransport(nsIOutputStream *sink,
|
||||
PRUint32 offset,
|
||||
PRUint32 limit,
|
||||
PRBool closeWhenDone)
|
||||
: mSink(sink)
|
||||
, mSinkCondition(NS_OK)
|
||||
, mOffset(offset)
|
||||
, mLimit(limit)
|
||||
, mSegSize(0)
|
||||
, mInProgress(PR_FALSE)
|
||||
, mCloseWhenDone(closeWhenDone)
|
||||
, mFirstTime(PR_TRUE)
|
||||
{
|
||||
NS_ADDREF(gSTS);
|
||||
}
|
||||
|
||||
virtual ~nsOutputStreamTransport()
|
||||
{
|
||||
nsStreamTransportService *serv = gSTS;
|
||||
NS_RELEASE(serv);
|
||||
}
|
||||
|
||||
static NS_METHOD ConsumePipeSegment(nsIInputStream *, void *, const char *,
|
||||
PRUint32, PRUint32, PRUint32 *);
|
||||
|
||||
private:
|
||||
// the pipe output end is never accessed from Run
|
||||
nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> mPipeIn;
|
||||
nsCOMPtr<nsITransportEventSink> mEventSink;
|
||||
nsCOMPtr<nsIOutputStream> mSink;
|
||||
nsresult mSinkCondition;
|
||||
PRUint32 mOffset;
|
||||
PRUint32 mLimit;
|
||||
PRUint32 mSegSize;
|
||||
PRPackedBool mInProgress;
|
||||
PRPackedBool mCloseWhenDone;
|
||||
PRPackedBool mFirstTime;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsOutputStreamTransport,
|
||||
nsIRunnable,
|
||||
nsITransport,
|
||||
nsIInputStreamNotify)
|
||||
|
||||
NS_METHOD
|
||||
nsOutputStreamTransport::ConsumePipeSegment(nsIInputStream *stream,
|
||||
void *closure,
|
||||
const char *segment,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countWritten)
|
||||
{
|
||||
nsOutputStreamTransport *trans = (nsOutputStreamTransport *) closure;
|
||||
|
||||
// apply write limit
|
||||
PRUint32 limit = trans->mLimit - trans->mOffset;
|
||||
if (count > limit) {
|
||||
count = limit;
|
||||
if (count == 0) {
|
||||
*countWritten = 0;
|
||||
return trans->mSinkCondition = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = trans->mSink->Write(segment, count, countWritten);
|
||||
if (NS_FAILED(rv))
|
||||
trans->mSinkCondition = rv;
|
||||
else if (*countWritten == 0)
|
||||
trans->mSinkCondition = NS_BASE_STREAM_CLOSED;
|
||||
else {
|
||||
trans->mOffset += *countWritten;
|
||||
if (trans->mEventSink)
|
||||
trans->mEventSink->OnTransportStatus(trans,
|
||||
nsITransport::STATUS_WRITING,
|
||||
trans->mOffset, trans->mLimit);
|
||||
}
|
||||
|
||||
return trans->mSinkCondition;
|
||||
}
|
||||
|
||||
/** nsIRunnable **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOutputStreamTransport::Run()
|
||||
{
|
||||
LOG(("nsOutputStreamTransport::Run\n"));
|
||||
|
||||
nsresult rv;
|
||||
PRUint32 n;
|
||||
|
||||
if (mFirstTime) {
|
||||
mFirstTime = PR_FALSE;
|
||||
|
||||
if (mOffset != ~0U) {
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
|
||||
if (seekable)
|
||||
seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
|
||||
}
|
||||
|
||||
// mOffset now holds the number of bytes written, which we will use to
|
||||
// enforce mLimit.
|
||||
mOffset = 0;
|
||||
}
|
||||
|
||||
// do as much work as we can before yielding this thread.
|
||||
for (;;) {
|
||||
rv = mPipeIn->ReadSegments(ConsumePipeSegment, this, mSegSize, &n);
|
||||
|
||||
LOG(("nsOutputStreamTransport: ReadSegments returned [this=%x rv=%x n=%u]\n",
|
||||
this, rv, n));
|
||||
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
||||
// wait for pipe to become readable
|
||||
mPipeIn->AsyncWait(this, 0, nsnull);
|
||||
break;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (n == 0)
|
||||
rv = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
else if (NS_FAILED(mSinkCondition))
|
||||
rv = mSinkCondition;
|
||||
|
||||
// check for sink error
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsOutputStreamTransport: got read error [this=%x error=%x]\n",
|
||||
this, rv));
|
||||
// close mPipeIn, propogating error condition...
|
||||
mPipeIn->CloseEx(rv);
|
||||
mPipeIn = 0;
|
||||
if (mCloseWhenDone)
|
||||
mSink->Close();
|
||||
mSink = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsIInputStreamNotify **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOutputStreamTransport::OnInputStreamReady(nsIAsyncInputStream *stream)
|
||||
{
|
||||
LOG(("nsOutputStreamTransport::OnInputStreamReady\n"));
|
||||
|
||||
// called on whatever thread added data to the pipe or closed it.
|
||||
NS_ASSERTION(mPipeIn == stream, "wrong stream");
|
||||
nsresult rv = gSTS->Dispatch(this);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsITransport **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOutputStreamTransport::OpenInputStream(PRUint32 flags,
|
||||
PRUint32 segsize,
|
||||
PRUint32 segcount,
|
||||
nsIInputStream **result)
|
||||
{
|
||||
// this transport only supports writing!
|
||||
NS_NOTREACHED("nsOutputStreamTransport::OpenInputStream");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOutputStreamTransport::OpenOutputStream(PRUint32 flags,
|
||||
PRUint32 segsize,
|
||||
PRUint32 segcount,
|
||||
nsIOutputStream **result)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
|
||||
|
||||
// XXX if the caller requests an unbuffered stream, then perhaps
|
||||
// we'd want to simply return mSink; however, then we would
|
||||
// not be writing to mSink on a background thread. is this ok?
|
||||
|
||||
PRBool nonblocking = !(flags & OPEN_BLOCKING);
|
||||
|
||||
net_ResolveSegmentParams(segsize, segcount);
|
||||
nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
|
||||
|
||||
nsresult rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
|
||||
getter_AddRefs(mPipeOut),
|
||||
PR_TRUE, nonblocking,
|
||||
segsize, segcount, segalloc);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mSegSize = segsize;
|
||||
mInProgress = PR_TRUE;
|
||||
|
||||
rv = gSTS->Dispatch(this); // now reading from pipe
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_ADDREF(*result = mPipeOut);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOutputStreamTransport::Close(nsresult reason)
|
||||
{
|
||||
if (NS_SUCCEEDED(reason))
|
||||
reason = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
return mPipeOut->CloseEx(reason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOutputStreamTransport::SetEventSink(nsITransportEventSink *sink,
|
||||
nsIEventQueue *eventQ)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
|
||||
|
||||
if (eventQ)
|
||||
return NewEventSinkProxy(sink, eventQ, getter_AddRefs(mEventSink));
|
||||
|
||||
mEventSink = sink;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsStreamTransportService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsStreamTransportService::nsStreamTransportService()
|
||||
: mLock(PR_NewLock())
|
||||
{
|
||||
gSTS = this;
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
gSTSLog = PR_NewLogModule("nsStreamTransport");
|
||||
#endif
|
||||
}
|
||||
|
||||
nsStreamTransportService::~nsStreamTransportService()
|
||||
{
|
||||
gSTS = 0;
|
||||
|
||||
if (mLock)
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStreamTransportService::Init()
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
LOG(("nsStreamTransportService::Init\n"));
|
||||
|
||||
return NS_NewThreadPool(getter_AddRefs(mPool),
|
||||
MIN_THREADS, MAX_THREADS, 0);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStreamTransportService::Shutdown()
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
LOG(("nsStreamTransportService::Shutdown\n"));
|
||||
|
||||
if (mPool) {
|
||||
mPool->Shutdown();
|
||||
mPool = 0;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsStreamTransportService::Dispatch(nsIRunnable *runnable)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
if (!mPool)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
return mPool->DispatchRequest(runnable);
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsStreamTransportService, nsIStreamTransportService)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStreamTransportService::CreateInputTransport(nsIInputStream *stream,
|
||||
PRInt32 offset,
|
||||
PRInt32 limit,
|
||||
PRBool closeWhenDone,
|
||||
nsITransport **result)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsInputStreamTransport *trans =
|
||||
new nsInputStreamTransport(stream, offset, limit, closeWhenDone);
|
||||
if (!trans)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(*result = trans);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream,
|
||||
PRInt32 offset,
|
||||
PRInt32 limit,
|
||||
PRBool closeWhenDone,
|
||||
nsITransport **result)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsOutputStreamTransport *trans =
|
||||
new nsOutputStreamTransport(stream, offset, limit, closeWhenDone);
|
||||
if (!trans)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(*result = trans);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "prlock.h"
|
||||
|
||||
class nsStreamTransportService : public nsIStreamTransportService
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMTRANSPORTSERVICE
|
||||
|
||||
nsStreamTransportService();
|
||||
virtual ~nsStreamTransportService();
|
||||
|
||||
/** used internally **/
|
||||
nsresult Dispatch(nsIRunnable *);
|
||||
|
||||
private:
|
||||
nsresult Dispatch_Locked(nsIRunnable *);
|
||||
|
||||
nsCOMPtr<nsIThreadPool> mPool;
|
||||
PRLock *mLock;
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the NPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the NPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIChannel.idl"
|
||||
|
||||
interface nsIFile;
|
||||
|
||||
/**
|
||||
* nsIFileChannel
|
||||
*/
|
||||
[scriptable, uuid(68a26506-f947-11d3-8cda-0060b0fc14a3)]
|
||||
interface nsIFileChannel : nsIChannel
|
||||
{
|
||||
readonly attribute nsIFile file;
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
#include "nsHttpConnectionInfo.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
void
|
||||
nsHttpConnectionInfo::SetOriginServer(const nsACString &host, PRInt32 port)
|
||||
{
|
||||
mHost = host;
|
||||
mPort = port == -1 ? DefaultPort() : port;
|
||||
|
||||
//
|
||||
// build hash key:
|
||||
//
|
||||
// the hash key uniquely identifies the connection type. two connections
|
||||
// are "equal" if they end up talking the same protocol to the same server.
|
||||
//
|
||||
|
||||
const char *keyHost;
|
||||
PRInt32 keyPort;
|
||||
|
||||
if (mUsingHttpProxy && !mUsingSSL) {
|
||||
keyHost = ProxyHost();
|
||||
keyPort = ProxyPort();
|
||||
}
|
||||
else {
|
||||
keyHost = Host();
|
||||
keyPort = Port();
|
||||
}
|
||||
|
||||
mHashKey.Assign(NS_LITERAL_CSTRING("..") +
|
||||
nsDependentCString(keyHost) +
|
||||
nsPrintfCString(":%d", keyPort));
|
||||
|
||||
if (mUsingHttpProxy)
|
||||
mHashKey.SetCharAt(0, 'P');
|
||||
if (mUsingSSL)
|
||||
mHashKey.SetCharAt(1, 'S');
|
||||
|
||||
// NOTE: for transparent proxies (e.g., SOCKS) we need to encode the proxy
|
||||
// type in the hash key (this ensures that we will continue to speak the
|
||||
// right protocol even if our proxy preferences change).
|
||||
if (!mUsingHttpProxy && mProxyInfo)
|
||||
mHashKey.Append(NS_LITERAL_CSTRING(" (") +
|
||||
nsDependentCString(ProxyType()) +
|
||||
NS_LITERAL_CSTRING(")"));
|
||||
}
|
|
@ -0,0 +1,770 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsHttpConnectionMgr.h"
|
||||
#include "nsHttpConnection.h"
|
||||
#include "nsHttpPipeline.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "nsIServiceManager.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
// defined by the socket transport service while active
|
||||
extern PRThread *gSocketThread;
|
||||
#endif
|
||||
|
||||
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsHttpConnectionMgr::nsHttpConnectionMgr()
|
||||
: mMonitor(nsAutoMonitor::NewMonitor("nsHttpConnectionMgr"))
|
||||
, mMaxConns(0)
|
||||
, mMaxConnsPerHost(0)
|
||||
, mMaxConnsPerProxy(0)
|
||||
, mMaxPersistConnsPerHost(0)
|
||||
, mMaxPersistConnsPerProxy(0)
|
||||
, mNumActiveConns(0)
|
||||
, mNumIdleConns(0)
|
||||
{
|
||||
LOG(("Creating nsHttpConnectionMgr @%x\n", this));
|
||||
}
|
||||
|
||||
nsHttpConnectionMgr::~nsHttpConnectionMgr()
|
||||
{
|
||||
LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
|
||||
|
||||
if (mMonitor)
|
||||
nsAutoMonitor::DestroyMonitor(mMonitor);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::Init(PRUint16 maxConns,
|
||||
PRUint16 maxConnsPerHost,
|
||||
PRUint16 maxConnsPerProxy,
|
||||
PRUint16 maxPersistConnsPerHost,
|
||||
PRUint16 maxPersistConnsPerProxy,
|
||||
PRUint16 maxRequestDelay,
|
||||
PRUint16 maxPipelinedRequests)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::Init\n"));
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
// do nothing if already initialized
|
||||
if (mSTS)
|
||||
return NS_OK;
|
||||
|
||||
// no need to do any special synchronization here since there cannot be
|
||||
// any activity on the socket thread (because Shutdown is synchronous).
|
||||
mMaxConns = maxConns;
|
||||
mMaxConnsPerHost = maxConnsPerHost;
|
||||
mMaxConnsPerProxy = maxConnsPerProxy;
|
||||
mMaxPersistConnsPerHost = maxPersistConnsPerHost;
|
||||
mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
|
||||
mMaxRequestDelay = maxRequestDelay;
|
||||
mMaxPipelinedRequests = maxPipelinedRequests;
|
||||
|
||||
nsresult rv;
|
||||
mSTS = do_GetService(kSocketTransportServiceCID, &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::Shutdown()
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::Shutdown\n"));
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
// do nothing if already shutdown
|
||||
if (!mSTS)
|
||||
return NS_OK;
|
||||
|
||||
nsresult rv = mSTS->PostEvent(this, MSG_SHUTDOWN, 0, nsnull);
|
||||
|
||||
// release our reference to the STS to prevent further events
|
||||
// from being posted. this is how we indicate that we are
|
||||
// shutting down.
|
||||
mSTS = 0;
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("unable to post SHUTDOWN message\n");
|
||||
return rv;
|
||||
}
|
||||
|
||||
// wait for shutdown event to complete
|
||||
mon.Wait();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::PostEvent(PRUint32 type, PRUint32 uparam, void *vparam)
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
NS_ENSURE_TRUE(mSTS, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
return mSTS->PostEvent(this, type, uparam, vparam);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x]\n", trans));
|
||||
|
||||
NS_ADDREF(trans);
|
||||
|
||||
nsresult rv = PostEvent(MSG_NEW_TRANSACTION, 0, trans);
|
||||
if (NS_FAILED(rv))
|
||||
NS_RELEASE(trans);
|
||||
|
||||
// if successful, then OnSocketEvent will be called
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
|
||||
|
||||
NS_ADDREF(trans);
|
||||
|
||||
nsresult rv = PostEvent(MSG_CANCEL_TRANSACTION, reason, trans);
|
||||
if (NS_FAILED(rv))
|
||||
NS_RELEASE(trans);
|
||||
|
||||
// if successful, then OnSocketEvent will be called
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::PruneDeadConnections()
|
||||
{
|
||||
return PostEvent(MSG_PRUNE_DEAD_CONNECTIONS, 0, nsnull);
|
||||
// if successful, then OnSocketEvent will be called
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::GetSTS(nsISocketTransportService **sts)
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
NS_IF_ADDREF(*sts = mSTS);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
|
||||
|
||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
nsHttpConnectionInfo *ci = nsnull;
|
||||
pipeline->GetConnectionInfo(&ci);
|
||||
if (ci) {
|
||||
nsCStringKey key(ci->HashKey());
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
||||
if (ent) {
|
||||
// search for another request to pipeline...
|
||||
PRInt32 i, count = ent->mPendingQ.Count();
|
||||
for (i=0; i<count; ++i) {
|
||||
nsHttpTransaction *trans = (nsHttpTransaction *) ent->mPendingQ[i];
|
||||
if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
|
||||
pipeline->AddTransaction(trans);
|
||||
|
||||
// remove transaction from pending queue
|
||||
ent->mPendingQ.RemoveElementAt(i);
|
||||
NS_RELEASE(trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
|
||||
|
||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
//
|
||||
// 1) remove the connection from the active list
|
||||
// 2) if keep-alive, add connection to idle list
|
||||
// 3) post event to process the pending transaction queue
|
||||
//
|
||||
|
||||
nsHttpConnectionInfo *ci = conn->ConnectionInfo();
|
||||
|
||||
nsCStringKey key(ci->HashKey());
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
||||
|
||||
NS_ASSERTION(ent, "no connection entry");
|
||||
if (ent) {
|
||||
ent->mActiveConns.RemoveElement(conn);
|
||||
mNumActiveConns--;
|
||||
if (conn->CanReuse()) {
|
||||
LOG((" adding connection to idle list\n"));
|
||||
// hold onto this connection in the idle list. we push it to
|
||||
// the end of the list so as to ensure that we'll visit older
|
||||
// connections first before getting to this one.
|
||||
ent->mIdleConns.AppendElement(conn);
|
||||
mNumIdleConns++;
|
||||
}
|
||||
else {
|
||||
LOG((" connection cannot be reused; closing connection\n"));
|
||||
// make sure the connection is closed and release our reference.
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return ProcessPendingQ(ci);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
|
||||
|
||||
NS_ADDREF(ci);
|
||||
nsresult rv = PostEvent(MSG_PROCESS_PENDING_Q, 0, ci);
|
||||
if (NS_FAILED(rv))
|
||||
NS_RELEASE(ci);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// enumeration callbacks
|
||||
|
||||
PRIntn PR_CALLBACK
|
||||
nsHttpConnectionMgr::ProcessOneTransactionCB(nsHashKey *key, void *data, void *closure)
|
||||
{
|
||||
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
||||
|
||||
if (self->ProcessPendingQForEntry(ent))
|
||||
return kHashEnumerateStop;
|
||||
|
||||
return kHashEnumerateNext;
|
||||
}
|
||||
|
||||
PRIntn PR_CALLBACK
|
||||
nsHttpConnectionMgr::PurgeOneIdleConnectionCB(nsHashKey *key, void *data, void *closure)
|
||||
{
|
||||
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
||||
|
||||
if (ent->mIdleConns.Count() > 0) {
|
||||
nsHttpConnection *conn = (nsHttpConnection *) ent->mIdleConns[0];
|
||||
ent->mIdleConns.RemoveElementAt(0);
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(conn);
|
||||
self->mNumIdleConns--;
|
||||
return kHashEnumerateStop;
|
||||
}
|
||||
|
||||
return kHashEnumerateNext;
|
||||
}
|
||||
|
||||
PRIntn PR_CALLBACK
|
||||
nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *closure)
|
||||
{
|
||||
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
||||
|
||||
LOG((" pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
|
||||
|
||||
PRInt32 count = ent->mIdleConns.Count();
|
||||
if (count > 0) {
|
||||
for (PRInt32 i=count-1; i>=0; --i) {
|
||||
nsHttpConnection *conn = (nsHttpConnection *) ent->mIdleConns[i];
|
||||
if (!conn->CanReuse()) {
|
||||
ent->mIdleConns.RemoveElementAt(i);
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(conn);
|
||||
self->mNumIdleConns--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if this entry is empty, then we can remove it.
|
||||
if (ent->mIdleConns.Count() == 0 &&
|
||||
ent->mActiveConns.Count() == 0 &&
|
||||
ent->mPendingQ.Count() == 0) {
|
||||
LOG((" removing empty connection entry\n"));
|
||||
return kHashEnumerateRemove;
|
||||
}
|
||||
|
||||
// else, use this opportunity to compact our arrays...
|
||||
ent->mIdleConns.Compact();
|
||||
ent->mActiveConns.Compact();
|
||||
ent->mPendingQ.Compact();
|
||||
|
||||
return kHashEnumerateNext;
|
||||
}
|
||||
|
||||
PRIntn PR_CALLBACK
|
||||
nsHttpConnectionMgr::ShutdownPassCB(nsHashKey *key, void *data, void *closure)
|
||||
{
|
||||
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
||||
|
||||
nsHttpTransaction *trans;
|
||||
nsHttpConnection *conn;
|
||||
|
||||
// close all active connections
|
||||
while (ent->mActiveConns.Count()) {
|
||||
conn = (nsHttpConnection *) ent->mActiveConns[0];
|
||||
|
||||
ent->mActiveConns.RemoveElementAt(0);
|
||||
self->mNumActiveConns--;
|
||||
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
|
||||
// close all idle connections
|
||||
while (ent->mIdleConns.Count()) {
|
||||
conn = (nsHttpConnection *) ent->mIdleConns[0];
|
||||
|
||||
ent->mIdleConns.RemoveElementAt(0);
|
||||
self->mNumIdleConns--;
|
||||
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
|
||||
// close all pending transactions
|
||||
while (ent->mPendingQ.Count()) {
|
||||
trans = (nsHttpTransaction *) ent->mPendingQ[0];
|
||||
|
||||
ent->mPendingQ.RemoveElementAt(0);
|
||||
|
||||
trans->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(trans);
|
||||
}
|
||||
|
||||
delete ent;
|
||||
return kHashEnumerateRemove;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
PRBool
|
||||
nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
|
||||
ent->mConnInfo->HashKey().get()));
|
||||
|
||||
PRInt32 i, count = ent->mPendingQ.Count();
|
||||
if (count > 0) {
|
||||
LOG((" pending-count=%u\n", count));
|
||||
nsHttpTransaction *trans = nsnull;
|
||||
nsHttpConnection *conn = nsnull;
|
||||
for (i=0; i<count; ++i) {
|
||||
trans = (nsHttpTransaction *) ent->mPendingQ[i];
|
||||
GetConnection(ent, trans->Caps(), &conn);
|
||||
if (conn)
|
||||
break;
|
||||
}
|
||||
if (conn) {
|
||||
LOG((" dispatching pending transaction...\n"));
|
||||
|
||||
// remove pending transaction
|
||||
ent->mPendingQ.RemoveElementAt(i);
|
||||
|
||||
nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
NS_RELEASE(trans);
|
||||
else {
|
||||
LOG((" DispatchTransaction failed [rv=%x]\n", rv));
|
||||
// on failure, just put the transaction back
|
||||
ent->mPendingQ.InsertElementAt(trans, i);
|
||||
// might be something wrong with the connection... close it.
|
||||
conn->Close(rv);
|
||||
}
|
||||
|
||||
NS_RELEASE(conn);
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// we're at the active connection limit if any one of the following conditions is true:
|
||||
// (1) at max-connections
|
||||
// (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
|
||||
// (3) keep-alive disabled and at max-connections-per-server
|
||||
PRBool
|
||||
nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
|
||||
{
|
||||
nsHttpConnectionInfo *ci = ent->mConnInfo;
|
||||
|
||||
LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
|
||||
ci->HashKey().get(), caps));
|
||||
|
||||
// use >= just to be safe
|
||||
if (mNumActiveConns >= mMaxConns) {
|
||||
LOG((" num active conns == max conns\n"));
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
nsHttpConnection *conn;
|
||||
PRInt32 i, totalCount, persistCount = 0;
|
||||
|
||||
totalCount = ent->mActiveConns.Count();
|
||||
|
||||
// count the number of persistent connections
|
||||
for (i=0; i<totalCount; ++i) {
|
||||
conn = (nsHttpConnection *) ent->mActiveConns[i];
|
||||
if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
|
||||
persistCount++;
|
||||
}
|
||||
|
||||
LOG((" total=%d, persist=%d\n", totalCount, persistCount));
|
||||
|
||||
PRUint16 maxConns;
|
||||
PRUint16 maxPersistConns;
|
||||
|
||||
if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
|
||||
maxConns = mMaxConnsPerProxy;
|
||||
maxPersistConns = mMaxPersistConnsPerProxy;
|
||||
}
|
||||
else {
|
||||
maxConns = mMaxConnsPerHost;
|
||||
maxPersistConns = mMaxPersistConnsPerHost;
|
||||
}
|
||||
|
||||
// use >= just to be safe
|
||||
return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
|
||||
(persistCount >= maxPersistConns) );
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
|
||||
nsHttpConnection **result)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
|
||||
ent->mConnInfo->HashKey().get(), PRUint32(caps)));
|
||||
|
||||
*result = nsnull;
|
||||
|
||||
if (AtActiveConnectionLimit(ent, caps)) {
|
||||
LOG((" at active connection limit!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
nsHttpConnection *conn = nsnull;
|
||||
|
||||
if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
|
||||
// search the idle connection list
|
||||
while (!conn && (ent->mIdleConns.Count() > 0)) {
|
||||
conn = (nsHttpConnection *) ent->mIdleConns[0];
|
||||
// we check if the connection can be reused before even checking if
|
||||
// it is a "matching" connection.
|
||||
if (!conn->CanReuse()) {
|
||||
LOG((" dropping stale connection: [conn=%x]\n", conn));
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
else
|
||||
LOG((" reusing connection [conn=%x]\n", conn));
|
||||
ent->mIdleConns.RemoveElementAt(0);
|
||||
mNumIdleConns--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
conn = new nsHttpConnection();
|
||||
if (!conn)
|
||||
return;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
nsresult rv = conn->Init(ent->mConnInfo, mMaxRequestDelay);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
// We created a new connection that will become active, purge the
|
||||
// oldest idle connection if we've reached the upper limit.
|
||||
if (mNumIdleConns + mNumActiveConns + 1 > mMaxConns)
|
||||
mCT.Enumerate(PurgeOneIdleConnectionCB, this);
|
||||
|
||||
// XXX this just purges a random idle connection. we should instead
|
||||
// enumerate the entire hash table to find the eldest idle connection.
|
||||
}
|
||||
|
||||
*result = conn;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
|
||||
nsAHttpTransaction *trans,
|
||||
PRUint8 caps,
|
||||
nsHttpConnection *conn)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
|
||||
ent->mConnInfo->HashKey().get(), trans, caps, conn));
|
||||
|
||||
nsHttpPipeline *pipeline = nsnull;
|
||||
if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
|
||||
LOG((" looking to build pipeline...\n"));
|
||||
if (BuildPipeline(ent, trans, &pipeline))
|
||||
trans = pipeline;
|
||||
}
|
||||
|
||||
// hold an owning ref to this connection
|
||||
ent->mActiveConns.AppendElement(conn);
|
||||
mNumActiveConns++;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
nsresult rv = conn->Activate(trans, caps);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG((" conn->Activate failed [rv=%x]\n", rv));
|
||||
ent->mActiveConns.RemoveElement(conn);
|
||||
mNumActiveConns--;
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
|
||||
// if we were unable to activate the pipeline, then this will destroy
|
||||
// the pipeline, which will cause each the transactions owned by the
|
||||
// pipeline to be restarted.
|
||||
NS_IF_RELEASE(pipeline);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
|
||||
nsAHttpTransaction *firstTrans,
|
||||
nsHttpPipeline **result)
|
||||
{
|
||||
if (mMaxPipelinedRequests < 2)
|
||||
return PR_FALSE;
|
||||
|
||||
nsHttpPipeline *pipeline = nsnull;
|
||||
nsHttpTransaction *trans;
|
||||
|
||||
PRInt32 i = 0, numAdded = 0;
|
||||
while (i < ent->mPendingQ.Count()) {
|
||||
trans = (nsHttpTransaction *) ent->mPendingQ[i];
|
||||
if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
|
||||
if (numAdded == 0) {
|
||||
pipeline = new nsHttpPipeline;
|
||||
if (!pipeline)
|
||||
return PR_FALSE;
|
||||
pipeline->AddTransaction(firstTrans);
|
||||
numAdded = 1;
|
||||
}
|
||||
pipeline->AddTransaction(trans);
|
||||
|
||||
// remove transaction from pending queue
|
||||
ent->mPendingQ.RemoveElementAt(i);
|
||||
NS_RELEASE(trans);
|
||||
|
||||
if (++numAdded == mMaxPipelinedRequests)
|
||||
break;
|
||||
}
|
||||
else
|
||||
++i; // skip to next pending transaction
|
||||
}
|
||||
|
||||
if (numAdded == 0)
|
||||
return PR_FALSE;
|
||||
|
||||
LOG((" pipelined %u transactions\n", numAdded));
|
||||
NS_ADDREF(*result = pipeline);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgShutdown()
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
|
||||
|
||||
mCT.Reset(ShutdownPassCB, this);
|
||||
|
||||
// signal shutdown complete
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mon.Notify();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::OnMsgNewTransaction(nsHttpTransaction *trans)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%x]\n", trans));
|
||||
|
||||
PRUint8 caps = trans->Caps();
|
||||
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
|
||||
NS_ASSERTION(ci, "no connection info");
|
||||
|
||||
nsCStringKey key(ci->HashKey());
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
||||
if (!ent) {
|
||||
ent = new nsConnectionEntry(ci);
|
||||
if (!ent)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mCT.Put(&key, ent);
|
||||
}
|
||||
|
||||
nsHttpConnection *conn;
|
||||
GetConnection(ent, caps, &conn);
|
||||
|
||||
nsresult rv;
|
||||
if (!conn) {
|
||||
LOG((" adding transaction to pending queue [trans=%x pending-count=%u]\n",
|
||||
trans, ent->mPendingQ.Count()+1));
|
||||
// put this transaction on the pending queue...
|
||||
ent->mPendingQ.AppendElement(trans);
|
||||
NS_ADDREF(trans);
|
||||
rv = NS_OK;
|
||||
}
|
||||
else {
|
||||
rv = DispatchTransaction(ent, trans, caps, conn);
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgCancelTransaction(nsHttpTransaction *trans,
|
||||
nsresult reason)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%x]\n", trans));
|
||||
|
||||
nsAHttpConnection *conn = trans->Connection();
|
||||
if (conn)
|
||||
conn->CloseTransaction(trans, reason);
|
||||
else {
|
||||
// make sure the transaction is not on the pending queue...
|
||||
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
|
||||
nsCStringKey key(ci->HashKey());
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
||||
if (ent) {
|
||||
PRInt32 index = ent->mPendingQ.IndexOf(trans);
|
||||
if (index >= 0) {
|
||||
ent->mPendingQ.RemoveElementAt(index);
|
||||
nsHttpTransaction *temp = trans;
|
||||
NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
|
||||
}
|
||||
}
|
||||
trans->Close(reason);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgProcessPendingQ(nsHttpConnectionInfo *ci)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
|
||||
|
||||
// start by processing the queue identified by the given connection info.
|
||||
nsCStringKey key(ci->HashKey());
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
||||
if (ent && ProcessPendingQForEntry(ent))
|
||||
return;
|
||||
|
||||
// if we reach here, it means that we couldn't dispatch a transaction
|
||||
// for the specified connection info. walk the connection table...
|
||||
|
||||
mCT.Enumerate(ProcessOneTransactionCB, this);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgPruneDeadConnections()
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
|
||||
|
||||
if (mNumIdleConns > 0)
|
||||
mCT.Enumerate(PruneDeadConnectionsCB, this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsISocketEventHandler)
|
||||
|
||||
// called on the socket thread
|
||||
NS_IMETHODIMP
|
||||
nsHttpConnectionMgr::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam)
|
||||
{
|
||||
switch (type) {
|
||||
case MSG_SHUTDOWN:
|
||||
OnMsgShutdown();
|
||||
break;
|
||||
case MSG_NEW_TRANSACTION:
|
||||
{
|
||||
nsHttpTransaction *trans = (nsHttpTransaction *) vparam;
|
||||
|
||||
nsresult rv = OnMsgNewTransaction(trans);
|
||||
if (NS_FAILED(rv))
|
||||
trans->Close(rv); // for whatever its worth
|
||||
|
||||
NS_RELEASE(trans);
|
||||
}
|
||||
break;
|
||||
case MSG_CANCEL_TRANSACTION:
|
||||
{
|
||||
nsHttpTransaction *trans = (nsHttpTransaction *) vparam;
|
||||
OnMsgCancelTransaction(trans, (nsresult) uparam);
|
||||
NS_RELEASE(trans);
|
||||
}
|
||||
break;
|
||||
case MSG_PROCESS_PENDING_Q:
|
||||
{
|
||||
nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) vparam;
|
||||
OnMsgProcessPendingQ(ci);
|
||||
NS_RELEASE(ci);
|
||||
}
|
||||
break;
|
||||
case MSG_PRUNE_DEAD_CONNECTIONS:
|
||||
OnMsgPruneDeadConnections();
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsHttpConnectionMgr_h__
|
||||
#define nsHttpConnectionMgr_h__
|
||||
|
||||
#include "nsHttpConnectionInfo.h"
|
||||
#include "nsHttpTransaction.h"
|
||||
#include "nsHashtable.h"
|
||||
#include "prmon.h"
|
||||
|
||||
#include "nsISocketTransportService.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsHttpConnection;
|
||||
class nsHttpPipeline;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsHttpConnectionMgr : public nsISocketEventHandler
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISOCKETEVENTHANDLER
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: functions below may only be called on the main thread.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
nsHttpConnectionMgr();
|
||||
virtual ~nsHttpConnectionMgr();
|
||||
|
||||
// XXX cleanup the way we pass around prefs.
|
||||
nsresult Init(PRUint16 maxConnections,
|
||||
PRUint16 maxConnectionsPerHost,
|
||||
PRUint16 maxConnectionsPerProxy,
|
||||
PRUint16 maxPersistentConnectionsPerHost,
|
||||
PRUint16 maxPersistentConnectionsPerProxy,
|
||||
PRUint16 maxRequestDelay,
|
||||
PRUint16 maxPipelinedRequests);
|
||||
nsresult Shutdown();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: functions below may be called on any thread.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// adds a transaction to the list of managed transactions.
|
||||
nsresult AddTransaction(nsHttpTransaction *);
|
||||
|
||||
// cancels a transaction w/ the given reason.
|
||||
nsresult CancelTransaction(nsHttpTransaction *, nsresult reason);
|
||||
|
||||
// called to force the connection manager to prune its list of idle
|
||||
// connections.
|
||||
nsresult PruneDeadConnections();
|
||||
|
||||
// called to get a reference to the socket transport service. the socket
|
||||
// transport service is not available when the connection manager is down.
|
||||
nsresult GetSTS(nsISocketTransportService **);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: functions below may be called only on the socket thread.
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// removes the next transaction for the specified connection from the
|
||||
// pending transaction queue.
|
||||
void AddTransactionToPipeline(nsHttpPipeline *);
|
||||
|
||||
// called by a connection when it has been closed or when it becomes idle.
|
||||
nsresult ReclaimConnection(nsHttpConnection *conn);
|
||||
|
||||
// called to force the transaction queue to be processed once more, giving
|
||||
// preference to the specified connection.
|
||||
nsresult ProcessPendingQ(nsHttpConnectionInfo *);
|
||||
|
||||
private:
|
||||
|
||||
enum {
|
||||
MSG_SHUTDOWN,
|
||||
MSG_NEW_TRANSACTION,
|
||||
MSG_CANCEL_TRANSACTION,
|
||||
MSG_PROCESS_PENDING_Q,
|
||||
MSG_PRUNE_DEAD_CONNECTIONS
|
||||
};
|
||||
|
||||
struct nsConnectionEntry
|
||||
{
|
||||
nsConnectionEntry(nsHttpConnectionInfo *ci)
|
||||
: mConnInfo(ci)
|
||||
{
|
||||
NS_ADDREF(mConnInfo);
|
||||
}
|
||||
~nsConnectionEntry() { NS_RELEASE(mConnInfo); }
|
||||
|
||||
nsHttpConnectionInfo *mConnInfo;
|
||||
nsVoidArray mPendingQ; // pending transaction queue
|
||||
nsVoidArray mActiveConns; // active connections
|
||||
nsVoidArray mIdleConns; // idle persistent connections
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: these members may be accessed from any thread (use mMonitor)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
PRMonitor *mMonitor;
|
||||
nsCOMPtr<nsISocketTransportService> mSTS; // cached reference to STS
|
||||
|
||||
// connection limits
|
||||
PRUint16 mMaxConns;
|
||||
PRUint16 mMaxConnsPerHost;
|
||||
PRUint16 mMaxConnsPerProxy;
|
||||
PRUint16 mMaxPersistConnsPerHost;
|
||||
PRUint16 mMaxPersistConnsPerProxy;
|
||||
PRUint16 mMaxRequestDelay; // in seconds
|
||||
PRUint16 mMaxPipelinedRequests;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: these members are only accessed on the socket transport thread
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static PRIntn PR_CALLBACK ProcessOneTransactionCB(nsHashKey *, void *, void *);
|
||||
static PRIntn PR_CALLBACK PurgeOneIdleConnectionCB(nsHashKey *, void *, void *);
|
||||
static PRIntn PR_CALLBACK PruneDeadConnectionsCB(nsHashKey *, void *, void *);
|
||||
static PRIntn PR_CALLBACK ShutdownPassCB(nsHashKey *, void *, void *);
|
||||
|
||||
PRBool ProcessPendingQForEntry(nsConnectionEntry *);
|
||||
PRBool AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
|
||||
void GetConnection(nsConnectionEntry *, PRUint8 caps, nsHttpConnection **);
|
||||
nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *,
|
||||
PRUint8 caps, nsHttpConnection *);
|
||||
PRBool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
|
||||
nsresult PostEvent(PRUint32 type, PRUint32 uparam, void *vparam);
|
||||
|
||||
void OnMsgShutdown();
|
||||
nsresult OnMsgNewTransaction(nsHttpTransaction *);
|
||||
void OnMsgCancelTransaction(nsHttpTransaction *trans, nsresult reason);
|
||||
void OnMsgProcessPendingQ(nsHttpConnectionInfo *ci);
|
||||
void OnMsgPruneDeadConnections();
|
||||
|
||||
// counters
|
||||
PRUint16 mNumActiveConns;
|
||||
PRUint16 mNumIdleConns;
|
||||
|
||||
//
|
||||
// the connection table
|
||||
//
|
||||
// this table is indexed by connection key. each entry is a
|
||||
// ConnectionEntry object.
|
||||
//
|
||||
nsHashtable mCT;
|
||||
};
|
||||
|
||||
#endif // !nsHttpConnectionMgr_h__
|
|
@ -0,0 +1,161 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prlog.h"
|
||||
#include "prenv.h"
|
||||
#include "prthread.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// set NSPR_LOG_MODULES=Test:5
|
||||
//
|
||||
static PRLogModuleInfo *gTestLog = nsnull;
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(gTestLog, PR_LOG_DEBUG, args)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static nsresult
|
||||
RunBlockingTest(const nsACString &host, PRInt32 port, nsIFile *file)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("RunBlockingTest\n"));
|
||||
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService(kSocketTransportServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> input;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(input), file);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsISocketTransport> trans;
|
||||
rv = sts->CreateTransport(nsnull, 0, host, port, nsnull, getter_AddRefs(trans));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIOutputStream> output;
|
||||
rv = trans->OpenOutputStream(nsITransport::OPEN_BLOCKING, 100, 10, getter_AddRefs(output));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
char buf[120];
|
||||
PRUint32 nr, nw;
|
||||
for (;;) {
|
||||
rv = input->Read(buf, sizeof(buf), &nr);
|
||||
if (NS_FAILED(rv) || (nr == 0)) return rv;
|
||||
|
||||
/*
|
||||
const char *p = buf;
|
||||
while (nr) {
|
||||
rv = output->Write(p, nr, &nw);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nr -= nw;
|
||||
p += nw;
|
||||
}
|
||||
*/
|
||||
|
||||
rv = output->Write(buf, nr, &nw);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_ASSERTION(nr == nw, "not all written");
|
||||
}
|
||||
|
||||
LOG((" done copying data.\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (argc < 4) {
|
||||
printf("usage: %s <host> <port> <file-to-read>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
char* hostName = argv[1];
|
||||
PRInt32 port = atoi(argv[2]);
|
||||
char* fileName = argv[3];
|
||||
{
|
||||
nsCOMPtr<nsIServiceManager> servMan;
|
||||
NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
|
||||
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
|
||||
NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
|
||||
if (registrar)
|
||||
registrar->AutoRegister(nsnull);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
gTestLog = PR_NewLogModule("Test");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsILocalFile> file;
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(fileName), PR_FALSE, getter_AddRefs(file));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = RunBlockingTest(nsDependentCString(hostName), port, file);
|
||||
if (NS_FAILED(rv))
|
||||
LOG(("RunBlockingTest failed [rv=%x]\n", rv));
|
||||
|
||||
// give background threads a chance to finish whatever work they may
|
||||
// be doing.
|
||||
LOG(("sleeping for 5 seconds...\n"));
|
||||
PR_Sleep(PR_SecondsToInterval(5));
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
rv = NS_ShutdownXPCOM(nsnull);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIProxyObjectManager.h"
|
||||
#include "nsIRequest.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsIEventQueue.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "prlog.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// set NSPR_LOG_MODULES=Test:5
|
||||
//
|
||||
static PRLogModuleInfo *gTestLog = nsnull;
|
||||
#define LOG(args) PR_LOG(gTestLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
||||
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
||||
static NS_DEFINE_CID(kEventQueueCID, NS_EVENTQUEUE_CID);
|
||||
|
||||
PRBool gDone = PR_FALSE;
|
||||
nsIEventQueue *gEventQ = nsnull;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void *PR_CALLBACK
|
||||
DoneEvent_Handler(PLEvent *ev)
|
||||
{
|
||||
gDone = PR_TRUE;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
DoneEvent_Cleanup(PLEvent *ev)
|
||||
{
|
||||
delete ev;
|
||||
}
|
||||
|
||||
static void
|
||||
PostDoneEvent()
|
||||
{
|
||||
LOG(("PostDoneEvent\n"));
|
||||
|
||||
PLEvent *ev = new PLEvent();
|
||||
|
||||
PL_InitEvent(ev, nsnull,
|
||||
DoneEvent_Handler,
|
||||
DoneEvent_Cleanup);
|
||||
|
||||
gEventQ->PostEvent(ev);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class MyListener : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
MyListener() {}
|
||||
virtual ~MyListener() {}
|
||||
|
||||
NS_IMETHOD OnStartRequest(nsIRequest *req, nsISupports *ctx)
|
||||
{
|
||||
LOG(("MyListener::OnStartRequest\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnDataAvailable(nsIRequest *req, nsISupports *ctx,
|
||||
nsIInputStream *stream,
|
||||
PRUint32 offset, PRUint32 count)
|
||||
{
|
||||
LOG(("MyListener::OnDataAvailable [offset=%u count=%u]\n", offset, count));
|
||||
|
||||
char buf[500];
|
||||
nsresult rv;
|
||||
|
||||
while (count) {
|
||||
PRUint32 n, amt = PR_MIN(count, sizeof(buf));
|
||||
|
||||
rv = stream->Read(buf, amt, &n);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG((" read returned 0x%08x\n", rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
LOG((" read %u bytes\n", n));
|
||||
count -= n;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
|
||||
{
|
||||
LOG(("MyListener::OnStopRequest [status=%x]\n", status));
|
||||
PostDoneEvent();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(MyListener,
|
||||
nsIRequestObserver,
|
||||
nsIStreamListener)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class MyCallbacks : public nsIInterfaceRequestor
|
||||
, public nsIProgressEventSink
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
MyCallbacks() {}
|
||||
virtual ~MyCallbacks() {}
|
||||
|
||||
NS_IMETHOD GetInterface(const nsID &iid, void **result)
|
||||
{
|
||||
return QueryInterface(iid, result);
|
||||
}
|
||||
|
||||
NS_IMETHOD OnStatus(nsIRequest *req, nsISupports *ctx, nsresult status,
|
||||
const PRUnichar *statusArg)
|
||||
{
|
||||
LOG(("MyCallbacks::OnStatus [status=%x]\n", status));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnProgress(nsIRequest *req, nsISupports *ctx,
|
||||
PRUint32 progress, PRUint32 progressMax)
|
||||
{
|
||||
LOG(("MyCallbacks::OnProgress [progress=%u/%u]\n", progress, progressMax));
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(MyCallbacks,
|
||||
nsIInterfaceRequestor,
|
||||
nsIProgressEventSink)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* asynchronously copy file.
|
||||
*/
|
||||
static nsresult
|
||||
RunTest(nsIFile *file)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("RunTest\n"));
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIURI> uri = do_CreateInstance(kSimpleURICID);
|
||||
if (uri)
|
||||
uri->SetSpec(NS_LITERAL_CSTRING("foo://bar"));
|
||||
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
rv = NS_NewInputStreamChannel(getter_AddRefs(chan), uri, stream,
|
||||
NS_LITERAL_CSTRING(""),
|
||||
NS_LITERAL_CSTRING(""));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = chan->SetNotificationCallbacks(new MyCallbacks());
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = chan->AsyncOpen(new MyListener(), nsnull);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
gDone = PR_FALSE;
|
||||
while (!gDone) {
|
||||
PLEvent *event;
|
||||
rv = gEventQ->WaitForEvent(&event);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = gEventQ->HandleEvent(event);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: %s <file-to-read>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
char* fileName = argv[1];
|
||||
{
|
||||
nsCOMPtr<nsIServiceManager> servMan;
|
||||
NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
|
||||
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
|
||||
NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
|
||||
if (registrar)
|
||||
registrar->AutoRegister(nsnull);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
gTestLog = PR_NewLogModule("Test");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIEventQueueService> eventQService =
|
||||
do_GetService(kEventQueueServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, &gEventQ);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsILocalFile> file;
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(fileName), PR_FALSE, getter_AddRefs(file));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = RunTest(file);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "RunTest failed");
|
||||
|
||||
NS_RELEASE(gEventQ);
|
||||
|
||||
// give background threads a chance to finish whatever work they may
|
||||
// be doing.
|
||||
PR_Sleep(PR_SecondsToInterval(1));
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
rv = NS_ShutdownXPCOM(nsnull);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIProxyObjectManager.h"
|
||||
#include "nsIRequest.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsIEventQueue.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "prlog.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// set NSPR_LOG_MODULES=Test:5
|
||||
//
|
||||
static PRLogModuleInfo *gTestLog = nsnull;
|
||||
#define LOG(args) PR_LOG(gTestLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
||||
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
||||
static NS_DEFINE_CID(kEventQueueCID, NS_EVENTQUEUE_CID);
|
||||
|
||||
PRBool gDone = PR_FALSE;
|
||||
nsIEventQueue *gEventQ = nsnull;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void *PR_CALLBACK
|
||||
DoneEvent_Handler(PLEvent *ev)
|
||||
{
|
||||
gDone = PR_TRUE;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
DoneEvent_Cleanup(PLEvent *ev)
|
||||
{
|
||||
delete ev;
|
||||
}
|
||||
|
||||
static void
|
||||
PostDoneEvent()
|
||||
{
|
||||
LOG(("PostDoneEvent\n"));
|
||||
|
||||
PLEvent *ev = new PLEvent();
|
||||
|
||||
PL_InitEvent(ev, nsnull,
|
||||
DoneEvent_Handler,
|
||||
DoneEvent_Cleanup);
|
||||
|
||||
gEventQ->PostEvent(ev);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class MyListener : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
MyListener() {}
|
||||
virtual ~MyListener() {}
|
||||
|
||||
NS_IMETHOD OnStartRequest(nsIRequest *req, nsISupports *ctx)
|
||||
{
|
||||
LOG(("MyListener::OnStartRequest\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnDataAvailable(nsIRequest *req, nsISupports *ctx,
|
||||
nsIInputStream *stream,
|
||||
PRUint32 offset, PRUint32 count)
|
||||
{
|
||||
LOG(("MyListener::OnDataAvailable [offset=%u count=%u]\n", offset, count));
|
||||
|
||||
char buf[500];
|
||||
nsresult rv;
|
||||
|
||||
while (count) {
|
||||
PRUint32 n, amt = PR_MIN(count, sizeof(buf));
|
||||
|
||||
rv = stream->Read(buf, amt, &n);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG((" read returned 0x%08x\n", rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
fwrite(buf, n, 1, stdout);
|
||||
printf("\n");
|
||||
|
||||
LOG((" read %u bytes\n", n));
|
||||
count -= n;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
|
||||
{
|
||||
LOG(("MyListener::OnStopRequest [status=%x]\n", status));
|
||||
PostDoneEvent();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(MyListener,
|
||||
nsIRequestObserver,
|
||||
nsIStreamListener)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* asynchronously copy file.
|
||||
*/
|
||||
static nsresult
|
||||
RunTest(nsIFile *file, PRInt32 offset, PRInt32 length)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("RunTest\n"));
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIInputStreamPump> pump;
|
||||
rv = NS_NewInputStreamPump(getter_AddRefs(pump), stream, offset, length);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = pump->AsyncRead(new MyListener(), nsnull);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
gDone = PR_FALSE;
|
||||
while (!gDone) {
|
||||
PLEvent *event;
|
||||
rv = gEventQ->WaitForEvent(&event);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = gEventQ->HandleEvent(event);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (argc < 4) {
|
||||
printf("usage: %s <file-to-read> <start-offset> <read-length>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
char* fileName = argv[1];
|
||||
PRInt32 offset = atoi(argv[2]);
|
||||
PRInt32 length = atoi(argv[3]);
|
||||
{
|
||||
nsCOMPtr<nsIServiceManager> servMan;
|
||||
NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
|
||||
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
|
||||
NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
|
||||
if (registrar)
|
||||
registrar->AutoRegister(nsnull);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
gTestLog = PR_NewLogModule("Test");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIEventQueueService> eventQService =
|
||||
do_GetService(kEventQueueServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, &gEventQ);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsILocalFile> file;
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(fileName), PR_FALSE, getter_AddRefs(file));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = RunTest(file, offset, length);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "RunTest failed");
|
||||
|
||||
NS_RELEASE(gEventQ);
|
||||
|
||||
// give background threads a chance to finish whatever work they may
|
||||
// be doing.
|
||||
PR_Sleep(PR_SecondsToInterval(1));
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
rv = NS_ShutdownXPCOM(nsnull);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,402 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIProxyObjectManager.h"
|
||||
#include "nsIRequest.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsIEventQueue.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "prlog.h"
|
||||
#include "prenv.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// set NSPR_LOG_MODULES=Test:5
|
||||
//
|
||||
static PRLogModuleInfo *gTestLog = nsnull;
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(gTestLog, PR_LOG_DEBUG, args)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
|
||||
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
||||
static NS_DEFINE_CID(kEventQueueCID, NS_EVENTQUEUE_CID);
|
||||
|
||||
PRBool gDone = PR_FALSE;
|
||||
nsIEventQueue* gEventQ = nsnull;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void *PR_CALLBACK
|
||||
DoneEvent_Handler(PLEvent *ev)
|
||||
{
|
||||
gDone = PR_TRUE;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
DoneEvent_Cleanup(PLEvent *ev)
|
||||
{
|
||||
delete ev;
|
||||
}
|
||||
|
||||
static void
|
||||
PostDoneEvent()
|
||||
{
|
||||
LOG(("PostDoneEvent\n"));
|
||||
|
||||
PLEvent *ev = new PLEvent();
|
||||
|
||||
PL_InitEvent(ev, nsnull,
|
||||
DoneEvent_Handler,
|
||||
DoneEvent_Cleanup);
|
||||
|
||||
gEventQ->PostEvent(ev);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define CHUNK_SIZE 500
|
||||
|
||||
class MyCopier : public nsIInputStreamNotify
|
||||
, public nsIOutputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
MyCopier()
|
||||
: mLock(nsnull)
|
||||
, mInputCondition(NS_OK)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MyCopier()
|
||||
{
|
||||
if (mLock)
|
||||
PR_DestroyLock(mLock);
|
||||
if (mInput)
|
||||
mInput->Close();
|
||||
if (mOutput)
|
||||
mOutput->Close();
|
||||
}
|
||||
|
||||
// called on any thread
|
||||
NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *inStr)
|
||||
{
|
||||
LOG(("OnInputStreamReady\n"));
|
||||
nsAutoLock lock(mLock);
|
||||
NS_ASSERTION(inStr == mInput, "unexpected stream");
|
||||
Process_Locked();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// called on any thread
|
||||
NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *outStr)
|
||||
{
|
||||
LOG(("OnOutputStreamReady\n"));
|
||||
nsAutoLock lock(mLock);
|
||||
NS_ASSERTION(outStr == mOutput, "unexpected stream");
|
||||
Process_Locked();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Close_Locked()
|
||||
{
|
||||
LOG(("Close_Locked\n"));
|
||||
|
||||
mOutput->Close();
|
||||
mOutput = 0;
|
||||
mInput->Close();
|
||||
mInput = 0;
|
||||
|
||||
// post done copying event
|
||||
PostDoneEvent();
|
||||
}
|
||||
|
||||
void Process_Locked()
|
||||
{
|
||||
while (1) {
|
||||
mInputCondition = NS_OK; // reset
|
||||
|
||||
PRUint32 n;
|
||||
nsresult rv = mOutput->WriteSegments(FillOutputBuffer, this, CHUNK_SIZE, &n);
|
||||
if (NS_FAILED(rv) || (n == 0)) {
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mOutput->AsyncWait(this, 0, nsnull);
|
||||
else if (mInputCondition == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mInput->AsyncWait(this, 0, nsnull);
|
||||
else
|
||||
Close_Locked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult AsyncCopy(nsITransport *srcTrans, nsITransport *destTrans)
|
||||
{
|
||||
mLock = PR_NewLock();
|
||||
if (!mLock)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> inStr;
|
||||
rv = srcTrans->OpenInputStream(0, 0, 0, getter_AddRefs(inStr));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outStr;
|
||||
rv = destTrans->OpenOutputStream(0, 0, 0, getter_AddRefs(outStr));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mInput = do_QueryInterface(inStr);
|
||||
mOutput = do_QueryInterface(outStr);
|
||||
|
||||
return mInput->AsyncWait(this, 0, nsnull);
|
||||
}
|
||||
|
||||
static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
|
||||
void *closure,
|
||||
char *buffer,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countRead)
|
||||
{
|
||||
MyCopier *self = (MyCopier *) closure;
|
||||
|
||||
nsresult rv = self->mInput->Read(buffer, count, countRead);
|
||||
if (NS_FAILED(rv))
|
||||
self->mInputCondition = rv;
|
||||
else if (*countRead == 0)
|
||||
self->mInputCondition = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
return self->mInputCondition;
|
||||
}
|
||||
|
||||
protected:
|
||||
PRLock *mLock;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInput;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mOutput;
|
||||
nsresult mInputCondition;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(MyCopier,
|
||||
nsIInputStreamNotify,
|
||||
nsIOutputStreamNotify)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* asynchronously copy file.
|
||||
*/
|
||||
static nsresult
|
||||
RunTest(nsIFile *srcFile, nsIFile *destFile)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("RunTest\n"));
|
||||
|
||||
nsCOMPtr<nsIStreamTransportService> sts =
|
||||
do_GetService(kStreamTransportServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> srcStr;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(srcStr), srcFile);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIOutputStream> destStr;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(destStr), destFile);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsITransport> srcTransport;
|
||||
rv = sts->CreateInputTransport(srcStr, -1, -1, PR_TRUE, getter_AddRefs(srcTransport));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsITransport> destTransport;
|
||||
rv = sts->CreateOutputTransport(destStr, -1, -1, PR_TRUE, getter_AddRefs(destTransport));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
MyCopier *copier = new MyCopier();
|
||||
if (copier == nsnull)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(copier);
|
||||
|
||||
rv = copier->AsyncCopy(srcTransport, destTransport);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PLEvent* event;
|
||||
|
||||
gDone = PR_FALSE;
|
||||
while (!gDone) {
|
||||
rv = gEventQ->WaitForEvent(&event);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = gEventQ->HandleEvent(event);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
NS_RELEASE(copier);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static nsresult
|
||||
RunBlockingTest(nsIFile *srcFile, nsIFile *destFile)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("RunBlockingTest\n"));
|
||||
|
||||
nsCOMPtr<nsIStreamTransportService> sts =
|
||||
do_GetService(kStreamTransportServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> srcIn;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(srcIn), srcFile);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIOutputStream> fileOut;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOut), destFile);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsITransport> destTransport;
|
||||
rv = sts->CreateOutputTransport(fileOut, -1, -1, PR_TRUE, getter_AddRefs(destTransport));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIOutputStream> destOut;
|
||||
rv = destTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 100, 10, getter_AddRefs(destOut));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
char buf[120];
|
||||
PRUint32 n;
|
||||
for (;;) {
|
||||
rv = srcIn->Read(buf, sizeof(buf), &n);
|
||||
if (NS_FAILED(rv) || (n == 0)) return rv;
|
||||
|
||||
rv = destOut->Write(buf, n, &n);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: %s <file-to-read>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
char* fileName = argv[1];
|
||||
{
|
||||
nsCOMPtr<nsIServiceManager> servMan;
|
||||
NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
|
||||
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
|
||||
NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
|
||||
if (registrar)
|
||||
registrar->AutoRegister(nsnull);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
gTestLog = PR_NewLogModule("Test");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIEventQueueService> eventQService =
|
||||
do_GetService(kEventQueueServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, &gEventQ);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsILocalFile> srcFile;
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(fileName), PR_FALSE, getter_AddRefs(srcFile));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIFile> destFile;
|
||||
rv = srcFile->Clone(getter_AddRefs(destFile));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCAutoString leafName;
|
||||
rv = destFile->GetNativeLeafName(leafName);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCAutoString newName;
|
||||
newName = leafName + NS_LITERAL_CSTRING(".1");
|
||||
rv = destFile->SetNativeLeafName(newName);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = RunTest(srcFile, destFile);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "RunTest failed");
|
||||
|
||||
newName = leafName + NS_LITERAL_CSTRING(".2");
|
||||
rv = destFile->SetNativeLeafName(newName);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = RunBlockingTest(srcFile, destFile);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "RunBlockingTest failed");
|
||||
|
||||
NS_RELEASE(gEventQ);
|
||||
|
||||
// give background threads a chance to finish whatever work they may
|
||||
// be doing.
|
||||
PR_Sleep(PR_SecondsToInterval(1));
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
rv = NS_ShutdownXPCOM(nsnull);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIInputStream.idl"
|
||||
|
||||
interface nsIInputStreamNotify;
|
||||
interface nsIEventQueue;
|
||||
|
||||
[scriptable, uuid(cc911e09-2a02-4d2d-ba4b-b2afb173e96d)]
|
||||
interface nsIAsyncInputStream : nsIInputStream
|
||||
{
|
||||
/**
|
||||
* This function extends nsIInputStream::close, allowing the caller to
|
||||
* specify the reason for closing the stream. Any future attempts to
|
||||
* access the stream (e.g., nsIInputStream::read) will result in an
|
||||
* exception with the value given by |reason|.
|
||||
*
|
||||
* close() is equivalent to closeEx(NS_BASE_STREAM_CLOSED).
|
||||
*
|
||||
* Any pending asyncWait will be notified.
|
||||
*/
|
||||
void closeEx(in nsresult reason);
|
||||
|
||||
/**
|
||||
* Asynchronously wait for the stream to be readable or closed.
|
||||
*
|
||||
* @param aNotify
|
||||
* This object is notified when the stream becomes ready.
|
||||
* @param aRequestedCount
|
||||
* Wait until at least this many bytes may be read. This is only
|
||||
* a suggestion to the underlying stream; it may be ignored.
|
||||
* @param aEventQ
|
||||
* Specify NULL to receive notification on ANY thread, or specify
|
||||
* that notification be delivered to a specific event queue. Caller
|
||||
* must pass a "resolved" event queue (see nsIEventQueueService).
|
||||
*/
|
||||
void asyncWait(in nsIInputStreamNotify aNotify,
|
||||
in unsigned long aRequestedCount,
|
||||
in nsIEventQueue aEventQ);
|
||||
};
|
||||
|
||||
[scriptable, uuid(20c665a3-0ebf-4ebb-97ba-794248bfa8fe)]
|
||||
interface nsIInputStreamNotify : nsISupports
|
||||
{
|
||||
void onInputStreamReady(in nsIAsyncInputStream aStream);
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIOutputStream.idl"
|
||||
|
||||
interface nsIOutputStreamNotify;
|
||||
interface nsIEventQueue;
|
||||
|
||||
[scriptable, uuid(04ba1b53-fad0-4033-91c9-3e24db22079c)]
|
||||
interface nsIAsyncOutputStream : nsIOutputStream
|
||||
{
|
||||
/**
|
||||
* This function extends nsIOutputStream::close, allowing the caller to
|
||||
* specify the reason for closing the stream. Any future attempts to
|
||||
* access the stream (e.g., nsIInputStream::write) will result in an
|
||||
* exception with the value given by |reason|.
|
||||
*
|
||||
* close() is equivalent to closeEx(NS_BASE_STREAM_CLOSED).
|
||||
*
|
||||
* Any pending asyncWait will be notified.
|
||||
*/
|
||||
void closeEx(in nsresult reason);
|
||||
|
||||
/**
|
||||
* Asynchronously wait for the stream to be writable or closed.
|
||||
*
|
||||
* @param aNotify
|
||||
* This object is notified when the stream becomes ready.
|
||||
* @param aRequestedCount
|
||||
* Wait until at least this many bytes may be written. This is
|
||||
* only a suggestion to the underlying stream; it may be ignored.
|
||||
* @param aEventQ
|
||||
* Specify NULL to receive notification on ANY thread, or specify
|
||||
* that notification be delivered to a specific event queue. Caller
|
||||
* must pass a "resolved" event queue (see nsIEventQueueService).
|
||||
*/
|
||||
void asyncWait(in nsIOutputStreamNotify aNotify,
|
||||
in unsigned long aRequestedCount,
|
||||
in nsIEventQueue aEventQ);
|
||||
};
|
||||
|
||||
[scriptable, uuid(614def11-1d77-409b-8153-ea3ae5babc3c)]
|
||||
interface nsIOutputStreamNotify : nsISupports
|
||||
{
|
||||
void onOutputStreamReady(in nsIAsyncOutputStream aStream);
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,445 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsIEventQueue.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsInputStreamReadyEvent : public PLEvent
|
||||
, public nsIInputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsInputStreamReadyEvent(nsIInputStreamNotify *notify,
|
||||
nsIEventQueue *eventQ)
|
||||
: mNotify(notify)
|
||||
, mEventQ(eventQ)
|
||||
{
|
||||
NS_INIT_ISUPPORTS();
|
||||
}
|
||||
|
||||
virtual ~nsInputStreamReadyEvent()
|
||||
{
|
||||
if (mNotify) {
|
||||
//
|
||||
// whoa!! looks like we never posted this event. take care not to
|
||||
// delete mNotify on the calling thread (which might be the wrong
|
||||
// thread).
|
||||
//
|
||||
nsCOMPtr<nsIInputStreamNotify> event;
|
||||
NS_NewInputStreamReadyEvent(getter_AddRefs(event), mNotify, mEventQ);
|
||||
mNotify = 0;
|
||||
if (event)
|
||||
event->OnInputStreamReady(nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
|
||||
{
|
||||
mStream = stream;
|
||||
|
||||
// will be released when event is handled
|
||||
NS_ADDREF_THIS();
|
||||
|
||||
PL_InitEvent(this, nsnull, EventHandler, EventCleanup);
|
||||
|
||||
if (mEventQ->PostEvent(this) == PR_FAILURE) {
|
||||
NS_WARNING("PostEvent failed");
|
||||
NS_RELEASE_THIS();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAsyncInputStream> mStream;
|
||||
nsCOMPtr<nsIInputStreamNotify> mNotify;
|
||||
nsCOMPtr<nsIEventQueue> mEventQ;
|
||||
|
||||
static void *PR_CALLBACK EventHandler(PLEvent *plevent)
|
||||
{
|
||||
nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent;
|
||||
ev->mNotify->OnInputStreamReady(ev->mStream);
|
||||
ev->mNotify = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK EventCleanup(PLEvent *plevent)
|
||||
{
|
||||
nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent;
|
||||
NS_RELEASE(ev);
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsInputStreamReadyEvent,
|
||||
nsIInputStreamNotify)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsOutputStreamReadyEvent : public PLEvent
|
||||
, public nsIOutputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsOutputStreamReadyEvent(nsIOutputStreamNotify *notify,
|
||||
nsIEventQueue *eventQ)
|
||||
: mNotify(notify)
|
||||
, mEventQ(eventQ)
|
||||
{
|
||||
NS_INIT_ISUPPORTS();
|
||||
}
|
||||
|
||||
virtual ~nsOutputStreamReadyEvent()
|
||||
{
|
||||
if (mNotify) {
|
||||
//
|
||||
// whoa!! looks like we never posted this event. take care not to
|
||||
// delete mNotify on the calling thread (which might be the wrong
|
||||
// thread).
|
||||
//
|
||||
nsCOMPtr<nsIOutputStreamNotify> event;
|
||||
NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mNotify, mEventQ);
|
||||
mNotify = 0;
|
||||
if (event)
|
||||
event->OnOutputStreamReady(nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
void Init(nsIOutputStreamNotify *notify, nsIEventQueue *eventQ)
|
||||
{
|
||||
mNotify = notify;
|
||||
mEventQ = eventQ;
|
||||
|
||||
PL_InitEvent(this, nsnull, EventHandler, EventCleanup);
|
||||
}
|
||||
|
||||
NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
|
||||
{
|
||||
mStream = stream;
|
||||
|
||||
// this will be released when the event is handled
|
||||
NS_ADDREF_THIS();
|
||||
|
||||
PL_InitEvent(this, nsnull, EventHandler, EventCleanup);
|
||||
|
||||
if (mEventQ->PostEvent(this) == PR_FAILURE) {
|
||||
NS_WARNING("PostEvent failed");
|
||||
NS_RELEASE_THIS();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAsyncOutputStream> mStream;
|
||||
nsCOMPtr<nsIOutputStreamNotify> mNotify;
|
||||
nsCOMPtr<nsIEventQueue> mEventQ;
|
||||
|
||||
static void *PR_CALLBACK EventHandler(PLEvent *plevent)
|
||||
{
|
||||
nsOutputStreamReadyEvent *ev = (nsOutputStreamReadyEvent *) plevent;
|
||||
ev->mNotify->OnOutputStreamReady(ev->mStream);
|
||||
ev->mNotify = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK EventCleanup(PLEvent *ev)
|
||||
{
|
||||
nsOutputStreamReadyEvent *event = (nsOutputStreamReadyEvent *) ev;
|
||||
NS_RELEASE(event);
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsOutputStreamReadyEvent,
|
||||
nsIOutputStreamNotify)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_COM nsresult
|
||||
NS_NewInputStreamReadyEvent(nsIInputStreamNotify **event,
|
||||
nsIInputStreamNotify *notify,
|
||||
nsIEventQueue *eventQ)
|
||||
{
|
||||
nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(notify, eventQ);
|
||||
if (!ev)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(*event = ev);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_COM nsresult
|
||||
NS_NewOutputStreamReadyEvent(nsIOutputStreamNotify **event,
|
||||
nsIOutputStreamNotify *notify,
|
||||
nsIEventQueue *eventQ)
|
||||
{
|
||||
nsOutputStreamReadyEvent *ev = new nsOutputStreamReadyEvent(notify, eventQ);
|
||||
if (!ev)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(*event = ev);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NS_AsyncCopy implementation
|
||||
|
||||
// this stream copier assumes the input stream is buffered (ReadSegments OK)
|
||||
class nsStreamCopierIB : public nsIInputStreamNotify
|
||||
, public nsIOutputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsStreamCopierIB(nsIAsyncInputStream *in,
|
||||
nsIAsyncOutputStream *out,
|
||||
PRUint32 chunksize)
|
||||
: mSource(in)
|
||||
, mSink(out)
|
||||
, mChunkSize(chunksize)
|
||||
{ NS_INIT_ISUPPORTS(); }
|
||||
virtual ~nsStreamCopierIB() {}
|
||||
|
||||
static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
|
||||
void *closure,
|
||||
const char *buffer,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countWritten)
|
||||
{
|
||||
nsStreamCopierIB *self = (nsStreamCopierIB *) closure;
|
||||
|
||||
nsresult rv = self->mSink->Write(buffer, count, countWritten);
|
||||
if (NS_FAILED(rv))
|
||||
self->mSinkCondition = rv;
|
||||
else if (*countWritten == 0)
|
||||
self->mSinkCondition = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
return self->mSinkCondition;
|
||||
}
|
||||
|
||||
// called on some random thread
|
||||
NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *in)
|
||||
{
|
||||
NS_ASSERTION(in == mSource, "unexpected stream");
|
||||
// Do all of our work from OnOutputStreamReady. This
|
||||
// way we're more likely to always be working on the
|
||||
// same thread.
|
||||
return mSink->AsyncWait(this, 0, nsnull);
|
||||
}
|
||||
|
||||
// called on some random thread
|
||||
NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
{
|
||||
NS_ASSERTION(out == mSink, "unexpected stream");
|
||||
for (;;) {
|
||||
mSinkCondition = NS_OK; // reset
|
||||
PRUint32 n;
|
||||
nsresult rv = mSource->ReadSegments(ConsumeInputBuffer, this, mChunkSize, &n);
|
||||
if (NS_FAILED(rv) || (n == 0)) {
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mSource->AsyncWait(this, 0, nsnull);
|
||||
else if (mSinkCondition == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mSink->AsyncWait(this, 0, nsnull);
|
||||
else {
|
||||
mSink = 0;
|
||||
mSource->CloseEx(mSinkCondition);
|
||||
mSource = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAsyncInputStream> mSource;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mSink;
|
||||
PRUint32 mChunkSize;
|
||||
nsresult mSinkCondition;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsStreamCopierIB,
|
||||
nsIInputStreamNotify,
|
||||
nsIOutputStreamNotify)
|
||||
|
||||
// this stream copier assumes the output stream is buffered (WriteSegments OK)
|
||||
class nsStreamCopierOB : public nsIInputStreamNotify
|
||||
, public nsIOutputStreamNotify
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsStreamCopierOB(nsIAsyncInputStream *in,
|
||||
nsIAsyncOutputStream *out,
|
||||
PRUint32 chunksize)
|
||||
: mSource(in)
|
||||
, mSink(out)
|
||||
, mChunkSize(chunksize)
|
||||
{ NS_INIT_ISUPPORTS(); }
|
||||
virtual ~nsStreamCopierOB() {}
|
||||
|
||||
static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
|
||||
void *closure,
|
||||
char *buffer,
|
||||
PRUint32 offset,
|
||||
PRUint32 count,
|
||||
PRUint32 *countRead)
|
||||
{
|
||||
nsStreamCopierOB *self = (nsStreamCopierOB *) closure;
|
||||
|
||||
nsresult rv = self->mSource->Read(buffer, count, countRead);
|
||||
if (NS_FAILED(rv))
|
||||
self->mSourceCondition = rv;
|
||||
else if (*countRead == 0)
|
||||
self->mSourceCondition = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
return self->mSourceCondition;
|
||||
}
|
||||
|
||||
// called on some random thread
|
||||
NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *in)
|
||||
{
|
||||
NS_ASSERTION(in == mSource, "unexpected stream");
|
||||
for (;;) {
|
||||
mSourceCondition = NS_OK; // reset
|
||||
PRUint32 n;
|
||||
nsresult rv = mSink->WriteSegments(FillOutputBuffer, this, mChunkSize, &n);
|
||||
if (NS_FAILED(rv) || (n == 0)) {
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mSink->AsyncWait(this, 0, nsnull);
|
||||
else if (mSourceCondition == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mSource->AsyncWait(this, 0, nsnull);
|
||||
else {
|
||||
mSource = 0;
|
||||
mSink->CloseEx(mSourceCondition);
|
||||
mSink = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// called on some random thread
|
||||
NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
{
|
||||
NS_ASSERTION(out == mSink, "unexpected stream");
|
||||
// Do all of our work from OnInputStreamReady. This
|
||||
// way we're more likely to always be working on the
|
||||
// same thread.
|
||||
return mSource->AsyncWait(this, 0, nsnull);
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAsyncInputStream> mSource;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mSink;
|
||||
PRUint32 mChunkSize;
|
||||
nsresult mSourceCondition;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsStreamCopierOB,
|
||||
nsIInputStreamNotify,
|
||||
nsIOutputStreamNotify)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_COM nsresult
|
||||
NS_AsyncCopy(nsIAsyncInputStream *source,
|
||||
nsIAsyncOutputStream *sink,
|
||||
PRBool bufferedSource,
|
||||
PRBool bufferedSink,
|
||||
PRUint32 segmentSize,
|
||||
PRUint32 segmentCount,
|
||||
nsIMemory *segmentAlloc)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// we need to insert a pipe if both the source and sink are not buffered.
|
||||
if (!bufferedSource && !bufferedSink) {
|
||||
nsCOMPtr<nsIAsyncInputStream> pipeIn;
|
||||
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
|
||||
|
||||
rv = NS_NewPipe2(getter_AddRefs(pipeIn),
|
||||
getter_AddRefs(pipeOut),
|
||||
PR_TRUE, PR_TRUE,
|
||||
segmentSize, segmentCount, segmentAlloc);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
//
|
||||
// fire off two async copies :-)
|
||||
//
|
||||
rv = NS_AsyncCopy(source, pipeOut, segmentSize, PR_FALSE, PR_TRUE);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = NS_AsyncCopy(pipeIn, sink, segmentSize, PR_TRUE, PR_FALSE);
|
||||
|
||||
// maybe calling NS_AsyncCopy twice is a bad idea!
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "uh-oh");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (bufferedSource) {
|
||||
// copy assuming ReadSegments OK
|
||||
nsStreamCopierIB *copier = new nsStreamCopierIB(source, sink, segmentSize);
|
||||
if (!copier)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(copier);
|
||||
// wait on the sink
|
||||
rv = sink->AsyncWait(copier, 0, nsnull);
|
||||
NS_RELEASE(copier);
|
||||
}
|
||||
else {
|
||||
// copy assuming WriteSegments OK
|
||||
nsStreamCopierOB *copier = new nsStreamCopierOB(source, sink, segmentSize);
|
||||
if (!copier)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(copier);
|
||||
// wait on the source since the sink is buffered and should therefore
|
||||
// already have room.
|
||||
rv = source->AsyncWait(copier, 0, nsnull);
|
||||
NS_RELEASE(copier);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsStreamUtils_h__
|
||||
#define nsStreamUtils_h__
|
||||
|
||||
#include "nscore.h"
|
||||
|
||||
class nsIAsyncInputStream;
|
||||
class nsIAsyncOutputStream;
|
||||
class nsIInputStreamNotify;
|
||||
class nsIOutputStreamNotify;
|
||||
class nsIEventQueue;
|
||||
class nsIMemory;
|
||||
|
||||
/**
|
||||
* A "one-shot" proxy of the OnInputStreamReady callback. The resulting
|
||||
* proxy object's OnInputStreamReady function may only be called once! The
|
||||
* proxy object ensures that the real notify object will be free'd on the
|
||||
* thread owning the given event queue regardless of what thread the proxy
|
||||
* object is destroyed on.
|
||||
*
|
||||
* This function is designed to be used to implement AsyncWait when the
|
||||
* aEventQ parameter is non-null.
|
||||
*/
|
||||
extern NS_COM nsresult
|
||||
NS_NewInputStreamReadyEvent(nsIInputStreamNotify **aEvent,
|
||||
nsIInputStreamNotify *aNotify,
|
||||
nsIEventQueue *aEventQ);
|
||||
|
||||
/**
|
||||
* A "one-shot" proxy of the OnOutputStreamReady callback. The resulting
|
||||
* proxy object's OnOutputStreamReady function may only be called once! The
|
||||
* proxy object ensures that the real notify object will be free'd on the
|
||||
* thread owning the given event queue regardless of what thread the proxy
|
||||
* object is destroyed on.
|
||||
*
|
||||
* This function is designed to be used to implement AsyncWait when the
|
||||
* aEventQ parameter is non-null.
|
||||
*/
|
||||
extern NS_COM nsresult
|
||||
NS_NewOutputStreamReadyEvent(nsIOutputStreamNotify **aEvent,
|
||||
nsIOutputStreamNotify *aNotify,
|
||||
nsIEventQueue *aEventQ);
|
||||
|
||||
/**
|
||||
* Asynchronously copy data from an input stream to an output stream.
|
||||
*
|
||||
* @param aSource
|
||||
* the source input stream containing the data to copy.
|
||||
* @param aSink
|
||||
* the data is copied to this output stream.
|
||||
* @param aChunkSize
|
||||
* read/write at most this many bytes at one time.
|
||||
* @param aBufferedSource
|
||||
* true if source implements ReadSegments.
|
||||
* @param aBufferedSink
|
||||
* true if sink implements WriteSegments.
|
||||
*
|
||||
* NOTE: the implementation does not rely on any event queues.
|
||||
*/
|
||||
extern NS_COM nsresult
|
||||
NS_AsyncCopy(nsIAsyncInputStream *aSource,
|
||||
nsIAsyncOutputStream *aSink,
|
||||
PRBool aBufferedSource = PR_TRUE,
|
||||
PRBool aBufferedSink = PR_TRUE,
|
||||
PRUint32 aSegmentSize = 4096,
|
||||
PRUint32 aSegmentCount = 1,
|
||||
nsIMemory *aSegmentAlloc = nsnull);
|
||||
|
||||
#endif // !nsStreamUtils_h__
|
|
@ -0,0 +1,84 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsEventQueueUtils_h__
|
||||
#define nsEventQueueUtils_h__
|
||||
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
inline nsresult
|
||||
NS_GetEventQueueService(nsIEventQueueService **result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
||||
nsCOMPtr<nsIEventQueueService> eqs = do_GetService(kEventQueueServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_ADDREF(*result = eqs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
NS_GetCurrentEventQ(nsIEventQueue **result,
|
||||
nsIEventQueueService *serv = nsnull)
|
||||
{
|
||||
nsCOMPtr<nsIEventQueueService> eqs;
|
||||
if (!serv) {
|
||||
nsresult rv = NS_GetEventQueueService(getter_AddRefs(eqs));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
serv = eqs;
|
||||
}
|
||||
return serv->GetThreadEventQueue(NS_CURRENT_THREAD, result);
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
NS_GetMainEventQ(nsIEventQueue **result,
|
||||
nsIEventQueueService *serv = nsnull)
|
||||
{
|
||||
nsCOMPtr<nsIEventQueueService> eqs;
|
||||
if (!serv) {
|
||||
nsresult rv = NS_GetEventQueueService(getter_AddRefs(eqs));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
serv = eqs;
|
||||
}
|
||||
return serv->GetThreadEventQueue(NS_UI_THREAD, result);
|
||||
}
|
||||
|
||||
#endif // !nsEventQueueUtils_h__
|
Загрузка…
Ссылка в новой задаче