Bug 1356611 - per connection cache of JoinConnecton() r=nwgh draft
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 14 Apr 2017 17:24:53 -0400
changeset 563235 2920e420ccc1d8baa8f0420835f98eb86af977f2
parent 562868 1a1069b27f40edbfbcf1aa81a5e7dfb39845a5fe
child 624424 0ffc34aaec852b03d21dedc31f9f2cb4e89794dd
push id54244
push userbmo:mcmanus@ducksong.com
push dateSat, 15 Apr 2017 17:07:05 +0000
reviewersnwgh
bugs1356611
milestone55.0a1
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;