Landing of FTP branch. Highlights include (a) ftp is now async, (b) removal of threadpool, (c) better caching of control socket. See posting on netlib newsgroup for details. reviewers include valeski@netscape.com, gordon@netscape.com. Superreveiwer is rpotts@netscape.com. Bugs fixed by this bug include 61678.

git-svn-id: svn://10.0.0.236/trunk@84751 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
dougt%netscape.com 2001-01-10 23:45:04 +00:00
parent cf0982bf33
commit bf9dd9e284
18 changed files with 5034 additions and 1030 deletions

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,6 @@ MODULE = necko
XPIDL_MODULE = necko_ftp
XPIDLSRCS = \
nsIConnectionCache.idl \
nsIFTPChannel.idl \
$(NULL)

View File

@ -46,4 +46,19 @@
#define NS_ERROR_FTP_MKDIR \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 26)
/**
* Status nsresult codes: used with nsINotification objects
*/
#define NS_NET_STATUS_BEGIN_FTP_TRANSACTION \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 27)
#define NS_NET_STATUS_END_FTP_TRANSACTION \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 28)
#endif // __ftpCore_h___

View File

@ -27,7 +27,6 @@ include <$(DEPTH)/config/config.mak>
XPIDL_MODULE = necko_ftp
XPIDLSRCS = \
.\nsIConnectionCache.idl \
.\nsIFTPChannel.idl \
$(NULL)

View File

@ -1,37 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
%{C++
class nsConnectionCacheObj;
%}
[ptr] native nsConnectionCacheObj(nsConnectionCacheObj);
[uuid(D40BB480-8709-11d3-A184-0050041CAF44)]
interface nsIConnectionCache : nsISupports
{
nsConnectionCacheObj RemoveConn(in string aKey);
void InsertConn(in string aKey, in nsConnectionCacheObj aConn);
};

View File

@ -22,8 +22,6 @@
#include "nsIChannel.idl"
interface nsIEventQueue;
// this interface provides a way for the FTP connection to
// synchronize with the owning channel.
@ -35,12 +33,7 @@ interface nsIFTPChannel : nsIChannel
};
%{C++
/**
* Status nsresult codes: used with nsINotification objects
*/
#define NS_NET_STATUS_BEGIN_FTP_TRANSACTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 27)
#define NS_NET_STATUS_END_FTP_TRANSACTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 28)
#include "ftpCore.h"
%}

View File

@ -34,7 +34,7 @@ CPPSRCS = \
nsFtpProtocolHandler.cpp \
nsFTPChannel.cpp \
nsFtpConnectionThread.cpp \
nsAsyncEvent.cpp \
nsFtpControlConnection.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a

View File

@ -33,7 +33,7 @@ CPP_OBJS = \
.\$(OBJDIR)\nsFtpProtocolHandler.obj \
.\$(OBJDIR)\nsFTPChannel.obj \
.\$(OBJDIR)\nsFtpConnectionThread.obj \
.\$(OBJDIR)\nsAsyncEvent.obj \
.\$(OBJDIR)\nsFtpControlConnection.obj \
$(NULL)
include <$(DEPTH)\config\rules.mak>
@ -43,3 +43,10 @@ install:: $(LIBRARY)
clobber::
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib
reallybuild::
nmake -f makefile.win
cd ..\..\..\build2
nmake -f makefile.win
cd ..\protocol\ftp\src

View File

@ -1,64 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef __nsconnectioncacheobj__h____
#define __nsconnectioncacheobj__h____
#include "nsCOMPtr.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsString.h"
// This class represents a cached connection for FTP connections.
// State pertinent to re-establishing an FTP "connection" is stored
// here between FTP URL loads.
//
// The cache object string key has the following syntax
// "HostPort" NOTE: no seperators.
class nsConnectionCacheObj {
public:
nsConnectionCacheObj(nsIChannel *aChannel,
nsIInputStream *aInputStream,
nsIOutputStream *aOutputStream)
{
MOZ_COUNT_CTOR(nsConnectionCacheObj);
mSocketTransport = aChannel;
mInputStream = aInputStream;
mOutputStream = aOutputStream;
mServerType = 0;
mList = PR_FALSE;
}
~nsConnectionCacheObj() {
MOZ_COUNT_DTOR(nsConnectionCacheObj);
}
nsCOMPtr<nsIChannel> mSocketTransport; // the connection
nsCOMPtr<nsIInputStream> mInputStream; // to read from server
nsCOMPtr<nsIOutputStream> mOutputStream; // to write to server
PRUint32 mServerType; // what kind of server is it.
nsCAutoString mCwd; // what dir are we in
PRBool mList; // are we sending LIST or NLST
nsAutoString mPassword;
};
#endif // __nsconnectioncacheobj__h____

View File

@ -20,7 +20,6 @@
* Contributor(s):
*/
// ftp implementation
#include "nsFTPChannel.h"
#include "nsIStreamListener.h"
@ -42,14 +41,14 @@ extern PRLogModuleInfo* gFTPLog;
// connection made and is used to negotiate the second, data, channel.
// The data channel is driven by the command channel and is either
// initiated by the server (PORT command) or by the client (PASV command).
// Client initiation is the most command case and is attempted first.
// Client initiation is the most common case and is attempted first.
nsFTPChannel::nsFTPChannel()
: mConnected(PR_FALSE),
mLoadAttributes(LOAD_NORMAL),
: mLoadAttributes(LOAD_NORMAL),
mSourceOffset(0),
mAmount(0),
mContentLength(-1),
mFTPState(nsnull),
mBufferSegmentSize(0),
mBufferMaxSize(0),
mLock(nsnull),
@ -62,9 +61,12 @@ nsFTPChannel::nsFTPChannel()
nsFTPChannel::~nsFTPChannel()
{
#if defined(PR_LOGGING)
nsXPIDLCString spec;
mURL->GetSpec(getter_Copies(spec));
PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("~nsFTPChannel() for %s", (const char*)spec));
#endif
NS_IF_RELEASE(mFTPState);
if (mLock) PR_DestroyLock(mLock);
}
@ -79,36 +81,26 @@ NS_IMPL_THREADSAFE_ISUPPORTS8(nsFTPChannel,
nsIStreamObserver);
nsresult
nsFTPChannel::Init(nsIURI* uri,
nsIProtocolHandler* aHandler,
nsIThreadPool* aPool)
nsFTPChannel::Init(nsIURI* uri)
{
nsresult rv = NS_OK;
if (mConnected) {
PR_LOG(gFTPLog, PR_LOG_DEBUG, ("ERROR nsFTPChannel:Init called while connected.\n"));
return NS_ERROR_ALREADY_CONNECTED;
}
// setup channel state
mHandler = aHandler;
NS_ASSERTION(aPool, "FTP channel needs a thread pool to play in");
if (!aPool) return NS_ERROR_NULL_POINTER;
mPool = aPool;
mURL = uri;
rv = mURL->GetHost(getter_Copies(mHost));
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(!mLock, "Init should only be called once on a channel");
mLock = PR_NewLock();
if (!mLock) return NS_ERROR_OUT_OF_MEMORY;
return rv;
if (!mLock) {
mLock = PR_NewLock();
if (!mLock) return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsFTPChannel::SetProxyChannel(nsIChannel *aChannel) {
if (mConnected) return NS_ERROR_ALREADY_CONNECTED;
nsFTPChannel::SetProxyChannel(nsIChannel *aChannel)
{
mProxyChannel = aChannel;
return NS_OK;
}
@ -168,16 +160,12 @@ nsFTPChannel::GetStatus(nsresult *status)
NS_IMETHODIMP
nsFTPChannel::Cancel(nsresult status) {
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
nsresult rv;
nsAutoLock lock(mLock);
mStatus = status;
if (mProxyChannel) {
return mProxyChannel->Cancel(status);
} else if (mConnThread) {
rv = mConnThread->Cancel(status);
mConnThread = nsnull;
mConnected = PR_FALSE;
return rv;
} else if (mFTPState) {
return mFTPState->Cancel(status);
}
return NS_OK;
}
@ -187,8 +175,8 @@ nsFTPChannel::Suspend(void) {
nsAutoLock lock(mLock);
if (mProxyChannel) {
return mProxyChannel->Suspend();
} else if (mConnThread) {
return mConnThread->Suspend();
} else if (mFTPState) {
return mFTPState->Suspend();
}
return NS_OK;
}
@ -198,8 +186,8 @@ nsFTPChannel::Resume(void) {
nsAutoLock lock(mLock);
if (mProxyChannel) {
return mProxyChannel->Resume();
} else if (mConnThread) {
return mConnThread->Resume();
} else if (mFTPState) {
return mFTPState->Resume();
}
return NS_OK;
}
@ -240,56 +228,10 @@ nsFTPChannel::SetURI(nsIURI* aURL)
NS_IMETHODIMP
nsFTPChannel::OpenInputStream(nsIInputStream **result)
{
nsresult rv = NS_OK;
PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFTPChannel::OpenInputStream() called\n"));
if (mProxyChannel) {
if (mProxyChannel)
return mProxyChannel->OpenInputStream(result);
}
if (mConnected) {
PR_LOG(gFTPLog, PR_LOG_DEBUG, ("ERROR nsFTPChannel:OpenInputStream called while connected.\n"));
return NS_ERROR_ALREADY_CONNECTED;
}
// create a pipe. The caller gets the input stream end,
// and the FTP thread get's the output stream end.
// The FTP thread will write to the output stream end
// when data become available to it.
nsCOMPtr<nsIOutputStream> bufOutStream; // we don't use this piece
nsCOMPtr<nsIStreamListener> listener;
rv = NS_NewSyncStreamListener(result, getter_AddRefs(bufOutStream),
getter_AddRefs(listener));
if (NS_FAILED(rv)) return rv;
mListener = listener; // ensure that we insert ourselves as the proxy listener
///////////////////////////
//// setup channel state
////////////////////////////////
//// setup the channel thread
nsFtpConnectionThread *thread = nsnull;
NS_NEWXPCOM(thread, nsFtpConnectionThread);
if (!thread) return NS_ERROR_OUT_OF_MEMORY;
mConnThread = thread;
rv = thread->Init(mHandler, this, mPrompter,
mBufferSegmentSize, mBufferMaxSize);
mHandler = 0;
if (NS_FAILED(rv)) return rv;
rv = thread->SetStreamListener(this, nsnull);
if (NS_FAILED(rv)) return rv;
if (mLoadGroup) {
rv = mLoadGroup->AddChannel(this, nsnull);
if (NS_FAILED(rv)) return rv;
}
mConnected = PR_TRUE;
return mPool->DispatchRequest((nsIRunnable*)thread);
NS_NOTREACHED("nsFTPChannel::OpenInputStream");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
@ -333,29 +275,21 @@ nsFTPChannel::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt)
return mProxyChannel->AsyncRead(this, ctxt);
}
if (mConnected) {
PR_LOG(gFTPLog, PR_LOG_DEBUG, ("ERROR nsFTPChannel:AsyncRead called while connected.\n"));
return NS_ERROR_ALREADY_CONNECTED;
}
////////////////////////////////
//// setup the channel thread
nsFtpConnectionThread *thread = nsnull;
NS_NEWXPCOM(thread, nsFtpConnectionThread);
if (!thread) return NS_ERROR_OUT_OF_MEMORY;
mConnThread = thread;
rv = thread->Init(mHandler, this, mPrompter,
mBufferSegmentSize, mBufferMaxSize);
mHandler = nsnull;
if (!mFTPState) {
NS_NEWXPCOM(mFTPState, nsFtpState);
if (!mFTPState) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mFTPState);
}
rv = mFTPState->Init(this, mPrompter,
mBufferSegmentSize, mBufferMaxSize);
if (NS_FAILED(rv)) return rv;
rv = thread->SetStreamListener(this, ctxt);
rv = mFTPState->SetStreamListener(this, ctxt);
if (NS_FAILED(rv)) return rv;
mConnected = PR_TRUE;
return mPool->DispatchRequest((nsIRunnable*)thread);
return mFTPState->Connect();
}
NS_IMETHODIMP
@ -379,30 +313,27 @@ nsFTPChannel::AsyncWrite(nsIInputStream *fromStream,
NS_ASSERTION(mAmount > 0, "FTP requires stream len info");
if (mAmount < 1) return NS_ERROR_NOT_INITIALIZED;
nsFtpConnectionThread *thread = nsnull;
NS_NEWXPCOM(thread, nsFtpConnectionThread);
if (!thread) return NS_ERROR_OUT_OF_MEMORY;
mConnThread = thread;
rv = thread->Init(mHandler, this, mPrompter,
mBufferSegmentSize, mBufferMaxSize);
mHandler = 0;
if (!mFTPState) {
NS_NEWXPCOM(mFTPState, nsFtpState);
if (!mFTPState) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mFTPState);
}
rv = mFTPState->Init(this, mPrompter,
mBufferSegmentSize, mBufferMaxSize);
if (NS_FAILED(rv)) return rv;
rv = thread->SetWriteStream(fromStream, mAmount);
rv = mFTPState->SetWriteStream(fromStream, mAmount);
if (NS_FAILED(rv)) return rv;
rv = thread->SetStreamObserver(this, ctxt);
rv = mFTPState->SetStreamObserver(this, ctxt);
if (NS_FAILED(rv)) return rv;
if (mLoadGroup) {
rv = mLoadGroup->AddChannel(this, nsnull);
if (NS_FAILED(rv)) return rv;
}
mConnected = PR_TRUE;
return mPool->DispatchRequest((nsIRunnable*)thread);
return mFTPState->Connect();
}
NS_IMETHODIMP
@ -659,8 +590,7 @@ nsFTPChannel::OnStopRequest(nsIChannel* aChannel, nsISupports* aContext,
nsresult aStatus, const PRUnichar* aStatusArg)
{
nsresult rv = NS_OK;
mConnThread = nsnull;
if (mLoadGroup) {
rv = mLoadGroup->RemoveChannel(this, nsnull, aStatus, aStatusArg);
if (NS_FAILED(rv)) return rv;

View File

@ -66,12 +66,8 @@ public:
static NS_METHOD
Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult);
// initializes the channel. creates the FTP connection thread
// and returns it so the protocol handler can cache it and
// join() it on shutdown.
nsresult Init(nsIURI* uri,
nsIProtocolHandler* aHandler,
nsIThreadPool* aPool);
// initializes the channel.
nsresult Init(nsIURI* uri);
nsresult SetProxyChannel(nsIChannel *aChannel);
@ -95,9 +91,7 @@ protected:
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsIStreamObserver> mObserver;
nsCOMPtr<nsIProtocolHandler> mHandler;
nsCOMPtr<nsIThreadPool> mPool; // the thread pool we want to use to fire off connections.
nsCOMPtr<nsIRequest> mConnThread;
nsFtpState* mFTPState;
PRUint32 mBufferSegmentSize;
PRUint32 mBufferMaxSize;
nsCOMPtr<nsIChannel> mProxyChannel; // a proxy channel

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,8 @@
* Contributor(s):
*/
#ifndef __nsftpconnectionthread__h_
#define __nsftpconnectionthread__h_
#ifndef __nsFtpState__h_
#define __nsFtpState__h_
#include "nsIThread.h"
#include "nsIRunnable.h"
@ -32,8 +32,6 @@
#include "prtime.h"
#include "nsString.h"
#include "nsIFTPChannel.h"
#include "nsIConnectionCache.h"
#include "nsConnectionCacheObj.h"
#include "nsIProtocolHandler.h"
#include "nsCOMPtr.h"
#include "nsXPIDLString.h"
@ -43,6 +41,8 @@
#include "nsIEventQueueService.h"
#include "nsIPrompt.h"
#include "nsFtpControlConnection.h"
// ftp server types
#define FTP_GENERIC_TYPE 0
#define FTP_UNIX_TYPE 1
@ -84,18 +84,18 @@ typedef enum _FTP_STATE {
// higher level ftp actions
typedef enum _FTP_ACTION { GET, PUT, MKDIR, DEL} FTP_ACTION;
class nsFtpConnectionThread : public nsIRunnable,
class nsFtpState : public nsIStreamListener,
public nsIRequest {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSISTREAMOBSERVER
NS_DECL_NSIREQUEST
nsFtpConnectionThread();
virtual ~nsFtpConnectionThread();
nsFtpState();
virtual ~nsFtpState();
nsresult Init(nsIProtocolHandler *aHandler,
nsIFTPChannel *aChannel,
nsresult Init(nsIFTPChannel *aChannel,
nsIPrompt *aPrompter,
PRUint32 bufferSegmentSize,
PRUint32 bufferMaxSize);
@ -109,6 +109,8 @@ public:
// use this to provide a stream to be written to the server.
nsresult SetWriteStream(nsIInputStream* aInStream, PRUint32 aWriteCount);
nsresult Connect();
private:
///////////////////////////////////
// BEGIN: STATE METHODS
@ -136,7 +138,6 @@ private:
///////////////////////////////////
// internal methods
nsresult StopProcessing();
FTP_STATE FindActionState(void);
void SetDirMIMEType(nsString& aString);
nsresult Process();
@ -145,6 +146,11 @@ private:
PRUint32 bufferSegmentSize,
PRUint32 bufferMaxSize,
nsIChannel** o_pTrans);
void KillControlConnnection();
nsresult StopProcessing();
nsresult EstablishControlConnection();
nsresult ControlAsyncWrite(nsCString& command);
///////////////////////////////////
// Private members
@ -152,16 +158,14 @@ private:
// ****** state machine vars
FTP_STATE mState; // the current state
FTP_STATE mNextState; // the next state
PRBool mKeepRunning; // thread event loop boolean
PRPackedBool mKeepRunning; // thread event loop boolean
PRPackedBool mCanceled; // indicates channel being canceled.
PRInt32 mResponseCode; // the last command response code
nsCAutoString mResponseMsg; // the last command response text
// ****** channel/transport/stream vars
nsCOMPtr<nsISocketTransportService> mSTS; // the socket transport service
nsCOMPtr<nsIChannel> mCPipe; // the command channel transport
nsCOMPtr<nsIChannel> mDPipe; // the data channel transport
nsCOMPtr<nsIOutputStream> mCOutStream; // command channel output
nsCOMPtr<nsIInputStream> mCInStream; // command channel input
nsFtpControlConnection* mControlConnection;// cacheable control connection (owns mCPipe)
nsCOMPtr<nsIChannel> mDPipe; // the data channel transport
// ****** consumer vars
nsCOMPtr<nsIStreamListener> mListener; // the consumer of our read events
@ -172,22 +176,17 @@ private:
// ****** connection cache vars
PRInt32 mServerType; // What kind of server are we talking to
PRBool mList; // Use LIST instead of NLST
PRPackedBool mList; // Use LIST instead of NLST
nsCAutoString mCwd; // Our current working dir.
nsCAutoString mCwdAttempt; // The dir we're trying to get into.
nsCAutoString mCacheKey; // the key into the cache hash.
PRBool mCachedConn; // is this connection from the cache
nsCOMPtr<nsIConnectionCache> mConnCache;// the nsISupports proxy ptr to our connection cache
nsConnectionCacheObj *mConn; // The cached connection.
// ****** protocol interpretation related state vars
nsAutoString mUsername; // username
nsAutoString mPassword; // password
FTP_ACTION mAction; // the higher level action (GET/PUT)
PRBool mBin; // transfer mode (ascii or binary)
PRBool mAnonymous; // try connecting anonymous (default)
PRBool mRetryPass; // retrying the password
PRPackedBool mBin; // transfer mode (ascii or binary)
PRPackedBool mAnonymous; // try connecting anonymous (default)
PRPackedBool mRetryPass; // retrying the password
nsresult mInternalError; // represents internal state errors
// ****** URI vars
@ -198,23 +197,26 @@ private:
nsXPIDLCString mPath; // the url's path
// ****** other vars
PRBool mConnected; // are we connected.
PRBool mSentStart; // have we sent an OnStartRequest() notification
PRUint8 mSuspendCount;// number of times we've been suspended.
PRUint32 mBufferSegmentSize;
PRUint32 mBufferMaxSize;
PRLock *mLock;
nsCOMPtr<nsIInputStream> mWriteStream; // This stream is written to the server.
PRUint32 mWriteCount; // The amount of data to write to the server.
PRBool mFireCallbacks; // Fire the listener callbacks.
PRPackedBool mFireCallbacks; // Fire the listener callbacks.
nsCOMPtr<nsIEventQueue> mEventQueue;
nsCOMPtr<nsIPrompt> mPrompter;
PRBool mIPv6Checked;
char *mIPv6ServerAddress; // Server IPv6 address; null if server not IPv6
PRPackedBool mIPv6Checked;
nsCOMPtr<nsIPrompt> mPrompter;
char *mIPv6ServerAddress; // Server IPv6 address; null if server not IPv6
// ***** control read gvars
PRPackedBool mControlReadContinue;
PRPackedBool mControlReadBrokenLine;
nsCAutoString mControlReadCarryOverBuf;
};
#define NS_FTP_BUFFER_READ_SIZE (8*1024)
#define NS_FTP_BUFFER_WRITE_SIZE (8*1024)
#endif //__nsftpconnectionthread__h_
#endif //__nsFtpState__h_

View File

@ -0,0 +1,227 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsFtpControlConnection.h"
#include "prlog.h"
#include "nsIPipe.h"
#include "nsIInputStream.h"
#if defined(PR_LOGGING)
extern PRLogModuleInfo* gFTPLog;
#endif /* PR_LOGGING */
//NS_IMPL_THREADSAFE_ISUPPORTS2(nsFtpControlConnection,
// nsIStreamListener,
// nsIStreamObserver);
NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsFtpControlConnection,
nsIStreamListener,
nsIStreamObserver);
NS_IMPL_THREADSAFE_ADDREF(nsFtpControlConnection);
nsrefcnt nsFtpControlConnection::Release(void)
{
nsrefcnt count;
NS_PRECONDITION(0 != mRefCnt, "dup release");
count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
NS_LOG_RELEASE(this, count, "nsFtpControlConnection");
if (0 == count) {
mRefCnt = 1; /* stabilize */
/* enable this to find non-threadsafe destructors: */
/* NS_ASSERT_OWNINGTHREAD(_class); */
NS_DELETEXPCOM(this);
return 0;
}
else if (1 == count && mConnected)
{
// mPipe has a reference to |this| caused by AsyncRead()
// Break this cycle by calling disconnect.
Disconnect();
}
return count;
}
nsFtpControlConnection::nsFtpControlConnection(nsIChannel* socketTransport)
: mCPipe(socketTransport)
{
NS_INIT_REFCNT();
PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("(%x) nsFtpControlConnection created", this));
mServerType = 0;
mConnected = mList = PR_FALSE;
mLock = PR_NewLock();
NS_ASSERTION(mLock, "null lock");
}
nsFtpControlConnection::~nsFtpControlConnection()
{
if (mLock) PR_DestroyLock(mLock);
PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("(%x) nsFtpControlConnection destroyed", this));
}
nsresult
nsFtpControlConnection::Connect()
{
if (!mCPipe)
return NS_ERROR_NULL_POINTER;
nsresult rv;
nsCOMPtr<nsIInputStream> inStream;
rv = NS_NewPipe(getter_AddRefs(inStream),
getter_AddRefs(mOutStream),
64, // segmentSize
256, // maxSize
PR_TRUE,
PR_TRUE);
if (NS_FAILED(rv)) return rv;
// so that we can share the nsIStreamObserver implemention, we will be checking the context
// to indicate between the read and the write transport. We will be passing the a non-null
// context for the write, and a null context for the read.
rv = mCPipe->AsyncWrite(inStream, NS_STATIC_CAST(nsIStreamObserver*, this), NS_STATIC_CAST(nsISupports*, this));
if (NS_FAILED(rv)) return rv;
// get the ball rolling by reading on the control socket.
rv = mCPipe->AsyncRead(NS_STATIC_CAST(nsIStreamListener*, this), nsnull);
if (NS_FAILED(rv)) return rv;
mConnected = PR_TRUE;
return NS_OK;
}
nsresult
nsFtpControlConnection::Disconnect()
{
if (!mConnected) return NS_ERROR_FAILURE;
PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("(%x) nsFtpControlConnection disconnecting", this));
mConnected = PR_FALSE;
if (mCPipe) mCPipe->Cancel(NS_BINDING_ABORTED);
return NS_OK;
}
nsresult
nsFtpControlConnection::Write(nsCString& command)
{
if (!mConnected)
return NS_ERROR_FAILURE;
#if defined(PR_LOGGING)
nsCString logString(command);
logString.ReplaceChar(CRLF, ' ');
PR_LOG(gFTPLog, PR_LOG_DEBUG, ("(%x) Writing \"%s\"\n", this, logString.GetBuffer()));
#endif
PRUint32 len = command.Length();
PRUint32 cnt;
nsresult rv = mOutStream->Write(command.GetBuffer(), len, &cnt);
if (NS_SUCCEEDED(rv) && len==cnt)
return NS_OK;
return NS_ERROR_FAILURE;
}
nsresult
nsFtpControlConnection::GetChannel(nsIChannel** controlChannel)
{
NS_IF_ADDREF(*controlChannel = mCPipe);
return NS_OK;
}
nsresult
nsFtpControlConnection::SetStreamListener(nsIStreamListener *aListener)
{
nsAutoLock lock(mLock);
mListener = aListener;
return NS_OK;
}
NS_IMETHODIMP
nsFtpControlConnection::OnStartRequest(nsIChannel *aChannel, nsISupports *aContext)
{
if (!mConnected)
return NS_OK;
// we do not care about notifications from the write channel.
// a non null context indicates that this is a write notification.
if (aContext != nsnull)
return NS_OK;
PR_Lock(mLock);
nsCOMPtr<nsIStreamListener> myListener = mListener;
PR_Unlock(mLock);
if (!myListener)
return NS_OK;
return mListener->OnStartRequest(aChannel, aContext);
}
NS_IMETHODIMP
nsFtpControlConnection::OnStopRequest(nsIChannel *aChannel, nsISupports *aContext,
nsresult aStatus, const PRUnichar* aStatusArg)
{
if (!mConnected)
return NS_OK;
// we do not care about successful notifications from the write channel.
// a non null context indicates that this is a write notification.
if (aContext != nsnull && NS_SUCCEEDED(aStatus))
return NS_OK;
PR_Lock(mLock);
nsCOMPtr<nsIStreamListener> myListener = mListener;
PR_Unlock(mLock);
if (!myListener)
return NS_OK;
return mListener->OnStopRequest(aChannel, aContext, aStatus, aStatusArg);
}
NS_IMETHODIMP
nsFtpControlConnection::OnDataAvailable(nsIChannel *aChannel,
nsISupports *aContext,
nsIInputStream *aInStream,
PRUint32 aOffset,
PRUint32 aCount)
{
if (!mConnected)
return NS_OK;
PR_Lock(mLock);
nsCOMPtr<nsIStreamListener> myListener = mListener;
PR_Unlock(mLock);
if (!myListener)
return NS_OK;
return myListener->OnDataAvailable(aChannel, aContext, aInStream,
aOffset, aCount);
}

View File

@ -0,0 +1,68 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsFtpControlConnection_h___
#define nsFtpControlConnection_h___
#include "nsCOMPtr.h"
#include "nsIStreamListener.h"
#include "nsIRequest.h"
#include "nsIChannel.h"
#include "nsString.h"
#include "nsIOutputStream.h"
#include "nsAutoLock.h"
class nsFtpControlConnection : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSISTREAMOBSERVER
nsFtpControlConnection(nsIChannel* socketTransport);
virtual ~nsFtpControlConnection();
nsresult Connect();
nsresult Disconnect();
nsresult Write(nsCString& command);
PRBool IsConnected() { return mConnected; }
nsresult GetChannel(nsIChannel** controlChannel);
nsresult SetStreamListener(nsIStreamListener *aListener);
PRUint32 mServerType; // what kind of server is it.
nsCAutoString mCwd; // what dir are we in
PRBool mList; // are we sending LIST or NLST
nsAutoString mPassword;
private:
PRLock* mLock; // protects mListener.
nsCOMPtr<nsIChannel> mCPipe;
nsCOMPtr<nsIOutputStream> mOutStream;
nsCOMPtr<nsIStreamListener> mListener;
PRBool mConnected;
};
#endif

View File

@ -1,37 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsIGenericFactory.h"
#include "nsFtpProtocolHandler.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFtpProtocolHandler, Init);
static nsModuleComponentInfo gResComponents[] = {
{ "The FTP Protocol Handler",
NS_FTPPROTOCOLHANDLER_CID,
NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp",
nsFtpProtocolHandlerConstructor
}
};
NS_IMPL_NSGETMODULE("ftp", gResComponents)

View File

@ -65,24 +65,28 @@ PRLogModuleInfo* gFTPLog = nsnull;
#endif /* PR_LOGGING */
static NS_DEFINE_IID(kIOServiceCID, NS_IOSERVICE_CID);
static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
static NS_DEFINE_CID(kProtocolProxyServiceCID, NS_PROTOCOLPROXYSERVICE_CID);
static NS_DEFINE_CID(kHTTPHandlerCID, NS_IHTTPHANDLER_CID);
static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID);
nsSupportsHashtable* nsFtpProtocolHandler::mRootConnectionList = nsnull;
////////////////////////////////////////////////////////////////////////////////
nsFtpProtocolHandler::nsFtpProtocolHandler() {
NS_INIT_REFCNT();
}
nsFtpProtocolHandler::~nsFtpProtocolHandler() {
PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("~nsFtpProtocolHandler() called"));
if (mLock) PR_DestroyLock(mLock);
if (mRootConnectionList) {
NS_DELETEXPCOM(mRootConnectionList);
mRootConnectionList = nsnull;
}
mIOSvc = 0;
}
NS_IMPL_THREADSAFE_ADDREF(nsFtpProtocolHandler);
NS_IMPL_THREADSAFE_RELEASE(nsFtpProtocolHandler);
NS_IMPL_QUERY_INTERFACE3(nsFtpProtocolHandler,
nsIProtocolHandler,
nsIConnectionCache,
nsIObserver);
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFtpProtocolHandler, nsIProtocolHandler);
nsresult
nsFtpProtocolHandler::Init() {
@ -95,22 +99,12 @@ nsFtpProtocolHandler::Init() {
mProxySvc = do_GetService(kProtocolProxyServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
NS_NEWXPCOM(mRootConnectionList, nsHashtable);
if (!mRootConnectionList) return NS_ERROR_OUT_OF_MEMORY;
rv = NS_NewThreadPool(getter_AddRefs(mPool),
NS_FTP_MIN_CONNECTION_COUNT,
NS_FTP_MAX_CONNECTION_COUNT,
NS_FTP_CONNECTION_STACK_SIZE);
mIOSvc = do_GetService(kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIObserverService> obsServ = do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsAutoString topic; topic.AssignWithConversion(NS_XPCOM_SHUTDOWN_OBSERVER_ID);
obsServ->AddObserver(this, topic.GetUnicode());
}
mLock = PR_NewLock();
if (!mLock) return NS_ERROR_OUT_OF_MEMORY;
mRootConnectionList = new nsSupportsHashtable(16, PR_TRUE); /* xxx what should be the size of this hashtable?? */
if (!mRootConnectionList) return NS_ERROR_OUT_OF_MEMORY;
NS_LOG_NEW_XPCOM(mRootConnectionList, "nsSupportsHashtable", sizeof(nsSupportsHashtable), __FILE__, __LINE__);
// XXX hack until xpidl supports error info directly (http://bugzilla.mozilla.org/show_bug.cgi?id=13423)
nsCOMPtr<nsIErrorService> errorService = do_GetService(kErrorServiceCID, &rv);
@ -166,18 +160,17 @@ nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
nsresult rv = NS_OK;
PRBool useProxy = PR_FALSE;
nsFTPChannel* channel;
nsFTPChannel* channel = nsnull;
rv = nsFTPChannel::Create(nsnull, NS_GET_IID(nsIChannel), (void**)&channel);
if (NS_FAILED(rv)) return rv;
rv = channel->Init(url, this, mPool);
rv = channel->Init(url);
if (NS_FAILED(rv)) {
NS_RELEASE(channel);
PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFtpProtocolHandler::NewChannel() FAILED\n"));
return rv;
}
if (NS_SUCCEEDED(mProxySvc->GetProxyEnabled(&useProxy)) && useProxy)
{
rv = mProxySvc->ExamineForProxy(url, channel);
@ -241,49 +234,53 @@ nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
return rv;
}
// nsIConnectionCache methods
NS_IMETHODIMP
nsFtpProtocolHandler::RemoveConn(const char *aKey, nsConnectionCacheObj* *_retval) {
// connection cache methods
nsresult
nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsISupports* *_retval) {
NS_ASSERTION(_retval, "null pointer");
nsCStringKey key(aKey);
nsAutoLock lock(mLock);
*_retval = (nsConnectionCacheObj*)mRootConnectionList->Remove(&key);
return NS_OK;
NS_ASSERTION(aKey, "null pointer");
*_retval = nsnull;
if (!mRootConnectionList)
return NS_ERROR_NULL_POINTER;
nsXPIDLCString spec;
aKey->GetPrePath(getter_Copies(spec));
nsCStringKey stringKey(spec);
// Do not have to addRef since there is only one connection (with this key)
// in this hash table at any time and that one has been addRef'ed
// by the Put(). If we change connection caching, we will have
// to re-address this.
if (mRootConnectionList)
mRootConnectionList->Remove(&stringKey, (nsISupports**)_retval);
if (*_retval)
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsFtpProtocolHandler::InsertConn(const char *aKey, nsConnectionCacheObj *aConn) {
nsresult
nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsISupports *aConn) {
NS_ASSERTION(aConn, "null pointer");
nsCStringKey key(aKey);
nsAutoLock lock(mLock);
mRootConnectionList->Put(&key, aConn);
return NS_OK;
}
NS_ASSERTION(aKey, "null pointer");
if (!mRootConnectionList)
return NS_ERROR_NULL_POINTER;
// cleans up a connection list entry
static PRBool PR_CALLBACK CleanupConnEntry(nsHashKey *aKey, void *aData, void *closure) {
delete (nsConnectionCacheObj*)aData;
return PR_TRUE;
}
// nsIObserver method
NS_IMETHODIMP
nsFtpProtocolHandler::Observe(nsISupports *aSubject,
const PRUnichar *aTopic,
const PRUnichar *someData ) {
nsresult rv;
if (mRootConnectionList) {
mRootConnectionList->Reset(CleanupConnEntry);
NS_DELETEXPCOM(mRootConnectionList);
mRootConnectionList = nsnull;
}
// remove ourself from the observer service.
NS_WITH_SERVICE(nsIObserverService, obsServ, NS_OBSERVERSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsAutoString topic; topic.AssignWithConversion(NS_XPCOM_SHUTDOWN_OBSERVER_ID);
obsServ->RemoveObserver(this, topic.GetUnicode());
}
return NS_OK;
nsXPIDLCString spec;
aKey->GetPrePath(getter_Copies(spec));
nsCStringKey stringKey(spec);
if (mRootConnectionList)
{
mRootConnectionList->Put(&stringKey, aConn);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -26,8 +26,7 @@
#include "nsIServiceManager.h"
#include "nsIProtocolHandler.h"
#include "nsHashtable.h"
#include "nsIConnectionCache.h"
#include "nsConnectionCacheObj.h"
#include "nsIIOService.h"
#include "nsIThreadPool.h"
#include "nsIObserverService.h"
#include "nsIProtocolProxyService.h"
@ -37,35 +36,28 @@
#define NS_FTPPROTOCOLHANDLER_CID \
{ 0x25029490, 0xf132, 0x11d2, { 0x95, 0x88, 0x0, 0x80, 0x5f, 0x36, 0x9f, 0x95 } }
class nsFtpProtocolHandler : public nsIProtocolHandler,
public nsIConnectionCache,
public nsIObserver
{
class nsFtpProtocolHandler : public nsIProtocolHandler{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
NS_DECL_NSICONNECTIONCACHE
NS_DECL_NSIOBSERVER
// nsFtpProtocolHandler methods:
nsFtpProtocolHandler() {
NS_INIT_REFCNT();
};
nsFtpProtocolHandler();
virtual ~nsFtpProtocolHandler();
// Define a Create method to be used with a factory:
static NS_METHOD Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult);
nsresult Init();
// FTP Connection list access
static nsresult InsertConnection(nsIURI *aKey, nsISupports *aConn);
static nsresult RemoveConnection(nsIURI *aKey, nsISupports **_retval);
protected:
nsHashtable* mRootConnectionList; // hash of FTP connections
nsCOMPtr<nsIThreadPool> mPool; // thread pool for FTP connections
nsCOMPtr<nsIProtocolProxyService> mProxySvc;
PRLock* mLock;
static nsSupportsHashtable* mRootConnectionList;
nsCOMPtr<nsIIOService> mIOSvc;
nsCOMPtr<nsIProtocolProxyService> mProxySvc;
};
#define NS_FTP_MIN_CONNECTION_COUNT 1
#define NS_FTP_MAX_CONNECTION_COUNT 4
#define NS_FTP_CONNECTION_STACK_SIZE 0
#endif /* nsFtpProtocolHandler_h___ */