First pass at a socket transport implementation...

This commit is contained in:
rpotts%netscape.com 1999-04-19 07:17:37 +00:00
Родитель 6064f90d77
Коммит 722b00f7fa
11 изменённых файлов: 1791 добавлений и 14 удалений

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

@ -23,6 +23,7 @@ EXPORTS = \
nsICancelable.h \
nsIConnectionGroup.h \
nsIFileTransportService.h \
nsISocketTransportService.h \
nsINetService.h \
nsIProtocolConnection.h \
nsIProtocolHandler.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;
};
////////////////////////////////////////////////////////////////////////////////

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

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

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

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

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

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

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

@ -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___ */

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

@ -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<mSelectFDSetCount; i++) {
if (mActiveTransportList[i] == aTransport) {
int last = mSelectFDSetCount-1;
NS_RELEASE(mActiveTransportList[i]);
// Move the last element in the array into the new empty slot...
if (i != last) {
memcpy(&mSelectFDSet[i], &mSelectFDSet[last], sizeof(mSelectFDSet[0]));
mSelectFDSet[last].fd = nsnull;
mActiveTransportList[i] = mActiveTransportList[last];
mActiveTransportList[last] = nsnull;
} else {
mSelectFDSet[i].fd = nsnull;
mActiveTransportList[i] = nsnull;
}
mSelectFDSetCount -= 1;
rv = NS_OK;
break;
}
}
return rv;
}
//
// --------------------------------------------------------------------------
// nsISupports implementation...
// --------------------------------------------------------------------------
//
NS_IMPL_ADDREF(nsSocketTransportService);
NS_IMPL_RELEASE(nsSocketTransportService);
NS_IMETHODIMP
nsSocketTransportService::QueryInterface(const nsIID& aIID, void* *aInstancePtr)
{
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(nsISocketTransportService::GetIID()) ||
aIID.Equals(kISupportsIID)) {
*aInstancePtr = NS_STATIC_CAST(nsISocketTransportService*, this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(nsIRunnable::GetIID())) {
*aInstancePtr = NS_STATIC_CAST(nsIRunnable*, this);
NS_ADDREF_THIS();
return NS_OK;
}
return NS_NOINTERFACE;
}
//
// --------------------------------------------------------------------------
// nsIRunnable implementation...
// --------------------------------------------------------------------------
//
NS_IMETHODIMP
nsSocketTransportService::Run(void)
{
//
// Initialize the FDSET used by PR_Poll(...). The first item in the FDSet
// is *always* the pollable event (ie. mThreadEvent).
//
mSelectFDSet[0].fd = mThreadEvent;
mSelectFDSet[0].in_flags = PR_POLL_READ;
mSelectFDSetCount = 1;
while (mThreadRunning) {
PRInt32 count;
nsSocketTransport* transport;
// XXX: PR_Poll(...) needs a timeout value...
count = PR_Poll(mSelectFDSet, mSelectFDSetCount, PR_INTERVAL_NO_TIMEOUT);
/* One or more sockets has data... */
if (count > 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;
}

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

@ -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___ */

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

@ -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 <windows.h>
#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 <host> <path>\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;
}

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

@ -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 <windows.h>
#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 <host>\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;
}

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

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