зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1337791 - Part 2: http/2 origin frame extension. r=nwgh
This commit is contained in:
Родитель
58fc1b834e
Коммит
06bd9a4bae
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче