зеркало из https://github.com/mozilla/gecko-dev.git
428 строки
12 KiB
C++
428 строки
12 KiB
C++
/* -*- 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 "nsIComponentManager.h"
|
|
#include "nsNetStream.h"
|
|
#include "nsSocketTransport.h"
|
|
#include "nsINetService.h"
|
|
#include "nsINetlibURL.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "netutils.h"
|
|
#include "mktcp.h"
|
|
#include "sockstub.h"
|
|
|
|
#include "plstr.h"
|
|
|
|
extern nsIStreamListener* ns_NewStreamListenerProxy(nsIStreamListener* aListener, nsIEventQueue* aEventQ);
|
|
static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
|
|
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
|
static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
|
static NS_DEFINE_IID(kIEventQueueServiceIID, NS_IEVENTQUEUESERVICE_IID);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// from nsISupports
|
|
|
|
static NS_DEFINE_IID(kITransportIID, NS_ITRANSPORT_IID);
|
|
NS_IMPL_ADDREF(nsSocketTransport)
|
|
NS_IMPL_RELEASE(nsSocketTransport)
|
|
NS_IMPL_QUERY_INTERFACE(nsSocketTransport, kITransportIID);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// from nsITransport:
|
|
|
|
NS_METHOD
|
|
nsSocketTransport::GetURL(nsIURI* *result)
|
|
{
|
|
*result = m_url;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
static NS_DEFINE_IID(kINetlibURLIID, NS_INETLIBURL_IID);
|
|
|
|
NS_METHOD nsSocketTransport::IsTransportOpen(PRBool * aSocketOpen)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (aSocketOpen)
|
|
*aSocketOpen = m_socketIsOpen;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Open means take the url and make a socket connection with the host...
|
|
The user will then write data to be sent to the host via our input stream...
|
|
*/
|
|
|
|
NS_METHOD nsSocketTransport::Open(nsIURI *pURL)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
// only actually try to do anything if we aren't open for business yet...
|
|
if (m_socketIsOpen == PR_FALSE)
|
|
{
|
|
m_socketIsOpen = PR_TRUE;
|
|
|
|
if (nsnull == pURL)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull == m_inputStreamConsumer)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_IF_ADDREF(pURL);
|
|
m_url = pURL;
|
|
|
|
if (!m_isFileConnection)
|
|
{
|
|
pURL->SetHostPort(m_port);
|
|
const char * hostName = nsnull;
|
|
pURL->GetHost(&hostName);
|
|
if (hostName)
|
|
{
|
|
PR_FREEIF(m_hostName);
|
|
m_hostName = PL_strdup(hostName);
|
|
}
|
|
}
|
|
|
|
// mscott --> okay here's where the fact that the transport implementation is just a hack on
|
|
// the old world comes in....right now this transport class supports both socket and file based
|
|
// connections. The protocol is blind to this. They just gave us a url like: news://newshost.
|
|
// this particular url wants a network based socket so we need to use the sockstub hack. In order
|
|
// to do this, we need to change the protocol part of the url string to sockstub....
|
|
// if the url wants a file based socket connection, we change the protocol part of the url to
|
|
// file:// and piggy back off of the file protocol code. Why do we do this here? Well the rest of the
|
|
// app (both the app who runs the url and the protocol which is handling the url) can use protocol
|
|
// url parts like imap, mailbox, http, etc. Then when they are done with it and hand it to us for
|
|
// the underlying socket to be opened, we can turn around and tweek the protocol name to use the old
|
|
// world technology to build it (sockstub and file).
|
|
|
|
if (m_isFileConnection)
|
|
pURL->SetProtocol("file");
|
|
else // we want a network socket so use sockstub
|
|
pURL->SetProtocol("sockstub");
|
|
|
|
// now the right protocol is set for the old networking world to use to complete the task
|
|
// so we can open it now..
|
|
rv = NS_OpenURL(pURL, m_inputStreamConsumer);
|
|
|
|
m_socketIsOpen = PR_TRUE;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsSocketTransport::SetInputStreamConsumer(nsIStreamListener* aListener)
|
|
{
|
|
// generates the thread safe proxy
|
|
m_inputStreamConsumer = ns_NewStreamListenerProxy(aListener, m_evQueue);
|
|
NS_IF_ADDREF(m_inputStreamConsumer);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_METHOD
|
|
nsSocketTransport::GetOutputStreamConsumer(nsIStreamListener ** aConsumer)
|
|
{
|
|
if (aConsumer)
|
|
{
|
|
// assuming the transport layer is a nsIStreamListener
|
|
*aConsumer = ns_NewStreamListenerProxy(this, m_evQueue);
|
|
// add ref bfore returning...
|
|
NS_IF_ADDREF(*aConsumer);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_METHOD
|
|
nsSocketTransport::GetOutputStream(nsIOutputStream ** aOutputStream)
|
|
{
|
|
if (aOutputStream) {
|
|
// a buffered stream supports the nsIOutputStream interface
|
|
nsBufferedStream * stream = new nsBufferedStream();
|
|
if (stream) // return the buffer in the format they want
|
|
stream->QueryInterface(kIOutputStreamIID, (void **) aOutputStream);
|
|
else
|
|
*aOutputStream = NULL;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::OnStartRequest(nsIURI* pURL, const char *aContentType)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::OnProgress(nsIURI* pURL,
|
|
PRUint32 aProgress,
|
|
PRUint32 aProgressMax)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::OnStatus(nsIURI* pURL, const PRUnichar* aMsg)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::OnStopRequest(nsIURI* pURL,
|
|
nsresult aStatus,
|
|
const PRUnichar* aMsg)
|
|
{
|
|
// if the protocol instance called OnStopRequest then they are effectively asking us
|
|
// to close the connection. But they don't want US to go away.....
|
|
// so we need to close the underlying socket....
|
|
CloseCurrentConnection();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::GetBindInfo(nsIURI* pURL,
|
|
nsStreamBindingInfo* aInfo)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::OnDataAvailable(nsIURI* pURL,
|
|
nsIInputStream *aIStream,
|
|
PRUint32 aLength)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRUint32 len, lenRead;
|
|
URL_Struct* URL_s;
|
|
m_ready_fd = NULL;
|
|
|
|
/* XXX: HACK The following is a hack.
|
|
* Get the URL_s structure and pass it to sockstub protocol code to get the
|
|
* socket FD so that we could write the data back.
|
|
*/
|
|
|
|
NS_PRECONDITION(m_socketIsOpen, "Uhoh, you tried to write to a socket before opening it by calling open...");
|
|
|
|
if (m_socketIsOpen == PR_TRUE) // only do something useful if we are open...
|
|
{
|
|
rv = GetURLInfo(pURL, &URL_s); // this call is necessay 'cause it forces us to get m_bindedUrlStruct if we don't have it.
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
if (nsnull != m_bindedUrlStruct)
|
|
/* Find the socket given URL_s pointer */
|
|
m_ready_fd = NET_GetSocketToHashTable(m_bindedUrlStruct);
|
|
}
|
|
else
|
|
return rv;
|
|
|
|
if (m_ready_fd == NULL)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
aIStream->GetLength(&len);
|
|
|
|
memset(m_buffer, '\0', NET_SOCKSTUB_BUF_SIZE);
|
|
while (len > 0)
|
|
{
|
|
if (len < NET_SOCKSTUB_BUF_SIZE)
|
|
lenRead = len;
|
|
else
|
|
lenRead = NET_SOCKSTUB_BUF_SIZE;
|
|
|
|
rv = aIStream->Read(m_buffer, lenRead, &lenRead);
|
|
if (NS_OK != rv)
|
|
return rv;
|
|
|
|
/* XXX: We should check if the write has succeeded or not */
|
|
(int) NET_BlockingWrite(m_ready_fd, m_buffer, lenRead);
|
|
len -= lenRead;
|
|
}
|
|
}
|
|
else
|
|
rv = NS_ERROR_FAILURE; // stream was not open...
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// from nsSocketTransport:
|
|
|
|
nsSocketTransport::nsSocketTransport(PRUint32 aPortToUse, const char * aHostName)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
|
|
// remember the port and host name
|
|
NS_PRECONDITION(aPortToUse > 0 && aHostName, "Creating a socket transport with an invalid port or host name.");
|
|
m_fileName = nsnull;
|
|
m_port = aPortToUse;
|
|
if (aHostName)
|
|
m_hostName = PL_strdup(aHostName);
|
|
else
|
|
m_hostName = PL_strdup("");
|
|
|
|
m_isFileConnection = PR_FALSE;
|
|
|
|
// common initialization code
|
|
Initialize();
|
|
}
|
|
|
|
nsSocketTransport::nsSocketTransport(const char * fileName)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
m_fileName = nsnull;
|
|
NS_PRECONDITION(fileName && *fileName, "Creating a socket with an invalid file name.");
|
|
if (fileName)
|
|
{
|
|
m_fileName = PL_strdup(fileName);
|
|
}
|
|
|
|
m_isFileConnection = PR_TRUE;
|
|
m_hostName = nsnull; // initialize value
|
|
|
|
// common initialization code
|
|
Initialize();
|
|
}
|
|
|
|
void nsSocketTransport::Initialize()
|
|
{
|
|
m_inputStream = NULL;
|
|
m_inputStreamConsumer = NULL;
|
|
m_url = NULL;
|
|
m_bindedUrlStruct = NULL;
|
|
|
|
m_outStream = NULL;
|
|
m_outStreamSize = 0;
|
|
m_socketIsOpen = PR_FALSE; // initially, we have not opened the socket...
|
|
|
|
/*
|
|
* Cache the EventQueueService...
|
|
*/
|
|
// XXX: What if this fails?
|
|
|
|
mEventQService = nsnull;
|
|
m_evQueue = nsnull;
|
|
nsresult rv = nsServiceManager::GetService(kEventQueueServiceCID,
|
|
kIEventQueueServiceIID,
|
|
(nsISupports **)&mEventQService);
|
|
|
|
if (nsnull != mEventQService)
|
|
mEventQService->GetThreadEventQueue(PR_GetCurrentThread(), &m_evQueue);
|
|
}
|
|
|
|
nsSocketTransport::~nsSocketTransport()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
/* XXX: HACK The following is a hack.
|
|
* Get the URL_s structure and pass it to sockstub protocol code to delete it
|
|
*/
|
|
|
|
rv = CloseCurrentConnection();
|
|
|
|
PR_FREEIF(m_fileName);
|
|
|
|
NS_IF_RELEASE(mEventQService);
|
|
NS_IF_RELEASE(m_evQueue);
|
|
NS_IF_RELEASE(m_outStream);
|
|
NS_IF_RELEASE(m_url);
|
|
NS_IF_RELEASE(m_inputStreamConsumer);
|
|
}
|
|
|
|
NS_IMETHODIMP nsSocketTransport::SetSocketBinding(nsIURI* pURL, URL_Struct_ ** aResult)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsINetlibURL *pNetlibURL = NULL;
|
|
if (pURL && aResult)
|
|
{
|
|
if (pURL)
|
|
rv = pURL->QueryInterface(kINetlibURLIID, (void**)&pNetlibURL);
|
|
if (NS_SUCCEEDED(rv) && pNetlibURL)
|
|
{
|
|
pNetlibURL->GetURLInfo(aResult);
|
|
NS_RELEASE(pNetlibURL);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSocketTransport::GetURLInfo(nsIURI* pURL, URL_Struct_ **aResult)
|
|
{
|
|
// hack alert!!! we use a url struct to bind a socket transport to an active entry
|
|
// running the sockstub protocol. however the url struct is bound to the url. if you run
|
|
// another url through the socket, you'll get a different active entry back! (i.e.
|
|
// you won't get the original active entry that should be bound to the transport.
|
|
// so i'm going to remember the url_struct used to create the active entry and we'll
|
|
// always return that url struct for the lifetime ofthe transport. This will ensure,
|
|
// that we always get the original active entry and those allows us to run multiple
|
|
// urls through this connection.
|
|
|
|
SetSocketBinding(pURL, aResult);
|
|
|
|
if (!m_bindedUrlStruct && aResult)
|
|
m_bindedUrlStruct = *aResult;
|
|
else // we must be running a new url.....
|
|
{
|
|
// this is a god awful hack...i'm ashamed.....swap the url associated with
|
|
// the connection info out from under us with the new url...pURL.
|
|
|
|
nsIConnectionInfo * pConn = (nsIConnectionInfo *) m_bindedUrlStruct->fe_data;
|
|
if (pConn)
|
|
pConn->SetURL(pURL);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsSocketTransport::CloseCurrentConnection()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
URL_Struct_ * URL_s = nsnull;
|
|
|
|
// mscott --> we used to interrupt the stream for the current url
|
|
// here. However, the stream has already been interrupted! A typical
|
|
// scenario: we lose a connection with the server. the stream is interrupted
|
|
// and it tells the protocol that the connection is going down (through an
|
|
// on stop binding call). The protocol is then releasing the transport which
|
|
// is causing close current connection to get called. But the stream
|
|
// has already been interrupted.....that's what started the whole process.
|
|
// so don't add code to interrupt it again.
|
|
|
|
// Note: If we need to provide the ability for the protocol to interrupt
|
|
// the stream manually then we need a separate method in the transport
|
|
// interface which would be used explicitly for protocol driven close
|
|
// connection rather than netlib driven close connection...
|
|
|
|
// now free any per socket state information...
|
|
m_socketIsOpen = PR_FALSE;
|
|
|
|
NS_IF_RELEASE(m_url);
|
|
m_url = nsnull;
|
|
PR_FREEIF(m_hostName);
|
|
return rv;
|
|
}
|