Bug 1334690 - Isolate AlternateService mappings by Origin Attributes. r=mcmanus draft
authorJonathan Hao <jhao@mozilla.com>
Tue, 14 Feb 2017 16:49:59 +0800
changeset 485185 0d3ed29c2059f776350bce4042372a4ad850ae7f
parent 485184 57e16386780d3bf52e5557f3570a8835eb99e361
child 545960 89cd33410ebbcec2a435c0b753379b7a8ff54907
push id45671
push userbmo:jhao@mozilla.com
push dateThu, 16 Feb 2017 10:03:31 +0000
reviewersmcmanus
bugs1334690
milestone54.0a1
Bug 1334690 - Isolate AlternateService mappings by Origin Attributes. r=mcmanus MozReview-Commit-ID: LWfmmMn25zT
netwerk/protocol/http/AlternateServices.cpp
netwerk/protocol/http/AlternateServices.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpHandler.h
--- a/netwerk/protocol/http/AlternateServices.cpp
+++ b/netwerk/protocol/http/AlternateServices.cpp
@@ -111,18 +111,21 @@ AltSvcMapping::ProcessHeader(const nsCSt
         maxage = atoi(PromiseFlatCString(currentValue).get());
         break;
       } else {
         LOG(("Alt Svc ignoring parameter %s", currentName.BeginReading()));
       }
     }
 
     if (clearEntry) {
-      LOG(("Alt Svc clearing mapping for %s:%d", originHost.get(), originPort));
-      gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort);
+      nsCString suffix;
+      originAttributes.CreateSuffix(suffix);
+      LOG(("Alt Svc clearing mapping for %s:%d:%s", originHost.get(),
+           originPort, suffix.get()));
+      gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort, originAttributes);
       continue;
     }
 
     // unescape modifies a c string in place, so afterwards
     // update nsCString length
     nsUnescape(npnToken.BeginWriting());
     npnToken.SetLength(strlen(npnToken.BeginReading()));
 
@@ -135,52 +138,55 @@ AltSvcMapping::ProcessHeader(const nsCSt
     }
 
     RefPtr<AltSvcMapping> mapping = new AltSvcMapping(gHttpHandler->ConnMgr()->GetStoragePtr(),
                                                       gHttpHandler->ConnMgr()->StorageEpoch(),
                                                       originScheme,
                                                       originHost, originPort,
                                                       username, privateBrowsing,
                                                       NowInSeconds() + maxage,
-                                                      hostname, portno, npnToken);
+                                                      hostname, portno, npnToken,
+                                                      originAttributes);
     if (mapping->TTL() <= 0) {
       LOG(("Alt Svc invalid map"));
       mapping = nullptr;
       // since this isn't a parse error, let's clear any existing mapping
       // as that would have happened if we had accepted the parameters.
-      gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort);
+      gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort, originAttributes);
     } else {
       gHttpHandler->UpdateAltServiceMapping(mapping, proxyInfo, callbacks, caps,
                                             originAttributes);
     }
   }
 }
 
 AltSvcMapping::AltSvcMapping(DataStorage *storage, int32_t epoch,
                              const nsACString &originScheme,
                              const nsACString &originHost,
                              int32_t originPort,
                              const nsACString &username,
                              bool privateBrowsing,
                              uint32_t expiresAt,
                              const nsACString &alternateHost,
                              int32_t alternatePort,
-                             const nsACString &npnToken)
+                             const nsACString &npnToken,
+                             const OriginAttributes &originAttributes)
   : mStorage(storage)
   , mStorageEpoch(epoch)
   , mAlternateHost(alternateHost)
   , mAlternatePort(alternatePort)
   , mOriginHost(originHost)
   , mOriginPort(originPort)
   , mUsername(username)
   , mPrivate(privateBrowsing)
   , mExpiresAt(expiresAt)
   , mValidated(false)
   , mMixedScheme(false)
   , mNPNToken(npnToken)
+  , mOriginAttributes(originAttributes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_FAILED(SchemeIsHTTPS(originScheme, mHttps))) {
     LOG(("AltSvcMapping ctor %p invalid scheme\n", this));
     mExpiresAt = 0; // invalid
   }
 
@@ -201,41 +207,47 @@ AltSvcMapping::AltSvcMapping(DataStorage
 
   if ((mAlternatePort == mOriginPort) &&
       mAlternateHost.EqualsIgnoreCase(mOriginHost.get())) {
     LOG(("Alt Svc is also origin Svc - ignoring\n"));
     mExpiresAt = 0; // invalid
   }
 
   if (mExpiresAt) {
-    MakeHashKey(mHashKey, originScheme, mOriginHost, mOriginPort, mPrivate);
+    MakeHashKey(mHashKey, originScheme, mOriginHost, mOriginPort, mPrivate,
+                mOriginAttributes);
   }
 }
 
 void
 AltSvcMapping::MakeHashKey(nsCString &outKey,
                            const nsACString &originScheme,
                            const nsACString &originHost,
                            int32_t originPort,
-                           bool privateBrowsing)
+                           bool privateBrowsing,
+                           const OriginAttributes &originAttributes)
 {
   outKey.Truncate();
 
   if (originPort == -1) {
     bool isHttps = originScheme.Equals("https");
     originPort = isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
   }
 
   outKey.Append(originScheme);
   outKey.Append(':');
   outKey.Append(originHost);
   outKey.Append(':');
   outKey.AppendInt(originPort);
   outKey.Append(':');
   outKey.Append(privateBrowsing ? 'P' : '.');
+  outKey.Append(':');
+  nsAutoCString suffix;
+  originAttributes.CreateSuffix(suffix);
+  outKey.Append(suffix);
 }
 
 int32_t
 AltSvcMapping::TTL()
 {
   return mExpiresAt - NowInSeconds();
 }
 
@@ -348,16 +360,20 @@ AltSvcMapping::Serialize(nsCString &out)
   out.Append(mNPNToken);
   out.Append(':');
   out.Append(mValidated ? 'y' : 'n');
   out.Append(':');
   out.AppendInt(mStorageEpoch);
   out.Append(':');
   out.Append(mMixedScheme ? 'y' : 'n');
   out.Append(':');
+  nsAutoCString suffix;
+  mOriginAttributes.CreateSuffix(suffix);
+  out.Append(suffix);
+  out.Append(':');
 }
 
 AltSvcMapping::AltSvcMapping(DataStorage *storage, int32_t epoch, const nsCString &str)
   : mStorage(storage)
   , mStorageEpoch(epoch)
 {
   mValidated = false;
   nsresult code;
@@ -389,20 +405,22 @@ COMPILER ERROR
     _NS_NEXT_TOKEN;
     mNPNToken = Substring(str, start, idx - start);
     _NS_NEXT_TOKEN;
     mValidated = Substring(str, start, idx - start).Equals(NS_LITERAL_CSTRING("y"));
     _NS_NEXT_TOKEN;
     mStorageEpoch = nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     _NS_NEXT_TOKEN;
     mMixedScheme = Substring(str, start, idx - start).Equals(NS_LITERAL_CSTRING("y"));
+    _NS_NEXT_TOKEN;
+    Unused << mOriginAttributes.PopulateFromSuffix(Substring(str, start, idx - start));
     #undef _NS_NEXT_TOKEN
 
     MakeHashKey(mHashKey, mHttps ? NS_LITERAL_CSTRING("https") : NS_LITERAL_CSTRING("http"),
-                mOriginHost, mOriginPort, mPrivate);
+                mOriginHost, mOriginPort, mPrivate, mOriginAttributes);
   } while (false);
 }
 
 // This is the asynchronous null transaction used to validate
 // an alt-svc advertisement only for https://
 class AltSvcTransaction final : public NullHttpTransaction
 {
 public:
@@ -908,17 +926,18 @@ AltSvcCache::UpdateAltServiceMapping(Alt
       // object deletes itself when done if started
       LOG(("AltSvcCache::UpdateAltServiceMapping %p .wk checker started %p\n", this, checker));
     }
   }
 }
 
 already_AddRefed<AltSvcMapping>
 AltSvcCache::GetAltServiceMapping(const nsACString &scheme, const nsACString &host,
-                                  int32_t port, bool privateBrowsing)
+                                  int32_t port, bool privateBrowsing,
+                                  const OriginAttributes &originAttributes)
 {
   bool isHTTPS;
   MOZ_ASSERT(NS_IsMainThread());
   if (!mStorage) {
     // DataStorage gives synchronous access to a memory based hash table
     // that is backed by disk where those writes are done asynchronously
     // on another thread
     mStorage = DataStorage::Get(NS_LITERAL_STRING("AlternateServices.txt"));
@@ -940,87 +959,95 @@ AltSvcCache::GetAltServiceMapping(const 
   if (!gHttpHandler->AllowAltSvc()) {
     return nullptr;
   }
   if (!gHttpHandler->AllowAltSvcOE() && !isHTTPS) {
     return nullptr;
   }
 
   nsAutoCString key;
-  AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing);
+  AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing, originAttributes);
   RefPtr<AltSvcMapping> existing = LookupMapping(key, privateBrowsing);
   LOG(("AltSvcCache::GetAltServiceMapping %p key=%s "
        "existing=%p validated=%d ttl=%d",
        this, key.get(), existing.get(), existing ? existing->Validated() : 0,
        existing ? existing->TTL() : 0));
   if (existing && !existing->Validated()) {
     existing = nullptr;
   }
   return existing.forget();
 }
 
 class ProxyClearHostMapping : public Runnable {
 public:
-  explicit ProxyClearHostMapping(const nsACString &host, int32_t port)
+  explicit ProxyClearHostMapping(const nsACString &host, int32_t port,
+                                 const OriginAttributes &originAttributes)
     : mHost(host)
     , mPort(port)
+    , mOriginAttributes(originAttributes)
     {}
 
     NS_IMETHOD Run() override
     {
       MOZ_ASSERT(NS_IsMainThread());
-      gHttpHandler->ConnMgr()->ClearHostMapping(mHost, mPort);
+      gHttpHandler->ConnMgr()->ClearHostMapping(mHost, mPort, mOriginAttributes);
       return NS_OK;
     }
 private:
     nsCString mHost;
     int32_t mPort;
+    OriginAttributes mOriginAttributes;
 };
 
 void
-AltSvcCache::ClearHostMapping(const nsACString &host, int32_t port)
+AltSvcCache::ClearHostMapping(const nsACString &host, int32_t port,
+                              const OriginAttributes &originAttributes)
 {
   if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIRunnable> event = new ProxyClearHostMapping(host, port);
+    nsCOMPtr<nsIRunnable> event = new ProxyClearHostMapping(host, port, originAttributes);
     if (event) {
       NS_DispatchToMainThread(event);
     }
     return;
   }
   nsAutoCString key;
-  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, true);
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, true,
+                             originAttributes);
   RefPtr<AltSvcMapping> existing = LookupMapping(key, true);
   if (existing) {
     existing->SetExpired();
   }
 
-  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, true);
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, true,
+                             originAttributes);
   existing = LookupMapping(key, true);
   if (existing) {
     existing->SetExpired();
   }
 
-  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, false);
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, false,
+                             originAttributes);
   existing = LookupMapping(key, false);
   if (existing) {
     existing->SetExpired();
   }
 
-  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, false);
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, false,
+                             originAttributes);
   existing = LookupMapping(key, false);
   if (existing) {
     existing->SetExpired();
   }
 }
 
 void
 AltSvcCache::ClearHostMapping(nsHttpConnectionInfo *ci)
 {
   if (!ci->GetOrigin().IsEmpty()) {
-    ClearHostMapping(ci->GetOrigin(), ci->OriginPort());
+    ClearHostMapping(ci->GetOrigin(), ci->OriginPort(), ci->GetOriginAttributes());
   }
 }
 
 void
 AltSvcCache::ClearAltServiceMappings()
 {
     MOZ_ASSERT(NS_IsMainThread());
     if (mStorage) {
--- a/netwerk/protocol/http/AlternateServices.h
+++ b/netwerk/protocol/http/AlternateServices.h
@@ -50,17 +50,18 @@ private: // ctor from ProcessHeader
                 const nsACString &originScheme,
                 const nsACString &originHost,
                 int32_t originPort,
                 const nsACString &username,
                 bool privateBrowsing,
                 uint32_t expiresAt,
                 const nsACString &alternateHost,
                 int32_t alternatePort,
-                const nsACString &npnToken);
+                const nsACString &npnToken,
+                const OriginAttributes &originAttributes);
 public:
   AltSvcMapping(DataStorage *storage, int32_t storageEpoch, const nsCString &serialized);
 
   static void ProcessHeader(const nsCString &buf, const nsCString &originScheme,
                             const nsCString &originHost, int32_t originPort,
                             const nsACString &username, bool privateBrowsing,
                             nsIInterfaceRequestor *callbacks, nsProxyInfo *proxyInfo,
                             uint32_t caps, const OriginAttributes &originAttributes);
@@ -87,17 +88,18 @@ public:
   void SetExpiresAt(int32_t val);
   void SetExpired();
   void Sync();
 
   static void MakeHashKey(nsCString &outKey,
                           const nsACString &originScheme,
                           const nsACString &originHost,
                           int32_t originPort,
-                          bool privateBrowsing);
+                          bool privateBrowsing,
+                          const OriginAttributes &originAttributes);
 
 private:
   virtual ~AltSvcMapping() {};
   void     SyncString(const nsCString& val);
   RefPtr<DataStorage> mStorage;
   int32_t             mStorageEpoch;
   void Serialize (nsCString &out);
 
@@ -115,16 +117,18 @@ private:
 
   MOZ_INIT_OUTSIDE_CTOR uint32_t mExpiresAt; // alt-svc mappping
 
   MOZ_INIT_OUTSIDE_CTOR bool mValidated;
   MOZ_INIT_OUTSIDE_CTOR bool mHttps; // origin is https://
   MOZ_INIT_OUTSIDE_CTOR bool mMixedScheme; // .wk allows http and https on same con
 
   nsCString mNPNToken;
+
+  OriginAttributes mOriginAttributes;
 };
 
 class AltSvcOverride : public nsIInterfaceRequestor
                      , public nsISpeculativeConnectionOverrider
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER
@@ -167,19 +171,20 @@ class AltSvcCache
 public:
   AltSvcCache() : mStorageEpoch(0) {}
   virtual ~AltSvcCache () {};
   void UpdateAltServiceMapping(AltSvcMapping *map, nsProxyInfo *pi,
                                nsIInterfaceRequestor *, uint32_t caps,
                                const OriginAttributes &originAttributes); // main thread
   already_AddRefed<AltSvcMapping> GetAltServiceMapping(const nsACString &scheme,
                                                        const nsACString &host,
-                                                       int32_t port, bool pb);
+                                                       int32_t port, bool pb,
+                                                       const OriginAttributes &originAttributes);
   void ClearAltServiceMappings();
-  void ClearHostMapping(const nsACString &host, int32_t port);
+  void ClearHostMapping(const nsACString &host, int32_t port, const OriginAttributes &originAttributes);
   void ClearHostMapping(nsHttpConnectionInfo *ci);
   DataStorage *GetStoragePtr() { return mStorage.get(); }
   int32_t      StorageEpoch()  { return mStorageEpoch; }
 
 private:
   already_AddRefed<AltSvcMapping> LookupMapping(const nsCString &key, bool privateBrowsing);
   RefPtr<DataStorage>             mStorage;
   int32_t                         mStorageEpoch;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5902,17 +5902,18 @@ nsHttpChannel::BeginConnect()
     RefPtr<AltSvcMapping> mapping;
     if (!mConnectionInfo && mAllowAltSvc && // per channel
         !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
         (scheme.Equals(NS_LITERAL_CSTRING("http")) ||
          scheme.Equals(NS_LITERAL_CSTRING("https"))) &&
         (!proxyInfo || proxyInfo->IsDirect()) &&
         (mapping = gHttpHandler->GetAltServiceMapping(scheme,
                                                       host, port,
-                                                      mPrivateBrowsing))) {
+                                                      mPrivateBrowsing,
+                                                      originAttributes))) {
         LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n",
              this, scheme.get(), mapping->AlternateHost().get(),
              mapping->AlternatePort(), mapping->HashKey().get()));
 
         if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) {
             nsAutoCString altUsedLine(mapping->AlternateHost());
             bool defaultPort = mapping->AlternatePort() ==
                 (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT);
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -246,19 +246,20 @@ public:
                                  const OriginAttributes &originAttributes)
     {
         mConnMgr->UpdateAltServiceMapping(map, proxyInfo, callbacks, caps,
                                           originAttributes);
     }
 
     already_AddRefed<AltSvcMapping> GetAltServiceMapping(const nsACString &scheme,
                                                          const nsACString &host,
-                                                         int32_t port, bool pb)
+                                                         int32_t port, bool pb,
+                                                         const OriginAttributes &originAttributes)
     {
-        return mConnMgr->GetAltServiceMapping(scheme, host, port, pb);
+        return mConnMgr->GetAltServiceMapping(scheme, host, port, pb, originAttributes);
     }
 
     //
     // The HTTP handler caches pointers to specific XPCOM services, and
     // provides the following helper routines for accessing those services:
     //
     nsresult GetStreamConverterService(nsIStreamConverterService **);
     nsresult GetIOService(nsIIOService** service);