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