Bug 1337791 - Part 2: http/2 origin frame extension. r=nwgh

This commit is contained in:
Patrick McManus 2017-04-03 17:23:55 -04:00
Родитель 58fc1b834e
Коммит 06bd9a4bae
13 изменённых файлов: 583 добавлений и 381 удалений

Просмотреть файл

@ -1596,6 +1596,9 @@ pref("network.http.altsvc.oe", true);
// Turn on 0RTT data for TLS 1.3
pref("security.tls.enable_0rtt_data", true);
// the origin extension impacts h2 coalescing
pref("network.http.originextension", true);
pref("network.http.diagnostics", false);
pref("network.http.pacing.requests.enabled", true);

Просмотреть файл

@ -32,6 +32,9 @@ public:
static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *, bool);
virtual bool TestJoinConnection(const nsACString &hostname, int32_t port) = 0;
virtual bool JoinConnection(const nsACString &hostname, int32_t port) = 0;
// MaybeReTunnel() is called by the connection manager when it cannot
// dispatch a tunneled transaction. That might be because the tunnels it
// expects to see are dead (and we may or may not be able to make more),

Просмотреть файл

@ -67,8 +67,7 @@ nsHttpConnectionMgr::OnMsgPrintDiagnostics(int32_t, ARefBase *)
ent->mHalfOpens.Length());
mLogData.AppendPrintf(" Coalescing Keys Length = %" PRIuSIZE "\n",
ent->mCoalescingKeys.Length());
mLogData.AppendPrintf(" Spdy using = %d, preferred = %d\n",
ent->mUsingSpdy, ent->mInPreferredHash);
mLogData.AppendPrintf(" Spdy using = %d\n", ent->mUsingSpdy);
uint32_t i;
for (i = 0; i < ent->mActiveConns.Length(); ++i) {

Просмотреть файл

@ -111,6 +111,7 @@ Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t versio
, mGoAwayOnPush(false)
, mUseH2Deps(false)
, mAttemptingEarlyData(attemptingEarlyData)
, mOriginFrameActivated(false)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -224,7 +225,8 @@ static Http2ControlFx sControlFunctions[] = {
Http2Session::RecvGoAway,
Http2Session::RecvWindowUpdate,
Http2Session::RecvContinuation,
Http2Session::RecvAltSvc // extension for type 0x0A
Http2Session::RecvAltSvc, // extension for type 0x0A
Http2Session::RecvOrigin // extension for type 0x0B
};
bool
@ -1798,26 +1800,23 @@ Http2Session::RecvPushPromise(Http2Session *self)
return NS_OK;
}
RefPtr<nsStandardURL> associatedURL, pushedURL;
rv = Http2Stream::MakeOriginURL(associatedStream->Origin(), associatedURL);
// does the pushed origin belong on this connection?
LOG3(("Http2Session::RecvPushPromise %p origin check %s", self,
pushedStream->Origin().get()));
RefPtr<nsStandardURL> pushedURL;
rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
nsAutoCString pushedHostName;
int32_t pushedPort = -1;
if (NS_SUCCEEDED(rv)) {
rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
rv = pushedURL->GetHost(pushedHostName);
}
LOG3(("Http2Session::RecvPushPromise %p checking %s == %s", self,
associatedStream->Origin().get(), pushedStream->Origin().get()));
bool match = false;
if (NS_SUCCEEDED(rv)) {
rv = associatedURL->Equals(pushedURL, &match);
rv = pushedURL->GetPort(&pushedPort);
}
if (NS_FAILED(rv)) {
// Fallback to string equality of origins. This won't be guaranteed to be as
// liberal as we want it to be, but it will at least be safe
match = associatedStream->Origin().Equals(pushedStream->Origin());
}
if (!match) {
LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin "
"associated origin %s .. pushed origin %s\n", self,
associatedStream->Origin().get(), pushedStream->Origin().get()));
if (NS_FAILED(rv) ||
!self->TestJoinConnection(pushedHostName, pushedPort)) {
LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin %s\n",
self, pushedStream->Origin().get()));
self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
self->ResetDownstreamState();
return NS_OK;
@ -2291,6 +2290,111 @@ Http2Session::RecvAltSvc(Http2Session *self)
return NS_OK;
}
void
Http2Session::Received421(nsHttpConnectionInfo *ci)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG3(("Http2Session::Recevied421 %p %d\n", this, mOriginFrameActivated));
if (!mOriginFrameActivated || !ci) {
return;
}
nsAutoCString key(ci->GetOrigin());
key.Append(':');
key.AppendInt(ci->OriginPort());
mOriginFrame.Remove(key);
LOG3(("Http2Session::Received421 %p key %s removed\n", this, key.get()));
}
// defined as an http2 extension - origin
// defines receipt of frame type 0x0b.. http://httpwg.org/http-extensions/origin-frame.html
// as this is an extension, never generate protocol error - just ignore problems
nsresult
Http2Session::RecvOrigin(Http2Session *self)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ORIGIN);
LOG3(("Http2Session::RecvOrigin %p Flags 0x%X id 0x%X\n", self,
self->mInputFrameFlags, self->mInputFrameID));
if (self->mInputFrameFlags & 0x0F) {
LOG3(("Http2Session::RecvOrigin %p leading flags must be 0", self));
self->ResetDownstreamState();
return NS_OK;
}
if (self->mInputFrameID) {
LOG3(("Http2Session::RecvOrigin %p not stream 0", self));
self->ResetDownstreamState();
return NS_OK;
}
if (self->ConnectionInfo()->UsingProxy()) {
LOG3(("Http2Session::RecvOrigin %p must not use proxy", self));
self->ResetDownstreamState();
return NS_OK;
}
if (!gHttpHandler->AllowOriginExtension()) {
LOG3(("Http2Session::RecvOrigin %p origin extension pref'd off", self));
self->ResetDownstreamState();
return NS_OK;
}
uint32_t offset = 0;
self->mOriginFrameActivated = true;
while (self->mInputFrameDataSize >= (offset + 2U)) {
uint16_t originLen = NetworkEndian::readUint16(
self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset);
LOG3(("Http2Session::RecvOrigin %p origin extension defined as %d bytes\n", self, originLen));
if (originLen + 2U + offset > self->mInputFrameDataSize) {
LOG3(("Http2Session::RecvOrigin %p origin len too big for frame", self));
break;
}
nsAutoCString originString;
RefPtr<nsStandardURL> originURL;
originString.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset + 2, originLen);
offset += originLen + 2;
if (NS_FAILED(Http2Stream::MakeOriginURL(originString, originURL))){
LOG3(("Http2Session::RecvOrigin %p origin frame string %s failed to parse\n", self, originString.get()));
continue;
}
LOG3(("Http2Session::RecvOrigin %p origin frame string %s parsed OK\n", self, originString.get()));
bool isHttps = false;
if (NS_FAILED(originURL->SchemeIs("https", &isHttps)) || !isHttps) {
LOG3(("Http2Session::RecvOrigin %p origin frame not https\n", self));
continue;
}
int32_t port = -1;
originURL->GetPort(&port);
if (port == -1) {
port = 443;
}
// dont use ->GetHostPort because we want explicit 443
nsAutoCString host;
originURL->GetHost(host);
nsAutoCString key(host);
key.Append(':');
key.AppendInt(port);
if (!self->mOriginFrame.Get(key)) {
self->mOriginFrame.Put(key, true);
RefPtr<nsHttpConnection> conn(self->HttpConnection());
MOZ_ASSERT(conn.get());
gHttpHandler->ConnMgr()->RegisterOriginCoalescingKey(conn, host, port);
} else {
LOG3(("Http2Session::RecvOrigin %p origin frame already in set\n", self));
}
}
self->ResetDownstreamState();
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsAHttpTransaction. It is expected that nsHttpConnection is the caller
// of these methods
@ -3848,6 +3952,15 @@ Http2Session::TakeHttpConnection()
return nullptr;
}
already_AddRefed<nsHttpConnection>
Http2Session::HttpConnection()
{
if (mConnection) {
return mConnection->HttpConnection();
}
return nullptr;
}
void
Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
{
@ -4005,5 +4118,102 @@ Http2Session::SendPing()
Unused << ResumeRecv();
}
bool
Http2Session::TestOriginFrame(const nsACString &hostname, int32_t port)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(mOriginFrameActivated);
nsAutoCString key(hostname);
key.Append (':');
key.AppendInt(port);
bool rv = mOriginFrame.Get(key);
LOG3(("TestOriginFrame() %p %s %d\n", this, key.get(), rv));
return rv;
}
bool
Http2Session::TestJoinConnection(const nsACString &hostname, int32_t port)
{
if (!mConnection || mClosed || mShouldGoAway) {
return false;
}
if (mOriginFrameActivated) {
bool originFrameResult = TestOriginFrame(hostname, port);
if (!originFrameResult) {
return false;
}
} else {
LOG3(("TestJoinConnection %p no origin frame check used.\n", this));
}
nsresult rv;
bool isJoined = false;
nsCOMPtr<nsISupports> securityInfo;
nsCOMPtr<nsISSLSocketControl> sslSocketControl;
mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
sslSocketControl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv) || !sslSocketControl) {
return false;
}
// try all the coalescable versions we support.
const SpdyInformation *info = gHttpHandler->SpdyInfo();
for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
if (info->ProtocolEnabled(index - 1)) {
rv = sslSocketControl->TestJoinConnection(info->VersionString[index - 1],
hostname, port, &isJoined);
if (NS_SUCCEEDED(rv) && isJoined) {
return true;
}
}
}
return false;
}
bool
Http2Session::JoinConnection(const nsACString &hostname, int32_t port)
{
if (!mConnection || mClosed || mShouldGoAway) {
return false;
}
if (mOriginFrameActivated) {
bool originFrameResult = TestOriginFrame(hostname, port);
if (!originFrameResult) {
return false;
}
} else {
LOG3(("JoinConnection %p no origin frame check used.\n", this));
}
nsresult rv;
bool isJoined = false;
nsCOMPtr<nsISupports> securityInfo;
nsCOMPtr<nsISSLSocketControl> sslSocketControl;
mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
sslSocketControl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv) || !sslSocketControl) {
return false;
}
// try all the coalescable versions we support.
const SpdyInformation *info = gHttpHandler->SpdyInfo();
for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
if (info->ProtocolEnabled(index - 1)) {
rv = sslSocketControl->JoinConnection(info->VersionString[index - 1],
hostname, port, &isJoined);
if (NS_SUCCEEDED(rv) && isJoined) {
return true;
}
}
}
return false;
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -50,6 +50,8 @@ public:
bool CanReuse() override { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams() override;
uint32_t SpdyVersion() override;
bool TestJoinConnection(const nsACString &hostname, int32_t port) override;
bool JoinConnection(const nsACString &hostname, int32_t port) override;
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
@ -89,7 +91,8 @@ public:
FRAME_TYPE_WINDOW_UPDATE = 0x8,
FRAME_TYPE_CONTINUATION = 0x9,
FRAME_TYPE_ALTSVC = 0xA,
FRAME_TYPE_LAST = 0xB
FRAME_TYPE_ORIGIN = 0xB,
FRAME_TYPE_LAST = 0xC
};
// NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway
@ -185,6 +188,7 @@ public:
static nsresult RecvWindowUpdate(Http2Session *);
static nsresult RecvContinuation(Http2Session *);
static nsresult RecvAltSvc(Http2Session *);
static nsresult RecvOrigin(Http2Session *);
char *EnsureOutputBuffer(uint32_t needed);
@ -243,6 +247,9 @@ public:
MOZ_MUST_USE bool Do0RTT() override final { return true; }
MOZ_MUST_USE nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) override final;
// For use by an HTTP2Stream
void Received421(nsHttpConnectionInfo *ci);
private:
// These internal states do not correspond to the states of the HTTP/2 specification
@ -508,6 +515,10 @@ private:
// The ID(s) of the stream(s) that we are getting 0RTT data from.
nsTArray<uint32_t> m0RTTStreams;
bool TestOriginFrame(const nsACString &name, int32_t port);
bool mOriginFrameActivated;
nsDataHashtable<nsCStringHashKey, bool> mOriginFrame;
private:
/// connect tunnels
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);

Просмотреть файл

@ -1022,6 +1022,7 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
nsresult errcode;
httpResponseCode = statusString.ToInteger(&errcode);
LOG3(("Http2Stream::ConvertResponseHeaders %p response code %d\n", this, httpResponseCode));
if (mIsTunnel) {
LOG3(("Http2Stream %p Tunnel Response code %d", this, httpResponseCode));
if ((httpResponseCode / 100) != 2) {
@ -1035,6 +1036,11 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
return NS_ERROR_ILLEGAL_VALUE;
}
if (httpResponseCode == 421) {
// Origin Frame requires 421 to remove this origin from the origin set
mSession->Received421(mTransaction->ConnectionInfo());
}
if (aHeadersIn.Length() && aHeadersOut.Length()) {
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
uint32_t ratio =

Просмотреть файл

@ -129,6 +129,9 @@ public:
// reference to it to the caller.
virtual already_AddRefed<nsHttpConnection> TakeHttpConnection() = 0;
// Like TakeHttpConnection() but do not drop our own ref
virtual already_AddRefed<nsHttpConnection> HttpConnection() = 0;
// Get the nsISocketTransport used by the connection without changing
// references or ownership.
virtual nsISocketTransport *Transport() = 0;
@ -161,7 +164,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpConnection, NS_AHTTPCONNECTION_IID)
void DontReuse() override; \
MOZ_MUST_USE nsresult PushBack(const char *, uint32_t) override; \
already_AddRefed<nsHttpConnection> TakeHttpConnection() override; \
/* \
already_AddRefed<nsHttpConnection> HttpConnection() override; \
/* \
Thes methods below have automatic definitions that just forward the \
function to a lower level connection object \
*/ \

Просмотреть файл

@ -883,6 +883,24 @@ nsHttpConnection::DontReuse()
mSpdySession->DontReuse();
}
bool
nsHttpConnection::TestJoinConnection(const nsACString &hostname, int32_t port)
{
if (mSpdySession && CanDirectlyActivate()) {
return mSpdySession->TestJoinConnection(hostname, port);
}
return false;
}
bool
nsHttpConnection::JoinConnection(const nsACString &hostname, int32_t port)
{
if (mSpdySession && CanDirectlyActivate()) {
return mSpdySession->JoinConnection(hostname, port);
}
return false;
}
bool
nsHttpConnection::CanReuse()
{

Просмотреть файл

@ -222,6 +222,9 @@ public:
// override of nsAHttpConnection
virtual uint32_t Version();
bool TestJoinConnection(const nsACString &hostname, int32_t port);
bool JoinConnection(const nsACString &hostname, int32_t port);
private:
// Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
enum TCPKeepaliveConfig {

Просмотреть файл

@ -100,6 +100,7 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
nsHttpConnectionMgr::~nsHttpConnectionMgr()
{
LOG(("Destroying nsHttpConnectionMgr @%p\n", this));
MOZ_ASSERT(mCoalescingHash.Count() == 0);
if (mTimeoutTick)
mTimeoutTick->Cancel();
}
@ -559,101 +560,6 @@ nsHttpConnectionMgr::ClearConnectionHistory()
return NS_OK;
}
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::LookupPreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
{
nsConnectionEntry *preferred = nullptr;
uint32_t len = ent->mCoalescingKeys.Length();
for (uint32_t i = 0; !preferred && (i < len); ++i) {
preferred = mSpdyPreferredHash.Get(ent->mCoalescingKeys[i]);
}
return preferred;
}
void
nsHttpConnectionMgr::StorePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
{
if (ent->mCoalescingKeys.IsEmpty()) {
return;
}
ent->mInPreferredHash = true;
uint32_t len = ent->mCoalescingKeys.Length();
for (uint32_t i = 0; i < len; ++i) {
mSpdyPreferredHash.Put(ent->mCoalescingKeys[i], ent);
}
}
void
nsHttpConnectionMgr::RemovePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
{
if (!ent->mInPreferredHash || ent->mCoalescingKeys.IsEmpty()) {
return;
}
ent->mInPreferredHash = false;
uint32_t len = ent->mCoalescingKeys.Length();
for (uint32_t i = 0; i < len; ++i) {
mSpdyPreferredHash.Remove(ent->mCoalescingKeys[i]);
}
}
// Given a nsHttpConnectionInfo find the connection entry object that
// contains either the nshttpconnection or nshttptransaction parameter.
// Normally this is done by the hashkey lookup of connectioninfo,
// but if spdy coalescing is in play it might be found in a redirected
// entry
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
nsHttpConnection *conn,
nsHttpTransaction *trans)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (!ci)
return nullptr;
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
// If there is no sign of coalescing (or it is disabled) then just
// return the primary hash lookup
if (!ent || !ent->mUsingSpdy || ent->mCoalescingKeys.IsEmpty())
return ent;
// If there is no preferred coalescing entry for this host (or the
// preferred entry is the one that matched the mCT hash lookup) then
// there is only option
nsConnectionEntry *preferred = LookupPreferredHash(ent);
if (!preferred || (preferred == ent))
return ent;
if (conn) {
// The connection could be either in preferred or ent. It is most
// likely the only active connection in preferred - so start with that.
if (preferred->mActiveConns.Contains(conn))
return preferred;
if (preferred->mIdleConns.Contains(conn))
return preferred;
}
if (trans) {
if (preferred->mUrgentStartQ.Contains(trans, PendingComparator())) {
return preferred;
}
nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
if (preferred->mPendingTransactionTable.Get(
trans->TopLevelOuterContentWindowId(), &infoArray)) {
if (infoArray->Contains(trans, PendingComparator())) {
return preferred;
}
}
}
// Neither conn nor trans found in preferred, use the default entry
return ent;
}
nsresult
nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
{
@ -661,11 +567,11 @@ nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
this, conn));
if (!conn->ConnectionInfo())
if (!conn->ConnectionInfo()) {
return NS_ERROR_UNEXPECTED;
}
nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
conn, nullptr);
nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
RefPtr<nsHttpConnection> deleteProtector(conn);
if (!ent || !ent->mIdleConns.RemoveElement(conn))
@ -689,8 +595,7 @@ nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection *conn)
return NS_ERROR_UNEXPECTED;
}
nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
conn, nullptr);
nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
if (!ent || !ent->mIdleConns.RemoveElement(conn)) {
return NS_ERROR_UNEXPECTED;
@ -700,99 +605,209 @@ nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection *conn)
return NS_OK;
}
nsHttpConnection *
nsHttpConnectionMgr::FindCoalescableConnectionByHashKey(nsConnectionEntry *ent,
const nsCString &key,
bool justKidding)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(ent->mConnInfo);
nsHttpConnectionInfo *ci = ent->mConnInfo;
nsTArray<nsWeakPtr> *listOfWeakConns = mCoalescingHash.Get(key);
if (!listOfWeakConns) {
return nullptr;
}
uint32_t listLen = listOfWeakConns->Length();
for (uint32_t j = 0; j < listLen; ) {
RefPtr<nsHttpConnection> potentialMatch = do_QueryReferent(listOfWeakConns->ElementAt(j));
if (!potentialMatch) {
// This is a connection that needs to be removed from the list
LOG(("FindCoalescableConnectionByHashKey() found old conn %p that has null weak ptr - removing\n",
listOfWeakConns->ElementAt(j).get()));
if (j != listLen - 1) {
listOfWeakConns->Elements()[j] = listOfWeakConns->Elements()[listLen - 1];
}
listOfWeakConns->RemoveElementAt(listLen - 1);
MOZ_ASSERT(listOfWeakConns->Length() == listLen - 1);
listLen--;
continue; // without adjusting iterator
}
bool couldJoin;
if (justKidding) {
couldJoin = potentialMatch->TestJoinConnection(ci->GetOrigin(), ci->OriginPort());
} else {
couldJoin = potentialMatch->JoinConnection(ci->GetOrigin(), ci->OriginPort());
}
if (couldJoin) {
LOG(("FindCoalescableConnectionByHashKey() found hashtable match %p for key %s of CI %s join ok\n",
potentialMatch.get(), key.get(), ci->HashKey().get()));
return potentialMatch.get();
} else {
LOG(("FindCoalescableConnectionByHashKey() found hashtable match %p for key %s of CI %s join failed\n",
potentialMatch.get(), key.get(), ci->HashKey().get()));
}
++j; // bypassed by continue when weakptr fails
}
if (!listLen) { // shrunk to 0 while iterating
LOG(("FindCoalescableConnectionByHashKey() removing empty list element\n"));
mCoalescingHash.Remove(key);
}
return nullptr;
}
static void
BuildOriginFrameHashKey(nsACString &newKey, nsHttpConnectionInfo *ci,
const nsACString &host, int32_t port)
{
newKey.Assign(host);
if (ci->GetAnonymous()) {
newKey.AppendLiteral("~A:");
} else {
newKey.AppendLiteral("~.:");
}
newKey.AppendInt(port);
newKey.AppendLiteral("/[");
nsAutoCString suffix;
ci->GetOriginAttributes().CreateSuffix(suffix);
newKey.Append(suffix);
newKey.AppendLiteral("]viaORIGIN.FRAME");
}
nsHttpConnection *
nsHttpConnectionMgr::FindCoalescableConnection(nsConnectionEntry *ent,
bool justKidding)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(ent->mConnInfo);
nsHttpConnectionInfo *ci = ent->mConnInfo;
LOG(("FindCoalescableConnection %s\n", ci->HashKey().get()));
// First try and look it up by origin frame
nsCString newKey;
BuildOriginFrameHashKey(newKey, ci, ci->GetOrigin(), ci->OriginPort());
nsHttpConnection *conn =
FindCoalescableConnectionByHashKey(ent, newKey, justKidding);
if (conn) {
LOG(("FindCoalescableConnection(%s) match conn %p on frame key %s\n",
ci->HashKey().get(), conn, newKey.get()));
return conn;
}
// now check for DNS based keys
// deleted conns (null weak pointers) are removed from list
uint32_t keyLen = ent->mCoalescingKeys.Length();
for (uint32_t i = 0; i < keyLen; ++i) {
conn = FindCoalescableConnectionByHashKey(ent, ent->mCoalescingKeys[i], justKidding);
if (conn) {
LOG(("FindCoalescableConnection(%s) match conn %p on dns key %s\n",
ci->HashKey().get(), conn, ent->mCoalescingKeys[i].get()));
return conn;
}
}
LOG(("FindCoalescableConnection(%s) no matching conn\n", ci->HashKey().get()));
return nullptr;
}
void
nsHttpConnectionMgr::UpdateCoalescingForNewConn(nsHttpConnection *conn,
nsConnectionEntry *ent)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(conn);
MOZ_ASSERT(conn->ConnectionInfo());
MOZ_ASSERT(ent);
MOZ_ASSERT(mCT.Get(conn->ConnectionInfo()->HashKey()) == ent);
nsHttpConnection *existingConn = FindCoalescableConnection(ent, true);
if (existingConn) {
LOG(("UpdateCoalescingForNewConn() found active conn that could have served newConn\n"
"graceful close of conn=%p to migrate to %p\n", conn, existingConn));
conn->DontReuse();
return;
}
// This connection might go into the mCoalescingHash for new transactions to be coalesced onto
// if it can accept new transactions
if (!conn->CanDirectlyActivate()) {
return;
}
uint32_t keyLen = ent->mCoalescingKeys.Length();
for (uint32_t i = 0;i < keyLen; ++i) {
LOG(("UpdateCoalescingForNewConn() registering conn %p %s under key %s\n",
conn, conn->ConnectionInfo()->HashKey().get(), ent->mCoalescingKeys[i].get()));
nsTArray<nsWeakPtr> *listOfWeakConns = mCoalescingHash.Get(ent->mCoalescingKeys[i]);
if (!listOfWeakConns) {
LOG(("UpdateCoalescingForNewConn() need new list element\n"));
listOfWeakConns = new nsTArray<nsWeakPtr>(1);
mCoalescingHash.Put(ent->mCoalescingKeys[i], listOfWeakConns);
}
listOfWeakConns->AppendElement(
do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
}
// Cancel any other pending connections - their associated transactions
// are in the pending queue and will be dispatched onto this new connection
for (int32_t index = ent->mHalfOpens.Length() - 1; index >= 0; --index) {
LOG(("UpdateCoalescingForNewConn() forcing halfopen abandon %p\n",
ent->mHalfOpens[index]));
ent->mHalfOpens[index]->Abandon();
}
if (ent->mActiveConns.Length() > 1) {
// this is a new connection that can be coalesced onto. hooray!
// if there are other connection to this entry (e.g.
// some could still be handshaking, shutting down, etc..) then close
// them down after any transactions that are on them are complete.
// This probably happened due to the parallel connection algorithm
// that is used only before the host is known to speak h2.
for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
nsHttpConnection *otherConn = ent->mActiveConns[index];
if (otherConn != conn) {
LOG(("UpdateCoalescingForNewConn() shutting down connection (%p) because new "
"spdy connection (%p) takes precedence\n", otherConn, conn));
otherConn->DontReuse();
}
}
}
}
// This function lets a connection, after completing the NPN phase,
// report whether or not it is using spdy through the usingSpdy
// argument. It would not be necessary if NPN were driven out of
// the connection manager. The connection entry associated with the
// connection is then updated to indicate whether or not we want to use
// spdy with that host and update the preliminary preferred host
// spdy with that host and update the coalescing hash
// entries used for de-sharding hostsnames.
void
nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
bool usingSpdy)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
conn, nullptr);
if (!ent)
if (!conn->ConnectionInfo()) {
return;
if (!usingSpdy)
}
nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
if (!ent || !usingSpdy) {
return;
}
ent->mUsingSpdy = true;
mNumSpdyActiveConns++;
// adjust timeout timer
uint32_t ttl = conn->TimeToLive();
uint64_t timeOfExpire = NowInSeconds() + ttl;
if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
if (!mTimer || timeOfExpire < mTimeOfNextWakeUp) {
PruneDeadConnectionsAfter(ttl);
// Lookup preferred directly from the hash instead of using
// GetSpdyPreferredEnt() because we want to avoid the cert compatibility
// check at this point because the cert is never part of the hash
// lookup. Filtering on that has to be done at the time of use
// rather than the time of registration (i.e. now).
nsConnectionEntry *joinedConnection;
nsConnectionEntry *preferred = LookupPreferredHash(ent);
LOG(("ReportSpdyConnection %p,%s conn %p prefers %p,%s\n",
ent, ent->mConnInfo->Origin(), conn, preferred,
preferred ? preferred->mConnInfo->Origin() : ""));
if (!preferred) {
// this becomes the preferred entry
StorePreferredHash(ent);
preferred = ent;
} else if ((preferred != ent) &&
(joinedConnection = GetSpdyPreferredEnt(ent)) &&
(joinedConnection != ent)) {
//
// A connection entry (e.g. made with a different hostname) with
// the same IP address is preferred for future transactions over this
// connection entry. Gracefully close down the connection to help
// new transactions migrate over.
LOG(("ReportSpdyConnection graceful close of conn=%p ent=%p to "
"migrate to preferred (desharding)\n", conn, ent));
conn->DontReuse();
} else if (preferred != ent) {
LOG (("ReportSpdyConnection preferred host may be in false start or "
"may have insufficient cert. Leave mapping in place but do not "
"abandon this connection yet."));
}
if ((preferred == ent) && conn->CanDirectlyActivate()) {
// this is a new spdy connection to the preferred entry
// Cancel any other pending connections - their associated transactions
// are in the pending queue and will be dispatched onto this connection
for (int32_t index = ent->mHalfOpens.Length() - 1;
index >= 0; --index) {
LOG(("ReportSpdyConnection forcing halfopen abandon %p\n",
ent->mHalfOpens[index]));
ent->mHalfOpens[index]->Abandon();
}
if (ent->mActiveConns.Length() > 1) {
// this is a new connection to an established preferred spdy host.
// if there is more than 1 live and established spdy connection (e.g.
// some could still be handshaking, shutting down, etc..) then close
// this one down after any transactions that are on it are complete.
// This probably happened due to the parallel connection algorithm
// that is used only before the host is known to speak spdy.
for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
nsHttpConnection *otherConn = ent->mActiveConns[index];
if (otherConn != conn) {
LOG(("ReportSpdyConnection shutting down connection (%p) because new "
"spdy connection (%p) takes precedence\n", otherConn, conn));
otherConn->DontReuse();
}
}
}
}
UpdateCoalescingForNewConn(conn, ent);
nsresult rv = ProcessPendingQ(ent->mConnInfo);
if (NS_FAILED(rv)) {
@ -808,108 +823,6 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
}
}
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
{
if (!gHttpHandler->IsSpdyEnabled() ||
!gHttpHandler->CoalesceSpdy() ||
aOriginalEntry->mConnInfo->GetNoSpdy() ||
aOriginalEntry->mCoalescingKeys.IsEmpty()) {
return nullptr;
}
nsConnectionEntry *preferred = LookupPreferredHash(aOriginalEntry);
// if there is no redirection no cert validation is required
if (preferred == aOriginalEntry)
return aOriginalEntry;
// if there is no preferred host or it is no longer using spdy
// then skip pooling
if (!preferred || !preferred->mUsingSpdy)
return nullptr;
// if there is not an active spdy session in this entry then
// we cannot pool because the cert upon activation may not
// be the same as the old one. Active sessions are prohibited
// from changing certs.
nsHttpConnection *activeSpdy = nullptr;
for (uint32_t index = 0; index < preferred->mActiveConns.Length(); ++index) {
if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
activeSpdy = preferred->mActiveConns[index];
break;
}
}
if (!activeSpdy) {
// remove the preferred status of this entry if it cannot be
// used for pooling.
RemovePreferredHash(preferred);
LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
"preferred host mapping %s to %s removed due to inactivity.\n",
aOriginalEntry->mConnInfo->Origin(),
preferred->mConnInfo->Origin()));
return nullptr;
}
// Check that the server cert supports redirection
nsresult rv;
bool isJoined = false;
nsCOMPtr<nsISupports> securityInfo;
nsCOMPtr<nsISSLSocketControl> sslSocketControl;
nsAutoCString negotiatedNPN;
activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
if (!securityInfo) {
NS_WARNING("cannot obtain spdy security info");
return nullptr;
}
sslSocketControl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv)) {
NS_WARNING("sslSocketControl QI Failed");
return nullptr;
}
// try all the spdy versions we support.
const SpdyInformation *info = gHttpHandler->SpdyInfo();
for (uint32_t index = SpdyInformation::kCount;
NS_SUCCEEDED(rv) && index > 0; --index) {
if (info->ProtocolEnabled(index - 1)) {
rv = sslSocketControl->JoinConnection(info->VersionString[index - 1],
aOriginalEntry->mConnInfo->GetOrigin(),
aOriginalEntry->mConnInfo->OriginPort(),
&isJoined);
if (NS_SUCCEEDED(rv) && isJoined) {
break;
}
}
}
if (NS_FAILED(rv) || !isJoined) {
LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
"Host %s cannot be confirmed to be joined "
"with %s connections. rv=%" PRIx32 " isJoined=%d",
preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
static_cast<uint32_t>(rv), isJoined));
Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, false);
return nullptr;
}
// IP pooling confirmed
LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
"Host %s has cert valid for %s connections, "
"so %s will be coalesced with %s",
preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
aOriginalEntry->mConnInfo->Origin(), preferred->mConnInfo->Origin()));
Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, true);
return preferred;
}
//-----------------------------------------------------------------------------
bool
nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
@ -1047,6 +960,9 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool consid
ent->mIdleConns.Length(), ent->mUrgentStartQ.Length(),
ent->PendingQLength()));
if (!ent->mUrgentStartQ.Length() && !ent->mPendingTransactionTable.Count()) {
return false;
}
ProcessSpdyPendingQ(ent);
bool dispatchedSuccessfully = false;
@ -1458,7 +1374,7 @@ nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
// essentially pipelining without head of line blocking
if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
RefPtr<nsHttpConnection> conn = GetSpdyPreferredConn(ent);
RefPtr<nsHttpConnection> conn = GetSpdyActiveConn(ent);
if (conn) {
if ((caps & NS_HTTP_ALLOW_KEEPALIVE) || !conn->IsExperienced()) {
LOG((" dispatch to spdy: [conn=%p]\n", conn.get()));
@ -1776,17 +1692,6 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(ci, !!trans->TunnelProvider());
// SPDY coalescing of hostnames means we might redirect from this
// connection entry onto the preferred one.
nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
if (preferredEntry && (preferredEntry != ent)) {
LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
"redirected via coalescing from %s to %s\n", trans,
ent->mConnInfo->Origin(), preferredEntry->mConnInfo->Origin()));
ent = preferredEntry;
}
ReportProxyTelemetry(ent);
// Check if the transaction already has a sticky reference to a connection.
@ -2002,14 +1907,13 @@ nsHttpConnectionMgr::DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo
// This function tries to dispatch the pending spdy transactions on
// the connection entry sent in as an argument. It will do so on the
// active spdy connection either in that same entry or in the
// redirected 'preferred' entry for the same coalescing hash key if
// coalescing is enabled.
// active spdy connection either in that same entry or from the
// coalescing hash table
void
nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
{
nsHttpConnection *conn = GetSpdyPreferredConn(ent);
nsHttpConnection *conn = GetSpdyActiveConn(ent);
if (!conn || !conn->CanDirectlyActivate()) {
return;
}
@ -2040,60 +1944,68 @@ nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, ARefBase *)
}
}
// Given a connection entry, return an active h2 connection
// that can be directly activated or null
nsHttpConnection *
nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
nsHttpConnectionMgr::GetSpdyActiveConn(nsConnectionEntry *ent)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(ent);
nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
// this entry is spdy-enabled if it is involved in a redirect
if (preferred) {
// all new connections for this entry will use spdy too
ent->mUsingSpdy = true;
} else {
preferred = ent;
}
if (!preferred->mUsingSpdy) {
return nullptr;
}
nsHttpConnection *rv = nullptr;
uint32_t activeLen = preferred->mActiveConns.Length();
nsHttpConnection *experienced = nullptr;
nsHttpConnection *noExperience = nullptr;
uint32_t activeLen = ent->mActiveConns.Length();
nsHttpConnectionInfo *ci = ent->mConnInfo;
uint32_t index;
// activeLen should generally be 1.. this is a setup race being resolved
// take a conn who can activate and is experienced
for (index = 0; index < activeLen; ++index) {
nsHttpConnection *tmp = preferred->mActiveConns[index];
if (tmp->CanDirectlyActivate() && tmp->IsExperienced()) {
rv = tmp;
break;
nsHttpConnection *tmp = ent->mActiveConns[index];
if (tmp->CanDirectlyActivate()) {
if (tmp->IsExperienced()) {
experienced = tmp;
break;
}
noExperience = tmp; // keep looking for a better option
}
}
// if that worked, cleanup anything else
if (rv) {
// if that worked, cleanup anything else and exit
if (experienced) {
for (index = 0; index < activeLen; ++index) {
nsHttpConnection *tmp = preferred->mActiveConns[index];
nsHttpConnection *tmp = ent->mActiveConns[index];
// in the case where there is a functional h2 session, drop the others
if (tmp != rv) {
if (tmp != experienced) {
tmp->DontReuse();
}
}
return rv;
LOG(("GetSpdyActiveConn() request for ent %p %s "
"found an active experienced connection %p in native connection entry\n",
ent, ci->HashKey().get(), experienced));
return experienced;
}
// take a conn who can activate and leave the rest alone
for (index = 0; index < activeLen; ++index) {
nsHttpConnection *tmp = preferred->mActiveConns[index];
if (tmp->CanDirectlyActivate()) {
rv = tmp;
break;
}
if (noExperience) {
LOG(("GetSpdyActiveConn() request for ent %p %s "
"found an active but inexperienced connection %p in native connection entry\n",
ent, ci->HashKey().get(), noExperience));
return noExperience;
}
return rv;
// there was no active spdy connection in the connection entry, but
// there might be one in the hash table for coalescing
nsHttpConnection *existingConn = FindCoalescableConnection(ent, false);
if (existingConn) {
LOG(("GetSpdyActiveConn() request for ent %p %s "
"found an active connection %p in the coalescing hashtable\n",
ent, ci->HashKey().get(), existingConn));
return existingConn;
}
LOG(("GetSpdyActiveConn() request for ent %p %s "
"did not find an active connection\n", ent, ci->HashKey().get()));
return nullptr;
}
//-----------------------------------------------------------------------------
@ -2174,6 +2086,7 @@ nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
mTrafficTimer->Cancel();
mTrafficTimer = nullptr;
}
mCoalescingHash.Clear();
// signal shutdown complete
nsCOMPtr<nsIRunnable> runnable =
@ -2213,8 +2126,10 @@ nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
RefPtr<nsHttpTransaction> trans = static_cast<nsHttpTransaction *>(param);
trans->SetPriority(priority);
nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
nullptr, trans);
if (!trans->ConnectionInfo()) {
return;
}
nsConnectionEntry *ent = mCT.Get(trans->ConnectionInfo()->HashKey());
if (ent) {
int32_t caps = trans->Caps();
@ -2258,9 +2173,10 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, ARefBase *param)
if (conn && !trans->IsDone()) {
conn->CloseTransaction(trans, closeCode);
} else {
nsConnectionEntry *ent =
LookupConnectionEntry(trans->ConnectionInfo(), nullptr, trans);
nsConnectionEntry *ent = nullptr;
if (trans->ConnectionInfo()) {
ent = mCT.Get(trans->ConnectionInfo()->HashKey());
}
if (ent) {
uint32_t caps = trans->Caps();
int32_t transIndex;
@ -2602,8 +2518,9 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, ARefBase *param)
// 3) post event to process the pending transaction queue
//
nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
conn, nullptr);
nsConnectionEntry *ent = conn->ConnectionInfo() ?
mCT.Get(conn->ConnectionInfo()->HashKey()) : nullptr;
if (!ent) {
// this can happen if the connection is made outside of the
// connection manager and is being "reclaimed" for use with
@ -2740,7 +2657,6 @@ nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
{
MOZ_COUNT_DTOR(nsConnectionEntry);
gHttpHandler->ConnMgr()->RemovePreferredHash(this);
}
// Read Timeout Tick handlers
@ -2967,13 +2883,6 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, ARefBase *param)
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo(), false);
// If spdy has previously made a preferred entry for this host via
// the ip pooling rules. If so, connect to the preferred host instead of
// the one directly passed in here.
nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
if (preferredEntry)
ent = preferredEntry;
uint32_t parallelSpeculativeConnectLimit =
gHttpHandler->ParallelSpeculativeConnectLimit();
bool ignoreIdle = false;
@ -3573,6 +3482,33 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
return rv;
}
// register a connection to receive CanJoinConnection() for particular
// origin keys
void
nsHttpConnectionMgr::RegisterOriginCoalescingKey(nsHttpConnection *conn,
const nsACString &host,
int32_t port)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpConnectionInfo *ci = conn ? conn->ConnectionInfo() : nullptr;
if (!ci || !conn->CanDirectlyActivate()) {
return;
}
nsCString newKey;
BuildOriginFrameHashKey(newKey, ci, host, port);
nsTArray<nsWeakPtr> *listOfWeakConns = mCoalescingHash.Get(newKey);
if (!listOfWeakConns) {
listOfWeakConns = new nsTArray<nsWeakPtr>(1);
mCoalescingHash.Put(newKey, listOfWeakConns);
}
listOfWeakConns->AppendElement(do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
LOG(("nsHttpConnectionMgr::RegisterOriginCoalescingKey "
"Established New Coalescing Key %s to %p %s\n",
newKey.get(), conn, ci->HashKey().get()));
}
// method for nsITransportEventSink
NS_IMETHODIMP
nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
@ -3650,7 +3586,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
nsAutoCString suffix;
mEnt->mConnInfo->GetOriginAttributes().CreateSuffix(suffix);
newKey->Append(suffix);
newKey->Append(']');
newKey->AppendLiteral("]viaDNS");
LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
"STATUS_CONNECTING_TO Established New Coalescing Key # %d for host "
"%s [%s]", i, mEnt->mConnInfo->Origin(), newKey->get()));
@ -3751,6 +3687,12 @@ ConnectionHandle::TakeHttpConnection()
return mConn.forget();
}
already_AddRefed<nsHttpConnection>
ConnectionHandle::HttpConnection()
{
RefPtr<nsHttpConnection> rv(mConn);
return rv.forget();
}
// nsConnectionEntry
@ -3758,7 +3700,6 @@ nsHttpConnectionMgr::
nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
: mConnInfo(ci)
, mUsingSpdy(false)
, mInPreferredHash(false)
, mPreferIPv4(false)
, mPreferIPv6(false)
, mUsedForConnection(false)
@ -3774,7 +3715,7 @@ nsHttpConnectionMgr::nsConnectionEntry::AvailableForDispatchNow()
}
return gHttpHandler->ConnMgr()->
GetSpdyPreferredConn(this) ? true : false;
GetSpdyActiveConn(this) ? true : false;
}
bool
@ -3828,9 +3769,10 @@ void
nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo *ci)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsConnectionEntry *ent = LookupConnectionEntry(ci, nullptr, nullptr);
if (ent)
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
if (ent) {
ent->ResetIPFamilyPreference();
}
}
uint32_t
@ -4021,7 +3963,7 @@ nsHttpConnectionMgr::MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
"change CI from %s to %s\n", proxyConn, specificCI->HashKey().get(),
wildCardCI->HashKey().get()));
nsConnectionEntry *ent = LookupConnectionEntry(specificCI, proxyConn, nullptr);
nsConnectionEntry *ent = mCT.Get(specificCI->HashKey());
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p using ent %p (spdy %d)\n",
proxyConn, ent, ent ? ent->mUsingSpdy : 0));

Просмотреть файл

@ -244,13 +244,9 @@ private:
// Spdy sometimes resolves the address in the socket manager in order
// to re-coalesce sharded HTTP hosts. The dotted decimal address is
// combined with the Anonymous flag from the connection information
// combined with the Anonymous flag and OA from the connection information
// to build the hash key for hosts in the same ip pool.
//
// When a set of hosts are coalesced together one of them is marked
// mSpdyPreferred. The mapping is maintained in the connection mananger
// mSpdyPreferred hash.
//
nsTArray<nsCString> mCoalescingKeys;
// To have the UsingSpdy flag means some host with the same connection
@ -258,8 +254,6 @@ private:
// connection is currently using spdy.
bool mUsingSpdy : 1;
bool mInPreferredHash : 1;
// Flags to remember our happy-eyeballs decision.
// Reset only by Ctrl-F5 reload.
// True when we've first connected an IPv4 server for this host,
@ -307,6 +301,7 @@ private:
public:
static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped);
void RegisterOriginCoalescingKey(nsHttpConnection *, const nsACString &host, int32_t port);
private:
@ -518,16 +513,14 @@ private:
MOZ_MUST_USE nsresult MakeNewConnection(nsConnectionEntry *ent,
PendingTransactionInfo *pendingTransInfo);
// Manage the preferred spdy connection entry for this address
nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
nsConnectionEntry *LookupPreferredHash(nsConnectionEntry *ent);
void StorePreferredHash(nsConnectionEntry *ent);
void RemovePreferredHash(nsConnectionEntry *ent);
nsHttpConnection *GetSpdyPreferredConn(nsConnectionEntry *ent);
nsDataHashtable<nsCStringHashKey, nsConnectionEntry *> mSpdyPreferredHash;
nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
nsHttpConnection *conn,
nsHttpTransaction *trans);
// Manage h2 connection coalescing
// The hashtable contains arrays of weak pointers to nsHttpConnections
nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > mCoalescingHash;
nsHttpConnection *FindCoalescableConnection(nsConnectionEntry *ent, bool justKidding);
nsHttpConnection *FindCoalescableConnectionByHashKey(nsConnectionEntry *ent, const nsCString &key, bool justKidding);
void UpdateCoalescingForNewConn(nsHttpConnection *conn, nsConnectionEntry *ent);
nsHttpConnection *GetSpdyActiveConn(nsConnectionEntry *ent);
void ProcessSpdyPendingQ(nsConnectionEntry *ent);
void DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,

Просмотреть файл

@ -215,6 +215,7 @@ nsHttpHandler::nsHttpHandler()
, mAllowPush(true)
, mEnableAltSvc(false)
, mEnableAltSvcOE(false)
, mEnableOriginExtension(false)
, mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
, mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
, mSpdyPushAllowance(32768)
@ -1381,6 +1382,13 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
mEnableAltSvcOE = cVar;
}
if (PREF_CHANGED(HTTP_PREF("originextension"))) {
rv = prefs->GetBoolPref(HTTP_PREF("originextension"),
&cVar);
if (NS_SUCCEEDED(rv))
mEnableOriginExtension = cVar;
}
if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val);
if (NS_SUCCEEDED(rv)) {

Просмотреть файл

@ -123,6 +123,7 @@ public:
bool AllowPush() { return mAllowPush; }
bool AllowAltSvc() { return mEnableAltSvc; }
bool AllowAltSvcOE() { return mEnableAltSvcOE; }
bool AllowOriginExtension() { return mEnableOriginExtension; }
uint32_t ConnectTimeout() { return mConnectTimeout; }
uint32_t ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
bool CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
@ -502,6 +503,7 @@ private:
uint32_t mAllowPush : 1;
uint32_t mEnableAltSvc : 1;
uint32_t mEnableAltSvcOE : 1;
uint32_t mEnableOriginExtension : 1;
// Try to use SPDY features instead of HTTP/1.1 over SSL
SpdyInformation mSpdyInfo;