LCOV - code coverage report
Current view: top level - netwerk/protocol/http - AlternateServices.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 46 578 8.0 %
Date: 2017-07-14 16:53:18 Functions: 4 52 7.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 ts=8 et tw=80 : */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "HttpLog.h"
       8             : 
       9             : #include "AlternateServices.h"
      10             : #include "LoadInfo.h"
      11             : #include "nsEscape.h"
      12             : #include "nsHttpConnectionInfo.h"
      13             : #include "nsHttpChannel.h"
      14             : #include "nsHttpHandler.h"
      15             : #include "nsThreadUtils.h"
      16             : #include "nsHttpTransaction.h"
      17             : #include "NullHttpTransaction.h"
      18             : #include "nsISSLStatusProvider.h"
      19             : #include "nsISSLStatus.h"
      20             : #include "nsISSLSocketControl.h"
      21             : #include "nsIWellKnownOpportunisticUtils.h"
      22             : 
      23             : /* RFC 7838 Alternative Services
      24             :    http://httpwg.org/http-extensions/opsec.html
      25             :     note that connections currently do not do mixed-scheme (the I attribute
      26             :     in the ConnectionInfo prevents it) but could, do not honor tls-commit and should
      27             :     not, and always require authentication
      28             : */
      29             : 
      30             : namespace mozilla {
      31             : namespace net {
      32             : 
      33             : // function places true in outIsHTTPS if scheme is https, false if
      34             : // http, and returns an error if neither. originScheme passed into
      35             : // alternate service should already be normalized to those lower case
      36             : // strings by the URI parser (and so there is an assert)- this is an extra check.
      37             : static nsresult
      38           6 : SchemeIsHTTPS(const nsACString &originScheme, bool &outIsHTTPS)
      39             : {
      40           6 :   outIsHTTPS = originScheme.Equals(NS_LITERAL_CSTRING("https"));
      41             : 
      42           6 :   if (!outIsHTTPS && !originScheme.Equals(NS_LITERAL_CSTRING("http"))) {
      43           0 :       MOZ_ASSERT(false, "unexpected scheme");
      44             :       return NS_ERROR_UNEXPECTED;
      45             :   }
      46           6 :   return NS_OK;
      47             : }
      48             : 
      49             : void
      50           0 : AltSvcMapping::ProcessHeader(const nsCString &buf, const nsCString &originScheme,
      51             :                              const nsCString &originHost, int32_t originPort,
      52             :                              const nsACString &username, bool privateBrowsing,
      53             :                              nsIInterfaceRequestor *callbacks, nsProxyInfo *proxyInfo,
      54             :                              uint32_t caps, const OriginAttributes &originAttributes)
      55             : {
      56           0 :   MOZ_ASSERT(NS_IsMainThread());
      57           0 :   LOG(("AltSvcMapping::ProcessHeader: %s\n", buf.get()));
      58           0 :   if (!callbacks) {
      59           0 :     return;
      60             :   }
      61             : 
      62           0 :   if (proxyInfo && !proxyInfo->IsDirect()) {
      63           0 :     LOG(("AltSvcMapping::ProcessHeader ignoring due to proxy\n"));
      64           0 :     return;
      65             :   }
      66             : 
      67             :   bool isHTTPS;
      68           0 :   if (NS_FAILED(SchemeIsHTTPS(originScheme, isHTTPS))) {
      69           0 :     return;
      70             :   }
      71           0 :   if (!isHTTPS && !gHttpHandler->AllowAltSvcOE()) {
      72           0 :     LOG(("Alt-Svc Response Header for http:// origin but OE disabled\n"));
      73           0 :     return;
      74             :   }
      75             : 
      76           0 :   LOG(("Alt-Svc Response Header %s\n", buf.get()));
      77           0 :   ParsedHeaderValueListList parsedAltSvc(buf);
      78             : 
      79           0 :   for (uint32_t index = 0; index < parsedAltSvc.mValues.Length(); ++index) {
      80           0 :     uint32_t maxage = 86400; // default
      81           0 :     nsAutoCString hostname;
      82           0 :     nsAutoCString npnToken;
      83           0 :     int32_t portno = originPort;
      84           0 :     bool clearEntry = false;
      85             : 
      86           0 :     for (uint32_t pairIndex = 0;
      87           0 :          pairIndex < parsedAltSvc.mValues[index].mValues.Length();
      88             :          ++pairIndex) {
      89             :       nsDependentCSubstring &currentName =
      90           0 :         parsedAltSvc.mValues[index].mValues[pairIndex].mName;
      91             :       nsDependentCSubstring &currentValue =
      92           0 :         parsedAltSvc.mValues[index].mValues[pairIndex].mValue;
      93             : 
      94           0 :       if (!pairIndex) {
      95           0 :         if (currentName.Equals(NS_LITERAL_CSTRING("clear"))) {
      96           0 :           clearEntry = true;
      97           0 :           break;
      98             :         }
      99             : 
     100             :         // h2=[hostname]:443
     101           0 :         npnToken = currentName;
     102           0 :         int32_t colonIndex = currentValue.FindChar(':');
     103           0 :         if (colonIndex >= 0) {
     104             :           portno =
     105           0 :             atoi(PromiseFlatCString(currentValue).get() + colonIndex + 1);
     106             :         } else {
     107           0 :           colonIndex = 0;
     108             :         }
     109           0 :         hostname.Assign(currentValue.BeginReading(), colonIndex);
     110           0 :       } else if (currentName.Equals(NS_LITERAL_CSTRING("ma"))) {
     111           0 :         maxage = atoi(PromiseFlatCString(currentValue).get());
     112           0 :         break;
     113             :       } else {
     114           0 :         LOG(("Alt Svc ignoring parameter %s", currentName.BeginReading()));
     115             :       }
     116             :     }
     117             : 
     118           0 :     if (clearEntry) {
     119           0 :       nsCString suffix;
     120           0 :       originAttributes.CreateSuffix(suffix);
     121           0 :       LOG(("Alt Svc clearing mapping for %s:%d:%s", originHost.get(),
     122             :            originPort, suffix.get()));
     123           0 :       gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort, originAttributes);
     124           0 :       continue;
     125             :     }
     126             : 
     127             :     // unescape modifies a c string in place, so afterwards
     128             :     // update nsCString length
     129           0 :     nsUnescape(npnToken.BeginWriting());
     130           0 :     npnToken.SetLength(strlen(npnToken.BeginReading()));
     131             : 
     132             :     uint32_t spdyIndex;
     133           0 :     SpdyInformation *spdyInfo = gHttpHandler->SpdyInfo();
     134           0 :     if (!(NS_SUCCEEDED(spdyInfo->GetNPNIndex(npnToken, &spdyIndex)) &&
     135           0 :           spdyInfo->ProtocolEnabled(spdyIndex))) {
     136           0 :       LOG(("Alt Svc unknown protocol %s, ignoring", npnToken.get()));
     137           0 :       continue;
     138             :     }
     139             : 
     140           0 :     RefPtr<AltSvcMapping> mapping = new AltSvcMapping(gHttpHandler->ConnMgr()->GetStoragePtr(),
     141           0 :                                                       gHttpHandler->ConnMgr()->StorageEpoch(),
     142             :                                                       originScheme,
     143             :                                                       originHost, originPort,
     144             :                                                       username, privateBrowsing,
     145           0 :                                                       NowInSeconds() + maxage,
     146             :                                                       hostname, portno, npnToken,
     147           0 :                                                       originAttributes);
     148           0 :     if (mapping->TTL() <= 0) {
     149           0 :       LOG(("Alt Svc invalid map"));
     150           0 :       mapping = nullptr;
     151             :       // since this isn't a parse error, let's clear any existing mapping
     152             :       // as that would have happened if we had accepted the parameters.
     153           0 :       gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort, originAttributes);
     154             :     } else {
     155           0 :       gHttpHandler->UpdateAltServiceMapping(mapping, proxyInfo, callbacks, caps,
     156           0 :                                             originAttributes);
     157             :     }
     158             :   }
     159             : }
     160             : 
     161           0 : AltSvcMapping::AltSvcMapping(DataStorage *storage, int32_t epoch,
     162             :                              const nsACString &originScheme,
     163             :                              const nsACString &originHost,
     164             :                              int32_t originPort,
     165             :                              const nsACString &username,
     166             :                              bool privateBrowsing,
     167             :                              uint32_t expiresAt,
     168             :                              const nsACString &alternateHost,
     169             :                              int32_t alternatePort,
     170             :                              const nsACString &npnToken,
     171           0 :                              const OriginAttributes &originAttributes)
     172             :   : mStorage(storage)
     173             :   , mStorageEpoch(epoch)
     174             :   , mAlternateHost(alternateHost)
     175             :   , mAlternatePort(alternatePort)
     176             :   , mOriginHost(originHost)
     177             :   , mOriginPort(originPort)
     178             :   , mUsername(username)
     179             :   , mPrivate(privateBrowsing)
     180             :   , mExpiresAt(expiresAt)
     181             :   , mValidated(false)
     182             :   , mMixedScheme(false)
     183             :   , mNPNToken(npnToken)
     184           0 :   , mOriginAttributes(originAttributes)
     185             : {
     186           0 :   MOZ_ASSERT(NS_IsMainThread());
     187             : 
     188           0 :   if (NS_FAILED(SchemeIsHTTPS(originScheme, mHttps))) {
     189           0 :     LOG(("AltSvcMapping ctor %p invalid scheme\n", this));
     190           0 :     mExpiresAt = 0; // invalid
     191             :   }
     192             : 
     193           0 :   if (mAlternatePort == -1) {
     194           0 :     mAlternatePort = mHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
     195             :   }
     196           0 :   if (mOriginPort == -1) {
     197           0 :     mOriginPort = mHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
     198             :   }
     199             : 
     200           0 :   LOG(("AltSvcMapping ctor %p %s://%s:%d to %s:%d\n", this,
     201             :        nsCString(originScheme).get(), mOriginHost.get(), mOriginPort,
     202             :        mAlternateHost.get(), mAlternatePort));
     203             : 
     204           0 :   if (mAlternateHost.IsEmpty()) {
     205           0 :     mAlternateHost = mOriginHost;
     206             :   }
     207             : 
     208           0 :   if ((mAlternatePort == mOriginPort) &&
     209           0 :       mAlternateHost.EqualsIgnoreCase(mOriginHost.get())) {
     210           0 :     LOG(("Alt Svc is also origin Svc - ignoring\n"));
     211           0 :     mExpiresAt = 0; // invalid
     212             :   }
     213             : 
     214           0 :   if (mExpiresAt) {
     215           0 :     MakeHashKey(mHashKey, originScheme, mOriginHost, mOriginPort, mPrivate,
     216           0 :                 mOriginAttributes);
     217             :   }
     218           0 : }
     219             : 
     220             : void
     221           6 : AltSvcMapping::MakeHashKey(nsCString &outKey,
     222             :                            const nsACString &originScheme,
     223             :                            const nsACString &originHost,
     224             :                            int32_t originPort,
     225             :                            bool privateBrowsing,
     226             :                            const OriginAttributes &originAttributes)
     227             : {
     228           6 :   outKey.Truncate();
     229             : 
     230           6 :   if (originPort == -1) {
     231           1 :     bool isHttps = originScheme.Equals("https");
     232           1 :     originPort = isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
     233             :   }
     234             : 
     235           6 :   outKey.Append(originScheme);
     236           6 :   outKey.Append(':');
     237           6 :   outKey.Append(originHost);
     238           6 :   outKey.Append(':');
     239           6 :   outKey.AppendInt(originPort);
     240           6 :   outKey.Append(':');
     241           6 :   outKey.Append(privateBrowsing ? 'P' : '.');
     242           6 :   outKey.Append(':');
     243          12 :   nsAutoCString suffix;
     244           6 :   originAttributes.CreateSuffix(suffix);
     245           6 :   outKey.Append(suffix);
     246           6 : }
     247             : 
     248             : int32_t
     249           0 : AltSvcMapping::TTL()
     250             : {
     251           0 :   return mExpiresAt - NowInSeconds();
     252             : }
     253             : 
     254             : void
     255           0 : AltSvcMapping::SyncString(const nsCString& str)
     256             : {
     257           0 :   MOZ_ASSERT(NS_IsMainThread());
     258           0 :   mStorage->Put(HashKey(), str,
     259           0 :                 mPrivate ? DataStorage_Private : DataStorage_Persistent);
     260           0 : }
     261             : 
     262             : void
     263           0 : AltSvcMapping::Sync()
     264             : {
     265           0 :   if (!mStorage) {
     266           0 :     return;
     267             :   }
     268           0 :   nsCString value;
     269           0 :   Serialize(value);
     270             : 
     271           0 :   if (!NS_IsMainThread()) {
     272           0 :     nsCOMPtr<nsIRunnable> r;
     273           0 :     r = NewRunnableMethod<nsCString>("net::AltSvcMapping::SyncString",
     274             :                                      this,
     275             :                                      &AltSvcMapping::SyncString,
     276           0 :                                      value);
     277           0 :     NS_DispatchToMainThread(r, NS_DISPATCH_NORMAL);
     278           0 :     return;
     279             :   }
     280             : 
     281           0 :   mStorage->Put(HashKey(), value,
     282           0 :                 mPrivate ? DataStorage_Private : DataStorage_Persistent);
     283             : }
     284             : 
     285             : void
     286           0 : AltSvcMapping::SetValidated(bool val)
     287             : {
     288           0 :   mValidated = val;
     289           0 :   Sync();
     290           0 : }
     291             : 
     292             : void
     293           0 : AltSvcMapping::SetMixedScheme(bool val)
     294             : {
     295           0 :   mMixedScheme = val;
     296           0 :   Sync();
     297           0 : }
     298             : 
     299             : void
     300           0 : AltSvcMapping::SetExpiresAt(int32_t val)
     301             : {
     302           0 :   mExpiresAt = val;
     303           0 :   Sync();
     304           0 : }
     305             : 
     306             : void
     307           0 : AltSvcMapping::SetExpired()
     308             : {
     309           0 :   LOG(("AltSvcMapping SetExpired %p origin %s alternate %s\n", this,
     310             :        mOriginHost.get(), mAlternateHost.get()));
     311           0 :   mExpiresAt = NowInSeconds() - 1;
     312           0 :   Sync();
     313           0 : }
     314             : 
     315             : bool
     316           0 : AltSvcMapping::RouteEquals(AltSvcMapping *map)
     317             : {
     318           0 :   MOZ_ASSERT(map->mHashKey.Equals(mHashKey));
     319           0 :   return mAlternateHost.Equals(map->mAlternateHost) &&
     320           0 :     (mAlternatePort == map->mAlternatePort) &&
     321           0 :     mNPNToken.Equals(map->mNPNToken);
     322             : }
     323             : 
     324             : void
     325           0 : AltSvcMapping::GetConnectionInfo(nsHttpConnectionInfo **outCI,
     326             :                                  nsProxyInfo *pi,
     327             :                                  const OriginAttributes &originAttributes)
     328             : {
     329             :   RefPtr<nsHttpConnectionInfo> ci =
     330             :     new nsHttpConnectionInfo(mOriginHost, mOriginPort, mNPNToken,
     331             :                              mUsername, pi, originAttributes,
     332           0 :                              mAlternateHost, mAlternatePort);
     333             : 
     334             :   // http:// without the mixed-scheme attribute needs to be segmented in the
     335             :   // connection manager connection information hash with this attribute
     336           0 :   if (!mHttps && !mMixedScheme) {
     337           0 :     ci->SetInsecureScheme(true);
     338             :   }
     339           0 :   ci->SetPrivate(mPrivate);
     340           0 :   ci.forget(outCI);
     341           0 : }
     342             : 
     343             : void
     344           0 : AltSvcMapping::Serialize(nsCString &out)
     345             : {
     346           0 :   out = mHttps ? NS_LITERAL_CSTRING("https:") : NS_LITERAL_CSTRING("http:");
     347           0 :   out.Append(mOriginHost);
     348           0 :   out.Append(':');
     349           0 :   out.AppendInt(mOriginPort);
     350           0 :   out.Append(':');
     351           0 :   out.Append(mAlternateHost);
     352           0 :   out.Append(':');
     353           0 :   out.AppendInt(mAlternatePort);
     354           0 :   out.Append(':');
     355           0 :   out.Append(mUsername);
     356           0 :   out.Append(':');
     357           0 :   out.Append(mPrivate ? 'y' : 'n');
     358           0 :   out.Append(':');
     359           0 :   out.AppendInt(mExpiresAt);
     360           0 :   out.Append(':');
     361           0 :   out.Append(mNPNToken);
     362           0 :   out.Append(':');
     363           0 :   out.Append(mValidated ? 'y' : 'n');
     364           0 :   out.Append(':');
     365           0 :   out.AppendInt(mStorageEpoch);
     366           0 :   out.Append(':');
     367           0 :   out.Append(mMixedScheme ? 'y' : 'n');
     368           0 :   out.Append(':');
     369           0 :   nsAutoCString suffix;
     370           0 :   mOriginAttributes.CreateSuffix(suffix);
     371           0 :   out.Append(suffix);
     372           0 :   out.Append(':');
     373           0 : }
     374             : 
     375           0 : AltSvcMapping::AltSvcMapping(DataStorage *storage, int32_t epoch, const nsCString &str)
     376             :   : mStorage(storage)
     377           0 :   , mStorageEpoch(epoch)
     378             : {
     379           0 :   mValidated = false;
     380             :   nsresult code;
     381             : 
     382             :   // The the do {} while(0) loop acts like try/catch(e){} with the break in _NS_NEXT_TOKEN
     383             :   do {
     384             : #ifdef _NS_NEXT_TOKEN
     385             : COMPILER ERROR
     386             : #endif
     387             :     #define _NS_NEXT_TOKEN start = idx + 1; idx = str.FindChar(':', start); if (idx < 0) break;
     388           0 :     int32_t start = 0;
     389             :     int32_t idx;
     390           0 :     idx = str.FindChar(':', start); if (idx < 0) break;
     391           0 :     mHttps = Substring(str, start, idx - start).Equals(NS_LITERAL_CSTRING("https"));
     392           0 :     _NS_NEXT_TOKEN;
     393           0 :     mOriginHost = Substring(str, start, idx - start);
     394           0 :     _NS_NEXT_TOKEN;
     395           0 :     mOriginPort = nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     396           0 :     _NS_NEXT_TOKEN;
     397           0 :     mAlternateHost = Substring(str, start, idx - start);
     398           0 :     _NS_NEXT_TOKEN;
     399           0 :     mAlternatePort = nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     400           0 :     _NS_NEXT_TOKEN;
     401           0 :     mUsername = Substring(str, start, idx - start);
     402           0 :     _NS_NEXT_TOKEN;
     403           0 :     mPrivate = Substring(str, start, idx - start).Equals(NS_LITERAL_CSTRING("y"));
     404           0 :     _NS_NEXT_TOKEN;
     405           0 :     mExpiresAt = nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     406           0 :     _NS_NEXT_TOKEN;
     407           0 :     mNPNToken = Substring(str, start, idx - start);
     408           0 :     _NS_NEXT_TOKEN;
     409           0 :     mValidated = Substring(str, start, idx - start).Equals(NS_LITERAL_CSTRING("y"));
     410           0 :     _NS_NEXT_TOKEN;
     411           0 :     mStorageEpoch = nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     412           0 :     _NS_NEXT_TOKEN;
     413           0 :     mMixedScheme = Substring(str, start, idx - start).Equals(NS_LITERAL_CSTRING("y"));
     414           0 :     _NS_NEXT_TOKEN;
     415           0 :     Unused << mOriginAttributes.PopulateFromSuffix(Substring(str, start, idx - start));
     416             :     #undef _NS_NEXT_TOKEN
     417             : 
     418           0 :     MakeHashKey(mHashKey, mHttps ? NS_LITERAL_CSTRING("https") : NS_LITERAL_CSTRING("http"),
     419           0 :                 mOriginHost, mOriginPort, mPrivate, mOriginAttributes);
     420             :   } while (false);
     421           0 : }
     422             : 
     423             : // This is the asynchronous null transaction used to validate
     424             : // an alt-svc advertisement only for https://
     425             : class AltSvcTransaction final : public NullHttpTransaction
     426             : {
     427             : public:
     428           0 :     AltSvcTransaction(AltSvcMapping *map,
     429             :                       nsHttpConnectionInfo *ci,
     430             :                       nsIInterfaceRequestor *callbacks,
     431             :                       uint32_t caps)
     432           0 :     : NullHttpTransaction(ci, callbacks, caps & ~NS_HTTP_ALLOW_KEEPALIVE)
     433             :     , mMapping(map)
     434             :     , mRunning(true)
     435             :     , mTriedToValidate(false)
     436           0 :     , mTriedToWrite(false)
     437             :   {
     438           0 :     LOG(("AltSvcTransaction ctor %p map %p [%s -> %s]",
     439             :          this, map, map->OriginHost().get(), map->AlternateHost().get()));
     440           0 :     MOZ_ASSERT(mMapping);
     441           0 :     MOZ_ASSERT(mMapping->HTTPS());
     442           0 :   }
     443             : 
     444           0 :   ~AltSvcTransaction() override
     445           0 :   {
     446           0 :     LOG(("AltSvcTransaction dtor %p map %p running %d",
     447             :          this, mMapping.get(), mRunning));
     448             : 
     449           0 :     if (mRunning) {
     450           0 :       MaybeValidate(NS_OK);
     451             :     }
     452           0 :     if (!mMapping->Validated()) {
     453             :       // try again later
     454           0 :       mMapping->SetExpiresAt(NowInSeconds() + 2);
     455             :     }
     456           0 :     LOG(("AltSvcTransaction dtor %p map %p validated %d [%s]",
     457             :          this, mMapping.get(), mMapping->Validated(),
     458             :          mMapping->HashKey().get()));
     459           0 :   }
     460             : 
     461             : private:
     462             :   // check on alternate route.
     463             :   // also evaluate 'reasonable assurances' for opportunistic security
     464           0 :   void MaybeValidate(nsresult reason)
     465             :   {
     466           0 :     MOZ_ASSERT(mMapping->HTTPS()); // http:// uses the .wk path
     467             : 
     468           0 :     if (mTriedToValidate) {
     469           0 :       return;
     470             :     }
     471           0 :     mTriedToValidate = true;
     472             : 
     473           0 :     LOG(("AltSvcTransaction::MaybeValidate() %p reason=%" PRIx32 " running=%d conn=%p write=%d",
     474             :          this, static_cast<uint32_t>(reason), mRunning, mConnection.get(), mTriedToWrite));
     475             : 
     476           0 :     if (mTriedToWrite && reason == NS_BASE_STREAM_CLOSED) {
     477             :       // The normal course of events is to cause the transaction to fail with CLOSED
     478             :       // on a write - so that's a success that means the HTTP/2 session is setup.
     479           0 :       reason = NS_OK;
     480             :     }
     481             : 
     482           0 :     if (NS_FAILED(reason) || !mRunning || !mConnection) {
     483           0 :       LOG(("AltSvcTransaction::MaybeValidate %p Failed due to precondition", this));
     484           0 :       return;
     485             :     }
     486             : 
     487             :     // insist on >= http/2
     488           0 :     uint32_t version = mConnection->Version();
     489           0 :     LOG(("AltSvcTransaction::MaybeValidate() %p version %d\n", this, version));
     490           0 :     if (version != HTTP_VERSION_2) {
     491           0 :       LOG(("AltSvcTransaction::MaybeValidate %p Failed due to protocol version", this));
     492           0 :       return;
     493             :     }
     494             : 
     495           0 :     nsCOMPtr<nsISupports> secInfo;
     496           0 :     mConnection->GetSecurityInfo(getter_AddRefs(secInfo));
     497           0 :     nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(secInfo);
     498             : 
     499           0 :     LOG(("AltSvcTransaction::MaybeValidate() %p socketControl=%p\n",
     500             :          this, socketControl.get()));
     501             : 
     502           0 :     if (socketControl->GetFailedVerification()) {
     503           0 :       LOG(("AltSvcTransaction::MaybeValidate() %p "
     504             :            "not validated due to auth error", this));
     505           0 :       return;
     506             :     }
     507             : 
     508           0 :     LOG(("AltSvcTransaction::MaybeValidate() %p "
     509             :          "validating alternate service with successful auth check", this));
     510           0 :     mMapping->SetValidated(true);
     511             :   }
     512             : 
     513             : public:
     514           0 :   void Close(nsresult reason) override
     515             :   {
     516           0 :     LOG(("AltSvcTransaction::Close() %p reason=%" PRIx32 " running %d",
     517             :          this, static_cast<uint32_t>(reason), mRunning));
     518             : 
     519           0 :     MaybeValidate(reason);
     520           0 :     if (!mMapping->Validated() && mConnection) {
     521           0 :       mConnection->DontReuse();
     522             :     }
     523           0 :     NullHttpTransaction::Close(reason);
     524           0 :   }
     525             : 
     526           0 :   nsresult ReadSegments(nsAHttpSegmentReader *reader,
     527             :                         uint32_t count, uint32_t *countRead) override
     528             :   {
     529           0 :     LOG(("AltSvcTransaction::ReadSegements() %p\n", this));
     530           0 :     mTriedToWrite = true;
     531           0 :     return NullHttpTransaction::ReadSegments(reader, count, countRead);
     532             :   }
     533             : 
     534             : private:
     535             :   RefPtr<AltSvcMapping>   mMapping;
     536             :   uint32_t                mRunning : 1;
     537             :   uint32_t                mTriedToValidate : 1;
     538             :   uint32_t                mTriedToWrite : 1;
     539             : };
     540             : 
     541             : class WellKnownChecker
     542             : {
     543             : public:
     544           0 :   WellKnownChecker(nsIURI *uri, const nsCString &origin, uint32_t caps, nsHttpConnectionInfo *ci, AltSvcMapping *mapping)
     545           0 :     : mWaiting(2) // waiting for 2 channels (default and alternate) to complete
     546             :     , mOrigin(origin)
     547           0 :     , mAlternatePort(ci->RoutedPort())
     548             :     , mMapping(mapping)
     549             :     , mCI(ci)
     550             :     , mURI(uri)
     551           0 :     , mCaps(caps)
     552             :   {
     553           0 :     LOG(("WellKnownChecker ctor %p\n", this));
     554           0 :     MOZ_ASSERT(!mMapping->HTTPS());
     555           0 :   }
     556             : 
     557           0 :   nsresult Start()
     558             :   {
     559           0 :     LOG(("WellKnownChecker::Start %p\n", this));
     560           0 :     nsCOMPtr<nsILoadInfo> loadInfo = new LoadInfo(nsContentUtils::GetSystemPrincipal(),
     561             :                                                   nullptr, nullptr,
     562             :                                                   nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
     563           0 :                                                   nsIContentPolicy::TYPE_OTHER);
     564           0 :     loadInfo->SetOriginAttributes(mCI->GetOriginAttributes());
     565             : 
     566           0 :     RefPtr<nsHttpChannel> chan = new nsHttpChannel();
     567             :     nsresult rv;
     568             : 
     569           0 :     mTransactionAlternate = new TransactionObserver(chan, this);
     570           0 :     RefPtr<nsHttpConnectionInfo> newCI = mCI->Clone();
     571           0 :     rv = MakeChannel(chan, mTransactionAlternate, newCI, mURI, mCaps, loadInfo);
     572           0 :     if (NS_FAILED(rv)) {
     573           0 :       return rv;
     574             :     }
     575           0 :     chan = new nsHttpChannel();
     576           0 :     mTransactionOrigin = new TransactionObserver(chan, this);
     577           0 :     newCI = nullptr;
     578           0 :     return MakeChannel(chan, mTransactionOrigin, newCI, mURI, mCaps, loadInfo);
     579             :   }
     580             : 
     581           0 :   void Done(TransactionObserver *finished)
     582             :   {
     583           0 :     MOZ_ASSERT(NS_IsMainThread());
     584           0 :     LOG(("WellKnownChecker::Done %p waiting for %d\n", this, mWaiting));
     585             : 
     586           0 :     mWaiting--; // another channel is complete
     587           0 :     if (!mWaiting) { // there are all complete!
     588           0 :       nsAutoCString mAlternateCT, mOriginCT;
     589           0 :       mTransactionOrigin->mChannel->GetContentType(mOriginCT);
     590           0 :       mTransactionAlternate->mChannel->GetContentType(mAlternateCT);
     591           0 :       nsCOMPtr<nsIWellKnownOpportunisticUtils> uu = do_CreateInstance(NS_WELLKNOWNOPPORTUNISTICUTILS_CONTRACTID);
     592           0 :       bool accepted = false;
     593             : 
     594           0 :       if (!mTransactionOrigin->mStatusOK) {
     595           0 :         LOG(("WellKnownChecker::Done %p origin was not 200 response code\n", this));
     596           0 :       } else if (!mTransactionAlternate->mAuthOK) {
     597           0 :         LOG(("WellKnownChecker::Done %p alternate was not TLS authenticated\n", this));
     598           0 :       } else if (!mTransactionAlternate->mStatusOK) {
     599           0 :         LOG(("WellKnownChecker::Done %p alternate was not 200 response code\n", this));
     600           0 :       } else if (!mTransactionAlternate->mVersionOK) {
     601           0 :         LOG(("WellKnownChecker::Done %p alternate was not at least h2\n", this));
     602           0 :       } else if (!mTransactionAlternate->mWKResponse.Equals(mTransactionOrigin->mWKResponse)) {
     603           0 :         LOG(("WellKnownChecker::Done %p alternate and origin "
     604             :              ".wk representations don't match\norigin: %s\alternate:%s\n", this,
     605             :              mTransactionOrigin->mWKResponse.get(),
     606             :              mTransactionAlternate->mWKResponse.get()));
     607           0 :       } else if (!mAlternateCT.Equals(mOriginCT)) {
     608           0 :         LOG(("WellKnownChecker::Done %p alternate and origin content types dont match\n", this));
     609           0 :       } else if (!mAlternateCT.Equals(NS_LITERAL_CSTRING("application/json"))) {
     610           0 :         LOG(("WellKnownChecker::Done %p .wk content type is %s\n", this, mAlternateCT.get()));
     611           0 :       } else if (!uu) {
     612           0 :         LOG(("WellKnownChecker::Done %p json parser service unavailable\n", this));
     613             :       } else {
     614           0 :         accepted = true;
     615             :       }
     616             : 
     617           0 :       if (accepted) {
     618           0 :         MOZ_ASSERT(!mMapping->HTTPS()); // https:// does not use .wk
     619             : 
     620           0 :         nsresult rv = uu->Verify(mTransactionAlternate->mWKResponse, mOrigin, mAlternatePort);
     621           0 :         if (NS_SUCCEEDED(rv)) {
     622           0 :           bool validWK = false;
     623           0 :           bool mixedScheme = false;
     624           0 :           int32_t lifetime = 0;
     625           0 :           Unused << uu->GetValid(&validWK);
     626           0 :           Unused << uu->GetLifetime(&lifetime);
     627           0 :           Unused << uu->GetMixed(&mixedScheme);
     628           0 :           if (!validWK) {
     629           0 :             LOG(("WellKnownChecker::Done %p json parser declares invalid\n%s\n", this, mTransactionAlternate->mWKResponse.get()));
     630           0 :             accepted = false;
     631             :           }
     632           0 :           if (accepted && (lifetime > 0)) {
     633           0 :             if (mMapping->TTL() > lifetime) {
     634           0 :               LOG(("WellKnownChecker::Done %p atl-svc lifetime reduced by .wk\n", this));
     635           0 :               mMapping->SetExpiresAt(NowInSeconds() + lifetime);
     636             :             } else {
     637           0 :               LOG(("WellKnownChecker::Done %p .wk lifetime exceeded alt-svc ma so ignored\n", this));
     638             :             }
     639             :           }
     640           0 :           if (accepted && mixedScheme) {
     641           0 :             mMapping->SetMixedScheme(true);
     642           0 :             LOG(("WellKnownChecker::Done %p atl-svc .wk allows mixed scheme\n", this));
     643             :           }
     644             :         } else {
     645           0 :           LOG(("WellKnownChecker::Done %p .wk jason eval failed to run\n", this));
     646           0 :           accepted = false;
     647             :         }
     648             :       }
     649             : 
     650           0 :       MOZ_ASSERT(!mMapping->Validated());
     651           0 :       if (accepted) {
     652           0 :         LOG(("WellKnownChecker::Done %p Alternate for %s ACCEPTED\n", this, mOrigin.get()));
     653           0 :         mMapping->SetValidated(true);
     654             :       } else {
     655           0 :         LOG(("WellKnownChecker::Done %p Alternate for %s FAILED\n", this, mOrigin.get()));
     656             :         // try again soon
     657           0 :         mMapping->SetExpiresAt(NowInSeconds() + 2);
     658             :       }
     659             : 
     660           0 :       delete this;
     661             :     }
     662           0 :   }
     663             : 
     664           0 :   ~WellKnownChecker()
     665           0 :   {
     666           0 :     LOG(("WellKnownChecker dtor %p\n", this));
     667           0 :   }
     668             : 
     669             : private:
     670             :   nsresult
     671           0 :   MakeChannel(nsHttpChannel *chan, TransactionObserver *obs, nsHttpConnectionInfo *ci,
     672             :               nsIURI *uri, uint32_t caps, nsILoadInfo *loadInfo)
     673             :   {
     674             :     uint64_t channelId;
     675             :     nsLoadFlags flags;
     676           0 :     if (NS_FAILED(gHttpHandler->NewChannelId(channelId)) ||
     677           0 :         NS_FAILED(chan->Init(uri, caps, nullptr, 0, nullptr, channelId)) ||
     678           0 :         NS_FAILED(chan->SetAllowAltSvc(false)) ||
     679           0 :         NS_FAILED(chan->SetRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_ERROR)) ||
     680           0 :         NS_FAILED(chan->SetLoadInfo(loadInfo)) ||
     681           0 :         NS_FAILED(chan->GetLoadFlags(&flags))) {
     682           0 :       return NS_ERROR_FAILURE;
     683             :     }
     684           0 :     flags |= HttpBaseChannel::LOAD_BYPASS_CACHE;
     685           0 :     if (NS_FAILED(chan->SetLoadFlags(flags))) {
     686           0 :       return NS_ERROR_FAILURE;
     687             :     }
     688           0 :     chan->SetTransactionObserver(obs);
     689           0 :     chan->SetConnectionInfo(ci);
     690           0 :     return chan->AsyncOpen2(obs);
     691             :   }
     692             : 
     693             :   RefPtr<TransactionObserver>  mTransactionAlternate;
     694             :   RefPtr<TransactionObserver>  mTransactionOrigin;
     695             :   uint32_t                     mWaiting; // semaphore
     696             :   nsCString                    mOrigin;
     697             :   int32_t                      mAlternatePort;
     698             :   RefPtr<AltSvcMapping>        mMapping;
     699             :   RefPtr<nsHttpConnectionInfo> mCI;
     700             :   nsCOMPtr<nsIURI>             mURI;
     701             :   uint32_t                     mCaps;
     702             : };
     703             : 
     704           0 : NS_IMPL_ISUPPORTS(TransactionObserver, nsIStreamListener)
     705             : 
     706           0 : TransactionObserver::TransactionObserver(nsHttpChannel *channel, WellKnownChecker *checker)
     707             :   : mChannel(channel)
     708             :   , mChecker(checker)
     709             :   , mRanOnce(false)
     710             :   , mAuthOK(false)
     711             :   , mVersionOK(false)
     712           0 :   , mStatusOK(false)
     713             : {
     714           0 :   LOG(("TransactionObserver ctor %p channel %p checker %p\n", this, channel, checker));
     715           0 :   mChannelRef = do_QueryInterface((nsIHttpChannel *)channel);
     716           0 : }
     717             : 
     718             : void
     719           0 : TransactionObserver::Complete(nsHttpTransaction *aTrans, nsresult reason)
     720             : {
     721             :   // socket thread
     722           0 :   MOZ_ASSERT(!NS_IsMainThread());
     723           0 :   if (mRanOnce) {
     724           0 :     return;
     725             :   }
     726           0 :   mRanOnce = true;
     727             : 
     728           0 :   RefPtr<nsAHttpConnection> conn = aTrans->GetConnectionReference();
     729           0 :   LOG(("TransactionObserver::Complete %p aTrans %p reason %" PRIx32 " conn %p\n",
     730             :        this, aTrans, static_cast<uint32_t>(reason), conn.get()));
     731           0 :   if (!conn) {
     732           0 :     return;
     733             :   }
     734           0 :   uint32_t version = conn->Version();
     735           0 :   mVersionOK = (((reason == NS_BASE_STREAM_CLOSED) || (reason == NS_OK)) &&
     736           0 :                 conn->Version() == HTTP_VERSION_2);
     737             : 
     738           0 :   nsCOMPtr<nsISupports> secInfo;
     739           0 :   conn->GetSecurityInfo(getter_AddRefs(secInfo));
     740           0 :   nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(secInfo);
     741           0 :   LOG(("TransactionObserver::Complete version %u socketControl %p\n",
     742             :        version, socketControl.get()));
     743           0 :   if (!socketControl) {
     744           0 :     return;
     745             :   }
     746             : 
     747           0 :   mAuthOK = !socketControl->GetFailedVerification();
     748           0 :   LOG(("TransactionObserve::Complete %p trans %p authOK %d versionOK %d\n",
     749             :        this, aTrans, mAuthOK, mVersionOK));
     750             : }
     751             : 
     752             : #define MAX_WK 32768
     753             : 
     754             : NS_IMETHODIMP
     755           0 : TransactionObserver::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
     756             : {
     757           0 :   MOZ_ASSERT(NS_IsMainThread());
     758             :   // only consider the first 32KB.. because really.
     759           0 :   mWKResponse.SetCapacity(MAX_WK);
     760           0 :   return NS_OK;
     761             : }
     762             : 
     763             : NS_IMETHODIMP
     764           0 : TransactionObserver::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
     765             :                                      nsIInputStream *aStream, uint64_t aOffset, uint32_t aCount)
     766             : {
     767           0 :   MOZ_ASSERT(NS_IsMainThread());
     768           0 :   uint64_t newLen = aCount + mWKResponse.Length();
     769           0 :   if (newLen < MAX_WK) {
     770           0 :     char *startByte =  reinterpret_cast<char *>(mWKResponse.BeginWriting()) + mWKResponse.Length();
     771             :     uint32_t amtRead;
     772           0 :     if (NS_SUCCEEDED(aStream->Read(startByte, aCount, &amtRead))) {
     773           0 :       MOZ_ASSERT(mWKResponse.Length() + amtRead < MAX_WK);
     774           0 :       mWKResponse.SetLength(mWKResponse.Length() + amtRead);
     775           0 :       LOG(("TransactionObserver onDataAvailable %p read %d of .wk [%d]\n",
     776             :            this, amtRead, mWKResponse.Length()));
     777             :     } else {
     778           0 :       LOG(("TransactionObserver onDataAvailable %p read error\n", this));
     779             :     }
     780             :   }
     781           0 :   return NS_OK;
     782             : }
     783             : 
     784             : NS_IMETHODIMP
     785           0 : TransactionObserver::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, nsresult code)
     786             : {
     787           0 :   MOZ_ASSERT(NS_IsMainThread());
     788           0 :   LOG(("TransactionObserver onStopRequest %p code %" PRIx32 "\n",
     789             :        this, static_cast<uint32_t>(code)));
     790           0 :   if (NS_SUCCEEDED(code)) {
     791           0 :     nsHttpResponseHead *hdrs = mChannel->GetResponseHead();
     792           0 :     LOG(("TransactionObserver onStopRequest %p http resp %d\n",
     793             :          this, hdrs ? hdrs->Status() : -1));
     794           0 :     mStatusOK = hdrs && (hdrs->Status() == 200);
     795             :   }
     796           0 :   if (mChecker) {
     797           0 :     mChecker->Done(this);
     798             :   }
     799           0 :   return NS_OK;
     800             : }
     801             : 
     802             : already_AddRefed<AltSvcMapping>
     803           6 : AltSvcCache::LookupMapping(const nsCString &key, bool privateBrowsing)
     804             : {
     805           6 :   LOG(("AltSvcCache::LookupMapping %p %s\n", this, key.get()));
     806           6 :   if (!mStorage) {
     807           0 :     LOG(("AltSvcCache::LookupMapping %p no backing store\n", this));
     808           0 :     return nullptr;
     809             :   }
     810             :   nsCString val(mStorage->Get(key,
     811          12 :                               privateBrowsing ? DataStorage_Private : DataStorage_Persistent));
     812           6 :   if (val.IsEmpty()) {
     813           6 :     LOG(("AltSvcCache::LookupMapping %p MISS\n", this));
     814           6 :     return nullptr;
     815             :   }
     816           0 :   RefPtr<AltSvcMapping> rv = new AltSvcMapping(mStorage, mStorageEpoch, val);
     817           0 :   if (!rv->Validated() && (rv->StorageEpoch() != mStorageEpoch)) {
     818             :     // this was an in progress validation abandoned in a different session
     819             :     // rare edge case will not detect session change - that's ok as only impact
     820             :     // will be loss of alt-svc to this origin for this session.
     821           0 :     LOG(("AltSvcCache::LookupMapping %p invalid hit - MISS\n", this));
     822           0 :     mStorage->Remove(key,
     823           0 :                      rv->Private() ? DataStorage_Private : DataStorage_Persistent);
     824           0 :     return nullptr;
     825             :   }
     826             : 
     827           0 :   if (rv->TTL() <= 0) {
     828           0 :     LOG(("AltSvcCache::LookupMapping %p expired hit - MISS\n", this));
     829           0 :     mStorage->Remove(key,
     830           0 :                      rv->Private() ? DataStorage_Private : DataStorage_Persistent);
     831           0 :     return nullptr;
     832             :   }
     833             : 
     834           0 :   MOZ_ASSERT(rv->Private() == privateBrowsing);
     835           0 :   LOG(("AltSvcCache::LookupMapping %p HIT %p\n", this, rv.get()));
     836           0 :   return rv.forget();
     837             : }
     838             : 
     839             : void
     840           0 : AltSvcCache::UpdateAltServiceMapping(AltSvcMapping *map, nsProxyInfo *pi,
     841             :                                      nsIInterfaceRequestor *aCallbacks,
     842             :                                      uint32_t caps,
     843             :                                      const OriginAttributes &originAttributes)
     844             : {
     845           0 :   MOZ_ASSERT(NS_IsMainThread());
     846           0 :   if (!mStorage) {
     847           0 :     return;
     848             :   }
     849           0 :   RefPtr<AltSvcMapping> existing = LookupMapping(map->HashKey(), map->Private());
     850           0 :   LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p existing %p %s validated=%d",
     851             :        this, map, existing.get(), map->AlternateHost().get(),
     852             :        existing ? existing->Validated() : 0));
     853             : 
     854           0 :   if (existing && existing->Validated()) {
     855           0 :     if (existing->RouteEquals(map)){
     856             :       // update expires in storage
     857             :       // if this is http:// then a ttl can only be extended via .wk, so ignore this
     858             :       // header path unless it is making things shorter
     859           0 :       if (existing->HTTPS()) {
     860           0 :         LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p updates ttl of %p\n",
     861             :              this, map, existing.get()));
     862           0 :         existing->SetExpiresAt(map->GetExpiresAt());
     863             :       } else {
     864           0 :         if (map->GetExpiresAt() < existing->GetExpiresAt()) {
     865           0 :           LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p reduces ttl of %p\n",
     866             :                this, map, existing.get()));
     867           0 :           existing->SetExpiresAt(map->GetExpiresAt());
     868             :         } else {
     869           0 :           LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p tries to extend %p but"
     870             :                " cannot as without .wk\n",
     871             :                this, map, existing.get()));
     872             :         }
     873             :       }
     874           0 :       return;
     875             :     }
     876             : 
     877             :     // new alternate. remove old entry and start new validation
     878           0 :     LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p overwrites %p\n",
     879             :          this, map, existing.get()));
     880           0 :     existing = nullptr;
     881           0 :     mStorage->Remove(map->HashKey(),
     882           0 :                      map->Private() ? DataStorage_Private : DataStorage_Persistent);
     883             :   }
     884             : 
     885           0 :   if (existing && !existing->Validated()) {
     886           0 :     LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p ignored because %p "
     887             :          "still in progress\n", this, map, existing.get()));
     888           0 :     return;
     889             :   }
     890             : 
     891             :   // start new validation
     892           0 :   MOZ_ASSERT(!map->Validated());
     893           0 :   map->Sync();
     894             : 
     895           0 :   RefPtr<nsHttpConnectionInfo> ci;
     896           0 :   map->GetConnectionInfo(getter_AddRefs(ci), pi, originAttributes);
     897           0 :   caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
     898           0 :   caps |= NS_HTTP_ERROR_SOFTLY;
     899             : 
     900           0 :   if (map->HTTPS()) {
     901           0 :     LOG(("AltSvcCache::UpdateAltServiceMapping %p validation via "
     902             :          "speculative connect started\n", this));
     903             :     // for https resources we only establish a connection
     904           0 :     nsCOMPtr<nsIInterfaceRequestor> callbacks = new AltSvcOverride(aCallbacks);
     905             :     RefPtr<AltSvcTransaction> nullTransaction =
     906           0 :       new AltSvcTransaction(map, ci, aCallbacks, caps);
     907           0 :     nsresult rv = gHttpHandler->ConnMgr()->SpeculativeConnect(ci, callbacks, caps, nullTransaction);
     908           0 :     if (NS_FAILED(rv)) {
     909           0 :       LOG(("AltSvcCache::UpdateAltServiceMapping %p "
     910             :            "speculative connect failed with code %08x\n", this,
     911             :            static_cast<uint32_t>(rv)));
     912             :     }
     913             :   } else {
     914             :     // for http:// resources we fetch .well-known too
     915           0 :     nsAutoCString origin (NS_LITERAL_CSTRING("http://") + map->OriginHost());
     916           0 :     if (map->OriginPort() != NS_HTTP_DEFAULT_PORT) {
     917           0 :       origin.Append(':');
     918           0 :       origin.AppendInt(map->OriginPort());
     919             :     }
     920             : 
     921           0 :     nsCOMPtr<nsIURI> wellKnown;
     922           0 :     nsAutoCString uri(origin);
     923           0 :     uri.Append(NS_LITERAL_CSTRING("/.well-known/http-opportunistic"));
     924           0 :     NS_NewURI(getter_AddRefs(wellKnown), uri);
     925             : 
     926           0 :     auto *checker = new WellKnownChecker(wellKnown, origin, caps, ci, map);
     927           0 :     if (NS_FAILED(checker->Start())) {
     928           0 :       LOG(("AltSvcCache::UpdateAltServiceMapping %p .wk checker failed to start\n", this));
     929           0 :       map->SetExpired();
     930           0 :       delete checker;
     931           0 :       checker = nullptr;
     932             :     } else {
     933             :       // object deletes itself when done if started
     934           0 :       LOG(("AltSvcCache::UpdateAltServiceMapping %p .wk checker started %p\n", this, checker));
     935             :     }
     936             :   }
     937             : }
     938             : 
     939             : already_AddRefed<AltSvcMapping>
     940           6 : AltSvcCache::GetAltServiceMapping(const nsACString &scheme, const nsACString &host,
     941             :                                   int32_t port, bool privateBrowsing,
     942             :                                   const OriginAttributes &originAttributes)
     943             : {
     944             :   bool isHTTPS;
     945           6 :   MOZ_ASSERT(NS_IsMainThread());
     946           6 :   if (!mStorage) {
     947             :     // DataStorage gives synchronous access to a memory based hash table
     948             :     // that is backed by disk where those writes are done asynchronously
     949             :     // on another thread
     950           1 :     mStorage = DataStorage::Get(DataStorageClass::AlternateServices);
     951           1 :     if (mStorage) {
     952           1 :       bool storageWillPersist = false;
     953           1 :       if (NS_FAILED(mStorage->Init(storageWillPersist))) {
     954           0 :         mStorage = nullptr;
     955             :       }
     956             :     }
     957           1 :     if (!mStorage) {
     958           0 :       LOG(("AltSvcCache::GetAltServiceMapping WARN NO STORAGE\n"));
     959             :     }
     960           1 :     mStorageEpoch = NowInSeconds();
     961             :   }
     962             : 
     963           6 :   if (NS_FAILED(SchemeIsHTTPS(scheme, isHTTPS))) {
     964           0 :     return nullptr;
     965             :   }
     966           6 :   if (!gHttpHandler->AllowAltSvc()) {
     967           0 :     return nullptr;
     968             :   }
     969           6 :   if (!gHttpHandler->AllowAltSvcOE() && !isHTTPS) {
     970           0 :     return nullptr;
     971             :   }
     972             : 
     973          12 :   nsAutoCString key;
     974           6 :   AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing, originAttributes);
     975          12 :   RefPtr<AltSvcMapping> existing = LookupMapping(key, privateBrowsing);
     976           6 :   LOG(("AltSvcCache::GetAltServiceMapping %p key=%s "
     977             :        "existing=%p validated=%d ttl=%d",
     978             :        this, key.get(), existing.get(), existing ? existing->Validated() : 0,
     979             :        existing ? existing->TTL() : 0));
     980           6 :   if (existing && !existing->Validated()) {
     981           0 :     existing = nullptr;
     982             :   }
     983           6 :   return existing.forget();
     984             : }
     985             : 
     986           0 : class ProxyClearHostMapping : public Runnable {
     987             : public:
     988           0 :   explicit ProxyClearHostMapping(const nsACString& host,
     989             :                                  int32_t port,
     990             :                                  const OriginAttributes& originAttributes)
     991           0 :     : Runnable("net::ProxyClearHostMapping")
     992             :     , mHost(host)
     993             :     , mPort(port)
     994           0 :     , mOriginAttributes(originAttributes)
     995             :   {
     996           0 :   }
     997             : 
     998           0 :   NS_IMETHOD Run() override
     999             :   {
    1000           0 :     MOZ_ASSERT(NS_IsMainThread());
    1001           0 :     gHttpHandler->ConnMgr()->ClearHostMapping(mHost, mPort, mOriginAttributes);
    1002           0 :     return NS_OK;
    1003             :     }
    1004             : private:
    1005             :     nsCString mHost;
    1006             :     int32_t mPort;
    1007             :     OriginAttributes mOriginAttributes;
    1008             : };
    1009             : 
    1010             : void
    1011           0 : AltSvcCache::ClearHostMapping(const nsACString &host, int32_t port,
    1012             :                               const OriginAttributes &originAttributes)
    1013             : {
    1014           0 :   if (!NS_IsMainThread()) {
    1015           0 :     nsCOMPtr<nsIRunnable> event = new ProxyClearHostMapping(host, port, originAttributes);
    1016           0 :     if (event) {
    1017           0 :       NS_DispatchToMainThread(event);
    1018             :     }
    1019           0 :     return;
    1020             :   }
    1021           0 :   nsAutoCString key;
    1022           0 :   AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, true,
    1023           0 :                              originAttributes);
    1024           0 :   RefPtr<AltSvcMapping> existing = LookupMapping(key, true);
    1025           0 :   if (existing) {
    1026           0 :     existing->SetExpired();
    1027             :   }
    1028             : 
    1029           0 :   AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, true,
    1030           0 :                              originAttributes);
    1031           0 :   existing = LookupMapping(key, true);
    1032           0 :   if (existing) {
    1033           0 :     existing->SetExpired();
    1034             :   }
    1035             : 
    1036           0 :   AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, false,
    1037           0 :                              originAttributes);
    1038           0 :   existing = LookupMapping(key, false);
    1039           0 :   if (existing) {
    1040           0 :     existing->SetExpired();
    1041             :   }
    1042             : 
    1043           0 :   AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, false,
    1044           0 :                              originAttributes);
    1045           0 :   existing = LookupMapping(key, false);
    1046           0 :   if (existing) {
    1047           0 :     existing->SetExpired();
    1048             :   }
    1049             : }
    1050             : 
    1051             : void
    1052           0 : AltSvcCache::ClearHostMapping(nsHttpConnectionInfo *ci)
    1053             : {
    1054           0 :   if (!ci->GetOrigin().IsEmpty()) {
    1055           0 :     ClearHostMapping(ci->GetOrigin(), ci->OriginPort(), ci->GetOriginAttributes());
    1056             :   }
    1057           0 : }
    1058             : 
    1059             : void
    1060           0 : AltSvcCache::ClearAltServiceMappings()
    1061             : {
    1062           0 :     MOZ_ASSERT(NS_IsMainThread());
    1063           0 :     if (mStorage) {
    1064           0 :       mStorage->Clear();
    1065             :     }
    1066           0 : }
    1067             : 
    1068             : NS_IMETHODIMP
    1069           0 : AltSvcOverride::GetInterface(const nsIID &iid, void **result)
    1070             : {
    1071           0 :   if (NS_SUCCEEDED(QueryInterface(iid, result)) && *result) {
    1072           0 :     return NS_OK;
    1073             :   }
    1074           0 :   return mCallbacks->GetInterface(iid, result);
    1075             : }
    1076             : 
    1077             : NS_IMETHODIMP
    1078           0 : AltSvcOverride::GetIgnoreIdle(bool *ignoreIdle)
    1079             : {
    1080           0 :   *ignoreIdle = true;
    1081           0 :   return NS_OK;
    1082             : }
    1083             : 
    1084             : NS_IMETHODIMP
    1085           0 : AltSvcOverride::GetParallelSpeculativeConnectLimit(
    1086             :   uint32_t *parallelSpeculativeConnectLimit)
    1087             : {
    1088           0 :   *parallelSpeculativeConnectLimit = 32;
    1089           0 :   return NS_OK;
    1090             : }
    1091             : 
    1092             : NS_IMETHODIMP
    1093           0 : AltSvcOverride::GetIsFromPredictor(bool *isFromPredictor)
    1094             : {
    1095           0 :   *isFromPredictor = false;
    1096           0 :   return NS_OK;
    1097             : }
    1098             : 
    1099             : NS_IMETHODIMP
    1100           0 : AltSvcOverride::GetAllow1918(bool *allow)
    1101             : {
    1102             :   // normally we don't do speculative connects to 1918.. and we use
    1103             :   // speculative connects for the mapping validation, so override
    1104             :   // that default here for alt-svc
    1105           0 :   *allow = true;
    1106           0 :   return NS_OK;
    1107             : }
    1108             : 
    1109           0 : NS_IMPL_ISUPPORTS(AltSvcOverride, nsIInterfaceRequestor, nsISpeculativeConnectionOverrider)
    1110             : 
    1111             : } // namespace net
    1112             : } // namespace mozilla

Generated by: LCOV version 1.13