/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim:set ts=4 sw=4 sts=4 et cin: *//* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla 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/MPL/ * * 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. * * The Initial Developer of the Original Code is * Netscape Communications. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Darin Fisher <darin@netscape.com> (original author) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */#include"nsHttpConnection.h"#include"nsHttpTransaction.h"#include"nsHttpRequestHead.h"#include"nsHttpResponseHead.h"#include"nsHttpHandler.h"#include"nsIOService.h"#include"nsISocketTransportService.h"#include"nsISocketTransport.h"#include"nsIServiceManager.h"#include"nsISSLSocketControl.h"#include"nsStringStream.h"#include"netCore.h"#include"nsNetCID.h"#include"nsAutoLock.h"#include"prmem.h"#ifdef DEBUG// defined by the socket transport service while activeexternPRThread*gSocketThread;#endifstaticNS_DEFINE_CID(kSocketTransportServiceCID,NS_SOCKETTRANSPORTSERVICE_CID);//-----------------------------------------------------------------------------// nsHttpConnection <public>//-----------------------------------------------------------------------------nsHttpConnection::nsHttpConnection():mTransaction(nsnull),mConnInfo(nsnull),mLock(nsnull),mLastReadTime(0),mIdleTimeout(0),mKeepAlive(PR_TRUE)// assume to keep-alive by default,mKeepAliveMask(PR_TRUE),mSupportsPipelining(PR_FALSE)// assume low-grade server,mIsReused(PR_FALSE),mCompletedSSLConnect(PR_FALSE){LOG(("Creating nsHttpConnection @%x\n",this));// grab a reference to the handler to ensure that it doesn't go away.nsHttpHandler*handler=gHttpHandler;NS_ADDREF(handler);}nsHttpConnection::~nsHttpConnection(){LOG(("Destroying nsHttpConnection @%x\n",this));NS_IF_RELEASE(mConnInfo);NS_IF_RELEASE(mTransaction);if(mLock){PR_DestroyLock(mLock);mLock=nsnull;}// release our reference to the handlernsHttpHandler*handler=gHttpHandler;NS_RELEASE(handler);}nsresultnsHttpConnection::Init(nsHttpConnectionInfo*info,PRUint16maxHangTime){LOG(("nsHttpConnection::Init [this=%x]\n",this));NS_ENSURE_ARG_POINTER(info);NS_ENSURE_TRUE(!mConnInfo,NS_ERROR_ALREADY_INITIALIZED);mLock=PR_NewLock();if(!mLock)returnNS_ERROR_OUT_OF_MEMORY;mConnInfo=info;NS_ADDREF(mConnInfo);mMaxHangTime=maxHangTime;mLastReadTime=NowInSeconds();returnNS_OK;}// called on the socket threadnsresultnsHttpConnection::Activate(nsAHttpTransaction*trans,PRUint8caps){nsresultrv;LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",this,trans,caps));NS_ENSURE_ARG_POINTER(trans);NS_ENSURE_TRUE(!mTransaction,NS_ERROR_IN_PROGRESS);// take ownership of the transactionmTransaction=trans;NS_ADDREF(mTransaction);// set mKeepAlive according to what will be requestedmKeepAliveMask=mKeepAlive=(caps&NS_HTTP_ALLOW_KEEPALIVE);// if we don't have a socket transport then create a new oneif(!mSocketTransport){rv=CreateTransport(caps);if(NS_FAILED(rv))gotoloser;}// need to handle SSL proxy CONNECT if this is the first time.if(mConnInfo->UsingSSL()&&mConnInfo->UsingHttpProxy()&&!mCompletedSSLConnect){rv=SetupSSLProxyConnect();if(NS_FAILED(rv))gotoloser;}// wait for the output stream to be readablerv=mSocketOut->AsyncWait(this,0,0,nsnull);if(NS_SUCCEEDED(rv))returnrv;loser:NS_RELEASE(mTransaction);returnrv;}voidnsHttpConnection::Close(nsresultreason){LOG(("nsHttpConnection::Close [this=%x reason=%x]\n",this,reason));NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");if(NS_FAILED(reason)){if(mSocketTransport){mSocketTransport->SetSecurityCallbacks(nsnull);mSocketTransport->SetEventSink(nsnull,nsnull);mSocketTransport->Close(reason);}mKeepAlive=PR_FALSE;}}// called on the socket threadnsresultnsHttpConnection::ProxyStartSSL(){LOG(("nsHttpConnection::ProxyStartSSL [this=%x]\n",this));#ifdef DEBUGNS_PRECONDITION(PR_GetCurrentThread()==gSocketThread,"wrong thread");#endifnsCOMPtr<nsISupports>securityInfo;nsresultrv=mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));if(NS_FAILED(rv))returnrv;nsCOMPtr<nsISSLSocketControl>ssl=do_QueryInterface(securityInfo,&rv);if(NS_FAILED(rv))returnrv;returnssl->ProxyStartSSL();}PRBoolnsHttpConnection::CanReuse(){returnIsKeepAlive()&&(NowInSeconds()-mLastReadTime<mIdleTimeout)&&IsAlive();}PRBoolnsHttpConnection::IsAlive(){if(!mSocketTransport)returnPR_FALSE;PRBoolalive;nsresultrv=mSocketTransport->IsAlive(&alive);if(NS_FAILED(rv))alive=PR_FALSE;//#define TEST_RESTART_LOGIC#ifdef TEST_RESTART_LOGICif(!alive){LOG(("pretending socket is still alive to test restart logic\n"));alive=PR_TRUE;}#endifreturnalive;}PRBoolnsHttpConnection::SupportsPipelining(nsHttpResponseHead*responseHead){// XXX there should be a strict mode available that disables this// blacklisting.// assuming connection is HTTP/1.1 with keep-alive enabledif(mConnInfo->UsingHttpProxy()&&!mConnInfo->UsingSSL()){// XXX check for bad proxy servers...returnPR_TRUE;}// XXX what about checking for a Via header? (transparent proxies)// check for bad origin serversconstchar*val=responseHead->PeekHeader(nsHttp::Server);if(!val)returnPR_FALSE;// no header, no love// the list of servers known to do bad things with pipelined requestsstaticconstchar*bad_servers[]={"Microsoft-IIS/4.","Microsoft-IIS/5.","Netscape-Enterprise/3.",nsnull};for(constchar**server=bad_servers;*server;++server){if(PL_strcasestr(val,*server)!=nsnull){LOG(("looks like this server does not support pipelining"));returnPR_FALSE;}}// ok, let's allow pipelining to this serverreturnPR_TRUE;}//----------------------------------------------------------------------------// nsHttpConnection::nsAHttpConnection compatible methods//----------------------------------------------------------------------------nsresultnsHttpConnection::OnHeadersAvailable(nsAHttpTransaction*trans,nsHttpRequestHead*requestHead,nsHttpResponseHead*responseHead,PRBool*reset){LOG(("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p response-head=%p]\n",this,trans,responseHead));NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");NS_ENSURE_ARG_POINTER(trans);NS_ASSERTION(responseHead,"No response head?");// If the server issued an explicit timeout, then we need to close down the// socket transport. We pass an error code of NS_ERROR_NET_RESET to// trigger the transactions 'restart' mechanism. We tell it to reset its// response headers so that it will be ready to receive the new response.if(responseHead->Status()==408){Close(NS_ERROR_NET_RESET);*reset=PR_TRUE;returnNS_OK;}// we won't change our keep-alive policy unless the server has explicitly// told us to do so.// inspect the connection headers for keep-alive info provided the// transaction completed successfully.constchar*val=responseHead->PeekHeader(nsHttp::Connection);if(!val)val=responseHead->PeekHeader(nsHttp::Proxy_Connection);// reset to default (the server may have changed since we last checked)mSupportsPipelining=PR_FALSE;if((responseHead->Version()<NS_HTTP_VERSION_1_1)||(requestHead->Version()<NS_HTTP_VERSION_1_1)){// HTTP/1.0 connections are by default NOT persistentif(val&&!PL_strcasecmp(val,"keep-alive"))mKeepAlive=PR_TRUE;elsemKeepAlive=PR_FALSE;}else{// HTTP/1.1 connections are by default persistentif(val&&!PL_strcasecmp(val,"close"))mKeepAlive=PR_FALSE;else{mKeepAlive=PR_TRUE;// Do not support pipelining when we are establishing// an SSL tunnel though an HTTP proxy. Pipelining support// determination must be based on comunication with the// target server in this case. See bug 422016 for futher// details.if(!mSSLProxyConnectStream)mSupportsPipelining=SupportsPipelining(responseHead);}}mKeepAliveMask=mKeepAlive;// if this connection is persistent, then the server may send a "Keep-Alive"// header specifying the maximum number of times the connection can be// reused as well as the maximum amount of time the connection can be idle// before the server will close it. we ignore the max reuse count, because// a "keep-alive" connection is by definition capable of being reused, and// we only care about being able to reuse it once. if a timeout is not // specified then we use our advertized timeout value.if(mKeepAlive){val=responseHead->PeekHeader(nsHttp::Keep_Alive);constchar*cp=PL_strcasestr(val,"timeout=");if(cp)mIdleTimeout=(PRUint32)atoi(cp+8);elsemIdleTimeout=gHttpHandler->IdleTimeout();LOG(("Connection can be reused [this=%x idle-timeout=%u]\n",this,mIdleTimeout));}// if we're doing an SSL proxy connect, then we need to check whether or not// the connect was successful. if so, then we have to reset the transaction// and step-up the socket connection to SSL. finally, we have to wake up the// socket write request.if(mSSLProxyConnectStream){mSSLProxyConnectStream=0;if(responseHead->Status()==200){LOG(("SSL proxy CONNECT succeeded!\n"));*reset=PR_TRUE;nsresultrv=ProxyStartSSL();if(NS_FAILED(rv))// XXX need to handle this for realLOG(("ProxyStartSSL failed [rv=%x]\n",rv));mCompletedSSLConnect=PR_TRUE;rv=mSocketOut->AsyncWait(this,0,0,nsnull);// XXX what if this fails -- need to handle this errorNS_ASSERTION(NS_SUCCEEDED(rv),"mSocketOut->AsyncWait failed");}else{LOG(("SSL proxy CONNECT failed!\n"));// NOTE: this cast is valid since this connection cannot be// processing a transaction pipeline until after the first HTTP/1.1// response.nsHttpTransaction*trans=static_cast<nsHttpTransaction*>(mTransaction);trans->SetSSLConnectFailed();}}returnNS_OK;}voidnsHttpConnection::GetSecurityInfo(nsISupports**secinfo){NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");if(mSocketTransport){if(NS_FAILED(mSocketTransport->GetSecurityInfo(secinfo)))*secinfo=nsnull;}}nsresultnsHttpConnection::ResumeSend(){LOG(("nsHttpConnection::ResumeSend [this=%p]\n",this));NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");if(mSocketOut)returnmSocketOut->AsyncWait(this,0,0,nsnull);NS_NOTREACHED("no socket output stream");returnNS_ERROR_UNEXPECTED;}nsresultnsHttpConnection::ResumeRecv(){LOG(("nsHttpConnection::ResumeRecv [this=%p]\n",this));NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");if(mSocketIn)returnmSocketIn->AsyncWait(this,0,0,nsnull);NS_NOTREACHED("no socket input stream");returnNS_ERROR_UNEXPECTED;}//-----------------------------------------------------------------------------// nsHttpConnection <private>//-----------------------------------------------------------------------------nsresultnsHttpConnection::CreateTransport(PRUint8caps){nsresultrv;NS_PRECONDITION(!mSocketTransport,"unexpected");nsCOMPtr<nsISocketTransportService>sts=do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID,&rv);if(NS_FAILED(rv))returnrv;// configure the socket type based on the connection type requested.constchar*types[1];if(mConnInfo->UsingSSL())types[0]="ssl";elsetypes[0]=gHttpHandler->DefaultSocketType();nsCOMPtr<nsISocketTransport>strans;PRUint32typeCount=(types[0]!=nsnull);rv=sts->CreateTransport(types,typeCount,nsDependentCString(mConnInfo->Host()),mConnInfo->Port(),mConnInfo->ProxyInfo(),getter_AddRefs(strans));if(NS_FAILED(rv))returnrv;PRUint32tmpFlags=0;if(caps&NS_HTTP_REFRESH_DNS)tmpFlags=nsISocketTransport::BYPASS_CACHE;if(caps&NS_HTTP_LOAD_ANONYMOUS)tmpFlags|=nsISocketTransport::ANONYMOUS_CONNECT;strans->SetConnectionFlags(tmpFlags);// NOTE: these create cyclical references, which we break inside// nsHttpConnection::Closerv=strans->SetEventSink(this,nsnull);if(NS_FAILED(rv))returnrv;rv=strans->SetSecurityCallbacks(this);if(NS_FAILED(rv))returnrv;// next open the socket streamsnsCOMPtr<nsIOutputStream>sout;rv=strans->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,0,0,getter_AddRefs(sout));if(NS_FAILED(rv))returnrv;nsCOMPtr<nsIInputStream>sin;rv=strans->OpenInputStream(nsITransport::OPEN_UNBUFFERED,0,0,getter_AddRefs(sin));if(NS_FAILED(rv))returnrv;mSocketTransport=strans;mSocketIn=do_QueryInterface(sin);mSocketOut=do_QueryInterface(sout);returnNS_OK;}voidnsHttpConnection::CloseTransaction(nsAHttpTransaction*trans,nsresultreason){LOG(("nsHttpConnection::CloseTransaction[this=%x trans=%x reason=%x]\n",this,trans,reason));NS_ASSERTION(trans==mTransaction,"wrong transaction");NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");// mask this error code because its not a real error.if(reason==NS_BASE_STREAM_CLOSED)reason=NS_OK;mTransaction->Close(reason);NS_RELEASE(mTransaction);mTransaction=0;if(NS_FAILED(reason))Close(reason);// flag the connection as reused here for convenience sake. certainly// it might be going away instead ;-)mIsReused=PR_TRUE;}NS_METHODnsHttpConnection::ReadFromStream(nsIInputStream*input,void*closure,constchar*buf,PRUint32offset,PRUint32count,PRUint32*countRead){// thunk for nsIInputStream instancensHttpConnection*conn=(nsHttpConnection*)closure;returnconn->OnReadSegment(buf,count,countRead);}nsresultnsHttpConnection::OnReadSegment(constchar*buf,PRUint32count,PRUint32*countRead){if(count==0){// some ReadSegments implementations will erroneously call the writer// to consume 0 bytes worth of data. we must protect against this case// or else we'd end up closing the socket prematurely.NS_ERROR("bad ReadSegments implementation");returnNS_ERROR_FAILURE;// stop iterating}nsresultrv=mSocketOut->Write(buf,count,countRead);if(NS_FAILED(rv))mSocketOutCondition=rv;elseif(*countRead==0)mSocketOutCondition=NS_BASE_STREAM_CLOSED;elsemSocketOutCondition=NS_OK;// reset conditionreturnmSocketOutCondition;}nsresultnsHttpConnection::OnSocketWritable(){LOG(("nsHttpConnection::OnSocketWritable [this=%x]\n",this));nsresultrv;PRUint32n;PRBoolagain=PR_TRUE;do{// if we're doing an SSL proxy connect, then we need to bypass calling// into the transaction.//// NOTE: this code path can't be shared since the transaction doesn't// implement nsIInputStream. doing so is not worth the added cost of// extra indirections during normal reading.//if(mSSLProxyConnectStream){LOG((" writing CONNECT request stream\n"));rv=mSSLProxyConnectStream->ReadSegments(ReadFromStream,this,nsIOService::gDefaultSegmentSize,&n);}else{LOG((" writing transaction request stream\n"));rv=mTransaction->ReadSegments(this,nsIOService::gDefaultSegmentSize,&n);}LOG((" ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",rv,n,mSocketOutCondition));// XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.if(rv==NS_BASE_STREAM_CLOSED){rv=NS_OK;n=0;}if(NS_FAILED(rv)){// if the transaction didn't want to write any more data, then// wait for the transaction to call ResumeSend.if(rv==NS_BASE_STREAM_WOULD_BLOCK)rv=NS_OK;again=PR_FALSE;}elseif(NS_FAILED(mSocketOutCondition)){if(mSocketOutCondition==NS_BASE_STREAM_WOULD_BLOCK)rv=mSocketOut->AsyncWait(this,0,0,nsnull);// continue writingelserv=mSocketOutCondition;again=PR_FALSE;}elseif(n==0){// // at this point we've written out the entire transaction, and now we// must wait for the server's response. we manufacture a status message// here to reflect the fact that we are waiting. this message will be// trumped (overwritten) if the server responds quickly.//mTransaction->OnTransportStatus(nsISocketTransport::STATUS_WAITING_FOR,LL_ZERO);rv=mSocketIn->AsyncWait(this,0,0,nsnull);// start readingagain=PR_FALSE;}// write more to the socket until error or end-of-request...}while(again);returnrv;}nsresultnsHttpConnection::OnWriteSegment(char*buf,PRUint32count,PRUint32*countWritten){if(count==0){// some WriteSegments implementations will erroneously call the reader// to provide 0 bytes worth of data. we must protect against this case// or else we'd end up closing the socket prematurely.NS_ERROR("bad WriteSegments implementation");returnNS_ERROR_FAILURE;// stop iterating}nsresultrv=mSocketIn->Read(buf,count,countWritten);if(NS_FAILED(rv))mSocketInCondition=rv;elseif(*countWritten==0)mSocketInCondition=NS_BASE_STREAM_CLOSED;elsemSocketInCondition=NS_OK;// reset conditionreturnmSocketInCondition;}nsresultnsHttpConnection::OnSocketReadable(){LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n",this));PRUint32now=NowInSeconds();if(mKeepAliveMask&&(now-mLastReadTime>=PRUint32(mMaxHangTime))){LOG(("max hang time exceeded!\n"));// give the handler a chance to create a new persistent connection to// this host if we've been busy for too long.mKeepAliveMask=PR_FALSE;gHttpHandler->ProcessPendingQ(mConnInfo);}mLastReadTime=now;nsresultrv;PRUint32n;PRBoolagain=PR_TRUE;do{rv=mTransaction->WriteSegments(this,nsIOService::gDefaultSegmentSize,&n);if(NS_FAILED(rv)){// if the transaction didn't want to take any more data, then// wait for the transaction to call ResumeRecv.if(rv==NS_BASE_STREAM_WOULD_BLOCK)rv=NS_OK;again=PR_FALSE;}elseif(NS_FAILED(mSocketInCondition)){// continue waiting for the socket if necessary...if(mSocketInCondition==NS_BASE_STREAM_WOULD_BLOCK)rv=mSocketIn->AsyncWait(this,0,0,nsnull);elserv=mSocketInCondition;again=PR_FALSE;}// read more from the socket until error...}while(again);returnrv;}nsresultnsHttpConnection::SetupSSLProxyConnect(){constchar*val;LOG(("nsHttpConnection::SetupSSLProxyConnect [this=%x]\n",this));NS_ENSURE_TRUE(!mSSLProxyConnectStream,NS_ERROR_ALREADY_INITIALIZED);nsCAutoStringbuf;nsresultrv=nsHttpHandler::GenerateHostPort(nsDependentCString(mConnInfo->Host()),mConnInfo->Port(),buf);if(NS_FAILED(rv))returnrv;// CONNECT host:port HTTP/1.1nsHttpRequestHeadrequest;request.SetMethod(nsHttp::Connect);request.SetVersion(gHttpHandler->HttpVersion());request.SetRequestURI(buf);request.SetHeader(nsHttp::User_Agent,gHttpHandler->UserAgent());// send this header for backwards compatibility.request.SetHeader(nsHttp::Proxy_Connection,NS_LITERAL_CSTRING("keep-alive"));// NOTE: this cast is valid since this connection cannot be processing a// transaction pipeline until after the first HTTP/1.1 response.nsHttpTransaction*trans=static_cast<nsHttpTransaction*>(mTransaction);val=trans->RequestHead()->PeekHeader(nsHttp::Host);if(val){// all HTTP/1.1 requests must include a Host header (even though it// may seem redundant in this case; see bug 82388).request.SetHeader(nsHttp::Host,nsDependentCString(val));}val=trans->RequestHead()->PeekHeader(nsHttp::Proxy_Authorization);if(val){// we don't know for sure if this authorization is intended for the// SSL proxy, so we add it just in case.request.SetHeader(nsHttp::Proxy_Authorization,nsDependentCString(val));}buf.Truncate();request.Flatten(buf,PR_FALSE);buf.AppendLiteral("\r\n");returnNS_NewCStringInputStream(getter_AddRefs(mSSLProxyConnectStream),buf);}//-----------------------------------------------------------------------------// nsHttpConnection::nsISupports//-----------------------------------------------------------------------------NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnection,nsIInputStreamCallback,nsIOutputStreamCallback,nsITransportEventSink,nsIInterfaceRequestor)//-----------------------------------------------------------------------------// nsHttpConnection::nsIInputStreamCallback//-----------------------------------------------------------------------------// called on the socket transport threadNS_IMETHODIMPnsHttpConnection::OnInputStreamReady(nsIAsyncInputStream*in){NS_ASSERTION(in==mSocketIn,"unexpected stream");NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");// if the transaction was dropped...if(!mTransaction){LOG((" no transaction; ignoring event\n"));returnNS_OK;}nsresultrv=OnSocketReadable();if(NS_FAILED(rv))CloseTransaction(mTransaction,rv);returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpConnection::nsIOutputStreamCallback//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream*out){NS_ASSERTION(out==mSocketOut,"unexpected stream");NS_ASSERTION(PR_GetCurrentThread()==gSocketThread,"wrong thread");// if the transaction was dropped...if(!mTransaction){LOG((" no transaction; ignoring event\n"));returnNS_OK;}nsresultrv=OnSocketWritable();if(NS_FAILED(rv))CloseTransaction(mTransaction,rv);returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpConnection::nsITransportEventSink//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpConnection::OnTransportStatus(nsITransport*trans,nsresultstatus,PRUint64progress,PRUint64progressMax){if(mTransaction)mTransaction->OnTransportStatus(status,progress);returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpConnection::nsIInterfaceRequestor//-----------------------------------------------------------------------------// not called on the socket transport threadNS_IMETHODIMPnsHttpConnection::GetInterface(constnsIID&iid,void**result){// NOTE: This function is only called on the UI thread via sync proxy from// the socket transport thread. If that weren't the case, then we'd// have to worry about the possibility of mTransaction going away// part-way through this function call. See CloseTransaction.NS_ASSERTION(PR_GetCurrentThread()!=gSocketThread,"wrong thread");if(mTransaction){nsCOMPtr<nsIInterfaceRequestor>callbacks;mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));if(callbacks)returncallbacks->GetInterface(iid,result);}returnNS_ERROR_NO_INTERFACE;}