зеркало из https://github.com/mozilla/pjs.git
bug 729133 speculative connections do full ssl and happen before cache lookup
This commit is contained in:
Родитель
0f71792492
Коммит
55fd7a5e3f
|
@ -112,6 +112,7 @@ CPPSRCS = \
|
|||
HttpChannelParentListener.cpp \
|
||||
SpdySession.cpp \
|
||||
SpdyStream.cpp \
|
||||
NullHttpTransaction.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче