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 
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include "TRRServiceChannel.h"
9 
10 #include "HttpLog.h"
11 #include "AltServiceChild.h"
12 #include "mozilla/ScopeExit.h"
13 #include "mozilla/StaticPrefs_network.h"
14 #include "mozilla/Unused.h"
15 #include "nsDNSPrefetch.h"
16 #include "nsEscape.h"
17 #include "nsHttpTransaction.h"
18 #include "nsICancelable.h"
19 #include "nsICachingChannel.h"
20 #include "nsIHttpPushListener.h"
21 #include "nsIProtocolProxyService2.h"
22 #include "nsIOService.h"
23 #include "nsISeekableStream.h"
24 #include "nsURLHelper.h"
25 #include "ProxyConfigLookup.h"
26 #include "TRRLoadInfo.h"
27 #include "ReferrerInfo.h"
28 #include "TRR.h"
29 
30 namespace mozilla {
31 namespace net {
32 
NS_IMPL_ADDREF(TRRServiceChannel)33 NS_IMPL_ADDREF(TRRServiceChannel)
34 
35 // Because nsSupportsWeakReference isn't thread-safe we must ensure that
36 // TRRServiceChannel is destroyed on the target thread. Any Release() called
37 // on a different thread is dispatched to the target thread.
38 bool TRRServiceChannel::DispatchRelease() {
39   if (mCurrentEventTarget->IsOnCurrentThread()) {
40     return false;
41   }
42 
43   mCurrentEventTarget->Dispatch(
44       NewNonOwningRunnableMethod("net::TRRServiceChannel::Release", this,
45                                  &TRRServiceChannel::Release),
46       NS_DISPATCH_NORMAL);
47 
48   return true;
49 }
50 
NS_IMETHODIMP_(MozExternalRefCountType)51 NS_IMETHODIMP_(MozExternalRefCountType)
52 TRRServiceChannel::Release() {
53   nsrefcnt count = mRefCnt - 1;
54   if (DispatchRelease()) {
55     // Redispatched to the target thread.
56     return count;
57   }
58 
59   MOZ_ASSERT(0 != mRefCnt, "dup release");
60   count = --mRefCnt;
61   NS_LOG_RELEASE(this, count, "TRRServiceChannel");
62 
63   if (0 == count) {
64     mRefCnt = 1;
65     delete (this);
66     return 0;
67   }
68 
69   return count;
70 }
71 
72 NS_INTERFACE_MAP_BEGIN(TRRServiceChannel)
NS_INTERFACE_MAP_ENTRY(nsIRequest)73   NS_INTERFACE_MAP_ENTRY(nsIRequest)
74   NS_INTERFACE_MAP_ENTRY(nsIChannel)
75   NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
76   NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
77   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
78   NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
79   NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
80   NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
81   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
82   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
83   NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
84   NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
85   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
86   NS_INTERFACE_MAP_ENTRY_CONCRETE(TRRServiceChannel)
87 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
88 
89 TRRServiceChannel::TRRServiceChannel()
90     : HttpAsyncAborter<TRRServiceChannel>(this),
91       mProxyRequest(nullptr, "TRRServiceChannel::mProxyRequest"),
92       mCurrentEventTarget(GetCurrentEventTarget()) {
93   LOG(("TRRServiceChannel ctor [this=%p]\n", this));
94 }
95 
~TRRServiceChannel()96 TRRServiceChannel::~TRRServiceChannel() {
97   LOG(("TRRServiceChannel dtor [this=%p]\n", this));
98 }
99 
100 NS_IMETHODIMP
Cancel(nsresult status)101 TRRServiceChannel::Cancel(nsresult status) {
102   LOG(("TRRServiceChannel::Cancel [this=%p status=%" PRIx32 "]\n", this,
103        static_cast<uint32_t>(status)));
104   if (mCanceled) {
105     LOG(("  ignoring; already canceled\n"));
106     return NS_OK;
107   }
108 
109   mCanceled = true;
110   mStatus = status;
111 
112   nsCOMPtr<nsICancelable> proxyRequest;
113   {
114     auto req = mProxyRequest.Lock();
115     proxyRequest.swap(*req);
116   }
117 
118   if (proxyRequest) {
119     NS_DispatchToMainThread(
120         NS_NewRunnableFunction(
121             "CancelProxyRequest",
122             [proxyRequest, status]() { proxyRequest->Cancel(status); }),
123         NS_DISPATCH_NORMAL);
124   }
125 
126   CancelNetworkRequest(status);
127   return NS_OK;
128 }
129 
CancelNetworkRequest(nsresult aStatus)130 void TRRServiceChannel::CancelNetworkRequest(nsresult aStatus) {
131   if (mTransaction) {
132     nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus);
133     if (NS_FAILED(rv)) {
134       LOG(("failed to cancel the transaction\n"));
135     }
136   }
137   if (mTransactionPump) mTransactionPump->Cancel(aStatus);
138 }
139 
140 NS_IMETHODIMP
Suspend()141 TRRServiceChannel::Suspend() {
142   LOG(("TRRServiceChannel::SuspendInternal [this=%p]\n", this));
143 
144   if (mTransactionPump) {
145     return mTransactionPump->Suspend();
146   }
147 
148   return NS_OK;
149 }
150 
151 NS_IMETHODIMP
Resume()152 TRRServiceChannel::Resume() {
153   LOG(("TRRServiceChannel::Resume [this=%p]\n", this));
154 
155   if (mTransactionPump) {
156     return mTransactionPump->Resume();
157   }
158 
159   return NS_OK;
160 }
161 
162 NS_IMETHODIMP
GetSecurityInfo(nsISupports ** securityInfo)163 TRRServiceChannel::GetSecurityInfo(nsISupports** securityInfo) {
164   NS_ENSURE_ARG_POINTER(securityInfo);
165   *securityInfo = mSecurityInfo;
166   NS_IF_ADDREF(*securityInfo);
167   return NS_OK;
168 }
169 
170 NS_IMETHODIMP
AsyncOpen(nsIStreamListener * aListener)171 TRRServiceChannel::AsyncOpen(nsIStreamListener* aListener) {
172   NS_ENSURE_ARG_POINTER(aListener);
173   NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS);
174   NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED);
175 
176   if (mCanceled) {
177     ReleaseListeners();
178     return mStatus;
179   }
180 
181   // HttpBaseChannel::MaybeWaitForUploadStreamLength can only be used on main
182   // thread, so we can only return an error here.
183 #ifdef NIGHTLY_BUILD
184   MOZ_ASSERT(!LoadPendingInputStreamLengthOperation());
185 #endif
186   if (LoadPendingInputStreamLengthOperation()) {
187     return NS_ERROR_FAILURE;
188   }
189 
190   if (!gHttpHandler->Active()) {
191     LOG(("  after HTTP shutdown..."));
192     ReleaseListeners();
193     return NS_ERROR_NOT_AVAILABLE;
194   }
195 
196   nsAutoCString scheme;
197   mURI->GetScheme(scheme);
198   if (!scheme.LowerCaseEqualsLiteral("https")) {
199     return NS_ERROR_FAILURE;
200   }
201 
202   nsresult rv = NS_CheckPortSafety(mURI);
203   if (NS_FAILED(rv)) {
204     ReleaseListeners();
205     return rv;
206   }
207 
208   StoreIsPending(true);
209   StoreWasOpened(true);
210 
211   mListener = aListener;
212 
213   mAsyncOpenTime = TimeStamp::Now();
214 
215   rv = MaybeResolveProxyAndBeginConnect();
216   if (NS_FAILED(rv)) {
217     Unused << AsyncAbort(rv);
218   }
219 
220   return NS_OK;
221 }
222 
MaybeResolveProxyAndBeginConnect()223 nsresult TRRServiceChannel::MaybeResolveProxyAndBeginConnect() {
224   nsresult rv;
225 
226   // The common case for HTTP channels is to begin proxy resolution and return
227   // at this point. The only time we know mProxyInfo already is if we're
228   // proxying a non-http protocol like ftp. We don't need to discover proxy
229   // settings if we are never going to make a network connection.
230   if (!mProxyInfo &&
231       !(mLoadFlags & (nsICachingChannel::LOAD_ONLY_FROM_CACHE |
232                       nsICachingChannel::LOAD_NO_NETWORK_IO)) &&
233       NS_SUCCEEDED(ResolveProxy())) {
234     return NS_OK;
235   }
236 
237   rv = BeginConnect();
238   if (NS_FAILED(rv)) {
239     Unused << AsyncAbort(rv);
240   }
241 
242   return NS_OK;
243 }
244 
ResolveProxy()245 nsresult TRRServiceChannel::ResolveProxy() {
246   LOG(("TRRServiceChannel::ResolveProxy [this=%p]\n", this));
247   if (!NS_IsMainThread()) {
248     return NS_DispatchToMainThread(
249         NewRunnableMethod("TRRServiceChannel::ResolveProxy", this,
250                           &TRRServiceChannel::ResolveProxy),
251         NS_DISPATCH_NORMAL);
252   }
253 
254   MOZ_ASSERT(NS_IsMainThread());
255 
256   // TODO: bug 1625171. Consider moving proxy resolution to socket process.
257   RefPtr<TRRServiceChannel> self = this;
258   nsCOMPtr<nsICancelable> proxyRequest;
259   nsresult rv = ProxyConfigLookup::Create(
260       [self](nsIProxyInfo* aProxyInfo, nsresult aStatus) {
261         self->OnProxyAvailable(nullptr, nullptr, aProxyInfo, aStatus);
262       },
263       mURI, mProxyResolveFlags, getter_AddRefs(proxyRequest));
264 
265   if (NS_FAILED(rv)) {
266     if (!mCurrentEventTarget->IsOnCurrentThread()) {
267       return mCurrentEventTarget->Dispatch(
268           NewRunnableMethod<nsresult>("TRRServiceChannel::AsyncAbort", this,
269                                       &TRRServiceChannel::AsyncAbort, rv),
270           NS_DISPATCH_NORMAL);
271     }
272   }
273 
274   {
275     auto req = mProxyRequest.Lock();
276     // We only set mProxyRequest if the channel hasn't already been cancelled
277     // on another thread.
278     if (!mCanceled) {
279       *req = proxyRequest.forget();
280     }
281   }
282 
283   // If the channel has been cancelled, we go ahead and cancel the proxy
284   // request right here.
285   if (proxyRequest) {
286     proxyRequest->Cancel(mStatus);
287   }
288 
289   return rv;
290 }
291 
292 NS_IMETHODIMP
OnProxyAvailable(nsICancelable * request,nsIChannel * channel,nsIProxyInfo * pi,nsresult status)293 TRRServiceChannel::OnProxyAvailable(nsICancelable* request, nsIChannel* channel,
294                                     nsIProxyInfo* pi, nsresult status) {
295   LOG(("TRRServiceChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
296        " mStatus=%" PRIx32 "]\n",
297        this, pi, static_cast<uint32_t>(status),
298        static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
299 
300   if (!mCurrentEventTarget->IsOnCurrentThread()) {
301     RefPtr<TRRServiceChannel> self = this;
302     nsCOMPtr<nsIProxyInfo> info = pi;
303     return mCurrentEventTarget->Dispatch(
304         NS_NewRunnableFunction("TRRServiceChannel::OnProxyAvailable",
305                                [self, info, status]() {
306                                  self->OnProxyAvailable(nullptr, nullptr, info,
307                                                         status);
308                                }),
309         NS_DISPATCH_NORMAL);
310   }
311 
312   MOZ_ASSERT(mCurrentEventTarget->IsOnCurrentThread());
313 
314   {
315     auto proxyRequest = mProxyRequest.Lock();
316     *proxyRequest = nullptr;
317   }
318 
319   nsresult rv;
320 
321   // If status is a failure code, then it means that we failed to resolve
322   // proxy info.  That is a non-fatal error assuming it wasn't because the
323   // request was canceled.  We just failover to DIRECT when proxy resolution
324   // fails (failure can mean that the PAC URL could not be loaded).
325 
326   if (NS_SUCCEEDED(status)) mProxyInfo = pi;
327 
328   if (!gHttpHandler->Active()) {
329     LOG(
330         ("nsHttpChannel::OnProxyAvailable [this=%p] "
331          "Handler no longer active.\n",
332          this));
333     rv = NS_ERROR_NOT_AVAILABLE;
334   } else {
335     rv = BeginConnect();
336   }
337 
338   if (NS_FAILED(rv)) {
339     Unused << AsyncAbort(rv);
340   }
341   return rv;
342 }
343 
BeginConnect()344 nsresult TRRServiceChannel::BeginConnect() {
345   LOG(("TRRServiceChannel::BeginConnect [this=%p]\n", this));
346   nsresult rv;
347 
348   // Construct connection info object
349   nsAutoCString host;
350   nsAutoCString scheme;
351   int32_t port = -1;
352   bool isHttps = mURI->SchemeIs("https");
353 
354   rv = mURI->GetScheme(scheme);
355   if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host);
356   if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port);
357   if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec);
358   if (NS_FAILED(rv)) {
359     return rv;
360   }
361 
362   // Just a warning here because some nsIURIs do not implement this method.
363   Unused << NS_WARN_IF(NS_FAILED(mURI->GetUsername(mUsername)));
364 
365   // Reject the URL if it doesn't specify a host
366   if (host.IsEmpty()) {
367     rv = NS_ERROR_MALFORMED_URI;
368     return rv;
369   }
370   LOG(("host=%s port=%d\n", host.get(), port));
371   LOG(("uri=%s\n", mSpec.get()));
372 
373   nsCOMPtr<nsProxyInfo> proxyInfo;
374   if (mProxyInfo) proxyInfo = do_QueryInterface(mProxyInfo);
375 
376   mRequestHead.SetHTTPS(isHttps);
377   mRequestHead.SetOrigin(scheme, host, port);
378 
379   RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo(
380       host, port, ""_ns, mUsername, proxyInfo, OriginAttributes(), isHttps);
381   // TODO: Bug 1622778 for using AltService in socket process.
382   StoreAllowAltSvc(XRE_IsParentProcess() && LoadAllowAltSvc());
383   bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo);
384   bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo &&
385                       !(mCaps & NS_HTTP_BE_CONSERVATIVE) &&
386                       !LoadBeConservative();
387 
388   RefPtr<AltSvcMapping> mapping;
389   if (!mConnectionInfo && LoadAllowAltSvc() &&  // per channel
390       (http2Allowed || http3Allowed) && !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
391       AltSvcMapping::AcceptableProxy(proxyInfo) &&
392       (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
393       (mapping = gHttpHandler->GetAltServiceMapping(
394            scheme, host, port, mPrivateBrowsing, OriginAttributes(),
395            http2Allowed, http3Allowed))) {
396     LOG(("TRRServiceChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n",
397          this, scheme.get(), mapping->AlternateHost().get(),
398          mapping->AlternatePort(), mapping->HashKey().get()));
399 
400     if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) {
401       nsAutoCString altUsedLine(mapping->AlternateHost());
402       bool defaultPort =
403           mapping->AlternatePort() ==
404           (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT);
405       if (!defaultPort) {
406         altUsedLine.AppendLiteral(":");
407         altUsedLine.AppendInt(mapping->AlternatePort());
408       }
409       rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine);
410       MOZ_ASSERT(NS_SUCCEEDED(rv));
411     }
412 
413     LOG(("TRRServiceChannel %p Using connection info from altsvc mapping",
414          this));
415     mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo,
416                                OriginAttributes());
417     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
418     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE, !isHttps);
419   } else if (mConnectionInfo) {
420     LOG(("TRRServiceChannel %p Using channel supplied connection info", this));
421     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
422   } else {
423     LOG(("TRRServiceChannel %p Using default connection info", this));
424 
425     mConnectionInfo = connInfo;
426     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
427   }
428 
429   // Need to re-ask the handler, since mConnectionInfo may not be the connInfo
430   // we used earlier
431   if (gHttpHandler->IsHttp2Excluded(mConnectionInfo)) {
432     StoreAllowSpdy(0);
433     mCaps |= NS_HTTP_DISALLOW_SPDY;
434     mConnectionInfo->SetNoSpdy(true);
435   }
436 
437   // If TimingEnabled flag is not set after OnModifyRequest() then
438   // clear the already recorded AsyncOpen value for consistency.
439   if (!LoadTimingEnabled()) mAsyncOpenTime = TimeStamp();
440 
441   // if this somehow fails we can go on without it
442   Unused << gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
443 
444   // Adjust mCaps according to our request headers:
445   //  - If "Connection: close" is set as a request header, then do not bother
446   //    trying to establish a keep-alive connection.
447   if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) {
448     mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE);
449   }
450 
451   if (gHttpHandler->CriticalRequestPrioritization()) {
452     if (mClassOfService & nsIClassOfService::Leader) {
453       mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
454     }
455     if (mClassOfService & nsIClassOfService::Unblocked) {
456       mCaps |= NS_HTTP_LOAD_UNBLOCKED;
457     }
458     if (mClassOfService & nsIClassOfService::UrgentStart &&
459         gHttpHandler->IsUrgentStartEnabled()) {
460       mCaps |= NS_HTTP_URGENT_START;
461       SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);
462     }
463   }
464 
465   // Force-Reload should reset the persistent connection pool for this host
466   if (mLoadFlags & LOAD_FRESH_CONNECTION) {
467     // just the initial document resets the whole pool
468     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
469       gHttpHandler->AltServiceCache()->ClearAltServiceMappings();
470       rv = gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanupWithConnInfo(
471           mConnectionInfo);
472       if (NS_FAILED(rv)) {
473         LOG((
474             "TRRServiceChannel::BeginConnect "
475             "DoShiftReloadConnectionCleanupWithConnInfo failed: %08x [this=%p]",
476             static_cast<uint32_t>(rv), this));
477       }
478     }
479   }
480 
481   if (mCanceled) {
482     return mStatus;
483   }
484 
485   MaybeStartDNSPrefetch();
486 
487   rv = ContinueOnBeforeConnect();
488   if (NS_FAILED(rv)) {
489     return rv;
490   }
491 
492   return NS_OK;
493 }
494 
ContinueOnBeforeConnect()495 nsresult TRRServiceChannel::ContinueOnBeforeConnect() {
496   LOG(("TRRServiceChannel::ContinueOnBeforeConnect [this=%p]\n", this));
497 
498   // ensure that we are using a valid hostname
499   if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Origin()))) {
500     return NS_ERROR_UNKNOWN_HOST;
501   }
502 
503   if (LoadIsTRRServiceChannel()) {
504     mCaps |= NS_HTTP_LARGE_KEEPALIVE;
505     mCaps |= NS_HTTP_DISALLOW_HTTPS_RR;
506   }
507 
508   mCaps |= NS_HTTP_TRR_FLAGS_FROM_MODE(nsIRequest::GetTRRMode());
509 
510   // Finalize ConnectionInfo flags before SpeculativeConnect
511   mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
512   mConnectionInfo->SetPrivate(mPrivateBrowsing);
513   mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
514   mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
515                                      LoadBeConservative());
516   mConnectionInfo->SetTlsFlags(mTlsFlags);
517   mConnectionInfo->SetIsTrrServiceChannel(LoadIsTRRServiceChannel());
518   mConnectionInfo->SetTRRMode(nsIRequest::GetTRRMode());
519   mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
520   mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
521 
522   return Connect();
523 }
524 
Connect()525 nsresult TRRServiceChannel::Connect() {
526   LOG(("TRRServiceChannel::Connect [this=%p]\n", this));
527 
528   nsresult rv = SetupTransaction();
529   if (NS_FAILED(rv)) {
530     return rv;
531   }
532 
533   rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
534   if (NS_FAILED(rv)) {
535     return rv;
536   }
537 
538   return mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump));
539 }
540 
SetupTransaction()541 nsresult TRRServiceChannel::SetupTransaction() {
542   LOG(("TRRServiceChannel::SetupTransaction [this=%p, cos=%u, prio=%d]\n", this,
543        mClassOfService, mPriority));
544 
545   NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
546 
547   nsresult rv;
548 
549   if (!LoadAllowSpdy()) {
550     mCaps |= NS_HTTP_DISALLOW_SPDY;
551   }
552   if (!LoadAllowHttp3()) {
553     mCaps |= NS_HTTP_DISALLOW_HTTP3;
554   }
555   if (LoadBeConservative()) {
556     mCaps |= NS_HTTP_BE_CONSERVATIVE;
557   }
558 
559   // Use the URI path if not proxying (transparent proxying such as proxy
560   // CONNECT does not count here). Also figure out what HTTP version to use.
561   nsAutoCString buf, path;
562   nsCString* requestURI;
563 
564   // This is the normal e2e H1 path syntax "/index.html"
565   rv = mURI->GetPathQueryRef(path);
566   if (NS_FAILED(rv)) {
567     return rv;
568   }
569 
570   // path may contain UTF-8 characters, so ensure that they're escaped.
571   if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces,
572                    buf)) {
573     requestURI = &buf;
574   } else {
575     requestURI = &path;
576   }
577 
578   // trim off the #ref portion if any...
579   int32_t ref1 = requestURI->FindChar('#');
580   if (ref1 != kNotFound) {
581     requestURI->SetLength(ref1);
582   }
583 
584   if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
585     mRequestHead.SetVersion(gHttpHandler->HttpVersion());
586   } else {
587     mRequestHead.SetPath(*requestURI);
588 
589     // RequestURI should be the absolute uri H1 proxy syntax
590     // "http://foo/index.html" so we will overwrite the relative version in
591     // requestURI
592     rv = mURI->GetUserPass(buf);
593     if (NS_FAILED(rv)) return rv;
594     if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
595                            strncmp(mSpec.get(), "https:", 6) == 0)) {
596       nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI);
597       rv = tempURI->GetAsciiSpec(path);
598       if (NS_FAILED(rv)) return rv;
599       requestURI = &path;
600     } else {
601       requestURI = &mSpec;
602     }
603 
604     // trim off the #ref portion if any...
605     int32_t ref2 = requestURI->FindChar('#');
606     if (ref2 != kNotFound) {
607       requestURI->SetLength(ref2);
608     }
609 
610     mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
611   }
612 
613   mRequestHead.SetRequestURI(*requestURI);
614 
615   // Force setting no-cache header for TRRServiceChannel.
616   // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
617   // no proxy is configured since we might be talking with a transparent
618   // proxy, i.e. one that operates at the network level.  See bug #14772.
619   rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
620   MOZ_ASSERT(NS_SUCCEEDED(rv));
621   // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
622   // no-cache'
623   if (mRequestHead.Version() >= HttpVersion::v1_1) {
624     rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
625     MOZ_ASSERT(NS_SUCCEEDED(rv));
626   }
627 
628   // create wrapper for this channel's notification callbacks
629   nsCOMPtr<nsIInterfaceRequestor> callbacks;
630   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
631                                          getter_AddRefs(callbacks));
632 
633   // create the transaction object
634   mTransaction = new nsHttpTransaction();
635   LOG1(("TRRServiceChannel %p created nsHttpTransaction %p\n", this,
636         mTransaction.get()));
637 
638   // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
639   if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS;
640 
641   if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
642     mCaps |= NS_HTTP_CALL_CONTENT_SNIFFER;
643   }
644 
645   if (LoadTimingEnabled()) mCaps |= NS_HTTP_TIMING_ENABLED;
646 
647   nsCOMPtr<nsIHttpPushListener> pushListener;
648   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
649                                 NS_GET_IID(nsIHttpPushListener),
650                                 getter_AddRefs(pushListener));
651   HttpTransactionShell::OnPushCallback pushCallback = nullptr;
652   if (pushListener) {
653     mCaps |= NS_HTTP_ONPUSH_LISTENER;
654     nsWeakPtr weakPtrThis(
655         do_GetWeakReference(static_cast<nsIHttpChannel*>(this)));
656     pushCallback = [weakPtrThis](uint32_t aPushedStreamId,
657                                  const nsACString& aUrl,
658                                  const nsACString& aRequestString,
659                                  HttpTransactionShell* aTransaction) {
660       if (nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(weakPtrThis)) {
661         return static_cast<TRRServiceChannel*>(channel.get())
662             ->OnPush(aPushedStreamId, aUrl, aRequestString, aTransaction);
663       }
664       return NS_ERROR_NOT_AVAILABLE;
665     };
666   }
667 
668   EnsureRequestContext();
669 
670   rv = mTransaction->Init(
671       mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength,
672       LoadUploadStreamHasHeaders(), mCurrentEventTarget, callbacks, this,
673       mTopBrowsingContextId, HttpTrafficCategory::eInvalid, mRequestContext,
674       mClassOfService, mInitialRwin, LoadResponseTimeoutEnabled(), mChannelId,
675       nullptr, std::move(pushCallback), mTransWithPushedStream,
676       mPushedStreamId);
677 
678   mTransWithPushedStream = nullptr;
679 
680   if (NS_FAILED(rv)) {
681     mTransaction = nullptr;
682     return rv;
683   }
684 
685   return rv;
686 }
687 
SetPushedStreamTransactionAndId(HttpTransactionShell * aTransWithPushedStream,uint32_t aPushedStreamId)688 void TRRServiceChannel::SetPushedStreamTransactionAndId(
689     HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId) {
690   MOZ_ASSERT(!mTransWithPushedStream);
691   LOG(("TRRServiceChannel::SetPushedStreamTransaction [this=%p] trans=%p", this,
692        aTransWithPushedStream));
693 
694   mTransWithPushedStream = aTransWithPushedStream;
695   mPushedStreamId = aPushedStreamId;
696 }
697 
OnPush(uint32_t aPushedStreamId,const nsACString & aUrl,const nsACString & aRequestString,HttpTransactionShell * aTransaction)698 nsresult TRRServiceChannel::OnPush(uint32_t aPushedStreamId,
699                                    const nsACString& aUrl,
700                                    const nsACString& aRequestString,
701                                    HttpTransactionShell* aTransaction) {
702   MOZ_ASSERT(aTransaction);
703   LOG(("TRRServiceChannel::OnPush [this=%p, trans=%p]\n", this, aTransaction));
704 
705   MOZ_ASSERT(mCaps & NS_HTTP_ONPUSH_LISTENER);
706   nsCOMPtr<nsIHttpPushListener> pushListener;
707   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
708                                 NS_GET_IID(nsIHttpPushListener),
709                                 getter_AddRefs(pushListener));
710 
711   if (!pushListener) {
712     LOG(
713         ("TRRServiceChannel::OnPush [this=%p] notification callbacks do not "
714          "implement nsIHttpPushListener\n",
715          this));
716     return NS_ERROR_NOT_AVAILABLE;
717   }
718 
719   nsCOMPtr<nsIURI> pushResource;
720   nsresult rv;
721 
722   // Create a Channel for the Push Resource
723   rv = NS_NewURI(getter_AddRefs(pushResource), aUrl);
724   if (NS_FAILED(rv)) {
725     return NS_ERROR_FAILURE;
726   }
727 
728   nsCOMPtr<nsILoadInfo> loadInfo =
729       static_cast<TRRLoadInfo*>(mLoadInfo.get())->Clone();
730   nsCOMPtr<nsIChannel> pushHttpChannel;
731   rv = gHttpHandler->CreateTRRServiceChannel(pushResource, nullptr, 0, nullptr,
732                                              loadInfo,
733                                              getter_AddRefs(pushHttpChannel));
734   NS_ENSURE_SUCCESS(rv, rv);
735 
736   rv = pushHttpChannel->SetLoadFlags(mLoadFlags);
737   NS_ENSURE_SUCCESS(rv, rv);
738 
739   RefPtr<TRRServiceChannel> channel;
740   CallQueryInterface(pushHttpChannel, channel.StartAssignment());
741   MOZ_ASSERT(channel);
742   if (!channel) {
743     return NS_ERROR_UNEXPECTED;
744   }
745 
746   // new channel needs mrqeuesthead and headers from pushedStream
747   channel->mRequestHead.ParseHeaderSet(aRequestString.BeginReading());
748   channel->mLoadGroup = mLoadGroup;
749   channel->mCallbacks = mCallbacks;
750 
751   // Link the pushed stream with the new channel and call listener
752   channel->SetPushedStreamTransactionAndId(aTransaction, aPushedStreamId);
753   rv = pushListener->OnPush(this, channel);
754   return rv;
755 }
756 
MaybeStartDNSPrefetch()757 void TRRServiceChannel::MaybeStartDNSPrefetch() {
758   if (mConnectionInfo->UsingHttpProxy() ||
759       (mLoadFlags & (nsICachingChannel::LOAD_NO_NETWORK_IO |
760                      nsICachingChannel::LOAD_ONLY_FROM_CACHE))) {
761     return;
762   }
763 
764   LOG(
765       ("TRRServiceChannel::MaybeStartDNSPrefetch [this=%p] "
766        "prefetching%s\n",
767        this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
768 
769   OriginAttributes originAttributes;
770   mDNSPrefetch =
771       new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode(), this,
772                         LoadTimingEnabled());
773   mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
774 }
775 
776 NS_IMETHODIMP
OnTransportStatus(nsITransport * trans,nsresult status,int64_t progress,int64_t progressMax)777 TRRServiceChannel::OnTransportStatus(nsITransport* trans, nsresult status,
778                                      int64_t progress, int64_t progressMax) {
779   return NS_OK;
780 }
781 
CallOnStartRequest()782 nsresult TRRServiceChannel::CallOnStartRequest() {
783   LOG(("TRRServiceChannel::CallOnStartRequest [this=%p]", this));
784 
785   if (LoadOnStartRequestCalled()) {
786     LOG(("CallOnStartRequest already invoked before"));
787     return mStatus;
788   }
789 
790   nsresult rv = NS_OK;
791   StoreTracingEnabled(false);
792 
793   // Ensure mListener->OnStartRequest will be invoked before exiting
794   // this function.
795   auto onStartGuard = MakeScopeExit([&] {
796     LOG(
797         ("  calling mListener->OnStartRequest by ScopeExit [this=%p, "
798          "listener=%p]\n",
799          this, mListener.get()));
800     MOZ_ASSERT(!LoadOnStartRequestCalled());
801 
802     if (mListener) {
803       nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
804       StoreOnStartRequestCalled(true);
805       deleteProtector->OnStartRequest(this);
806     }
807     StoreOnStartRequestCalled(true);
808   });
809 
810   if (mResponseHead && !mResponseHead->HasContentCharset()) {
811     mResponseHead->SetContentCharset(mContentCharsetHint);
812   }
813 
814   LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
815        mListener.get()));
816 
817   // About to call OnStartRequest, dismiss the guard object.
818   onStartGuard.release();
819 
820   if (mListener) {
821     MOZ_ASSERT(!LoadOnStartRequestCalled(),
822                "We should not call OsStartRequest twice");
823     nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
824     StoreOnStartRequestCalled(true);
825     rv = deleteProtector->OnStartRequest(this);
826     if (NS_FAILED(rv)) return rv;
827   } else {
828     NS_WARNING("OnStartRequest skipped because of null listener");
829     StoreOnStartRequestCalled(true);
830   }
831 
832   if (!mResponseHead) {
833     return NS_OK;
834   }
835 
836   nsAutoCString contentEncoding;
837   rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
838   if (NS_FAILED(rv) || contentEncoding.IsEmpty()) {
839     return NS_OK;
840   }
841 
842   // DoApplyContentConversions can only be called on the main thread.
843   if (NS_IsMainThread()) {
844     nsCOMPtr<nsIStreamListener> listener;
845     rv =
846         DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
847     if (NS_FAILED(rv)) {
848       return rv;
849     }
850 
851     AfterApplyContentConversions(rv, listener);
852     return NS_OK;
853   }
854 
855   Suspend();
856 
857   RefPtr<TRRServiceChannel> self = this;
858   rv = NS_DispatchToMainThread(
859       NS_NewRunnableFunction("TRRServiceChannel::DoApplyContentConversions",
860                              [self]() {
861                                nsCOMPtr<nsIStreamListener> listener;
862                                nsresult rv = self->DoApplyContentConversions(
863                                    self->mListener, getter_AddRefs(listener),
864                                    nullptr);
865                                self->AfterApplyContentConversions(rv, listener);
866                              }),
867       NS_DISPATCH_NORMAL);
868   if (NS_FAILED(rv)) {
869     Resume();
870     return rv;
871   }
872 
873   return NS_OK;
874 }
875 
AfterApplyContentConversions(nsresult aResult,nsIStreamListener * aListener)876 void TRRServiceChannel::AfterApplyContentConversions(
877     nsresult aResult, nsIStreamListener* aListener) {
878   LOG(("TRRServiceChannel::AfterApplyContentConversions [this=%p]", this));
879   if (!mCurrentEventTarget->IsOnCurrentThread()) {
880     RefPtr<TRRServiceChannel> self = this;
881     nsCOMPtr<nsIStreamListener> listener = aListener;
882     self->mCurrentEventTarget->Dispatch(
883         NS_NewRunnableFunction(
884             "TRRServiceChannel::AfterApplyContentConversions",
885             [self, aResult, listener]() {
886               self->Resume();
887               self->AfterApplyContentConversions(aResult, listener);
888             }),
889         NS_DISPATCH_NORMAL);
890     return;
891   }
892 
893   if (mCanceled) {
894     return;
895   }
896 
897   if (NS_FAILED(aResult)) {
898     Unused << AsyncAbort(aResult);
899     return;
900   }
901 
902   if (aListener) {
903     mListener = aListener;
904     mCompressListener = aListener;
905     StoreHasAppliedConversion(true);
906   }
907 }
908 
ProcessAltService()909 void TRRServiceChannel::ProcessAltService() {
910   // e.g. Alt-Svc: h2=":443"; ma=60
911   // e.g. Alt-Svc: h2="otherhost:443"
912   // Alt-Svc       = 1#( alternative *( OWS ";" OWS parameter ) )
913   // alternative   = protocol-id "=" alt-authority
914   // protocol-id   = token ; percent-encoded ALPN protocol identifier
915   // alt-authority = quoted-string ;  containing [ uri-host ] ":" port
916 
917   if (!LoadAllowAltSvc()) {  // per channel opt out
918     return;
919   }
920 
921   if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) {
922     return;
923   }
924 
925   nsCString scheme;
926   mURI->GetScheme(scheme);
927   bool isHttp = scheme.EqualsLiteral("http");
928   if (!isHttp && !scheme.EqualsLiteral("https")) {
929     return;
930   }
931 
932   nsCString altSvc;
933   Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc);
934   if (altSvc.IsEmpty()) {
935     return;
936   }
937 
938   if (!nsHttp::IsReasonableHeaderValue(altSvc)) {
939     LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));
940     return;
941   }
942 
943   nsCString originHost;
944   int32_t originPort = 80;
945   mURI->GetPort(&originPort);
946   if (NS_FAILED(mURI->GetAsciiHost(originHost))) {
947     return;
948   }
949 
950   nsCOMPtr<nsIInterfaceRequestor> callbacks;
951   nsCOMPtr<nsProxyInfo> proxyInfo;
952   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
953                                          getter_AddRefs(callbacks));
954   if (mProxyInfo) {
955     proxyInfo = do_QueryInterface(mProxyInfo);
956   }
957 
958   auto processHeaderTask = [altSvc, scheme, originHost, originPort,
959                             userName(mUsername),
960                             privateBrowsing(mPrivateBrowsing), callbacks,
961                             proxyInfo, caps(mCaps)]() {
962     if (XRE_IsSocketProcess()) {
963       AltServiceChild::ProcessHeader(altSvc, scheme, originHost, originPort,
964                                      userName, privateBrowsing, callbacks,
965                                      proxyInfo, caps & NS_HTTP_DISALLOW_SPDY,
966                                      OriginAttributes());
967       return;
968     }
969 
970     AltSvcMapping::ProcessHeader(
971         altSvc, scheme, originHost, originPort, userName, privateBrowsing,
972         callbacks, proxyInfo, caps & NS_HTTP_DISALLOW_SPDY, OriginAttributes());
973   };
974 
975   if (NS_IsMainThread()) {
976     processHeaderTask();
977     return;
978   }
979 
980   NS_DispatchToMainThread(NS_NewRunnableFunction(
981       "TRRServiceChannel::ProcessAltService", std::move(processHeaderTask)));
982 }
983 
984 NS_IMETHODIMP
OnStartRequest(nsIRequest * request)985 TRRServiceChannel::OnStartRequest(nsIRequest* request) {
986   LOG(("TRRServiceChannel::OnStartRequest [this=%p request=%p status=%" PRIx32
987        "]\n",
988        this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
989 
990   if (!(mCanceled || NS_FAILED(mStatus))) {
991     // capture the request's status, so our consumers will know ASAP of any
992     // connection failures, etc - bug 93581
993     nsresult status;
994     request->GetStatus(&status);
995     mStatus = status;
996   }
997 
998   MOZ_ASSERT(request == mTransactionPump, "Unexpected request");
999 
1000   StoreAfterOnStartRequestBegun(true);
1001   if (mTransaction) {
1002     if (!mSecurityInfo) {
1003       // grab the security info from the connection object; the transaction
1004       // is guaranteed to own a reference to the connection.
1005       mSecurityInfo = mTransaction->SecurityInfo();
1006     }
1007   }
1008 
1009   if (NS_SUCCEEDED(mStatus) && mTransaction) {
1010     // mTransactionPump doesn't hit OnInputStreamReady and call this until
1011     // all of the response headers have been acquired, so we can take
1012     // ownership of them from the transaction.
1013     mResponseHead = mTransaction->TakeResponseHead();
1014     if (mResponseHead) {
1015       uint32_t httpStatus = mResponseHead->Status();
1016       if ((httpStatus < 500) && (httpStatus != 421) && (httpStatus != 407)) {
1017         ProcessAltService();
1018       }
1019 
1020       if (httpStatus == 300 || httpStatus == 301 || httpStatus == 302 ||
1021           httpStatus == 303 || httpStatus == 307 || httpStatus == 308) {
1022         nsresult rv = SyncProcessRedirection(httpStatus);
1023         if (NS_SUCCEEDED(rv)) {
1024           return rv;
1025         }
1026 
1027         mStatus = rv;
1028         DoNotifyListener();
1029         return rv;
1030       }
1031     } else {
1032       NS_WARNING("No response head in OnStartRequest");
1033     }
1034   }
1035 
1036   // avoid crashing if mListener happens to be null...
1037   if (!mListener) {
1038     MOZ_ASSERT_UNREACHABLE("mListener is null");
1039     return NS_OK;
1040   }
1041 
1042   return CallOnStartRequest();
1043 }
1044 
SyncProcessRedirection(uint32_t aHttpStatus)1045 nsresult TRRServiceChannel::SyncProcessRedirection(uint32_t aHttpStatus) {
1046   nsAutoCString location;
1047 
1048   // if a location header was not given, then we can't perform the redirect,
1049   // so just carry on as though this were a normal response.
1050   if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location))) {
1051     return NS_ERROR_FAILURE;
1052   }
1053 
1054   // make sure non-ASCII characters in the location header are escaped.
1055   nsAutoCString locationBuf;
1056   if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
1057                    locationBuf)) {
1058     location = locationBuf;
1059   }
1060 
1061   LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(),
1062        uint32_t(mRedirectionLimit)));
1063 
1064   nsCOMPtr<nsIURI> redirectURI;
1065   nsresult rv = NS_NewURI(getter_AddRefs(redirectURI), location);
1066 
1067   if (NS_FAILED(rv)) {
1068     LOG(("Invalid URI for redirect: Location: %s\n", location.get()));
1069     return NS_ERROR_CORRUPTED_CONTENT;
1070   }
1071 
1072   // move the reference of the old location to the new one if the new
1073   // one has none.
1074   PropagateReferenceIfNeeded(mURI, redirectURI);
1075 
1076   bool rewriteToGET =
1077       ShouldRewriteRedirectToGET(aHttpStatus, mRequestHead.ParsedMethod());
1078 
1079   // Let's not rewrite the method to GET for TRR requests.
1080   if (rewriteToGET) {
1081     return NS_ERROR_FAILURE;
1082   }
1083 
1084   // If the method is not safe (such as POST, PUT, DELETE, ...)
1085   if (!mRequestHead.IsSafeMethod()) {
1086     LOG(("TRRServiceChannel: unsafe redirect to:%s\n", location.get()));
1087   }
1088 
1089   uint32_t redirectFlags;
1090   if (nsHttp::IsPermanentRedirect(aHttpStatus)) {
1091     redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
1092   } else {
1093     redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
1094   }
1095 
1096   nsCOMPtr<nsIChannel> newChannel;
1097   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
1098       static_cast<TRRLoadInfo*>(mLoadInfo.get())->Clone();
1099   rv = gHttpHandler->CreateTRRServiceChannel(redirectURI, nullptr, 0, nullptr,
1100                                              redirectLoadInfo,
1101                                              getter_AddRefs(newChannel));
1102   if (NS_FAILED(rv)) {
1103     return rv;
1104   }
1105 
1106   rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET,
1107                                redirectFlags);
1108   if (NS_FAILED(rv)) {
1109     return rv;
1110   }
1111 
1112   // Make sure to do this after we received redirect veto answer,
1113   // i.e. after all sinks had been notified
1114   newChannel->SetOriginalURI(mOriginalURI);
1115 
1116   rv = newChannel->AsyncOpen(mListener);
1117   LOG(("  new channel AsyncOpen returned %" PRIX32, static_cast<uint32_t>(rv)));
1118 
1119   // close down this channel
1120   Cancel(NS_BINDING_REDIRECTED);
1121 
1122   ReleaseListeners();
1123 
1124   return NS_OK;
1125 }
1126 
SetupReplacementChannel(nsIURI * aNewURI,nsIChannel * aNewChannel,bool aPreserveMethod,uint32_t aRedirectFlags)1127 nsresult TRRServiceChannel::SetupReplacementChannel(nsIURI* aNewURI,
1128                                                     nsIChannel* aNewChannel,
1129                                                     bool aPreserveMethod,
1130                                                     uint32_t aRedirectFlags) {
1131   LOG(
1132       ("TRRServiceChannel::SetupReplacementChannel "
1133        "[this=%p newChannel=%p preserveMethod=%d]",
1134        this, aNewChannel, aPreserveMethod));
1135 
1136   nsresult rv = HttpBaseChannel::SetupReplacementChannel(
1137       aNewURI, aNewChannel, aPreserveMethod, aRedirectFlags);
1138   if (NS_FAILED(rv)) {
1139     return rv;
1140   }
1141 
1142   rv = CheckRedirectLimit(aRedirectFlags);
1143   NS_ENSURE_SUCCESS(rv, rv);
1144 
1145   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
1146   if (!httpChannel) {
1147     MOZ_ASSERT(false);
1148     return NS_ERROR_FAILURE;
1149   }
1150 
1151   // convey the ApplyConversion flag (bug 91862)
1152   nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
1153   if (encodedChannel) {
1154     encodedChannel->SetApplyConversion(LoadApplyConversion());
1155   }
1156 
1157   // mContentTypeHint is empty when this channel is used to download
1158   // ODoHConfigs.
1159   if (mContentTypeHint.IsEmpty()) {
1160     return NS_OK;
1161   }
1162 
1163   // Make sure we set content-type on the old channel properly.
1164   MOZ_ASSERT(mContentTypeHint.Equals("application/dns-message") ||
1165              mContentTypeHint.Equals("application/oblivious-dns-message"));
1166 
1167   // Apply TRR specific settings. Note that we already know mContentTypeHint is
1168   // "application/dns-message" or "application/oblivious-dns-message" here.
1169   return TRR::SetupTRRServiceChannelInternal(
1170       httpChannel,
1171       mRequestHead.ParsedMethod() == nsHttpRequestHead::kMethod_Get,
1172       mContentTypeHint);
1173 }
1174 
1175 NS_IMETHODIMP
OnDataAvailable(nsIRequest * request,nsIInputStream * input,uint64_t offset,uint32_t count)1176 TRRServiceChannel::OnDataAvailable(nsIRequest* request, nsIInputStream* input,
1177                                    uint64_t offset, uint32_t count) {
1178   LOG(("TRRServiceChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64
1179        " count=%" PRIu32 "]\n",
1180        this, request, offset, count));
1181 
1182   // don't send out OnDataAvailable notifications if we've been canceled.
1183   if (mCanceled) return mStatus;
1184 
1185   MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
1186 
1187   if (mListener) {
1188     return mListener->OnDataAvailable(this, input, offset, count);
1189   }
1190 
1191   return NS_ERROR_ABORT;
1192 }
1193 
1194 NS_IMETHODIMP
OnStopRequest(nsIRequest * request,nsresult status)1195 TRRServiceChannel::OnStopRequest(nsIRequest* request, nsresult status) {
1196   LOG(("TRRServiceChannel::OnStopRequest [this=%p request=%p status=%" PRIx32
1197        "]\n",
1198        this, request, static_cast<uint32_t>(status)));
1199 
1200   if (mCanceled || NS_FAILED(mStatus)) status = mStatus;
1201 
1202   mTransactionTimings = mTransaction->Timings();
1203   mTransaction = nullptr;
1204   mTransactionPump = nullptr;
1205 
1206   if (mListener) {
1207     LOG(("TRRServiceChannel %p calling OnStopRequest\n", this));
1208     MOZ_ASSERT(LoadOnStartRequestCalled(),
1209                "OnStartRequest should be called before OnStopRequest");
1210     MOZ_ASSERT(!LoadOnStopRequestCalled(),
1211                "We should not call OnStopRequest twice");
1212     StoreOnStopRequestCalled(true);
1213     mListener->OnStopRequest(this, status);
1214   }
1215   StoreOnStopRequestCalled(true);
1216 
1217   mDNSPrefetch = nullptr;
1218 
1219   if (mLoadGroup) {
1220     mLoadGroup->RemoveRequest(this, nullptr, status);
1221   }
1222 
1223   ReleaseListeners();
1224   return NS_OK;
1225 }
1226 
1227 NS_IMETHODIMP
OnLookupComplete(nsICancelable * request,nsIDNSRecord * rec,nsresult status)1228 TRRServiceChannel::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
1229                                     nsresult status) {
1230   LOG(
1231       ("TRRServiceChannel::OnLookupComplete [this=%p] prefetch complete%s: "
1232        "%s status[0x%" PRIx32 "]\n",
1233        this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
1234        NS_SUCCEEDED(status) ? "success" : "failure",
1235        static_cast<uint32_t>(status)));
1236 
1237   // We no longer need the dns prefetch object. Note: mDNSPrefetch could be
1238   // validly null if OnStopRequest has already been called.
1239   // We only need the domainLookup timestamps when not loading from cache
1240   if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
1241     TimeStamp connectStart = mTransaction->GetConnectStart();
1242     TimeStamp requestStart = mTransaction->GetRequestStart();
1243     // We only set the domainLookup timestamps if we're not using a
1244     // persistent connection.
1245     if (requestStart.IsNull() && connectStart.IsNull()) {
1246       mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
1247       mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
1248     }
1249   }
1250   mDNSPrefetch = nullptr;
1251 
1252   // Unset DNS cache refresh if it was requested,
1253   if (mCaps & NS_HTTP_REFRESH_DNS) {
1254     mCaps &= ~NS_HTTP_REFRESH_DNS;
1255     if (mTransaction) {
1256       mTransaction->SetDNSWasRefreshed();
1257     }
1258   }
1259 
1260   return NS_OK;
1261 }
1262 
1263 NS_IMETHODIMP
LogBlockedCORSRequest(const nsAString & aMessage,const nsACString & aCategory)1264 TRRServiceChannel::LogBlockedCORSRequest(const nsAString& aMessage,
1265                                          const nsACString& aCategory) {
1266   return NS_ERROR_NOT_IMPLEMENTED;
1267 }
1268 
1269 NS_IMETHODIMP
LogMimeTypeMismatch(const nsACString & aMessageName,bool aWarning,const nsAString & aURL,const nsAString & aContentType)1270 TRRServiceChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
1271                                        bool aWarning, const nsAString& aURL,
1272                                        const nsAString& aContentType) {
1273   return NS_ERROR_NOT_IMPLEMENTED;
1274 }
1275 
1276 NS_IMETHODIMP
GetIsAuthChannel(bool * aIsAuthChannel)1277 TRRServiceChannel::GetIsAuthChannel(bool* aIsAuthChannel) {
1278   return NS_ERROR_NOT_IMPLEMENTED;
1279 }
1280 
1281 NS_IMETHODIMP
SetNotificationCallbacks(nsIInterfaceRequestor * aCallbacks)1282 TRRServiceChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
1283   mCallbacks = aCallbacks;
1284   return NS_OK;
1285 }
1286 
1287 NS_IMETHODIMP
SetPriority(int32_t value)1288 TRRServiceChannel::SetPriority(int32_t value) {
1289   return NS_ERROR_NOT_IMPLEMENTED;
1290 }
1291 
OnClassOfServiceUpdated()1292 void TRRServiceChannel::OnClassOfServiceUpdated() {
1293   LOG(("TRRServiceChannel::OnClassOfServiceUpdated this=%p, cos=%u", this,
1294        mClassOfService));
1295 
1296   if (mTransaction) {
1297     gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction,
1298                                                     mClassOfService);
1299   }
1300 }
1301 
1302 NS_IMETHODIMP
SetClassFlags(uint32_t inFlags)1303 TRRServiceChannel::SetClassFlags(uint32_t inFlags) {
1304   uint32_t previous = mClassOfService;
1305   mClassOfService = inFlags;
1306   if (previous != mClassOfService) {
1307     OnClassOfServiceUpdated();
1308   }
1309   return NS_OK;
1310 }
1311 
1312 NS_IMETHODIMP
AddClassFlags(uint32_t inFlags)1313 TRRServiceChannel::AddClassFlags(uint32_t inFlags) {
1314   uint32_t previous = mClassOfService;
1315   mClassOfService |= inFlags;
1316   if (previous != mClassOfService) {
1317     OnClassOfServiceUpdated();
1318   }
1319   return NS_OK;
1320 }
1321 
1322 NS_IMETHODIMP
ClearClassFlags(uint32_t inFlags)1323 TRRServiceChannel::ClearClassFlags(uint32_t inFlags) {
1324   uint32_t previous = mClassOfService;
1325   mClassOfService &= ~inFlags;
1326   if (previous != mClassOfService) {
1327     OnClassOfServiceUpdated();
1328   }
1329   return NS_OK;
1330 }
1331 
1332 NS_IMETHODIMP
ResumeAt(uint64_t aStartPos,const nsACString & aEntityID)1333 TRRServiceChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) {
1334   return NS_ERROR_NOT_IMPLEMENTED;
1335 }
1336 
DoAsyncAbort(nsresult aStatus)1337 void TRRServiceChannel::DoAsyncAbort(nsresult aStatus) {
1338   Unused << AsyncAbort(aStatus);
1339 }
1340 
1341 NS_IMETHODIMP
GetProxyInfo(nsIProxyInfo ** result)1342 TRRServiceChannel::GetProxyInfo(nsIProxyInfo** result) {
1343   if (!mConnectionInfo) {
1344     *result = mProxyInfo;
1345   } else {
1346     *result = mConnectionInfo->ProxyInfo();
1347   }
1348   NS_IF_ADDREF(*result);
1349   return NS_OK;
1350 }
1351 
GetHttpProxyConnectResponseCode(int32_t * aResponseCode)1352 NS_IMETHODIMP TRRServiceChannel::GetHttpProxyConnectResponseCode(
1353     int32_t* aResponseCode) {
1354   NS_ENSURE_ARG_POINTER(aResponseCode);
1355 
1356   *aResponseCode = -1;
1357   return NS_OK;
1358 }
1359 
1360 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * aLoadFlags)1361 TRRServiceChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
1362   return HttpBaseChannel::GetLoadFlags(aLoadFlags);
1363 }
1364 
1365 NS_IMETHODIMP
SetLoadFlags(nsLoadFlags aLoadFlags)1366 TRRServiceChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
1367   if (aLoadFlags & (nsICachingChannel::LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE |
1368                     nsICachingChannel::LOAD_NO_NETWORK_IO)) {
1369     MOZ_ASSERT(false, "Wrong load flags!");
1370     return NS_ERROR_FAILURE;
1371   }
1372 
1373   return HttpBaseChannel::SetLoadFlags(aLoadFlags);
1374 }
1375 
1376 NS_IMETHODIMP
GetURI(nsIURI ** aURI)1377 TRRServiceChannel::GetURI(nsIURI** aURI) {
1378   return HttpBaseChannel::GetURI(aURI);
1379 }
1380 
1381 NS_IMETHODIMP
GetNotificationCallbacks(nsIInterfaceRequestor ** aCallbacks)1382 TRRServiceChannel::GetNotificationCallbacks(
1383     nsIInterfaceRequestor** aCallbacks) {
1384   return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
1385 }
1386 
1387 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** aLoadGroup)1388 TRRServiceChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
1389   return HttpBaseChannel::GetLoadGroup(aLoadGroup);
1390 }
1391 
1392 NS_IMETHODIMP
GetRequestMethod(nsACString & aMethod)1393 TRRServiceChannel::GetRequestMethod(nsACString& aMethod) {
1394   return HttpBaseChannel::GetRequestMethod(aMethod);
1395 }
1396 
DoNotifyListener()1397 void TRRServiceChannel::DoNotifyListener() {
1398   LOG(("TRRServiceChannel::DoNotifyListener this=%p", this));
1399 
1400   // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
1401   // LOAD_ONLY_IF_MODIFIED) we want to set AfterOnStartRequestBegun to true
1402   // before notifying listener.
1403   if (!LoadAfterOnStartRequestBegun()) {
1404     StoreAfterOnStartRequestBegun(true);
1405   }
1406 
1407   if (mListener && !LoadOnStartRequestCalled()) {
1408     nsCOMPtr<nsIStreamListener> listener = mListener;
1409     StoreOnStartRequestCalled(true);
1410     listener->OnStartRequest(this);
1411   }
1412   StoreOnStartRequestCalled(true);
1413 
1414   // Make sure IsPending is set to false. At this moment we are done from
1415   // the point of view of our consumer and we have to report our self
1416   // as not-pending.
1417   StoreIsPending(false);
1418 
1419   if (mListener && !LoadOnStopRequestCalled()) {
1420     nsCOMPtr<nsIStreamListener> listener = mListener;
1421     StoreOnStopRequestCalled(true);
1422     listener->OnStopRequest(this, mStatus);
1423   }
1424   StoreOnStopRequestCalled(true);
1425 
1426   // We have to make sure to drop the references to listeners and callbacks
1427   // no longer needed.
1428   ReleaseListeners();
1429 
1430   DoNotifyListenerCleanup();
1431 }
1432 
DoNotifyListenerCleanup()1433 void TRRServiceChannel::DoNotifyListenerCleanup() {}
1434 
1435 NS_IMETHODIMP
GetDomainLookupStart(TimeStamp * _retval)1436 TRRServiceChannel::GetDomainLookupStart(TimeStamp* _retval) {
1437   if (mTransaction) {
1438     *_retval = mTransaction->GetDomainLookupStart();
1439   } else {
1440     *_retval = mTransactionTimings.domainLookupStart;
1441   }
1442   return NS_OK;
1443 }
1444 
1445 NS_IMETHODIMP
GetDomainLookupEnd(TimeStamp * _retval)1446 TRRServiceChannel::GetDomainLookupEnd(TimeStamp* _retval) {
1447   if (mTransaction) {
1448     *_retval = mTransaction->GetDomainLookupEnd();
1449   } else {
1450     *_retval = mTransactionTimings.domainLookupEnd;
1451   }
1452   return NS_OK;
1453 }
1454 
1455 NS_IMETHODIMP
GetConnectStart(TimeStamp * _retval)1456 TRRServiceChannel::GetConnectStart(TimeStamp* _retval) {
1457   if (mTransaction) {
1458     *_retval = mTransaction->GetConnectStart();
1459   } else {
1460     *_retval = mTransactionTimings.connectStart;
1461   }
1462   return NS_OK;
1463 }
1464 
1465 NS_IMETHODIMP
GetTcpConnectEnd(TimeStamp * _retval)1466 TRRServiceChannel::GetTcpConnectEnd(TimeStamp* _retval) {
1467   if (mTransaction) {
1468     *_retval = mTransaction->GetTcpConnectEnd();
1469   } else {
1470     *_retval = mTransactionTimings.tcpConnectEnd;
1471   }
1472   return NS_OK;
1473 }
1474 
1475 NS_IMETHODIMP
GetSecureConnectionStart(TimeStamp * _retval)1476 TRRServiceChannel::GetSecureConnectionStart(TimeStamp* _retval) {
1477   if (mTransaction) {
1478     *_retval = mTransaction->GetSecureConnectionStart();
1479   } else {
1480     *_retval = mTransactionTimings.secureConnectionStart;
1481   }
1482   return NS_OK;
1483 }
1484 
1485 NS_IMETHODIMP
GetConnectEnd(TimeStamp * _retval)1486 TRRServiceChannel::GetConnectEnd(TimeStamp* _retval) {
1487   if (mTransaction) {
1488     *_retval = mTransaction->GetConnectEnd();
1489   } else {
1490     *_retval = mTransactionTimings.connectEnd;
1491   }
1492   return NS_OK;
1493 }
1494 
1495 NS_IMETHODIMP
GetRequestStart(TimeStamp * _retval)1496 TRRServiceChannel::GetRequestStart(TimeStamp* _retval) {
1497   if (mTransaction) {
1498     *_retval = mTransaction->GetRequestStart();
1499   } else {
1500     *_retval = mTransactionTimings.requestStart;
1501   }
1502   return NS_OK;
1503 }
1504 
1505 NS_IMETHODIMP
GetResponseStart(TimeStamp * _retval)1506 TRRServiceChannel::GetResponseStart(TimeStamp* _retval) {
1507   if (mTransaction) {
1508     *_retval = mTransaction->GetResponseStart();
1509   } else {
1510     *_retval = mTransactionTimings.responseStart;
1511   }
1512   return NS_OK;
1513 }
1514 
1515 NS_IMETHODIMP
GetResponseEnd(TimeStamp * _retval)1516 TRRServiceChannel::GetResponseEnd(TimeStamp* _retval) {
1517   if (mTransaction) {
1518     *_retval = mTransaction->GetResponseEnd();
1519   } else {
1520     *_retval = mTransactionTimings.responseEnd;
1521   }
1522   return NS_OK;
1523 }
1524 
SetLoadGroup(nsILoadGroup * aLoadGroup)1525 NS_IMETHODIMP TRRServiceChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
1526   return NS_OK;
1527 }
1528 
1529 NS_IMETHODIMP
TimingAllowCheck(nsIPrincipal * aOrigin,bool * aResult)1530 TRRServiceChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* aResult) {
1531   NS_ENSURE_ARG_POINTER(aResult);
1532   *aResult = true;
1533   return NS_OK;
1534 }
1535 
SameOriginWithOriginalUri(nsIURI * aURI)1536 bool TRRServiceChannel::SameOriginWithOriginalUri(nsIURI* aURI) { return true; }
1537 
1538 }  // namespace net
1539 }  // namespace mozilla
1540