bug 729133 speculative connections do full ssl and happen before cache lookup

This commit is contained in:
Patrick McManus 2012-02-25 17:07:52 -05:00
parent ed30ba1e82
commit fe7653eb59
10 changed files with 498 additions and 49 deletions

View File

@ -112,6 +112,7 @@ CPPSRCS = \
HttpChannelParentListener.cpp \
SpdySession.cpp \
SpdyStream.cpp \
NullHttpTransaction.cpp \
$(NULL)
LOCAL_INCLUDES = \

View File

@ -0,0 +1,193 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* ***** 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
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick McManus <mcmanus@ducksong.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 "nsHttp.h"
#include "NullHttpTransaction.h"
#include "nsProxyRelease.h"
#include "nsHttpHandler.h"
namespace mozilla {
namespace net {
NS_IMPL_THREADSAFE_ISUPPORTS0(NullHttpTransaction);
NullHttpTransaction::NullHttpTransaction(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
nsIEventTarget *target,
PRUint8 caps)
: mStatus(NS_OK)
, mCaps(caps)
, mCallbacks(callbacks)
, mEventTarget(target)
, mConnectionInfo(ci)
, mRequestHead(nsnull)
{
}
NullHttpTransaction::~NullHttpTransaction()
{
if (mCallbacks) {
nsIInterfaceRequestor *cbs = nsnull;
mCallbacks.swap(cbs);
NS_ProxyRelease(mEventTarget, cbs);
}
delete mRequestHead;
}
void
NullHttpTransaction::SetConnection(nsAHttpConnection *conn)
{
mConnection = conn;
}
nsAHttpConnection *
NullHttpTransaction::Connection()
{
return mConnection.get();
}
void
NullHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **outCB,
nsIEventTarget **outTarget)
{
nsCOMPtr<nsIInterfaceRequestor> copyCB(mCallbacks);
*outCB = copyCB;
copyCB.forget();
if (outTarget) {
nsCOMPtr<nsIEventTarget> copyET(mEventTarget);
*outTarget = copyET;
copyET.forget();
}
}
void
NullHttpTransaction::OnTransportStatus(nsITransport* transport,
nsresult status, PRUint64 progress)
{
}
bool
NullHttpTransaction::IsDone()
{
return true;
}
nsresult
NullHttpTransaction::Status()
{
return mStatus;
}
PRUint32
NullHttpTransaction::Available()
{
return 0;
}
nsresult
NullHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
PRUint32 count, PRUint32 *countRead)
{
*countRead = 0;
return NS_BASE_STREAM_CLOSED;
}
nsresult
NullHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
PRUint32 count, PRUint32 *countWritten)
{
*countWritten = 0;
return NS_BASE_STREAM_CLOSED;
}
PRUint32
NullHttpTransaction::Http1xTransactionCount()
{
return 0;
}
nsHttpRequestHead *
NullHttpTransaction::RequestHead()
{
// We suport a requesthead at all so that a CONNECT tunnel transaction
// can obtain a Host header from it, but we lazy-popualate that header.
if (!mRequestHead) {
mRequestHead = new nsHttpRequestHead();
nsCAutoString hostHeader;
nsCString host(mConnectionInfo->GetHost());
nsresult rv = nsHttpHandler::GenerateHostPort(host,
mConnectionInfo->Port(),
hostHeader);
if (NS_SUCCEEDED(rv))
mRequestHead->SetHeader(nsHttp::Host, hostHeader);
// CONNECT tunnels may also want Proxy-Authorization but that is a lot
// harder to determine, so for now we will let those connections fail in
// the NullHttpTransaction and let them be retried from the pending queue
// with a bound transcation
}
return mRequestHead;
}
nsresult
NullHttpTransaction::TakeSubTransactions(
nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
void
NullHttpTransaction::SetSSLConnectFailed()
{
}
void
NullHttpTransaction::Close(nsresult reason)
{
mStatus = reason;
mConnection = nsnull;
}
} // namespace mozilla::net
} // namespace mozilla

View File

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick McManus <mcmanus@ducksong.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 ***** */
#ifndef mozilla_net_NullHttpTransaction_h
#define mozilla_net_NullHttpTransaction_h
#include "nsAHttpTransaction.h"
#include "nsAHttpConnection.h"
#include "nsIInterfaceRequestor.h"
#include "nsIEventTarget.h"
#include "nsHttpConnectionInfo.h"
#include "nsHttpRequestHead.h"
// This is the minimal nsAHttpTransaction implementation. A NullHttpTransaction
// can be used to drive connection level semantics (such as SSL handshakes
// tunnels) so that a nsHttpConnection becomes fully established in
// anticiation of a real transaction needing to use it soon.
namespace mozilla { namespace net {
class NullHttpTransaction : public nsAHttpTransaction
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPTRANSACTION
NullHttpTransaction(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
nsIEventTarget *target,
PRUint8 caps);
~NullHttpTransaction();
PRUint8 Caps() { return mCaps; }
nsHttpConnectionInfo *ConnectionInfo() { return mConnectionInfo; }
private:
nsresult mStatus;
PRUint8 mCaps;
nsRefPtr<nsAHttpConnection> mConnection;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsIEventTarget> mEventTarget;
nsRefPtr<nsHttpConnectionInfo> mConnectionInfo;
nsHttpRequestHead *mRequestHead;
};
}} // namespace mozilla::net
#endif // mozilla_net_NullHttpTransaction_h

View File

@ -70,6 +70,7 @@
#include "nsDOMError.h"
#include "nsAlgorithm.h"
#include "sampler.h"
#include "NullHttpTransaction.h"
using namespace mozilla;
@ -230,6 +231,16 @@ nsHttpChannel::Connect(bool firstTime)
// true when called from AsyncOpen
if (firstTime) {
// Before we take the latency hit of dealing with the cache, try and
// get the TCP (and SSL) handshakes going so they can overlap.
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
getter_AddRefs(callbacks));
if (callbacks)
gHttpHandler->SpeculativeConnect(mConnectionInfo,
callbacks, NS_GetCurrentThread());
// are we offline?
bool offline = gIOService->IsOffline();
if (offline)

View File

@ -400,10 +400,9 @@ nsHttpConnection::SetupNPN(PRUint8 caps)
mNPNComplete = true;
if (mConnInfo->UsingSSL() &&
!(caps & NS_HTTP_DISALLOW_SPDY) &&
!mConnInfo->UsingHttpProxy() &&
gHttpHandler->IsSpdyEnabled()) {
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation"));
!mConnInfo->UsingHttpProxy()) {
LOG(("nsHttpConnection::SetupNPN Setting up "
"Next Protocol Negotiation"));
nsCOMPtr<nsISupports> securityInfo;
nsresult rv =
mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
@ -416,7 +415,12 @@ nsHttpConnection::SetupNPN(PRUint8 caps)
return;
nsTArray<nsCString> protocolArray;
protocolArray.AppendElement(NS_LITERAL_CSTRING("spdy/2"));
if (gHttpHandler->IsSpdyEnabled() &&
!(caps & NS_HTTP_DISALLOW_SPDY)) {
LOG(("nsHttpConnection::SetupNPN Allow SPDY NPN selection"));
protocolArray.AppendElement(NS_LITERAL_CSTRING("spdy/2"));
}
protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
@ -1101,7 +1105,7 @@ nsHttpConnection::OnSocketWritable()
rv, n, mSocketOutCondition));
// XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
if (rv == NS_BASE_STREAM_CLOSED) {
if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) {
rv = NS_OK;
n = 0;
}

View File

@ -91,8 +91,6 @@ nsHttpConnectionInfo*
nsHttpConnectionInfo::Clone() const
{
nsHttpConnectionInfo* clone = new nsHttpConnectionInfo(mHost, mPort, mProxyInfo, mUsingSSL);
if (!clone)
return nsnull;
// Make sure the anonymous flag is transferred!
clone->SetAnonymous(mHashKey.CharAt(2) == 'A');

View File

@ -98,6 +98,7 @@ public:
SetOriginServer(nsDependentCString(host), port);
}
// OK to treat this as an infalible allocation
nsHttpConnectionInfo* Clone() const;
const char *ProxyHost() const { return mProxyInfo ? mProxyInfo->Host().get() : nsnull; }

View File

@ -53,6 +53,7 @@
#include "mozilla/Telemetry.h"
using namespace mozilla;
using namespace mozilla::net;
// defined by the socket transport service while active
extern PRThread *gSocketThread;
@ -335,6 +336,24 @@ nsHttpConnectionMgr::ClosePersistentConnections()
return PostEvent(&nsHttpConnectionMgr::OnMsgClosePersistentConnections);
}
nsresult
nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
nsIEventTarget *target)
{
LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
ci->HashKey().get()));
nsRefPtr<NullHttpTransaction> trans =
new NullHttpTransaction(ci, callbacks, target, 0);
nsresult rv =
PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, trans);
if (NS_SUCCEEDED(rv))
trans.forget();
return rv;
}
nsresult
nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
{
@ -1053,6 +1072,22 @@ nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key,
return PL_DHASH_NEXT;
}
bool
nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
// If this host is trying to negotiate a SPDY session right now,
// don't create any new ssl connections until the result of the
// negotiation is known.
return ent->mConnInfo->UsingSSL() &&
gHttpHandler->IsSpdyEnabled() &&
!ent->mConnInfo->UsingHttpProxy() &&
(!ent->mTestedSpdy || ent->mUsingSpdy) &&
(ent->mHalfOpens.Length() || ent->mActiveConns.Length());
}
void
nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
nsHttpTransaction *trans,
@ -1115,19 +1150,25 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
// does not create new transports under any circumstances.
if (onlyReusedConnection)
return;
if (gHttpHandler->IsSpdyEnabled() &&
ent->mConnInfo->UsingSSL() &&
!ent->mConnInfo->UsingHttpProxy())
{
// If this host is trying to negotiate a SPDY session right now,
// don't create any new connections until the result of the
// negotiation is known.
if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
(ent->mHalfOpens.Length() || ent->mActiveConns.Length()))
PRUint32 halfOpenLength = ent->mHalfOpens.Length();
for (PRUint32 i = 0; i < halfOpenLength; i++) {
if (ent->mHalfOpens[i]->IsSpeculative()) {
// We've found a speculative connection in the half
// open list. Remove the speculative bit from it and that
// connection can later be used for this transaction
// (or another one in the pending queue) - we don't
// need to open a new connection here.
LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]\n"
"Found a speculative half open connection\n",
ent->mConnInfo->HashKey().get()));
ent->mHalfOpens[i]->SetSpeculative(false);
return;
}
}
if (RestrictConnections(ent))
return;
// Check if we need to purge an idle connection. Note that we may have
// removed one above; if so, this will be a no-op. We do this before
@ -1153,7 +1194,7 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
ent, ent->mUsingSpdy));
nsresult rv = CreateTransport(ent, trans);
nsresult rv = CreateTransport(ent, trans, trans->Caps(), false);
if (NS_FAILED(rv))
trans->Close(rv);
return;
@ -1193,15 +1234,19 @@ nsHttpConnectionMgr::RecvdConnect()
nsresult
nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
nsHttpTransaction *trans)
nsAHttpTransaction *trans,
PRUint8 caps,
bool speculative)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
nsresult rv = sock->SetupPrimaryStreams();
NS_ENSURE_SUCCESS(rv, rv);
ent->mHalfOpens.AppendElement(sock);
if (speculative)
sock->SetSpeculative(true);
return NS_OK;
}
@ -1227,6 +1272,28 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
return rv;
}
return DispatchAbstractTransaction(ent, aTrans, caps, conn, priority);
}
// Use this method for dispatching nsAHttpTransction's. It can only safely be
// used upon first use of a connection when NPN has not negotiated SPDY vs
// HTTP/1 yet as multiplexing onto an existing SPDY session requires a
// concrete nsHttpTransaction
nsresult
nsHttpConnectionMgr::DispatchAbstractTransaction(nsConnectionEntry *ent,
nsAHttpTransaction *aTrans,
PRUint8 caps,
nsHttpConnection *conn,
PRInt32 priority)
{
NS_ABORT_IF_FALSE(!conn->UsingSpdy(),
"Spdy Must Not Use DispatchAbstractTransaction");
LOG(("nsHttpConnectionMgr::DispatchAbstractTransaction "
"[ci=%s trans=%x caps=%x conn=%x]\n",
ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
nsresult rv;
nsConnectionHandle *handle = new nsConnectionHandle(conn);
if (!handle)
return NS_ERROR_OUT_OF_MEMORY;
@ -1310,6 +1377,19 @@ nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
return true;
}
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *ci)
{
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
if (ent)
return ent;
nsHttpConnectionInfo *clone = ci->Clone();
ent = new nsConnectionEntry(clone);
mCT.Put(ci->HashKey(), ent);
return ent;
}
nsresult
nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
{
@ -1328,16 +1408,7 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
NS_ASSERTION(ci, "no connection info");
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
if (!ent) {
nsHttpConnectionInfo *clone = ci->Clone();
if (!clone)
return NS_ERROR_OUT_OF_MEMORY;
ent = new nsConnectionEntry(clone);
if (!ent)
return NS_ERROR_OUT_OF_MEMORY;
mCT.Put(ci->HashKey(), ent);
}
nsConnectionEntry *ent = GetOrCreateConnectionEntry(ci);
// SPDY coalescing of hostnames means we might redirect from this
// connection entry onto the preferred one.
@ -1599,6 +1670,26 @@ nsHttpConnectionMgr::OnMsgClosePersistentConnections(PRInt32, void *)
mCT.Enumerate(ClosePersistentConnectionsCB, this);
}
void
nsHttpConnectionMgr::OnMsgSpeculativeConnect(PRInt32, void *param)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
nsRefPtr<NullHttpTransaction> trans =
dont_AddRef(static_cast<NullHttpTransaction *>(param));
LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
trans->ConnectionInfo()->HashKey().get()));
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(trans->ConnectionInfo());
if (!ent->mIdleConns.Length() && !RestrictConnections(ent) &&
!AtActiveConnectionLimit(ent, trans->Caps())) {
CreateTransport(ent, trans, trans->Caps(), true);
}
}
void
nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
{
@ -1889,9 +1980,12 @@ NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnectionMgr::nsHalfOpenSocket,
nsHttpConnectionMgr::
nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
nsHttpTransaction *trans)
nsAHttpTransaction *trans,
PRUint8 caps)
: mEnt(ent),
mTransaction(trans)
mTransaction(trans),
mCaps(caps),
mSpeculative(false)
{
NS_ABORT_IF_FALSE(ent && trans, "constructor with null arguments");
LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
@ -1941,10 +2035,10 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 tmpFlags = 0;
if (mTransaction->Caps() & NS_HTTP_REFRESH_DNS)
if (mCaps & NS_HTTP_REFRESH_DNS)
tmpFlags = nsISocketTransport::BYPASS_CACHE;
if (mTransaction->Caps() & NS_HTTP_LOAD_ANONYMOUS)
if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
// For backup connections, we disable IPv6. That's because some users have
@ -2036,7 +2130,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
if (timeout) {
if (timeout && !mTransaction->IsDone()) {
// Setup the timer that will establish a backup socket
// if we do not get a writable event on the main one.
// We do this because a lost SYN takes a very long time
@ -2099,7 +2193,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
NS_ABORT_IF_FALSE(timer == mSynTimer, "wrong timer");
if (!gHttpHandler->ConnMgr()->
AtActiveConnectionLimit(mEnt, mTransaction->Caps())) {
AtActiveConnectionLimit(mEnt, mCaps)) {
SetupBackupStreams();
}
@ -2166,13 +2260,13 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
// if this is still in the pending list, remove it and dispatch it
index = mEnt->mPendingQ.IndexOf(mTransaction);
if (index != -1) {
NS_ABORT_IF_FALSE(!mSpeculative,
"Speculative Half Open found mTranscation");
nsRefPtr<nsHttpTransaction> temp = dont_AddRef(mEnt->mPendingQ[index]);
mEnt->mPendingQ.RemoveElementAt(index);
nsHttpTransaction *temp = mTransaction;
NS_RELEASE(temp);
gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
mTransaction->Caps(),
conn);
rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, temp,
mCaps, conn);
}
else {
// this transaction was dispatched off the pending q before all the
@ -2191,8 +2285,31 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
// minimum granularity we can expect a server to be timing out with.
conn->SetIsReusedAfter(950);
nsRefPtr<nsHttpConnection> copy(conn); // because onmsg*() expects to drop a reference
gHttpHandler->ConnMgr()->OnMsgReclaimConnection(NS_OK, conn.forget().get());
// if we are using ssl and no other transactions are waiting right now,
// then form a null transaction to drive the SSL handshake to
// completion. Afterwards the connection will be 100% ready for the next
// transaction to use it.
if (mEnt->mConnInfo->UsingSSL() && !mEnt->mPendingQ.Length()) {
LOG(("nsHalfOpenSocket::OnOutputStreamReady null transaction will "
"be used to finish SSL handshake on conn %p\n", conn.get()));
nsRefPtr<NullHttpTransaction> trans =
new NullHttpTransaction(mEnt->mConnInfo,
callbacks, callbackTarget,
mCaps & ~NS_HTTP_ALLOW_PIPELINING);
gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
rv = gHttpHandler->ConnMgr()->
DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
}
else {
// otherwise just put this in the persistent connection pool
LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
"returning conn %p to pool\n", conn.get()));
nsRefPtr<nsHttpConnection> copy(conn);
// forget() to effectively addref because onmsg*() will drop a ref
gHttpHandler->ConnMgr()->OnMsgReclaimConnection(
NS_OK, conn.forget().get());
}
}
return rv;

View File

@ -42,6 +42,7 @@
#include "nsHttpConnectionInfo.h"
#include "nsHttpConnection.h"
#include "nsHttpTransaction.h"
#include "NullHttpTransaction.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsClassHashtable.h"
@ -125,6 +126,16 @@ public:
// transport service is not available when the connection manager is down.
nsresult GetSocketThreadTarget(nsIEventTarget **);
// called to indicate a transaction for the connectionInfo is likely coming
// soon. The connection manager may use this information to start a TCP
// and/or SSL level handshake for that resource immediately so that it is
// ready when the transaction is submitted. No obligation is taken on by the
// connection manager, nor is the submitter obligated to actually submit a
// real transaction for this connectionInfo.
nsresult SpeculativeConnect(nsHttpConnectionInfo *,
nsIInterfaceRequestor *,
nsIEventTarget *);
// called when a connection is done processing a transaction. if the
// connection can be reused then it will be added to the idle list, else
// it will be closed.
@ -251,7 +262,8 @@ private:
NS_DECL_NSITIMERCALLBACK
nsHalfOpenSocket(nsConnectionEntry *ent,
nsHttpTransaction *trans);
nsAHttpTransaction *trans,
PRUint8 caps);
~nsHalfOpenSocket();
nsresult SetupStreams(nsISocketTransport **,
@ -264,14 +276,27 @@ private:
void CancelBackupTimer();
void Abandon();
nsHttpTransaction *Transaction() { return mTransaction; }
nsAHttpTransaction *Transaction() { return mTransaction; }
bool IsSpeculative() { return mSpeculative; }
void SetSpeculative(bool val) { mSpeculative = val; }
private:
nsConnectionEntry *mEnt;
nsRefPtr<nsHttpTransaction> mTransaction;
nsRefPtr<nsAHttpTransaction> mTransaction;
nsCOMPtr<nsISocketTransport> mSocketTransport;
nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
nsCOMPtr<nsIAsyncInputStream> mStreamIn;
PRUint8 mCaps;
// mSpeculative is set if the socket was created from
// SpeculativeConnect(). It is cleared when a transaction would normally
// start a new connection from scratch but instead finds this one in
// the half open list and claims it for its own use. (which due to
// the vagaries of scheduling from the pending queue might not actually
// match up - but it prevents a speculative connection from opening
// more connections that are needed.)
bool mSpeculative;
// for syn retry
nsCOMPtr<nsITimer> mSynTimer;
@ -312,18 +337,24 @@ private:
static PLDHashOperator ClosePersistentConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
bool ProcessPendingQForEntry(nsConnectionEntry *);
bool AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
bool RestrictConnections(nsConnectionEntry *);
void GetConnection(nsConnectionEntry *, nsHttpTransaction *,
bool, nsHttpConnection **);
nsresult DispatchTransaction(nsConnectionEntry *, nsHttpTransaction *,
PRUint8 caps, nsHttpConnection *);
nsresult DispatchAbstractTransaction(nsConnectionEntry *,
nsAHttpTransaction *, PRUint8 caps,
nsHttpConnection *, PRInt32);
bool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
nsresult ProcessNewTransaction(nsHttpTransaction *);
nsresult EnsureSocketThreadTargetIfOnline();
void ClosePersistentConnections(nsConnectionEntry *ent);
nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
nsresult CreateTransport(nsConnectionEntry *, nsAHttpTransaction *,
PRUint8, bool);
void AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
void StartedConnect();
void RecvdConnect();
nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *);
// Manage the preferred spdy connection entry for this address
nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
@ -394,6 +425,7 @@ private:
void OnMsgCancelTransaction (PRInt32, void *);
void OnMsgProcessPendingQ (PRInt32, void *);
void OnMsgPruneDeadConnections (PRInt32, void *);
void OnMsgSpeculativeConnect (PRInt32, void *);
void OnMsgReclaimConnection (PRInt32, void *);
void OnMsgUpdateParam (PRInt32, void *);
void OnMsgClosePersistentConnections (PRInt32, void *);

View File

@ -181,6 +181,13 @@ public:
return mConnMgr->GetSocketThreadTarget(target);
}
nsresult SpeculativeConnect(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
nsIEventTarget *target)
{
return mConnMgr->SpeculativeConnect(ci, callbacks, target);
}
// for anything that wants to know if we're in private browsing mode.
bool InPrivateBrowsingMode();