Mozilla/mozilla/netwerk/base/src/nsInputStreamChannel.cpp
dougt%netscape.com 175245e2de Relanding Necko Changes.
Revising nsIChannel to allow for overlapped i/o. This consists of three parts:

1. Factoring nsIChannel into a protocol specific part, the nsIChannel, and a socket specific, the nsITransport.
2. Derive the nsIChannel from a nsIRequest.
2. Changes the notification system from necko and the URILoader to pass the nsIRequest interface instead of nsIChannel interface.

This goal stems from wanting to be able to have active AsyncRead and AsyncWrite operations on nsSocketTransport.
This is desired because it would greatly simplify the task of maintaining persistent/reusable socket connections
for FTP, HTTP, and Imap (and potentially other protocols). The problem with the existing nsIChannel interface is
that it does not allow one to selectively suspend just one of the read or write operations while keeping the other active.

r=darin@netscape.com
sr=rpotts@netscape.com


git-svn-id: svn://10.0.0.236/trunk@87587 18797224-902f-48f8-a5cc-f745e15eee43
2001-02-21 20:38:08 +00:00

514 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsInputStreamChannel.h"
#include "nsCOMPtr.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIFileTransportService.h"
#include "netCore.h"
#include "nsXPIDLString.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
static NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID);
////////////////////////////////////////////////////////////////////////////////
// nsInputStreamIO methods:
NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamIO,
nsIInputStreamIO,
nsIStreamIO)
nsInputStreamIO::nsInputStreamIO()
: mName(nsnull), mContentType(nsnull), mContentLength(-1), mStatus(NS_OK)
{
NS_INIT_REFCNT();
}
nsInputStreamIO::~nsInputStreamIO()
{
(void)Close(NS_OK);
if (mName) nsCRT::free(mName);
if (mContentType) nsCRT::free(mContentType);
}
NS_METHOD
nsInputStreamIO::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
if (aOuter)
return NS_ERROR_NO_AGGREGATION;
nsInputStreamIO* io = new nsInputStreamIO();
if (io == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(io);
nsresult rv = io->QueryInterface(aIID, aResult);
NS_RELEASE(io);
return rv;
}
NS_IMETHODIMP
nsInputStreamIO::Init(const char* name, nsIInputStream* input,
const char* contentType, PRInt32 contentLength)
{
mName = nsCRT::strdup(name);
if (mName == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
mInputStream = input;
if (contentType) {
mContentType = nsCRT::strdup(contentType);
const char *constContentType = mContentType;
if (!constContentType) return NS_ERROR_OUT_OF_MEMORY;
char* semicolon = PL_strchr(constContentType, ';');
CBufDescriptor cbd(constContentType,
PR_TRUE,
semicolon ? (semicolon-constContentType) + 1: PL_strlen(constContentType), // capacity
semicolon ? (semicolon-constContentType) : PL_strlen(constContentType));
nsCAutoString(cbd).ToLowerCase();
}
mContentLength = contentLength;
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamIO::Open(char **contentType, PRInt32 *contentLength)
{
*contentType = nsCRT::strdup(mContentType);
if (*contentType == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
*contentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamIO::Close(nsresult status)
{
mStatus = status;
if (mInputStream)
return mInputStream->Close();
else
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamIO::GetInputStream(nsIInputStream * *aInputStream)
{
*aInputStream = mInputStream;
NS_ADDREF(*aInputStream);
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamIO::GetOutputStream(nsIOutputStream * *aOutputStream)
{
// this method should never be called
NS_NOTREACHED("nsInputStreamIO::GetOutputStream");
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsInputStreamIO::GetName(char* *aName)
{
*aName = nsCRT::strdup(mName);
return *aName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
////////////////////////////////////////////////////////////////////////////////
// nsStreamIOChannel methods:
nsStreamIOChannel::nsStreamIOChannel()
: mContentType(nsnull), mContentLength(-1),
mBufferSegmentSize(0), mBufferMaxSize(0),
mLoadAttributes(LOAD_NORMAL), mStatus(NS_OK)
{
NS_INIT_REFCNT();
}
nsStreamIOChannel::~nsStreamIOChannel()
{
if (mContentType) nsCRT::free(mContentType);
}
NS_METHOD
nsStreamIOChannel::Create(nsISupports *aOuter, REFNSIID aIID,
void **aResult)
{
if (aOuter)
return NS_ERROR_NO_AGGREGATION;
nsStreamIOChannel* channel = new nsStreamIOChannel();
if (channel == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(channel);
nsresult rv = channel->QueryInterface(aIID, aResult);
NS_RELEASE(channel);
return rv;
}
NS_IMETHODIMP
nsStreamIOChannel::Init(nsIURI* uri, nsIStreamIO* io)
{
mURI = uri;
mStreamIO = io;
return NS_OK;
}
NS_INTERFACE_MAP_BEGIN(nsStreamIOChannel)
NS_INTERFACE_MAP_ENTRY(nsIStreamIOChannel)
NS_INTERFACE_MAP_ENTRY(nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIStreamProvider)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIStreamObserver, nsIStreamListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
NS_INTERFACE_MAP_END_THREADSAFE
NS_IMPL_THREADSAFE_ADDREF(nsStreamIOChannel)
NS_IMPL_THREADSAFE_RELEASE(nsStreamIOChannel)
NS_IMETHODIMP
nsStreamIOChannel::GetName(PRUnichar* *result)
{
nsresult rv;
nsXPIDLCString urlStr;
rv = mURI->GetSpec(getter_Copies(urlStr));
if (NS_FAILED(rv)) return rv;
nsString name;
name.AppendWithConversion(urlStr);
*result = name.ToNewUnicode();
return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsStreamIOChannel::IsPending(PRBool *result)
{
if (mRequest)
return mRequest->IsPending(result);
*result = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetStatus(nsresult *status)
{
*status = mStatus;
// if we don't have a status error of our own to report
// then we should propogate the status error of the underlying
// file transport (if we have one)
if (NS_SUCCEEDED(mStatus) && mRequest)
mRequest->GetStatus(status);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::Cancel(nsresult status)
{
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
mStatus = status;
if (mRequest)
return mRequest->Cancel(status);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::Suspend(void)
{
if (mRequest)
return mRequest->Suspend();
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::Resume(void)
{
if (mRequest)
return mRequest->Resume();
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannel implementation:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsStreamIOChannel::GetOriginalURI(nsIURI* *aURI)
{
*aURI = mOriginalURI ? mOriginalURI : mURI;
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetOriginalURI(nsIURI* aURI)
{
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetURI(nsIURI* *aURI)
{
*aURI = mURI;
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetURI(nsIURI* aURI)
{
mURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::Open(nsIInputStream **result)
{
return mStreamIO->GetInputStream(result);
}
NS_IMETHODIMP
nsStreamIOChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
{
nsresult rv;
NS_ASSERTION(listener, "no listener");
SetListener(listener);
if (mLoadGroup) {
nsCOMPtr<nsILoadGroupListenerFactory> factory;
//
// Create a load group "proxy" listener...
//
rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory));
if (factory) {
nsIStreamListener *newListener;
rv = factory->CreateLoadGroupListener(GetListener(), &newListener);
if (NS_SUCCEEDED(rv)) {
mUserObserver = newListener;
NS_RELEASE(newListener);
}
}
rv = mLoadGroup->AddRequest(this, nsnull);
if (NS_FAILED(rv)) return rv;
}
if (mFileTransport == nsnull) {
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) goto done;
rv = fts->CreateTransportFromStreamIO(mStreamIO, getter_AddRefs(mFileTransport));
if (NS_FAILED(rv)) goto done;
}
#if 0
if (mContentType == nsnull) {
rv = mStreamIO->Open(&mContentType, &mContentLength);
if (NS_FAILED(rv)) goto done;
}
#endif
rv = mFileTransport->AsyncRead(this, ctxt, 0, -1, 0, getter_AddRefs(mRequest));
done:
if (NS_FAILED(rv)) {
nsresult rv2;
rv2 = mLoadGroup->RemoveRequest(this, ctxt, rv, nsnull);
NS_ASSERTION(NS_SUCCEEDED(rv2), "RemoveRequest failed");
// release the transport so that we don't think we're in progress
mFileTransport = nsnull;
}
return rv;
}
NS_IMETHODIMP
nsStreamIOChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
*aLoadAttributes = mLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
mLoadAttributes = aLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup.get();
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
*aNotificationCallbacks = mCallbacks.get();
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
mCallbacks = aNotificationCallbacks;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetContentType(char * *aContentType)
{
nsresult rv;
if (mContentType == nsnull) {
rv = mStreamIO->Open(&mContentType, &mContentLength);
if (NS_FAILED(rv)) return rv;
}
*aContentType = nsCRT::strdup(mContentType);
if (*aContentType == nsnull) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetContentType(const char *aContentType)
{
mContentType = nsCRT::strdup(aContentType);
if (*aContentType == nsnull) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::GetContentLength(PRInt32 *aContentLength)
{
nsresult rv;
if (mContentType == nsnull) {
rv = mStreamIO->Open(&mContentType, &mContentLength);
if (NS_FAILED(rv)) return rv;
}
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsStreamIOChannel::SetContentLength(PRInt32 aContentLength)
{
mContentLength = aContentLength;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIStreamObserver implementation:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsStreamIOChannel::OnStartRequest(nsIRequest *request, nsISupports* context)
{
NS_ASSERTION(mUserObserver, "No listener...");
return mUserObserver->OnStartRequest(this, context);
}
NS_IMETHODIMP
nsStreamIOChannel::OnStopRequest(nsIRequest *request, nsISupports* context,
nsresult aStatus, const PRUnichar* aStatusArg)
{
nsresult rv;
if (mUserObserver) {
rv = mUserObserver->OnStopRequest(this, context, aStatus, aStatusArg);
if (NS_FAILED(rv)) return rv;
}
if (mLoadGroup) {
if (NS_SUCCEEDED(rv)) {
rv = mLoadGroup->RemoveRequest(this, context, aStatus, aStatusArg);
if (NS_FAILED(rv)) return rv;
}
}
// Release the reference to the consumer stream listener...
mUserObserver = null_nsCOMPtr();
mFileTransport = null_nsCOMPtr();
return mStreamIO->Close(aStatus);
}
////////////////////////////////////////////////////////////////////////////////
// nsIStreamListener implementation:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsStreamIOChannel::OnDataAvailable(nsIRequest *request, nsISupports* context,
nsIInputStream *aIStream, PRUint32 aSourceOffset,
PRUint32 aLength)
{
return GetListener()->OnDataAvailable(this, context, aIStream,
aSourceOffset, aLength);
}
////////////////////////////////////////////////////////////////////////////////
// nsIStreamProvider implementation:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsStreamIOChannel::OnDataWritable(nsIRequest *request, nsISupports *context,
nsIOutputStream *aOStream,
PRUint32 aOffset, PRUint32 aLength)
{
return GetProvider()->OnDataWritable(this, context, aOStream,
aOffset, aLength);
}
////////////////////////////////////////////////////////////////////////////////