Files
Mozilla/mozilla/netwerk/base/src/nsFileTransport.cpp
brade%netscape.com 0309643e19 remove unused static kFileTransportServiceCID saving 16 bytes (bug 117927; rs=darin)
git-svn-id: svn://10.0.0.236/trunk@111661 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-09 14:21:00 +00:00

1110 lines
34 KiB
C++

/* -*- Mode: C++; tab-width: 4; 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 <limits.h>
#include "nsFileTransport.h"
#include "nsFileTransportService.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsAutoLock.h"
#include "netCore.h"
#include "nsIFileStreams.h"
#include "nsCOMPtr.h"
#include "nsReadableUtils.h"
#include "nsIProxyObjectManager.h"
#include "nsNetUtil.h"
static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID);
#define NS_OUTPUT_STREAM_BUFFER_SIZE (64 * 1024)
//////////////////////////////////////////////////////////////////////////////////
#if defined(PR_LOGGING)
static PRLogModuleInfo *gFileTransportLog = nsnull;
#endif
#define LOG(args) PR_LOG(gFileTransportLog, PR_LOG_DEBUG, args)
//////////////////////////////////////////////////////////////////////////////////
//
// An nsFileTransportSourceWrapper captures the number of bytes read from an
// input stream.
//
class nsFileTransportSourceWrapper : public nsIInputStream
{
public:
NS_DECL_ISUPPORTS
nsFileTransportSourceWrapper()
: mBytesRead(0), mLastError(NS_OK) { NS_INIT_ISUPPORTS(); }
virtual ~nsFileTransportSourceWrapper() {}
//
// nsIInputStream implementation...
//
NS_IMETHOD Close() {
return mSource->Close();
}
NS_IMETHOD Available(PRUint32 *aCount) {
return mSource->Available(aCount);
}
NS_IMETHOD Read(char *aBuf, PRUint32 aCount, PRUint32 *aBytesRead) {
nsresult rv = mSource->Read(aBuf, aCount, aBytesRead);
if (NS_SUCCEEDED(rv))
mBytesRead += *aBytesRead;
mLastError = rv;
return rv;
}
NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
PRUint32 aCount, PRUint32 *aBytesRead) {
nsresult rv = mSource->ReadSegments(aWriter, aClosure, aCount, aBytesRead);
if (NS_SUCCEEDED(rv))
mBytesRead += *aBytesRead;
mLastError = rv;
return rv;
}
NS_IMETHOD GetNonBlocking(PRBool *aValue) {
return mSource->GetNonBlocking(aValue);
}
NS_IMETHOD GetObserver(nsIInputStreamObserver **aObserver) {
return mSource->GetObserver(aObserver);
}
NS_IMETHOD SetObserver(nsIInputStreamObserver *aObserver) {
return mSource->SetObserver(aObserver);
}
//
// Helper functions
//
void SetSource(nsIInputStream *aSource) {
mSource = aSource;
}
PRUint32 GetBytesRead() {
return mBytesRead;
}
nsresult GetLastError() {
return mLastError;
}
protected:
//
// State variables
//
PRUint32 mBytesRead;
nsCOMPtr<nsIInputStream> mSource;
nsresult mLastError;
};
// This must be threadsafe since different threads can run the same transport
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileTransportSourceWrapper, nsIInputStream)
//////////////////////////////////////////////////////////////////////////////////
//
// An nsFileTransportSinkWrapper captures the number of bytes written to an
// output stream.
//
class nsFileTransportSinkWrapper : public nsIOutputStream
{
public:
NS_DECL_ISUPPORTS
nsFileTransportSinkWrapper()
: mBytesWritten(0), mLastError(NS_OK) { NS_INIT_ISUPPORTS(); }
virtual ~nsFileTransportSinkWrapper() {}
//
// nsIInputStream implementation...
//
NS_IMETHOD Close() {
return mSink->Close();
}
NS_IMETHOD Flush() {
return mSink->Flush();
}
NS_IMETHOD Write(const char *aBuf, PRUint32 aCount, PRUint32 *aBytesWritten) {
nsresult rv = mSink->Write(aBuf, aCount, aBytesWritten);
if (NS_SUCCEEDED(rv))
mBytesWritten += *aBytesWritten;
mLastError = rv;
return rv;
}
NS_IMETHOD WriteFrom(nsIInputStream *aSource, PRUint32 aCount, PRUint32 *aBytesWritten) {
nsresult rv = mSink->WriteFrom(aSource, aCount, aBytesWritten);
if (NS_SUCCEEDED(rv))
mBytesWritten += *aBytesWritten;
mLastError = rv;
return rv;
}
NS_IMETHOD WriteSegments(nsReadSegmentFun aReader, void *aClosure,
PRUint32 aCount, PRUint32 *aBytesWritten) {
nsresult rv = mSink->WriteSegments(aReader, aClosure, aCount, aBytesWritten);
if (NS_SUCCEEDED(rv))
mBytesWritten += *aBytesWritten;
mLastError = rv;
return rv;
}
NS_IMETHOD GetNonBlocking(PRBool *aValue) {
return mSink->GetNonBlocking(aValue);
}
NS_IMETHOD SetNonBlocking(PRBool aValue) {
return mSink->SetNonBlocking(aValue);
}
NS_IMETHOD GetObserver(nsIOutputStreamObserver **aObserver) {
return mSink->GetObserver(aObserver);
}
NS_IMETHOD SetObserver(nsIOutputStreamObserver *aObserver) {
return mSink->SetObserver(aObserver);
}
//
// Helper functions
//
void SetSink(nsIOutputStream *aSink) {
mSink = aSink;
}
PRUint32 GetBytesWritten() {
return mBytesWritten;
}
nsresult GetLastError() {
return mLastError;
}
protected:
//
// State variables
//
PRUint32 mBytesWritten;
nsCOMPtr<nsIOutputStream> mSink;
nsresult mLastError;
};
// This must be threadsafe since different threads can run the same transport
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileTransportSinkWrapper, nsIOutputStream)
//////////////////////////////////////////////////////////////////////////////////
nsFileTransport::nsFileTransport()
: mContentType(nsnull),
mBufferSegmentSize(NS_FILE_TRANSPORT_DEFAULT_SEGMENT_SIZE),
mBufferMaxSize(NS_FILE_TRANSPORT_DEFAULT_BUFFER_SIZE),
mXferState(CLOSED),
mRunState(RUNNING),
mCancelStatus(NS_OK),
mSuspendCount(0),
mLock(nsnull),
mActive(PR_FALSE),
mCloseStreamWhenDone(PR_TRUE),
mStatus(NS_OK),
mOffset(0),
mTotalAmount(-1),
mTransferAmount(-1),
mSourceWrapper(nsnull),
mSinkWrapper(nsnull),
mService(nsnull)
{
NS_INIT_ISUPPORTS();
#if defined(PR_LOGGING)
if (!gFileTransportLog)
gFileTransportLog = PR_NewLogModule("nsFileTransport");
#endif
}
nsresult
nsFileTransport::Init(nsFileTransportService *aService, nsIFile* file, PRInt32 ioFlags, PRInt32 perm)
{
nsresult rv;
nsCOMPtr<nsIFileIO> io;
rv = NS_NewFileIO(getter_AddRefs(io), file, ioFlags, perm);
if (NS_FAILED(rv)) return rv;
return Init(aService, io);
}
nsresult
nsFileTransport::Init(nsFileTransportService *aService, const char* name, nsIInputStream* inStr,
const char* contentType, PRInt32 contentLength, PRBool closeStreamWhenDone)
{
nsresult rv;
nsCOMPtr<nsIInputStreamIO> io;
rv = NS_NewInputStreamIO(getter_AddRefs(io),
name, inStr, contentType, contentLength);
if (NS_FAILED(rv)) return rv;
mCloseStreamWhenDone = closeStreamWhenDone;
return Init(aService, io);
}
nsresult
nsFileTransport::Init(nsFileTransportService *aService, nsIStreamIO* io)
{
nsresult rv = NS_OK;
if (mLock == nsnull) {
mLock = PR_NewLock();
if (mLock == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
}
mStreamIO = io;
nsXPIDLCString name;
rv = mStreamIO->GetName(getter_Copies(name));
mStreamName = NS_STATIC_CAST(const char*, name);
NS_ASSERTION(NS_SUCCEEDED(rv), "GetName failed");
NS_ADDREF(mService = aService);
PR_AtomicIncrement(&mService->mTotalTransports);
return rv;
}
nsFileTransport::~nsFileTransport()
{
if (mXferState != CLOSED)
DoClose();
NS_ASSERTION(mSourceWrapper == nsnull, "transport not closed");
NS_ASSERTION(mSink == nsnull, "transport not closed");
NS_ASSERTION(mSinkWrapper == nsnull, "transport not closed");
if (mLock) {
PR_DestroyLock(mLock);
mLock = nsnull;
}
if (mContentType) {
nsCRT::free(mContentType);
mContentType = nsnull;
}
NS_IF_RELEASE(mService);
}
NS_IMPL_THREADSAFE_ISUPPORTS4(nsFileTransport,
nsITransport,
nsITransportRequest,
nsIRequest,
nsIRunnable)
NS_METHOD
nsFileTransport::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
{
nsFileTransport* fc = new nsFileTransport();
if (fc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(fc);
nsresult rv = fc->QueryInterface(aIID, aResult);
NS_RELEASE(fc);
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// From nsIRequest
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsFileTransport::GetName(PRUnichar* *result)
{
*result = ToNewUnicode(mStreamName);
return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsFileTransport::IsPending(PRBool *result)
{
*result = mXferState != CLOSED;
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::GetStatus(nsresult *status)
{
*status = mRunState == CANCELED ? mCancelStatus : mStatus;
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::Cancel(nsresult status)
{
nsresult rv = NS_OK;
nsAutoLock lock(mLock);
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
mCancelStatus = status;
// Only dispatch a new thread, if there isn't one currently active.
if (!mActive) {
#ifdef TIMING
mStartTime = PR_IntervalNow();
#endif
rv = mService->DispatchRequest(this);
}
LOG(("nsFileTransport: Cancel [this=%x %s]\n", this, mStreamName.get()));
return rv;
}
NS_IMETHODIMP
nsFileTransport::Suspend()
{
nsAutoLock lock(mLock);
if (mRunState != CANCELED) {
LOG(("nsFileTransport: Suspend [this=%x %s]\n", this, mStreamName.get()));
PR_AtomicIncrement(&mSuspendCount);
mService->AddSuspendedTransport(this);
}
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::Resume()
{
nsAutoLock lock(mLock);
if (mRunState != CANCELED) {
LOG(("nsFileTransport: Resume [this=%x %s]\n", this, mStreamName.get()));
// Allow negative suspend count
PR_AtomicDecrement(&mSuspendCount);
mService->RemoveSuspendedTransport(this);
// Only dispatch a new thread, if there isn't one currently active.
if (!mActive && (mSuspendCount == 0)) {
mRunState = RUNNING;
#ifdef TIMING
mStartTime = PR_IntervalNow();
#endif
mStatus = mService->DispatchRequest(this);
}
}
else
LOG(("nsFileTransport: Resume ignored [this=%x %s] status=%x cancelstatus=%x\n",
this, mStreamName.get(), mStatus, mCancelStatus));
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::GetLoadGroup(nsILoadGroup **loadGroup)
{
*loadGroup = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::SetLoadGroup(nsILoadGroup *loadGroup)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFileTransport::GetLoadFlags(nsLoadFlags *loadFlags)
{
*loadFlags = nsIRequest::LOAD_NORMAL;
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::SetLoadFlags(nsLoadFlags loadFlags)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////////////
// From nsITransportRequest
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsFileTransport::GetTransport(nsITransport **result)
{
NS_ENSURE_ARG_POINTER(result);
NS_ADDREF(*result = this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// From nsITransport
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsFileTransport::OpenInputStream(PRUint32 aTransferOffset,
PRUint32 aTransferCount,
PRUint32 aFlags,
nsIInputStream **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
NS_ASSERTION(aTransferCount == PRUint32(-1), "need to wrap input stream in one that truncates");
nsresult rv = mStreamIO->GetInputStream(aResult);
if (NS_SUCCEEDED(rv) && aTransferOffset) {
nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(*aResult, &rv));
if (NS_SUCCEEDED(rv) && seekable) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, aTransferOffset);
}
}
return rv;
}
NS_IMETHODIMP
nsFileTransport::OpenOutputStream(PRUint32 aTransferOffset,
PRUint32 aTransferCount,
PRUint32 aFlags,
nsIOutputStream **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
NS_ASSERTION(aTransferCount == PRUint32(-1), "need to wrap output stream in one that truncates");
nsresult rv = mStreamIO->GetOutputStream(aResult);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(*aResult, &rv));
if (seekable) {
if (aTransferOffset)
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, aTransferOffset);
if (NS_SUCCEEDED(rv))
rv = seekable->SetEOF();
}
// a stream need not support nsISeekableStream unless a transfer offset
// greater than zero was requested.
else if (aTransferOffset == 0)
rv = NS_OK;
return rv;
}
NS_IMETHODIMP
nsFileTransport::AsyncRead(nsIStreamListener *aListener,
nsISupports *aContext,
PRUint32 aTransferOffset,
PRUint32 aTransferCount,
PRUint32 aFlags,
nsIRequest **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
nsresult rv = NS_OK;
if (mXferState != CLOSED)
return NS_ERROR_IN_PROGRESS;
NS_ASSERTION(aListener, "need to supply an nsIStreamListener");
rv = NS_NewStreamListenerProxy(getter_AddRefs(mListener),
aListener, nsnull,
mBufferSegmentSize,
mBufferMaxSize);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(mContext == nsnull, "context not released");
mContext = aContext;
mOffset = aTransferOffset;
mTransferAmount = aTransferCount;
mXferState = OPEN_FOR_READ;
LOG(("nsFileTransport: AsyncRead [this=%x %s] mOffset=%d mTransferAmount=%d\n",
this, mStreamName.get(), mOffset, mTransferAmount));
#ifdef TIMING
mStartTime = PR_IntervalNow();
#endif
rv = mService->DispatchRequest(this);
if (NS_FAILED(rv)) return rv;
NS_ADDREF(*aResult = this);
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::AsyncWrite(nsIStreamProvider *aProvider,
nsISupports *aContext,
PRUint32 aTransferOffset,
PRUint32 aTransferCount,
PRUint32 aFlags,
nsIRequest **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
nsresult rv = NS_OK;
LOG(("nsFileTransport: AsyncWrite [this=%x, provider=%x]\n",
this, aProvider));
if (mXferState != CLOSED)
return NS_ERROR_IN_PROGRESS;
NS_ASSERTION(aProvider, "need to supply an nsIStreamProvider");
rv = NS_NewStreamProviderProxy(getter_AddRefs(mProvider),
aProvider, nsnull,
mBufferSegmentSize,
mBufferMaxSize);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(mContext == nsnull, "context not released");
mContext = aContext;
mOffset = aTransferOffset;
mTransferAmount = aTransferCount;
mXferState = OPEN_FOR_WRITE;
LOG(("nsFileTransport: AsyncWrite [this=%x %s] mOffset=%d mTransferAmount=%d\n",
this, mStreamName.get(), mOffset, mTransferAmount));
#ifdef TIMING
mStartTime = PR_IntervalNow();
#endif
rv = mService->DispatchRequest(this);
if (NS_FAILED(rv)) return rv;
NS_ADDREF(*aResult = this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIRunnable methods:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsFileTransport::Run(void)
{
PR_Lock(mLock);
mActive = PR_TRUE;
LOG(("nsFileTransport: Inside Run\n"));
#ifdef TIMING
PRIntervalTime now = PR_IntervalNow();
#ifdef DEBUG
printf("nsFileTransport: latency=%u ticks\n", now - mStartTime);
#endif
#endif
if (mRunState == SUSPENDED && NS_FAILED(mCancelStatus))
mRunState = CANCELED;
while (mXferState != CLOSED && mRunState != SUSPENDED) {
//
// Change transfer state if canceled.
//
if (mRunState == CANCELED) {
if (mXferState == OPEN_FOR_READ ||
mXferState == START_READ ||
mXferState == READING ||
mXferState == END_READ)
mXferState = END_READ;
else if (mXferState == OPEN_FOR_WRITE ||
mXferState == START_WRITE ||
mXferState == WRITING ||
mXferState == END_WRITE)
mXferState = END_WRITE;
else
mXferState = CLOSING;
mStatus = mCancelStatus;
}
// Grab a local reference to mProgressSink
nsCOMPtr<nsIProgressEventSink> progressSink(mProgressSink);
//
// While processing, we allow Suspend, Resume, and Cancel.
//
PR_Unlock(mLock);
Process(progressSink);
PR_Lock(mLock);
//
// Were we canceled ?
//
if (NS_FAILED(mCancelStatus))
mRunState = CANCELED;
//
// Were we suspended ?
//
else if (mSuspendCount > 0) {
mRunState = SUSPENDED;
mService->AddSuspendedTransport(this);
}
}
LOG(("nsFileTransport: Leaving Run [xferState=%d runState=%d]\n",
mXferState, mRunState));
mActive = PR_FALSE;
PR_Unlock(mLock);
return NS_OK;
}
void
nsFileTransport::Process(nsIProgressEventSink *progressSink)
{
LOG(("nsFileTransport: Inside Process [this=%x state=%x status=%x]\n",
this, mXferState, mStatus));
switch (mXferState) {
case OPEN_FOR_READ: {
mStatus = mStreamIO->Open(&mContentType, &mTotalAmount);
LOG(("nsFileTransport: OPEN_FOR_READ [this=%x %s] status=%x\n", this, mStreamName.get(), mStatus));
if (mListener) {
nsresult rv = mListener->OnStartRequest(this, mContext); // always send the start notification
if (NS_SUCCEEDED(mStatus))
mStatus = rv;
}
PR_AtomicIncrement(&mService->mInUseTransports);
mXferState = NS_FAILED(mStatus) ? END_READ : START_READ;
break;
}
case START_READ: {
LOG(("nsFileTransport: START_READ [this=%x %s]\n", this, mStreamName.get()));
nsCOMPtr<nsIInputStream> source;
mStatus = mStreamIO->GetInputStream(getter_AddRefs(source));
if (NS_FAILED(mStatus)) {
LOG(("nsFileTransport: mStreamIO->GetInputStream() failed [this=%x rv=%x]\n",
this, mStatus));
mXferState = END_READ;
return;
}
if (mOffset > 0) {
// if we need to set a starting offset, QI for the nsISeekableStream
// and set it
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(source, &mStatus);
if (NS_SUCCEEDED(mStatus)) {
// for now, assume the offset is always relative to the start of the
// file (position 0) so use PR_SEEK_SET
mStatus = seekable->Seek(PR_SEEK_SET, mOffset);
}
if (NS_FAILED(mStatus)) {
mXferState = END_READ;
return;
}
}
if (!mSourceWrapper) {
//
// Allocate an input stream wrapper to capture the number of bytes
// read from source.
//
NS_NEWXPCOM(mSourceWrapper, nsFileTransportSourceWrapper);
if (!mSourceWrapper) {
mStatus = NS_ERROR_OUT_OF_MEMORY;
mXferState = END_READ;
return;
}
NS_ADDREF(mSourceWrapper);
mSourceWrapper->SetSource(source);
}
// capture the total amount for progress information
if (mTransferAmount < 0) {
mTransferAmount = mTotalAmount;
} else
mTotalAmount = mTransferAmount;
mXferState = READING;
break;
}
case READING: {
// Read at most mBufferMaxSize.
PRInt32 transferAmt = mBufferMaxSize;
if (mTransferAmount >= 0)
transferAmt = PR_MIN(transferAmt, mTransferAmount);
LOG(("nsFileTransport: READING [this=%x %s] transferAmt=%u mBufferMaxSize=%u\n",
this, mStreamName.get(), transferAmt, mBufferMaxSize));
PRUint32 total = 0, offset = mSourceWrapper->GetBytesRead();
// Give the listener a chance to read at most transferAmt bytes from
// the source input stream.
nsresult status = mListener->OnDataAvailable(this, mContext,
mSourceWrapper,
mOffset, transferAmt);
if (NS_SUCCEEDED(status)) {
// Find out what was read.
total = mSourceWrapper->GetBytesRead() - offset;
LOG(("status = %x total = %d\n", status, total));
if (total == 0) {
status = mSourceWrapper->GetLastError();
if (NS_SUCCEEDED(status)) { // eof
LOG(("eof\n"));
status = NS_BASE_STREAM_CLOSED;
}
}
else if (mTransferAmount > 0) {
mTransferAmount -= total;
if (mTransferAmount == 0) {
LOG(("mTransferAmount == 0\n"));
status = NS_BASE_STREAM_CLOSED;
}
}
}
// Handle the various return codes.
if (status == NS_BASE_STREAM_WOULD_BLOCK) {
LOG(("nsFileTransport: READING [this=%x %s] listener would block; suspending self.\n",
this, mStreamName.get()));
mStatus = NS_OK;
PR_AtomicIncrement(&mSuspendCount);
}
else if (status == NS_BASE_STREAM_CLOSED) {
LOG(("nsFileTransport: READING [this=%x %s] done reading file.\n",
this, mStreamName.get()));
mStatus = NS_OK;
mXferState = END_READ;
}
else if (NS_FAILED(status)) {
LOG(("nsFileTransport: READING [this=%x %s] error reading file.\n",
this, mStreamName.get()));
mStatus = status;
mXferState = END_READ;
}
if (total) {
// something was read...
offset += total;
mOffset += total;
LOG(("nsFileTransport: READING [this=%x %s] read %u bytes [offset=%u]\n",
this, mStreamName.get(), total, mOffset));
if (progressSink)
progressSink->OnProgress(this, mContext, offset,
PR_MAX(mTotalAmount, 0));
}
break;
}
case END_READ: {
LOG(("nsFileTransport: END_READ [this=%x %s] status=%x\n",
this, mStreamName.get(), mStatus));
PR_AtomicDecrement(&mService->mInUseTransports);
#if DEBUG
if (NS_FAILED(mStatus))
printf("Error reading file %s\n", mStreamName.get());
#endif
#if defined (DEBUG_dougt) || defined (DEBUG_warren) || defined (DEBUG_bienvenu)
NS_ASSERTION(mTransferAmount <= 0 || NS_FAILED(mStatus), "didn't transfer all the data");
#endif
if (mTransferAmount > 0 && NS_SUCCEEDED(mStatus)) {
//
// This happens when the requested read amount is more than the amount
// of the data in the stream/file, or if the listener returned
// NS_BASE_STREAM_CLOSED.
//
mStatus = NS_BASE_STREAM_CLOSED;
}
// need to close before calling OnStopRequest, in case the listener
// is reusing the stream.
mXferState = CLOSING;
DoClose();
nsCOMPtr <nsISupports> saveContext = mContext;
nsCOMPtr <nsIStreamListener> saveListener = mListener;
mListener = nsnull;
mContext = nsnull;
// close the data source
NS_IF_RELEASE(mSourceWrapper);
mSourceWrapper = nsnull;
if (saveListener) {
saveListener->OnStopRequest(this, saveContext, mStatus);
saveListener = 0;
}
if (progressSink) {
progressSink->OnStatus(this, saveContext,
NS_NET_STATUS_READ_FROM,
NS_ConvertASCIItoUCS2(mStreamName).get());
}
break;
}
case OPEN_FOR_WRITE: {
LOG(("nsFileTransport: OPEN_FOR_WRITE [this=%x %s]\n",
this, mStreamName.get()));
mStatus = mStreamIO->Open(&mContentType, &mTotalAmount);
if (mStatus == NS_ERROR_FILE_NOT_FOUND)
mStatus = NS_OK;
if (mProvider) {
// always send the start notification
nsresult rv = mProvider->OnStartRequest(this, mContext);
if (NS_SUCCEEDED(mStatus))
mStatus = rv;
}
PR_AtomicIncrement(&mService->mInUseTransports);
mXferState = NS_FAILED(mStatus) ? END_WRITE : START_WRITE;
break;
}
case START_WRITE: {
LOG(("nsFileTransport: START_WRITE [this=%x %s]\n",
this, mStreamName.get()));
mStatus = mStreamIO->GetOutputStream(getter_AddRefs(mSink));
if (NS_FAILED(mStatus)) {
mXferState = END_WRITE;
return;
}
// QI to a seekable stream so we can set EOF and Seek (if required)
nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mSink, &mStatus));
if (seekable) {
if (mOffset)
mStatus = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
if (NS_SUCCEEDED(mStatus))
mStatus = seekable->SetEOF();
}
// a stream need not support nsISeekableStream unless a transfer offset
// greater than zero was requested.
else if (mOffset == 0)
mStatus = NS_OK;
if (NS_FAILED(mStatus)) {
mXferState = END_WRITE;
return;
}
mOffset = 0;
if (!mSinkWrapper) {
//
// Allocate an output stream wrapper to capture the number of bytes
// written to mSink.
//
NS_NEWXPCOM(mSinkWrapper, nsFileTransportSinkWrapper);
if (!mSinkWrapper) {
mStatus = NS_ERROR_OUT_OF_MEMORY;
mXferState = END_WRITE;
return;
}
NS_ADDREF(mSinkWrapper);
mSinkWrapper->SetSink(mSink);
}
mXferState = WRITING;
break;
}
case WRITING: {
// Write at most mBufferMaxSize
PRUint32 transferAmt = mBufferMaxSize;
if (mTransferAmount >= 0)
transferAmt = PR_MIN(transferAmt, (PRUint32)mTransferAmount);
PRUint32 total, offset = mSinkWrapper->GetBytesWritten();
// Ask the provider for data
nsresult status = mProvider->OnDataWritable(this, mContext,
mSinkWrapper,
mOffset, transferAmt);
if (NS_SUCCEEDED(status)) {
// Find out what was written.
total = mSinkWrapper->GetBytesWritten() - offset;
if (total == 0) {
status = mSinkWrapper->GetLastError();
if (NS_SUCCEEDED(status)) // eof
status = NS_BASE_STREAM_CLOSED;
}
else if (mTransferAmount > 0) {
mTransferAmount -= total;
if (mTransferAmount == 0)
status = NS_BASE_STREAM_CLOSED;
}
}
// Handle the various return codes.
if (status == NS_BASE_STREAM_WOULD_BLOCK) {
LOG(("nsFileTransport: WRITING [this=%x %s] provider would block; suspending self.\n",
this, mStreamName.get()));
mStatus = NS_OK;
PR_AtomicIncrement(&mSuspendCount);
}
else if (status == NS_BASE_STREAM_CLOSED) {
LOG(("nsFileTransport: WRITING [this=%x %s] no more data to be written.\n",
this, mStreamName.get()));
mStatus = NS_OK;
mXferState = END_WRITE;
}
else if (NS_FAILED(status)) {
LOG(("nsFileTransport: WRITING [this=%x %s] provider failed.\n",
this, mStreamName.get()));
mStatus = status;
mXferState = END_WRITE;
}
else {
// something was written...
offset += total;
mOffset += total;
LOG(("nsFileTransport: WRITING [this=%x %s] wrote %u bytes [offset=%u]\n",
this, mStreamName.get(), total, mOffset));
if (progressSink)
progressSink->OnProgress(this, mContext, offset,
PR_MAX(mTotalAmount, 0));
}
break;
}
case END_WRITE: {
LOG(("nsFileTransport: END_WRITE [this=%x %s] status=%x\n",
this, mStreamName.get(), mStatus));
PR_AtomicDecrement(&mService->mInUseTransports);
#if defined (DEBUG_dougt) || defined (DEBUG_warren)
NS_ASSERTION(mTransferAmount <= 0 || NS_FAILED(mStatus), "didn't transfer all the data");
#endif
if (mTransferAmount > 0 && NS_SUCCEEDED(mStatus)) {
// This happens when the requested write amount is more than the amount
// of the data in the stream/file.
mStatus = NS_BASE_STREAM_CLOSED;
}
if (mSink) {
mSink->Flush();
mSink = 0;
}
NS_IF_RELEASE(mSinkWrapper);
mSinkWrapper = nsnull;
if (mProvider) {
mProvider->OnStopRequest(this, mContext, mStatus);
mProvider = 0;
}
if (progressSink)
progressSink->OnStatus(this, mContext,
NS_NET_STATUS_WROTE_TO,
NS_ConvertASCIItoUCS2(mStreamName).get());
mContext = 0;
mXferState = CLOSING;
break;
}
case CLOSING: {
DoClose();
break;
}
case CLOSED: {
NS_NOTREACHED("trying to continue a quiescent file transfer");
break;
}
}
LOG(("nsFileTransport: Leaving Process [this=%x state=%x status=%x]\n",
this, mXferState, mStatus));
}
void
nsFileTransport::DoClose(void)
{
if (mCloseStreamWhenDone)
{
LOG(("nsFileTransport: CLOSING [this=%x %s] status=%x\n",
this, mStreamName.get(), mStatus));
// XXX closing the stream io prevents this file transport from being reused
if (mStreamIO) {
nsresult rv = NS_OK;
rv = mStreamIO->Close(mStatus);
NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected Close failure");
mStreamIO = 0;
}
}
mXferState = CLOSED;
PR_AtomicDecrement(&mService->mConnectedTransports);
}
NS_IMETHODIMP
nsFileTransport::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
{
NS_ENSURE_ARG_POINTER(aCallbacks);
*aCallbacks = mNotificationCallbacks;
NS_IF_ADDREF(*aCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsFileTransport::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks,
PRUint32 aFlags)
{
nsAutoLock lock(mLock);
mNotificationCallbacks = aCallbacks;
mProgressSink = 0;
if (!mNotificationCallbacks || (aFlags & DONT_REPORT_PROGRESS))
return NS_OK;
nsCOMPtr<nsIProgressEventSink> sink(do_GetInterface(mNotificationCallbacks));
if (!sink) return NS_OK;
if (aFlags & DONT_PROXY_PROGRESS) {
mProgressSink = sink;
return NS_OK;
}
// Otherwise, generate a proxied event sink
nsresult rv;
nsCOMPtr<nsIProxyObjectManager> proxyMgr =
do_GetService(kProxyObjectManagerCID, &rv);
if (NS_FAILED(rv)) return rv;
return proxyMgr->GetProxyForObject(NS_CURRENT_EVENTQ, // calling thread
NS_GET_IID(nsIProgressEventSink),
sink,
PROXY_ASYNC | PROXY_ALWAYS,
getter_AddRefs(mProgressSink));
}
////////////////////////////////////////////////////////////////////////////////