diff --git a/netwerk/protocol/http/src/nsAHttpConnection.h b/netwerk/protocol/http/src/nsAHttpConnection.h new file mode 100644 index 00000000000..e1b94fdeca8 --- /dev/null +++ b/netwerk/protocol/http/src/nsAHttpConnection.h @@ -0,0 +1,48 @@ +#ifndef nsAHttpConnection_h__ +#define nsAHttpConnection_h__ + +#include "nsISupports.h" + +class nsAHttpTransaction; +class nsHttpResponseHead; +class nsHttpConnectionInfo; + +//---------------------------------------------------------------------------- +// Abstract base class for a HTTP connection +//---------------------------------------------------------------------------- + +class nsAHttpConnection : public nsISupports +{ +public: + // called by a transaction when the response headers have all been read. + // the connection can force the transaction to reset it's response headers, + // and prepare for a new set of response headers, by setting |*reset=TRUE|. + virtual nsresult OnHeadersAvailable(nsAHttpTransaction *, + nsHttpResponseHead *, + PRBool *reset) = 0; + + // called by a transaction when it completes normally. + virtual nsresult OnTransactionComplete(nsAHttpTransaction *, + nsresult status) = 0; + + // called by a transaction to suspend/resume the connection. + virtual nsresult OnSuspend() = 0; + virtual nsresult OnResume() = 0; + + // get a reference to the connection's connection-info object. + virtual void GetConnectionInfo(nsHttpConnectionInfo **) = 0; + + // called by a transaction to remove itself from the connection (eg. when + // it reads premature EOF and must restart itself). + virtual void DropTransaction(nsAHttpTransaction *) = 0; + + // called by a transaction to determine whether or not the connection is + // persistent... important in determining the end of a response. + virtual PRBool IsPersistent() = 0; + + // called by a transaction when the transaction reads more from the socket + // than it should have (eg. containing part of the next pipelined response). + virtual nsresult PushBack(char *data, PRUint32 length) = 0; +}; + +#endif // nsAHttpConnection_h__ diff --git a/netwerk/protocol/http/src/nsAHttpTransaction.h b/netwerk/protocol/http/src/nsAHttpTransaction.h new file mode 100644 index 00000000000..2d44c58a874 --- /dev/null +++ b/netwerk/protocol/http/src/nsAHttpTransaction.h @@ -0,0 +1,50 @@ +#ifndef nsAHttpTransaction_h__ +#define nsAHttpTransaction_h__ + +#include "nsISupports.h" + +class nsAHttpConnection; +class nsIInputStream; +class nsIOutputStream; +class nsIInterfaceRequestor; + +//---------------------------------------------------------------------------- +// Abstract base class for a HTTP transaction +//---------------------------------------------------------------------------- + +class nsAHttpTransaction : public nsISupports +{ +public: + // called by the connection when it takes ownership of the transaction. + virtual void SetConnection(nsAHttpConnection *) = 0; + + // called by the connection to pass socket security-info to the transaction. + virtual void SetSecurityInfo(nsISupports *) = 0; + + // called by the connection to get notification callbacks to set on the + // socket transport. + virtual void GetNotificationCallbacks(nsIInterfaceRequestor **) = 0; + + // called by the connection to indicate that the socket can be written to. + // the transaction returns NS_BASE_STREAM_CLOSED when it is finished + // writing its request(s). + virtual nsresult OnDataWritable(nsIOutputStream *) = 0; + + // called by the connection to indicate that the socket can be read from. + // the transaction can return NS_BASE_STREAM_WOULD_BLOCK to suspend the + // socket read request. + virtual nsresult OnDataReadable(nsIInputStream *) = 0; + + // called by the connection when the transaction should stop, either due + // to normal completion, cancelation, or some socket transport error. + virtual nsresult OnStopTransaction(nsresult status) = 0; + + // called by the connection to report socket status. + virtual void OnStatus(nsresult status, const PRUnichar *statusText) = 0; + + // called by the connection to check the transaction status. + virtual PRBool IsDone() = 0; + virtual nsresult Status() = 0; +}; + +#endif // nsAHttpTransaction_h__ diff --git a/netwerk/protocol/http/src/nsHttpConnection.cpp b/netwerk/protocol/http/src/nsHttpConnection.cpp index 788616b43d5..da85f903b57 100644 --- a/netwerk/protocol/http/src/nsHttpConnection.cpp +++ b/netwerk/protocol/http/src/nsHttpConnection.cpp @@ -99,7 +99,8 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime) // called from any thread, with the connection lock held nsresult -nsHttpConnection::SetTransaction(nsHttpTransaction *transaction) +nsHttpConnection::SetTransaction(nsAHttpTransaction *transaction, + PRUint8 caps) { LOG(("nsHttpConnection::SetTransaction [this=%x trans=%x]\n", this, transaction)); @@ -112,160 +113,11 @@ nsHttpConnection::SetTransaction(nsHttpTransaction *transaction) NS_ADDREF(mTransaction); // default mKeepAlive according to what will be requested - mKeepAliveMask = mKeepAlive = - mTransaction->Capabilities() & NS_HTTP_ALLOW_KEEPALIVE; + mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE); return ActivateConnection(); } -// called from the socket thread -nsresult -nsHttpConnection::OnHeadersAvailable(nsHttpTransaction *trans, PRBool *reset) -{ - LOG(("nsHttpConnection::OnHeadersAvailable [this=%x trans=%x]\n", - this, trans)); - - NS_ENSURE_ARG_POINTER(trans); - - // we won't change our keep-alive policy unless the server has explicitly - // told us to do so. - - if (!trans || !trans->ResponseHead()) { - LOG(("trans->ResponseHead() = %x\n", trans)); - return NS_OK; - } - - // inspect the connection headers for keep-alive info provided the - // transaction completed successfully. - const char *val = trans->ResponseHead()->PeekHeader(nsHttp::Connection); - if (!val) - val = trans->ResponseHead()->PeekHeader(nsHttp::Proxy_Connection); - - if ((trans->ResponseHead()->Version() < NS_HTTP_VERSION_1_1) || - (nsHttpHandler::get()->DefaultVersion() < NS_HTTP_VERSION_1_1)) { - // HTTP/1.0 connections are by default NOT persistent - if (val && !PL_strcasecmp(val, "keep-alive")) - mKeepAlive = PR_TRUE; - else - mKeepAlive = PR_FALSE; - } - else { - // HTTP/1.1 connections are by default persistent - if (val && !PL_strcasecmp(val, "close")) - mKeepAlive = PR_FALSE; - else - mKeepAlive = PR_TRUE; - } - 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 = trans->ResponseHead()->PeekHeader(nsHttp::Keep_Alive); - - const char *cp = PL_strcasestr(val, "timeout="); - if (cp) - mIdleTimeout = (PRUint32) atoi(cp + 8); - else - mIdleTimeout = nsHttpHandler::get()->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 (trans->ResponseHead()->Status() == 200) { - LOG(("SSL proxy CONNECT succeeded!\n")); - *reset = PR_TRUE; - ProxyStepUp(); - mWriteRequest->Resume(); - } - else { - LOG(("SSL proxy CONNECT failed!\n")); - // close out the write request - mWriteRequest->Cancel(NS_OK); - } - } - - return NS_OK; -} - -// called from any thread -nsresult -nsHttpConnection::OnTransactionComplete(nsHttpTransaction *trans, nsresult status) -{ - LOG(("nsHttpConnection::OnTransactionComplete [this=%x trans=%x status=%x]\n", - this, trans, status)); - - // trans may not be mTransaction - if (trans != mTransaction) - return NS_OK; - - nsCOMPtr writeReq, readReq; - - // clear the read/write requests atomically. - { - nsAutoLock lock(mLock); - writeReq = mWriteRequest; - readReq = mReadRequest; - } - - // cancel the requests... this will cause OnStopRequest to be fired - if (writeReq) - writeReq->Cancel(status); - if (readReq) - readReq->Cancel(status); - - return NS_OK; -} - -// not called from the socket thread -nsresult -nsHttpConnection::Suspend() -{ - // we only bother to suspend the read request, since that's the only - // one that will effect our consumers. - - nsCOMPtr readReq; - { - nsAutoLock lock(mLock); - readReq = mReadRequest; - } - - if (readReq) - readReq->Suspend(); - - return NS_OK; -} - -// not called from the socket thread -nsresult -nsHttpConnection::Resume() -{ - // we only need to worry about resuming the read request, since that's - // the only one that can be suspended. - - nsCOMPtr readReq; - { - nsAutoLock lock(mLock); - readReq = mReadRequest; - } - - if (readReq) - readReq->Resume(); - - return NS_OK; -} - // called from the socket thread nsresult nsHttpConnection::ProxyStepUp() @@ -304,10 +156,166 @@ nsHttpConnection::IsAlive() return isAlive; } +//---------------------------------------------------------------------------- +// nsHttpConnection::nsAHttpConnection +//---------------------------------------------------------------------------- + +// called from the socket thread +nsresult +nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans, + nsHttpResponseHead *responseHead, + PRBool *reset) +{ + LOG(("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p response-head=%p]\n", + this, trans, responseHead)); + + NS_ENSURE_ARG_POINTER(trans); + + // we won't change our keep-alive policy unless the server has explicitly + // told us to do so. + + if (!trans || !responseHead) { + LOG(("nothing to do\n")); + return NS_OK; + } + + // inspect the connection headers for keep-alive info provided the + // transaction completed successfully. + const char *val = responseHead->PeekHeader(nsHttp::Connection); + if (!val) + val = responseHead->PeekHeader(nsHttp::Proxy_Connection); + + if ((responseHead->Version() < NS_HTTP_VERSION_1_1) || + (nsHttpHandler::get()->DefaultVersion() < NS_HTTP_VERSION_1_1)) { + // HTTP/1.0 connections are by default NOT persistent + if (val && !PL_strcasecmp(val, "keep-alive")) + mKeepAlive = PR_TRUE; + else + mKeepAlive = PR_FALSE; + } + else { + // HTTP/1.1 connections are by default persistent + if (val && !PL_strcasecmp(val, "close")) + mKeepAlive = PR_FALSE; + else + mKeepAlive = PR_TRUE; + } + 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); + + const char *cp = PL_strcasestr(val, "timeout="); + if (cp) + mIdleTimeout = (PRUint32) atoi(cp + 8); + else + mIdleTimeout = nsHttpHandler::get()->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; + ProxyStepUp(); + mWriteRequest->Resume(); + } + else { + LOG(("SSL proxy CONNECT failed!\n")); + // close out the write request + mWriteRequest->Cancel(NS_OK); + } + } + + return NS_OK; +} + +// called from any thread +nsresult +nsHttpConnection::OnTransactionComplete(nsAHttpTransaction *trans, nsresult status) +{ + LOG(("nsHttpConnection::OnTransactionComplete [this=%x trans=%x status=%x]\n", + this, trans, status)); + + // trans may not be mTransaction + if (trans != mTransaction) + return NS_OK; + + nsCOMPtr writeReq, readReq; + + // clear the read/write requests atomically. + { + nsAutoLock lock(mLock); + writeReq = mWriteRequest; + readReq = mReadRequest; + } + + // cancel the requests... this will cause OnStopRequest to be fired + if (writeReq) + writeReq->Cancel(status); + if (readReq) + readReq->Cancel(status); + + return NS_OK; +} + +// not called from the socket thread +nsresult +nsHttpConnection::OnSuspend() +{ + // we only bother to suspend the read request, since that's the only + // one that will effect our consumers. + + nsCOMPtr readReq; + { + nsAutoLock lock(mLock); + readReq = mReadRequest; + } + + if (readReq) + readReq->Suspend(); + + return NS_OK; +} + +// not called from the socket thread +nsresult +nsHttpConnection::OnResume() +{ + // we only need to worry about resuming the read request, since that's + // the only one that can be suspended. + + nsCOMPtr readReq; + { + nsAutoLock lock(mLock); + readReq = mReadRequest; + } + + if (readReq) + readReq->Resume(); + + return NS_OK; +} + // called on the socket thread void -nsHttpConnection::DropTransaction() +nsHttpConnection::DropTransaction(nsAHttpTransaction *trans) { + LOG(("nsHttpConnection::DropTransaction [trans=%p]\n", trans)); + // the assertion here is that the transaction will not be destroyed // by this release. we unfortunately don't have a threadsafe way of // asserting this. @@ -361,7 +369,7 @@ nsHttpConnection::ActivateConnection() // by the same token, we need to ensure that the transaction stays around. // and we must not access it via mTransaction, since mTransaction is null'd // in our OnStopRequest. - nsHttpTransaction *trans = mTransaction; + nsAHttpTransaction *trans = mTransaction; NS_ADDREF(trans); // We need to tell the socket transport what origin server we're @@ -485,15 +493,19 @@ nsHttpConnection::SetupSSLProxyConnect() request.SetVersion(nsHttpHandler::get()->DefaultVersion()); request.SetRequestURI(buf.get()); request.SetHeader(nsHttp::User_Agent, nsHttpHandler::get()->UserAgent()); + + // NOTE: this cast is valid since this connection cannot be processing a + // transaction pipeline until after the first HTTP/1.1 response. + nsHttpTransaction *trans = NS_STATIC_CAST(nsHttpTransaction *, mTransaction); - val = mTransaction->RequestHead()->PeekHeader(nsHttp::Host); + 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, val); } - val = mTransaction->RequestHead()->PeekHeader(nsHttp::Proxy_Authorization); + 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. @@ -597,7 +609,7 @@ nsHttpConnection::OnStopRequest(nsIRequest *request, nsISupports *ctxt, // make sure mTransaction is clear before calling OnStopTransaction if (mTransaction) { - nsHttpTransaction *trans = mTransaction; + nsAHttpTransaction *trans = mTransaction; mTransaction = nsnull; trans->OnStopTransaction(status); @@ -702,8 +714,11 @@ nsHttpConnection::GetInterface(const nsIID &iid, void **result) if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) return QueryInterface(iid, result); - if (mTransaction && mTransaction->Callbacks()) - return mTransaction->Callbacks()->GetInterface(iid, result); + if (mTransaction) { + nsCOMPtr callbacks; + mTransaction->GetNotificationCallbacks(getter_AddRefs(callbacks)); + return callbacks->GetInterface(iid, result); + } return NS_ERROR_NO_INTERFACE; } diff --git a/netwerk/protocol/http/src/nsHttpConnection.h b/netwerk/protocol/http/src/nsHttpConnection.h index a20bd6a21d7..2836109f791 100644 --- a/netwerk/protocol/http/src/nsHttpConnection.h +++ b/netwerk/protocol/http/src/nsHttpConnection.h @@ -25,6 +25,9 @@ #define nsHttpConnection_h__ #include "nsHttp.h" +#include "nsHttpConnectionInfo.h" +#include "nsAHttpConnection.h" +#include "nsAHttpTransaction.h" #include "nsIStreamListener.h" #include "nsIStreamProvider.h" #include "nsISocketTransport.h" @@ -33,23 +36,18 @@ #include "nsIInterfaceRequestorUtils.h" #include "nsIEventQueue.h" #include "nsIInputStream.h" -#include "nsIProxyInfo.h" -#include "nsCOMPtr.h" #include "nsXPIDLString.h" -#include "plstr.h" -#include "prclist.h" -#include "nsCRT.h" +#include "nsCOMPtr.h" #include "prlock.h" class nsHttpHandler; -class nsHttpConnectionInfo; -class nsHttpTransaction; //----------------------------------------------------------------------------- // nsHttpConnection - represents a connection to a HTTP server (or proxy) //----------------------------------------------------------------------------- -class nsHttpConnection : public nsIStreamListener +class nsHttpConnection : public nsAHttpConnection + , public nsIStreamListener , public nsIStreamProvider , public nsIProgressEventSink , public nsIInterfaceRequestor @@ -74,18 +72,7 @@ public: // SetTransaction causes the given transaction to be processed on this // connection. It fails if there is already an existing transaction. - nsresult SetTransaction(nsHttpTransaction *); - - // called by the transaction to inform the connection that all of the - // headers are available. - nsresult OnHeadersAvailable(nsHttpTransaction *, PRBool *reset); - - // called by the transaction to inform the connection that it is done. - nsresult OnTransactionComplete(nsHttpTransaction *, nsresult status); - - // called by the transaction to suspend/resume a read-in-progress - nsresult Suspend(); - nsresult Resume(); + nsresult SetTransaction(nsAHttpTransaction *, PRUint8 capabilities); // called to cause the underlying socket to start speaking SSL nsresult ProxyStepUp(); @@ -96,11 +83,19 @@ public: mKeepAlive = PR_FALSE; mIdleTimeout = 0; } - void DropTransaction(); - - nsHttpTransaction *Transaction() { return mTransaction; } + nsAHttpTransaction *Transaction() { return mTransaction; } nsHttpConnectionInfo *ConnectionInfo() { return mConnectionInfo; } + // nsAHttpConnection methods: + nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpResponseHead *, PRBool *reset); + nsresult OnTransactionComplete(nsAHttpTransaction *, nsresult status); + nsresult OnSuspend(); + nsresult OnResume(); + void GetConnectionInfo(nsHttpConnectionInfo **ci) { NS_IF_ADDREF(*ci = mConnectionInfo); } + void DropTransaction(nsAHttpTransaction *); + PRBool IsPersistent() { return IsKeepAlive(); } + nsresult PushBack(char *data, PRUint32 length) { return NS_OK; } + private: nsresult ActivateConnection(); nsresult CreateTransport(); @@ -116,7 +111,7 @@ private: nsCOMPtr mSSLProxyConnectStream; - nsHttpTransaction *mTransaction; // hard ref + nsAHttpTransaction *mTransaction; // hard ref nsHttpConnectionInfo *mConnectionInfo; // hard ref PRLock *mLock; @@ -132,99 +127,4 @@ private: PRPackedBool mReadDone; }; -//----------------------------------------------------------------------------- -// nsHttpConnectionInfo - holds the properties of a connection -//----------------------------------------------------------------------------- - -class nsHttpConnectionInfo -{ -public: - nsHttpConnectionInfo(const char *host, PRInt32 port, - nsIProxyInfo* proxyInfo, - PRBool usingSSL=PR_FALSE) - : mRef(0) - , mProxyInfo(proxyInfo) - , mUsingSSL(usingSSL) - { - LOG(("Creating nsHttpConnectionInfo @%x\n", this)); - - mUsingHttpProxy = (proxyInfo && !nsCRT::strcmp(proxyInfo->Type(), "http")); - - SetOriginServer(host, port); - } - - ~nsHttpConnectionInfo() - { - LOG(("Destroying nsHttpConnectionInfo @%x\n", this)); - } - - nsrefcnt AddRef() - { - return PR_AtomicIncrement((PRInt32 *) &mRef); - } - - nsrefcnt Release() - { - nsrefcnt n = PR_AtomicDecrement((PRInt32 *) &mRef); - if (n == 0) - delete this; - return n; - } - - nsresult SetOriginServer(const char* host, PRInt32 port) - { - if (host) - mHost.Adopt(nsCRT::strdup(host)); - mPort = port == -1 ? DefaultPort() : port; - - return NS_OK; - } - - const char *ProxyHost() const { return mProxyInfo ? mProxyInfo->Host() : nsnull; } - PRInt32 ProxyPort() const { return mProxyInfo ? mProxyInfo->Port() : -1; } - const char *ProxyType() const { return mProxyInfo ? mProxyInfo->Type() : nsnull; } - - // Compare this connection info to another... - // Two connections are 'equal' if they end up talking the same - // protocol to the same server. This is needed to properly manage - // persistent connections to proxies - // Note that we don't care about transparent proxies - - // it doesn't matter if we're talking via socks or not, since - // a request will end up at the same host. - PRBool Equals(const nsHttpConnectionInfo *info) - { - // Strictly speaking, we could talk to a proxy on the same port - // and reuse the connection. Its not worth the extra strcmp. - if ((info->mUsingHttpProxy != mUsingHttpProxy) || - (info->mUsingSSL != mUsingSSL)) - return PR_FALSE; - - // if its a proxy, then compare the proxy servers. - if (mUsingHttpProxy && !mUsingSSL) - return (!PL_strcasecmp(info->ProxyHost(), ProxyHost()) && - info->ProxyPort() == ProxyPort()); - - // otherwise, just check the hosts - return (!PL_strcasecmp(info->mHost, mHost) && - info->mPort == mPort); - - } - - const char *Host() { return mHost; } - PRInt32 Port() { return mPort; } - nsIProxyInfo *ProxyInfo() { return mProxyInfo; } - PRBool UsingHttpProxy() { return mUsingHttpProxy; } - PRBool UsingSSL() { return mUsingSSL; } - - PRInt32 DefaultPort() { return mUsingSSL ? 443 : 80; } - -private: - nsrefcnt mRef; - nsXPIDLCString mHost; - PRInt32 mPort; - nsCOMPtr mProxyInfo; - PRPackedBool mUsingHttpProxy; - PRPackedBool mUsingSSL; -}; - #endif // nsHttpConnection_h__ diff --git a/netwerk/protocol/http/src/nsHttpConnectionInfo.h b/netwerk/protocol/http/src/nsHttpConnectionInfo.h new file mode 100644 index 00000000000..ba02fc90601 --- /dev/null +++ b/netwerk/protocol/http/src/nsHttpConnectionInfo.h @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpConnectionInfo_h__ +#define nsHttpConnectionInfo_h__ + +#include "nsHttp.h" +#include "nsIProxyInfo.h" +#include "nsXPIDLString.h" +#include "nsCOMPtr.h" +#include "plstr.h" +#include "nsCRT.h" + +//----------------------------------------------------------------------------- +// nsHttpConnectionInfo - holds the properties of a connection +//----------------------------------------------------------------------------- + +class nsHttpConnectionInfo +{ +public: + nsHttpConnectionInfo(const char *host, PRInt32 port, + nsIProxyInfo* proxyInfo, + PRBool usingSSL=PR_FALSE) + : mRef(0) + , mProxyInfo(proxyInfo) + , mUsingSSL(usingSSL) + { + LOG(("Creating nsHttpConnectionInfo @%x\n", this)); + + mUsingHttpProxy = (proxyInfo && !nsCRT::strcmp(proxyInfo->Type(), "http")); + + SetOriginServer(host, port); + } + + ~nsHttpConnectionInfo() + { + LOG(("Destroying nsHttpConnectionInfo @%x\n", this)); + } + + nsrefcnt AddRef() + { + return PR_AtomicIncrement((PRInt32 *) &mRef); + } + + nsrefcnt Release() + { + nsrefcnt n = PR_AtomicDecrement((PRInt32 *) &mRef); + if (n == 0) + delete this; + return n; + } + + nsresult SetOriginServer(const char* host, PRInt32 port) + { + if (host) + mHost.Adopt(nsCRT::strdup(host)); + mPort = port == -1 ? DefaultPort() : port; + + return NS_OK; + } + + const char *ProxyHost() const { return mProxyInfo ? mProxyInfo->Host() : nsnull; } + PRInt32 ProxyPort() const { return mProxyInfo ? mProxyInfo->Port() : -1; } + const char *ProxyType() const { return mProxyInfo ? mProxyInfo->Type() : nsnull; } + + // Compare this connection info to another... + // Two connections are 'equal' if they end up talking the same + // protocol to the same server. This is needed to properly manage + // persistent connections to proxies + // Note that we don't care about transparent proxies - + // it doesn't matter if we're talking via socks or not, since + // a request will end up at the same host. + PRBool Equals(const nsHttpConnectionInfo *info) + { + // Strictly speaking, we could talk to a proxy on the same port + // and reuse the connection. Its not worth the extra strcmp. + if ((info->mUsingHttpProxy != mUsingHttpProxy) || + (info->mUsingSSL != mUsingSSL)) + return PR_FALSE; + + // if its a proxy, then compare the proxy servers. + if (mUsingHttpProxy && !mUsingSSL) + return (!PL_strcasecmp(info->ProxyHost(), ProxyHost()) && + info->ProxyPort() == ProxyPort()); + + // otherwise, just check the hosts + return (!PL_strcasecmp(info->mHost, mHost) && + info->mPort == mPort); + + } + + const char *Host() { return mHost; } + PRInt32 Port() { return mPort; } + nsIProxyInfo *ProxyInfo() { return mProxyInfo; } + PRBool UsingHttpProxy() { return mUsingHttpProxy; } + PRBool UsingSSL() { return mUsingSSL; } + + PRInt32 DefaultPort() { return mUsingSSL ? 443 : 80; } + +private: + nsrefcnt mRef; + nsXPIDLCString mHost; + PRInt32 mPort; + nsCOMPtr mProxyInfo; + PRPackedBool mUsingHttpProxy; + PRPackedBool mUsingSSL; +}; + +#endif // nsHttpConnectionInfo_h__ diff --git a/netwerk/protocol/http/src/nsHttpHandler.cpp b/netwerk/protocol/http/src/nsHttpHandler.cpp index bffb195de3f..b5fa4928710 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -476,7 +476,7 @@ nsHttpHandler::ProcessTransactionQ() nsresult nsHttpHandler::CancelTransaction(nsHttpTransaction *trans, nsresult status) { - nsHttpConnection *conn; + nsAHttpConnection *conn; LOG(("nsHttpHandler::CancelTransaction [trans=%x status=%x]\n", trans, status)); @@ -797,7 +797,7 @@ nsHttpHandler::InitiateTransaction_Locked(nsHttpTransaction *trans, // we must not hold the connection lock while making this call // as it could lead to deadlocks. PR_Unlock(mConnectionLock); - rv = conn->SetTransaction(trans); + rv = conn->SetTransaction(trans, trans->Capabilities()); PR_Lock(mConnectionLock); if (NS_FAILED(rv)) { diff --git a/netwerk/protocol/http/src/nsHttpTransaction.cpp b/netwerk/protocol/http/src/nsHttpTransaction.cpp index 948ad374d28..3f9480da287 100644 --- a/netwerk/protocol/http/src/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/src/nsHttpTransaction.cpp @@ -178,17 +178,6 @@ nsHttpTransaction::SetupRequest(nsHttpRequestHead *requestHead, return rv; } -nsresult -nsHttpTransaction::SetConnection(nsHttpConnection *conn) -{ - LOG(("nsHttpTransaction::SetConnection [this=%x mConnection=%x conn=%x]\n", - this, mConnection, conn)); - - mConnection = conn; - NS_IF_ADDREF(mConnection); - return NS_OK; -} - nsHttpResponseHead * nsHttpTransaction::TakeResponseHead() { @@ -200,6 +189,10 @@ nsHttpTransaction::TakeResponseHead() return head; } +//---------------------------------------------------------------------------- +// nsHttpTransaction::nsAHttpTransaction +//---------------------------------------------------------------------------- + // called on the socket transport thread nsresult nsHttpTransaction::OnDataWritable(nsIOutputStream *os) @@ -328,19 +321,21 @@ nsHttpTransaction::Restart() NS_ADDREF_THIS(); // we don't want the connection to send anymore notifications to us. - mConnection->DropTransaction(); + mConnection->DropTransaction(this); - nsHttpConnectionInfo *ci = mConnection->ConnectionInfo(); - NS_ADDREF(ci); + nsHttpConnectionInfo *ci = nsnull; + mConnection->GetConnectionInfo(&ci); + NS_ASSERTION(ci, "connection info should be non-null"); + if (ci) { + // we must release the connection before re-initiating this transaction + // since we'll be getting a new connection. + NS_RELEASE(mConnection); - // we must release the connection before calling initiate transaction - // since we will be getting a new connection. - NS_RELEASE(mConnection); + rv = nsHttpHandler::get()->InitiateTransaction(this, ci); + NS_ASSERTION(NS_SUCCEEDED(rv), "InitiateTransaction failed"); - rv = nsHttpHandler::get()->InitiateTransaction(this, ci); - NS_ASSERTION(NS_SUCCEEDED(rv), "InitiateTransaction failed"); - - NS_RELEASE(ci); + NS_RELEASE(ci); + } NS_RELEASE_THIS(); return NS_OK; } @@ -492,7 +487,7 @@ nsHttpTransaction::HandleContentStart() #endif // notify the connection, give it a chance to cause a reset. PRBool reset = PR_FALSE; - mConnection->OnHeadersAvailable(this, &reset); + mConnection->OnHeadersAvailable(this, mResponseHead, &reset); // looks like we should ignore this response, resetting... if (reset) { @@ -578,10 +573,10 @@ nsHttpTransaction::HandleContent(char *buf, } else if (mContentLength >= 0) { // HTTP/1.0 servers have been known to send erroneous Content-Length - // headers. So, unless the connection is keep-alive, we must make + // headers. So, unless the connection is persistent, we must make // allowances for a possibly invalid Content-Length header. Thus, if - // NOT keep-alive, we simply accept everything in |buf|. - if (mConnection->IsKeepAlive()) { + // NOT persistent, we simply accept everything in |buf|. + if (mConnection->IsPersistent()) { *countRead = PRUint32(mContentLength) - mContentRead; *countRead = PR_MIN(count, *countRead); } @@ -761,7 +756,7 @@ nsHttpTransaction::Suspend() { LOG(("nsHttpTransaction::Suspend [this=%x]\n", this)); if (mConnection && !mTransactionDone) - mConnection->Suspend(); + mConnection->OnSuspend(); return NS_OK; } @@ -771,7 +766,7 @@ nsHttpTransaction::Resume() { LOG(("nsHttpTransaction::Resume [this=%x]\n", this)); if (mConnection && !mTransactionDone) - mConnection->Resume(); + mConnection->OnResume(); return NS_OK; } diff --git a/netwerk/protocol/http/src/nsHttpTransaction.h b/netwerk/protocol/http/src/nsHttpTransaction.h index 49e191b0746..9e59aea1bb6 100644 --- a/netwerk/protocol/http/src/nsHttpTransaction.h +++ b/netwerk/protocol/http/src/nsHttpTransaction.h @@ -26,6 +26,8 @@ #include "nsHttp.h" #include "nsHttpHeaderArray.h" +#include "nsAHttpTransaction.h" +#include "nsAHttpConnection.h" #include "nsIStreamListener.h" #include "nsIInputStream.h" #include "nsIInterfaceRequestor.h" @@ -37,7 +39,6 @@ class nsHttpRequestHead; class nsHttpResponseHead; -class nsHttpConnection; class nsHttpChunkedDecoder; //----------------------------------------------------------------------------- @@ -45,7 +46,8 @@ class nsHttpChunkedDecoder; // intended to run on the socket thread. //----------------------------------------------------------------------------- -class nsHttpTransaction : public nsIRequest +class nsHttpTransaction : public nsAHttpTransaction + , public nsIRequest , public nsIInputStream { public: @@ -57,43 +59,32 @@ public: nsHttpTransaction(nsIStreamListener *, nsIInterfaceRequestor *, PRUint8 caps); virtual ~nsHttpTransaction(); - nsrefcnt RefCnt() { return mRefCnt; } - - // Called when assigned to a connection - nsresult SetConnection(nsHttpConnection *); - - // Called by the connection to set the associated socket security info - void SetSecurityInfo(nsISupports *info) { mSecurityInfo = info; } - // Called to initialize the transaction nsresult SetupRequest(nsHttpRequestHead *, nsIInputStream *); nsIStreamListener *Listener() { return mListener; } - nsHttpConnection *Connection() { return mConnection; } + nsAHttpConnection *Connection() { return mConnection; } nsHttpRequestHead *RequestHead() { return mRequestHead; } nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nsnull; } nsIInterfaceRequestor *Callbacks() { return mCallbacks; } nsIEventQueue *ConsumerEventQ() { return mConsumerEventQ; } nsISupports *SecurityInfo() { return mSecurityInfo; } - PRBool IsDone() { return mTransactionDone; } - nsresult Status() { return mStatus; } PRUint8 Capabilities() { return mCapabilities; } // Called to take ownership of the response headers; the transaction // will drop any reference to the response headers after this call. nsHttpResponseHead *TakeResponseHead(); - // Called to write data to the socket until return NS_BASE_STREAM_CLOSED + // nsAHttpTransaction methods: + void SetConnection(nsAHttpConnection *conn) { NS_IF_ADDREF(mConnection = conn); } + void SetSecurityInfo(nsISupports *info) { mSecurityInfo = info; } + void GetNotificationCallbacks(nsIInterfaceRequestor **cb) { NS_IF_ADDREF(*cb = mCallbacks); } nsresult OnDataWritable(nsIOutputStream *); - - // Called to read data from the socket buffer nsresult OnDataReadable(nsIInputStream *); - - // Called when the transaction should stop, possibly prematurely with an error. nsresult OnStopTransaction(nsresult); - - // Called by the connection to report socket status void OnStatus(nsresult status, const PRUnichar *statusText); + PRBool IsDone() { return mTransactionDone; } + nsresult Status() { return mStatus; } private: nsresult Restart(); @@ -114,7 +105,7 @@ private: nsCOMPtr mConsumerEventQ; nsCOMPtr mSecurityInfo; - nsHttpConnection *mConnection; // hard ref + nsAHttpConnection *mConnection; // hard ref nsCString mReqHeaderBuf; // flattened request headers nsCOMPtr mReqHeaderStream; // header data stream