Bug 1356611 - per connection cache of JoinConnecton() r=nwgh
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 14 Apr 2017 17:24:53 -0400
changeset 353362 2d2f8ee3dd5bd0de6bc4003c0245dd9ce5dd2aa6
parent 353361 9c3586c3ec1c0f4f36e2c6562b5d6a8b5b84dc7a
child 353363 e7c2fe034f25de722dc886af7047e4b7dd12e864
push id40858
push usermcmanus@ducksong.com
push dateSun, 16 Apr 2017 00:07:38 +0000
treeherderautoland@2d2f8ee3dd5b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnwgh
bugs1356611
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1356611 - per connection cache of JoinConnecton() r=nwgh MozReview-Commit-ID: 6s0x9OWhhN1
netwerk/protocol/http/Http2Session.cpp
netwerk/protocol/http/Http2Session.h
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -84,16 +84,17 @@ Http2Session::Http2Session(nsISocketTran
   , mDownstreamRstReason(NO_HTTP_ERROR)
   , mExpectedHeaderID(0)
   , mExpectedPushPromiseID(0)
   , mContinuedPromiseStream(0)
   , mFlatHTTPResponseHeadersOut(0)
   , mShouldGoAway(false)
   , mClosed(false)
   , mCleanShutdown(false)
+  , mReceivedSettings(false)
   , mTLSProfileConfirmed(false)
   , mGoAwayReason(NO_HTTP_ERROR)
   , mClientGoAwayReason(UNASSIGNED)
   , mPeerGoAwayReason(UNASSIGNED)
   , mGoAwayID(0)
   , mOutgoingGoAwayID(0)
   , mConcurrent(0)
   , mServerPushedResources(0)
@@ -1499,16 +1500,18 @@ Http2Session::RecvSettings(Http2Session 
   if (self->mInputFrameDataSize % 6) {
     // Number of Settings is determined by dividing by each 6 byte setting
     // entry. So the payload must be a multiple of 6.
     LOG3(("Http2Session::RecvSettings %p SETTINGS wrong length data=%d",
           self, self->mInputFrameDataSize));
     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
   }
 
+  self->mReceivedSettings = true;
+
   uint32_t numEntries = self->mInputFrameDataSize / 6;
   LOG3(("Http2Session::RecvSettings %p SETTINGS Control Frame "
         "with %d entries ack=%X", self, numEntries,
         self->mInputFrameFlags & kFlag_ACK));
 
   if ((self->mInputFrameFlags & kFlag_ACK) && self->mInputFrameDataSize) {
     LOG3(("Http2Session::RecvSettings %p ACK with non zero payload is err\n", self));
     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
@@ -4148,94 +4151,106 @@ Http2Session::TestOriginFrame(const nsAC
     LOG3(("TestOriginFrame() %p sni test %d\n", this, rv));
   }
   return rv;
 }
 
 bool
 Http2Session::TestJoinConnection(const nsACString &hostname, int32_t port)
 {
+  return RealJoinConnection(hostname, port, true);
+}
+
+bool
+Http2Session::JoinConnection(const nsACString &hostname, int32_t port)
+{
+  return RealJoinConnection(hostname, port, false);
+}
+
+bool
+Http2Session::RealJoinConnection(const nsACString &hostname, int32_t port,
+                                 bool justKidding)
+{
   if (!mConnection || mClosed || mShouldGoAway) {
     return false;
   }
 
+  nsHttpConnectionInfo *ci = ConnectionInfo();
+  if (nsCString(hostname).EqualsIgnoreCase(ci->Origin()) && (port == ci->OriginPort())) {
+    return true;
+ }
+
+  if (!mReceivedSettings) {
+    return false;
+  }
+
   if (mOriginFrameActivated) {
     bool originFrameResult = TestOriginFrame(hostname, port);
     if (!originFrameResult) {
       return false;
     }
   } else {
-    LOG3(("TestJoinConnection %p no origin frame check used.\n", this));
+    LOG3(("JoinConnection %p no origin frame check used.\n", this));
+  }
+
+  nsAutoCString key(hostname);
+  key.Append(':');
+  key.Append(justKidding ? 'k' : '.');
+  key.AppendInt(port);
+  bool cachedResult;
+  if (mJoinConnectionCache.Get(key, &cachedResult)) {
+    LOG(("joinconnection [%p %s] %s result=%d cache\n",
+         this, ConnectionInfo()->HashKey().get(), key.get(),
+         cachedResult));
+    return cachedResult;
   }
 
   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],
+  static_assert(SpdyInformation::kCount == 1, "assume 1 alpn version");
+  bool joinedReturn = false;
+  if (info->ProtocolEnabled(0)) {
+    if (justKidding) {
+      rv = sslSocketControl->TestJoinConnection(info->VersionString[0],
                                                 hostname, port, &isJoined);
-      if (NS_SUCCEEDED(rv) && isJoined) {
-        return true;
-      }
+    } else {
+      rv = sslSocketControl->JoinConnection(info->VersionString[0],
+                                            hostname, port, &isJoined);
+    }
+    if (NS_SUCCEEDED(rv) && isJoined) {
+      joinedReturn = 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;
-      }
+
+  LOG(("joinconnection [%p %s] %s result=%d lookup\n",
+       this, ConnectionInfo()->HashKey().get(), key.get(), joinedReturn));
+  mJoinConnectionCache.Put(key, joinedReturn);
+  if (!justKidding) {
+    // cache a kidding entry too as this one is good for both
+    nsAutoCString key2(hostname);
+    key2.Append(':');
+    key2.Append('k');
+    key2.AppendInt(port);
+    if (!mJoinConnectionCache.Get(key2)) {
+      mJoinConnectionCache.Put(key2, joinedReturn);
     }
   }
-  return false;
+  return joinedReturn;
 }
 
 void
 Http2Session::ThrottleResponse(bool aThrottle)
 {
   // Response throttling on an h2 connection will be implemented later.
 }
 
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -422,16 +422,19 @@ private:
   bool                 mShouldGoAway;
 
   // the session has received a nsAHttpTransaction::Close()  call
   bool                 mClosed;
 
   // the session received a GoAway frame with a valid GoAwayID
   bool                 mCleanShutdown;
 
+  // the session received the opening SETTINGS frame from the server
+  bool                 mReceivedSettings;
+
   // The TLS comlpiance checks are not done in the ctor beacuse of bad
   // exception handling - so we do them at IO time and cache the result
   bool                 mTLSProfileConfirmed;
 
   // A specifc reason code for the eventual GoAway frame. If set to NO_HTTP_ERROR
   // only NO_HTTP_ERROR, PROTOCOL_ERROR, or INTERNAL_ERROR will be sent.
   errorType            mGoAwayReason;
 
@@ -513,20 +516,23 @@ private:
   bool mGoAwayOnPush;
 
   bool mUseH2Deps;
 
   bool mAttemptingEarlyData;
   // The ID(s) of the stream(s) that we are getting 0RTT data from.
   nsTArray<uint32_t> m0RTTStreams;
 
+  bool RealJoinConnection(const nsACString &hostname, int32_t port, bool jk);
   bool TestOriginFrame(const nsACString &name, int32_t port);
   bool mOriginFrameActivated;
   nsDataHashtable<nsCStringHashKey, bool> mOriginFrame;
 
+  nsDataHashtable<nsCStringHashKey, bool> mJoinConnectionCache;
+
 private:
 /// connect tunnels
   void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
   void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
   void RegisterTunnel(Http2Stream *);
   void UnRegisterTunnel(Http2Stream *);
   uint32_t FindTunnelCount(nsHttpConnectionInfo *);
   nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;