From 722b00f7fabe2df2bd1072df533c10eccd3c502f Mon Sep 17 00:00:00 2001 From: "rpotts%netscape.com" Date: Mon, 19 Apr 1999 07:17:37 +0000 Subject: [PATCH] First pass at a socket transport implementation... --- netwerk/base/public/makefile.win | 1 + .../base/public/nsISocketTransportService.h | 26 +- netwerk/base/public/nsIStreamObserver.h | 10 + netwerk/base/src/makefile.win | 3 + netwerk/base/src/nsSocketTransport.cpp | 672 ++++++++++++++++++ netwerk/base/src/nsSocketTransport.h | 117 +++ netwerk/base/src/nsSocketTransportService.cpp | 449 ++++++++++++ netwerk/base/src/nsSocketTransportService.h | 79 ++ netwerk/test/TestSocketIO.cpp | 256 +++++++ netwerk/test/TestSocketInput.cpp | 185 +++++ netwerk/test/makefile.win | 7 +- 11 files changed, 1791 insertions(+), 14 deletions(-) create mode 100644 netwerk/base/src/nsSocketTransport.cpp create mode 100644 netwerk/base/src/nsSocketTransport.h create mode 100644 netwerk/base/src/nsSocketTransportService.cpp create mode 100644 netwerk/base/src/nsSocketTransportService.h create mode 100644 netwerk/test/TestSocketIO.cpp create mode 100644 netwerk/test/TestSocketInput.cpp diff --git a/netwerk/base/public/makefile.win b/netwerk/base/public/makefile.win index 5baefe54a0b..00a129a870f 100644 --- a/netwerk/base/public/makefile.win +++ b/netwerk/base/public/makefile.win @@ -23,6 +23,7 @@ EXPORTS = \ nsICancelable.h \ nsIConnectionGroup.h \ nsIFileTransportService.h \ + nsISocketTransportService.h \ nsINetService.h \ nsIProtocolConnection.h \ nsIProtocolHandler.h \ diff --git a/netwerk/base/public/nsISocketTransportService.h b/netwerk/base/public/nsISocketTransportService.h index 2924b9e83ce..859bfe2ccda 100644 --- a/netwerk/base/public/nsISocketTransportService.h +++ b/netwerk/base/public/nsISocketTransportService.h @@ -23,32 +23,32 @@ class nsITransport; -// XXX regenerate: #define NS_ISOCKETTRANSPORTSERVICE_IID \ -{ /* 2355dca0-ea35-11d2-931b-00104ba0fd40 */ \ - 0x2355dca0, \ - 0xea35, \ +{ /* 9610f120-ef12-11d2-92b6-00105a1b0d64 */ \ + 0x9610f120, \ + 0xef12, \ 0x11d2, \ - {0x93, 0x1b, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \ + {0x92, 0xb6, 0x00, 0x10, 0x5a, 0x1b, 0x0d, 0x64} \ } -// XXX regenerate: #define NS_SOCKETTRANSPORTSERVICE_CID \ -{ /* 2bb2b250-ea35-11d2-931b-00104ba0fd40 */ \ - 0x2bb2b250, \ - 0xea35, \ +{ /* c07e81e0-ef12-11d2-92b6-00105a1b0d64 */ \ + 0xc07e81e0, \ + 0xef12, \ 0x11d2, \ - {0x93, 0x1b, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \ + {0x92, 0xb6, 0x00, 0x10, 0x5a, 0x1b, 0x0d, 0x64} \ } + class nsISocketTransportService : public nsISupports { public: - NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISOCKETTRANSPORTSERVICE_IID); + NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISOCKETTRANSPORTSERVICE_IID); - NS_IMETHOD CreateTransport(const char* host, PRInt32 port, - nsITransport* *result) = 0; + NS_IMETHOD CreateTransport(const char* host, PRInt32 port, + nsITransport* *result) = 0; + NS_IMETHOD Shutdown(void) = 0; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/netwerk/base/public/nsIStreamObserver.h b/netwerk/base/public/nsIStreamObserver.h index 0dfae9e3eba..7d2c0d56754 100644 --- a/netwerk/base/public/nsIStreamObserver.h +++ b/netwerk/base/public/nsIStreamObserver.h @@ -20,6 +20,7 @@ #define nsIStreamObserver_h___ #include "nsISupports.h" +#include "plevent.h" class nsIUrl; class nsIString; @@ -64,6 +65,15 @@ public: }; +// An asynchronous stream observer is used to ship data over to another thread specified +// by the thread's event queue. The receiver stream observer is then used to receive +// the notifications on the other thread. +extern nsresult +NS_NewAsyncStreamObserver(nsIStreamObserver* *result, + PLEventQueue* eventQueue, + nsIStreamObserver* receiver); + + // Generic status codes for OnStopBinding: #define NS_BINDING_SUCCEEDED NS_OK #define NS_BINDING_FAILED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 1) diff --git a/netwerk/base/src/makefile.win b/netwerk/base/src/makefile.win index f342a33b8a5..3b6416a417a 100644 --- a/netwerk/base/src/makefile.win +++ b/netwerk/base/src/makefile.win @@ -25,6 +25,7 @@ EXPORTS = \ nsConnectionGroup.h \ nsNetService.h \ nsFileTransportService.h \ + nsSocketTransportService.h \ nsUrl.h \ $(NULL) @@ -34,6 +35,8 @@ CPP_OBJS = \ .\$(OBJDIR)\nsSyncStreamListener.obj \ .\$(OBJDIR)\nsFileTransport.obj \ .\$(OBJDIR)\nsFileTransportService.obj \ + .\$(OBJDIR)\nsSocketTransport.obj \ + .\$(OBJDIR)\nsSocketTransportService.obj \ .\$(OBJDIR)\nsNetService.obj \ .\$(OBJDIR)\nsUrl.obj \ $(NULL) diff --git a/netwerk/base/src/nsSocketTransport.cpp b/netwerk/base/src/nsSocketTransport.cpp new file mode 100644 index 00000000000..fe3ce239b9d --- /dev/null +++ b/netwerk/base/src/nsSocketTransport.cpp @@ -0,0 +1,672 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nspr.h" +#include "nsCRT.h" +#include "nscore.h" +#include "nsIStreamListener.h" +#include "nsSocketTransport.h" +#include "nsSocketTransportService.h" + +// +// This is the State table which maps current state to next state +// for each socket operation... +// +nsSocketState gStateTable[eSocketOperation_Max][eSocketState_Max] = { + // eSocketOperation_None: + { + eSocketState_Error, // Created -> Error + eSocketState_Error, // WaitDNS -> Error + eSocketState_Error, // Closed -> Error + eSocketState_Error, // WaitConnect -> Error + eSocketState_Error, // Connected -> Error + eSocketState_Error, // WaitRead -> Error + eSocketState_Error, // WaitWrite -> Error + eSocketState_Error, // Done -> Error + eSocketState_Error, // Timeout -> Error + eSocketState_Error // Error -> Error + }, + // eSocketOperation_Connect: + { + eSocketState_WaitDNS, // Created -> WaitDNS + eSocketState_Closed, // WaitDNS -> Closed + eSocketState_WaitConnect, // Closed -> WaitConnect + eSocketState_Connected, // WaitConnect -> Connected + eSocketState_Connected, // Connected -> Done + eSocketState_Error, // WaitRead -> Error + eSocketState_Error, // WaitWrite -> Error + eSocketState_Connected, // Done -> Connected + eSocketState_Error, // Timeout -> Error + eSocketState_Error // Error -> Error + }, + // eSocketOperation_Read: + { + eSocketState_WaitDNS, // Created -> WaitDNS + eSocketState_Closed, // WaitDNS -> Closed + eSocketState_WaitConnect, // Closed -> WaitConnect + eSocketState_Connected, // WaitConenct -> Connected + eSocketState_WaitRead, // Connected -> WaitRead + eSocketState_Done, // WaitRead -> Done + eSocketState_Error, // WaitWrite -> Error + eSocketState_Connected, // Done -> Connected + eSocketState_Error, // Timeout -> Error + eSocketState_Error // Error -> Error + }, + // eSocketOperation_Write: + { + eSocketState_WaitDNS, // Created -> WaitDNS + eSocketState_Closed, // WaitDNS -> Closed + eSocketState_WaitConnect, // Closed -> WaitConnect + eSocketState_Connected, // WaitConenct -> Connected + eSocketState_WaitWrite, // Connected -> WaitWrite + eSocketState_Error, // WaitRead -> Error + eSocketState_Done, // WaitWrite -> Done + eSocketState_Connected, // Done -> Connected + eSocketState_Error, // Timeout -> Error + eSocketState_Error // Error -> Error + } +}; + +// +// This is the timeout value (in milliseconds) for calls to PR_Connect(...). +// +// The gConnectTimeout gets initialized the first time a nsSocketTransport +// is created... This interval is then passed to all PR_Connect() calls... +// +#define CONNECT_TIMEOUT_IN_MS 20 + +static int gTimeoutIsInitialized = 0; +static PRIntervalTime gConnectTimeout = -1; + +// +// This is the global buffer used by all nsSocketTransport instances when +// reading from or writing to the network. +// +#define MAX_IO_BUFFER_SIZE 8192 + +static char gIOBuffer[MAX_IO_BUFFER_SIZE]; + + + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + + +nsSocketTransport::nsSocketTransport() +{ + NS_INIT_REFCNT(); + + PR_INIT_CLIST(this); + + mHostName = nsnull; + mPort = 0; + mSocketFD = nsnull; + + mCurrentState = eSocketState_Created; + mOperation = eSocketOperation_None; + mSelectFlags = 0; + + mReadStream = nsnull; + mWriteStream = nsnull; + mListener = nsnull; + mContext = nsnull; + mService = nsnull; + + // + // Set up Internet defaults... + // + memset(&mNetAddress, 0, sizeof(mNetAddress)); + PR_InitializeNetAddr(PR_IpAddrNull, 0, &mNetAddress); + + // + // Initialize the global connect timeout value if necessary... + // + if (0 == gTimeoutIsInitialized) { + gConnectTimeout = PR_MillisecondsToInterval(CONNECT_TIMEOUT_IN_MS); + gTimeoutIsInitialized = 1; + } +} + + +nsSocketTransport::~nsSocketTransport() +{ + NS_IF_RELEASE(mContext); + NS_IF_RELEASE(mListener); + NS_IF_RELEASE(mReadStream); + NS_IF_RELEASE(mWriteStream); + NS_IF_RELEASE(mService); + + if (mHostName) { + PR_Free(mHostName); + } + + if (mSocketFD) { + PR_Close(mSocketFD); + mSocketFD = nsnull; + } +} + + +nsresult nsSocketTransport::Init(nsSocketTransportService* aService, + const char* aHost, + PRInt32 aPort) +{ + nsresult rv = NS_OK; + + mService = aService; + NS_ADDREF(mService); + + mPort = aPort; + if (aHost) { + // Copy the host name... + // + // XXX: This is so evil! Since this is a char* it must be freed with + // PR_Free(...) NOT delete[] like PRUnichar* buffers... + // + mHostName = nsCRT::strdup(aHost); + if (!mHostName) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + // aHost was nsnull... + else { + rv = NS_ERROR_NULL_POINTER; + } + + return rv; +} + + +nsresult nsSocketTransport::Process(PRInt16 aSelectFlags) +{ + nsresult rv = NS_OK; + + while ((eSocketOperation_None != mOperation) && (rv == NS_OK)) + { + switch (mCurrentState) { + case eSocketState_Created: + case eSocketState_Closed: + break; + + case eSocketState_Connected: + if (mListener) { + mListener->OnStartBinding(mContext); + } + break; + + case eSocketState_Done: + if (mListener) { + mListener->OnStopBinding(mContext, rv, nsnull); + } + NS_IF_RELEASE(mContext); + mCurrentState = gStateTable[mOperation][mCurrentState]; + mOperation = eSocketOperation_None; + continue; + + case eSocketState_WaitDNS: + rv = doResolveHost(); + break; + + case eSocketState_WaitConnect: + rv = doConnection(aSelectFlags); + break; + + case eSocketState_WaitRead: + rv = doRead(aSelectFlags); + break; + + case eSocketState_WaitWrite: + rv = doWrite(aSelectFlags); + break; + + case eSocketState_Timeout: + NS_ASSERTION(0, "Unexpected state..."); + rv = NS_ERROR_FAILURE; + break; + + case eSocketState_Error: + NS_ASSERTION(0, "Unexpected Error..."); + } + // + // If the current state has successfully completed, then move to the + // next state for the current operation... + // + if (NS_OK == rv) { + mCurrentState = gStateTable[mOperation][mCurrentState]; + // + // Any select flags are *only* valid the first time through the loop... + // + aSelectFlags = 0; + } + } + + return rv; +} + + +nsresult nsSocketTransport::doResolveHost(void) +{ + PRStatus status; + nsresult rv = NS_OK; + + NS_ASSERTION(eSocketState_WaitDNS == mCurrentState, "Wrong state."); + // + // Initialize the port used for the connection... + // + // XXX: The list of ports must be restricted - see net_bad_ports_table[] in + // mozilla/network/main/mkconect.c + // + mNetAddress.inet.port = PR_htons(mPort); + + // + // Resolve the address of the given host... + // + char dbbuf[PR_NETDB_BUF_SIZE]; + PRHostEnt hostEnt; + + status = PR_GetHostByName(mHostName, dbbuf, sizeof(dbbuf), &hostEnt); + if (PR_SUCCESS == status) { + if (hostEnt.h_addr_list) { + memcpy(&mNetAddress.inet.ip, hostEnt.h_addr_list[0], + sizeof(mNetAddress.inet.ip)); + } else { + // XXX: What should happen here? The GetHostByName(...) succeeded but + // there are *no* A records... + } + } + // DNS lookup failed... + else { + rv = NS_ERROR_FAILURE; + } + + return rv; +} + + +nsresult nsSocketTransport::doConnection(PRInt16 aSelectFlags) +{ + PRStatus status; + nsresult rv = NS_OK; + + NS_ASSERTION(eSocketState_WaitConnect == mCurrentState, "Wrong state."); + // + // Step 1: + // Create a new TCP socket structure (if necessary)... + // + if (!mSocketFD) { + mSocketFD = PR_NewTCPSocket(); + if (mSocketFD) { + PRSocketOptionData opt; + + // Make the socket non-blocking... + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_TRUE; + status = PR_SetSocketOption(mSocketFD, &opt); + if (PR_SUCCESS != status) { + rv = NS_ERROR_FAILURE; + } + + // XXX: Is this still necessary? +#if defined(XP_WIN16) || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK)) + opt.option = PR_SockOpt_Linger; + opt.value.linger.polarity = PR_TRUE; + opt.value.linger.linger = PR_INTERVAL_NO_WAIT; + PR_SetSocketOption(*sock, &opt); +#endif /* XP_WIN16 || XP_OS2*/ + } + else { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // + // Step 2: + // Initiate the connect() to the host... + // + // This is only done the first time doConnection(...) is called. + // + if (NS_SUCCEEDED(rv) && (0 == aSelectFlags)) { + status = PR_Connect(mSocketFD, &mNetAddress, gConnectTimeout); + if (PR_SUCCESS != status) { + PRErrorCode code = PR_GetError(); + // + // If the PR_Connect(...) would block, then return WOULD_BLOCK... + // It is the callers responsibility to place the transport on the + // select list of the transport thread... + // + if ((PR_WOULD_BLOCK_ERROR == code) || + (PR_IN_PROGRESS_ERROR == code)) { + + // Set up the select flags for connect... + mSelectFlags = (PR_POLL_READ | PR_POLL_EXCEPT | PR_POLL_WRITE); + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + // + // If the socket is already connected, then return success... + // + else if (PR_IS_CONNECTED_ERROR == code) { + rv = NS_OK; + } + // + // The connection was refused... + // + else { + // Connection refused... + // XXX: what should the next state be? + mCurrentState = eSocketState_Error; + rv = NS_ERROR_FAILURE; + } + } + } + // + // Step 3: + // Process the flags returned by PR_Poll() if any... + // + else if (NS_SUCCEEDED(rv) && aSelectFlags) { + if (PR_POLL_EXCEPT & aSelectFlags) { + // XXX: what should the next state be? + mCurrentState = eSocketState_Error; + rv = NS_ERROR_FAILURE; + } + // + // The connection was successful... + // + else if (PR_POLL_WRITE & aSelectFlags) { + rv = NS_OK; + } + } + + return rv; +} + + +nsresult nsSocketTransport::doRead(PRInt16 aSelectFlags) +{ + PRUint32 size, bytesWritten, totalBytesWritten; + PRInt32 len; + PRErrorCode code; + nsresult rv = NS_OK; + + NS_ASSERTION(eSocketState_WaitRead == mCurrentState, "Wrong state."); + + totalBytesWritten = 0; + do { + // + // Determine how much space is available in the input stream... + // + // Since, the data is being read into a global buffer from the net, unless + // it can all be pushed into the stream it will be lost! + // + mReadStream->GetLength(&size); + size = MAX_IO_BUFFER_SIZE - size; + if (size > MAX_IO_BUFFER_SIZE) { + size = MAX_IO_BUFFER_SIZE; + } + + if (size > 0) { + len = PR_Read(mSocketFD, gIOBuffer, size); + if (len > 0) { + rv = mReadStream->Fill(gIOBuffer, len, &bytesWritten); + NS_ASSERTION(bytesWritten == (PRUint32)len, "Data was lost during read."); + + totalBytesWritten += bytesWritten; + } + // + // The read operation has completed... + // + else if (len == 0) { + // + // Notify the listener that the read operation has completed... + // + //mListener->OnStopBinding(mContext, rv, nsnull); + rv = NS_OK; + break; + } + // Error... + else { + code = PR_GetError(); + + if (PR_WOULD_BLOCK_ERROR == code) { + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + else { + rv = NS_ERROR_FAILURE; + } + } + } + // + // There is no room in the input stream for more data... Give the + // consumer more time to empty the stream... + // + else { + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + } while (NS_OK == rv); + + if (bytesWritten) { + mListener->OnDataAvailable(mContext, mReadStream, bytesWritten); + } + + // + // Set up the select flags for connect... + // + if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + mSelectFlags = (PR_POLL_READ | PR_POLL_EXCEPT); + } + + return rv; +} + + +nsresult nsSocketTransport::doWrite(PRInt16 aSelectFlags) +{ + PRUint32 size, bytesRead; + PRInt32 len; + PRErrorCode code; + nsresult rv = NS_OK; + + NS_ASSERTION(eSocketState_WaitWrite == mCurrentState, "Wrong state."); + + do { + rv = mWriteStream->Read(gIOBuffer, sizeof(gIOBuffer), &bytesRead); + if (NS_SUCCEEDED(rv)) { + if (bytesRead > 0) { + len = PR_Write(mSocketFD, gIOBuffer, bytesRead); + if (len < 0) { + code = PR_GetError(); + + if (PR_WOULD_BLOCK_ERROR == code) { + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + else { + rv = NS_ERROR_FAILURE; + } + } + } + // + // The write operation has completed... + // + else if (bytesRead == 0) { + // + // Notify the listener that the write operation has completed... + // + //mListener->OnStopBinding(mContext, rv, nsnull); + rv = NS_OK; + break; + } + } + } while(NS_OK == rv); + + // + // Set up the select flags for connect... + // + if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + mSelectFlags = (PR_POLL_WRITE | PR_POLL_EXCEPT); + } + + return rv; +} + + +nsresult nsSocketTransport::CloseConnection(void) +{ + PRStatus status; + nsresult rv = NS_OK; + + NS_ASSERTION(mSocketFD, "Socket does not exist"); + + status = PR_Close(mSocketFD); + if (PR_SUCCESS != status) { + rv = NS_ERROR_FAILURE; + } + mSocketFD = nsnull; + + if (NS_SUCCEEDED(rv)) { + mCurrentState = eSocketState_Closed; + } + + return rv; +} + + +// +// -------------------------------------------------------------------------- +// nsISupports implementation... +// -------------------------------------------------------------------------- +// +NS_IMPL_ISUPPORTS(nsSocketTransport, nsITransport::GetIID()); + + +// +// -------------------------------------------------------------------------- +// nsICancelable implementation... +// -------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSocketTransport::Cancel(void) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsSocketTransport::Suspend(void) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsSocketTransport::Resume(void) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + + + +// +// -------------------------------------------------------------------------- +// nsITransport implementation... +// -------------------------------------------------------------------------- +// + +NS_IMETHODIMP +nsSocketTransport::AsyncRead(nsISupports* aContext, + PLEventQueue* aAppEventQueue, + nsIStreamListener* aListener) +{ + nsresult rv = NS_OK; + + if (eSocketOperation_None != mOperation) { + // XXX: This should be NS_ERROR_IN_PROGRESS... + rv = NS_ERROR_FAILURE; + } + + if (NS_SUCCEEDED(rv) && !mReadStream) { + rv = NS_NewByteBufferInputStream(&mReadStream, PR_FALSE, + MAX_IO_BUFFER_SIZE); + } + + if (NS_SUCCEEDED(rv)) { + NS_IF_RELEASE(mContext); + mContext = aContext; + NS_IF_ADDREF(mContext); + + NS_IF_RELEASE(mListener); + rv = NS_NewAsyncStreamListener(&mListener, aAppEventQueue, aListener); + } + + if (NS_SUCCEEDED(rv)) { + mOperation = eSocketOperation_Read; + + mService->AddToWorkQ(this); + } + + return rv; +} + + +NS_IMETHODIMP +nsSocketTransport::AsyncWrite(nsIInputStream* aFromStream, + nsISupports* aContext, + PLEventQueue* aAppEventQueue, + nsIStreamObserver* aObserver) +{ + nsresult rv = NS_OK; + + if (eSocketOperation_None != mOperation) { + // XXX: This should be NS_ERROR_IN_PROGRESS... + rv = NS_ERROR_FAILURE; + } + + if (NS_SUCCEEDED(rv)) { + NS_IF_RELEASE(mWriteStream); + mWriteStream = aFromStream; + NS_ADDREF(mWriteStream); + + NS_IF_RELEASE(mContext); + mContext = aContext; + NS_IF_ADDREF(mContext); + + NS_IF_RELEASE(mListener); + nsIStreamObserver* aListener; + rv = NS_NewAsyncStreamObserver(&aListener, aAppEventQueue, aObserver); + // XXX: This is really evil... + mListener = (nsIStreamListener*)aListener; + } + + if (NS_SUCCEEDED(rv)) { + mOperation = eSocketOperation_Write; + mService->AddToWorkQ(this); + } + + return rv; +} + + +NS_IMETHODIMP +nsSocketTransport::OpenInputStream(nsIInputStream* *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsSocketTransport::OpenOutputStream(nsIOutputStream* *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + + diff --git a/netwerk/base/src/nsSocketTransport.h b/netwerk/base/src/nsSocketTransport.h new file mode 100644 index 00000000000..719816f33f9 --- /dev/null +++ b/netwerk/base/src/nsSocketTransport.h @@ -0,0 +1,117 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef nsSocketTransport_h___ +#define nsSocketTransport_h___ + +#include "prclist.h" +#include "prio.h" +#include "prnetdb.h" + +#include "nsITransport.h" +#include "nsIInputStream.h" +#include "nsIByteBufferInputStream.h" + +enum nsSocketState { + eSocketState_Created = 0, + eSocketState_WaitDNS = 1, + eSocketState_Closed = 2, + eSocketState_WaitConnect = 3, + eSocketState_Connected = 4, + eSocketState_WaitRead = 5, + eSocketState_WaitWrite = 6, + eSocketState_Done = 7, + eSocketState_Timeout = 8, + eSocketState_Error = 9, + eSocketState_Max = 10 +}; + +enum nsSocketOperation { + eSocketOperation_None = 0, + eSocketOperation_Connect = 1, + eSocketOperation_Read = 2, + eSocketOperation_Write = 3, + eSocketOperation_Max = 4 +}; + + +class nsSocketTransportService; + +class nsSocketTransport : public PRCList, + public nsITransport +{ +public: + // nsISupports methods: + NS_DECL_ISUPPORTS + + // nsICancelable methods: + NS_IMETHOD Cancel(void); + NS_IMETHOD Suspend(void); + NS_IMETHOD Resume(void); + + // nsITransport methods: + NS_IMETHOD AsyncRead(nsISupports* context, + PLEventQueue* appEventQueue, + nsIStreamListener* listener); + NS_IMETHOD AsyncWrite(nsIInputStream* fromStream, + nsISupports* context, + PLEventQueue* appEventQueue, + nsIStreamObserver* observer); + NS_IMETHOD OpenInputStream(nsIInputStream* *result); + NS_IMETHOD OpenOutputStream(nsIOutputStream* *result); + + + // nsSocketTransport methods: + nsSocketTransport(); + virtual ~nsSocketTransport(); + + nsresult Init(nsSocketTransportService* aService, + const char* aHost, + PRInt32 aPort); + nsresult Process(PRInt16 aSelectFlags); + + nsresult doConnection(PRInt16 aSelectFlags); + nsresult doResolveHost(void); + nsresult doRead(PRInt16 aSelectFlags); + nsresult doWrite(PRInt16 aSelectFlags); + + nsresult CloseConnection(void); + + PRFileDesc* GetSocket(void) { return mSocketFD; } + PRInt16 GetSelectFlags(void) { return mSelectFlags; } + +protected: + nsSocketState mCurrentState; + nsSocketOperation mOperation; + + PRFileDesc* mSocketFD; + PRNetAddr mNetAddress; + PRInt16 mSelectFlags; + + char* mHostName; + PRInt32 mPort; + + nsISupports* mContext; + nsIStreamListener* mListener; + nsIByteBufferInputStream* mReadStream; + nsIInputStream* mWriteStream; + + nsSocketTransportService* mService; +}; + +#endif /* nsSocketTransport_h___ */ diff --git a/netwerk/base/src/nsSocketTransportService.cpp b/netwerk/base/src/nsSocketTransportService.cpp new file mode 100644 index 00000000000..bfc4807aae6 --- /dev/null +++ b/netwerk/base/src/nsSocketTransportService.cpp @@ -0,0 +1,449 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#include "nsSocketTransportService.h" +#include "nsSocketTransport.h" + + +#define MAX_OPEN_CONNECTIONS 10 + + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + + +nsSocketTransportService::nsSocketTransportService() +{ + NS_INIT_REFCNT(); + + PR_INIT_CLIST(&mWorkQ); + + mThread = nsnull; + mThreadEvent = nsnull; + mThreadLock = nsnull; + + mSelectFDSet = nsnull; + mSelectFDSetCount = 0; + + mActiveTransportList = nsnull; + + mThreadRunning = PR_FALSE; +} + + +nsSocketTransportService::~nsSocketTransportService() +{ + // + // It is impossible for the nsSocketTransportService to be deleted while + // the transport thread is running because it holds a reference to the + // nsIRunnable (ie. the nsSocketTransportService instance)... + // + NS_ASSERTION(!mThread && !mThreadRunning, + "The socket transport thread is still running..."); + + if (mSelectFDSet) { + PR_Free(mSelectFDSet); + mSelectFDSet = nsnull; + } + + if (mActiveTransportList) { + PR_Free(mActiveTransportList); + mActiveTransportList = nsnull; + } + + if (mThreadEvent) { + PR_DestroyPollableEvent(mThreadEvent); + mThreadEvent = nsnull; + } + + if (mThreadLock) { + PR_DestroyLock(mThreadLock); + mThreadLock = nsnull; + } +} + + +nsresult nsSocketTransportService::Init(void) +{ + nsresult rv = NS_OK; + + NS_ASSERTION(!mThread, "Socket transport thread has already been created!."); + + // + // Create FDSET list used by PR_Poll(...) + // + if (!mSelectFDSet) { + mSelectFDSet = (PRPollDesc*)PR_Malloc(sizeof(PRPollDesc)*MAX_OPEN_CONNECTIONS); + if (mSelectFDSet) { + memset(mSelectFDSet, 0, sizeof(PRPollDesc)*MAX_OPEN_CONNECTIONS); + } else { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // + // Create the list of Active transport objects... This list contains the + // nsSocketTransport corresponding to each PRFileDesc* in the mSelectFDSet + // + if (NS_SUCCEEDED(rv) && !mActiveTransportList) { + mActiveTransportList = (nsSocketTransport**)PR_Malloc(sizeof(nsSocketTransport*)*MAX_OPEN_CONNECTIONS); + if (mActiveTransportList) { + memset(mActiveTransportList, 0, sizeof(nsSocketTransport*)*MAX_OPEN_CONNECTIONS); + } else { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // + // Create the pollable event used to immediately wake up the transport + // thread when it is blocked in PR_Poll(...) + // + if (NS_SUCCEEDED(rv) && !mThreadEvent) { + mThreadEvent = PR_NewPollableEvent(); + if (!mThreadEvent) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // + // Create the synchronization lock for the transport thread... + // + if (NS_SUCCEEDED(rv) && !mThreadLock) { + mThreadLock = PR_NewLock(); + if (!mThreadLock) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + // + // Create the transport thread... + // + if (NS_SUCCEEDED(rv) && !mThread) { + mThreadRunning = PR_TRUE; + rv = NS_NewThread(&mThread, this); + } + + return rv; +} + + +nsresult nsSocketTransportService::AddToWorkQ(nsSocketTransport* aTransport) +{ + PRStatus status; + PRBool bFireEvent; + nsresult rv = NS_OK; + + NS_ADDREF(aTransport); + Lock(); + bFireEvent = PR_CLIST_IS_EMPTY(&mWorkQ); + PR_APPEND_LINK(aTransport, &mWorkQ); + Unlock(); + + // + // Only fire an event if this is the first entry in the workQ. Otherwise, + // the event has already been fired and the transport thread will process + // all of the entries at once... + // + if (bFireEvent) { + status = PR_SetPollableEvent(mThreadEvent); + if (PR_FAILURE == status) { + rv = NS_ERROR_FAILURE; + } + } + return rv; +} + + +nsresult nsSocketTransportService::ProcessWorkQ(void) +{ + nsresult rv = NS_OK; + + // + // Only process pending operations while there is space available in the + // select list... + // + // XXX: Need a way to restart the ProcessWorkQ(...) when space becomes + // available in the select set... + // + Lock(); + while (!PR_CLIST_IS_EMPTY(&mWorkQ) && + (MAX_OPEN_CONNECTIONS > mSelectFDSetCount)) { + nsSocketTransport* transport; + + // Get the next item off of the workQ... + transport = (nsSocketTransport*)PR_LIST_HEAD(&mWorkQ); + PR_REMOVE_AND_INIT_LINK(transport); + + // Try to perform the operation... + // + // Do not pass any select flags... + rv = transport->Process(0); + // + // If the operation would block, then add it to the select list for + // later processing when the data arrives... + // + if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + rv = AddToSelectList(transport); + } + // Release the transport object (since it is no longer on the WorkQ). + NS_RELEASE(transport); + } + Unlock(); + + return rv; +} + +nsresult nsSocketTransportService::AddToSelectList(nsSocketTransport* aTransport) +{ + nsresult rv = NS_OK; + + if (aTransport && (MAX_OPEN_CONNECTIONS > mSelectFDSetCount) ) { + PRPollDesc* pfd; + // Add the FileDesc to the PRPollDesc list... + pfd = &mSelectFDSet[mSelectFDSetCount]; + pfd->fd = aTransport->GetSocket();; + pfd->in_flags = aTransport->GetSelectFlags(); + pfd->out_flags = 0; + // Add the transport instance to the corresponding active transport list... + NS_ADDREF(aTransport); + mActiveTransportList[mSelectFDSetCount] = aTransport; + mSelectFDSetCount += 1; + } + else { + rv = NS_ERROR_FAILURE; + } + + return rv; +} + + +nsresult nsSocketTransportService::RemoveFromSelectList(nsSocketTransport* aTransport) +{ + int i; + nsresult rv = NS_ERROR_FAILURE; + + for (i=1; i 0) { + nsresult rv; + int i; + + /* Process any sockets with data first... */ + for (i=mSelectFDSetCount-1; i>=1; i--) { + PRPollDesc* pfd; + PRInt16 out_flags; + + pfd = &mSelectFDSet[i]; + if (pfd->out_flags) { + // Clear the out_flags for next time... + out_flags = pfd->out_flags; + pfd->out_flags = 0; + + transport = mActiveTransportList[i]; + NS_ASSERTION(transport, "Null transport in active list..."); + if (transport) { + rv = transport->Process(out_flags); + if (NS_BASE_STREAM_WOULD_BLOCK == rv) { + // Update the select flags... + pfd->in_flags = transport->GetSelectFlags(); + } + // + // If the operation completed, then remove the entry from the + // select list... + // + else { + rv = RemoveFromSelectList(transport); + } + } + } + } + + /* Process any pending operations on the mWorkQ... */ + if (mSelectFDSet[0].out_flags) { + // + // Clear the pollable event... This call should *never* block since + // PR_Poll(...) said that it had been fired... + // + NS_ASSERTION(!(mSelectFDSet[0].out_flags & PR_POLL_EXCEPT), + "Exception on Pollable event."); + PR_WaitForPollableEvent(mThreadEvent); + + rv = ProcessWorkQ(); + } + } + /* PR_Poll(...) timeout... */ + else if (count == 0) { + } + /* PR_Poll(...) error.. */ + else { + } + } + + return NS_OK; +} + + +// +// -------------------------------------------------------------------------- +// nsISocketTransportService implementation... +// -------------------------------------------------------------------------- +// +NS_IMETHODIMP +nsSocketTransportService::CreateTransport(const char* aHost, + PRInt32 aPort, + nsITransport** aResult) +{ + nsresult rv = NS_OK; + nsSocketTransport* transport = nsnull; + + // Parameter validation... + NS_ASSERTION(aResult, "aResult == nsnull."); + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + + // Create and initialize a new connection object... + NS_NEWXPCOM(transport, nsSocketTransport); + if (transport) { + rv = transport->Init(this, aHost, aPort); + if (NS_FAILED(rv)) { + delete transport; + transport = nsnull; + } + } + else { + rv = NS_ERROR_OUT_OF_MEMORY; + } + + // Set the reference count to one... + if (NS_SUCCEEDED(rv)) { + NS_ADDREF(transport); + } + *aResult = transport; + + return rv; +} + + +NS_IMETHODIMP +nsSocketTransportService::Shutdown(void) +{ + PRStatus status; + nsresult rv = NS_OK; + + if (mThread) { + // + // Clear the running flag and wake up the transport thread... + // + mThreadRunning = PR_FALSE; + status = PR_SetPollableEvent(mThreadEvent); + + // XXX: what should happen if this fails? + NS_ASSERTION(PR_SUCCESS == status, "Unable to wake up the transport thread."); + + // Wait for the transport thread to exit nsIRunnable::Run() + if (PR_SUCCESS == status) { + mThread->Join(); + } + + NS_RELEASE(mThread); + } else { + rv = NS_ERROR_FAILURE; + } + + return rv; +} \ No newline at end of file diff --git a/netwerk/base/src/nsSocketTransportService.h b/netwerk/base/src/nsSocketTransportService.h new file mode 100644 index 00000000000..a8faba6bf74 --- /dev/null +++ b/netwerk/base/src/nsSocketTransportService.h @@ -0,0 +1,79 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef nsSocketTransportService_h___ +#define nsSocketTransportService_h___ + +#include "nspr.h" +#include "nsIThread.h" +#include "nsISocketTransportService.h" +#include "nsIInputStream.h" + +class nsSocketTransport; + +class nsSocketTransportService : public nsISocketTransportService, + public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + + // nsISocketTransportService methods: + NS_IMETHOD CreateTransport(const char* aHost, + PRInt32 aPort, + nsITransport** aResult); + + NS_IMETHOD Shutdown(void); + + // nsIRunnable methods: + NS_IMETHOD Run(void); + + // nsSocketTransportService methods: + nsSocketTransportService(); + virtual ~nsSocketTransportService(); + + nsresult Init(void); + + nsresult AddToWorkQ(nsSocketTransport* aTransport); + + // The following methods are called by the transport thread... + nsresult ProcessWorkQ(void); + + nsresult AddToSelectList(nsSocketTransport* aTransport); + nsresult RemoveFromSelectList(nsSocketTransport* aTransport); + +protected: + // Inline helpers... + void Lock (void) { NS_ASSERTION(mThreadLock, "Lock null."); PR_Lock(mThreadLock); } + void Unlock(void) { NS_ASSERTION(mThreadLock, "Lock null."); PR_Unlock(mThreadLock); } + + nsIThread* mThread; + PRFileDesc* mThreadEvent; + PRLock* mThreadLock; + PRBool mThreadRunning; + + PRCList mWorkQ; + + PRInt32 mSelectFDSetCount; + PRPollDesc* mSelectFDSet; + nsSocketTransport** mActiveTransportList; +}; + + +#endif /* nsSocketTransportService_h___ */ + + diff --git a/netwerk/test/TestSocketIO.cpp b/netwerk/test/TestSocketIO.cpp new file mode 100644 index 00000000000..d4f4d3b9019 --- /dev/null +++ b/netwerk/test/TestSocketIO.cpp @@ -0,0 +1,256 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "stdio.h" +#include + +#include "nspr.h" +#include "nscore.h" +#include "nsXPComCIID.h" +#include "nsISocketTransportService.h" +#include "nsIEventQueueService.h" +#include "nsIServiceManager.h" +#include "nsITransport.h" +#include "nsIStreamObserver.h" +#include "nsIStreamListener.h" +#include "nsIInputStream.h" +#include "nsIByteBufferInputStream.h" + +#ifdef XP_PC +#define XPCOM_DLL "xpcom32.dll" +#else +#ifdef XP_MAC +#include "nsMacRepository.h" +#else +#define XPCOM_DLL "libxpcom.so" +#endif +#endif + +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); + +static int gKeepRunning = 1; +static PLEventQueue* gEventQ = nsnull; + +class InputTestConsumer : public nsIStreamListener +{ +public: + + InputTestConsumer(); + virtual ~InputTestConsumer(); + + // ISupports interface... + NS_DECL_ISUPPORTS + + // IStreamListener interface... + NS_IMETHOD OnStartBinding(nsISupports* context); + + NS_IMETHOD OnDataAvailable(nsISupports* context, + nsIInputStream *aIStream, + PRUint32 aLength); + + NS_IMETHOD OnStopBinding(nsISupports* context, + nsresult aStatus, + nsIString* aMsg); +}; + + +InputTestConsumer::InputTestConsumer() +{ + NS_INIT_REFCNT(); +} + +InputTestConsumer::~InputTestConsumer() +{ +} + + +NS_DEFINE_IID(kIStreamListenerIID, NS_ISTREAMLISTENER_IID); +NS_IMPL_ISUPPORTS(InputTestConsumer,kIStreamListenerIID); + + +NS_IMETHODIMP +InputTestConsumer::OnStartBinding(nsISupports* context) +{ +/// printf("\n+++ InputTestConsumer::OnStartBinding +++\n"); + return NS_OK; +} + + +NS_IMETHODIMP +InputTestConsumer::OnDataAvailable(nsISupports* context, + nsIInputStream *aIStream, + PRUint32 aLength) +{ + char buf[1025]; + PRUint32 amt; + do { + nsresult rv = aIStream->Read(buf, 1024, &amt); + buf[amt] = '\0'; + printf(buf); + } while (amt != 0); + + return NS_OK; +} + + +NS_IMETHODIMP +InputTestConsumer::OnStopBinding(nsISupports* context, + nsresult aStatus, + nsIString* aMsg) +{ + gKeepRunning = 0; +/// printf("\n+++ InputTestConsumer::OnStopBinding +++\n"); + return NS_OK; +} + + + +class TestWriteObserver : public nsIStreamObserver +{ +public: + + TestWriteObserver(nsITransport* aTransport); + virtual ~TestWriteObserver(); + + // ISupports interface... + NS_DECL_ISUPPORTS + + // IStreamObserver interface... + NS_IMETHOD OnStartBinding(nsISupports* context); + + NS_IMETHOD OnStopBinding(nsISupports* context, + nsresult aStatus, + nsIString* aMsg); +protected: + nsITransport* mTransport; +}; + + +TestWriteObserver::TestWriteObserver(nsITransport* aTransport) +{ + NS_INIT_REFCNT(); + mTransport = aTransport; + NS_ADDREF(mTransport); +} + +TestWriteObserver::~TestWriteObserver() +{ + NS_RELEASE(mTransport); +} + + +NS_IMPL_ISUPPORTS(TestWriteObserver,nsIStreamObserver::GetIID()); + + +NS_IMETHODIMP +TestWriteObserver::OnStartBinding(nsISupports* context) +{ +/// printf("\n+++ TestWriteObserver::OnStartBinding +++\n"); + return NS_OK; +} + + +NS_IMETHODIMP +TestWriteObserver::OnStopBinding(nsISupports* context, + nsresult aStatus, + nsIString* aMsg) +{ +/// printf("\n+++ TestWriteObserver::OnStopBinding +++\n"); + + mTransport->AsyncRead(nsnull, gEventQ, new InputTestConsumer); + return NS_OK; +} + + + + + +int +main(int argc, char* argv[]) +{ + nsresult rv; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return -1; + } + + char* hostName = argv[1]; + char* fileName = argv[2]; + int port = 80; + + // XXX why do I have to do this?! + nsComponentManager::RegisterComponent(kEventQueueServiceCID, NULL, NULL, XPCOM_DLL, PR_FALSE, PR_FALSE); + rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup, + "components"); + if (NS_FAILED(rv)) return rv; + + // Create the Event Queue for this thread... + NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = eventQService->CreateThreadEventQueue(); + if (NS_FAILED(rv)) return rv; + + eventQService->GetThreadEventQueue(PR_CurrentThread(), &gEventQ); + + // Create the Socket transport service... + NS_WITH_SERVICE(nsISocketTransportService, sts, kSocketTransportServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + // Create a stream for the data being written to the server... + nsIByteBufferInputStream* stream; + PRUint32 bytesWritten; + + rv = NS_NewByteBufferInputStream(&stream); + if (NS_FAILED(rv)) return rv; + + char *buffer = PR_smprintf("GET %s HTML/1.0\r\n\r\n", fileName); + stream->Fill(buffer, strlen(buffer), &bytesWritten); +/// printf("Request is: %s\n", buffer); + + // Create the socket transport... + nsITransport* transport; + rv = sts->CreateTransport(hostName, port, &transport); + if (NS_SUCCEEDED(rv)) { + TestWriteObserver* observer = new TestWriteObserver(transport); + + transport->AsyncWrite(stream, nsnull, gEventQ, observer); + + NS_RELEASE(transport); + } + + // Enter the message pump to allow the URL load to proceed. + while ( gKeepRunning ) { +#ifdef XP_PC + MSG msg; + + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + } + + sts->Shutdown(); + NS_RELEASE(sts); + NS_RELEASE(eventQService); + + return 0; +} + diff --git a/netwerk/test/TestSocketInput.cpp b/netwerk/test/TestSocketInput.cpp new file mode 100644 index 00000000000..79f6f8a587f --- /dev/null +++ b/netwerk/test/TestSocketInput.cpp @@ -0,0 +1,185 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "stdio.h" +#include + +#include "nscore.h" +#include "nsXPComCIID.h" +#include "nsISocketTransportService.h" +#include "nsIEventQueueService.h" +#include "nsIServiceManager.h" +#include "nsITransport.h" +#include "nsIStreamListener.h" +#include "nsIInputStream.h" + +#ifdef XP_PC +#define XPCOM_DLL "xpcom32.dll" +#else +#ifdef XP_MAC +#include "nsMacRepository.h" +#else +#define XPCOM_DLL "libxpcom.so" +#endif +#endif + +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); + +static int gKeepRunning = 1; + +class InputTestConsumer : public nsIStreamListener +{ +public: + + InputTestConsumer(); + virtual ~InputTestConsumer(); + + // ISupports interface... + NS_DECL_ISUPPORTS + + // IStreamListener interface... + NS_IMETHOD OnStartBinding(nsISupports* context); + + NS_IMETHOD OnDataAvailable(nsISupports* context, + nsIInputStream *aIStream, + PRUint32 aLength); + + NS_IMETHOD OnStopBinding(nsISupports* context, + nsresult aStatus, + nsIString* aMsg); +}; + + +InputTestConsumer::InputTestConsumer() +{ + NS_INIT_REFCNT(); +} + +InputTestConsumer::~InputTestConsumer() +{ +} + + +NS_DEFINE_IID(kIStreamListenerIID, NS_ISTREAMLISTENER_IID); +NS_IMPL_ISUPPORTS(InputTestConsumer,kIStreamListenerIID); + + +NS_IMETHODIMP +InputTestConsumer::OnStartBinding(nsISupports* context) +{ + printf("+++ OnStartBinding +++\n"); + return NS_OK; +} + + +NS_IMETHODIMP +InputTestConsumer::OnDataAvailable(nsISupports* context, + nsIInputStream *aIStream, + PRUint32 aLength) +{ + char buf[1025]; + while (aLength > 0) { + PRUint32 amt; + nsresult rv = aIStream->Read(buf, 1024, &amt); + buf[amt] = '\0'; + printf(buf); + aLength -= amt; + } + + return NS_OK; +} + + +NS_IMETHODIMP +InputTestConsumer::OnStopBinding(nsISupports* context, + nsresult aStatus, + nsIString* aMsg) +{ + gKeepRunning = 0; + printf("+++ OnStopBinding +++\n"); + return NS_OK; +} + + + + + +int +main(int argc, char* argv[]) +{ + nsresult rv; + + if (argc < 1) { + printf("usage: %s \n", argv[0]); + return -1; + } + + int port; + char* hostName = argv[1]; +//nsString portString(argv[2]); + +//port = portString.ToInteger(&rv); + port = 13; + + // XXX why do I have to do this?! + nsComponentManager::RegisterComponent(kEventQueueServiceCID, NULL, NULL, XPCOM_DLL, PR_FALSE, PR_FALSE); + rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup, + "components"); + if (NS_FAILED(rv)) return rv; + + // Create the Event Queue for this thread... + NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + PLEventQueue* eventQ; + rv = eventQService->CreateThreadEventQueue(); + if (NS_FAILED(rv)) return rv; + + eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQ); + + NS_WITH_SERVICE(nsISocketTransportService, sts, kSocketTransportServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + nsITransport* transport; + + rv = sts->CreateTransport(hostName, port, &transport); + if (NS_SUCCEEDED(rv)) { + transport->AsyncRead(nsnull, eventQ, new InputTestConsumer); + + NS_RELEASE(transport); + } + + // Enter the message pump to allow the URL load to proceed. + while ( gKeepRunning ) { +#ifdef XP_PC + MSG msg; + + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + } + + sts->Shutdown(); + NS_RELEASE(sts); + NS_RELEASE(eventQService); + + return 0; +} + diff --git a/netwerk/test/makefile.win b/netwerk/test/makefile.win index a41e094ddab..4121c6974bc 100644 --- a/netwerk/test/makefile.win +++ b/netwerk/test/makefile.win @@ -21,7 +21,9 @@ IGNORE_MANIFEST=1 MAKE_OBJ_TYPE = EXE PROG1 = .\$(OBJDIR)\TestFileInput.exe -PROGRAMS = $(PROG1) +PROG2 = .\$(OBJDIR)\TestSocketInput.exe +PROG3 = .\$(OBJDIR)\TestSocketIO.exe +PROGRAMS = $(PROG1) $(PROG2) $(PROG3) LCFLAGS=-DUSE_NSREG -GX @@ -68,3 +70,6 @@ clobber:: $(PROG1): $(OBJDIR) TestFileInput.cpp +$(PROG2): $(OBJDIR) TestSocketInput.cpp + +$(PROG3): $(OBJDIR) TestSocketIO.cpp