new files for bug 176919 "async streams" r=dougt,gordon sr=sspitzer a=valeski,asa

This commit is contained in:
darin%netscape.com 2003-01-18 01:27:53 +00:00
Родитель 135161218a
Коммит 2347ef8985
30 изменённых файлов: 9333 добавлений и 0 удалений

Просмотреть файл

@ -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);
};

1181
xpcom/io/nsPipe3.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

445
xpcom/io/nsStreamUtils.cpp Normal file
Просмотреть файл

@ -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;
}

105
xpcom/io/nsStreamUtils.h Normal file
Просмотреть файл

@ -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__