1853072
[gecko.git] / 
blob1853072e9983db1c680885792e33b8d185c17ec0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */
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/. */
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
10 #include <inttypes.h>
12 #include "mozilla/dom/nsCSPContext.h"
13 #include "mozilla/ScopeExit.h"
14 #include "mozilla/Sprintf.h"
16 #include "nsHttp.h"
17 #include "nsHttpChannel.h"
18 #include "nsHttpChannelAuthProvider.h"
19 #include "nsHttpHandler.h"
20 #include "nsIApplicationCacheService.h"
21 #include "nsIApplicationCacheContainer.h"
22 #include "nsICacheStorageService.h"
23 #include "nsICacheStorage.h"
24 #include "nsICacheEntry.h"
25 #include "nsICaptivePortalService.h"
26 #include "nsICookieService.h"
27 #include "nsICryptoHash.h"
28 #include "nsINetworkInterceptController.h"
29 #include "nsINSSErrorsService.h"
30 #include "nsISecurityReporter.h"
31 #include "nsIStringBundle.h"
32 #include "nsIStreamListenerTee.h"
33 #include "nsISeekableStream.h"
34 #include "nsILoadGroupChild.h"
35 #include "nsIProtocolProxyService2.h"
36 #include "nsIURIClassifier.h"
37 #include "nsMimeTypes.h"
38 #include "nsNetCID.h"
39 #include "nsNetUtil.h"
40 #include "nsIURL.h"
41 #include "nsIURIMutator.h"
42 #include "nsIStreamTransportService.h"
43 #include "prnetdb.h"
44 #include "nsEscape.h"
45 #include "nsStreamUtils.h"
46 #include "nsIOService.h"
47 #include "nsDNSPrefetch.h"
48 #include "nsChannelClassifier.h"
49 #include "nsIRedirectResultListener.h"
50 #include "mozIThirdPartyUtil.h"
51 #include "mozilla/dom/ContentVerifier.h"
52 #include "mozilla/TimeStamp.h"
53 #include "nsError.h"
54 #include "nsPrintfCString.h"
55 #include "nsAlgorithm.h"
56 #include "nsQueryObject.h"
57 #include "nsThreadUtils.h"
58 #include "GeckoProfiler.h"
59 #include "nsIConsoleService.h"
60 #include "mozilla/AntiTrackingCommon.h"
61 #include "mozilla/Attributes.h"
62 #include "mozilla/DebugOnly.h"
63 #include "mozilla/Preferences.h"
64 #include "mozilla/Services.h"
65 #include "nsISSLSocketControl.h"
66 #include "sslt.h"
67 #include "nsContentUtils.h"
68 #include "nsContentSecurityManager.h"
69 #include "nsIClassOfService.h"
70 #include "nsIPermissionManager.h"
71 #include "nsIPrincipal.h"
72 #include "nsIScriptError.h"
73 #include "nsIScriptSecurityManager.h"
74 #include "nsITransportSecurityInfo.h"
75 #include "nsIWebProgressListener.h"
76 #include "LoadContextInfo.h"
77 #include "netCore.h"
78 #include "nsHttpTransaction.h"
79 #include "nsICacheEntryDescriptor.h"
80 #include "nsICancelable.h"
81 #include "nsIHttpChannelAuthProvider.h"
82 #include "nsIHttpChannelInternal.h"
83 #include "nsIPrompt.h"
84 #include "nsInputStreamPump.h"
85 #include "nsIURIFixup.h"
86 #include "nsURLHelper.h"
87 #include "nsISocketTransport.h"
88 #include "nsIStreamConverterService.h"
89 #include "nsISiteSecurityService.h"
90 #include "nsString.h"
91 #include "nsCRT.h"
92 #include "CacheObserver.h"
93 #include "mozilla/dom/PerformanceStorage.h"
94 #include "mozilla/Telemetry.h"
95 #include "AlternateServices.h"
96 #include "InterceptedChannel.h"
97 #include "nsIHttpPushListener.h"
98 #include "nsIX509Cert.h"
99 #include "ScopedNSSTypes.h"
100 #include "nsIDeprecationWarner.h"
101 #include "mozilla/dom/Document.h"
102 #include "nsICompressConvStats.h"
103 #include "nsCORSListenerProxy.h"
104 #include "nsISocketProvider.h"
105 #include "mozilla/extensions/StreamFilterParent.h"
106 #include "mozilla/net/Predictor.h"
107 #include "mozilla/MathAlgorithms.h"
108 #include "mozilla/NullPrincipal.h"
109 #include "CacheControlParser.h"
110 #include "nsMixedContentBlocker.h"
111 #include "CacheStorageService.h"
112 #include "HttpChannelParent.h"
113 #include "HttpChannelParentListener.h"
114 #include "InterceptedHttpChannel.h"
115 #include "nsIBufferedStreams.h"
116 #include "nsIFileStreams.h"
117 #include "nsIMIMEInputStream.h"
118 #include "nsIMultiplexInputStream.h"
119 #include "../../cache2/CacheFileUtils.h"
120 #include "nsINetworkLinkService.h"
121 #include "mozilla/dom/PromiseNativeHandler.h"
122 #include "mozilla/dom/Promise.h"
123 #include "mozilla/dom/ServiceWorkerUtils.h"
124 #include "mozilla/net/AsyncUrlChannelClassifier.h"
125 #include "mozilla/net/UrlClassifierFeatureFactory.h"
127 #ifdef MOZ_TASK_TRACER
128 #  include "GeckoTaskTracer.h"
129 #endif
131 #ifdef MOZ_GECKO_PROFILER
132 #  include "ProfilerMarkerPayload.h"
133 #endif
135 namespace mozilla {
137 using namespace dom;
139 namespace net {
141 namespace {
143 static bool sRCWNEnabled = false;
144 static uint32_t sRCWNQueueSizeNormal = 50;
145 static uint32_t sRCWNQueueSizePriority = 10;
146 static uint32_t sRCWNSmallResourceSizeKB = 256;
147 static uint32_t sRCWNMinWaitMs = 0;
148 static uint32_t sRCWNMaxWaitMs = 500;
150 // True if the local cache should be bypassed when processing a request.
151 #define BYPASS_LOCAL_CACHE(loadFlags)           \
152   (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
153                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
155 #define RECOVER_FROM_CACHE_FILE_ERROR(result) \
156   ((result) == NS_ERROR_FILE_NOT_FOUND ||     \
157    (result) == NS_ERROR_FILE_CORRUPTED || (result) == NS_ERROR_OUT_OF_MEMORY)
159 #define WRONG_RACING_RESPONSE_SOURCE(req)                                     \
160   (mRaceCacheWithNetwork &&                                                   \
161    (((mFirstResponseSource == RESPONSE_FROM_CACHE) && (req != mCachePump)) || \
162     ((mFirstResponseSource == RESPONSE_FROM_NETWORK) &&                       \
163      (req != mTransactionPump))))
165 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
167 void AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss) {
168   Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2, hitOrMiss);
171 // Computes and returns a SHA1 hash of the input buffer. The input buffer
172 // must be a null-terminated string.
173 nsresult Hash(const char *buf, nsACString &hash) {
174   nsresult rv;
176   nsCOMPtr<nsICryptoHash> hasher =
177       do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
178   NS_ENSURE_SUCCESS(rv, rv);
180   rv = hasher->Init(nsICryptoHash::SHA1);
181   NS_ENSURE_SUCCESS(rv, rv);
183   rv =
184       hasher->Update(reinterpret_cast<unsigned const char *>(buf), strlen(buf));
185   NS_ENSURE_SUCCESS(rv, rv);
187   rv = hasher->Finish(true, hash);
188   NS_ENSURE_SUCCESS(rv, rv);
190   return NS_OK;
193 bool IsInSubpathOfAppCacheManifest(nsIApplicationCache *cache,
194                                    nsACString const &uriSpec) {
195   MOZ_ASSERT(cache);
197   nsresult rv;
199   nsCOMPtr<nsIURI> uri;
200   rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
201   if (NS_FAILED(rv)) {
202     return false;
203   }
205   nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv));
206   if (NS_FAILED(rv)) {
207     return false;
208   }
210   nsAutoCString directory;
211   rv = url->GetDirectory(directory);
212   if (NS_FAILED(rv)) {
213     return false;
214   }
216   nsCOMPtr<nsIURI> manifestURI;
217   rv = cache->GetManifestURI(getter_AddRefs(manifestURI));
218   if (NS_FAILED(rv)) {
219     return false;
220   }
222   nsCOMPtr<nsIURL> manifestURL(do_QueryInterface(manifestURI, &rv));
223   if (NS_FAILED(rv)) {
224     return false;
225   }
227   nsAutoCString manifestDirectory;
228   rv = manifestURL->GetDirectory(manifestDirectory);
229   if (NS_FAILED(rv)) {
230     return false;
231   }
233   return StringBeginsWith(directory, manifestDirectory);
236 }  // unnamed namespace
238 // We only treat 3xx responses as redirects if they have a Location header and
239 // the status code is in a whitelist.
240 bool nsHttpChannel::WillRedirect(nsHttpResponseHead *response) {
241   return IsRedirectStatus(response->Status()) &&
242          response->HasHeader(nsHttp::Location);
245 nsresult StoreAuthorizationMetaData(nsICacheEntry *entry,
246                                     nsHttpRequestHead *requestHead);
248 class AutoRedirectVetoNotifier {
249  public:
250   explicit AutoRedirectVetoNotifier(nsHttpChannel *channel)
251       : mChannel(channel) {
252     if (mChannel->mHasAutoRedirectVetoNotifier) {
253       MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");
254       mChannel = nullptr;
255       return;
256     }
258     mChannel->mHasAutoRedirectVetoNotifier = true;
259   }
260   ~AutoRedirectVetoNotifier() { ReportRedirectResult(false); }
261   void RedirectSucceeded() { ReportRedirectResult(true); }
263  private:
264   nsHttpChannel *mChannel;
265   void ReportRedirectResult(bool succeeded);
268 void AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded) {
269   if (!mChannel) return;
271   mChannel->mRedirectChannel = nullptr;
273   if (succeeded) {
274     mChannel->RemoveAsNonTailRequest();
275   }
277   nsCOMPtr<nsIRedirectResultListener> vetoHook;
278   NS_QueryNotificationCallbacks(mChannel, NS_GET_IID(nsIRedirectResultListener),
279                                 getter_AddRefs(vetoHook));
281   nsHttpChannel *channel = mChannel;
282   mChannel = nullptr;
284   if (vetoHook) vetoHook->OnRedirectResult(succeeded);
286   // Drop after the notification
287   channel->mHasAutoRedirectVetoNotifier = false;
290 //-----------------------------------------------------------------------------
291 // nsHttpChannel <public>
292 //-----------------------------------------------------------------------------
294 nsHttpChannel::nsHttpChannel()
295     : HttpAsyncAborter<nsHttpChannel>(this),
296       mCacheDisposition(kCacheUnresolved),
297       mLogicalOffset(0),
298       mPostID(0),
299       mRequestTime(0),
300       mOfflineCacheLastModifiedTime(0),
301       mSuspendTotalTime(0),
302       mRedirectType(0),
303       mCacheOpenWithPriority(false),
304       mCacheQueueSizeWhenOpen(0),
305       mCachedContentIsValid(false),
306       mCachedContentIsPartial(false),
307       mCacheOnlyMetadata(false),
308       mTransactionReplaced(false),
309       mAuthRetryPending(false),
310       mProxyAuthPending(false),
311       mCustomAuthHeader(false),
312       mResuming(false),
313       mInitedCacheEntry(false),
314       mFallbackChannel(false),
315       mCustomConditionalRequest(false),
316       mFallingBack(false),
317       mWaitingForRedirectCallback(false),
318       mRequestTimeInitialized(false),
319       mCacheEntryIsReadOnly(false),
320       mCacheEntryIsWriteOnly(false),
321       mCacheEntriesToWaitFor(0),
322       mConcurrentCacheAccess(0),
323       mIsPartialRequest(0),
324       mHasAutoRedirectVetoNotifier(0),
325       mPinCacheContent(0),
326       mIsCorsPreflightDone(0),
327       mStronglyFramed(false),
328       mUsedNetwork(0),
329       mAuthConnectionRestartable(0),
330       mChannelClassifierCancellationPending(0),
331       mAsyncResumePending(0),
332       mPushedStream(nullptr),
333       mLocalBlocklist(false),
334       mOnTailUnblock(nullptr),
335       mWarningReporter(nullptr),
336       mIsReadingFromCache(false),
337       mFirstResponseSource(RESPONSE_PENDING),
338       mRaceCacheWithNetwork(false),
339       mRaceDelay(0),
340       mIgnoreCacheEntry(false),
341       mRCWNLock("nsHttpChannel.mRCWNLock"),
342       mDidReval(false) {
343   LOG(("Creating nsHttpChannel [this=%p]\n", this));
344   mChannelCreationTime = PR_Now();
345   mChannelCreationTimestamp = TimeStamp::Now();
348 nsHttpChannel::~nsHttpChannel() {
349   LOG(("Destroying nsHttpChannel [this=%p]\n", this));
351   if (mAuthProvider) {
352     DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
353     MOZ_ASSERT(NS_SUCCEEDED(rv));
354   }
356   ReleaseMainThreadOnlyReferences();
359 void nsHttpChannel::ReleaseMainThreadOnlyReferences() {
360   if (NS_IsMainThread()) {
361     // Already on main thread, let dtor to
362     // take care of releasing references
363     return;
364   }
366   nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
367   arrayToRelease.AppendElement(mApplicationCacheForWrite.forget());
368   arrayToRelease.AppendElement(mAuthProvider.forget());
369   arrayToRelease.AppendElement(mRedirectURI.forget());
370   arrayToRelease.AppendElement(mRedirectChannel.forget());
371   arrayToRelease.AppendElement(mPreflightChannel.forget());
373   NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
376 nsresult nsHttpChannel::Init(nsIURI *uri, uint32_t caps, nsProxyInfo *proxyInfo,
377                              uint32_t proxyResolveFlags, nsIURI *proxyURI,
378                              uint64_t channelId,
379                              nsContentPolicyType aContentPolicyType) {
380   nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, proxyResolveFlags,
381                                       proxyURI, channelId, aContentPolicyType);
382   if (NS_FAILED(rv)) return rv;
384   LOG1(("nsHttpChannel::Init [this=%p]\n", this));
386   return rv;
389 nsresult nsHttpChannel::AddSecurityMessage(const nsAString &aMessageTag,
390                                            const nsAString &aMessageCategory) {
391   if (mWarningReporter) {
392     return mWarningReporter->ReportSecurityMessage(aMessageTag,
393                                                    aMessageCategory);
394   }
395   return HttpBaseChannel::AddSecurityMessage(aMessageTag, aMessageCategory);
398 NS_IMETHODIMP
399 nsHttpChannel::LogBlockedCORSRequest(const nsAString &aMessage,
400                                      const nsACString &aCategory) {
401   if (mWarningReporter) {
402     return mWarningReporter->LogBlockedCORSRequest(aMessage, aCategory);
403   }
404   return NS_ERROR_UNEXPECTED;
407 NS_IMETHODIMP
408 nsHttpChannel::LogMimeTypeMismatch(const nsACString &aMessageName,
409                                    bool aWarning, const nsAString &aURL,
410                                    const nsAString &aContentType) {
411   if (mWarningReporter) {
412     return mWarningReporter->LogMimeTypeMismatch(aMessageName, aWarning, aURL,
413                                                  aContentType);
414   }
415   return NS_ERROR_UNEXPECTED;
418 //-----------------------------------------------------------------------------
419 // nsHttpChannel <private>
420 //-----------------------------------------------------------------------------
422 nsresult nsHttpChannel::PrepareToConnect() {
423   LOG(("nsHttpChannel::PrepareToConnect [this=%p]\n", this));
425   AddCookiesToRequest();
427   // notify "http-on-modify-request" observers
428   CallOnModifyRequestObservers();
430   SetLoadGroupUserAgentOverride();
432   // Check if request was cancelled during on-modify-request or on-useragent.
433   if (mCanceled) {
434     return mStatus;
435   }
437   if (mSuspendCount) {
438     // We abandon the connection here if there was one.
439     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
440     MOZ_ASSERT(!mCallOnResume);
441     mCallOnResume = [](nsHttpChannel *self) {
442       self->HandleOnBeforeConnect();
443       return NS_OK;
444     };
445     return NS_OK;
446   }
448   return OnBeforeConnect();
451 void nsHttpChannel::HandleContinueCancellingByChannelClassifier(
452     nsresult aErrorCode) {
453   MOZ_ASSERT(
454       UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
455   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
457   if (mSuspendCount) {
458     LOG(
459         ("Waiting until resume HandleContinueCancellingByChannelClassifier "
460          "[this=%p]\n",
461          this));
462     mCallOnResume = [aErrorCode](nsHttpChannel *self) {
463       self->HandleContinueCancellingByChannelClassifier(aErrorCode);
464       return NS_OK;
465     };
466     return;
467   }
469   LOG(("nsHttpChannel::HandleContinueCancellingByChannelClassifier [this=%p]\n",
470        this));
471   ContinueCancellingByChannelClassifier(aErrorCode);
474 void nsHttpChannel::HandleOnBeforeConnect() {
475   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
476   nsresult rv;
478   if (mSuspendCount) {
479     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
480     mCallOnResume = [](nsHttpChannel *self) {
481       self->HandleOnBeforeConnect();
482       return NS_OK;
483     };
484     return;
485   }
487   LOG(("nsHttpChannel::HandleOnBeforeConnect [this=%p]\n", this));
488   rv = OnBeforeConnect();
489   if (NS_FAILED(rv)) {
490     CloseCacheEntry(false);
491     Unused << AsyncAbort(rv);
492   }
495 nsresult nsHttpChannel::OnBeforeConnect() {
496   nsresult rv;
498   // Check if request was cancelled during suspend AFTER on-modify-request or
499   // on-useragent.
500   if (mCanceled) {
501     return mStatus;
502   }
504   // Check to see if we should redirect this channel elsewhere by
505   // nsIHttpChannel.redirectTo API request
506   if (mAPIRedirectToURI) {
507     return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
508   }
510   // Note that we are only setting the "Upgrade-Insecure-Requests" request
511   // header for *all* navigational requests instead of all requests as
512   // defined in the spec, see:
513   // https://www.w3.org/TR/upgrade-insecure-requests/#preference
514   nsContentPolicyType type = mLoadInfo
515                                  ? mLoadInfo->GetExternalContentPolicyType()
516                                  : nsIContentPolicy::TYPE_OTHER;
518   if (type == nsIContentPolicy::TYPE_DOCUMENT ||
519       type == nsIContentPolicy::TYPE_SUBDOCUMENT) {
520     rv = SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
521                           NS_LITERAL_CSTRING("1"), false);
522     NS_ENSURE_SUCCESS(rv, rv);
523   }
525   bool isHttps = false;
526   rv = mURI->SchemeIs("https", &isHttps);
527   NS_ENSURE_SUCCESS(rv, rv);
528   nsCOMPtr<nsIPrincipal> resultPrincipal;
529   if (!isHttps && mLoadInfo) {
530     nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
531         this, getter_AddRefs(resultPrincipal));
532   }
533   OriginAttributes originAttributes;
534   if (!NS_GetOriginAttributes(this, originAttributes)) {
535     return NS_ERROR_FAILURE;
536   }
537   bool isHttp = false;
538   rv = mURI->SchemeIs("http", &isHttp);
539   NS_ENSURE_SUCCESS(rv, rv);
541   // At this point it is no longer possible to call
542   // HttpBaseChannel::UpgradeToSecure.
543   mUpgradableToSecure = false;
544   if (isHttp) {
545     bool shouldUpgrade = mUpgradeToSecure;
546     if (!shouldUpgrade) {
547       rv = NS_ShouldSecureUpgrade(mURI, mLoadInfo, resultPrincipal,
548                                   mPrivateBrowsing, mAllowSTS, originAttributes,
549                                   shouldUpgrade);
550       NS_ENSURE_SUCCESS(rv, rv);
551     }
552     if (shouldUpgrade) {
553       return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
554     }
555   }
557   // ensure that we are using a valid hostname
558   if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Origin())))
559     return NS_ERROR_UNKNOWN_HOST;
561   if (mUpgradeProtocolCallback) {
562     // Websockets can run over HTTP/2, but other upgrades can't.
563     if (mUpgradeProtocol.EqualsLiteral("websocket") &&
564         gHttpHandler->IsH2WebsocketsEnabled()) {
565       // Need to tell the conn manager that we're ok with http/2 even with
566       // the allow keepalive bit not set. That bit needs to stay off,
567       // though, in case we end up having to fallback to http/1.1 (where
568       // we absolutely do want to disable keepalive).
569       mCaps |= NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE;
570     } else {
571       mCaps |= NS_HTTP_DISALLOW_SPDY;
572     }
573   }
575   if (mTRR) {
576     mCaps |= NS_HTTP_LARGE_KEEPALIVE | NS_HTTP_DISABLE_TRR;
577   }
579   if (mLoadFlags & LOAD_DISABLE_TRR) {
580     mCaps |= NS_HTTP_DISABLE_TRR;
581   }
583   // Finalize ConnectionInfo flags before SpeculativeConnect
584   mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
585   mConnectionInfo->SetPrivate(mPrivateBrowsing);
586   mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
587   mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
588                                      mBeConservative);
589   mConnectionInfo->SetTlsFlags(mTlsFlags);
590   mConnectionInfo->SetTrrUsed(mTRR);
591   mConnectionInfo->SetTrrDisabled(mCaps & NS_HTTP_DISABLE_TRR);
592   mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
593   mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
595   // notify "http-on-before-connect" observers
596   gHttpHandler->OnBeforeConnect(this);
598   // Check if request was cancelled during http-on-before-connect.
599   if (mCanceled) {
600     return mStatus;
601   }
603   if (mSuspendCount) {
604     // We abandon the connection here if there was one.
605     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
606     MOZ_ASSERT(!mCallOnResume);
607     mCallOnResume = [](nsHttpChannel *self) {
608       self->OnBeforeConnectContinue();
609       return NS_OK;
610     };
611     return NS_OK;
612   }
614   return Connect();
617 void nsHttpChannel::OnBeforeConnectContinue() {
618   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
619   nsresult rv;
621   if (mSuspendCount) {
622     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
623     mCallOnResume = [](nsHttpChannel *self) {
624       self->OnBeforeConnectContinue();
625       return NS_OK;
626     };
627     return;
628   }
630   LOG(("nsHttpChannel::OnBeforeConnectContinue [this=%p]\n", this));
631   rv = Connect();
632   if (NS_FAILED(rv)) {
633     CloseCacheEntry(false);
634     Unused << AsyncAbort(rv);
635   }
638 nsresult nsHttpChannel::Connect() {
639   LOG(("nsHttpChannel::Connect [this=%p]\n", this));
641   // Don't allow resuming when cache must be used
642   if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
643     LOG(("Resuming from cache is not supported yet"));
644     return NS_ERROR_DOCUMENT_NOT_CACHED;
645   }
647   if (ShouldIntercept()) {
648     return RedirectToInterceptedChannel();
649   }
651   bool isTrackingResource = mIsThirdPartyTrackingResource;  // is atomic
652   LOG(("nsHttpChannel %p tracking resource=%d, cos=%u", this,
653        isTrackingResource, mClassOfService));
655   if (isTrackingResource) {
656     AddClassFlags(nsIClassOfService::Tail);
657   }
659   if (WaitingForTailUnblock()) {
660     MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
661     mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock;
662     return NS_OK;
663   }
665   return ConnectOnTailUnblock();
668 nsresult nsHttpChannel::ConnectOnTailUnblock() {
669   nsresult rv;
671   LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this));
673   // Consider opening a TCP connection right away.
674   SpeculativeConnect();
676   // open a cache entry for this channel...
677   bool isHttps = false;
678   rv = mURI->SchemeIs("https", &isHttps);
679   NS_ENSURE_SUCCESS(rv, rv);
680   rv = OpenCacheEntry(isHttps);
682   // do not continue if asyncOpenCacheEntry is in progress
683   if (AwaitingCacheCallbacks()) {
684     LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n",
685          this));
686     MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
688     if (mNetworkTriggered && mWaitingForProxy) {
689       // Someone has called TriggerNetwork(), meaning we are racing the
690       // network with the cache.
691       mWaitingForProxy = false;
692       return ContinueConnect();
693     }
695     return NS_OK;
696   }
698   if (NS_FAILED(rv)) {
699     LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n",
700          static_cast<uint32_t>(rv)));
701     // if this channel is only allowed to pull from the cache, then
702     // we must fail if we were unable to open a cache entry.
703     if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
704       // If we have a fallback URI (and we're not already
705       // falling back), process the fallback asynchronously.
706       if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
707         return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
708       }
709       return NS_ERROR_DOCUMENT_NOT_CACHED;
710     }
711     // otherwise, let's just proceed without using the cache.
712   }
714   if (mRaceCacheWithNetwork && ((mCacheEntry && !mCachedContentIsValid &&
715                                  (mDidReval || mCachedContentIsPartial)) ||
716                                 mIgnoreCacheEntry)) {
717     // We won't send the conditional request because the unconditional
718     // request was already sent (see bug 1377223).
719     AccumulateCategorical(
720         Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent);
721   }
723   // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI
724   // returns, then we may not have started reading from the cache.
725   // If the content is valid, we should attempt to do so, as technically the
726   // cache has won the race.
727   if (mRaceCacheWithNetwork && mCachedContentIsValid) {
728     Unused << ReadFromCache(true);
729   }
731   return TriggerNetwork();
734 nsresult nsHttpChannel::ContinueConnect() {
735   // If we need to start a CORS preflight, do it now!
736   // Note that it is important to do this before the early returns below.
737   if (!mIsCorsPreflightDone && mRequireCORSPreflight) {
738     MOZ_ASSERT(!mPreflightChannel);
739     nsresult rv = nsCORSListenerProxy::StartCORSPreflight(
740         this, this, mUnsafeHeaders, getter_AddRefs(mPreflightChannel));
741     return rv;
742   }
744   MOZ_RELEASE_ASSERT(!mRequireCORSPreflight || mIsCorsPreflightDone,
745                      "CORS preflight must have been finished by the time we "
746                      "do the rest of ContinueConnect");
748   // we may or may not have a cache entry at this point
749   if (mCacheEntry) {
750     // read straight from the cache if possible...
751     if (mCachedContentIsValid) {
752       nsRunnableMethod<nsHttpChannel> *event = nullptr;
753       nsresult rv;
754       if (!mCachedContentIsPartial) {
755         rv = AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
756         if (NS_FAILED(rv)) {
757           LOG(("  AsyncCall failed (%08x)", static_cast<uint32_t>(rv)));
758         }
759       }
760       rv = ReadFromCache(true);
761       if (NS_FAILED(rv) && event) {
762         event->Revoke();
763       }
765       AccumulateCacheHitTelemetry(kCacheHit);
766       mCacheDisposition = kCacheHit;
768       return rv;
769     }
770     if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
771       // the cache contains the requested resource, but it must be
772       // validated before we can reuse it.  since we are not allowed
773       // to hit the net, there's nothing more to do.  the document
774       // is effectively not in the cache.
775       LOG(("  !mCachedContentIsValid && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
776       return NS_ERROR_DOCUMENT_NOT_CACHED;
777     }
778   } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
779     // If we have a fallback URI (and we're not already
780     // falling back), process the fallback asynchronously.
781     if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
782       return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
783     }
784     LOG(("  !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
785     return NS_ERROR_DOCUMENT_NOT_CACHED;
786   }
788   if (mLoadFlags & LOAD_NO_NETWORK_IO) {
789     LOG(("  mLoadFlags & LOAD_NO_NETWORK_IO"));
790     return NS_ERROR_DOCUMENT_NOT_CACHED;
791   }
793   // hit the net...
794   return DoConnect();
797 nsresult nsHttpChannel::DoConnect(nsHttpTransaction *aTransWithStickyConn) {
798   LOG(("nsHttpChannel::DoConnect [this=%p, aTransWithStickyConn=%p]\n", this,
799        aTransWithStickyConn));
801   nsresult rv = SetupTransaction();
802   if (NS_FAILED(rv)) {
803     return rv;
804   }
806   if (aTransWithStickyConn) {
807     rv = gHttpHandler->InitiateTransactionWithStickyConn(
808         mTransaction, mPriority, aTransWithStickyConn);
809   } else {
810     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
811   }
813   if (NS_FAILED(rv)) {
814     return rv;
815   }
817   rv = mTransactionPump->AsyncRead(this, nullptr);
818   if (NS_FAILED(rv)) {
819     return rv;
820   }
822   uint32_t suspendCount = mSuspendCount;
823   if (mAsyncResumePending) {
824     LOG(
825         ("  Suspend()'ing transaction pump once because of async resume pending"
826          ", sc=%u, pump=%p, this=%p",
827          suspendCount, mTransactionPump.get(), this));
828     ++suspendCount;
829   }
830   while (suspendCount--) {
831     mTransactionPump->Suspend();
832   }
834   return NS_OK;
837 void nsHttpChannel::SpeculativeConnect() {
838   // Before we take the latency hit of dealing with the cache, try and
839   // get the TCP (and SSL) handshakes going so they can overlap.
841   // don't speculate if we are on uses of the offline application cache,
842   // if we are offline, when doing http upgrade (i.e.
843   // websockets bootstrap), or if we can't do keep-alive (because then we
844   // couldn't reuse the speculative connection anyhow).
845   if (mApplicationCache || gIOService->IsOffline() ||
846       mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE))
847     return;
849   // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
850   // LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network,
851   // so skip preconnects for them.
852   if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE |
853                     LOAD_NO_NETWORK_IO | LOAD_CHECK_OFFLINE_CACHE))
854     return;
856   if (mAllowStaleCacheContent) {
857     return;
858   }
860   nsCOMPtr<nsIInterfaceRequestor> callbacks;
861   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
862                                          getter_AddRefs(callbacks));
863   if (!callbacks) return;
865   Unused << gHttpHandler->SpeculativeConnect(
866       mConnectionInfo, callbacks,
867       mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_DISABLE_TRR |
868                NS_HTTP_DISABLE_IPV4 | NS_HTTP_DISABLE_IPV6));
871 void nsHttpChannel::DoNotifyListenerCleanup() {
872   // We don't need this info anymore
873   CleanRedirectCacheChainIfNecessary();
876 void nsHttpChannel::ReleaseListeners() {
877   HttpBaseChannel::ReleaseListeners();
878   mChannelClassifier = nullptr;
879   mWarningReporter = nullptr;
882 void nsHttpChannel::DoAsyncAbort(nsresult aStatus) {
883   Unused << AsyncAbort(aStatus);
886 void nsHttpChannel::HandleAsyncRedirect() {
887   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
889   if (mSuspendCount) {
890     LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
891     mCallOnResume = [](nsHttpChannel *self) {
892       self->HandleAsyncRedirect();
893       return NS_OK;
894     };
895     return;
896   }
898   nsresult rv = NS_OK;
900   LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this));
902   // since this event is handled asynchronously, it is possible that this
903   // channel could have been canceled, in which case there would be no point
904   // in processing the redirect.
905   if (NS_SUCCEEDED(mStatus)) {
906     PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
907     rv = AsyncProcessRedirection(mResponseHead->Status());
908     if (NS_FAILED(rv)) {
909       PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
910       // TODO: if !DoNotRender3xxBody(), render redirect body instead.
911       // But first we need to cache 3xx bodies (bug 748510)
912       rv = ContinueHandleAsyncRedirect(rv);
913       MOZ_ASSERT(NS_SUCCEEDED(rv));
914     }
915   } else {
916     rv = ContinueHandleAsyncRedirect(mStatus);
917     MOZ_ASSERT(NS_SUCCEEDED(rv));
918   }
921 nsresult nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) {
922   if (NS_FAILED(rv)) {
923     // If AsyncProcessRedirection fails, then we have to send out the
924     // OnStart/OnStop notifications.
925     LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n",
926          static_cast<uint32_t>(rv)));
928     bool redirectsEnabled = !mLoadInfo || !mLoadInfo->GetDontFollowRedirects();
930     if (redirectsEnabled) {
931       // TODO: stop failing original channel if redirect vetoed?
932       mStatus = rv;
934       DoNotifyListener();
936       // Blow away cache entry if we couldn't process the redirect
937       // for some reason (the cache entry might be corrupt).
938       if (mCacheEntry) {
939         mCacheEntry->AsyncDoom(nullptr);
940       }
941     } else {
942       DoNotifyListener();
943     }
944   }
946   CloseCacheEntry(true);
948   mIsPending = false;
950   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
952   return NS_OK;
955 void nsHttpChannel::HandleAsyncNotModified() {
956   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
958   if (mSuspendCount) {
959     LOG(("Waiting until resume to do async not-modified [this=%p]\n", this));
960     mCallOnResume = [](nsHttpChannel *self) {
961       self->HandleAsyncNotModified();
962       return NS_OK;
963     };
964     return;
965   }
967   LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
969   DoNotifyListener();
971   CloseCacheEntry(false);
973   mIsPending = false;
975   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
978 void nsHttpChannel::HandleAsyncFallback() {
979   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
981   if (mSuspendCount) {
982     LOG(("Waiting until resume to do async fallback [this=%p]\n", this));
983     mCallOnResume = [](nsHttpChannel *self) {
984       self->HandleAsyncFallback();
985       return NS_OK;
986     };
987     return;
988   }
990   nsresult rv = NS_OK;
992   LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n", this));
994   // since this event is handled asynchronously, it is possible that this
995   // channel could have been canceled, in which case there would be no point
996   // in processing the fallback.
997   if (!mCanceled) {
998     PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
999     bool waitingForRedirectCallback;
1000     rv = ProcessFallback(&waitingForRedirectCallback);
1001     if (waitingForRedirectCallback) return;
1002     PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
1003   }
1005   rv = ContinueHandleAsyncFallback(rv);
1006   MOZ_ASSERT(NS_SUCCEEDED(rv));
1009 nsresult nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv) {
1010   if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) {
1011     // If ProcessFallback fails, then we have to send out the
1012     // OnStart/OnStop notifications.
1013     LOG(("ProcessFallback failed [rv=%" PRIx32 ", %d]\n",
1014          static_cast<uint32_t>(rv), mFallingBack));
1015     mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED;
1016     DoNotifyListener();
1017   }
1019   mIsPending = false;
1021   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
1023   return rv;
1026 nsresult nsHttpChannel::SetupTransaction() {
1027   LOG(("nsHttpChannel::SetupTransaction [this=%p, cos=%u, prio=%d]\n", this,
1028        mClassOfService, mPriority));
1030   NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
1032   nsresult rv;
1034   mozilla::MutexAutoLock lock(mRCWNLock);
1036   // If we're racing cache with network, conditional or byte range header
1037   // could be added in OnCacheEntryCheck. We cannot send conditional request
1038   // without having the entry, so we need to remove the headers here and
1039   // ignore the cache entry in OnCacheEntryAvailable.
1040   if (mRaceCacheWithNetwork && AwaitingCacheCallbacks()) {
1041     if (mDidReval) {
1042       LOG(("  Removing conditional request headers"));
1043       UntieValidationRequest();
1044       mDidReval = false;
1045       mIgnoreCacheEntry = true;
1046     }
1048     if (mCachedContentIsPartial) {
1049       LOG(("  Removing byte range request headers"));
1050       UntieByteRangeRequest();
1051       mCachedContentIsPartial = false;
1052       mIgnoreCacheEntry = true;
1053     }
1055     if (mIgnoreCacheEntry) {
1056       if (!mAvailableCachedAltDataType.IsEmpty()) {
1057         mAvailableCachedAltDataType.Truncate();
1058         mAltDataLength = 0;
1059       }
1060       mCacheInputStream.CloseAndRelease();
1061     }
1062   }
1064   mUsedNetwork = 1;
1066   if (!mAllowSpdy) {
1067     mCaps |= NS_HTTP_DISALLOW_SPDY;
1068   }
1069   if (mBeConservative) {
1070     mCaps |= NS_HTTP_BE_CONSERVATIVE;
1071   }
1073   // Use the URI path if not proxying (transparent proxying such as proxy
1074   // CONNECT does not count here). Also figure out what HTTP version to use.
1075   nsAutoCString buf, path;
1076   nsCString *requestURI;
1078   // This is the normal e2e H1 path syntax "/index.html"
1079   rv = mURI->GetPathQueryRef(path);
1080   if (NS_FAILED(rv)) {
1081     return rv;
1082   }
1084   // path may contain UTF-8 characters, so ensure that they're escaped.
1085   if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces,
1086                    buf)) {
1087     requestURI = &buf;
1088   } else {
1089     requestURI = &path;
1090   }
1092   // trim off the #ref portion if any...
1093   int32_t ref1 = requestURI->FindChar('#');
1094   if (ref1 != kNotFound) {
1095     requestURI->SetLength(ref1);
1096   }
1098   if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
1099     mRequestHead.SetVersion(gHttpHandler->HttpVersion());
1100   } else {
1101     mRequestHead.SetPath(*requestURI);
1103     // RequestURI should be the absolute uri H1 proxy syntax
1104     // "http://foo/index.html" so we will overwrite the relative version in
1105     // requestURI
1106     rv = mURI->GetUserPass(buf);
1107     if (NS_FAILED(rv)) return rv;
1108     if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
1109                            strncmp(mSpec.get(), "https:", 6) == 0)) {
1110       nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
1111       if (NS_WARN_IF(!urifixup)) {
1112         return NS_ERROR_FAILURE;
1113       }
1115       nsCOMPtr<nsIURI> tempURI;
1116       nsresult rv = urifixup->CreateExposableURI(mURI, getter_AddRefs(tempURI));
1117       if (NS_WARN_IF(NS_FAILED(rv))) {
1118         return rv;
1119       }
1121       rv = tempURI->GetAsciiSpec(path);
1122       if (NS_FAILED(rv)) return rv;
1123       requestURI = &path;
1124     } else {
1125       requestURI = &mSpec;
1126     }
1128     // trim off the #ref portion if any...
1129     int32_t ref2 = requestURI->FindChar('#');
1130     if (ref2 != kNotFound) {
1131       requestURI->SetLength(ref2);
1132     }
1134     mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
1135   }
1137   mRequestHead.SetRequestURI(*requestURI);
1139   // set the request time for cache expiration calculations
1140   mRequestTime = NowInSeconds();
1141   mRequestTimeInitialized = true;
1143   // if doing a reload, force end-to-end
1144   if (mLoadFlags & LOAD_BYPASS_CACHE) {
1145     // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
1146     // no proxy is configured since we might be talking with a transparent
1147     // proxy, i.e. one that operates at the network level.  See bug #14772.
1148     rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
1149     MOZ_ASSERT(NS_SUCCEEDED(rv));
1150     // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
1151     // no-cache'
1152     if (mRequestHead.Version() >= HttpVersion::v1_1) {
1153       rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
1154       MOZ_ASSERT(NS_SUCCEEDED(rv));
1155     }
1156   } else if ((mLoadFlags & VALIDATE_ALWAYS) && !mCacheEntryIsWriteOnly) {
1157     // We need to send 'Cache-Control: max-age=0' to force each cache along
1158     // the path to the origin server to revalidate its own entry, if any,
1159     // with the next cache or server.  See bug #84847.
1160     //
1161     // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
1162     if (mRequestHead.Version() >= HttpVersion::v1_1)
1163       rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true);
1164     else
1165       rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
1166     MOZ_ASSERT(NS_SUCCEEDED(rv));
1167   }
1169   if (mResuming) {
1170     char byteRange[32];
1171     SprintfLiteral(byteRange, "bytes=%" PRIu64 "-", mStartPos);
1172     rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
1173     MOZ_ASSERT(NS_SUCCEEDED(rv));
1175     if (!mEntityID.IsEmpty()) {
1176       // Also, we want an error if this resource changed in the meantime
1177       // Format of the entity id is: escaped_etag/size/lastmod
1178       nsCString::const_iterator start, end, slash;
1179       mEntityID.BeginReading(start);
1180       mEntityID.EndReading(end);
1181       mEntityID.BeginReading(slash);
1183       if (FindCharInReadable('/', slash, end)) {
1184         nsAutoCString ifMatch;
1185         rv = mRequestHead.SetHeader(
1186             nsHttp::If_Match,
1187             NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
1188         MOZ_ASSERT(NS_SUCCEEDED(rv));
1190         ++slash;  // Incrementing, so that searching for '/' won't find
1191                   // the same slash again
1192       }
1194       if (FindCharInReadable('/', slash, end)) {
1195         rv = mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
1196                                     Substring(++slash, end));
1197         MOZ_ASSERT(NS_SUCCEEDED(rv));
1198       }
1199     }
1200   }
1202   // create wrapper for this channel's notification callbacks
1203   nsCOMPtr<nsIInterfaceRequestor> callbacks;
1204   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
1205                                          getter_AddRefs(callbacks));
1207   // create the transaction object
1208   mTransaction = new nsHttpTransaction();
1209   LOG1(("nsHttpChannel %p created nsHttpTransaction %p\n", this,
1210         mTransaction.get()));
1211   mTransaction->SetTransactionObserver(mTransactionObserver);
1212   mTransactionObserver = nullptr;
1214   // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
1215   if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS;
1217   if (mTimingEnabled) mCaps |= NS_HTTP_TIMING_ENABLED;
1219   if (mUpgradeProtocolCallback) {
1220     rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
1221     MOZ_ASSERT(NS_SUCCEEDED(rv));
1222     rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(),
1223                                     true);
1224     MOZ_ASSERT(NS_SUCCEEDED(rv));
1225     mCaps |= NS_HTTP_STICKY_CONNECTION;
1226     mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
1227   }
1229   if (mPushedStream) {
1230     mTransaction->SetPushedStream(mPushedStream);
1231     mPushedStream = nullptr;
1232   }
1234   nsCOMPtr<nsIHttpPushListener> pushListener;
1235   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
1236                                 NS_GET_IID(nsIHttpPushListener),
1237                                 getter_AddRefs(pushListener));
1238   if (pushListener) {
1239     mCaps |= NS_HTTP_ONPUSH_LISTENER;
1240   }
1242   EnsureTopLevelOuterContentWindowId();
1244   nsCOMPtr<nsIAsyncInputStream> responseStream;
1245   rv = mTransaction->Init(
1246       mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength,
1247       mUploadStreamHasHeaders, GetCurrentThreadEventTarget(), callbacks, this,
1248       mTopLevelOuterContentWindowId, getter_AddRefs(responseStream));
1249   if (NS_FAILED(rv)) {
1250     mTransaction = nullptr;
1251     return rv;
1252   }
1254   mTransaction->SetClassOfService(mClassOfService);
1255   if (EnsureRequestContext()) {
1256     mTransaction->SetRequestContext(mRequestContext);
1257   }
1259   rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
1260                                  responseStream);
1261   return rv;
1264 enum class Report { Error, Warning };
1266 // Helper Function to report messages to the console when the loaded
1267 // script had a wrong MIME type.
1268 void ReportMimeTypeMismatch(nsHttpChannel *aChannel, const char *aMessageName,
1269                             nsIURI *aURI, const nsACString &aContentType,
1270                             Report report) {
1271   NS_ConvertUTF8toUTF16 spec(aURI->GetSpecOrDefault());
1272   NS_ConvertUTF8toUTF16 contentType(aContentType);
1274   aChannel->LogMimeTypeMismatch(nsCString(aMessageName),
1275                                 report == Report::Warning, spec, contentType);
1278 // Check and potentially enforce X-Content-Type-Options: nosniff
1279 nsresult ProcessXCTO(nsHttpChannel *aChannel, nsIURI *aURI,
1280                      nsHttpResponseHead *aResponseHead,
1281                      nsILoadInfo *aLoadInfo) {
1282   if (!aURI || !aResponseHead || !aLoadInfo) {
1283     // if there is no uri, no response head or no loadInfo, then there is
1284     // nothing to do
1285     return NS_OK;
1286   }
1288   // 1) Query the XCTO header and check if 'nosniff' is the first value.
1289   nsAutoCString contentTypeOptionsHeader;
1290   Unused << aResponseHead->GetHeader(nsHttp::X_Content_Type_Options,
1291                                      contentTypeOptionsHeader);
1292   if (contentTypeOptionsHeader.IsEmpty()) {
1293     // if there is no XCTO header, then there is nothing to do.
1294     return NS_OK;
1295   }
1296   // XCTO header might contain multiple values which are comma separated, so:
1297   // a) let's skip all subsequent values
1298   //     e.g. "   NoSniFF   , foo " will be "   NoSniFF   "
1299   int32_t idx = contentTypeOptionsHeader.Find(",");
1300   if (idx > 0) {
1301     contentTypeOptionsHeader = Substring(contentTypeOptionsHeader, 0, idx);
1302   }
1303   // b) let's trim all surrounding whitespace
1304   //    e.g. "   NoSniFF   " -> "NoSniFF"
1305   nsHttp::TrimHTTPWhitespace(contentTypeOptionsHeader,
1306                              contentTypeOptionsHeader);
1307   // c) let's compare the header (ignoring case)
1308   //    e.g. "NoSniFF" -> "nosniff"
1309   //    if it's not 'nosniff' then there is nothing to do here
1310   if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) {
1311     // since we are getting here, the XCTO header was sent;
1312     // a non matching value most likely means a mistake happenend;
1313     // e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.
1314     NS_ConvertUTF8toUTF16 char16_header(contentTypeOptionsHeader);
1315     const char16_t *params[] = {char16_header.get()};
1316     RefPtr<Document> doc;
1317     aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
1318     nsContentUtils::ReportToConsole(
1319         nsIScriptError::warningFlag, NS_LITERAL_CSTRING("XCTO"), doc,
1320         nsContentUtils::eSECURITY_PROPERTIES, "XCTOHeaderValueMissing", params,
1321         ArrayLength(params));
1322     return NS_OK;
1323   }
1325   // 2) Query the content type from the channel
1326   nsAutoCString contentType;
1327   aResponseHead->ContentType(contentType);
1329   // 3) Compare the expected MIME type with the actual type
1330   if (aLoadInfo->GetExternalContentPolicyType() ==
1331       nsIContentPolicy::TYPE_STYLESHEET) {
1332     if (contentType.EqualsLiteral(TEXT_CSS)) {
1333       return NS_OK;
1334     }
1335     ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
1336                            Report::Error);
1337     return NS_ERROR_CORRUPTED_CONTENT;
1338   }
1340   if (aLoadInfo->GetExternalContentPolicyType() ==
1341       nsIContentPolicy::TYPE_SCRIPT) {
1342     if (nsContentUtils::IsJavascriptMIMEType(
1343             NS_ConvertUTF8toUTF16(contentType))) {
1344       return NS_OK;
1345     }
1346     ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
1347                            Report::Error);
1348     return NS_ERROR_CORRUPTED_CONTENT;
1349   }
1350   return NS_OK;
1353 // Ensure that a load of type script has correct MIME type
1354 nsresult EnsureMIMEOfScript(nsHttpChannel *aChannel, nsIURI *aURI,
1355                             nsHttpResponseHead *aResponseHead,
1356                             nsILoadInfo *aLoadInfo) {
1357   if (!aURI || !aResponseHead || !aLoadInfo) {
1358     // if there is no uri, no response head or no loadInfo, then there is
1359     // nothing to do
1360     return NS_OK;
1361   }
1363   if (aLoadInfo->GetExternalContentPolicyType() !=
1364       nsIContentPolicy::TYPE_SCRIPT) {
1365     // if this is not a script load, then there is nothing to do
1366     return NS_OK;
1367   }
1369   nsAutoCString contentType;
1370   aResponseHead->ContentType(contentType);
1371   NS_ConvertUTF8toUTF16 typeString(contentType);
1373   if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
1374     // script load has type script
1375     AccumulateCategorical(
1376         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::javaScript);
1377     return NS_OK;
1378   }
1380   switch (aLoadInfo->InternalContentPolicyType()) {
1381     case nsIContentPolicy::TYPE_SCRIPT:
1382     case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
1383     case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
1384     case nsIContentPolicy::TYPE_INTERNAL_MODULE:
1385     case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
1386       AccumulateCategorical(
1387           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::script_load);
1388       break;
1389     case nsIContentPolicy::TYPE_INTERNAL_WORKER:
1390     case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
1391       AccumulateCategorical(
1392           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::worker_load);
1393       break;
1394     case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
1395       AccumulateCategorical(
1396           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::serviceworker_load);
1397       break;
1398     case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
1399       AccumulateCategorical(
1400           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::importScript_load);
1401       break;
1402     default:
1403       MOZ_ASSERT_UNREACHABLE("unexpected script type");
1404       break;
1405   }
1407   nsCOMPtr<nsIURI> requestURI;
1408   aLoadInfo->LoadingPrincipal()->GetURI(getter_AddRefs(requestURI));
1410   nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
1411   bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
1412   nsresult rv = ssm->CheckSameOriginURI(requestURI, aURI, false, isPrivateWin);
1413   if (NS_SUCCEEDED(rv)) {
1414     // same origin
1415     AccumulateCategorical(
1416         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::same_origin);
1417   } else {
1418     bool cors = false;
1419     nsAutoCString corsOrigin;
1420     rv = aResponseHead->GetHeader(
1421         nsHttp::ResolveAtom("Access-Control-Allow-Origin"), corsOrigin);
1422     if (NS_SUCCEEDED(rv)) {
1423       if (corsOrigin.Equals("*")) {
1424         cors = true;
1425       } else {
1426         nsCOMPtr<nsIURI> corsOriginURI;
1427         rv = NS_NewURI(getter_AddRefs(corsOriginURI), corsOrigin);
1428         if (NS_SUCCEEDED(rv)) {
1429           bool isPrivateWin =
1430               aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
1431           rv = ssm->CheckSameOriginURI(requestURI, corsOriginURI, false,
1432                                        isPrivateWin);
1433           if (NS_SUCCEEDED(rv)) {
1434             cors = true;
1435           }
1436         }
1437       }
1438     }
1439     if (cors) {
1440       // cors origin
1441       AccumulateCategorical(
1442           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::CORS_origin);
1443     } else {
1444       // cross origin
1445       AccumulateCategorical(
1446           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::cross_origin);
1447     }
1448   }
1450   bool block = false;
1451   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
1452     // script load has type image
1453     AccumulateCategorical(
1454         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::image);
1455     block = true;
1456   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("audio/"))) {
1457     // script load has type audio
1458     AccumulateCategorical(
1459         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::audio);
1460     block = true;
1461   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("video/"))) {
1462     // script load has type video
1463     AccumulateCategorical(
1464         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::video);
1465     block = true;
1466   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/csv"))) {
1467     // script load has type text/csv
1468     AccumulateCategorical(
1469         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_csv);
1470     block = true;
1471   }
1473   if (block) {
1474     // Instead of consulting Preferences::GetBool() all the time we
1475     // can cache the result to speed things up.
1476     static bool sCachedBlockScriptWithWrongMime = false;
1477     static bool sIsInited = false;
1478     if (!sIsInited) {
1479       sIsInited = true;
1480       Preferences::AddBoolVarCache(&sCachedBlockScriptWithWrongMime,
1481                                    "security.block_script_with_wrong_mime",
1482                                    true);
1483     }
1485     // Do not block the load if the feature is not enabled.
1486     if (!sCachedBlockScriptWithWrongMime) {
1487       return NS_OK;
1488     }
1490     ReportMimeTypeMismatch(aChannel, "BlockScriptWithWrongMimeType2", aURI,
1491                            contentType, Report::Error);
1492     return NS_ERROR_CORRUPTED_CONTENT;
1493   }
1495   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/plain"))) {
1496     // script load has type text/plain
1497     AccumulateCategorical(
1498         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_plain);
1499   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/xml"))) {
1500     // script load has type text/xml
1501     AccumulateCategorical(
1502         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_xml);
1503   } else if (StringBeginsWith(contentType,
1504                               NS_LITERAL_CSTRING("application/octet-stream"))) {
1505     // script load has type application/octet-stream
1506     AccumulateCategorical(
1507         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_octet_stream);
1508   } else if (StringBeginsWith(contentType,
1509                               NS_LITERAL_CSTRING("application/xml"))) {
1510     // script load has type application/xml
1511     AccumulateCategorical(
1512         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_xml);
1513   } else if (StringBeginsWith(contentType,
1514                               NS_LITERAL_CSTRING("application/json"))) {
1515     // script load has type application/json
1516     AccumulateCategorical(
1517         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_json);
1518   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/json"))) {
1519     // script load has type text/json
1520     AccumulateCategorical(
1521         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_json);
1522   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/html"))) {
1523     // script load has type text/html
1524     AccumulateCategorical(
1525         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_html);
1526   } else if (contentType.IsEmpty()) {
1527     // script load has no type
1528     AccumulateCategorical(
1529         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::empty);
1530   } else {
1531     // script load has unknown type
1532     AccumulateCategorical(
1533         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::unknown);
1534   }
1536   // We restrict importScripts() in worker code to JavaScript MIME types.
1537   nsContentPolicyType internalType = aLoadInfo->InternalContentPolicyType();
1538   if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS) {
1539     // Instead of consulting Preferences::GetBool() all the time we
1540     // can cache the result to speed things up.
1541     static bool sCachedBlockImportScriptsWithWrongMime = false;
1542     static bool sIsInited = false;
1543     if (!sIsInited) {
1544       sIsInited = true;
1545       Preferences::AddBoolVarCache(
1546           &sCachedBlockImportScriptsWithWrongMime,
1547           "security.block_importScripts_with_wrong_mime", true);
1548     }
1550     // Do not block the load if the feature is not enabled.
1551     if (!sCachedBlockImportScriptsWithWrongMime) {
1552       return NS_OK;
1553     }
1555     ReportMimeTypeMismatch(aChannel, "BlockImportScriptsWithWrongMimeType",
1556                            aURI, contentType, Report::Error);
1557     return NS_ERROR_CORRUPTED_CONTENT;
1558   }
1560   // ES6 modules require a strict MIME type check.
1561   if (internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE ||
1562       internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD) {
1563     ReportMimeTypeMismatch(aChannel, "BlockModuleWithWrongMimeType", aURI,
1564                            contentType, Report::Error);
1565     return NS_ERROR_CORRUPTED_CONTENT;
1566   }
1568   return NS_OK;
1571 // Warn when a load of type script uses a wrong MIME type and
1572 // wasn't blocked by EnsureMIMEOfScript or ProcessXCTO.
1573 void WarnWrongMIMEOfScript(nsHttpChannel *aChannel, nsIURI *aURI,
1574                            nsHttpResponseHead *aResponseHead,
1575                            nsILoadInfo *aLoadInfo) {
1576   if (!aURI || !aResponseHead || !aLoadInfo) {
1577     // If there is no uri, no response head or no loadInfo, then there is
1578     // nothing to do.
1579     return;
1580   }
1582   if (aLoadInfo->GetExternalContentPolicyType() !=
1583       nsIContentPolicy::TYPE_SCRIPT) {
1584     // If this is not a script load, then there is nothing to do.
1585     return;
1586   }
1588   nsAutoCString contentType;
1589   aResponseHead->ContentType(contentType);
1590   NS_ConvertUTF8toUTF16 typeString(contentType);
1591   if (!nsContentUtils::IsJavascriptMIMEType(typeString)) {
1592     ReportMimeTypeMismatch(aChannel, "WarnScriptWithWrongMimeType", aURI,
1593                            contentType, Report::Warning);
1594   }
1597 nsresult nsHttpChannel::CallOnStartRequest() {
1598   LOG(("nsHttpChannel::CallOnStartRequest [this=%p]", this));
1600   MOZ_RELEASE_ASSERT(!mRequireCORSPreflight || mIsCorsPreflightDone,
1601                      "CORS preflight must have been finished by the time we "
1602                      "call OnStartRequest");
1604   if (mOnStartRequestCalled) {
1605     // This can only happen when a range request loading rest of the data
1606     // after interrupted concurrent cache read asynchronously failed, e.g.
1607     // the response range bytes are not as expected or this channel has
1608     // been externally canceled.
1609     //
1610     // It's legal to bypass CallOnStartRequest for that case since we've
1611     // already called OnStartRequest on our listener and also added all
1612     // content converters before.
1613     MOZ_ASSERT(mConcurrentCacheAccess);
1614     LOG(("CallOnStartRequest already invoked before"));
1615     return mStatus;
1616   }
1618   mTracingEnabled = false;
1620   // Ensure mListener->OnStartRequest will be invoked before exiting
1621   // this function.
1622   auto onStartGuard = MakeScopeExit([&] {
1623     LOG(
1624         ("  calling mListener->OnStartRequest by ScopeExit [this=%p, "
1625          "listener=%p]\n",
1626          this, mListener.get()));
1627     MOZ_ASSERT(!mOnStartRequestCalled);
1629     if (mListener) {
1630       nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
1631       deleteProtector->OnStartRequest(this, nullptr);
1632     }
1634     mOnStartRequestCalled = true;
1635   });
1637   nsresult rv = EnsureMIMEOfScript(this, mURI, mResponseHead, mLoadInfo);
1638   NS_ENSURE_SUCCESS(rv, rv);
1640   rv = ProcessXCTO(this, mURI, mResponseHead, mLoadInfo);
1641   NS_ENSURE_SUCCESS(rv, rv);
1643   WarnWrongMIMEOfScript(this, mURI, mResponseHead, mLoadInfo);
1645   // Allow consumers to override our content type
1646   if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
1647     // NOTE: We can have both a txn pump and a cache pump when the cache
1648     // content is partial. In that case, we need to read from the cache,
1649     // because that's the one that has the initial contents. If that fails
1650     // then give the transaction pump a shot.
1652     nsIChannel *thisChannel = static_cast<nsIChannel *>(this);
1654     bool typeSniffersCalled = false;
1655     if (mCachePump) {
1656       typeSniffersCalled =
1657           NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel));
1658     }
1660     if (!typeSniffersCalled && mTransactionPump) {
1661       mTransactionPump->PeekStream(CallTypeSniffers, thisChannel);
1662     }
1663   }
1665   bool unknownDecoderStarted = false;
1666   if (mResponseHead && !mResponseHead->HasContentType()) {
1667     MOZ_ASSERT(mConnectionInfo, "Should have connection info here");
1668     if (!mContentTypeHint.IsEmpty())
1669       mResponseHead->SetContentType(mContentTypeHint);
1670     else if (mResponseHead->Version() == HttpVersion::v0_9 &&
1671              mConnectionInfo->OriginPort() != mConnectionInfo->DefaultPort())
1672       mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
1673     else {
1674       // Uh-oh.  We had better find out what type we are!
1675       nsCOMPtr<nsIStreamConverterService> serv;
1676       rv = gHttpHandler->GetStreamConverterService(getter_AddRefs(serv));
1677       // If we failed, we just fall through to the "normal" case
1678       if (NS_SUCCEEDED(rv)) {
1679         nsCOMPtr<nsIStreamListener> converter;
1680         rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, "*/*", mListener,
1681                                     nullptr, getter_AddRefs(converter));
1682         if (NS_SUCCEEDED(rv)) {
1683           mListener = converter;
1684           unknownDecoderStarted = true;
1685         }
1686       }
1687     }
1688   }
1690   if (mResponseHead && !mResponseHead->HasContentCharset())
1691     mResponseHead->SetContentCharset(mContentCharsetHint);
1693   LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
1694        mListener.get()));
1696   // About to call OnStartRequest, dismiss the guard object.
1697   onStartGuard.release();
1699   if (mListener) {
1700     MOZ_ASSERT(!mOnStartRequestCalled,
1701                "We should not call OsStartRequest twice");
1702     nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
1703     rv = deleteProtector->OnStartRequest(this, nullptr);
1704     mOnStartRequestCalled = true;
1705     if (NS_FAILED(rv)) return rv;
1706   } else {
1707     NS_WARNING("OnStartRequest skipped because of null listener");
1708     mOnStartRequestCalled = true;
1709   }
1711   // Install stream converter if required.
1712   // If we use unknownDecoder, stream converters will be installed later (in
1713   // nsUnknownDecoder) after OnStartRequest is called for the real listener.
1714   if (!unknownDecoderStarted) {
1715     nsCOMPtr<nsIStreamListener> listener;
1716     rv =
1717         DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
1718     if (NS_FAILED(rv)) {
1719       return rv;
1720     }
1721     if (listener) {
1722       mListener = listener;
1723       mCompressListener = listener;
1724     }
1725   }
1727   // if this channel is for a download, close off access to the cache.
1728   if (mCacheEntry && mChannelIsForDownload) {
1729     mCacheEntry->AsyncDoom(nullptr);
1731     // We must keep the cache entry in case of partial request.
1732     // Concurrent access is the same, we need the entry in
1733     // OnStopRequest.
1734     // We also need the cache entry when racing cache with network to find
1735     // out what is the source of the data.
1736     if (!mCachedContentIsPartial && !mConcurrentCacheAccess &&
1737         !(mRaceCacheWithNetwork &&
1738           mFirstResponseSource == RESPONSE_FROM_CACHE)) {
1739       CloseCacheEntry(false);
1740     }
1741   }
1743   if (!mCanceled) {
1744     // create offline cache entry if offline caching was requested
1745     if (ShouldUpdateOfflineCacheEntry()) {
1746       LOG(("writing to the offline cache"));
1747       rv = InitOfflineCacheEntry();
1748       if (NS_FAILED(rv)) return rv;
1750       // InitOfflineCacheEntry may have closed mOfflineCacheEntry
1751       if (mOfflineCacheEntry) {
1752         rv = InstallOfflineCacheListener();
1753         if (NS_FAILED(rv)) return rv;
1754       }
1755     } else if (mApplicationCacheForWrite) {
1756       LOG(("offline cache is up to date, not updating"));
1757       CloseOfflineCacheEntry();
1758     }
1759   }
1761   // Check for a Content-Signature header and inject mediator if the header is
1762   // requested and available.
1763   // If requested (mLoadInfo->GetVerifySignedContent), but not present, or
1764   // present but not valid, fail this channel and return
1765   // NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a
1766   // fallback load in nsDocShell.
1767   // Note that OnStartRequest has already been called on the target stream
1768   // listener at this point. We have to add the listener here that late to
1769   // ensure that it's the last listener and can thus block the load in
1770   // OnStopRequest.
1771   if (!mCanceled) {
1772     rv = ProcessContentSignatureHeader(mResponseHead);
1773     if (NS_FAILED(rv)) {
1774       LOG(("Content-signature verification failed.\n"));
1775       return rv;
1776     }
1777   }
1779   return NS_OK;
1782 nsresult nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) {
1783   // Failure to set up a proxy tunnel via CONNECT means one of the following:
1784   // 1) Proxy wants authorization, or forbids.
1785   // 2) DNS at proxy couldn't resolve target URL.
1786   // 3) Proxy connection to target failed or timed out.
1787   // 4) Eve intercepted our CONNECT, and is replying with malicious HTML.
1788   //
1789   // Our current architecture would parse the proxy's response content with
1790   // the permission of the target URL.  Given #4, we must avoid rendering the
1791   // body of the reply, and instead give the user a (hopefully helpful)
1792   // boilerplate error page, based on just the HTTP status of the reply.
1794   MOZ_ASSERT(mConnectionInfo->UsingConnect(),
1795              "proxy connect failed but not using CONNECT?");
1796   nsresult rv;
1797   switch (httpStatus) {
1798     case 300:
1799     case 301:
1800     case 302:
1801     case 303:
1802     case 307:
1803     case 308:
1804       // Bad redirect: not top-level, or it's a POST, bad/missing Location,
1805       // or ProcessRedirect() failed for some other reason.  Legal
1806       // redirects that fail because site not available, etc., are handled
1807       // elsewhere, in the regular codepath.
1808       rv = NS_ERROR_CONNECTION_REFUSED;
1809       break;
1810     case 403:  // HTTP/1.1: "Forbidden"
1811     case 407:  // ProcessAuthentication() failed
1812     case 501:  // HTTP/1.1: "Not Implemented"
1813       // user sees boilerplate Mozilla "Proxy Refused Connection" page.
1814       rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1815       break;
1816     // Squid sends 404 if DNS fails (regular 404 from target is tunneled)
1817     case 404:  // HTTP/1.1: "Not Found"
1818     // RFC 2616: "some deployed proxies are known to return 400 or 500 when
1819     // DNS lookups time out."  (Squid uses 500 if it runs out of sockets: so
1820     // we have a conflict here).
1821     case 400:  // HTTP/1.1 "Bad Request"
1822     case 500:  // HTTP/1.1: "Internal Server Error"
1823       /* User sees: "Address Not Found: Firefox can't find the server at
1824        * www.foo.com."
1825        */
1826       rv = NS_ERROR_UNKNOWN_HOST;
1827       break;
1828     case 502:  // HTTP/1.1: "Bad Gateway" (invalid resp from target server)
1829     // Squid returns 503 if target request fails for anything but DNS.
1830     case 503:  // HTTP/1.1: "Service Unavailable"
1831       /* User sees: "Failed to Connect:
1832        *  Firefox can't establish a connection to the server at
1833        *  www.foo.com.  Though the site seems valid, the browser
1834        *  was unable to establish a connection."
1835        */
1836       rv = NS_ERROR_CONNECTION_REFUSED;
1837       break;
1838     // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to
1839     // do here: picking target timeout, as DNS covered by 400/404/500
1840     case 504:  // HTTP/1.1: "Gateway Timeout"
1841       // user sees: "Network Timeout: The server at www.foo.com
1842       //              is taking too long to respond."
1843       rv = NS_ERROR_NET_TIMEOUT;
1844       break;
1845     // Confused proxy server or malicious response
1846     default:
1847       rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1848       break;
1849   }
1850   LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n", this,
1851        httpStatus));
1852   Cancel(rv);
1853   {
1854     nsresult rv = CallOnStartRequest();
1855     if (NS_FAILED(rv)) {
1856       LOG(("CallOnStartRequest failed [this=%p httpStatus=%u rv=%08x]\n", this,
1857            httpStatus, static_cast<uint32_t>(rv)));
1858     }
1859   }
1860   return rv;
1863 static void GetSTSConsoleErrorTag(uint32_t failureResult,
1864                                   nsAString &consoleErrorTag) {
1865   switch (failureResult) {
1866     case nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:
1867       consoleErrorTag = NS_LITERAL_STRING("STSUntrustworthyConnection");
1868       break;
1869     case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
1870       consoleErrorTag = NS_LITERAL_STRING("STSCouldNotParseHeader");
1871       break;
1872     case nsISiteSecurityService::ERROR_NO_MAX_AGE:
1873       consoleErrorTag = NS_LITERAL_STRING("STSNoMaxAge");
1874       break;
1875     case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
1876       consoleErrorTag = NS_LITERAL_STRING("STSMultipleMaxAges");
1877       break;
1878     case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
1879       consoleErrorTag = NS_LITERAL_STRING("STSInvalidMaxAge");
1880       break;
1881     case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
1882       consoleErrorTag = NS_LITERAL_STRING("STSMultipleIncludeSubdomains");
1883       break;
1884     case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
1885       consoleErrorTag = NS_LITERAL_STRING("STSInvalidIncludeSubdomains");
1886       break;
1887     case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
1888       consoleErrorTag = NS_LITERAL_STRING("STSCouldNotSaveState");
1889       break;
1890     default:
1891       consoleErrorTag = NS_LITERAL_STRING("STSUnknownError");
1892       break;
1893   }
1896 static void GetPKPConsoleErrorTag(uint32_t failureResult,
1897                                   nsAString &consoleErrorTag) {
1898   switch (failureResult) {
1899     case nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:
1900       consoleErrorTag = NS_LITERAL_STRING("PKPUntrustworthyConnection");
1901       break;
1902     case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
1903       consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotParseHeader");
1904       break;
1905     case nsISiteSecurityService::ERROR_NO_MAX_AGE:
1906       consoleErrorTag = NS_LITERAL_STRING("PKPNoMaxAge");
1907       break;
1908     case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
1909       consoleErrorTag = NS_LITERAL_STRING("PKPMultipleMaxAges");
1910       break;
1911     case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
1912       consoleErrorTag = NS_LITERAL_STRING("PKPInvalidMaxAge");
1913       break;
1914     case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
1915       consoleErrorTag = NS_LITERAL_STRING("PKPMultipleIncludeSubdomains");
1916       break;
1917     case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
1918       consoleErrorTag = NS_LITERAL_STRING("PKPInvalidIncludeSubdomains");
1919       break;
1920     case nsISiteSecurityService::ERROR_INVALID_PIN:
1921       consoleErrorTag = NS_LITERAL_STRING("PKPInvalidPin");
1922       break;
1923     case nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS:
1924       consoleErrorTag = NS_LITERAL_STRING("PKPMultipleReportURIs");
1925       break;
1926     case nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN:
1927       consoleErrorTag = NS_LITERAL_STRING("PKPPinsetDoesNotMatch");
1928       break;
1929     case nsISiteSecurityService::ERROR_NO_BACKUP_PIN:
1930       consoleErrorTag = NS_LITERAL_STRING("PKPNoBackupPin");
1931       break;
1932     case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
1933       consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotSaveState");
1934       break;
1935     case nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN:
1936       consoleErrorTag = NS_LITERAL_STRING("PKPRootNotBuiltIn");
1937       break;
1938     default:
1939       consoleErrorTag = NS_LITERAL_STRING("PKPUnknownError");
1940       break;
1941   }
1945  * Process a single security header. Only two types are supported: HSTS and
1946  * HPKP.
1947  */
1948 nsresult nsHttpChannel::ProcessSingleSecurityHeader(
1949     uint32_t aType, nsITransportSecurityInfo *aSecInfo, uint32_t aFlags) {
1950   nsHttpAtom atom;
1951   switch (aType) {
1952     case nsISiteSecurityService::HEADER_HSTS:
1953       atom = nsHttp::ResolveAtom("Strict-Transport-Security");
1954       break;
1955     case nsISiteSecurityService::HEADER_HPKP:
1956       atom = nsHttp::ResolveAtom("Public-Key-Pins");
1957       break;
1958     default:
1959       MOZ_ASSERT_UNREACHABLE("Invalid security header type");
1960       return NS_ERROR_FAILURE;
1961   }
1963   nsAutoCString securityHeader;
1964   nsresult rv = mResponseHead->GetHeader(atom, securityHeader);
1965   if (NS_SUCCEEDED(rv)) {
1966     nsISiteSecurityService *sss = gHttpHandler->GetSSService();
1967     NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
1968     // Process header will now discard the headers itself if the channel
1969     // wasn't secure (whereas before it had to be checked manually)
1970     OriginAttributes originAttributes;
1971     NS_GetOriginAttributes(this, originAttributes);
1972     uint32_t failureResult;
1973     uint32_t headerSource = nsISiteSecurityService::SOURCE_ORGANIC_REQUEST;
1974     rv = sss->ProcessHeader(aType, mURI, securityHeader, aSecInfo, aFlags,
1975                             headerSource, originAttributes, nullptr, nullptr,
1976                             &failureResult);
1977     if (NS_FAILED(rv)) {
1978       nsAutoString consoleErrorCategory;
1979       nsAutoString consoleErrorTag;
1980       switch (aType) {
1981         case nsISiteSecurityService::HEADER_HSTS:
1982           GetSTSConsoleErrorTag(failureResult, consoleErrorTag);
1983           consoleErrorCategory = NS_LITERAL_STRING("Invalid HSTS Headers");
1984           break;
1985         case nsISiteSecurityService::HEADER_HPKP:
1986           GetPKPConsoleErrorTag(failureResult, consoleErrorTag);
1987           consoleErrorCategory = NS_LITERAL_STRING("Invalid HPKP Headers");
1988           break;
1989         default:
1990           return NS_ERROR_FAILURE;
1991       }
1992       Unused << AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
1993       LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n",
1994            atom.get()));
1995     }
1996   } else {
1997     if (rv != NS_ERROR_NOT_AVAILABLE) {
1998       // All other errors are fatal
1999       NS_ENSURE_SUCCESS(rv, rv);
2000     }
2001     LOG(("nsHttpChannel: No %s header, continuing load.\n", atom.get()));
2002   }
2003   return NS_OK;
2007  * Decide whether or not to remember Strict-Transport-Security, and whether
2008  * or not to enforce channel integrity.
2010  * @return NS_ERROR_FAILURE if there's security information missing even though
2011  *             it's an HTTPS connection.
2012  */
2013 nsresult nsHttpChannel::ProcessSecurityHeaders() {
2014   nsresult rv;
2015   bool isHttps = false;
2016   rv = mURI->SchemeIs("https", &isHttps);
2017   NS_ENSURE_SUCCESS(rv, rv);
2019   // If this channel is not loading securely, STS or PKP doesn't do anything.
2020   // In the case of HSTS, the upgrade to HTTPS takes place earlier in the
2021   // channel load process.
2022   if (!isHttps) return NS_OK;
2024   nsAutoCString asciiHost;
2025   rv = mURI->GetAsciiHost(asciiHost);
2026   NS_ENSURE_SUCCESS(rv, NS_OK);
2028   // If the channel is not a hostname, but rather an IP, do not process STS
2029   // or PKP headers
2030   PRNetAddr hostAddr;
2031   if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr))
2032     return NS_OK;
2034   // mSecurityInfo may not always be present, and if it's not then it is okay
2035   // to just disregard any security headers since we know nothing about the
2036   // security of the connection.
2037   NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
2039   uint32_t flags =
2040       NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
2042   // Get the TransportSecurityInfo
2043   nsCOMPtr<nsITransportSecurityInfo> transSecInfo =
2044       do_QueryInterface(mSecurityInfo);
2045   NS_ENSURE_TRUE(transSecInfo, NS_ERROR_FAILURE);
2047   rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HSTS,
2048                                    transSecInfo, flags);
2049   NS_ENSURE_SUCCESS(rv, rv);
2051   rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP,
2052                                    transSecInfo, flags);
2053   NS_ENSURE_SUCCESS(rv, rv);
2055   return NS_OK;
2058 nsresult nsHttpChannel::ProcessContentSignatureHeader(
2059     nsHttpResponseHead *aResponseHead) {
2060   nsresult rv = NS_OK;
2062   // we only do this if we require it in loadInfo
2063   if (!mLoadInfo || !mLoadInfo->GetVerifySignedContent()) {
2064     return NS_OK;
2065   }
2067   NS_ENSURE_TRUE(aResponseHead, NS_ERROR_ABORT);
2068   nsAutoCString contentSignatureHeader;
2069   nsHttpAtom atom = nsHttp::ResolveAtom("Content-Signature");
2070   rv = aResponseHead->GetHeader(atom, contentSignatureHeader);
2071   if (NS_FAILED(rv)) {
2072     LOG(("Content-Signature header is missing but expected."));
2073     DoInvalidateCacheEntry(mURI);
2074     return NS_ERROR_INVALID_SIGNATURE;
2075   }
2077   // if we require a signature but it is empty, fail
2078   if (contentSignatureHeader.IsEmpty()) {
2079     DoInvalidateCacheEntry(mURI);
2080     LOG(("An expected content-signature header is missing.\n"));
2081     return NS_ERROR_INVALID_SIGNATURE;
2082   }
2084   // we ensure a content type here to avoid running into problems with
2085   // content sniffing, which might sniff parts of the content before we can
2086   // verify the signature
2087   if (!aResponseHead->HasContentType()) {
2088     NS_WARNING(
2089         "Empty content type can get us in trouble when verifying "
2090         "content signatures");
2091     return NS_ERROR_INVALID_SIGNATURE;
2092   }
2093   // create a new listener that meadiates the content
2094   RefPtr<ContentVerifier> contentVerifyingMediator =
2095       new ContentVerifier(mListener, nullptr);
2096   rv = contentVerifyingMediator->Init(contentSignatureHeader, this, nullptr);
2097   NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
2098   mListener = contentVerifyingMediator;
2100   return NS_OK;
2104  * Decide whether or not to send a security report and, if so, give the
2105  * SecurityReporter the information required to send such a report.
2106  */
2107 void nsHttpChannel::ProcessSecurityReport(nsresult status) {
2108   uint32_t errorClass;
2109   nsCOMPtr<nsINSSErrorsService> errSvc =
2110       do_GetService("@mozilla.org/nss_errors_service;1");
2111   // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
2112   // not in the set of errors covered by the NSS errors service.
2113   nsresult rv = errSvc->GetErrorClass(status, &errorClass);
2114   if (!NS_SUCCEEDED(rv)) {
2115     return;
2116   }
2118   // if the content was not loaded succesfully and we have security info,
2119   // send a TLS error report - we must do this early as other parts of
2120   // OnStopRequest can return early
2121   bool reportingEnabled =
2122       Preferences::GetBool("security.ssl.errorReporting.enabled");
2123   bool reportingAutomatic =
2124       Preferences::GetBool("security.ssl.errorReporting.automatic");
2125   if (!mSecurityInfo || !reportingEnabled || !reportingAutomatic) {
2126     return;
2127   }
2129   nsCOMPtr<nsITransportSecurityInfo> secInfo = do_QueryInterface(mSecurityInfo);
2130   nsCOMPtr<nsISecurityReporter> errorReporter =
2131       do_GetService("@mozilla.org/securityreporter;1");
2133   if (!secInfo || !mURI) {
2134     return;
2135   }
2137   nsAutoCString hostStr;
2138   int32_t port;
2139   rv = mURI->GetHost(hostStr);
2140   if (!NS_SUCCEEDED(rv)) {
2141     return;
2142   }
2144   rv = mURI->GetPort(&port);
2146   if (NS_SUCCEEDED(rv)) {
2147     errorReporter->ReportTLSError(secInfo, hostStr, port);
2148   }
2151 bool nsHttpChannel::IsHTTPS() {
2152   bool isHttps;
2153   if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps) return false;
2154   return true;
2157 void nsHttpChannel::ProcessSSLInformation() {
2158   // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
2159   // can be whitelisted for TLS False Start in future sessions. We could
2160   // do the same for DH but its rarity doesn't justify the lookup.
2162   if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo || !IsHTTPS() ||
2163       mPrivateBrowsing)
2164     return;
2166   nsCOMPtr<nsITransportSecurityInfo> securityInfo =
2167       do_QueryInterface(mSecurityInfo);
2168   if (!securityInfo) return;
2170   uint32_t state;
2171   if (securityInfo && NS_SUCCEEDED(securityInfo->GetSecurityState(&state)) &&
2172       (state & nsIWebProgressListener::STATE_IS_BROKEN)) {
2173     // Send weak crypto warnings to the web console
2174     if (state & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
2175       nsString consoleErrorTag = NS_LITERAL_STRING("WeakCipherSuiteWarning");
2176       nsString consoleErrorCategory = NS_LITERAL_STRING("SSL");
2177       Unused << AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
2178     }
2179   }
2181   // Send (SHA-1) signature algorithm errors to the web console
2182   nsCOMPtr<nsIX509Cert> cert;
2183   securityInfo->GetServerCert(getter_AddRefs(cert));
2184   if (cert) {
2185     UniqueCERTCertificate nssCert(cert->GetCert());
2186     if (nssCert) {
2187       SECOidTag tag = SECOID_GetAlgorithmTag(&nssCert->signature);
2188       LOG(("Checking certificate signature: The OID tag is %i [this=%p]\n", tag,
2189            this));
2190       // Check to see if the signature is sha-1 based.
2191       // Not including checks for SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE
2192       // from http://tools.ietf.org/html/rfc2437#section-8 since I
2193       // can't see reference to it outside this spec
2194       if (tag == SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION ||
2195           tag == SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST ||
2196           tag == SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE) {
2197         nsString consoleErrorTag = NS_LITERAL_STRING("SHA1Sig");
2198         nsString consoleErrorMessage = NS_LITERAL_STRING("SHA-1 Signature");
2199         Unused << AddSecurityMessage(consoleErrorTag, consoleErrorMessage);
2200       }
2201     }
2202   }
2205 void nsHttpChannel::ProcessAltService() {
2206   // e.g. Alt-Svc: h2=":443"; ma=60
2207   // e.g. Alt-Svc: h2="otherhost:443"
2208   // Alt-Svc       = 1#( alternative *( OWS ";" OWS parameter ) )
2209   // alternative   = protocol-id "=" alt-authority
2210   // protocol-id   = token ; percent-encoded ALPN protocol identifier
2211   // alt-authority = quoted-string ;  containing [ uri-host ] ":" port
2213   if (!mAllowAltSvc) {  // per channel opt out
2214     return;
2215   }
2217   if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) {
2218     return;
2219   }
2221   nsAutoCString scheme;
2222   mURI->GetScheme(scheme);
2223   bool isHttp = scheme.EqualsLiteral("http");
2224   if (!isHttp && !scheme.EqualsLiteral("https")) {
2225     return;
2226   }
2228   nsAutoCString altSvc;
2229   Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc);
2230   if (altSvc.IsEmpty()) {
2231     return;
2232   }
2234   if (!nsHttp::IsReasonableHeaderValue(altSvc)) {
2235     LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));
2236     return;
2237   }
2239   nsAutoCString originHost;
2240   int32_t originPort = 80;
2241   mURI->GetPort(&originPort);
2242   if (NS_FAILED(mURI->GetAsciiHost(originHost))) {
2243     return;
2244   }
2246   nsCOMPtr<nsIInterfaceRequestor> callbacks;
2247   nsCOMPtr<nsProxyInfo> proxyInfo;
2248   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
2249                                          getter_AddRefs(callbacks));
2250   if (mProxyInfo) {
2251     proxyInfo = do_QueryInterface(mProxyInfo);
2252   }
2254   OriginAttributes originAttributes;
2255   NS_GetOriginAttributes(this, originAttributes);
2257   AltSvcMapping::ProcessHeader(
2258       altSvc, scheme, originHost, originPort, mUsername, mPrivateBrowsing,
2259       callbacks, proxyInfo, mCaps & NS_HTTP_DISALLOW_SPDY, originAttributes);
2262 nsresult nsHttpChannel::ProcessResponse() {
2263   uint32_t httpStatus = mResponseHead->Status();
2265   LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", this,
2266        httpStatus));
2268   // Gather data on whether the transaction and page (if this is
2269   // the initial page load) is being loaded with SSL.
2270   Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_IS_SSL,
2271                         mConnectionInfo->EndToEndSSL());
2272   if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
2273     Telemetry::Accumulate(Telemetry::HTTP_PAGELOAD_IS_SSL,
2274                           mConnectionInfo->EndToEndSSL());
2275   }
2277   if (Telemetry::CanRecordPrereleaseData()) {
2278     // how often do we see something like Alt-Svc: "443:quic,p=1"
2279     nsAutoCString alt_service;
2280     Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, alt_service);
2281     bool saw_quic =
2282         (!alt_service.IsEmpty() && PL_strstr(alt_service.get(), "quic"))
2283             ? true
2284             : false;
2285     Telemetry::Accumulate(Telemetry::HTTP_SAW_QUIC_ALT_PROTOCOL, saw_quic);
2287     // Gather data on how many URLS get redirected
2288     switch (httpStatus) {
2289       case 200:
2290         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 0);
2291         break;
2292       case 301:
2293         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 1);
2294         break;
2295       case 302:
2296         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 2);
2297         break;
2298       case 304:
2299         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 3);
2300         break;
2301       case 307:
2302         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 4);
2303         break;
2304       case 308:
2305         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 5);
2306         break;
2307       case 400:
2308         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 6);
2309         break;
2310       case 401:
2311         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 7);
2312         break;
2313       case 403:
2314         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 8);
2315         break;
2316       case 404:
2317         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 9);
2318         break;
2319       case 500:
2320         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 10);
2321         break;
2322       default:
2323         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 11);
2324         break;
2325     }
2326   }
2328   // Let the predictor know whether this was a cacheable response or not so
2329   // that it knows whether or not to possibly prefetch this resource in the
2330   // future.
2331   // We use GetReferringPage because mReferrer may not be set at all, or may
2332   // not be a full URI (HttpBaseChannel::SetReferrer has the gorey details).
2333   // If that's null, though, we'll fall back to mReferrer just in case (this
2334   // is especially useful in xpcshell tests, where we don't have an actual
2335   // pageload to get a referrer from).
2336   nsCOMPtr<nsIURI> referrer = GetReferringPage();
2337   if (!referrer) {
2338     referrer = mReferrer;
2339   }
2341   if (referrer) {
2342     nsCOMPtr<nsILoadContextInfo> lci = GetLoadContextInfo(this);
2343     mozilla::net::Predictor::UpdateCacheability(
2344         referrer, mURI, httpStatus, mRequestHead, mResponseHead, lci,
2345         mIsThirdPartyTrackingResource);
2346   }
2348   // Only allow 407 (authentication required) to continue
2349   if (mTransaction && mTransaction->ProxyConnectFailed() && httpStatus != 407) {
2350     return ProcessFailedProxyConnect(httpStatus);
2351   }
2353   MOZ_ASSERT(!mCachedContentIsValid || mRaceCacheWithNetwork,
2354              "We should not be hitting the network if we have valid cached "
2355              "content unless we are racing the network and cache");
2357   ProcessSSLInformation();
2359   // notify "http-on-examine-response" observers
2360   gHttpHandler->OnExamineResponse(this);
2362   return ContinueProcessResponse1();
2365 void nsHttpChannel::AsyncContinueProcessResponse() {
2366   nsresult rv;
2367   rv = ContinueProcessResponse1();
2368   if (NS_FAILED(rv)) {
2369     // A synchronous failure here would normally be passed as the return
2370     // value from OnStartRequest, which would in turn cancel the request.
2371     // If we're continuing asynchronously, we need to cancel the request
2372     // ourselves.
2373     Unused << Cancel(rv);
2374   }
2377 nsresult nsHttpChannel::ContinueProcessResponse1() {
2378   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
2379   nsresult rv;
2381   if (mSuspendCount) {
2382     LOG(("Waiting until resume to finish processing response [this=%p]\n",
2383          this));
2384     mCallOnResume = [](nsHttpChannel *self) {
2385       self->AsyncContinueProcessResponse();
2386       return NS_OK;
2387     };
2388     return NS_OK;
2389   }
2391   // Check if request was cancelled during http-on-examine-response.
2392   if (mCanceled) {
2393     return CallOnStartRequest();
2394   }
2396   uint32_t httpStatus = mResponseHead->Status();
2398   // STS, Cookies and Alt-Service should not be handled on proxy failure.
2399   // If proxy CONNECT response needs to complete, wait to process connection
2400   // for Strict-Transport-Security.
2401   if (!(mTransaction && mTransaction->ProxyConnectFailed()) &&
2402       (httpStatus != 407)) {
2403     nsAutoCString cookie;
2404     if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Cookie, cookie))) {
2405       SetCookie(cookie.get());
2406     }
2408     // Given a successful connection, process any STS or PKP data that's
2409     // relevant.
2410     DebugOnly<nsresult> rv = ProcessSecurityHeaders();
2411     MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
2413     if ((httpStatus < 500) && (httpStatus != 421)) {
2414       ProcessAltService();
2415     }
2416   }
2418   if (mConcurrentCacheAccess && mCachedContentIsPartial && httpStatus != 206) {
2419     LOG(
2420         ("  only expecting 206 when doing partial request during "
2421          "interrupted cache concurrent read"));
2422     return NS_ERROR_CORRUPTED_CONTENT;
2423   }
2425   // handle unused username and password in url (see bug 232567)
2426   if (httpStatus != 401 && httpStatus != 407) {
2427     if (!mAuthRetryPending) {
2428       rv = mAuthProvider->CheckForSuperfluousAuth();
2429       if (NS_FAILED(rv)) {
2430         LOG(("  CheckForSuperfluousAuth failed (%08x)",
2431              static_cast<uint32_t>(rv)));
2432       }
2433     }
2434     if (mCanceled) return CallOnStartRequest();
2436     // reset the authentication's current continuation state because ourvr
2437     // last authentication attempt has been completed successfully
2438     rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
2439     if (NS_FAILED(rv)) {
2440       LOG(("  Disconnect failed (%08x)", static_cast<uint32_t>(rv)));
2441     }
2442     mAuthProvider = nullptr;
2443     LOG(("  continuation state has been reset"));
2444   }
2446   rv = NS_OK;
2447   if (mRedirectTabPromise && !mCanceled) {
2448     MOZ_ASSERT(!mOnStartRequestCalled);
2450     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
2451     rv = StartCrossProcessRedirect();
2452     if (NS_SUCCEEDED(rv)) {
2453       return NS_OK;
2454     }
2455     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
2456   }
2458   // No process switch needed, continue as normal.
2459   return ContinueProcessResponse2(rv);
2462 nsresult nsHttpChannel::ContinueProcessResponse2(nsresult rv) {
2463   if (NS_FAILED(rv) && !mCanceled) {
2464     // The process switch failed, cancel this channel.
2465     Cancel(rv);
2466     return CallOnStartRequest();
2467   }
2469   if (mAPIRedirectToURI && !mCanceled) {
2470     MOZ_ASSERT(!mOnStartRequestCalled);
2471     nsCOMPtr<nsIURI> redirectTo;
2472     mAPIRedirectToURI.swap(redirectTo);
2474     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
2475     rv = StartRedirectChannelToURI(redirectTo,
2476                                    nsIChannelEventSink::REDIRECT_TEMPORARY);
2477     if (NS_SUCCEEDED(rv)) {
2478       return NS_OK;
2479     }
2480     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
2481   }
2483   // Hack: ContinueProcessResponse3 uses NS_OK to detect successful
2484   // redirects, so we distinguish this codepath (a non-redirect that's
2485   // processing normally) by passing in a bogus error code.
2486   return ContinueProcessResponse3(NS_BINDING_FAILED);
2489 nsresult nsHttpChannel::ContinueProcessResponse3(nsresult rv) {
2490   LOG(("nsHttpChannel::ContinueProcessResponse3 [this=%p, rv=%" PRIx32 "]",
2491        this, static_cast<uint32_t>(rv)));
2493   if (NS_SUCCEEDED(rv)) {
2494     // redirectTo() has passed through, we don't want to go on with
2495     // this channel.  It will now be canceled by the redirect handling
2496     // code that called this function.
2497     return NS_OK;
2498   }
2500   rv = NS_OK;
2502   uint32_t httpStatus = mResponseHead->Status();
2504   // handle different server response categories.  Note that we handle
2505   // caching or not caching of error pages in
2506   // nsHttpResponseHead::MustValidate; if you change this switch, update that
2507   // one
2508   switch (httpStatus) {
2509     case 200:
2510     case 203:
2511       // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
2512       // So if a server does that and sends 200 instead of 206 that we
2513       // expect, notify our caller.
2514       // However, if we wanted to start from the beginning, let it go through
2515       if (mResuming && mStartPos != 0) {
2516         LOG(("Server ignored our Range header, cancelling [this=%p]\n", this));
2517         Cancel(NS_ERROR_NOT_RESUMABLE);
2518         rv = CallOnStartRequest();
2519         break;
2520       }
2521       // these can normally be cached
2522       rv = ProcessNormal();
2523       MaybeInvalidateCacheEntryForSubsequentGet();
2524       break;
2525     case 206:
2526       if (mCachedContentIsPartial) {  // an internal byte range request...
2527         auto func = [](auto *self, nsresult aRv) {
2528           return self->ContinueProcessResponseAfterPartialContent(aRv);
2529         };
2530         rv = ProcessPartialContent(func);
2531         // Directly call ContinueProcessResponseAfterPartialContent if channel
2532         // is not suspended or ProcessPartialContent throws.
2533         if (!mSuspendCount || NS_FAILED(rv)) {
2534           return ContinueProcessResponseAfterPartialContent(rv);
2535         }
2536         return NS_OK;
2537       } else {
2538         mCacheInputStream.CloseAndRelease();
2539         rv = ProcessNormal();
2540       }
2541       break;
2542     case 300:
2543     case 301:
2544     case 302:
2545     case 307:
2546     case 308:
2547     case 303:
2548 #if 0
2549     case 305: // disabled as a security measure (see bug 187996).
2550 #endif
2551       // don't store the response body for redirects
2552       MaybeInvalidateCacheEntryForSubsequentGet();
2553       PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse4);
2554       rv = AsyncProcessRedirection(httpStatus);
2555       if (NS_FAILED(rv)) {
2556         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse4);
2557         LOG(("AsyncProcessRedirection failed [rv=%" PRIx32 "]\n",
2558              static_cast<uint32_t>(rv)));
2559         // don't cache failed redirect responses.
2560         if (mCacheEntry) mCacheEntry->AsyncDoom(nullptr);
2561         if (DoNotRender3xxBody(rv)) {
2562           mStatus = rv;
2563           DoNotifyListener();
2564         } else {
2565           rv = ContinueProcessResponse4(rv);
2566         }
2567       }
2568       break;
2569     case 304:
2570       if (!ShouldBypassProcessNotModified()) {
2571         auto func = [](auto *self, nsresult aRv) {
2572           return self->ContinueProcessResponseAfterNotModified(aRv);
2573         };
2574         rv = ProcessNotModified(func);
2575         // Directly call ContinueProcessResponseAfterNotModified if channel
2576         // is not suspended or ProcessNotModified throws.
2577         if (!mSuspendCount || NS_FAILED(rv)) {
2578           return ContinueProcessResponseAfterNotModified(rv);
2579         }
2580         return NS_OK;
2581       }
2583       // Don't cache uninformative 304
2584       if (mCustomConditionalRequest) {
2585         CloseCacheEntry(false);
2586       }
2588       if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) {
2589         rv = ProcessNormal();
2590       }
2591       break;
2592     case 401:
2593     case 407:
2594       if (MOZ_UNLIKELY(mCustomAuthHeader) && httpStatus == 401) {
2595         // When a custom auth header fails, we don't want to try
2596         // any cached credentials, nor we want to ask the user.
2597         // It's up to the consumer to re-try w/o setting a custom
2598         // auth header if cached credentials should be attempted.
2599         rv = NS_ERROR_FAILURE;
2600       } else {
2601         rv = mAuthProvider->ProcessAuthentication(
2602             httpStatus, mConnectionInfo->EndToEndSSL() && mTransaction &&
2603                             mTransaction->ProxyConnectFailed());
2604       }
2605       if (rv == NS_ERROR_IN_PROGRESS) {
2606         // authentication prompt has been invoked and result
2607         // is expected asynchronously
2608         mAuthRetryPending = true;
2609         if (httpStatus == 407 ||
2610             (mTransaction && mTransaction->ProxyConnectFailed()))
2611           mProxyAuthPending = true;
2613         // suspend the transaction pump to stop receiving the
2614         // unauthenticated content data. We will throw that data
2615         // away when user provides credentials or resume the pump
2616         // when user refuses to authenticate.
2617         LOG(
2618             ("Suspending the transaction, asynchronously prompting for "
2619              "credentials"));
2620         mTransactionPump->Suspend();
2621         rv = NS_OK;
2622       } else if (NS_FAILED(rv)) {
2623         LOG(("ProcessAuthentication failed [rv=%" PRIx32 "]\n",
2624              static_cast<uint32_t>(rv)));
2625         if (mTransaction && mTransaction->ProxyConnectFailed()) {
2626           return ProcessFailedProxyConnect(httpStatus);
2627         }
2628         if (!mAuthRetryPending) {
2629           rv = mAuthProvider->CheckForSuperfluousAuth();
2630           if (NS_FAILED(rv)) {
2631             LOG(("CheckForSuperfluousAuth failed [rv=%x]\n",
2632                  static_cast<uint32_t>(rv)));
2633           }
2634         }
2635         rv = ProcessNormal();
2636       } else {
2637         mAuthRetryPending = true;  // see DoAuthRetry
2638       }
2639       break;
2641     case 425:
2642       // Do not cache 425.
2643       CloseCacheEntry(false);
2644       MOZ_FALLTHROUGH;  // process normally
2645     default:
2646       rv = ProcessNormal();
2647       MaybeInvalidateCacheEntryForSubsequentGet();
2648       break;
2649   }
2651   UpdateCacheDisposition(false, false);
2652   return rv;
2655 nsresult nsHttpChannel::ContinueProcessResponseAfterPartialContent(
2656     nsresult aRv) {
2657   LOG(
2658       ("nsHttpChannel::ContinueProcessResponseAfterPartialContent "
2659        "[this=%p, rv=%" PRIx32 "]",
2660        this, static_cast<uint32_t>(aRv)));
2662   UpdateCacheDisposition(false, NS_SUCCEEDED(aRv));
2663   return aRv;
2666 nsresult nsHttpChannel::ContinueProcessResponseAfterNotModified(nsresult aRv) {
2667   LOG(
2668       ("nsHttpChannel::ContinueProcessResponseAfterNotModified "
2669        "[this=%p, rv=%" PRIx32 "]",
2670        this, static_cast<uint32_t>(aRv)));
2672   if (NS_SUCCEEDED(aRv)) {
2673     mTransactionReplaced = true;
2674     UpdateCacheDisposition(true, false);
2675     return NS_OK;
2676   }
2678   LOG(("ProcessNotModified failed [rv=%" PRIx32 "]\n",
2679        static_cast<uint32_t>(aRv)));
2681   // We cannot read from the cache entry, it might be in an
2682   // incosistent state.  Doom it and redirect the channel
2683   // to the same URI to reload from the network.
2684   mCacheInputStream.CloseAndRelease();
2685   if (mCacheEntry) {
2686     mCacheEntry->AsyncDoom(nullptr);
2687     mCacheEntry = nullptr;
2688   }
2690   nsresult rv =
2691       StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
2692   if (NS_SUCCEEDED(rv)) {
2693     return NS_OK;
2694   }
2696   // Don't cache uninformative 304
2697   if (mCustomConditionalRequest) {
2698     CloseCacheEntry(false);
2699   }
2701   if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) {
2702     rv = ProcessNormal();
2703   }
2705   UpdateCacheDisposition(false, false);
2706   return rv;
2709 void nsHttpChannel::UpdateCacheDisposition(bool aSuccessfulReval,
2710                                            bool aPartialContentUsed) {
2711   if (mRaceDelay && !mRaceCacheWithNetwork &&
2712       (mCachedContentIsPartial || mDidReval)) {
2713     if (aSuccessfulReval || aPartialContentUsed) {
2714       AccumulateCategorical(
2715           Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::CachedContentUsed);
2716     } else {
2717       AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::
2718                                 CachedContentNotUsed);
2719     }
2720   }
2722   if (Telemetry::CanRecordPrereleaseData()) {
2723     CacheDisposition cacheDisposition;
2724     if (!mDidReval) {
2725       cacheDisposition = kCacheMissed;
2726     } else if (aSuccessfulReval) {
2727       cacheDisposition = kCacheHitViaReval;
2728     } else {
2729       cacheDisposition = kCacheMissedViaReval;
2730     }
2731     AccumulateCacheHitTelemetry(cacheDisposition);
2732     mCacheDisposition = cacheDisposition;
2734     Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_VERSION,
2735                           static_cast<uint32_t>(mResponseHead->Version()));
2737     if (mResponseHead->Version() == HttpVersion::v0_9) {
2738       // DefaultPortTopLevel = 0, DefaultPortSubResource = 1,
2739       // NonDefaultPortTopLevel = 2, NonDefaultPortSubResource = 3
2740       uint32_t v09Info = 0;
2741       if (!(mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)) {
2742         v09Info += 1;
2743       }
2744       if (mConnectionInfo->OriginPort() != mConnectionInfo->DefaultPort()) {
2745         v09Info += 2;
2746       }
2747       Telemetry::Accumulate(Telemetry::HTTP_09_INFO, v09Info);
2748     }
2749   }
2752 nsresult nsHttpChannel::ContinueProcessResponse4(nsresult rv) {
2753   bool doNotRender = DoNotRender3xxBody(rv);
2755   if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) {
2756     bool isHTTP = false;
2757     if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP))) isHTTP = false;
2758     if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP)))
2759       isHTTP = false;
2761     if (!isHTTP) {
2762       // This was a blocked attempt to redirect and subvert the system by
2763       // redirecting to another protocol (perhaps javascript:)
2764       // In that case we want to throw an error instead of displaying the
2765       // non-redirected response body.
2766       LOG(("ContinueProcessResponse4 detected rejected Non-HTTP Redirection"));
2767       doNotRender = true;
2768       rv = NS_ERROR_CORRUPTED_CONTENT;
2769     }
2770   }
2772   if (doNotRender) {
2773     Cancel(rv);
2774     DoNotifyListener();
2775     return rv;
2776   }
2778   if (NS_SUCCEEDED(rv)) {
2779     UpdateInhibitPersistentCachingFlag();
2781     rv = InitCacheEntry();
2782     if (NS_FAILED(rv)) {
2783       LOG(
2784           ("ContinueProcessResponse4 "
2785            "failed to init cache entry [rv=%x]\n",
2786            static_cast<uint32_t>(rv)));
2787     }
2788     CloseCacheEntry(false);
2790     if (mApplicationCacheForWrite) {
2791       // Store response in the offline cache
2792       Unused << InitOfflineCacheEntry();
2793       CloseOfflineCacheEntry();
2794     }
2795     return NS_OK;
2796   }
2798   LOG(("ContinueProcessResponse4 got failure result [rv=%" PRIx32 "]\n",
2799        static_cast<uint32_t>(rv)));
2800   if (mTransaction && mTransaction->ProxyConnectFailed()) {
2801     return ProcessFailedProxyConnect(mRedirectType);
2802   }
2803   return ProcessNormal();
2806 nsresult nsHttpChannel::ProcessNormal() {
2807   nsresult rv;
2809   LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
2811   bool succeeded;
2812   rv = GetRequestSucceeded(&succeeded);
2813   if (NS_SUCCEEDED(rv) && !succeeded) {
2814     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
2815     bool waitingForRedirectCallback;
2816     Unused << ProcessFallback(&waitingForRedirectCallback);
2817     if (waitingForRedirectCallback) {
2818       // The transaction has been suspended by ProcessFallback.
2819       return NS_OK;
2820     }
2821     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
2822   }
2824   return ContinueProcessNormal(NS_OK);
2827 nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) {
2828   LOG(("nsHttpChannel::ContinueProcessNormal [this=%p]", this));
2830   if (NS_FAILED(rv)) {
2831     // Fill the failure status here, we have failed to fall back, thus we
2832     // have to report our status as failed.
2833     mStatus = rv;
2834     DoNotifyListener();
2835     return rv;
2836   }
2838   if (mFallingBack) {
2839     // Do not continue with normal processing, fallback is in
2840     // progress now.
2841     return NS_OK;
2842   }
2844   // if we're here, then any byte-range requests failed to result in a partial
2845   // response.  we must clear this flag to prevent BufferPartialContent from
2846   // being called inside our OnDataAvailable (see bug 136678).
2847   mCachedContentIsPartial = false;
2849   ClearBogusContentEncodingIfNeeded();
2851   UpdateInhibitPersistentCachingFlag();
2853   // this must be called before firing OnStartRequest, since http clients,
2854   // such as imagelib, expect our cache entry to already have the correct
2855   // expiration time (bug 87710).
2856   if (mCacheEntry) {
2857     rv = InitCacheEntry();
2858     if (NS_FAILED(rv)) CloseCacheEntry(true);
2859   }
2861   // Check that the server sent us what we were asking for
2862   if (mResuming) {
2863     // Create an entity id from the response
2864     nsAutoCString id;
2865     rv = GetEntityID(id);
2866     if (NS_FAILED(rv)) {
2867       // If creating an entity id is not possible -> error
2868       Cancel(NS_ERROR_NOT_RESUMABLE);
2869     } else if (mResponseHead->Status() != 206 &&
2870                mResponseHead->Status() != 200) {
2871       // Probably 404 Not Found, 412 Precondition Failed or
2872       // 416 Invalid Range -> error
2873       LOG(("Unexpected response status while resuming, aborting [this=%p]\n",
2874            this));
2875       Cancel(NS_ERROR_ENTITY_CHANGED);
2876     }
2877     // If we were passed an entity id, verify it's equal to the server's
2878     else if (!mEntityID.IsEmpty()) {
2879       if (!mEntityID.Equals(id)) {
2880         LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",
2881              mEntityID.get(), id.get(), this));
2882         Cancel(NS_ERROR_ENTITY_CHANGED);
2883       }
2884     }
2885   }
2887   rv = CallOnStartRequest();
2888   if (NS_FAILED(rv)) return rv;
2890   // install cache listener if we still have a cache entry open
2891   if (mCacheEntry && !mCacheEntryIsReadOnly) {
2892     rv = InstallCacheListener();
2893     if (NS_FAILED(rv)) return rv;
2894   }
2896   return NS_OK;
2899 nsresult nsHttpChannel::PromptTempRedirect() {
2900   if (!gHttpHandler->PromptTempRedirect()) {
2901     return NS_OK;
2902   }
2903   nsresult rv;
2904   nsCOMPtr<nsIStringBundleService> bundleService =
2905       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
2906   if (NS_FAILED(rv)) return rv;
2908   nsCOMPtr<nsIStringBundle> stringBundle;
2909   rv =
2910       bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle));
2911   if (NS_FAILED(rv)) return rv;
2913   nsAutoString messageString;
2914   rv = stringBundle->GetStringFromName("RepostFormData", messageString);
2915   if (NS_SUCCEEDED(rv)) {
2916     bool repost = false;
2918     nsCOMPtr<nsIPrompt> prompt;
2919     GetCallback(prompt);
2920     if (!prompt) return NS_ERROR_NO_INTERFACE;
2922     prompt->Confirm(nullptr, messageString.get(), &repost);
2923     if (!repost) return NS_ERROR_FAILURE;
2924   }
2926   return rv;
2929 nsresult nsHttpChannel::ProxyFailover() {
2930   LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this));
2932   nsresult rv;
2934   nsCOMPtr<nsIProtocolProxyService> pps =
2935       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
2936   if (NS_FAILED(rv)) return rv;
2938   nsCOMPtr<nsIProxyInfo> pi;
2939   rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus,
2940                                 getter_AddRefs(pi));
2941   if (NS_FAILED(rv)) return rv;
2943   // XXXbz so where does this codepath remove us from the loadgroup,
2944   // exactly?
2945   return AsyncDoReplaceWithProxy(pi);
2948 void nsHttpChannel::HandleAsyncRedirectChannelToHttps() {
2949   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
2951   if (mSuspendCount) {
2952     LOG(("Waiting until resume to do async redirect to https [this=%p]\n",
2953          this));
2954     mCallOnResume = [](nsHttpChannel *self) {
2955       self->HandleAsyncRedirectChannelToHttps();
2956       return NS_OK;
2957     };
2958     return;
2959   }
2961   nsresult rv = StartRedirectChannelToHttps();
2962   if (NS_FAILED(rv)) {
2963     rv = ContinueAsyncRedirectChannelToURI(rv);
2964     if (NS_FAILED(rv)) {
2965       LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
2966            static_cast<uint32_t>(rv), this));
2967     }
2968   }
2971 nsresult nsHttpChannel::StartRedirectChannelToHttps() {
2972   LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
2974   nsCOMPtr<nsIURI> upgradedURI;
2975   nsresult rv = NS_GetSecureUpgradedURI(mURI, getter_AddRefs(upgradedURI));
2976   NS_ENSURE_SUCCESS(rv, rv);
2978   return StartRedirectChannelToURI(
2979       upgradedURI, nsIChannelEventSink::REDIRECT_PERMANENT |
2980                        nsIChannelEventSink::REDIRECT_STS_UPGRADE);
2983 void nsHttpChannel::HandleAsyncAPIRedirect() {
2984   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
2985   MOZ_ASSERT(mAPIRedirectToURI, "How did that happen?");
2987   if (mSuspendCount) {
2988     LOG(("Waiting until resume to do async API redirect [this=%p]\n", this));
2989     mCallOnResume = [](nsHttpChannel *self) {
2990       self->HandleAsyncAPIRedirect();
2991       return NS_OK;
2992     };
2993     return;
2994   }
2996   nsresult rv = StartRedirectChannelToURI(
2997       mAPIRedirectToURI, nsIChannelEventSink::REDIRECT_PERMANENT);
2998   if (NS_FAILED(rv)) {
2999     rv = ContinueAsyncRedirectChannelToURI(rv);
3000     if (NS_FAILED(rv)) {
3001       LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
3002            static_cast<uint32_t>(rv), this));
3003     }
3004   }
3007 nsresult nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI,
3008                                                   uint32_t flags) {
3009   nsresult rv = NS_OK;
3010   LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
3012   nsCOMPtr<nsIChannel> newChannel;
3013   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
3014       CloneLoadInfoForRedirect(upgradedURI, flags);
3016   nsCOMPtr<nsIIOService> ioService;
3017   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
3018   NS_ENSURE_SUCCESS(rv, rv);
3020   rv = NS_NewChannelInternal(getter_AddRefs(newChannel), upgradedURI,
3021                              redirectLoadInfo,
3022                              nullptr,  // PerformanceStorage
3023                              nullptr,  // aLoadGroup
3024                              nullptr,  // aCallbacks
3025                              nsIRequest::LOAD_NORMAL, ioService);
3026   NS_ENSURE_SUCCESS(rv, rv);
3028   rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
3029   NS_ENSURE_SUCCESS(rv, rv);
3031   // Inform consumers about this fake redirect
3032   mRedirectChannel = newChannel;
3034   PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
3035   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
3037   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
3039   if (NS_FAILED(rv)) {
3040     AutoRedirectVetoNotifier notifier(this);
3042     /* Remove the async call to ContinueAsyncRedirectChannelToURI().
3043      * It is called directly by our callers upon return (to clean up
3044      * the failed redirect). */
3045     PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
3046   }
3048   return rv;
3051 nsresult nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv) {
3052   LOG(("nsHttpChannel::ContinueAsyncRedirectChannelToURI [this=%p]", this));
3054   // Since we handle mAPIRedirectToURI also after on-examine-response handler
3055   // rather drop it here to avoid any redirect loops, even just hypothetical.
3056   mAPIRedirectToURI = nullptr;
3058   if (NS_SUCCEEDED(rv)) {
3059     rv = OpenRedirectChannel(rv);
3060   }
3062   if (NS_FAILED(rv)) {
3063     // Cancel the channel here, the update to https had been vetoed
3064     // but from the security reasons we have to discard the whole channel
3065     // load.
3066     Cancel(rv);
3067   }
3069   if (mLoadGroup) {
3070     mLoadGroup->RemoveRequest(this, nullptr, mStatus);
3071   }
3073   if (NS_FAILED(rv) && !mCachePump && !mTransactionPump) {
3074     // We have to manually notify the listener because there is not any pump
3075     // that would call our OnStart/StopRequest after resume from waiting for
3076     // the redirect callback.
3077     DoNotifyListener();
3078   }
3080   return rv;
3083 nsresult nsHttpChannel::OpenRedirectChannel(nsresult rv) {
3084   AutoRedirectVetoNotifier notifier(this);
3086   // Make sure to do this after we received redirect veto answer,
3087   // i.e. after all sinks had been notified
3088   mRedirectChannel->SetOriginalURI(mOriginalURI);
3090   // open new channel
3091   rv = mRedirectChannel->AsyncOpen(mListener);
3093   NS_ENSURE_SUCCESS(rv, rv);
3095   mStatus = NS_BINDING_REDIRECTED;
3097   notifier.RedirectSucceeded();
3099   ReleaseListeners();
3101   return NS_OK;
3104 nsresult nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo *pi) {
3105   LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
3106   nsresult rv;
3108   nsCOMPtr<nsIChannel> newChannel;
3109   rv = gHttpHandler->NewProxiedChannel2(mURI, pi, mProxyResolveFlags, mProxyURI,
3110                                         mLoadInfo, getter_AddRefs(newChannel));
3111   if (NS_FAILED(rv)) return rv;
3113   uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
3115   rv = SetupReplacementChannel(mURI, newChannel, true, flags);
3116   if (NS_FAILED(rv)) return rv;
3118   // Inform consumers about this fake redirect
3119   mRedirectChannel = newChannel;
3121   PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
3122   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
3124   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
3126   if (NS_FAILED(rv)) {
3127     AutoRedirectVetoNotifier notifier(this);
3128     PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
3129   }
3131   return rv;
3134 nsresult nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv) {
3135   AutoRedirectVetoNotifier notifier(this);
3137   if (NS_FAILED(rv)) return rv;
3139   MOZ_ASSERT(mRedirectChannel, "No redirect channel?");
3141   // Make sure to do this after we received redirect veto answer,
3142   // i.e. after all sinks had been notified
3143   mRedirectChannel->SetOriginalURI(mOriginalURI);
3146   // open new channel
3147   rv = mRedirectChannel->AsyncOpen(mListener);
3148   NS_ENSURE_SUCCESS(rv, rv);
3150   mStatus = NS_BINDING_REDIRECTED;
3152   notifier.RedirectSucceeded();
3154   ReleaseListeners();
3156   return rv;
3159 nsresult nsHttpChannel::ResolveProxy() {
3160   LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this));
3162   nsresult rv;
3164   nsCOMPtr<nsIProtocolProxyService> pps =
3165       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
3166   if (NS_FAILED(rv)) return rv;
3168   // using the nsIProtocolProxyService2 allows a minor performance
3169   // optimization, but if an add-on has only provided the original interface
3170   // then it is ok to use that version.
3171   nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
3172   if (pps2) {
3173     rv = pps2->AsyncResolve2(this, mProxyResolveFlags, this, nullptr,
3174                              getter_AddRefs(mProxyRequest));
3175   } else {
3176     rv = pps->AsyncResolve(static_cast<nsIChannel *>(this), mProxyResolveFlags,
3177                            this, nullptr, getter_AddRefs(mProxyRequest));
3178   }
3180   return rv;
3183 bool nsHttpChannel::ResponseWouldVary(nsICacheEntry *entry) {
3184   nsresult rv;
3185   nsAutoCString buf, metaKey;
3186   Unused << mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
3187   if (!buf.IsEmpty()) {
3188     NS_NAMED_LITERAL_CSTRING(prefix, "request-");
3190     // enumerate the elements of the Vary header...
3191     char *val = buf.BeginWriting();  // going to munge buf
3192     char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
3193     while (token) {
3194       LOG(
3195           ("nsHttpChannel::ResponseWouldVary [channel=%p] "
3196            "processing %s\n",
3197            this, token));
3198       //
3199       // if "*", then assume response would vary.  technically speaking,
3200       // "Vary: header, *" is not permitted, but we allow it anyways.
3201       //
3202       // We hash values of cookie-headers for the following reasons:
3203       //
3204       //   1- cookies can be very large in size
3205       //
3206       //   2- cookies may contain sensitive information.  (for parity with
3207       //      out policy of not storing Set-cookie headers in the cache
3208       //      meta data, we likewise do not want to store cookie headers
3209       //      here.)
3210       //
3211       if (*token == '*')
3212         return true;  // if we encounter this, just get out of here
3214       // build cache meta data key...
3215       metaKey = prefix + nsDependentCString(token);
3217       // check the last value of the given request header to see if it has
3218       // since changed.  if so, then indeed the cached response is invalid.
3219       nsCString lastVal;
3220       entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
3221       LOG(
3222           ("nsHttpChannel::ResponseWouldVary [channel=%p] "
3223            "stored value = \"%s\"\n",
3224            this, lastVal.get()));
3226       // Look for value of "Cookie" in the request headers
3227       nsHttpAtom atom = nsHttp::ResolveAtom(token);
3228       nsAutoCString newVal;
3229       bool hasHeader = NS_SUCCEEDED(mRequestHead.GetHeader(atom, newVal));
3230       if (!lastVal.IsEmpty()) {
3231         // value for this header in cache, but no value in request
3232         if (!hasHeader) {
3233           return true;  // yes - response would vary
3234         }
3236         // If this is a cookie-header, stored metadata is not
3237         // the value itself but the hash. So we also hash the
3238         // outgoing value here in order to compare the hashes
3239         nsAutoCString hash;
3240         if (atom == nsHttp::Cookie) {
3241           rv = Hash(newVal.get(), hash);
3242           // If hash failed, be conservative (the cached hash
3243           // exists at this point) and claim response would vary
3244           if (NS_FAILED(rv)) return true;
3245           newVal = hash;
3247           LOG(
3248               ("nsHttpChannel::ResponseWouldVary [this=%p] "
3249                "set-cookie value hashed to %s\n",
3250                this, newVal.get()));
3251         }
3253         if (!newVal.Equals(lastVal)) {
3254           return true;  // yes, response would vary
3255         }
3257       } else if (hasHeader) {  // old value is empty, but newVal is set
3258         return true;
3259       }
3261       // next token...
3262       token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
3263     }
3264   }
3265   return false;
3268 // We need to have an implementation of this function just so that we can keep
3269 // all references to mCallOnResume of type nsHttpChannel:  it's not OK in C++
3270 // to set a member function ptr to  a base class function.
3271 void nsHttpChannel::HandleAsyncAbort() {
3272   HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
3275 //-----------------------------------------------------------------------------
3276 // nsHttpChannel <byte-range>
3277 //-----------------------------------------------------------------------------
3279 bool nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength,
3280                                 bool ignoreMissingPartialLen) const {
3281   bool hasContentEncoding =
3282       mCachedResponseHead->HasHeader(nsHttp::Content_Encoding);
3284   nsAutoCString etag;
3285   Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, etag);
3286   bool hasWeakEtag =
3287       !etag.IsEmpty() && StringBeginsWith(etag, NS_LITERAL_CSTRING("W/"));
3289   return (partialLen < contentLength) &&
3290          (partialLen > 0 || ignoreMissingPartialLen) && !hasContentEncoding &&
3291          !hasWeakEtag && mCachedResponseHead->IsResumable() &&
3292          !mCustomConditionalRequest && !mCachedResponseHead->NoStore();
3295 nsresult nsHttpChannel::MaybeSetupByteRangeRequest(
3296     int64_t partialLen, int64_t contentLength, bool ignoreMissingPartialLen) {
3297   // Be pesimistic
3298   mIsPartialRequest = false;
3300   if (!IsResumable(partialLen, contentLength, ignoreMissingPartialLen))
3301     return NS_ERROR_NOT_RESUMABLE;
3303   // looks like a partial entry we can reuse; add If-Range
3304   // and Range headers.
3305   nsresult rv = SetupByteRangeRequest(partialLen);
3306   if (NS_FAILED(rv)) {
3307     // Make the request unconditional again.
3308     UntieByteRangeRequest();
3309   }
3311   return rv;
3314 nsresult nsHttpChannel::SetupByteRangeRequest(int64_t partialLen) {
3315   // cached content has been found to be partial, add necessary request
3316   // headers to complete cache entry.
3318   // use strongest validator available...
3319   nsAutoCString val;
3320   Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, val);
3321   if (val.IsEmpty())
3322     Unused << mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
3323   if (val.IsEmpty()) {
3324     // if we hit this code it means mCachedResponseHead->IsResumable() is
3325     // either broken or not being called.
3326     MOZ_ASSERT_UNREACHABLE("no cache validator");
3327     mIsPartialRequest = false;
3328     return NS_ERROR_FAILURE;
3329   }
3331   char buf[64];
3332   SprintfLiteral(buf, "bytes=%" PRId64 "-", partialLen);
3334   DebugOnly<nsresult> rv;
3335   rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
3336   MOZ_ASSERT(NS_SUCCEEDED(rv));
3337   rv = mRequestHead.SetHeader(nsHttp::If_Range, val);
3338   MOZ_ASSERT(NS_SUCCEEDED(rv));
3339   mIsPartialRequest = true;
3341   return NS_OK;
3344 void nsHttpChannel::UntieByteRangeRequest() {
3345   DebugOnly<nsresult> rv;
3346   rv = mRequestHead.ClearHeader(nsHttp::Range);
3347   MOZ_ASSERT(NS_SUCCEEDED(rv));
3348   rv = mRequestHead.ClearHeader(nsHttp::If_Range);
3349   MOZ_ASSERT(NS_SUCCEEDED(rv));
3352 nsresult nsHttpChannel::ProcessPartialContent(
3353     const std::function<nsresult(nsHttpChannel *, nsresult)>
3354         &aContinueProcessResponseFunc) {
3355   // ok, we've just received a 206
3356   //
3357   // we need to stream whatever data is in the cache out first, and then
3358   // pick up whatever data is on the wire, writing it into the cache.
3360   LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this));
3362   NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
3363   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
3365   // Make sure to clear bogus content-encodings before looking at the header
3366   ClearBogusContentEncodingIfNeeded();
3368   // Check if the content-encoding we now got is different from the one we
3369   // got before
3370   nsAutoCString contentEncoding, cachedContentEncoding;
3371   // It is possible that there is not such headers
3372   Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
3373   Unused << mCachedResponseHead->GetHeader(nsHttp::Content_Encoding,
3374                                            cachedContentEncoding);
3375   if (PL_strcasecmp(contentEncoding.get(), cachedContentEncoding.get()) != 0) {
3376     Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);
3377     return CallOnStartRequest();
3378   }
3380   nsresult rv;
3382   int64_t cachedContentLength = mCachedResponseHead->ContentLength();
3383   int64_t entitySize = mResponseHead->TotalEntitySize();
3385   nsAutoCString contentRange;
3386   Unused << mResponseHead->GetHeader(nsHttp::Content_Range, contentRange);
3387   LOG(
3388       ("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] "
3389        "original content-length %" PRId64 ", entity-size %" PRId64
3390        ", content-range %s\n",
3391        this, mTransaction.get(), cachedContentLength, entitySize,
3392        contentRange.get()));
3394   if ((entitySize >= 0) && (cachedContentLength >= 0) &&
3395       (entitySize != cachedContentLength)) {
3396     LOG(
3397         ("nsHttpChannel::ProcessPartialContent [this=%p] "
3398          "206 has different total entity size than the content length "
3399          "of the original partially cached entity.\n",
3400          this));
3402     mCacheEntry->AsyncDoom(nullptr);
3403     Cancel(NS_ERROR_CORRUPTED_CONTENT);
3404     return CallOnStartRequest();
3405   }
3407   if (mConcurrentCacheAccess) {
3408     // We started to read cached data sooner than its write has been done.
3409     // But the concurrent write has not finished completely, so we had to
3410     // do a range request.  Now let the content coming from the network
3411     // be presented to consumers and also stored to the cache entry.
3413     rv = InstallCacheListener(mLogicalOffset);
3414     if (NS_FAILED(rv)) return rv;
3416     if (mOfflineCacheEntry) {
3417       rv = InstallOfflineCacheListener(mLogicalOffset);
3418       if (NS_FAILED(rv)) return rv;
3419     }
3420   } else {
3421     // suspend the current transaction
3422     rv = mTransactionPump->Suspend();
3423     if (NS_FAILED(rv)) return rv;
3424   }
3426   // merge any new headers with the cached response headers
3427   rv = mCachedResponseHead->UpdateHeaders(mResponseHead);
3428   if (NS_FAILED(rv)) return rv;
3430   // update the cached response head
3431   nsAutoCString head;
3432   mCachedResponseHead->Flatten(head, true);
3433   rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
3434   if (NS_FAILED(rv)) return rv;
3436   // make the cached response be the current response
3437   mResponseHead = std::move(mCachedResponseHead);
3439   UpdateInhibitPersistentCachingFlag();
3441   rv = UpdateExpirationTime();
3442   if (NS_FAILED(rv)) return rv;
3444   // notify observers interested in looking at a response that has been
3445   // merged with any cached headers (http-on-examine-merged-response).
3446   gHttpHandler->OnExamineMergedResponse(this);
3448   if (mConcurrentCacheAccess) {
3449     mCachedContentIsPartial = false;
3450     // Leave the mConcurrentCacheAccess flag set, we want to use it
3451     // to prevent duplicate OnStartRequest call on the target listener
3452     // in case this channel is canceled before it gets its OnStartRequest
3453     // from the http transaction.
3454     return rv;
3455   }
3457   // Now we continue reading the network response.
3458   // the cached content is valid, although incomplete.
3459   mCachedContentIsValid = true;
3460   return CallOrWaitForResume([aContinueProcessResponseFunc](auto *self) {
3461     nsresult rv = self->ReadFromCache(false);
3462     return aContinueProcessResponseFunc(self, rv);
3463   });
3466 nsresult nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone) {
3467   nsresult rv;
3469   LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
3471   // by default, assume we would have streamed all data or failed...
3472   *streamDone = true;
3474   // setup cache listener to append to cache entry
3475   int64_t size;
3476   rv = mCacheEntry->GetDataSize(&size);
3477   if (NS_FAILED(rv)) return rv;
3479   rv = InstallCacheListener(size);
3480   if (NS_FAILED(rv)) return rv;
3482   // Entry is valid, do it now, after the output stream has been opened,
3483   // otherwise when done earlier, pending readers would consider the cache
3484   // entry still as partial (CacheEntry::GetDataSize would return the partial
3485   // data size) and consumers would do the conditional request again.
3486   rv = mCacheEntry->SetValid();
3487   if (NS_FAILED(rv)) return rv;
3489   // need to track the logical offset of the data being sent to our listener
3490   mLogicalOffset = size;
3492   // we're now completing the cached content, so we can clear this flag.
3493   // this puts us in the state of a regular download.
3494   mCachedContentIsPartial = false;
3495   // The cache input stream pump is finished, we do not need it any more.
3496   // (see bug 1313923)
3497   mCachePump = nullptr;
3499   // resume the transaction if it exists, otherwise the pipe contained the
3500   // remaining part of the document and we've now streamed all of the data.
3501   if (mTransactionPump) {
3502     rv = mTransactionPump->Resume();
3503     if (NS_SUCCEEDED(rv)) *streamDone = false;
3504   } else
3505     MOZ_ASSERT_UNREACHABLE("no transaction");
3506   return rv;
3509 //-----------------------------------------------------------------------------
3510 // nsHttpChannel <cache>
3511 //-----------------------------------------------------------------------------
3513 bool nsHttpChannel::ShouldBypassProcessNotModified() {
3514   if (mCustomConditionalRequest) {
3515     LOG(("Bypassing ProcessNotModified due to custom conditional headers"));
3516     return true;
3517   }
3519   if (!mDidReval) {
3520     LOG(
3521         ("Server returned a 304 response even though we did not send a "
3522          "conditional request"));
3523     return true;
3524   }
3526   return false;
3529 nsresult nsHttpChannel::ProcessNotModified(
3530     const std::function<nsresult(nsHttpChannel *, nsresult)>
3531         &aContinueProcessResponseFunc) {
3532   nsresult rv;
3534   LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this));
3536   // Assert ShouldBypassProcessNotModified() has been checked before call to
3537   // ProcessNotModified().
3538   MOZ_ASSERT(!ShouldBypassProcessNotModified());
3540   MOZ_ASSERT(mCachedResponseHead);
3541   MOZ_ASSERT(mCacheEntry);
3542   NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED);
3544   // If the 304 response contains a Last-Modified different than the
3545   // one in our cache that is pretty suspicious and is, in at least the
3546   // case of bug 716840, a sign of the server having previously corrupted
3547   // our cache with a bad response. Take the minor step here of just dooming
3548   // that cache entry so there is a fighting chance of getting things on the
3549   // right track.
3551   nsAutoCString lastModifiedCached;
3552   nsAutoCString lastModified304;
3554   rv =
3555       mCachedResponseHead->GetHeader(nsHttp::Last_Modified, lastModifiedCached);
3556   if (NS_SUCCEEDED(rv)) {
3557     rv = mResponseHead->GetHeader(nsHttp::Last_Modified, lastModified304);
3558   }
3560   if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) {
3561     LOG(
3562         ("Cache Entry and 304 Last-Modified Headers Do Not Match "
3563          "[%s] and [%s]\n",
3564          lastModifiedCached.get(), lastModified304.get()));
3566     mCacheEntry->AsyncDoom(nullptr);
3567     Telemetry::Accumulate(Telemetry::CACHE_LM_INCONSISTENT, true);
3568   }
3570   // merge any new headers with the cached response headers
3571   rv = mCachedResponseHead->UpdateHeaders(mResponseHead);
3572   if (NS_FAILED(rv)) return rv;
3574   // update the cached response head
3575   nsAutoCString head;
3576   mCachedResponseHead->Flatten(head, true);
3577   rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
3578   if (NS_FAILED(rv)) return rv;
3580   // make the cached response be the current response
3581   mResponseHead = std::move(mCachedResponseHead);
3583   UpdateInhibitPersistentCachingFlag();
3585   rv = UpdateExpirationTime();
3586   if (NS_FAILED(rv)) return rv;
3588   rv = AddCacheEntryHeaders(mCacheEntry);
3589   if (NS_FAILED(rv)) return rv;
3591   // notify observers interested in looking at a reponse that has been
3592   // merged with any cached headers
3593   gHttpHandler->OnExamineMergedResponse(this);
3595   mCachedContentIsValid = true;
3597   // Tell other consumers the entry is OK to use
3598   rv = mCacheEntry->SetValid();
3599   if (NS_FAILED(rv)) return rv;
3601   return CallOrWaitForResume([aContinueProcessResponseFunc](auto *self) {
3602     nsresult rv = self->ReadFromCache(false);
3603     return aContinueProcessResponseFunc(self, rv);
3604   });
3607 nsresult nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback) {
3608   LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
3609   nsresult rv;
3611   *waitingForRedirectCallback = false;
3612   mFallingBack = false;
3614   // At this point a load has failed (either due to network problems
3615   // or an error returned on the server).  Perform an application
3616   // cache fallback if we have a URI to fall back to.
3617   if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) {
3618     LOG(("  choosing not to fallback [%p,%s,%d]", mApplicationCache.get(),
3619          mFallbackKey.get(), mFallbackChannel));
3620     return NS_OK;
3621   }
3623   // Make sure the fallback entry hasn't been marked as a foreign
3624   // entry.
3625   uint32_t fallbackEntryType;
3626   rv = mApplicationCache->GetTypes(mFallbackKey, &fallbackEntryType);
3627   NS_ENSURE_SUCCESS(rv, rv);
3629   if (fallbackEntryType & nsIApplicationCache::ITEM_FOREIGN) {
3630     // This cache points to a fallback that refers to a different
3631     // manifest.  Refuse to fall back.
3632     return NS_OK;
3633   }
3635   if (!IsInSubpathOfAppCacheManifest(mApplicationCache, mFallbackKey)) {
3636     // Refuse to fallback if the fallback key is not contained in the same
3637     // path as the cache manifest.
3638     return NS_OK;
3639   }
3641   MOZ_ASSERT(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK,
3642              "Fallback entry not marked correctly!");
3644   // Kill any offline cache entry, and disable offline caching for the
3645   // fallback.
3646   if (mOfflineCacheEntry) {
3647     mOfflineCacheEntry->AsyncDoom(nullptr);
3648     mOfflineCacheEntry = nullptr;
3649   }
3651   mApplicationCacheForWrite = nullptr;
3652   mOfflineCacheEntry = nullptr;
3654   // Close the current cache entry.
3655   CloseCacheEntry(true);
3657   // Create a new channel to load the fallback entry.
3658   RefPtr<nsIChannel> newChannel;
3659   rv = gHttpHandler->NewChannel2(mURI, mLoadInfo, getter_AddRefs(newChannel));
3660   NS_ENSURE_SUCCESS(rv, rv);
3662   uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
3663   rv = SetupReplacementChannel(mURI, newChannel, true, redirectFlags);
3664   NS_ENSURE_SUCCESS(rv, rv);
3666   // Make sure the new channel loads from the fallback key.
3667   nsCOMPtr<nsIHttpChannelInternal> httpInternal =
3668       do_QueryInterface(newChannel, &rv);
3669   NS_ENSURE_SUCCESS(rv, rv);
3671   rv = httpInternal->SetupFallbackChannel(mFallbackKey.get());
3672   NS_ENSURE_SUCCESS(rv, rv);
3674   // ... and fallbacks should only load from the cache.
3675   uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE | LOAD_ONLY_FROM_CACHE;
3676   rv = newChannel->SetLoadFlags(newLoadFlags);
3678   // Inform consumers about this fake redirect
3679   mRedirectChannel = newChannel;
3681   PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
3682   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
3684   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
3686   if (NS_FAILED(rv)) {
3687     AutoRedirectVetoNotifier notifier(this);
3688     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
3689     return rv;
3690   }
3692   // Indicate we are now waiting for the asynchronous redirect callback
3693   // if all went OK.
3694   *waitingForRedirectCallback = true;
3695   return NS_OK;
3698 nsresult nsHttpChannel::ContinueProcessFallback(nsresult rv) {
3699   AutoRedirectVetoNotifier notifier(this);
3701   if (NS_FAILED(rv)) return rv;
3703   MOZ_ASSERT(mRedirectChannel, "No redirect channel?");
3705   // Make sure to do this after we received redirect veto answer,
3706   // i.e. after all sinks had been notified
3707   mRedirectChannel->SetOriginalURI(mOriginalURI);
3709   rv = mRedirectChannel->AsyncOpen(mListener);
3710   NS_ENSURE_SUCCESS(rv, rv);
3712   if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
3713     MaybeWarnAboutAppCache();
3714   }
3716   // close down this channel
3717   Cancel(NS_BINDING_REDIRECTED);
3719   notifier.RedirectSucceeded();
3721   ReleaseListeners();
3723   mFallingBack = true;
3725   return NS_OK;
3728 // Determines if a request is a byte range request for a subrange,
3729 // i.e. is a byte range request, but not a 0- byte range request.
3730 static bool IsSubRangeRequest(nsHttpRequestHead &aRequestHead) {
3731   nsAutoCString byteRange;
3732   if (NS_FAILED(aRequestHead.GetHeader(nsHttp::Range, byteRange))) {
3733     return false;
3734   }
3735   return !byteRange.EqualsLiteral("bytes=0-");
3738 nsresult nsHttpChannel::OpenCacheEntry(bool isHttps) {
3739   // Drop this flag here
3740   mConcurrentCacheAccess = 0;
3742   mLoadedFromApplicationCache = false;
3744   LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
3746   // make sure we're not abusing this function
3747   MOZ_ASSERT(!mCacheEntry, "cache entry already open");
3749   if (mRequestHead.IsPost()) {
3750     // If the post id is already set then this is an attempt to replay
3751     // a post transaction via the cache.  Otherwise, we need a unique
3752     // post id for this transaction.
3753     if (mPostID == 0) mPostID = gHttpHandler->GenerateUniqueID();
3754   } else if (!mRequestHead.IsGet() && !mRequestHead.IsHead()) {
3755     // don't use the cache for other types of requests
3756     return NS_OK;
3757   }
3759   // Pick up an application cache from the notification
3760   // callbacks if available and if we are not an intercepted channel.
3761   if (!mApplicationCache && mInheritApplicationCache) {
3762     nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
3763     GetCallback(appCacheContainer);
3765     if (appCacheContainer) {
3766       appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
3767     }
3768   }
3770   return OpenCacheEntryInternal(isHttps, mApplicationCache, true);
3773 nsresult nsHttpChannel::OpenCacheEntryInternal(
3774     bool isHttps, nsIApplicationCache *applicationCache,
3775     bool allowApplicationCache) {
3776   MOZ_ASSERT_IF(!allowApplicationCache, !applicationCache);
3778   nsresult rv;
3780   if (mResuming) {
3781     // We don't support caching for requests initiated
3782     // via nsIResumableChannel.
3783     return NS_OK;
3784   }
3786   // Don't cache byte range requests which are subranges, only cache 0-
3787   // byte range requests.
3788   if (IsSubRangeRequest(mRequestHead)) {
3789     return NS_OK;
3790   }
3792   // Handle correctly mCacheEntriesToWaitFor
3793   AutoCacheWaitFlags waitFlags(this);
3795   nsAutoCString cacheKey;
3796   nsAutoCString extension;
3798   nsCOMPtr<nsICacheStorageService> cacheStorageService(
3799       services::GetCacheStorageService());
3800   if (!cacheStorageService) {
3801     return NS_ERROR_NOT_AVAILABLE;
3802   }
3804   nsCOMPtr<nsICacheStorage> cacheStorage;
3805   nsCOMPtr<nsIURI> openURI;
3806   if (!mFallbackKey.IsEmpty() && mFallbackChannel) {
3807     // This is a fallback channel, open fallback URI instead
3808     rv = NS_NewURI(getter_AddRefs(openURI), mFallbackKey);
3809     NS_ENSURE_SUCCESS(rv, rv);
3810   } else {
3811     openURI = mURI;
3812   }
3814   RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
3815   if (!info) {
3816     return NS_ERROR_FAILURE;
3817   }
3819   uint32_t cacheEntryOpenFlags;
3820   bool offline = gIOService->IsOffline();
3822   bool maybeRCWN = false;
3824   nsAutoCString cacheControlRequestHeader;
3825   Unused << mRequestHead.GetHeader(nsHttp::Cache_Control,
3826                                    cacheControlRequestHeader);
3827   CacheControlParser cacheControlRequest(cacheControlRequestHeader);
3828   if (cacheControlRequest.NoStore()) {
3829     goto bypassCacheEntryOpen;
3830   }
3832   if (offline || (mLoadFlags & INHIBIT_CACHING)) {
3833     if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) {
3834       goto bypassCacheEntryOpen;
3835     }
3836     cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY;
3837     mCacheEntryIsReadOnly = true;
3838   } else if (BYPASS_LOCAL_CACHE(mLoadFlags) && !applicationCache) {
3839     cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
3840   } else {
3841     cacheEntryOpenFlags =
3842         nsICacheStorage::OPEN_NORMALLY | nsICacheStorage::CHECK_MULTITHREADED;
3843   }
3845   // Remember the request is a custom conditional request so that we can
3846   // process any 304 response correctly.
3847   mCustomConditionalRequest =
3848       mRequestHead.HasHeader(nsHttp::If_Modified_Since) ||
3849       mRequestHead.HasHeader(nsHttp::If_None_Match) ||
3850       mRequestHead.HasHeader(nsHttp::If_Unmodified_Since) ||
3851       mRequestHead.HasHeader(nsHttp::If_Match) ||
3852       mRequestHead.HasHeader(nsHttp::If_Range);
3854   if (!mPostID && applicationCache) {
3855     rv = cacheStorageService->AppCacheStorage(info, applicationCache,
3856                                               getter_AddRefs(cacheStorage));
3857   } else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
3858     rv = cacheStorageService->MemoryCacheStorage(
3859         info,  // ? choose app cache as well...
3860         getter_AddRefs(cacheStorage));
3861   } else if (mPinCacheContent) {
3862     rv = cacheStorageService->PinningCacheStorage(info,
3863                                                   getter_AddRefs(cacheStorage));
3864   } else {
3865     bool lookupAppCache =
3866         (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE)) &&
3867         !mPostID && MOZ_LIKELY(allowApplicationCache);
3868     // Try to race only if we use disk cache storage and we don't lookup
3869     // app cache first
3870     maybeRCWN = (!lookupAppCache) && mRequestHead.IsSafeMethod();
3871     rv = cacheStorageService->DiskCacheStorage(info, lookupAppCache,
3872                                                getter_AddRefs(cacheStorage));
3873   }
3874   NS_ENSURE_SUCCESS(rv, rv);
3876   if ((mClassOfService & nsIClassOfService::Leader) ||
3877       (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI))
3878     cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
3880   // Only for backward compatibility with the old cache back end.
3881   // When removed, remove the flags and related code snippets.
3882   if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY)
3883     cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY;
3885   if (mPostID) {
3886     extension.Append(nsPrintfCString("%d", mPostID));
3887   }
3888   if (mTRR) {
3889     extension.Append("TRR");
3890   }
3892   if (mIsThirdPartyTrackingResource &&
3893       !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(this, mURI,
3894                                                                nullptr)) {
3895     nsCOMPtr<nsIURI> topWindowURI;
3896     rv = GetTopWindowURI(getter_AddRefs(topWindowURI));
3897     bool isDocument = false;
3898     if (NS_FAILED(rv) && NS_SUCCEEDED(GetIsMainDocumentChannel(&isDocument)) &&
3899         isDocument) {
3900       // For top-level documents, use the document channel's origin to compute
3901       // the unique storage space identifier instead of the top Window URI.
3902       rv = NS_GetFinalChannelURI(this, getter_AddRefs(topWindowURI));
3903       NS_ENSURE_SUCCESS(rv, rv);
3904     }
3906     nsAutoString topWindowOrigin;
3907     rv = nsContentUtils::GetUTFOrigin(topWindowURI ? topWindowURI : mURI,
3908                                       topWindowOrigin);
3909     NS_ENSURE_SUCCESS(rv, rv);
3911     extension.Append("-unique:");
3912     extension.Append(NS_ConvertUTF16toUTF8(topWindowOrigin));
3913   }
3915   mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
3916   mCacheQueueSizeWhenOpen =
3917       CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
3919   if (sRCWNEnabled && maybeRCWN && !mApplicationCacheForWrite) {
3920     bool hasAltData = false;
3921     uint32_t sizeInKb = 0;
3922     rv = cacheStorage->GetCacheIndexEntryAttrs(openURI, extension, &hasAltData,
3923                                                &sizeInKb);
3925     // We will attempt to race the network vs the cache if we've found
3926     // this entry in the cache index, and it has appropriate attributes
3927     // (doesn't have alt-data, and has a small size)
3928     if (NS_SUCCEEDED(rv) && !hasAltData &&
3929         sizeInKb < sRCWNSmallResourceSizeKB) {
3930       MaybeRaceCacheWithNetwork();
3931     }
3932   }
3934   if (!mCacheOpenDelay) {
3935     MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
3936     if (mNetworkTriggered) {
3937       mRaceCacheWithNetwork = sRCWNEnabled;
3938     }
3939     rv = cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags,
3940                                     this);
3941   } else {
3942     // We pass `this` explicitly as a parameter due to the raw pointer
3943     // to refcounted object in lambda analysis.
3944     mCacheOpenFunc = [openURI, extension, cacheEntryOpenFlags,
3945                       cacheStorage](nsHttpChannel *self) -> void {
3946       MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
3947       if (self->mNetworkTriggered) {
3948         self->mRaceCacheWithNetwork = true;
3949       }
3950       cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, self);
3951     };
3953     // calls nsHttpChannel::Notify after `mCacheOpenDelay` milliseconds
3954     NS_NewTimerWithCallback(getter_AddRefs(mCacheOpenTimer), this,
3955                             mCacheOpenDelay, nsITimer::TYPE_ONE_SHOT);
3956   }
3957   NS_ENSURE_SUCCESS(rv, rv);
3959   waitFlags.Keep(WAIT_FOR_CACHE_ENTRY);
3961 bypassCacheEntryOpen:
3962   if (!mApplicationCacheForWrite || !allowApplicationCache) return NS_OK;
3964   // If there is an app cache to write to, open the entry right now in parallel.
3966   // make sure we're not abusing this function
3967   MOZ_ASSERT(!mOfflineCacheEntry, "cache entry already open");
3969   if (offline) {
3970     // only put things in the offline cache while online
3971     return NS_OK;
3972   }
3974   if (mLoadFlags & INHIBIT_CACHING) {
3975     // respect demand not to cache
3976     return NS_OK;
3977   }
3979   if (!mRequestHead.IsGet()) {
3980     // only cache complete documents offline
3981     return NS_OK;
3982   }
3984   rv = cacheStorageService->AppCacheStorage(info, mApplicationCacheForWrite,
3985                                             getter_AddRefs(cacheStorage));
3986   NS_ENSURE_SUCCESS(rv, rv);
3988   rv = cacheStorage->AsyncOpenURI(mURI, EmptyCString(),
3989                                   nsICacheStorage::OPEN_TRUNCATE, this);
3990   NS_ENSURE_SUCCESS(rv, rv);
3992   waitFlags.Keep(WAIT_FOR_OFFLINE_CACHE_ENTRY);
3994   return NS_OK;
3997 nsresult nsHttpChannel::CheckPartial(nsICacheEntry *aEntry, int64_t *aSize,
3998                                      int64_t *aContentLength) {
3999   return nsHttp::CheckPartial(
4000       aEntry, aSize, aContentLength,
4001       mCachedResponseHead ? mCachedResponseHead : mResponseHead);
4004 void nsHttpChannel::UntieValidationRequest() {
4005   DebugOnly<nsresult> rv;
4006   // Make the request unconditional again.
4007   rv = mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
4008   MOZ_ASSERT(NS_SUCCEEDED(rv));
4009   rv = mRequestHead.ClearHeader(nsHttp::If_None_Match);
4010   MOZ_ASSERT(NS_SUCCEEDED(rv));
4011   rv = mRequestHead.ClearHeader(nsHttp::ETag);
4012   MOZ_ASSERT(NS_SUCCEEDED(rv));
4015 NS_IMETHODIMP
4016 nsHttpChannel::OnCacheEntryCheck(nsICacheEntry *entry,
4017                                  nsIApplicationCache *appCache,
4018                                  uint32_t *aResult) {
4019   nsresult rv = NS_OK;
4021   LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]", this,
4022        entry));
4024   mozilla::MutexAutoLock lock(mRCWNLock);
4026   if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_NETWORK) {
4027     LOG(
4028         ("Not using cached response because we've already got one from the "
4029          "network\n"));
4030     *aResult = ENTRY_NOT_WANTED;
4032     // Net-win indicates that mOnStartRequestTimestamp is from net.
4033     int64_t savedTime =
4034         (TimeStamp::Now() - mOnStartRequestTimestamp).ToMilliseconds();
4035     Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME,
4036                           savedTime);
4037     return NS_OK;
4038   } else if (mRaceCacheWithNetwork &&
4039              mFirstResponseSource == RESPONSE_PENDING) {
4040     mOnCacheEntryCheckTimestamp = TimeStamp::Now();
4041   }
4043   nsAutoCString cacheControlRequestHeader;
4044   Unused << mRequestHead.GetHeader(nsHttp::Cache_Control,
4045                                    cacheControlRequestHeader);
4046   CacheControlParser cacheControlRequest(cacheControlRequestHeader);
4048   if (cacheControlRequest.NoStore()) {
4049     LOG(
4050         ("Not using cached response based on no-store request cache "
4051          "directive\n"));
4052     *aResult = ENTRY_NOT_WANTED;
4053     return NS_OK;
4054   }
4056   // Be pessimistic: assume the cache entry has no useful data.
4057   *aResult = ENTRY_WANTED;
4058   mCachedContentIsValid = false;
4060   nsCString buf;
4062   // Get the method that was used to generate the cached response
4063   rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
4064   NS_ENSURE_SUCCESS(rv, rv);
4066   bool methodWasHead = buf.EqualsLiteral("HEAD");
4067   bool methodWasGet = buf.EqualsLiteral("GET");
4069   if (methodWasHead) {
4070     // The cached response does not contain an entity.  We can only reuse
4071     // the response if the current request is also HEAD.
4072     if (!mRequestHead.IsHead()) {
4073       return NS_OK;
4074     }
4075   }
4076   buf.Adopt(nullptr);
4078   // We'll need this value in later computations...
4079   uint32_t lastModifiedTime;
4080   rv = entry->GetLastModified(&lastModifiedTime);
4081   NS_ENSURE_SUCCESS(rv, rv);
4083   // Determine if this is the first time that this cache entry
4084   // has been accessed during this session.
4085   bool fromPreviousSession =
4086       (gHttpHandler->SessionStartTime() > lastModifiedTime);
4088   // Get the cached HTTP response headers
4089   mCachedResponseHead = new nsHttpResponseHead();
4091   rv = nsHttp::GetHttpResponseHeadFromCacheEntry(entry, mCachedResponseHead);
4092   NS_ENSURE_SUCCESS(rv, rv);
4094   bool isCachedRedirect = WillRedirect(mCachedResponseHead);
4096   // Do not return 304 responses from the cache, and also do not return
4097   // any other non-redirect 3xx responses from the cache (see bug 759043).
4098   NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) || isCachedRedirect,
4099                  NS_ERROR_ABORT);
4101   if (mCachedResponseHead->NoStore() && mCacheEntryIsReadOnly) {
4102     // This prevents loading no-store responses when navigating back
4103     // while the browser is set to work offline.
4104     LOG(("  entry loading as read-only but is no-store, set INHIBIT_CACHING"));
4105     mLoadFlags |= nsIRequest::INHIBIT_CACHING;
4106   }
4108   // Don't bother to validate items that are read-only,
4109   // unless they are read-only because of INHIBIT_CACHING or because
4110   // we're updating the offline cache.
4111   // Don't bother to validate if this is a fallback entry.
4112   if (!mApplicationCacheForWrite &&
4113       (appCache || (mCacheEntryIsReadOnly &&
4114                     !(mLoadFlags & nsIRequest::INHIBIT_CACHING)))) {
4115     if (!appCache) {
4116       int64_t size, contentLength;
4117       rv = CheckPartial(entry, &size, &contentLength);
4118       NS_ENSURE_SUCCESS(rv, rv);
4120       if (contentLength != int64_t(-1) && contentLength != size) {
4121         *aResult = ENTRY_NOT_WANTED;
4122         return NS_OK;
4123       }
4124     }
4126     rv = OpenCacheInputStream(entry, true, !!appCache);
4127     if (NS_SUCCEEDED(rv)) {
4128       mCachedContentIsValid = true;
4129       entry->MaybeMarkValid();
4130     }
4131     return rv;
4132   }
4134   bool wantCompleteEntry = false;
4136   if (!methodWasHead && !isCachedRedirect) {
4137     // If the cached content-length is set and it does not match the data
4138     // size of the cached content, then the cached response is partial...
4139     // either we need to issue a byte range request or we need to refetch
4140     // the entire document.
4141     //
4142     // We exclude redirects from this check because we (usually) strip the
4143     // entity when we store the cache entry, and even if we didn't, we
4144     // always ignore a cached redirect's entity anyway. See bug 759043.
4145     int64_t size, contentLength;
4146     rv = CheckPartial(entry, &size, &contentLength);
4147     NS_ENSURE_SUCCESS(rv, rv);
4149     if (size == int64_t(-1)) {
4150       LOG(("  write is in progress"));
4151       if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
4152         LOG(
4153             ("  not interested in the entry, "
4154              "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified"));
4156         *aResult = ENTRY_NOT_WANTED;
4157         return NS_OK;
4158       }
4160       // Ignore !(size > 0) from the resumability condition
4161       if (!IsResumable(size, contentLength, true)) {
4162         if (IsNavigation()) {
4163           LOG(
4164               ("  bypassing wait for the entry, "
4165                "this is a navigational load"));
4166           *aResult = ENTRY_NOT_WANTED;
4167           return NS_OK;
4168         }
4170         LOG(
4171             ("  wait for entry completion, "
4172              "response is not resumable"));
4174         wantCompleteEntry = true;
4175       } else {
4176         mConcurrentCacheAccess = 1;
4177       }
4178     } else if (contentLength != int64_t(-1) && contentLength != size) {
4179       LOG(
4180           ("Cached data size does not match the Content-Length header "
4181            "[content-length=%" PRId64 " size=%" PRId64 "]\n",
4182            contentLength, size));
4184       rv = MaybeSetupByteRangeRequest(size, contentLength);
4185       mCachedContentIsPartial = NS_SUCCEEDED(rv) && mIsPartialRequest;
4186       if (mCachedContentIsPartial) {
4187         rv = OpenCacheInputStream(entry, false, !!appCache);
4188         if (NS_FAILED(rv)) {
4189           UntieByteRangeRequest();
4190           return rv;
4191         }
4193         *aResult = ENTRY_NEEDS_REVALIDATION;
4194         return NS_OK;
4195       }
4197       if (size == 0 && mCacheOnlyMetadata) {
4198         // Don't break cache entry load when the entry's data size
4199         // is 0 and mCacheOnlyMetadata flag is set. In that case we
4200         // want to proceed since the LOAD_ONLY_IF_MODIFIED flag is
4201         // also set.
4202         MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
4203       } else {
4204         return rv;
4205       }
4206     }
4207   }
4209   bool isHttps = false;
4210   rv = mURI->SchemeIs("https", &isHttps);
4211   NS_ENSURE_SUCCESS(rv, rv);
4213   bool doValidation = false;
4214   bool canAddImsHeader = true;
4216   bool isForcedValid = false;
4217   entry->GetIsForcedValid(&isForcedValid);
4219   bool weaklyFramed, isImmutable;
4220   nsHttp::DetermineFramingAndImmutability(entry, mCachedResponseHead, isHttps,
4221                                           &weaklyFramed, &isImmutable);
4223   // Cached entry is not the entity we request (see bug #633743)
4224   if (ResponseWouldVary(entry)) {
4225     LOG(("Validating based on Vary headers returning TRUE\n"));
4226     canAddImsHeader = false;
4227     doValidation = true;
4228   } else {
4229     doValidation = nsHttp::ValidationRequired(
4230         isForcedValid, mCachedResponseHead, mLoadFlags, mAllowStaleCacheContent,
4231         isImmutable, mCustomConditionalRequest, mRequestHead, entry,
4232         cacheControlRequest, fromPreviousSession);
4233   }
4235   // If a content signature is expected to be valid in this load,
4236   // set doValidation to force a signature check.
4237   if (!doValidation && mLoadInfo && mLoadInfo->GetVerifySignedContent()) {
4238     doValidation = true;
4239   }
4241   nsAutoCString requestedETag;
4242   if (!doValidation &&
4243       NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag)) &&
4244       (methodWasGet || methodWasHead)) {
4245     nsAutoCString cachedETag;
4246     Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, cachedETag);
4247     if (!cachedETag.IsEmpty() &&
4248         (StringBeginsWith(cachedETag, NS_LITERAL_CSTRING("W/")) ||
4249          !requestedETag.Equals(cachedETag))) {
4250       // User has defined If-Match header, if the cached entry is not
4251       // matching the provided header value or the cached ETag is weak,
4252       // force validation.
4253       doValidation = true;
4254     }
4255   }
4257   // Previous error should not be propagated.
4258   rv = NS_OK;
4260   if (!doValidation) {
4261     //
4262     // Check the authorization headers used to generate the cache entry.
4263     // We must validate the cache entry if:
4264     //
4265     // 1) the cache entry was generated prior to this session w/
4266     //    credentials (see bug 103402).
4267     // 2) the cache entry was generated w/o credentials, but would now
4268     //    require credentials (see bug 96705).
4269     //
4270     // NOTE: this does not apply to proxy authentication.
4271     //
4272     entry->GetMetaDataElement("auth", getter_Copies(buf));
4273     doValidation =
4274         (fromPreviousSession && !buf.IsEmpty()) ||
4275         (buf.IsEmpty() && mRequestHead.HasHeader(nsHttp::Authorization));
4276   }
4278   // Bug #561276: We maintain a chain of cache-keys which returns cached
4279   // 3xx-responses (redirects) in order to detect cycles. If a cycle is
4280   // found, ignore the cached response and hit the net. Otherwise, use
4281   // the cached response and add the cache-key to the chain. Note that
4282   // a limited number of redirects (cached or not) is allowed and is
4283   // enforced independently of this mechanism
4284   if (!doValidation && isCachedRedirect) {
4285     nsAutoCString cacheKey;
4286     rv = GenerateCacheKey(mPostID, cacheKey);
4287     MOZ_ASSERT(NS_SUCCEEDED(rv));
4289     if (!mRedirectedCachekeys)
4290       mRedirectedCachekeys = new nsTArray<nsCString>();
4291     else if (mRedirectedCachekeys->Contains(cacheKey))
4292       doValidation = true;
4294     LOG(("Redirection-chain %s key %s\n",
4295          doValidation ? "contains" : "does not contain", cacheKey.get()));
4297     // Append cacheKey if not in the chain already
4298     if (!doValidation) mRedirectedCachekeys->AppendElement(cacheKey);
4299   }
4301   mCachedContentIsValid = !doValidation;
4303   if (doValidation) {
4304     //
4305     // now, we are definitely going to issue a HTTP request to the server.
4306     // make it conditional if possible.
4307     //
4308     // do not attempt to validate no-store content, since servers will not
4309     // expect it to be cached.  (we only keep it in our cache for the
4310     // purposes of back/forward, etc.)
4311     //
4312     // the request method MUST be either GET or HEAD (see bug 175641) and
4313     // the cached response code must be < 400
4314     //
4315     // the cached content must not be weakly framed or marked immutable
4316     //
4317     // do not override conditional headers when consumer has defined its own
4318     if (!mCachedResponseHead->NoStore() &&
4319         (mRequestHead.IsGet() || mRequestHead.IsHead()) &&
4320         !mCustomConditionalRequest && !weaklyFramed && !isImmutable &&
4321         (mCachedResponseHead->Status() < 400)) {
4322       if (mConcurrentCacheAccess) {
4323         // In case of concurrent read and also validation request we
4324         // must wait for the current writer to close the output stream
4325         // first.  Otherwise, when the writer's job would have been interrupted
4326         // before all the data were downloaded, we'd have to do a range request
4327         // which would be a second request in line during this channel's
4328         // life-time.  nsHttpChannel is not designed to do that, so rather
4329         // turn off concurrent read and wait for entry's completion.
4330         // Then only re-validation or range-re-validation request will go out.
4331         mConcurrentCacheAccess = 0;
4332         // This will cause that OnCacheEntryCheck is called again with the same
4333         // entry after the writer is done.
4334         wantCompleteEntry = true;
4335       } else {
4336         nsAutoCString val;
4337         // Add If-Modified-Since header if a Last-Modified was given
4338         // and we are allowed to do this (see bugs 510359 and 269303)
4339         if (canAddImsHeader) {
4340           Unused << mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
4341           if (!val.IsEmpty()) {
4342             rv = mRequestHead.SetHeader(nsHttp::If_Modified_Since, val);
4343             MOZ_ASSERT(NS_SUCCEEDED(rv));
4344           }
4345         }
4346         // Add If-None-Match header if an ETag was given in the response
4347         Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, val);
4348         if (!val.IsEmpty()) {
4349           rv = mRequestHead.SetHeader(nsHttp::If_None_Match, val);
4350           MOZ_ASSERT(NS_SUCCEEDED(rv));
4351         }
4352         mDidReval = true;
4353       }
4354     }
4355   }
4357   if (mCachedContentIsValid || mDidReval) {
4358     rv = OpenCacheInputStream(entry, mCachedContentIsValid, !!appCache);
4359     if (NS_FAILED(rv)) {
4360       // If we can't get the entity then we have to act as though we
4361       // don't have the cache entry.
4362       if (mDidReval) {
4363         UntieValidationRequest();
4364         mDidReval = false;
4365       }
4366       mCachedContentIsValid = false;
4367     }
4368   }
4370   if (mDidReval)
4371     *aResult = ENTRY_NEEDS_REVALIDATION;
4372   else if (wantCompleteEntry)
4373     *aResult = RECHECK_AFTER_WRITE_FINISHED;
4374   else {
4375     *aResult = ENTRY_WANTED;
4376   }
4378   if (mCachedContentIsValid) {
4379     entry->MaybeMarkValid();
4380   }
4382   LOG(
4383       ("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d "
4384        "result=%d]\n",
4385        this, doValidation, *aResult));
4386   return rv;
4389 NS_IMETHODIMP
4390 nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry *entry, bool aNew,
4391                                      nsIApplicationCache *aAppCache,
4392                                      nsresult status) {
4393   MOZ_ASSERT(NS_IsMainThread());
4395   nsresult rv;
4397   LOG(
4398       ("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
4399        "new=%d appcache=%p status=%" PRIx32
4400        " mAppCache=%p mAppCacheForWrite=%p]\n",
4401        this, entry, aNew, aAppCache, static_cast<uint32_t>(status),
4402        mApplicationCache.get(), mApplicationCacheForWrite.get()));
4404   // if the channel's already fired onStopRequest, then we should ignore
4405   // this event.
4406   if (!mIsPending) {
4407     mCacheInputStream.CloseAndRelease();
4408     return NS_OK;
4409   }
4411   rv = OnCacheEntryAvailableInternal(entry, aNew, aAppCache, status);
4412   if (NS_FAILED(rv)) {
4413     CloseCacheEntry(false);
4414     if (mRaceCacheWithNetwork && mNetworkTriggered &&
4415         mFirstResponseSource != RESPONSE_FROM_CACHE) {
4416       // Ignore the error if we're racing cache with network and the cache
4417       // didn't win, The network part will handle cancelation or any other
4418       // error. Otherwise we could end up calling the listener twice, see
4419       // bug 1397593.
4420       LOG(
4421           ("  not calling AsyncAbort() because we're racing cache with "
4422            "network"));
4423     } else {
4424       Unused << AsyncAbort(rv);
4425     }
4426   }
4428   return NS_OK;
4431 nsresult nsHttpChannel::OnCacheEntryAvailableInternal(
4432     nsICacheEntry *entry, bool aNew, nsIApplicationCache *aAppCache,
4433     nsresult status) {
4434   nsresult rv;
4436   if (mCanceled) {
4437     LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n", this,
4438          static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
4439     return mStatus;
4440   }
4442   if (mIgnoreCacheEntry) {
4443     if (!entry || aNew) {
4444       // We use this flag later to decide whether to report
4445       // LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent. We didn't have
4446       // an usable entry, so drop the flag.
4447       mIgnoreCacheEntry = false;
4448     }
4449     entry = nullptr;
4450     status = NS_ERROR_NOT_AVAILABLE;
4451   }
4453   if (aAppCache) {
4454     if (mApplicationCache == aAppCache && !mCacheEntry) {
4455       rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
4456     } else if (mApplicationCacheForWrite == aAppCache && aNew &&
4457                !mOfflineCacheEntry) {
4458       rv = OnOfflineCacheEntryForWritingAvailable(entry, aAppCache, status);
4459     } else {
4460       rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
4461     }
4462   } else {
4463     rv = OnNormalCacheEntryAvailable(entry, aNew, status);
4464   }
4466   if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
4467     // If we have a fallback URI (and we're not already
4468     // falling back), process the fallback asynchronously.
4469     if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
4470       return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
4471     }
4473     return NS_ERROR_DOCUMENT_NOT_CACHED;
4474   }
4476   if (NS_FAILED(rv)) {
4477     return rv;
4478   }
4480   // We may be waiting for more callbacks...
4481   if (AwaitingCacheCallbacks()) {
4482     return NS_OK;
4483   }
4485   if (mRaceCacheWithNetwork && ((mCacheEntry && !mCachedContentIsValid &&
4486                                  (mDidReval || mCachedContentIsPartial)) ||
4487                                 mIgnoreCacheEntry)) {
4488     // We won't send the conditional request because the unconditional
4489     // request was already sent (see bug 1377223).
4490     AccumulateCategorical(
4491         Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent);
4492   }
4494   if (mRaceCacheWithNetwork && mCachedContentIsValid) {
4495     Unused << ReadFromCache(true);
4496   }
4498   return TriggerNetwork();
4501 nsresult nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry,
4502                                                     bool aNew,
4503                                                     nsresult aEntryStatus) {
4504   mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
4506   if (NS_FAILED(aEntryStatus) || aNew) {
4507     // Make sure this flag is dropped.  It may happen the entry is doomed
4508     // between OnCacheEntryCheck and OnCacheEntryAvailable.
4509     mCachedContentIsValid = false;
4511     // From the same reason remove any conditional headers added
4512     // in OnCacheEntryCheck.
4513     if (mDidReval) {
4514       LOG(("  Removing conditional request headers"));
4515       UntieValidationRequest();
4516       mDidReval = false;
4517     }
4519     if (mCachedContentIsPartial) {
4520       LOG(("  Removing byte range request headers"));
4521       UntieByteRangeRequest();
4522       mCachedContentIsPartial = false;
4523     }
4525     if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
4526       // if this channel is only allowed to pull from the cache, then
4527       // we must fail if we were unable to open a cache entry for read.
4528       return NS_ERROR_DOCUMENT_NOT_CACHED;
4529     }
4530   }
4532   if (NS_SUCCEEDED(aEntryStatus)) {
4533     mCacheEntry = aEntry;
4534     mCacheEntryIsWriteOnly = aNew;
4536     if (!aNew && !mAsyncOpenTime.IsNull()) {
4537       // We use microseconds for IO operations. For consistency let's use
4538       // microseconds here too.
4539       uint32_t duration = (TimeStamp::Now() - mAsyncOpenTime).ToMicroseconds();
4540       bool isSlow = false;
4541       if ((mCacheOpenWithPriority &&
4542            mCacheQueueSizeWhenOpen >= sRCWNQueueSizePriority) ||
4543           (!mCacheOpenWithPriority &&
4544            mCacheQueueSizeWhenOpen >= sRCWNQueueSizeNormal)) {
4545         isSlow = true;
4546       }
4547       CacheFileUtils::CachePerfStats::AddValue(
4548           CacheFileUtils::CachePerfStats::ENTRY_OPEN, duration, isSlow);
4549     }
4551     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
4552       Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, false);
4553     }
4554   }
4556   return NS_OK;
4559 nsresult nsHttpChannel::OnOfflineCacheEntryAvailable(
4560     nsICacheEntry *aEntry, bool aNew, nsIApplicationCache *aAppCache,
4561     nsresult aEntryStatus) {
4562   MOZ_ASSERT(!mApplicationCache || aAppCache == mApplicationCache);
4563   MOZ_ASSERT(!aNew || !aEntry || mApplicationCacheForWrite);
4565   mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
4567   nsresult rv;
4569   if (NS_SUCCEEDED(aEntryStatus)) {
4570     if (!mApplicationCache) {
4571       mApplicationCache = aAppCache;
4572     }
4574     // We successfully opened an offline cache session and the entry,
4575     // so indicate we will load from the offline cache.
4576     mLoadedFromApplicationCache = true;
4577     mCacheEntryIsReadOnly = true;
4578     mCacheEntry = aEntry;
4579     mCacheEntryIsWriteOnly = false;
4581     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI && !mApplicationCacheForWrite) {
4582       MaybeWarnAboutAppCache();
4583     }
4585     return NS_OK;
4586   }
4588   if (!mApplicationCacheForWrite && !mFallbackChannel) {
4589     if (!mApplicationCache) {
4590       mApplicationCache = aAppCache;
4591     }
4593     // Check for namespace match.
4594     nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
4595     rv = mApplicationCache->GetMatchingNamespace(
4596         mSpec, getter_AddRefs(namespaceEntry));
4597     NS_ENSURE_SUCCESS(rv, rv);
4599     uint32_t namespaceType = 0;
4600     if (!namespaceEntry ||
4601         NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
4602         (namespaceType & (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
4603                           nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) ==
4604             0) {
4605       // When loading from an application cache, only items
4606       // on the whitelist or matching a
4607       // fallback namespace should hit the network...
4608       mLoadFlags |= LOAD_ONLY_FROM_CACHE;
4610       // ... and if there were an application cache entry,
4611       // we would have found it earlier.
4612       return NS_ERROR_CACHE_KEY_NOT_FOUND;
4613     }
4615     if (namespaceType & nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
4616       nsAutoCString namespaceSpec;
4617       rv = namespaceEntry->GetNamespaceSpec(namespaceSpec);
4618       NS_ENSURE_SUCCESS(rv, rv);
4620       // This prevents fallback attacks injected by an insecure subdirectory
4621       // for the whole origin (or a parent directory).
4622       if (!IsInSubpathOfAppCacheManifest(mApplicationCache, namespaceSpec)) {
4623         return NS_OK;
4624       }
4626       rv = namespaceEntry->GetData(mFallbackKey);
4627       NS_ENSURE_SUCCESS(rv, rv);
4628     }
4630     if (namespaceType & nsIApplicationCacheNamespace::NAMESPACE_BYPASS) {
4631       LOG(
4632           ("nsHttpChannel::OnOfflineCacheEntryAvailable this=%p, URL matches "
4633            "NETWORK,"
4634            " looking for a regular cache entry",
4635            this));
4637       bool isHttps = false;
4638       rv = mURI->SchemeIs("https", &isHttps);
4639       NS_ENSURE_SUCCESS(rv, rv);
4641       rv = OpenCacheEntryInternal(isHttps, nullptr,
4642                                   false /* don't allow appcache lookups */);
4643       if (NS_FAILED(rv)) {
4644         // Don't let this fail when cache entry can't be synchronously open.
4645         // We want to go forward even without a regular cache entry.
4646         return NS_OK;
4647       }
4648     }
4649   }
4651   return NS_OK;
4654 nsresult nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(
4655     nsICacheEntry *aEntry, nsIApplicationCache *aAppCache,
4656     nsresult aEntryStatus) {
4657   MOZ_ASSERT(mApplicationCacheForWrite &&
4658              aAppCache == mApplicationCacheForWrite);
4660   mCacheEntriesToWaitFor &= ~WAIT_FOR_OFFLINE_CACHE_ENTRY;
4662   if (NS_SUCCEEDED(aEntryStatus)) {
4663     mOfflineCacheEntry = aEntry;
4664     if (NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))) {
4665       mOfflineCacheLastModifiedTime = 0;
4666     }
4667   }
4669   return aEntryStatus;
4672 // Generates the proper cache-key for this instance of nsHttpChannel
4673 nsresult nsHttpChannel::GenerateCacheKey(uint32_t postID,
4674                                          nsACString &cacheKey) {
4675   AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(), postID,
4676                    cacheKey);
4677   return NS_OK;
4680 // Assembles a cache-key from the given pieces of information and |mLoadFlags|
4681 void nsHttpChannel::AssembleCacheKey(const char *spec, uint32_t postID,
4682                                      nsACString &cacheKey) {
4683   cacheKey.Truncate();
4685   if (mLoadFlags & LOAD_ANONYMOUS) {
4686     cacheKey.AssignLiteral("anon&");
4687   }
4689   if (postID) {
4690     char buf[32];
4691     SprintfLiteral(buf, "id=%x&", postID);
4692     cacheKey.Append(buf);
4693   }
4695   if (!cacheKey.IsEmpty()) {
4696     cacheKey.AppendLiteral("uri=");
4697   }
4699   // Strip any trailing #ref from the URL before using it as the key
4700   const char *p = strchr(spec, '#');
4701   if (p)
4702     cacheKey.Append(spec, p - spec);
4703   else
4704     cacheKey.Append(spec);
4707 nsresult DoUpdateExpirationTime(nsHttpChannel *aSelf,
4708                                 nsICacheEntry *aCacheEntry,
4709                                 nsHttpResponseHead *aResponseHead,
4710                                 uint32_t &aExpirationTime) {
4711   MOZ_ASSERT(aExpirationTime == 0);
4712   NS_ENSURE_TRUE(aResponseHead, NS_ERROR_FAILURE);
4714   nsresult rv;
4716   if (!aResponseHead->MustValidate()) {
4717     uint32_t freshnessLifetime = 0;
4719     rv = aResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
4720     if (NS_FAILED(rv)) return rv;
4722     if (freshnessLifetime > 0) {
4723       uint32_t now = NowInSeconds(), currentAge = 0;
4725       rv = aResponseHead->ComputeCurrentAge(now, aSelf->GetRequestTime(),
4726                                             &currentAge);
4727       if (NS_FAILED(rv)) return rv;
4729       LOG(("freshnessLifetime = %u, currentAge = %u\n", freshnessLifetime,
4730            currentAge));
4732       if (freshnessLifetime > currentAge) {
4733         uint32_t timeRemaining = freshnessLifetime - currentAge;
4734         // be careful... now + timeRemaining may overflow
4735         if (now + timeRemaining < now)
4736           aExpirationTime = uint32_t(-1);
4737         else
4738           aExpirationTime = now + timeRemaining;
4739       } else
4740         aExpirationTime = 0;
4741     }
4742   }
4744   rv = aCacheEntry->SetExpirationTime(aExpirationTime);
4745   NS_ENSURE_SUCCESS(rv, rv);
4747   return rv;
4750 // UpdateExpirationTime is called when a new response comes in from the server.
4751 // It updates the stored response-time and sets the expiration time on the
4752 // cache entry.
4754 // From section 13.2.4 of RFC2616, we compute expiration time as follows:
4756 //    timeRemaining = freshnessLifetime - currentAge
4757 //    expirationTime = now + timeRemaining
4759 nsresult nsHttpChannel::UpdateExpirationTime() {
4760   uint32_t expirationTime = 0;
4761   nsresult rv =
4762       DoUpdateExpirationTime(this, mCacheEntry, mResponseHead, expirationTime);
4763   NS_ENSURE_SUCCESS(rv, rv);
4765   if (mOfflineCacheEntry) {
4766     rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
4767     NS_ENSURE_SUCCESS(rv, rv);
4768   }
4770   return NS_OK;
4773 bool nsHttpChannel::ShouldUpdateOfflineCacheEntry() {
4774   if (!mApplicationCacheForWrite || !mOfflineCacheEntry) {
4775     return false;
4776   }
4778   // if we're updating the cache entry, update the offline cache entry too
4779   if (mCacheEntry && mCacheEntryIsWriteOnly) {
4780     return true;
4781   }
4783   // if there's nothing in the offline cache, add it
4784   if (mOfflineCacheEntry) {
4785     return true;
4786   }
4788   // if the document is newer than the offline entry, update it
4789   uint32_t docLastModifiedTime;
4790   nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
4791   if (NS_FAILED(rv)) {
4792     return true;
4793   }
4795   if (mOfflineCacheLastModifiedTime == 0) {
4796     return false;
4797   }
4799   if (docLastModifiedTime > mOfflineCacheLastModifiedTime) {
4800     return true;
4801   }
4803   return false;
4806 nsresult nsHttpChannel::OpenCacheInputStream(nsICacheEntry *cacheEntry,
4807                                              bool startBuffering,
4808                                              bool checkingAppCacheEntry) {
4809   nsresult rv;
4811   bool isHttps = false;
4812   rv = mURI->SchemeIs("https", &isHttps);
4813   NS_ENSURE_SUCCESS(rv, rv);
4815   if (isHttps) {
4816     rv = cacheEntry->GetSecurityInfo(getter_AddRefs(mCachedSecurityInfo));
4817     if (NS_FAILED(rv)) {
4818       LOG(("failed to parse security-info [channel=%p, entry=%p]", this,
4819            cacheEntry));
4820       NS_WARNING("failed to parse security-info");
4821       cacheEntry->AsyncDoom(nullptr);
4822       return rv;
4823     }
4825     // XXX: We should not be skilling this check in the offline cache
4826     // case, but we have to do so now to work around bug 794507.
4827     bool mustHaveSecurityInfo =
4828         !mLoadedFromApplicationCache && !checkingAppCacheEntry;
4829     MOZ_ASSERT(mCachedSecurityInfo || !mustHaveSecurityInfo);
4830     if (!mCachedSecurityInfo && mustHaveSecurityInfo) {
4831       LOG(
4832           ("mCacheEntry->GetSecurityInfo returned success but did not "
4833            "return the security info [channel=%p, entry=%p]",
4834            this, cacheEntry));
4835       cacheEntry->AsyncDoom(nullptr);
4836       return NS_ERROR_UNEXPECTED;  // XXX error code
4837     }
4838   }
4840   // Keep the conditions below in sync with the conditions in ReadFromCache.
4842   rv = NS_OK;
4844   if (WillRedirect(mCachedResponseHead)) {
4845     // Do not even try to read the entity for a redirect because we do not
4846     // return an entity to the application when we process redirects.
4847     LOG(("Will skip read of cached redirect entity\n"));
4848     return NS_OK;
4849   }
4851   if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
4852       !mCachedContentIsPartial) {
4853     // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
4854     // cached entity.
4855     if (!mApplicationCacheForWrite) {
4856       LOG(
4857           ("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
4858            "load flag\n"));
4859       return NS_OK;
4860     }
4862     // If offline caching has been requested and the offline cache needs
4863     // updating, we must complete the call even if the main cache entry
4864     // is up to date. We don't know yet for sure whether the offline
4865     // cache needs updating because at this point we haven't opened it
4866     // for writing yet, so we have to start reading the cached entity now
4867     // just in case.
4868     LOG(
4869         ("May skip read from cache based on LOAD_ONLY_IF_MODIFIED "
4870          "load flag\n"));
4871   }
4873   // Open an input stream for the entity, so that the call to OpenInputStream
4874   // happens off the main thread.
4875   nsCOMPtr<nsIInputStream> stream;
4877   // If an alternate representation was requested, try to open the alt
4878   // input stream.
4879   // If the entry has a "is-from-child" metadata, then only open the altdata
4880   // stream if the consumer is also from child.
4881   bool altDataFromChild = false;
4882   {
4883     nsCString value;
4884     rv = cacheEntry->GetMetaDataElement("alt-data-from-child",
4885                                         getter_Copies(value));
4886     altDataFromChild = !value.IsEmpty();
4887   }
4889   nsAutoCString altDataType;
4890   Unused << cacheEntry->GetAltDataType(altDataType);
4892   nsAutoCString contentType;
4893   mCachedResponseHead->ContentType(contentType);
4895   bool foundAltData = false;
4896   if (!altDataType.IsEmpty() && !mPreferredCachedAltDataTypes.IsEmpty() &&
4897       altDataFromChild == mAltDataForChild) {
4898     for (auto &pref : mPreferredCachedAltDataTypes) {
4899       if (mozilla::Get<0>(pref) == altDataType &&
4900           (mozilla::Get<1>(pref).IsEmpty() ||
4901            mozilla::Get<1>(pref) == contentType)) {
4902         foundAltData = true;
4903         break;
4904       }
4905     }
4906   }
4907   if (foundAltData) {
4908     rv = cacheEntry->OpenAlternativeInputStream(altDataType,
4909                                                 getter_AddRefs(stream));
4910     if (NS_SUCCEEDED(rv)) {
4911       LOG(("Opened alt-data input stream type=%s", altDataType.get()));
4912       // We have succeeded.
4913       mAvailableCachedAltDataType = altDataType;
4914       // Set the correct data size on the channel.
4915       int64_t altDataSize;
4916       if (NS_SUCCEEDED(cacheEntry->GetAltDataSize(&altDataSize))) {
4917         mAltDataLength = altDataSize;
4918       }
4919     }
4920   }
4922   if (!stream) {
4923     rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream));
4924   }
4926   if (NS_FAILED(rv)) {
4927     LOG(
4928         ("Failed to open cache input stream [channel=%p, "
4929          "mCacheEntry=%p]",
4930          this, cacheEntry));
4931     return rv;
4932   }
4934   if (startBuffering) {
4935     bool nonBlocking;
4936     rv = stream->IsNonBlocking(&nonBlocking);
4937     if (NS_SUCCEEDED(rv) && nonBlocking) startBuffering = false;
4938   }
4940   if (!startBuffering) {
4941     // Bypass wrapping the input stream for the new cache back-end since
4942     // nsIStreamTransportService expects a blocking stream.  Preloading of
4943     // the data must be done on the level of the cache backend, internally.
4944     //
4945     // We do not connect the stream to the stream transport service if we
4946     // have to validate the entry with the server. If we did, we would get
4947     // into a race condition between the stream transport service reading
4948     // the existing contents and the opening of the cache entry's output
4949     // stream to write the new contents in the case where we get a non-304
4950     // response.
4951     LOG(
4952         ("Opened cache input stream without buffering [channel=%p, "
4953          "mCacheEntry=%p, stream=%p]",
4954          this, cacheEntry, stream.get()));
4955     mCacheInputStream.takeOver(stream);
4956     return rv;
4957   }
4959   // Have the stream transport service start reading the entity on one of its
4960   // background threads.
4962   nsCOMPtr<nsITransport> transport;
4963   nsCOMPtr<nsIInputStream> wrapper;
4965   nsCOMPtr<nsIStreamTransportService> sts(
4966       services::GetStreamTransportService());
4967   rv = sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
4968   if (NS_SUCCEEDED(rv)) {
4969     rv = sts->CreateInputTransport(stream, true, getter_AddRefs(transport));
4970   }
4971   if (NS_SUCCEEDED(rv)) {
4972     rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
4973   }
4974   if (NS_SUCCEEDED(rv)) {
4975     LOG(
4976         ("Opened cache input stream [channel=%p, wrapper=%p, "
4977          "transport=%p, stream=%p]",
4978          this, wrapper.get(), transport.get(), stream.get()));
4979   } else {
4980     LOG(
4981         ("Failed to open cache input stream [channel=%p, "
4982          "wrapper=%p, transport=%p, stream=%p]",
4983          this, wrapper.get(), transport.get(), stream.get()));
4985     stream->Close();
4986     return rv;
4987   }
4989   mCacheInputStream.takeOver(wrapper);
4991   return NS_OK;
4994 // Actually process the cached response that we started to handle in CheckCache
4995 // and/or StartBufferingCachedEntity.
4996 nsresult nsHttpChannel::ReadFromCache(bool alreadyMarkedValid) {
4997   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
4998   NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE);
4999   NS_ENSURE_TRUE(!mCachePump, NS_OK);  // already opened
5001   LOG(
5002       ("nsHttpChannel::ReadFromCache [this=%p] "
5003        "Using cached copy of: %s\n",
5004        this, mSpec.get()));
5006   // When racing the cache with the network with a timer, and we get data from
5007   // the cache, we should prevent the timer from triggering a network request.
5008   if (mNetworkTriggerTimer) {
5009     mNetworkTriggerTimer->Cancel();
5010     mNetworkTriggerTimer = nullptr;
5011   }
5013   if (mRaceCacheWithNetwork) {
5014     MOZ_ASSERT(mFirstResponseSource != RESPONSE_FROM_CACHE);
5015     if (mFirstResponseSource == RESPONSE_PENDING) {
5016       LOG(("First response from cache\n"));
5017       mFirstResponseSource = RESPONSE_FROM_CACHE;
5019       // Cancel the transaction because we will serve the request from the cache
5020       CancelNetworkRequest(NS_BINDING_ABORTED);
5021       if (mTransactionPump && mSuspendCount) {
5022         uint32_t suspendCount = mSuspendCount;
5023         while (suspendCount--) {
5024           mTransactionPump->Resume();
5025         }
5026       }
5027       mTransaction = nullptr;
5028       mTransactionPump = nullptr;
5029     } else {
5030       MOZ_ASSERT(mFirstResponseSource == RESPONSE_FROM_NETWORK);
5031       LOG(
5032           ("Skipping read from cache because first response was from "
5033            "network\n"));
5035       if (!mOnCacheEntryCheckTimestamp.IsNull()) {
5036         TimeStamp currentTime = TimeStamp::Now();
5037         int64_t savedTime =
5038             (currentTime - mOnStartRequestTimestamp).ToMilliseconds();
5039         Telemetry::Accumulate(
5040             Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME, savedTime);
5042         int64_t diffTime =
5043             (currentTime - mOnCacheEntryCheckTimestamp).ToMilliseconds();
5044         Telemetry::Accumulate(
5045             Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_OCEC_ON_START_DIFF,
5046             diffTime);
5047       }
5048       return NS_OK;
5049     }
5050   }
5052   if (mCachedResponseHead) mResponseHead = std::move(mCachedResponseHead);
5054   UpdateInhibitPersistentCachingFlag();
5056   // if we don't already have security info, try to get it from the cache
5057   // entry. there are two cases to consider here: 1) we are just reading
5058   // from the cache, or 2) this may be due to a 304 not modified response,
5059   // in which case we could have security info from a socket transport.
5060   if (!mSecurityInfo) mSecurityInfo = mCachedSecurityInfo;
5062   if (!alreadyMarkedValid && !mCachedContentIsPartial) {
5063     // We validated the entry, and we have write access to the cache, so
5064     // mark the cache entry as valid in order to allow others access to
5065     // this cache entry.
5066     //
5067     // TODO: This should be done asynchronously so we don't take the cache
5068     // service lock on the main thread.
5069     mCacheEntry->MaybeMarkValid();
5070   }
5072   nsresult rv;
5074   // Keep the conditions below in sync with the conditions in
5075   // StartBufferingCachedEntity.
5077   if (WillRedirect(mResponseHead)) {
5078     // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
5079     // to avoid event dispatching latency.
5080     MOZ_ASSERT(!mCacheInputStream);
5081     LOG(("Skipping skip read of cached redirect entity\n"));
5082     return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
5083   }
5085   if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
5086     if (!mApplicationCacheForWrite) {
5087       LOG(
5088           ("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
5089            "load flag\n"));
5090       MOZ_ASSERT(!mCacheInputStream);
5091       // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
5092       // here, to avoid event dispatching latency.
5093       return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
5094     }
5096     if (!ShouldUpdateOfflineCacheEntry()) {
5097       LOG(
5098           ("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
5099            "load flag (mApplicationCacheForWrite not null case)\n"));
5100       mCacheInputStream.CloseAndRelease();
5101       // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
5102       // here, to avoid event dispatching latency.
5103       return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
5104     }
5105   }
5107   MOZ_ASSERT(mCacheInputStream);
5108   if (!mCacheInputStream) {
5109     NS_ERROR(
5110         "mCacheInputStream is null but we're expecting to "
5111         "be able to read from it.");
5112     return NS_ERROR_UNEXPECTED;
5113   }
5115   nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget();
5117   rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream, 0, 0,
5118                                  true);
5119   if (NS_FAILED(rv)) {
5120     inputStream->Close();
5121     return rv;
5122   }
5124   rv = mCachePump->AsyncRead(this, nullptr);
5125   if (NS_FAILED(rv)) return rv;
5127   if (mTimingEnabled) mCacheReadStart = TimeStamp::Now();
5129   uint32_t suspendCount = mSuspendCount;
5130   if (mAsyncResumePending) {
5131     LOG(
5132         ("  Suspend()'ing cache pump once because of async resume pending"
5133          ", sc=%u, pump=%p, this=%p",
5134          suspendCount, mCachePump.get(), this));
5135     ++suspendCount;
5136   }
5137   while (suspendCount--) {
5138     mCachePump->Suspend();
5139   }
5141   return NS_OK;
5144 void nsHttpChannel::CloseCacheEntry(bool doomOnFailure) {
5145   mCacheInputStream.CloseAndRelease();
5147   if (!mCacheEntry) return;
5149   LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32
5150        " mCacheEntryIsWriteOnly=%x",
5151        this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)),
5152        mCacheEntryIsWriteOnly));
5154   // If we have begun to create or replace a cache entry, and that cache
5155   // entry is not complete and not resumable, then it needs to be doomed.
5156   // Otherwise, CheckCache will make the mistake of thinking that the
5157   // partial cache entry is complete.
5159   bool doom = false;
5160   if (mInitedCacheEntry) {
5161     MOZ_ASSERT(mResponseHead, "oops");
5162     if (NS_FAILED(mStatus) && doomOnFailure && mCacheEntryIsWriteOnly &&
5163         !mResponseHead->IsResumable())
5164       doom = true;
5165   } else if (mCacheEntryIsWriteOnly)
5166     doom = true;
5168   if (doom) {
5169     LOG(("  dooming cache entry!!"));
5170     mCacheEntry->AsyncDoom(nullptr);
5171   } else {
5172     // Store updated security info, makes cached EV status race less likely
5173     // (see bug 1040086)
5174     if (mSecurityInfo) mCacheEntry->SetSecurityInfo(mSecurityInfo);
5175   }
5177   mCachedResponseHead = nullptr;
5179   mCachePump = nullptr;
5180   // This releases the entry for other consumers to use.
5181   // We call Dismiss() in case someone still keeps a reference
5182   // to this entry handle.
5183   mCacheEntry->Dismiss();
5184   mCacheEntry = nullptr;
5185   mCacheEntryIsWriteOnly = false;
5186   mInitedCacheEntry = false;
5189 void nsHttpChannel::CloseOfflineCacheEntry() {
5190   if (!mOfflineCacheEntry) return;
5192   LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]", this));
5194   if (NS_FAILED(mStatus)) {
5195     mOfflineCacheEntry->AsyncDoom(nullptr);
5196   } else {
5197     bool succeeded;
5198     if (NS_SUCCEEDED(GetRequestSucceeded(&succeeded)) && !succeeded)
5199       mOfflineCacheEntry->AsyncDoom(nullptr);
5200   }
5202   mOfflineCacheEntry = nullptr;
5205 // Initialize the cache entry for writing.
5206 //  - finalize storage policy
5207 //  - store security info
5208 //  - update expiration time
5209 //  - store headers and other meta data
5210 nsresult nsHttpChannel::InitCacheEntry() {
5211   nsresult rv;
5213   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED);
5214   // if only reading, nothing to be done here.
5215   if (mCacheEntryIsReadOnly) return NS_OK;
5217   // Don't cache the response again if already cached...
5218   if (mCachedContentIsValid) return NS_OK;
5220   LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n", this,
5221        mCacheEntry.get()));
5223   bool recreate = !mCacheEntryIsWriteOnly;
5224   bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING;
5226   if (!recreate && dontPersist) {
5227     // If the current entry is persistent but we inhibit peristence
5228     // then force recreation of the entry as memory/only.
5229     rv = mCacheEntry->GetPersistent(&recreate);
5230     if (NS_FAILED(rv)) return rv;
5231   }
5233   if (recreate) {
5234     LOG(
5235         ("  we have a ready entry, but reading it again from the server -> "
5236          "recreating cache entry\n"));
5237     // clean the altData cache and reset this to avoid wrong content length
5238     mAvailableCachedAltDataType.Truncate();
5240     nsCOMPtr<nsICacheEntry> currentEntry;
5241     currentEntry.swap(mCacheEntry);
5242     rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry));
5243     if (NS_FAILED(rv)) {
5244       LOG(("  recreation failed, the response will not be cached"));
5245       return NS_OK;
5246     }
5248     mCacheEntryIsWriteOnly = true;
5249   }
5251   // Set the expiration time for this cache entry
5252   rv = UpdateExpirationTime();
5253   if (NS_FAILED(rv)) return rv;
5255   // mark this weakly framed until a response body is seen
5256   mCacheEntry->SetMetaDataElement("strongly-framed", "0");
5258   rv = AddCacheEntryHeaders(mCacheEntry);
5259   if (NS_FAILED(rv)) return rv;
5261   mInitedCacheEntry = true;
5263   // Don't perform the check when writing (doesn't make sense)
5264   mConcurrentCacheAccess = 0;
5266   return NS_OK;
5269 void nsHttpChannel::UpdateInhibitPersistentCachingFlag() {
5270   // The no-store directive within the 'Cache-Control:' header indicates
5271   // that we must not store the response in a persistent cache.
5272   if (mResponseHead->NoStore()) mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
5274   // Only cache SSL content on disk if the pref is set
5275   bool isHttps;
5276   if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
5277       NS_SUCCEEDED(mURI->SchemeIs("https", &isHttps)) && isHttps) {
5278     mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
5279   }
5282 nsresult nsHttpChannel::InitOfflineCacheEntry() {
5283   // This function can be called even when we fail to connect (bug 551990)
5285   if (!mOfflineCacheEntry) {
5286     return NS_OK;
5287   }
5289   if (!mResponseHead || mResponseHead->NoStore()) {
5290     if (mResponseHead && mResponseHead->NoStore()) {
5291       mOfflineCacheEntry->AsyncDoom(nullptr);
5292     }
5294     CloseOfflineCacheEntry();
5296     if (mResponseHead && mResponseHead->NoStore()) {
5297       return NS_ERROR_NOT_AVAILABLE;
5298     }
5300     return NS_OK;
5301   }
5303   // This entry's expiration time should match the main entry's expiration
5304   // time.  UpdateExpirationTime() will keep it in sync once the offline
5305   // cache entry has been created.
5306   if (mCacheEntry) {
5307     uint32_t expirationTime;
5308     nsresult rv = mCacheEntry->GetExpirationTime(&expirationTime);
5309     NS_ENSURE_SUCCESS(rv, rv);
5311     mOfflineCacheEntry->SetExpirationTime(expirationTime);
5312   }
5314   return AddCacheEntryHeaders(mOfflineCacheEntry);
5317 nsresult DoAddCacheEntryHeaders(nsHttpChannel *self, nsICacheEntry *entry,
5318                                 nsHttpRequestHead *requestHead,
5319                                 nsHttpResponseHead *responseHead,
5320                                 nsISupports *securityInfo) {
5321   nsresult rv;
5323   LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", self));
5324   // Store secure data in memory only
5325   if (securityInfo) entry->SetSecurityInfo(securityInfo);
5327   // Store the HTTP request method with the cache entry so we can distinguish
5328   // for example GET and HEAD responses.
5329   nsAutoCString method;
5330   requestHead->Method(method);
5331   rv = entry->SetMetaDataElement("request-method", method.get());
5332   if (NS_FAILED(rv)) return rv;
5334   // Store the HTTP authorization scheme used if any...
5335   rv = StoreAuthorizationMetaData(entry, requestHead);
5336   if (NS_FAILED(rv)) return rv;
5338   // Iterate over the headers listed in the Vary response header, and
5339   // store the value of the corresponding request header so we can verify
5340   // that it has not varied when we try to re-use the cached response at
5341   // a later time.  Take care to store "Cookie" headers only as hashes
5342   // due to security considerations and the fact that they can be pretty
5343   // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.
5344   //
5345   // NOTE: if "Vary: accept, cookie", then we will store the "accept" header
5346   // in the cache.  we could try to avoid needlessly storing the "accept"
5347   // header in this case, but it doesn't seem worth the extra code to perform
5348   // the check.
5349   {
5350     nsAutoCString buf, metaKey;
5351     Unused << responseHead->GetHeader(nsHttp::Vary, buf);
5352     if (!buf.IsEmpty()) {
5353       NS_NAMED_LITERAL_CSTRING(prefix, "request-");
5355       char *bufData = buf.BeginWriting();  // going to munge buf
5356       char *token = nsCRT::strtok(bufData, NS_HTTP_HEADER_SEPS, &bufData);
5357       while (token) {
5358         LOG(
5359             ("nsHttpChannel::AddCacheEntryHeaders [this=%p] "
5360              "processing %s",
5361              self, token));
5362         if (*token != '*') {
5363           nsHttpAtom atom = nsHttp::ResolveAtom(token);
5364           nsAutoCString val;
5365           nsAutoCString hash;
5366           if (NS_SUCCEEDED(requestHead->GetHeader(atom, val))) {
5367             // If cookie-header, store a hash of the value
5368             if (atom == nsHttp::Cookie) {
5369               LOG(
5370                   ("nsHttpChannel::AddCacheEntryHeaders [this=%p] "
5371                    "cookie-value %s",
5372                    self, val.get()));
5373               rv = Hash(val.get(), hash);
5374               // If hash failed, store a string not very likely
5375               // to be the result of subsequent hashes
5376               if (NS_FAILED(rv)) {
5377                 val = NS_LITERAL_CSTRING("<hash failed>");
5378               } else {
5379                 val = hash;
5380               }
5382               LOG(("   hashed to %s\n", val.get()));
5383             }
5385             // build cache meta data key and set meta data element...
5386             metaKey = prefix + nsDependentCString(token);
5387             entry->SetMetaDataElement(metaKey.get(), val.get());
5388           } else {
5389             LOG(
5390                 ("nsHttpChannel::AddCacheEntryHeaders [this=%p] "
5391                  "clearing metadata for %s",
5392                  self, token));
5393             metaKey = prefix + nsDependentCString(token);
5394             entry->SetMetaDataElement(metaKey.get(), nullptr);
5395           }
5396         }
5397         token = nsCRT::strtok(bufData, NS_HTTP_HEADER_SEPS, &bufData);
5398       }
5399     }
5400   }
5402   // Store the received HTTP head with the cache entry as an element of
5403   // the meta data.
5404   nsAutoCString head;
5405   responseHead->Flatten(head, true);
5406   rv = entry->SetMetaDataElement("response-head", head.get());
5407   if (NS_FAILED(rv)) return rv;
5408   head.Truncate();
5409   responseHead->FlattenNetworkOriginalHeaders(head);
5410   rv = entry->SetMetaDataElement("original-response-headers", head.get());
5411   if (NS_FAILED(rv)) return rv;
5413   // Indicate we have successfully finished setting metadata on the cache entry.
5414   rv = entry->MetaDataReady();
5416   return rv;
5419 nsresult nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry *entry) {
5420   return DoAddCacheEntryHeaders(this, entry, &mRequestHead, mResponseHead,
5421                                 mSecurityInfo);
5424 inline void GetAuthType(const char *challenge, nsCString &authType) {
5425   const char *p;
5427   // get the challenge type
5428   if ((p = strchr(challenge, ' ')) != nullptr)
5429     authType.Assign(challenge, p - challenge);
5430   else
5431     authType.Assign(challenge);
5434 nsresult StoreAuthorizationMetaData(nsICacheEntry *entry,
5435                                     nsHttpRequestHead *requestHead) {
5436   // Not applicable to proxy authorization...
5437   nsAutoCString val;
5438   if (NS_FAILED(requestHead->GetHeader(nsHttp::Authorization, val))) {
5439     return NS_OK;
5440   }
5442   // eg. [Basic realm="wally world"]
5443   nsAutoCString buf;
5444   GetAuthType(val.get(), buf);
5445   return entry->SetMetaDataElement("auth", buf.get());
5448 // Finalize the cache entry
5449 //  - may need to rewrite response headers if any headers changed
5450 //  - may need to recalculate the expiration time if any headers changed
5451 //  - called only for freshly written cache entries
5452 nsresult nsHttpChannel::FinalizeCacheEntry() {
5453   LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this));
5455   // Don't update this meta-data on 304
5456   if (mStronglyFramed && !mCachedContentIsValid && mCacheEntry) {
5457     LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p] Is Strongly Framed\n",
5458          this));
5459     mCacheEntry->SetMetaDataElement("strongly-framed", "1");
5460   }
5462   if (mResponseHead && mResponseHeadersModified) {
5463     // Set the expiration time for this cache entry
5464     nsresult rv = UpdateExpirationTime();
5465     if (NS_FAILED(rv)) return rv;
5466   }
5467   return NS_OK;
5470 // Open an output stream to the cache entry and insert a listener tee into
5471 // the chain of response listeners.
5472 nsresult nsHttpChannel::InstallCacheListener(int64_t offset) {
5473   nsresult rv;
5475   LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
5477   MOZ_ASSERT(mCacheEntry);
5478   MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial ||
5479              mRaceCacheWithNetwork);
5480   MOZ_ASSERT(mListener);
5482   nsAutoCString contentEncoding, contentType;
5483   Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
5484   mResponseHead->ContentType(contentType);
5485   // If the content is compressible and the server has not compressed it,
5486   // mark the cache entry for compression.
5487   if (contentEncoding.IsEmpty() &&
5488       (contentType.EqualsLiteral(TEXT_HTML) ||
5489        contentType.EqualsLiteral(TEXT_PLAIN) ||
5490        contentType.EqualsLiteral(TEXT_CSS) ||
5491        contentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
5492        contentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
5493        contentType.EqualsLiteral(TEXT_XML) ||
5494        contentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
5495        contentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
5496        contentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
5497        contentType.EqualsLiteral(APPLICATION_XHTML_XML))) {
5498     rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0");
5499     if (NS_FAILED(rv)) {
5500       LOG(("unable to mark cache entry for compression"));
5501     }
5502   }
5504   LOG(("Trading cache input stream for output stream [channel=%p]", this));
5506   // We must close the input stream first because cache entries do not
5507   // correctly handle having an output stream and input streams open at
5508   // the same time.
5509   mCacheInputStream.CloseAndRelease();
5511   int64_t predictedSize = mResponseHead->TotalEntitySize();
5512   if (predictedSize != -1) {
5513     predictedSize -= offset;
5514   }
5516   nsCOMPtr<nsIOutputStream> out;
5517   rv =
5518       mCacheEntry->OpenOutputStream(offset, predictedSize, getter_AddRefs(out));
5519   if (rv == NS_ERROR_NOT_AVAILABLE) {
5520     LOG(("  entry doomed, not writing it [channel=%p]", this));
5521     // Entry is already doomed.
5522     // This may happen when expiration time is set to past and the entry
5523     // has been removed by the background eviction logic.
5524     return NS_OK;
5525   }
5526   if (rv == NS_ERROR_FILE_TOO_BIG) {
5527     LOG(("  entry would exceed max allowed size, not writing it [channel=%p]",
5528          this));
5529     return NS_OK;
5530   }
5531   if (NS_FAILED(rv)) return rv;
5533   if (mCacheOnlyMetadata) {
5534     LOG(("Not storing content, cacheOnlyMetadata set"));
5535     // We must open and then close the output stream of the cache entry.
5536     // This way we indicate the content has been written (despite with zero
5537     // length) and the entry is now in the ready state with "having data".
5539     out->Close();
5540     return NS_OK;
5541   }
5543   // XXX disk cache does not support overlapped i/o yet
5544 #if 0
5545     // Mark entry valid inorder to allow simultaneous reading...
5546     rv = mCacheEntry->MarkValid();
5547     if (NS_FAILED(rv)) return rv;
5548 #endif
5550   nsCOMPtr<nsIStreamListenerTee> tee =
5551       do_CreateInstance(kStreamListenerTeeCID, &rv);
5552   if (NS_FAILED(rv)) return rv;
5554   LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%" PRIx32, tee.get(),
5555        static_cast<uint32_t>(rv)));
5556   rv = tee->Init(mListener, out, nullptr);
5557   if (NS_FAILED(rv)) return rv;
5559   mListener = tee;
5560   return NS_OK;
5563 nsresult nsHttpChannel::InstallOfflineCacheListener(int64_t offset) {
5564   nsresult rv;
5566   LOG(("Preparing to write data into the offline cache [uri=%s]\n",
5567        mSpec.get()));
5569   MOZ_ASSERT(mOfflineCacheEntry);
5570   MOZ_ASSERT(mListener);
5572   nsCOMPtr<nsIOutputStream> out;
5573   rv = mOfflineCacheEntry->OpenOutputStream(offset, -1, getter_AddRefs(out));
5574   if (NS_FAILED(rv)) return rv;
5576   nsCOMPtr<nsIStreamListenerTee> tee =
5577       do_CreateInstance(kStreamListenerTeeCID, &rv);
5578   if (NS_FAILED(rv)) return rv;
5580   rv = tee->Init(mListener, out, nullptr);
5581   if (NS_FAILED(rv)) return rv;
5583   mListener = tee;
5585   return NS_OK;
5588 void nsHttpChannel::ClearBogusContentEncodingIfNeeded() {
5589   // For .gz files, apache sends both a Content-Type: application/x-gzip
5590   // as well as Content-Encoding: gzip, which is completely wrong.  In
5591   // this case, we choose to ignore the rogue Content-Encoding header. We
5592   // must do this early on so as to prevent it from being seen up stream.
5593   // The same problem exists for Content-Encoding: compress in default
5594   // Apache installs.
5595   nsAutoCString contentType;
5596   mResponseHead->ContentType(contentType);
5597   if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") &&
5598       (contentType.EqualsLiteral(APPLICATION_GZIP) ||
5599        contentType.EqualsLiteral(APPLICATION_GZIP2) ||
5600        contentType.EqualsLiteral(APPLICATION_GZIP3))) {
5601     // clear the Content-Encoding header
5602     mResponseHead->ClearHeader(nsHttp::Content_Encoding);
5603   } else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding,
5604                                            "compress") &&
5605              (contentType.EqualsLiteral(APPLICATION_COMPRESS) ||
5606               contentType.EqualsLiteral(APPLICATION_COMPRESS2))) {
5607     // clear the Content-Encoding header
5608     mResponseHead->ClearHeader(nsHttp::Content_Encoding);
5609   }
5612 //-----------------------------------------------------------------------------
5613 // nsHttpChannel <redirect>
5614 //-----------------------------------------------------------------------------
5616 nsresult nsHttpChannel::SetupReplacementChannel(nsIURI *newURI,
5617                                                 nsIChannel *newChannel,
5618                                                 bool preserveMethod,
5619                                                 uint32_t redirectFlags) {
5620   LOG(
5621       ("nsHttpChannel::SetupReplacementChannel "
5622        "[this=%p newChannel=%p preserveMethod=%d]",
5623        this, newChannel, preserveMethod));
5625   nsresult rv = HttpBaseChannel::SetupReplacementChannel(
5626       newURI, newChannel, preserveMethod, redirectFlags);
5627   if (NS_FAILED(rv)) return rv;
5629   rv = CheckRedirectLimit(redirectFlags);
5630   NS_ENSURE_SUCCESS(rv, rv);
5632   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
5633   if (!httpChannel) return NS_OK;  // no other options to set
5635   // convey the mApplyConversion flag (bug 91862)
5636   nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
5637   if (encodedChannel) encodedChannel->SetApplyConversion(mApplyConversion);
5639   // transfer the resume information
5640   if (mResuming) {
5641     nsCOMPtr<nsIResumableChannel> resumableChannel(
5642         do_QueryInterface(newChannel));
5643     if (!resumableChannel) {
5644       NS_WARNING(
5645           "Got asked to resume, but redirected to non-resumable channel!");
5646       return NS_ERROR_NOT_RESUMABLE;
5647     }
5648     resumableChannel->ResumeAt(mStartPos, mEntityID);
5649   }
5651   nsCOMPtr<nsIHttpChannelInternal> internalChannel =
5652       do_QueryInterface(newChannel, &rv);
5653   if (NS_SUCCEEDED(rv)) {
5654     TimeStamp timestamp;
5655     rv = GetNavigationStartTimeStamp(&timestamp);
5656     if (NS_WARN_IF(NS_FAILED(rv))) {
5657       return rv;
5658     }
5659     if (timestamp) {
5660       Unused << internalChannel->SetNavigationStartTimeStamp(timestamp);
5661     }
5662   }
5664   return NS_OK;
5667 nsresult nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType) {
5668   LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n", this,
5669        redirectType));
5671   nsAutoCString location;
5673   // if a location header was not given, then we can't perform the redirect,
5674   // so just carry on as though this were a normal response.
5675   if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location)))
5676     return NS_ERROR_FAILURE;
5678   // If we were told to not follow redirects automatically, then again
5679   // carry on as though this were a normal response.
5680   if (mLoadInfo && mLoadInfo->GetDontFollowRedirects()) {
5681     return NS_ERROR_FAILURE;
5682   }
5684   // make sure non-ASCII characters in the location header are escaped.
5685   nsAutoCString locationBuf;
5686   if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
5687                    locationBuf))
5688     location = locationBuf;
5690   mRedirectType = redirectType;
5692   LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(),
5693        uint32_t(mRedirectionLimit)));
5695   nsresult rv = CreateNewURI(location.get(), getter_AddRefs(mRedirectURI));
5697   if (NS_FAILED(rv)) {
5698     LOG(("Invalid URI for redirect: Location: %s\n", location.get()));
5699     return NS_ERROR_CORRUPTED_CONTENT;
5700   }
5702   if (mApplicationCache) {
5703     // if we are redirected to a different origin check if there is a fallback
5704     // cache entry to fall back to. we don't care about file strict
5705     // checking, at least mURI is not a file URI.
5706     if (!NS_SecurityCompareURIs(mURI, mRedirectURI, false)) {
5707       PushRedirectAsyncFunc(
5708           &nsHttpChannel::ContinueProcessRedirectionAfterFallback);
5709       bool waitingForRedirectCallback;
5710       Unused << ProcessFallback(&waitingForRedirectCallback);
5711       if (waitingForRedirectCallback) return NS_OK;
5712       PopRedirectAsyncFunc(
5713           &nsHttpChannel::ContinueProcessRedirectionAfterFallback);
5714     }
5715   }
5717   return ContinueProcessRedirectionAfterFallback(NS_OK);
5720 nsresult nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) {
5721   if (NS_SUCCEEDED(rv) && mFallingBack) {
5722     // do not continue with redirect processing, fallback is in
5723     // progress now.
5724     return NS_OK;
5725   }
5727   // Kill the current cache entry if we are redirecting
5728   // back to ourself.
5729   bool redirectingBackToSameURI = false;
5730   if (mCacheEntry && mCacheEntryIsWriteOnly &&
5731       NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
5732       redirectingBackToSameURI)
5733     mCacheEntry->AsyncDoom(nullptr);
5735   // move the reference of the old location to the new one if the new
5736   // one has none.
5737   PropagateReferenceIfNeeded(mURI, mRedirectURI);
5739   bool rewriteToGET =
5740       ShouldRewriteRedirectToGET(mRedirectType, mRequestHead.ParsedMethod());
5742   // prompt if the method is not safe (such as POST, PUT, DELETE, ...)
5743   if (!rewriteToGET && !mRequestHead.IsSafeMethod()) {
5744     rv = PromptTempRedirect();
5745     if (NS_FAILED(rv)) return rv;
5746   }
5748 #ifdef MOZ_GECKO_PROFILER
5749   if (profiler_is_active()) {
5750     int32_t priority = PRIORITY_NORMAL;
5751     GetPriority(&priority);
5752     profiler_add_network_marker(
5753         mURI, priority, mChannelId, NetworkLoadType::LOAD_REDIRECT,
5754         mLastStatusReported, TimeStamp::Now(), mLogicalOffset,
5755         mCacheDisposition, nullptr, mRedirectURI);
5756   }
5757 #endif
5759   nsCOMPtr<nsIIOService> ioService;
5760   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
5761   if (NS_FAILED(rv)) return rv;
5763   uint32_t redirectFlags;
5764   if (nsHttp::IsPermanentRedirect(mRedirectType))
5765     redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
5766   else
5767     redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
5769   nsCOMPtr<nsIChannel> newChannel;
5770   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
5771       CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
5772   rv = NS_NewChannelInternal(getter_AddRefs(newChannel), mRedirectURI,
5773                              redirectLoadInfo,
5774                              nullptr,  // PerformanceStorage
5775                              nullptr,  // aLoadGroup
5776                              nullptr,  // aCallbacks
5777                              nsIRequest::LOAD_NORMAL, ioService);
5778   NS_ENSURE_SUCCESS(rv, rv);
5780   rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET,
5781                                redirectFlags);
5782   if (NS_FAILED(rv)) return rv;
5784   // verify that this is a legal redirect
5785   mRedirectChannel = newChannel;
5787   PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
5788   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
5790   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
5792   if (NS_FAILED(rv)) {
5793     AutoRedirectVetoNotifier notifier(this);
5794     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
5795   }
5797   return rv;
5800 nsresult nsHttpChannel::ContinueProcessRedirection(nsresult rv) {
5801   AutoRedirectVetoNotifier notifier(this);
5803   LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%" PRIx32 ",this=%p]\n",
5804        static_cast<uint32_t>(rv), this));
5805   if (NS_FAILED(rv)) return rv;
5807   MOZ_ASSERT(mRedirectChannel, "No redirect channel?");
5809   // Make sure to do this after we received redirect veto answer,
5810   // i.e. after all sinks had been notified
5811   mRedirectChannel->SetOriginalURI(mOriginalURI);
5813   // XXX we used to talk directly with the script security manager, but that
5814   // should really be handled by the event sink implementation.
5816   // begin loading the new channel
5817   rv = mRedirectChannel->AsyncOpen(mListener);
5818   LOG(("  new channel AsyncOpen returned %" PRIX32, static_cast<uint32_t>(rv)));
5819   NS_ENSURE_SUCCESS(rv, rv);
5821   // close down this channel
5822   Cancel(NS_BINDING_REDIRECTED);
5824   notifier.RedirectSucceeded();
5826   ReleaseListeners();
5828   return NS_OK;
5831 //-----------------------------------------------------------------------------
5832 // nsHttpChannel <auth>
5833 //-----------------------------------------------------------------------------
5835 NS_IMETHODIMP nsHttpChannel::OnAuthAvailable() {
5836   LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this));
5838   // setting mAuthRetryPending flag and resuming the transaction
5839   // triggers process of throwing away the unauthenticated data already
5840   // coming from the network
5841   mAuthRetryPending = true;
5842   mProxyAuthPending = false;
5843   LOG(("Resuming the transaction, we got credentials from user"));
5844   if (mTransactionPump) {
5845     mTransactionPump->Resume();
5846   }
5848   return NS_OK;
5851 NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel) {
5852   LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this));
5854   if (mTransactionPump) {
5855     // If the channel is trying to authenticate to a proxy and
5856     // that was canceled we cannot show the http response body
5857     // from the 40x as that might mislead the user into thinking
5858     // it was a end host response instead of a proxy reponse.
5859     // This must check explicitly whether a proxy auth was being done
5860     // because we do want to show the content if this is an error from
5861     // the origin server.
5862     if (mProxyAuthPending) Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED);
5864     // ensure call of OnStartRequest of the current listener here,
5865     // it would not be called otherwise at all
5866     nsresult rv = CallOnStartRequest();
5868     // drop mAuthRetryPending flag and resume the transaction
5869     // this resumes load of the unauthenticated content data (which
5870     // may have been canceled if we don't want to show it)
5871     mAuthRetryPending = false;
5872     LOG(("Resuming the transaction, user cancelled the auth dialog"));
5873     mTransactionPump->Resume();
5875     if (NS_FAILED(rv)) mTransactionPump->Cancel(rv);
5876   }
5878   mProxyAuthPending = false;
5879   return NS_OK;
5882 NS_IMETHODIMP nsHttpChannel::CloseStickyConnection() {
5883   LOG(("nsHttpChannel::CloseStickyConnection this=%p", this));
5885   // Require we are between OnStartRequest and OnStopRequest, because
5886   // what we do here takes effect in OnStopRequest (not reusing the
5887   // connection for next authentication round).
5888   if (!mIsPending) {
5889     LOG(("  channel not pending"));
5890     NS_ERROR(
5891         "CloseStickyConnection not called before OnStopRequest, won't have any "
5892         "effect");
5893     return NS_ERROR_UNEXPECTED;
5894   }
5896   MOZ_ASSERT(mTransaction);
5897   if (!mTransaction) {
5898     return NS_ERROR_UNEXPECTED;
5899   }
5901   if (!(mCaps & NS_HTTP_STICKY_CONNECTION ||
5902         mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
5903     LOG(("  not sticky"));
5904     return NS_OK;
5905   }
5907   mTransaction->DontReuseConnection();
5908   return NS_OK;
5911 NS_IMETHODIMP nsHttpChannel::ConnectionRestartable(bool aRestartable) {
5912   LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d", this,
5913        aRestartable));
5914   mAuthConnectionRestartable = aRestartable;
5915   return NS_OK;
5918 //-----------------------------------------------------------------------------
5919 // nsHttpChannel::nsISupports
5920 //-----------------------------------------------------------------------------
5922 NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
5923 NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
5925 NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
5926   NS_INTERFACE_MAP_ENTRY(nsIRequest)
5927   NS_INTERFACE_MAP_ENTRY(nsIChannel)
5928   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
5929   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
5930   NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
5931   NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
5932   NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
5933   NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
5934   NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
5935   NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
5936   NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
5937   NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)
5938   NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
5939   NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
5940   NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
5941   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
5942   NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
5943   NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
5944   NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
5945   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
5946   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
5947   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
5948   NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
5949   NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
5950   NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
5951   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
5952   NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
5953   NS_INTERFACE_MAP_ENTRY(nsIRaceCacheWithNetwork)
5954   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
5955   NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
5956   NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback)
5957   NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpChannel)
5958 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
5960 //-----------------------------------------------------------------------------
5961 // nsHttpChannel::nsIRequest
5962 //-----------------------------------------------------------------------------
5964 NS_IMETHODIMP
5965 nsHttpChannel::Cancel(nsresult status) {
5966   MOZ_ASSERT(NS_IsMainThread());
5967   // We should never have a pump open while a CORS preflight is in progress.
5968   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
5969 #ifdef DEBUG
5970   if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
5971     MOZ_CRASH_UNSAFE_PRINTF(
5972         "Blocking classifier error %" PRIx32
5973         " need to be handled by CancelByChannelClassifier()",
5974         static_cast<uint32_t>(status));
5975   }
5976 #endif
5978   LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 "]\n", this,
5979        static_cast<uint32_t>(status)));
5980   if (mCanceled) {
5981     LOG(("  ignoring; already canceled\n"));
5982     return NS_OK;
5983   }
5985   if (mWaitingForRedirectCallback) {
5986     LOG(("channel canceled during wait for redirect callback"));
5987   }
5989   return CancelInternal(status);
5992 NS_IMETHODIMP
5993 nsHttpChannel::CancelByChannelClassifier(nsresult aErrorCode) {
5994   MOZ_ASSERT(
5995       UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
5996   MOZ_ASSERT(NS_IsMainThread());
5997   // We should never have a pump open while a CORS preflight is in progress.
5998   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
6000   LOG(("nsHttpChannel::CancelByChannelClassifier [this=%p]\n", this));
6002   if (mCanceled) {
6003     LOG(("  ignoring; already canceled\n"));
6004     return NS_OK;
6005   }
6007   // We are being canceled by the channel classifier because of tracking
6008   // protection, but we haven't yet had a chance to dispatch the
6009   // "http-on-modify-request" notifications yet (this would normally be
6010   // done in PrepareToConnect()).  So do that now, before proceeding to
6011   // cancel.
6012   //
6013   // Note that running these observers can itself result in the channel
6014   // being canceled.  In that case, we accept that cancelation code as
6015   // the cause of the cancelation, as if the classification of the channel
6016   // would have occurred past this point!
6018   // notify "http-on-modify-request" observers
6019   CallOnModifyRequestObservers();
6021   SetLoadGroupUserAgentOverride();
6023   // Check if request was cancelled during on-modify-request or on-useragent.
6024   if (mCanceled) {
6025     return mStatus;
6026   }
6028   if (mSuspendCount) {
6029     LOG(("Waiting until resume in Cancel [this=%p]\n", this));
6030     MOZ_ASSERT(!mCallOnResume);
6031     mChannelClassifierCancellationPending = 1;
6032     mCallOnResume = [aErrorCode](nsHttpChannel *self) {
6033       self->HandleContinueCancellingByChannelClassifier(aErrorCode);
6034       return NS_OK;
6035     };
6036     return NS_OK;
6037   }
6039   // Check to see if we should redirect this channel elsewhere by
6040   // nsIHttpChannel.redirectTo API request
6041   if (mAPIRedirectToURI) {
6042     mChannelClassifierCancellationPending = 1;
6043     return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
6044   }
6046   return CancelInternal(aErrorCode);
6049 void nsHttpChannel::ContinueCancellingByChannelClassifier(nsresult aErrorCode) {
6050   MOZ_ASSERT(
6051       UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
6052   MOZ_ASSERT(NS_IsMainThread());
6053   // We should never have a pump open while a CORS preflight is in progress.
6054   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
6056   LOG(("nsHttpChannel::ContinueCancellingByChannelClassifier [this=%p]\n",
6057        this));
6058   if (mCanceled) {
6059     LOG(("  ignoring; already canceled\n"));
6060     return;
6061   }
6063   // Check to see if we should redirect this channel elsewhere by
6064   // nsIHttpChannel.redirectTo API request
6065   if (mAPIRedirectToURI) {
6066     Unused << AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
6067     return;
6068   }
6070   Unused << CancelInternal(aErrorCode);
6073 nsresult nsHttpChannel::CancelInternal(nsresult status) {
6074   bool channelClassifierCancellationPending =
6075       !!mChannelClassifierCancellationPending;
6076   if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
6077     mChannelClassifierCancellationPending = 0;
6078   }
6080   mCanceled = true;
6081   mStatus = status;
6082   if (mProxyRequest) mProxyRequest->Cancel(status);
6083   CancelNetworkRequest(status);
6084   mCacheInputStream.CloseAndRelease();
6085   if (mCachePump) mCachePump->Cancel(status);
6086   if (mAuthProvider) mAuthProvider->Cancel(status);
6087   if (mPreflightChannel) mPreflightChannel->Cancel(status);
6088   if (mRequestContext && mOnTailUnblock) {
6089     mOnTailUnblock = nullptr;
6090     mRequestContext->CancelTailedRequest(this);
6091     CloseCacheEntry(false);
6092     Unused << AsyncAbort(status);
6093   } else if (channelClassifierCancellationPending) {
6094     // If we're coming from an asynchronous path when canceling a channel due
6095     // to safe-browsing protection, we need to AsyncAbort the channel now.
6096     Unused << AsyncAbort(status);
6097   }
6098   return NS_OK;
6101 void nsHttpChannel::CancelNetworkRequest(nsresult aStatus) {
6102   if (mTransaction) {
6103     nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus);
6104     if (NS_FAILED(rv)) {
6105       LOG(("failed to cancel the transaction\n"));
6106     }
6107   }
6108   if (mTransactionPump) mTransactionPump->Cancel(aStatus);
6111 NS_IMETHODIMP
6112 nsHttpChannel::Suspend() {
6113   nsresult rv = SuspendInternal();
6115   nsresult rvParentChannel = NS_OK;
6116   if (mParentChannel) {
6117     rvParentChannel = mParentChannel->SuspendMessageDiversion();
6118   }
6120   return NS_FAILED(rv) ? rv : rvParentChannel;
6123 NS_IMETHODIMP
6124 nsHttpChannel::Resume() {
6125   nsresult rv = ResumeInternal();
6127   nsresult rvParentChannel = NS_OK;
6128   if (mParentChannel) {
6129     rvParentChannel = mParentChannel->ResumeMessageDiversion();
6130   }
6132   return NS_FAILED(rv) ? rv : rvParentChannel;
6135 //-----------------------------------------------------------------------------
6136 // nsHttpChannel::nsIChannel
6137 //-----------------------------------------------------------------------------
6139 NS_IMETHODIMP
6140 nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo) {
6141   NS_ENSURE_ARG_POINTER(securityInfo);
6142   *securityInfo = mSecurityInfo;
6143   NS_IF_ADDREF(*securityInfo);
6144   return NS_OK;
6147 // If any of the functions that AsyncOpen calls returns immediately an error
6148 // AsyncAbort(which calls onStart/onStopRequest) does not need to be call.
6149 // To be sure that they are not call ReleaseListeners() is called.
6150 // If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on
6151 // any error.
6152 NS_IMETHODIMP
6153 nsHttpChannel::AsyncOpen(nsIStreamListener *aListener) {
6154   nsCOMPtr<nsIStreamListener> listener = aListener;
6155   nsresult rv =
6156       nsContentSecurityManager::doContentSecurityCheck(this, listener);
6157   if (NS_WARN_IF(NS_FAILED(rv))) {
6158     ReleaseListeners();
6159     return rv;
6160   }
6161   MOZ_ASSERT(
6162       !mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
6163           mLoadInfo->GetInitialSecurityCheckDone() ||
6164           (mLoadInfo->GetSecurityMode() ==
6165                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
6166            nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
6167       "security flags in loadInfo but doContentSecurityCheck() not called");
6169   LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
6171 #ifdef MOZ_TASK_TRACER
6172   if (tasktracer::IsStartLogging()) {
6173     uint64_t sourceEventId, parentTaskId;
6174     tasktracer::SourceEventType sourceEventType;
6175     GetCurTraceInfo(&sourceEventId, &parentTaskId, &sourceEventType);
6176     nsAutoCString urispec;
6177     mURI->GetSpec(urispec);
6178     tasktracer::AddLabel("nsHttpChannel::AsyncOpen %s", urispec.get());
6179   }
6180 #endif
6182 #ifdef MOZ_GECKO_PROFILER
6183   mLastStatusReported =
6184       TimeStamp::Now();  // in case we enable the profiler after AsyncOpen()
6185   if (profiler_is_active()) {
6186     profiler_add_network_marker(
6187         mURI, mPriority, mChannelId, NetworkLoadType::LOAD_START,
6188         mChannelCreationTimestamp, mLastStatusReported, 0, mCacheDisposition);
6189   }
6190 #endif
6192   NS_CompareLoadInfoAndLoadContext(this);
6194 #ifdef DEBUG
6195   AssertPrivateBrowsingId();
6196 #endif
6198   NS_ENSURE_ARG_POINTER(listener);
6199   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
6200   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
6202   if (MaybeWaitForUploadStreamLength(listener, nullptr)) {
6203     return NS_OK;
6204   }
6206   MOZ_ASSERT(NS_IsMainThread());
6208   if (!gHttpHandler->Active()) {
6209     LOG(("  after HTTP shutdown..."));
6210     ReleaseListeners();
6211     return NS_ERROR_NOT_AVAILABLE;
6212   }
6214   static bool sRCWNInited = false;
6215   if (!sRCWNInited) {
6216     sRCWNInited = true;
6217     Preferences::AddBoolVarCache(&sRCWNEnabled, "network.http.rcwn.enabled");
6218     Preferences::AddUintVarCache(
6219         &sRCWNQueueSizeNormal,
6220         "network.http.rcwn.cache_queue_normal_threshold");
6221     Preferences::AddUintVarCache(
6222         &sRCWNQueueSizePriority,
6223         "network.http.rcwn.cache_queue_priority_threshold");
6224     Preferences::AddUintVarCache(&sRCWNSmallResourceSizeKB,
6225                                  "network.http.rcwn.small_resource_size_kb");
6226     Preferences::AddUintVarCache(&sRCWNMinWaitMs,
6227                                  "network.http.rcwn.min_wait_before_racing_ms");
6228     Preferences::AddUintVarCache(&sRCWNMaxWaitMs,
6229                                  "network.http.rcwn.max_wait_before_racing_ms");
6230   }
6232   rv = NS_CheckPortSafety(mURI);
6233   if (NS_FAILED(rv)) {
6234     ReleaseListeners();
6235     return rv;
6236   }
6238   if (!mLoadGroup && !mCallbacks) {
6239     // If no one called SetLoadGroup or SetNotificationCallbacks, the private
6240     // state has not been updated on PrivateBrowsingChannel (which we derive
6241     // from) Hence, we have to call UpdatePrivateBrowsing() here
6242     UpdatePrivateBrowsing();
6243   }
6245   if (WaitingForTailUnblock()) {
6246     // This channel is marked as Tail and is part of a request context
6247     // that has positive number of non-tailed requestst, hence this channel
6248     // has been put to a queue.
6249     // When tail is unblocked, OnTailUnblock on this channel will be called
6250     // to continue AsyncOpen.
6251     mListener = listener;
6252     MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
6253     mOnTailUnblock = &nsHttpChannel::AsyncOpenOnTailUnblock;
6255     LOG(("  put on hold until tail is unblocked"));
6256     return NS_OK;
6257   }
6259   // Remember the cookie header that was set, if any
6260   nsAutoCString cookieHeader;
6261   if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookieHeader))) {
6262     mUserSetCookieHeader = cookieHeader;
6263   }
6265   // Set user agent override, do so before OnOpeningRequest notification
6266   // since we want to allow consumers of that notification change or remove
6267   // the User-Agent request header.
6268   HttpBaseChannel::SetDocshellUserAgentOverride();
6270   // After we notify any observers (on-opening-request, loadGroup, etc) we
6271   // must return NS_OK and return any errors asynchronously via
6272   // OnStart/OnStopRequest.  Observers may add a reference to the channel
6273   // and expect to get OnStopRequest so they know when to drop the reference,
6274   // etc.
6276   // notify "http-on-opening-request" observers, but not if this is a redirect
6277   if (!(mLoadFlags & LOAD_REPLACE)) {
6278     gHttpHandler->OnOpeningRequest(this);
6279   }
6281   mIsPending = true;
6282   mWasOpened = true;
6284   mListener = listener;
6286   // PauseTask/DelayHttpChannel queuing
6287   if (!DelayHttpChannelQueue::AttemptQueueChannel(this)) {
6288     // If fuzzyfox is disabled; or adding to the queue failed, the channel must
6289     // continue.
6290     AsyncOpenFinal(TimeStamp::Now());
6291   }
6293   return NS_OK;
6296 nsresult nsHttpChannel::AsyncOpenFinal(TimeStamp aTimeStamp) {
6297   // Added due to PauseTask/DelayHttpChannel
6298   nsresult rv;
6300   if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
6302   // record asyncopen time unconditionally and clear it if we
6303   // don't want it after OnModifyRequest() weighs in. But waiting for
6304   // that to complete would mean we don't include proxy resolution in the
6305   // timing.
6306   if (!mAsyncOpenTimeOverriden) {
6307     mAsyncOpenTime = aTimeStamp;
6308   }
6310   // Remember we have Authorization header set here.  We need to check on it
6311   // just once and early, AsyncOpen is the best place.
6312   mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization);
6314   // The common case for HTTP channels is to begin proxy resolution and return
6315   // at this point. The only time we know mProxyInfo already is if we're
6316   // proxying a non-http protocol like ftp. We don't need to discover proxy
6317   // settings if we are never going to make a network connection.
6318   if (!mProxyInfo &&
6319       !(mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) &&
6320       NS_SUCCEEDED(ResolveProxy())) {
6321     return NS_OK;
6322   }
6324   rv = BeginConnect();
6325   if (NS_FAILED(rv)) {
6326     CloseCacheEntry(false);
6327     Unused << AsyncAbort(rv);
6328   }
6330   return NS_OK;
6333 nsresult nsHttpChannel::AsyncOpenOnTailUnblock() {
6334   return AsyncOpen(mListener);
6337 already_AddRefed<nsChannelClassifier>
6338 nsHttpChannel::GetOrCreateChannelClassifier() {
6339   if (!mChannelClassifier) {
6340     mChannelClassifier = new nsChannelClassifier(this);
6341     LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n", this,
6342          mChannelClassifier.get()));
6343   }
6345   RefPtr<nsChannelClassifier> classifier = mChannelClassifier;
6346   return classifier.forget();
6349 // BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by
6350 // functions that called BeginConnect if needed. Only AsyncOpen and
6351 // OnProxyAvailable ever call BeginConnect.
6352 nsresult nsHttpChannel::BeginConnect() {
6353   LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
6354   nsresult rv;
6356   // Construct connection info object
6357   nsAutoCString host;
6358   nsAutoCString scheme;
6359   int32_t port = -1;
6360   bool isHttps = false;
6362   rv = mURI->GetScheme(scheme);
6363   if (NS_SUCCEEDED(rv)) rv = mURI->SchemeIs("https", &isHttps);
6364   if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host);
6365   if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port);
6366   if (NS_SUCCEEDED(rv)) mURI->GetUsername(mUsername);
6367   if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec);
6368   if (NS_FAILED(rv)) {
6369     return rv;
6370   }
6372   // Reject the URL if it doesn't specify a host
6373   if (host.IsEmpty()) {
6374     rv = NS_ERROR_MALFORMED_URI;
6375     return rv;
6376   }
6377   LOG(("host=%s port=%d\n", host.get(), port));
6378   LOG(("uri=%s\n", mSpec.get()));
6380   nsCOMPtr<nsProxyInfo> proxyInfo;
6381   if (mProxyInfo) proxyInfo = do_QueryInterface(mProxyInfo);
6383   if (mCaps & NS_HTTP_CONNECT_ONLY) {
6384     if (!proxyInfo) {
6385       LOG(("return failure: no proxy for connect-only channel\n"));
6386       return NS_ERROR_FAILURE;
6387     }
6389     if (!proxyInfo->IsHTTP() && !proxyInfo->IsHTTPS()) {
6390       LOG(("return failure: non-http proxy for connect-only channel\n"));
6391       return NS_ERROR_FAILURE;
6392     }
6393   }
6395   mRequestHead.SetHTTPS(isHttps);
6396   mRequestHead.SetOrigin(scheme, host, port);
6398   SetOriginHeader();
6399   SetDoNotTrack();
6401   OriginAttributes originAttributes;
6402   NS_GetOriginAttributes(this, originAttributes);
6404   RefPtr<nsHttpConnectionInfo> connInfo =
6405       new nsHttpConnectionInfo(host, port, EmptyCString(), mUsername, proxyInfo,
6406                                originAttributes, isHttps);
6407   mAllowAltSvc = (mAllowAltSvc && !gHttpHandler->IsSpdyBlacklisted(connInfo));
6409   RefPtr<AltSvcMapping> mapping;
6410   if (!mConnectionInfo && mAllowAltSvc &&  // per channel
6411       !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
6412       AltSvcMapping::AcceptableProxy(proxyInfo) &&
6413       (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
6414       (mapping = gHttpHandler->GetAltServiceMapping(
6415            scheme, host, port, mPrivateBrowsing, originAttributes))) {
6416     LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", this,
6417          scheme.get(), mapping->AlternateHost().get(), mapping->AlternatePort(),
6418          mapping->HashKey().get()));
6420     if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) {
6421       nsAutoCString altUsedLine(mapping->AlternateHost());
6422       bool defaultPort =
6423           mapping->AlternatePort() ==
6424           (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT);
6425       if (!defaultPort) {
6426         altUsedLine.AppendLiteral(":");
6427         altUsedLine.AppendInt(mapping->AlternatePort());
6428       }
6429       rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine);
6430       MOZ_ASSERT(NS_SUCCEEDED(rv));
6431     }
6433     nsCOMPtr<nsIConsoleService> consoleService =
6434         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
6435     if (consoleService) {
6436       nsAutoString message(
6437           NS_LITERAL_STRING("Alternate Service Mapping found: "));
6438       AppendASCIItoUTF16(scheme, message);
6439       message.AppendLiteral(u"://");
6440       AppendASCIItoUTF16(host, message);
6441       message.AppendLiteral(u":");
6442       message.AppendInt(port);
6443       message.AppendLiteral(u" to ");
6444       AppendASCIItoUTF16(scheme, message);
6445       message.AppendLiteral(u"://");
6446       AppendASCIItoUTF16(mapping->AlternateHost(), message);
6447       message.AppendLiteral(u":");
6448       message.AppendInt(mapping->AlternatePort());
6449       consoleService->LogStringMessage(message.get());
6450     }
6452     LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this));
6453     mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo,
6454                                originAttributes);
6455     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
6456     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE, !isHttps);
6457   } else if (mConnectionInfo) {
6458     LOG(("nsHttpChannel %p Using channel supplied connection info", this));
6459     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
6460   } else {
6461     LOG(("nsHttpChannel %p Using default connection info", this));
6463     mConnectionInfo = connInfo;
6464     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
6465   }
6467   // Need to re-ask the handler, since mConnectionInfo may not be the connInfo
6468   // we used earlier
6469   if (gHttpHandler->IsSpdyBlacklisted(mConnectionInfo)) {
6470     mAllowSpdy = 0;
6471     mCaps |= NS_HTTP_DISALLOW_SPDY;
6472     mConnectionInfo->SetNoSpdy(true);
6473   }
6475   mAuthProvider = new nsHttpChannelAuthProvider();
6476   rv = mAuthProvider->Init(this);
6477   if (NS_FAILED(rv)) {
6478     return rv;
6479   }
6481   // check to see if authorization headers should be included
6482   // mCustomAuthHeader is set in AsyncOpen if we find Authorization header
6483   rv = mAuthProvider->AddAuthorizationHeaders(mCustomAuthHeader);
6484   if (NS_FAILED(rv)) {
6485     LOG(("nsHttpChannel %p AddAuthorizationHeaders failed (%08x)", this,
6486          static_cast<uint32_t>(rv)));
6487   }
6489   // If mTimingEnabled flag is not set after OnModifyRequest() then
6490   // clear the already recorded AsyncOpen value for consistency.
6491   if (!mTimingEnabled) mAsyncOpenTime = TimeStamp();
6493   // if this somehow fails we can go on without it
6494   Unused << gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
6496   if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
6497     mCaps |= NS_HTTP_REFRESH_DNS;
6499   // Adjust mCaps according to our request headers:
6500   //  - If "Connection: close" is set as a request header, then do not bother
6501   //    trying to establish a keep-alive connection.
6502   if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close"))
6503     mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE);
6505   if (gHttpHandler->CriticalRequestPrioritization()) {
6506     if (mClassOfService & nsIClassOfService::Leader) {
6507       mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
6508     }
6509     if (mClassOfService & nsIClassOfService::Unblocked) {
6510       mCaps |= NS_HTTP_LOAD_UNBLOCKED;
6511     }
6512     if (mClassOfService & nsIClassOfService::UrgentStart &&
6513         gHttpHandler->IsUrgentStartEnabled()) {
6514       mCaps |= NS_HTTP_URGENT_START;
6515       SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);
6516     }
6517   }
6519   // Force-Reload should reset the persistent connection pool for this host
6520   if (mLoadFlags & LOAD_FRESH_CONNECTION) {
6521     // just the initial document resets the whole pool
6522     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
6523       gHttpHandler->ConnMgr()->ClearAltServiceMappings();
6524       rv = gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(
6525           mConnectionInfo);
6526       if (NS_FAILED(rv)) {
6527         LOG(
6528             ("nsHttpChannel::BeginConnect "
6529              "DoShiftReloadConnectionCleanup failed: %08x [this=%p]",
6530              static_cast<uint32_t>(rv), this));
6531       }
6532     }
6533   }
6535   // We may have been cancelled already, either by on-modify-request
6536   // listeners or load group observers; in that case, we should not send the
6537   // request to the server
6538   if (mCanceled) {
6539     return mStatus;
6540   }
6542   if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
6543     MaybeStartDNSPrefetch();
6544     return ContinueBeginConnectWithResult();
6545   }
6547   // We are about to do an async lookup to check if the URI is a
6548   // tracker. If yes, this channel will be canceled by channel classifier.
6549   // Chances are the lookup is not needed so CheckIsTrackerWithLocalTable()
6550   // will return an error and then we can BeginConnectActual() right away.
6551   RefPtr<nsHttpChannel> self = this;
6552   bool willCallback = NS_SUCCEEDED(
6553       AsyncUrlChannelClassifier::CheckChannel(this, [self]() -> void {
6554         nsresult rv = self->BeginConnectActual();
6555         if (NS_FAILED(rv)) {
6556           // Since this error is thrown asynchronously so that the caller
6557           // of BeginConnect() will not do clean up for us. We have to do
6558           // it on our own.
6559           self->CloseCacheEntry(false);
6560           Unused << self->AsyncAbort(rv);
6561         }
6562       }));
6564   if (!willCallback) {
6565     // We can do BeginConnectActual immediately if CheckIsTrackerWithLocalTable
6566     // is failed. Note that we don't need to handle the failure because
6567     // BeginConnect() will return synchronously and the caller will be
6568     // responsible for handling it.
6569     return BeginConnectActual();
6570   }
6572   return NS_OK;
6575 void nsHttpChannel::MaybeStartDNSPrefetch() {
6576   if (!mConnectionInfo->UsingHttpProxy() &&
6577       !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
6578     // Start a DNS lookup very early in case the real open is queued the DNS can
6579     // happen in parallel. Do not do so in the presence of an HTTP proxy as
6580     // all lookups other than for the proxy itself are done by the proxy.
6581     // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
6582     // LOAD_ONLY_FROM_CACHE flags are set.
6583     //
6584     // We keep the DNS prefetch object around so that we can retrieve
6585     // timing information from it. There is no guarantee that we actually
6586     // use the DNS prefetch data for the real connection, but as we keep
6587     // this data around for 3 minutes by default, this should almost always
6588     // be correct, and even when it isn't, the timing still represents _a_
6589     // valid DNS lookup timing for the site, even if it is not _the_
6590     // timing we used.
6591     LOG(("nsHttpChannel::MaybeStartDNSPrefetch [this=%p] prefetching%s\n", this,
6592          mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
6593     OriginAttributes originAttributes;
6594     NS_GetOriginAttributes(this, originAttributes);
6595     mDNSPrefetch =
6596         new nsDNSPrefetch(mURI, originAttributes, this, mTimingEnabled);
6597     mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
6598   }
6601 nsresult nsHttpChannel::BeginConnectActual() {
6602   if (mCanceled) {
6603     return mStatus;
6604   }
6606   AUTO_PROFILER_LABEL("nsHttpChannel::BeginConnectActual", NETWORK);
6608   if (mChannelClassifierCancellationPending) {
6609     LOG(
6610         ("Waiting for safe-browsing protection cancellation in "
6611          "BeginConnectActual [this=%p]\n",
6612          this));
6613     return NS_OK;
6614   }
6616   MaybeStartDNSPrefetch();
6618   nsresult rv = ContinueBeginConnectWithResult();
6619   if (NS_FAILED(rv)) {
6620     return rv;
6621   }
6623   // Start nsChannelClassifier to catch phishing and malware URIs.
6624   RefPtr<nsChannelClassifier> channelClassifier =
6625       GetOrCreateChannelClassifier();
6626   LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
6627        channelClassifier.get(), this));
6628   channelClassifier->Start();
6630   return NS_OK;
6633 NS_IMETHODIMP
6634 nsHttpChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize) {
6635   if (mCacheEntry && !mCacheEntryIsWriteOnly) {
6636     int64_t dataSize = 0;
6637     mCacheEntry->GetDataSize(&dataSize);
6638     *aEncodedBodySize = dataSize;
6639   } else {
6640     *aEncodedBodySize = mLogicalOffset;
6641   }
6642   return NS_OK;
6645 //-----------------------------------------------------------------------------
6646 // nsHttpChannel::nsIHttpChannelInternal
6647 //-----------------------------------------------------------------------------
6649 NS_IMETHODIMP
6650 nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey) {
6651   ENSURE_CALLED_BEFORE_CONNECT();
6653   LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]\n", this,
6654        aFallbackKey));
6655   mFallbackChannel = true;
6656   mFallbackKey = aFallbackKey;
6658   return NS_OK;
6661 NS_IMETHODIMP
6662 nsHttpChannel::SetChannelIsForDownload(bool aChannelIsForDownload) {
6663   if (aChannelIsForDownload) {
6664     AddClassFlags(nsIClassOfService::Throttleable);
6665   } else {
6666     ClearClassFlags(nsIClassOfService::Throttleable);
6667   }
6669   return HttpBaseChannel::SetChannelIsForDownload(aChannelIsForDownload);
6672 base::ProcessId nsHttpChannel::ProcessId() {
6673   nsCOMPtr<nsIParentChannel> parentChannel;
6674   NS_QueryNotificationCallbacks(this, parentChannel);
6675   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
6676   if (httpParent) {
6677     return httpParent->OtherPid();
6678   }
6679   return base::GetCurrentProcId();
6682 bool nsHttpChannel::AttachStreamFilter(
6683     mozilla::ipc::Endpoint<extensions::PStreamFilterParent> &&aEndpoint)
6686   nsCOMPtr<nsIParentChannel> parentChannel;
6687   NS_QueryNotificationCallbacks(this, parentChannel);
6688   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
6689   if (httpParent) {
6690     return httpParent->SendAttachStreamFilter(std::move(aEndpoint));
6691   }
6693   extensions::StreamFilterParent::Attach(this, std::move(aEndpoint));
6694   return true;
6697 NS_IMETHODIMP
6698 nsHttpChannel::GetNavigationStartTimeStamp(TimeStamp *aTimeStamp) {
6699   LOG(("nsHttpChannel::GetNavigationStartTimeStamp %p", this));
6700   MOZ_ASSERT(aTimeStamp);
6701   *aTimeStamp = mNavigationStartTimeStamp;
6702   return NS_OK;
6705 NS_IMETHODIMP
6706 nsHttpChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp) {
6707   LOG(("nsHttpChannel::SetNavigationStartTimeStamp %p", this));
6708   mNavigationStartTimeStamp = aTimeStamp;
6709   return NS_OK;
6712 //-----------------------------------------------------------------------------
6713 // nsHttpChannel::nsISupportsPriority
6714 //-----------------------------------------------------------------------------
6716 NS_IMETHODIMP
6717 nsHttpChannel::SetPriority(int32_t value) {
6718   int16_t newValue = clamped<int32_t>(value, INT16_MIN, INT16_MAX);
6719   if (mPriority == newValue) return NS_OK;
6721   LOG(("nsHttpChannel::SetPriority %p p=%d", this, newValue));
6723   mPriority = newValue;
6724   if (mTransaction) {
6725     nsresult rv = gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
6726     if (NS_FAILED(rv)) {
6727       LOG(
6728           ("nsHttpChannel::SetPriority [this=%p] "
6729            "RescheduleTransaction failed (%08x)",
6730            this, static_cast<uint32_t>(rv)));
6731     }
6732   }
6734   // If this channel is the real channel for an e10s channel, notify the
6735   // child side about the priority change as well.
6736   nsCOMPtr<nsIParentChannel> parentChannel;
6737   NS_QueryNotificationCallbacks(this, parentChannel);
6738   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
6739   if (httpParent) {
6740     httpParent->DoSendSetPriority(newValue);
6741   }
6743   return NS_OK;
6746 nsresult nsHttpChannel::ContinueBeginConnectWithResult() {
6747   LOG(("nsHttpChannel::ContinueBeginConnectWithResult [this=%p]", this));
6748   MOZ_ASSERT(!mCallOnResume, "How did that happen?");
6750   nsresult rv;
6752   if (mSuspendCount) {
6753     LOG(("Waiting until resume to do async connect [this=%p]\n", this));
6754     mCallOnResume = [](nsHttpChannel *self) {
6755       self->ContinueBeginConnect();
6756       return NS_OK;
6757     };
6758     rv = NS_OK;
6759   } else if (mCanceled) {
6760     // We may have been cancelled already, by nsChannelClassifier in that
6761     // case, we should not send the request to the server
6762     rv = mStatus;
6763   } else {
6764     rv = PrepareToConnect();
6765   }
6767   LOG(
6768       ("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p "
6769        "rv=%" PRIx32 " mCanceled=%u]\n",
6770        this, static_cast<uint32_t>(rv), static_cast<bool>(mCanceled)));
6771   return rv;
6774 void nsHttpChannel::ContinueBeginConnect() {
6775   LOG(("nsHttpChannel::ContinueBeginConnect this=%p", this));
6777   nsresult rv = ContinueBeginConnectWithResult();
6778   if (NS_FAILED(rv)) {
6779     CloseCacheEntry(false);
6780     Unused << AsyncAbort(rv);
6781   }
6784 //-----------------------------------------------------------------------------
6785 // HttpChannel::nsIClassOfService
6786 //-----------------------------------------------------------------------------
6788 void nsHttpChannel::OnClassOfServiceUpdated() {
6789   LOG(("nsHttpChannel::OnClassOfServiceUpdated this=%p, cos=%u", this,
6790        mClassOfService));
6792   if (mTransaction) {
6793     gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction,
6794                                                     mClassOfService);
6795   }
6796   if (EligibleForTailing()) {
6797     RemoveAsNonTailRequest();
6798   } else {
6799     AddAsNonTailRequest();
6800   }
6803 NS_IMETHODIMP
6804 nsHttpChannel::SetClassFlags(uint32_t inFlags) {
6805   uint32_t previous = mClassOfService;
6806   mClassOfService = inFlags;
6807   if (previous != mClassOfService) {
6808     OnClassOfServiceUpdated();
6809   }
6810   return NS_OK;
6813 NS_IMETHODIMP
6814 nsHttpChannel::AddClassFlags(uint32_t inFlags) {
6815   uint32_t previous = mClassOfService;
6816   mClassOfService |= inFlags;
6817   if (previous != mClassOfService) {
6818     OnClassOfServiceUpdated();
6819   }
6820   return NS_OK;
6823 NS_IMETHODIMP
6824 nsHttpChannel::ClearClassFlags(uint32_t inFlags) {
6825   uint32_t previous = mClassOfService;
6826   mClassOfService &= ~inFlags;
6827   if (previous != mClassOfService) {
6828     OnClassOfServiceUpdated();
6829   }
6830   return NS_OK;
6833 //-----------------------------------------------------------------------------
6834 // nsHttpChannel::nsIProtocolProxyCallback
6835 //-----------------------------------------------------------------------------
6837 NS_IMETHODIMP
6838 nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
6839                                 nsIProxyInfo *pi, nsresult status) {
6840   LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
6841        " mStatus=%" PRIx32 "]\n",
6842        this, pi, static_cast<uint32_t>(status),
6843        static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
6844   mProxyRequest = nullptr;
6846   nsresult rv;
6848   // If status is a failure code, then it means that we failed to resolve
6849   // proxy info.  That is a non-fatal error assuming it wasn't because the
6850   // request was canceled.  We just failover to DIRECT when proxy resolution
6851   // fails (failure can mean that the PAC URL could not be loaded).
6853   if (NS_SUCCEEDED(status)) mProxyInfo = pi;
6855   if (!gHttpHandler->Active()) {
6856     LOG(
6857         ("nsHttpChannel::OnProxyAvailable [this=%p] "
6858          "Handler no longer active.\n",
6859          this));
6860     rv = NS_ERROR_NOT_AVAILABLE;
6861   } else {
6862     rv = BeginConnect();
6863   }
6865   if (NS_FAILED(rv)) {
6866     CloseCacheEntry(false);
6867     Unused << AsyncAbort(rv);
6868   }
6869   return rv;
6872 //-----------------------------------------------------------------------------
6873 // nsHttpChannel::nsIProxiedChannel
6874 //-----------------------------------------------------------------------------
6876 NS_IMETHODIMP
6877 nsHttpChannel::GetProxyInfo(nsIProxyInfo **result) {
6878   if (!mConnectionInfo)
6879     *result = mProxyInfo;
6880   else
6881     *result = mConnectionInfo->ProxyInfo();
6882   NS_IF_ADDREF(*result);
6883   return NS_OK;
6886 //-----------------------------------------------------------------------------
6887 // nsHttpChannel::nsITimedChannel
6888 //-----------------------------------------------------------------------------
6890 NS_IMETHODIMP
6891 nsHttpChannel::GetDomainLookupStart(TimeStamp *_retval) {
6892   if (mTransaction)
6893     *_retval = mTransaction->GetDomainLookupStart();
6894   else
6895     *_retval = mTransactionTimings.domainLookupStart;
6896   return NS_OK;
6899 NS_IMETHODIMP
6900 nsHttpChannel::GetDomainLookupEnd(TimeStamp *_retval) {
6901   if (mTransaction)
6902     *_retval = mTransaction->GetDomainLookupEnd();
6903   else
6904     *_retval = mTransactionTimings.domainLookupEnd;
6905   return NS_OK;
6908 NS_IMETHODIMP
6909 nsHttpChannel::GetConnectStart(TimeStamp *_retval) {
6910   if (mTransaction)
6911     *_retval = mTransaction->GetConnectStart();
6912   else
6913     *_retval = mTransactionTimings.connectStart;
6914   return NS_OK;
6917 NS_IMETHODIMP
6918 nsHttpChannel::GetTcpConnectEnd(TimeStamp *_retval) {
6919   if (mTransaction)
6920     *_retval = mTransaction->GetTcpConnectEnd();
6921   else
6922     *_retval = mTransactionTimings.tcpConnectEnd;
6923   return NS_OK;
6926 NS_IMETHODIMP
6927 nsHttpChannel::GetSecureConnectionStart(TimeStamp *_retval) {
6928   if (mTransaction)
6929     *_retval = mTransaction->GetSecureConnectionStart();
6930   else
6931     *_retval = mTransactionTimings.secureConnectionStart;
6932   return NS_OK;
6935 NS_IMETHODIMP
6936 nsHttpChannel::GetConnectEnd(TimeStamp *_retval) {
6937   if (mTransaction)
6938     *_retval = mTransaction->GetConnectEnd();
6939   else
6940     *_retval = mTransactionTimings.connectEnd;
6941   return NS_OK;
6944 NS_IMETHODIMP
6945 nsHttpChannel::GetRequestStart(TimeStamp *_retval) {
6946   if (mTransaction)
6947     *_retval = mTransaction->GetRequestStart();
6948   else
6949     *_retval = mTransactionTimings.requestStart;
6950   return NS_OK;
6953 NS_IMETHODIMP
6954 nsHttpChannel::GetResponseStart(TimeStamp *_retval) {
6955   if (mTransaction)
6956     *_retval = mTransaction->GetResponseStart();
6957   else
6958     *_retval = mTransactionTimings.responseStart;
6959   return NS_OK;
6962 NS_IMETHODIMP
6963 nsHttpChannel::GetResponseEnd(TimeStamp *_retval) {
6964   if (mTransaction)
6965     *_retval = mTransaction->GetResponseEnd();
6966   else
6967     *_retval = mTransactionTimings.responseEnd;
6968   return NS_OK;
6971 //-----------------------------------------------------------------------------
6972 // nsHttpChannel::nsIHttpAuthenticableChannel
6973 //-----------------------------------------------------------------------------
6975 NS_IMETHODIMP
6976 nsHttpChannel::GetIsSSL(bool *aIsSSL) {
6977   // this attribute is really misnamed - it wants to know if
6978   // https:// is being used. SSL might be used to cover http://
6979   // in some circumstances (proxies, http/2, etc..)
6980   return mURI->SchemeIs("https", aIsSSL);
6983 NS_IMETHODIMP
6984 nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect) {
6985   *aProxyMethodIsConnect = mConnectionInfo->UsingConnect();
6986   return NS_OK;
6989 NS_IMETHODIMP
6990 nsHttpChannel::GetServerResponseHeader(nsACString &value) {
6991   if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
6992   return mResponseHead->GetHeader(nsHttp::Server, value);
6995 NS_IMETHODIMP
6996 nsHttpChannel::GetProxyChallenges(nsACString &value) {
6997   if (!mResponseHead) return NS_ERROR_UNEXPECTED;
6998   return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value);
7001 NS_IMETHODIMP
7002 nsHttpChannel::GetWWWChallenges(nsACString &value) {
7003   if (!mResponseHead) return NS_ERROR_UNEXPECTED;
7004   return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value);
7007 NS_IMETHODIMP
7008 nsHttpChannel::SetProxyCredentials(const nsACString &value) {
7009   return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value);
7012 NS_IMETHODIMP
7013 nsHttpChannel::SetWWWCredentials(const nsACString &value) {
7014   // This method is called when various browser initiated authorization
7015   // code sets the credentials.  We need to flag this header as the
7016   // "browser default" so it does not show up in the ServiceWorker
7017   // FetchEvent.  This may actually get called more than once, though,
7018   // so we clear the header first since "default" headers are not
7019   // allowed to overwrite normally.
7020   Unused << mRequestHead.ClearHeader(nsHttp::Authorization);
7021   return mRequestHead.SetHeader(nsHttp::Authorization, value, false,
7022                                 nsHttpHeaderArray::eVarietyRequestDefault);
7025 //-----------------------------------------------------------------------------
7026 // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we
7027 // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.
7030 NS_IMETHODIMP
7031 nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) {
7032   return HttpBaseChannel::GetLoadFlags(aLoadFlags);
7035 NS_IMETHODIMP
7036 nsHttpChannel::GetURI(nsIURI **aURI) { return HttpBaseChannel::GetURI(aURI); }
7038 NS_IMETHODIMP
7039 nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) {
7040   return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
7043 NS_IMETHODIMP
7044 nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) {
7045   return HttpBaseChannel::GetLoadGroup(aLoadGroup);
7048 NS_IMETHODIMP
7049 nsHttpChannel::GetRequestMethod(nsACString &aMethod) {
7050   return HttpBaseChannel::GetRequestMethod(aMethod);
7053 //-----------------------------------------------------------------------------
7054 // nsHttpChannel::nsIRequestObserver
7055 //-----------------------------------------------------------------------------
7057 // This class is used to convert from a DOM promise to a MozPromise.
7058 class DomPromiseListener final : dom::PromiseNativeHandler {
7059   NS_DECL_ISUPPORTS
7061   static RefPtr<nsHttpChannel::TabPromise> Create(dom::Promise *aDOMPromise) {
7062     MOZ_ASSERT(aDOMPromise);
7063     RefPtr<DomPromiseListener> handler = new DomPromiseListener();
7064     RefPtr<nsHttpChannel::TabPromise> promise =
7065         handler->mPromiseHolder.Ensure(__func__);
7066     aDOMPromise->AppendNativeHandler(handler);
7067     return promise;
7068   }
7070   virtual void ResolvedCallback(JSContext *aCx,
7071                                 JS::Handle<JS::Value> aValue) override {
7072     nsCOMPtr<nsITabParent> tabParent;
7073     JS::Rooted<JSObject *> obj(aCx, &aValue.toObject());
7074     nsresult rv = UnwrapArg<nsITabParent>(aCx, obj, getter_AddRefs(tabParent));
7075     if (NS_FAILED(rv)) {
7076       mPromiseHolder.Reject(rv, __func__);
7077       return;
7078     }
7079     mPromiseHolder.Resolve(tabParent, __func__);
7080   }
7082   virtual void RejectedCallback(JSContext *aCx,
7083                                 JS::Handle<JS::Value> aValue) override {
7084     if (!aValue.isInt32()) {
7085       mPromiseHolder.Reject(NS_ERROR_DOM_NOT_NUMBER_ERR, __func__);
7086       return;
7087     }
7088     mPromiseHolder.Reject((nsresult)aValue.toInt32(), __func__);
7089   }
7091  private:
7092   DomPromiseListener() = default;
7093   ~DomPromiseListener() = default;
7094   MozPromiseHolder<nsHttpChannel::TabPromise> mPromiseHolder;
7097 NS_IMPL_ISUPPORTS0(DomPromiseListener)
7099 NS_IMETHODIMP nsHttpChannel::SwitchProcessTo(dom::Promise *aTabPromise,
7100                                              uint64_t aIdentifier) {
7101   MOZ_ASSERT(NS_IsMainThread());
7102   NS_ENSURE_ARG(aTabPromise);
7104   LOG(("nsHttpChannel::SwitchProcessTo [this=%p]", this));
7105   LogCallingScriptLocation(this);
7107   // We cannot do this after OnStartRequest of the listener has been called.
7108   NS_ENSURE_FALSE(mOnStartRequestCalled, NS_ERROR_NOT_AVAILABLE);
7110   mRedirectTabPromise = DomPromiseListener::Create(aTabPromise);
7111   mCrossProcessRedirectIdentifier = aIdentifier;
7112   return NS_OK;
7115 nsresult nsHttpChannel::StartCrossProcessRedirect() {
7116   nsresult rv;
7118   rv = CheckRedirectLimit(nsIChannelEventSink::REDIRECT_INTERNAL);
7119   NS_ENSURE_SUCCESS(rv, rv);
7121   RefPtr<HttpChannelParentListener> listener = do_QueryObject(mCallbacks);
7122   MOZ_ASSERT(listener);
7124   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
7125       CloneLoadInfoForRedirect(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
7127   listener->TriggerCrossProcessRedirect(this, redirectLoadInfo,
7128                                         mCrossProcessRedirectIdentifier);
7130   // This will suspend the channel
7131   rv = WaitForRedirectCallback();
7133   return rv;
7136 NS_IMETHODIMP
7137 nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
7138   nsresult rv;
7140   MOZ_ASSERT(mRequestObserversCalled);
7142   AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
7144   if (!(mCanceled || NS_FAILED(mStatus)) &&
7145       !WRONG_RACING_RESPONSE_SOURCE(request)) {
7146     // capture the request's status, so our consumers will know ASAP of any
7147     // connection failures, etc - bug 93581
7148     nsresult status;
7149     request->GetStatus(&status);
7150     mStatus = status;
7151   }
7153   LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32
7154        "]\n",
7155        this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
7157   Telemetry::Accumulate(Telemetry::HTTP_CHANNEL_ONSTART_SUCCESS,
7158                         NS_SUCCEEDED(mStatus));
7160   if (mRaceCacheWithNetwork) {
7161     LOG(
7162         ("  racingNetAndCache - mFirstResponseSource:%d fromCache:%d "
7163          "fromNet:%d\n",
7164          static_cast<int32_t>(mFirstResponseSource), request == mCachePump,
7165          request == mTransactionPump));
7166     if (mFirstResponseSource == RESPONSE_PENDING) {
7167       // When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE
7168       // earlier in ReadFromCache, so this must be a response from the network.
7169       MOZ_ASSERT(request == mTransactionPump);
7170       LOG(("  First response from network\n"));
7171       {
7172         // Race condition with OnCacheEntryCheck, which is not limited
7173         // to main thread.
7174         mozilla::MutexAutoLock lock(mRCWNLock);
7175         mFirstResponseSource = RESPONSE_FROM_NETWORK;
7176         mOnStartRequestTimestamp = TimeStamp::Now();
7178         // Conditional or byte range header could be added in
7179         // OnCacheEntryCheck. We need to remove them because the
7180         // request might be sent again due to auth retry and we must
7181         // not send these headers without having the entry.
7182         if (mDidReval) {
7183           LOG(("  Removing conditional request headers"));
7184           UntieValidationRequest();
7185           mDidReval = false;
7186         }
7187         if (mCachedContentIsPartial) {
7188           LOG(("  Removing byte range request headers"));
7189           UntieByteRangeRequest();
7190           mCachedContentIsPartial = false;
7191         }
7192       }
7193       mAvailableCachedAltDataType.Truncate();
7194     } else if (WRONG_RACING_RESPONSE_SOURCE(request)) {
7195       LOG(("  Early return when racing. This response not needed."));
7196       return NS_OK;
7197     }
7198   }
7200   // Make sure things are what we expect them to be...
7201   MOZ_ASSERT(request == mCachePump || request == mTransactionPump,
7202              "Unexpected request");
7204   MOZ_ASSERT(mRaceCacheWithNetwork || !(mTransactionPump && mCachePump) ||
7205                  mCachedContentIsPartial,
7206              "If we have both pumps, the cache content must be partial");
7208   mAfterOnStartRequestBegun = true;
7209   if (mOnStartRequestTimestamp.IsNull()) {
7210     mOnStartRequestTimestamp = TimeStamp::Now();
7211   }
7213   Telemetry::Accumulate(Telemetry::HTTP_ONSTART_SUSPEND_TOTAL_TIME,
7214                         mSuspendTotalTime);
7216   if (!mSecurityInfo && !mCachePump && mTransaction) {
7217     // grab the security info from the connection object; the transaction
7218     // is guaranteed to own a reference to the connection.
7219     mSecurityInfo = mTransaction->SecurityInfo();
7220   }
7222   // don't enter this block if we're reading from the cache...
7223   if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
7224     // mTransactionPump doesn't hit OnInputStreamReady and call this until
7225     // all of the response headers have been acquired, so we can take ownership
7226     // of them from the transaction.
7227     mResponseHead = mTransaction->TakeResponseHead();
7228     // the response head may be null if the transaction was cancelled.  in
7229     // which case we just need to call OnStartRequest/OnStopRequest.
7230     if (mResponseHead) return ProcessResponse();
7232     NS_WARNING("No response head in OnStartRequest");
7233   }
7235   // cache file could be deleted on our behalf, it could contain errors or
7236   // it failed to allocate memory, reload from network here.
7237   if (mCacheEntry && mCachePump && RECOVER_FROM_CACHE_FILE_ERROR(mStatus)) {
7238     LOG(("  cache file error, reloading from server"));
7239     mCacheEntry->AsyncDoom(nullptr);
7240     rv =
7241         StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
7242     if (NS_SUCCEEDED(rv)) return NS_OK;
7243   }
7245   // avoid crashing if mListener happens to be null...
7246   if (!mListener) {
7247     MOZ_ASSERT_UNREACHABLE("mListener is null");
7248     return NS_OK;
7249   }
7251   // before we check for redirects, check if the load should be shifted into a
7252   // new process.
7253   rv = NS_OK;
7254   if (mRedirectTabPromise && !mCanceled) {
7255     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
7256     rv = StartCrossProcessRedirect();
7257     if (NS_SUCCEEDED(rv)) {
7258       return NS_OK;
7259     }
7260     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
7261   }
7263   // No process change is needed, so continue on to ContinueOnStartRequest1.
7264   return ContinueOnStartRequest1(rv);
7267 nsresult nsHttpChannel::ContinueOnStartRequest1(nsresult result) {
7268   nsresult rv;
7270   // if process selection failed, cancel this load.
7271   if (NS_FAILED(result) && !mCanceled) {
7272     Cancel(result);
7273     return CallOnStartRequest();
7274   }
7276   // before we start any content load, check for redirectTo being called
7277   // this code is executed mainly before we start load from the cache
7278   if (mAPIRedirectToURI && !mCanceled) {
7279     nsAutoCString redirectToSpec;
7280     mAPIRedirectToURI->GetAsciiSpec(redirectToSpec);
7281     LOG(("  redirectTo called with uri=%s", redirectToSpec.BeginReading()));
7283     MOZ_ASSERT(!mOnStartRequestCalled);
7285     nsCOMPtr<nsIURI> redirectTo;
7286     mAPIRedirectToURI.swap(redirectTo);
7288     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
7289     rv = StartRedirectChannelToURI(redirectTo,
7290                                    nsIChannelEventSink::REDIRECT_TEMPORARY);
7291     if (NS_SUCCEEDED(rv)) {
7292       return NS_OK;
7293     }
7294     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
7295   }
7297   // Hack: ContinueOnStartRequest2 uses NS_OK to detect successful redirects,
7298   // so we distinguish this codepath (a non-redirect that's processing
7299   // normally) by passing in a bogus error code.
7300   return ContinueOnStartRequest2(NS_BINDING_FAILED);
7303 nsresult nsHttpChannel::ContinueOnStartRequest2(nsresult result) {
7304   if (NS_SUCCEEDED(result)) {
7305     // Redirect has passed through, we don't want to go on with this
7306     // channel.  It will now be canceled by the redirect handling code
7307     // that called this function.
7308     return NS_OK;
7309   }
7311   // on proxy errors, try to failover
7312   if (mConnectionInfo->ProxyInfo() &&
7313       (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
7314        mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
7315        mStatus == NS_ERROR_NET_TIMEOUT)) {
7316     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
7317     if (NS_SUCCEEDED(ProxyFailover())) return NS_OK;
7318     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
7319   }
7321   // Hack: ContinueOnStartRequest3 uses NS_OK to detect successful redirects,
7322   // so we distinguish this codepath (a non-redirect that's processing
7323   // normally) by passing in a bogus error code.
7324   return ContinueOnStartRequest3(NS_BINDING_FAILED);
7327 nsresult nsHttpChannel::ContinueOnStartRequest3(nsresult result) {
7328   if (NS_SUCCEEDED(result)) {
7329     // Redirect has passed through, we don't want to go on with this
7330     // channel.  It will now be canceled by the redirect handling code
7331     // that called this function.
7332     return NS_OK;
7333   }
7335   // on other request errors, try to fall back
7336   if (NS_FAILED(mStatus)) {
7337     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest4);
7338     bool waitingForRedirectCallback;
7339     Unused << ProcessFallback(&waitingForRedirectCallback);
7340     if (waitingForRedirectCallback) return NS_OK;
7341     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest4);
7342   }
7344   return ContinueOnStartRequest4(NS_OK);
7347 nsresult nsHttpChannel::ContinueOnStartRequest4(nsresult result) {
7348   LOG(("nsHttpChannel::ContinueOnStartRequest4 [this=%p]", this));
7350   if (mFallingBack) return NS_OK;
7352   return CallOnStartRequest();
7355 NS_IMETHODIMP
7356 nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
7357                              nsresult status) {
7358   AUTO_PROFILER_LABEL("nsHttpChannel::OnStopRequest", NETWORK);
7360   LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%" PRIx32 "]\n",
7361        this, request, static_cast<uint32_t>(status)));
7363   LOG(("OnStopRequest %p requestFromCache: %d mFirstResponseSource: %d\n", this,
7364        request == mCachePump, static_cast<int32_t>(mFirstResponseSource)));
7366   MOZ_ASSERT(NS_IsMainThread(),
7367              "OnStopRequest should only be called from the main thread");
7369   if (WRONG_RACING_RESPONSE_SOURCE(request)) {
7370     return NS_OK;
7371   }
7373   if (NS_FAILED(status)) {
7374     ProcessSecurityReport(status);
7375   }
7377   // If this load failed because of a security error, it may be because we
7378   // are in a captive portal - trigger an async check to make sure.
7379   int32_t nsprError = -1 * NS_ERROR_GET_CODE(status);
7380   if (mozilla::psm::IsNSSErrorCode(nsprError)) {
7381     gIOService->RecheckCaptivePortal();
7382   }
7384   if (mTimingEnabled && request == mCachePump) {
7385     mCacheReadEnd = TimeStamp::Now();
7387     ReportNetVSCacheTelemetry();
7388   }
7390   // allow content to be cached if it was loaded successfully (bug #482935)
7391   bool contentComplete = NS_SUCCEEDED(status);
7393   // honor the cancelation status even if the underlying transaction completed.
7394   if (mCanceled || NS_FAILED(mStatus)) status = mStatus;
7396   if (mCachedContentIsPartial) {
7397     if (NS_SUCCEEDED(status)) {
7398       // mTransactionPump should be suspended
7399       MOZ_ASSERT(request != mTransactionPump,
7400                  "byte-range transaction finished prematurely");
7402       if (request == mCachePump) {
7403         bool streamDone;
7404         status = OnDoneReadingPartialCacheEntry(&streamDone);
7405         if (NS_SUCCEEDED(status) && !streamDone) return status;
7406         // otherwise, fall through and fire OnStopRequest...
7407       } else if (request == mTransactionPump) {
7408         MOZ_ASSERT(mConcurrentCacheAccess);
7409       } else
7410         MOZ_ASSERT_UNREACHABLE("unexpected request");
7411     }
7412     // Do not to leave the transaction in a suspended state in error cases.
7413     if (NS_FAILED(status) && mTransaction) {
7414       nsresult rv = gHttpHandler->CancelTransaction(mTransaction, status);
7415       if (NS_FAILED(rv)) {
7416         LOG(("  CancelTransaction failed (%08x)", static_cast<uint32_t>(rv)));
7417       }
7418     }
7419   }
7421   nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
7422   if (conv) {
7423     conv->GetDecodedDataLength(&mDecodedBodySize);
7424   }
7426   bool isFromNet = request == mTransactionPump;
7428   if (mTransaction) {
7429     // determine if we should call DoAuthRetry
7430     bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
7431     mStronglyFramed = mTransaction->ResponseIsComplete();
7432     LOG(("nsHttpChannel %p has a strongly framed transaction: %d", this,
7433          mStronglyFramed));
7435     // Save the reference of |mTransaction| to |transactionWithStickyConn|
7436     // when it has a sticky connection.
7437     // In the case we need to retry an authentication request, we need to
7438     // reuse the connection of |transactionWithStickyConn|.
7439     RefPtr<nsHttpTransaction> transactionWithStickyConn;
7440     if (mCaps & NS_HTTP_STICKY_CONNECTION ||
7441         mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION) {
7442       transactionWithStickyConn = mTransaction;
7443       LOG(("  transaction %p has sticky connection",
7444            transactionWithStickyConn.get()));
7445     }
7447     // this code relies on the code in nsHttpTransaction::Close, which
7448     // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
7449     // keep the connection around after the transaction is finished.
7450     //
7451     LOG(("  mAuthRetryPending=%d, status=%" PRIx32 ", sticky conn cap=%d",
7452          mAuthRetryPending, static_cast<uint32_t>(status),
7453          mCaps & NS_HTTP_STICKY_CONNECTION));
7454     // We must check caps for stickinness also on the transaction because it
7455     // might have been updated by the transaction itself during inspection of
7456     // the reposnse headers yet on the socket thread (found connection based
7457     // auth schema).
7459     if ((mAuthRetryPending || NS_FAILED(status)) && transactionWithStickyConn) {
7460       if (NS_FAILED(status)) {
7461         // Close (don't reuse) the sticky connection if it's in the middle
7462         // of an NTLM negotiation and this channel has been cancelled.
7463         // There are proxy servers known to get confused when we send
7464         // a new request over such a half-stated connection.
7465         if (!mAuthConnectionRestartable) {
7466           LOG(("  not reusing a half-authenticated sticky connection"));
7467           transactionWithStickyConn->DontReuseConnection();
7468         }
7469       }
7470     }
7472     if (mCaps & NS_HTTP_STICKY_CONNECTION) {
7473       mTransaction->SetH2WSConnRefTaken();
7474     }
7476     mTransferSize = mTransaction->GetTransferSize();
7478     // If we are using the transaction to serve content, we also save the
7479     // time since async open in the cache entry so we can compare telemetry
7480     // between cache and net response.
7481     // Do not store the time of conditional requests because even if we
7482     // fetch the data from the server, the time includes loading of the old
7483     // cache entry which would skew the network load time.
7484     if (request == mTransactionPump && mCacheEntry && !mDidReval &&
7485         !mCustomConditionalRequest && !mAsyncOpenTime.IsNull() &&
7486         !mOnStartRequestTimestamp.IsNull()) {
7487       uint64_t onStartTime =
7488           (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
7489       uint64_t onStopTime =
7490           (TimeStamp::Now() - mAsyncOpenTime).ToMilliseconds();
7491       Unused << mCacheEntry->SetNetworkTimes(onStartTime, onStopTime);
7492     }
7494     mResponseTrailers = mTransaction->TakeResponseTrailers();
7496     // at this point, we're done with the transaction
7497     mTransactionTimings = mTransaction->Timings();
7498     mTransaction = nullptr;
7499     mTransactionPump = nullptr;
7501     // We no longer need the dns prefetch object
7502     if (mDNSPrefetch && mDNSPrefetch->TimingsValid() &&
7503         !mTransactionTimings.requestStart.IsNull() &&
7504         !mTransactionTimings.connectStart.IsNull() &&
7505         mDNSPrefetch->EndTimestamp() <= mTransactionTimings.connectStart) {
7506       // We only need the domainLookup timestamps when not using a
7507       // persistent connection, meaning if the endTimestamp < connectStart
7508       mTransactionTimings.domainLookupStart = mDNSPrefetch->StartTimestamp();
7509       mTransactionTimings.domainLookupEnd = mDNSPrefetch->EndTimestamp();
7510     }
7511     mDNSPrefetch = nullptr;
7513     // handle auth retry...
7514     if (authRetry) {
7515       mAuthRetryPending = false;
7516       auto continueOSR = [authRetry, isFromNet, contentComplete,
7517                           transactionWithStickyConn](auto *self,
7518                                                      nsresult aStatus) {
7519         return self->ContinueOnStopRequestAfterAuthRetry(
7520             aStatus, authRetry, isFromNet, contentComplete,
7521             transactionWithStickyConn);
7522       };
7523       status = DoAuthRetry(transactionWithStickyConn, continueOSR);
7524       if (NS_SUCCEEDED(status)) {
7525         return NS_OK;
7526       }
7527     }
7528     return ContinueOnStopRequestAfterAuthRetry(status, authRetry, isFromNet,
7529                                                contentComplete,
7530                                                transactionWithStickyConn);
7531   }
7533   return ContinueOnStopRequest(status, isFromNet, contentComplete);
7536 nsresult nsHttpChannel::ContinueOnStopRequestAfterAuthRetry(
7537     nsresult aStatus, bool aAuthRetry, bool aIsFromNet, bool aContentComplete,
7538     nsHttpTransaction *aTransWithStickyConn) {
7539   LOG(
7540       ("nsHttpChannel::ContinueOnStopRequestAfterAuthRetry "
7541        "[this=%p, aStatus=%" PRIx32
7542        " aAuthRetry=%d, aIsFromNet=%d, aTransWithStickyConn=%p]\n",
7543        this, static_cast<uint32_t>(aStatus), aAuthRetry, aIsFromNet,
7544        aTransWithStickyConn));
7546   if (aAuthRetry && NS_SUCCEEDED(aStatus)) {
7547     return NS_OK;
7548   }
7550   // If DoAuthRetry failed, or if we have been cancelled since showing
7551   // the auth. dialog, then we need to send OnStartRequest now
7552   if (aAuthRetry || (mAuthRetryPending && NS_FAILED(aStatus))) {
7553     MOZ_ASSERT(NS_FAILED(aStatus), "should have a failure code here");
7554     // NOTE: since we have a failure status, we can ignore the return
7555     // value from onStartRequest.
7556     LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
7557          mListener.get()));
7558     if (mListener) {
7559       MOZ_ASSERT(!mOnStartRequestCalled,
7560                  "We should not call OnStartRequest twice.");
7561       mListener->OnStartRequest(this, nullptr);
7562       mOnStartRequestCalled = true;
7563     } else {
7564       NS_WARNING("OnStartRequest skipped because of null listener");
7565     }
7566   }
7568   // if this transaction has been replaced, then bail.
7569   if (mTransactionReplaced) {
7570     LOG(("Transaction replaced\n"));
7571     // This was just the network check for a 304 response.
7572     mFirstResponseSource = RESPONSE_PENDING;
7573     return NS_OK;
7574   }
7576   bool upgradeWebsocket = mUpgradeProtocolCallback && aTransWithStickyConn &&
7577                           mResponseHead &&
7578                           ((mResponseHead->Status() == 101 &&
7579                             mResponseHead->Version() == HttpVersion::v1_1) ||
7580                            (mResponseHead->Status() == 200 &&
7581                             mResponseHead->Version() == HttpVersion::v2_0));
7583   bool upgradeConnect = mUpgradeProtocolCallback && aTransWithStickyConn &&
7584                         (mCaps & NS_HTTP_CONNECT_ONLY) && mResponseHead &&
7585                         mResponseHead->Status() == 200;
7587   if (upgradeWebsocket || upgradeConnect) {
7588     nsresult rv = gHttpHandler->ConnMgr()->CompleteUpgrade(
7589         aTransWithStickyConn, mUpgradeProtocolCallback);
7590     if (NS_FAILED(rv)) {
7591       LOG(("  CompleteUpgrade failed with %08x", static_cast<uint32_t>(rv)));
7592     }
7593   }
7595   return ContinueOnStopRequest(aStatus, aIsFromNet, aContentComplete);
7598 nsresult nsHttpChannel::ContinueOnStopRequest(nsresult aStatus, bool aIsFromNet,
7599                                               bool aContentComplete) {
7600   LOG(
7601       ("nsHttpChannel::ContinueOnStopRequest "
7602        "[this=%p aStatus=%" PRIx32 ", aIsFromNet=%d]\n",
7603        this, static_cast<uint32_t>(aStatus), aIsFromNet));
7605   // HTTP_CHANNEL_DISPOSITION TELEMETRY
7606   enum ChannelDisposition {
7607     kHttpCanceled = 0,
7608     kHttpDisk = 1,
7609     kHttpNetOK = 2,
7610     kHttpNetEarlyFail = 3,
7611     kHttpNetLateFail = 4,
7612     kHttpsCanceled = 8,
7613     kHttpsDisk = 9,
7614     kHttpsNetOK = 10,
7615     kHttpsNetEarlyFail = 11,
7616     kHttpsNetLateFail = 12
7617   } chanDisposition = kHttpCanceled;
7618   // HTTP_CHANNEL_DISPOSITION_UPGRADE TELEMETRY
7619   Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE upgradeChanDisposition =
7620       Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel;
7622   // HTTP 0.9 is more likely to be an error than really 0.9, so count it that
7623   // way
7624   if (mCanceled) {
7625     chanDisposition = kHttpCanceled;
7626     upgradeChanDisposition =
7627         Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel;
7628   } else if (!mUsedNetwork || (mRaceCacheWithNetwork &&
7629                                mFirstResponseSource == RESPONSE_FROM_CACHE)) {
7630     chanDisposition = kHttpDisk;
7631     upgradeChanDisposition =
7632         Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::disk;
7633   } else if (NS_SUCCEEDED(aStatus) && mResponseHead &&
7634              mResponseHead->Version() != HttpVersion::v0_9) {
7635     chanDisposition = kHttpNetOK;
7636     upgradeChanDisposition =
7637         Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netOk;
7638   } else if (!mTransferSize) {
7639     chanDisposition = kHttpNetEarlyFail;
7640     upgradeChanDisposition =
7641         Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netEarlyFail;
7642   } else {
7643     chanDisposition = kHttpNetLateFail;
7644     upgradeChanDisposition =
7645         Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netLateFail;
7646   }
7647   // Browser upgrading only happens on HTTPS pages for mixed passive content
7648   // when upgrading is enabled.
7649   nsCString upgradeKey;
7650   if (IsHTTPS()) {
7651     // Browser upgrading is disabled and the content is already HTTPS
7652     upgradeKey = NS_LITERAL_CSTRING("disabledNoReason");
7653     // Checks "security.mixed_content.upgrade_display_content" is true
7654     if (nsMixedContentBlocker::ShouldUpgradeMixedDisplayContent()) {
7655       if (mLoadInfo && mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
7656         // HTTP content the browser has upgraded to HTTPS
7657         upgradeKey = NS_LITERAL_CSTRING("enabledUpgrade");
7658       } else {
7659         // Content wasn't upgraded but is already HTTPS
7660         upgradeKey = NS_LITERAL_CSTRING("enabledNoReason");
7661       }
7662     }
7663     // shift http to https disposition enums
7664     chanDisposition =
7665         static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled);
7666   } else if (mLoadInfo && mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
7667     // HTTP content the browser would upgrade to HTTPS if upgrading was enabled
7668     upgradeKey = NS_LITERAL_CSTRING("disabledUpgrade");
7669   } else {
7670     // HTTP content that wouldn't upgrade
7671     upgradeKey = nsMixedContentBlocker::ShouldUpgradeMixedDisplayContent()
7672                      ? NS_LITERAL_CSTRING("enabledWont")
7673                      : NS_LITERAL_CSTRING("disabledWont");
7674   }
7675   Telemetry::AccumulateCategoricalKeyed(upgradeKey, upgradeChanDisposition);
7676   LOG(("  nsHttpChannel::OnStopRequest ChannelDisposition %d\n",
7677        chanDisposition));
7678   Telemetry::Accumulate(Telemetry::HTTP_CHANNEL_DISPOSITION, chanDisposition);
7680   // if needed, check cache entry has all data we expect
7681   if (mCacheEntry && mCachePump && mConcurrentCacheAccess && aContentComplete) {
7682     int64_t size, contentLength;
7683     nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength);
7684     if (NS_SUCCEEDED(rv)) {
7685       if (size == int64_t(-1)) {
7686         // mayhemer TODO - we have to restart read from cache here at the size
7687         // offset
7688         MOZ_ASSERT(false);
7689         LOG(
7690             ("  cache entry write is still in progress, but we just "
7691              "finished reading the cache entry"));
7692       } else if (contentLength != int64_t(-1) && contentLength != size) {
7693         LOG(("  concurrent cache entry write has been interrupted"));
7694         mCachedResponseHead = std::move(mResponseHead);
7695         // Ignore zero partial length because we also want to resume when
7696         // no data at all has been read from the cache.
7697         rv = MaybeSetupByteRangeRequest(size, contentLength, true);
7698         if (NS_SUCCEEDED(rv) && mIsPartialRequest) {
7699           // Prevent read from cache again
7700           mCachedContentIsValid = 0;
7701           mCachedContentIsPartial = 1;
7703           // Perform the range request
7704           rv = ContinueConnect();
7705           if (NS_SUCCEEDED(rv)) {
7706             LOG(("  performing range request"));
7707             mCachePump = nullptr;
7708             return NS_OK;
7709           }
7710           LOG(("  but range request perform failed 0x%08" PRIx32,
7711                static_cast<uint32_t>(rv)));
7712           aStatus = NS_ERROR_NET_INTERRUPT;
7713         } else {
7714           LOG(("  but range request setup failed rv=0x%08" PRIx32
7715                ", failing load",
7716                static_cast<uint32_t>(rv)));
7717         }
7718       }
7719     }
7720   }
7722   mIsPending = false;
7723   mStatus = aStatus;
7725   // perform any final cache operations before we close the cache entry.
7726   if (mCacheEntry && mRequestTimeInitialized) {
7727     bool writeAccess;
7728     // New implementation just returns value of the !mCacheEntryIsReadOnly flag
7729     // passed in. Old implementation checks on nsICache::ACCESS_WRITE flag.
7730     mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly, &writeAccess);
7731     if (writeAccess) {
7732       nsresult rv = FinalizeCacheEntry();
7733       if (NS_FAILED(rv)) {
7734         LOG(("FinalizeCacheEntry failed (%08x)", static_cast<uint32_t>(rv)));
7735       }
7736     }
7737   }
7739   ReportRcwnStats(aIsFromNet);
7741   // Register entry to the PerformanceStorage resource timing
7742   MaybeReportTimingData();
7744 #ifdef MOZ_GECKO_PROFILER
7745   if (profiler_is_active() && !mRedirectURI) {
7746     // Don't include this if we already redirected
7747     // These do allocations/frees/etc; avoid if not active
7748     nsCOMPtr<nsIURI> uri;
7749     GetURI(getter_AddRefs(uri));
7750     int32_t priority = PRIORITY_NORMAL;
7751     GetPriority(&priority);
7752     profiler_add_network_marker(
7753         uri, priority, mChannelId, NetworkLoadType::LOAD_STOP,
7754         mLastStatusReported, TimeStamp::Now(), mLogicalOffset,
7755         mCacheDisposition, &mTransactionTimings, nullptr);
7756   }
7757 #endif
7759   if (mListener) {
7760     LOG(("nsHttpChannel %p calling OnStopRequest\n", this));
7761     MOZ_ASSERT(mOnStartRequestCalled,
7762                "OnStartRequest should be called before OnStopRequest");
7763     MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
7764     mListener->OnStopRequest(this, nullptr, aStatus);
7765     mOnStopRequestCalled = true;
7766   }
7768   // notify "http-on-stop-connect" observers
7769   gHttpHandler->OnStopRequest(this);
7771   RemoveAsNonTailRequest();
7773   // If a preferred alt-data type was set, this signals the consumer is
7774   // interested in reading and/or writing the alt-data representation.
7775   // We need to hold a reference to the cache entry in case the listener calls
7776   // openAlternativeOutputStream() after CloseCacheEntry() clears mCacheEntry.
7777   if (!mPreferredCachedAltDataTypes.IsEmpty()) {
7778     mAltDataCacheEntry = mCacheEntry;
7779   }
7781   CloseCacheEntry(!aContentComplete);
7783   if (mOfflineCacheEntry) CloseOfflineCacheEntry();
7785   if (mLoadGroup) {
7786     mLoadGroup->RemoveRequest(this, nullptr, aStatus);
7787   }
7789   // We don't need this info anymore
7790   CleanRedirectCacheChainIfNecessary();
7792   ReleaseListeners();
7794   return NS_OK;
7797 //-----------------------------------------------------------------------------
7798 // nsHttpChannel::nsIStreamListener
7799 //-----------------------------------------------------------------------------
7801 class OnTransportStatusAsyncEvent : public Runnable {
7802  public:
7803   OnTransportStatusAsyncEvent(nsITransportEventSink *aEventSink,
7804                               nsresult aTransportStatus, int64_t aProgress,
7805                               int64_t aProgressMax)
7806       : Runnable("net::OnTransportStatusAsyncEvent"),
7807         mEventSink(aEventSink),
7808         mTransportStatus(aTransportStatus),
7809         mProgress(aProgress),
7810         mProgressMax(aProgressMax) {
7811     MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
7812   }
7814   NS_IMETHOD Run() override {
7815     MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
7816     if (mEventSink) {
7817       mEventSink->OnTransportStatus(nullptr, mTransportStatus, mProgress,
7818                                     mProgressMax);
7819     }
7820     return NS_OK;
7821   }
7823  private:
7824   nsCOMPtr<nsITransportEventSink> mEventSink;
7825   nsresult mTransportStatus;
7826   int64_t mProgress;
7827   int64_t mProgressMax;
7830 NS_IMETHODIMP
7831 nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
7832                                nsIInputStream *input, uint64_t offset,
7833                                uint32_t count) {
7834   nsresult rv;
7835   AUTO_PROFILER_LABEL("nsHttpChannel::OnDataAvailable", NETWORK);
7837   LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64
7838        " count=%" PRIu32 "]\n",
7839        this, request, offset, count));
7841   LOG(("  requestFromCache: %d mFirstResponseSource: %d\n",
7842        request == mCachePump, static_cast<int32_t>(mFirstResponseSource)));
7844   // don't send out OnDataAvailable notifications if we've been canceled.
7845   if (mCanceled) return mStatus;
7847   if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) ||
7848       (request == mTransactionPump && mTransactionReplaced)) {
7849     uint32_t n;
7850     return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
7851   }
7853   MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
7855   MOZ_ASSERT(!(mCachedContentIsPartial && (request == mTransactionPump)),
7856              "transaction pump not suspended");
7858   mIsReadingFromCache = (request == mCachePump);
7860   if (mListener) {
7861     //
7862     // synthesize transport progress event.  we do this here since we want
7863     // to delay OnProgress events until we start streaming data.  this is
7864     // crucially important since it impacts the lock icon (see bug 240053).
7865     //
7866     nsresult transportStatus;
7867     if (request == mCachePump)
7868       transportStatus = NS_NET_STATUS_READING;
7869     else
7870       transportStatus = NS_NET_STATUS_RECEIVING_FROM;
7872     // mResponseHead may reference new or cached headers, but either way it
7873     // holds our best estimate of the total content length.  Even in the case
7874     // of a byte range request, the content length stored in the cached
7875     // response headers is what we want to use here.
7877     int64_t progressMax = -1;
7878     rv = GetContentLength(&progressMax);
7879     if (NS_FAILED(rv)) {
7880       NS_WARNING("GetContentLength failed");
7881     }
7882     int64_t progress = mLogicalOffset + count;
7884     if ((progress > progressMax) && (progressMax != -1)) {
7885       NS_WARNING(
7886           "unexpected progress values - "
7887           "is server exceeding content length?");
7888     }
7890     // make sure params are in range for js
7891     if (!InScriptableRange(progressMax)) {
7892       progressMax = -1;
7893     }
7895     if (!InScriptableRange(progress)) {
7896       progress = -1;
7897     }
7899     if (NS_IsMainThread()) {
7900       OnTransportStatus(nullptr, transportStatus, progress, progressMax);
7901     } else {
7902       rv = NS_DispatchToMainThread(new OnTransportStatusAsyncEvent(
7903           this, transportStatus, progress, progressMax));
7904       NS_ENSURE_SUCCESS(rv, rv);
7905     }
7907     //
7908     // we have to manually keep the logical offset of the stream up-to-date.
7909     // we cannot depend solely on the offset provided, since we may have
7910     // already streamed some data from another source (see, for example,
7911     // OnDoneReadingPartialCacheEntry).
7912     //
7913     int64_t offsetBefore = 0;
7914     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
7915     if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
7916       seekable = nullptr;
7917     }
7919     nsresult rv =
7920         mListener->OnDataAvailable(this, nullptr, input, mLogicalOffset, count);
7921     if (NS_SUCCEEDED(rv)) {
7922       // by contract mListener must read all of "count" bytes, but
7923       // nsInputStreamPump is tolerant to seekable streams that violate that
7924       // and it will redeliver incompletely read data. So we need to do
7925       // the same thing when updating the progress counter to stay in sync.
7926       int64_t offsetAfter, delta;
7927       if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) {
7928         delta = offsetAfter - offsetBefore;
7929         if (delta != count) {
7930           count = delta;
7932           NS_WARNING("Listener OnDataAvailable contract violation");
7933           nsCOMPtr<nsIConsoleService> consoleService =
7934               do_GetService(NS_CONSOLESERVICE_CONTRACTID);
7935           nsAutoString message(NS_LITERAL_STRING(
7936               "http channel Listener OnDataAvailable contract violation"));
7937           if (consoleService) {
7938             consoleService->LogStringMessage(message.get());
7939           }
7940         }
7941       }
7942       mLogicalOffset += count;
7943     }
7945     return rv;
7946   }
7948   return NS_ERROR_ABORT;
7951 //-----------------------------------------------------------------------------
7952 // nsHttpChannel::nsIThreadRetargetableRequest
7953 //-----------------------------------------------------------------------------
7955 NS_IMETHODIMP
7956 nsHttpChannel::RetargetDeliveryTo(nsIEventTarget *aNewTarget) {
7957   MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
7959   NS_ENSURE_ARG(aNewTarget);
7960   if (aNewTarget->IsOnCurrentThread()) {
7961     NS_WARNING("Retargeting delivery to same thread");
7962     return NS_OK;
7963   }
7964   if (!mTransactionPump && !mCachePump) {
7965     LOG(("nsHttpChannel::RetargetDeliveryTo %p %p no pump available\n", this,
7966          aNewTarget));
7967     return NS_ERROR_NOT_AVAILABLE;
7968   }
7970   nsresult rv = NS_OK;
7971   // If both cache pump and transaction pump exist, we're probably dealing
7972   // with partially cached content. So, we must be able to retarget both.
7973   nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump;
7974   nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
7975   if (mCachePump) {
7976     retargetableCachePump = do_QueryObject(mCachePump);
7977     // nsInputStreamPump should implement this interface.
7978     MOZ_ASSERT(retargetableCachePump);
7979     rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget);
7980   }
7981   if (NS_SUCCEEDED(rv) && mTransactionPump) {
7982     retargetableTransactionPump = do_QueryObject(mTransactionPump);
7983     // nsInputStreamPump should implement this interface.
7984     MOZ_ASSERT(retargetableTransactionPump);
7985     rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);
7987     // If retarget fails for transaction pump, we must restore mCachePump.
7988     if (NS_FAILED(rv) && retargetableCachePump) {
7989       nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
7990       NS_ENSURE_TRUE(main, NS_ERROR_UNEXPECTED);
7991       rv = retargetableCachePump->RetargetDeliveryTo(main);
7992     }
7993   }
7994   return rv;
7997 NS_IMETHODIMP
7998 nsHttpChannel::GetDeliveryTarget(nsIEventTarget **aEventTarget) {
7999   if (mCachePump) {
8000     return mCachePump->GetDeliveryTarget(aEventTarget);
8001   }
8002   if (mTransactionPump) {
8003     return mTransactionPump->GetDeliveryTarget(aEventTarget);
8004   }
8005   return NS_ERROR_NOT_AVAILABLE;
8008 //-----------------------------------------------------------------------------
8009 // nsHttpChannel::nsThreadRetargetableStreamListener
8010 //-----------------------------------------------------------------------------
8012 NS_IMETHODIMP
8013 nsHttpChannel::CheckListenerChain() {
8014   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
8015   nsresult rv = NS_OK;
8016   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
8017       do_QueryInterface(mListener, &rv);
8018   if (retargetableListener) {
8019     rv = retargetableListener->CheckListenerChain();
8020   }
8021   return rv;
8024 //-----------------------------------------------------------------------------
8025 // nsHttpChannel::nsITransportEventSink
8026 //-----------------------------------------------------------------------------
8028 NS_IMETHODIMP
8029 nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
8030                                  int64_t progress, int64_t progressMax) {
8031   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only");
8032   // cache the progress sink so we don't have to query for it each time.
8033   if (!mProgressSink) GetCallback(mProgressSink);
8035   if (status == NS_NET_STATUS_CONNECTED_TO ||
8036       status == NS_NET_STATUS_WAITING_FOR) {
8037     if (mTransaction) {
8038       mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr);
8039     } else {
8040       nsCOMPtr<nsISocketTransport> socketTransport = do_QueryInterface(trans);
8041       if (socketTransport) {
8042         socketTransport->GetSelfAddr(&mSelfAddr);
8043         socketTransport->GetPeerAddr(&mPeerAddr);
8044       }
8045     }
8046   }
8048   // block socket status event after Cancel or OnStopRequest has been called.
8049   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
8050     LOG(("sending progress%s notification [this=%p status=%" PRIx32
8051          " progress=%" PRId64 "/%" PRId64 "]\n",
8052          (mLoadFlags & LOAD_BACKGROUND) ? "" : " and status", this,
8053          static_cast<uint32_t>(status), progress, progressMax));
8055     if (!(mLoadFlags & LOAD_BACKGROUND)) {
8056       nsAutoCString host;
8057       mURI->GetHost(host);
8058       mProgressSink->OnStatus(this, nullptr, status,
8059                               NS_ConvertUTF8toUTF16(host).get());
8060     }
8062     if (progress > 0) {
8063       if ((progress > progressMax) && (progressMax != -1)) {
8064         NS_WARNING("unexpected progress values");
8065       }
8067       // Try to get mProgressSink if it was nulled out during OnStatus.
8068       if (!mProgressSink) {
8069         GetCallback(mProgressSink);
8070       }
8071       if (mProgressSink) {
8072         mProgressSink->OnProgress(this, nullptr, progress, progressMax);
8073       }
8074     }
8075   }
8077   return NS_OK;
8080 //-----------------------------------------------------------------------------
8081 // nsHttpChannel::nsICacheInfoChannel
8082 //-----------------------------------------------------------------------------
8084 NS_IMETHODIMP
8085 nsHttpChannel::IsFromCache(bool *value) {
8086   if (!mIsPending) return NS_ERROR_NOT_AVAILABLE;
8088   if (!mRaceCacheWithNetwork) {
8089     // return false if reading a partial cache entry; the data isn't
8090     // entirely from the cache!
8091     *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
8092              mCachedContentIsValid && !mCachedContentIsPartial;
8093     return NS_OK;
8094   }
8096   // If we are racing network and cache (or skipping the cache)
8097   // we just return the first response source.
8098   *value = mFirstResponseSource == RESPONSE_FROM_CACHE;
8100   return NS_OK;
8103 NS_IMETHODIMP
8104 nsHttpChannel::GetCacheEntryId(uint64_t *aCacheEntryId) {
8105   bool fromCache = false;
8106   if (NS_FAILED(IsFromCache(&fromCache)) || !fromCache || !mCacheEntry ||
8107       NS_FAILED(mCacheEntry->GetCacheEntryId(aCacheEntryId))) {
8108     return NS_ERROR_NOT_AVAILABLE;
8109   }
8111   return NS_OK;
8114 NS_IMETHODIMP
8115 nsHttpChannel::GetCacheTokenFetchCount(int32_t *_retval) {
8116   NS_ENSURE_ARG_POINTER(_retval);
8117   nsCOMPtr<nsICacheEntry> cacheEntry =
8118       mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
8119   if (!cacheEntry) {
8120     return NS_ERROR_NOT_AVAILABLE;
8121   }
8123   return cacheEntry->GetFetchCount(_retval);
8126 NS_IMETHODIMP
8127 nsHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval) {
8128   NS_ENSURE_ARG_POINTER(_retval);
8129   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
8131   return mCacheEntry->GetExpirationTime(_retval);
8134 NS_IMETHODIMP
8135 nsHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval) {
8136   nsresult rv;
8138   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
8140   nsCString cachedCharset;
8141   rv = mCacheEntry->GetMetaDataElement("charset", getter_Copies(cachedCharset));
8142   if (NS_SUCCEEDED(rv)) _retval = cachedCharset;
8144   return rv;
8147 NS_IMETHODIMP
8148 nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset) {
8149   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
8151   return mCacheEntry->SetMetaDataElement("charset",
8152                                          PromiseFlatCString(aCharset).get());
8155 NS_IMETHODIMP
8156 nsHttpChannel::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) {
8157   LOG(("nsHttpChannel::SetAllowStaleCacheContent [this=%p, allow=%d]", this,
8158        aAllowStaleCacheContent));
8159   mAllowStaleCacheContent = aAllowStaleCacheContent;
8160   return NS_OK;
8162 NS_IMETHODIMP
8163 nsHttpChannel::GetAllowStaleCacheContent(bool *aAllowStaleCacheContent) {
8164   NS_ENSURE_ARG(aAllowStaleCacheContent);
8165   *aAllowStaleCacheContent = mAllowStaleCacheContent;
8166   return NS_OK;
8169 NS_IMETHODIMP
8170 nsHttpChannel::PreferAlternativeDataType(const nsACString &aType,
8171                                          const nsACString &aContentType) {
8172   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
8173   mPreferredCachedAltDataTypes.AppendElement(
8174       MakePair(nsCString(aType), nsCString(aContentType)));
8175   return NS_OK;
8178 const nsTArray<mozilla::Tuple<nsCString, nsCString>>
8179     &nsHttpChannel::PreferredAlternativeDataTypes() {
8180   return mPreferredCachedAltDataTypes;
8183 NS_IMETHODIMP
8184 nsHttpChannel::GetAlternativeDataType(nsACString &aType) {
8185   // must be called during or after OnStartRequest
8186   if (!mAfterOnStartRequestBegun) {
8187     return NS_ERROR_NOT_AVAILABLE;
8188   }
8189   aType = mAvailableCachedAltDataType;
8190   return NS_OK;
8193 NS_IMETHODIMP
8194 nsHttpChannel::OpenAlternativeOutputStream(const nsACString &type,
8195                                            int64_t predictedSize,
8196                                            nsIOutputStream **_retval) {
8197   // OnStopRequest will clear mCacheEntry, but we may use mAltDataCacheEntry
8198   // if the consumer called PreferAlternativeDataType()
8199   nsCOMPtr<nsICacheEntry> cacheEntry =
8200       mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
8201   if (!cacheEntry) {
8202     return NS_ERROR_NOT_AVAILABLE;
8203   }
8204   nsresult rv =
8205       cacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
8206   if (NS_SUCCEEDED(rv)) {
8207     // Clear this metadata flag in case it exists.
8208     // The caller of this method may set it again.
8209     cacheEntry->SetMetaDataElement("alt-data-from-child", nullptr);
8210   }
8211   return rv;
8214 NS_IMETHODIMP
8215 nsHttpChannel::GetOriginalInputStream(nsIInputStreamReceiver *aReceiver) {
8216   if (aReceiver == nullptr) {
8217     return NS_ERROR_INVALID_ARG;
8218   }
8219   nsCOMPtr<nsIInputStream> inputStream;
8221   nsCOMPtr<nsICacheEntry> cacheEntry =
8222       mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
8223   if (cacheEntry) {
8224     cacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
8225   }
8226   aReceiver->OnInputStreamReady(inputStream);
8227   return NS_OK;
8230 //-----------------------------------------------------------------------------
8231 // nsHttpChannel::nsICachingChannel
8232 //-----------------------------------------------------------------------------
8234 NS_IMETHODIMP
8235 nsHttpChannel::GetCacheToken(nsISupports **token) {
8236   NS_ENSURE_ARG_POINTER(token);
8237   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
8238   return CallQueryInterface(mCacheEntry, token);
8241 NS_IMETHODIMP
8242 nsHttpChannel::SetCacheToken(nsISupports *token) {
8243   return NS_ERROR_NOT_IMPLEMENTED;
8246 NS_IMETHODIMP
8247 nsHttpChannel::GetOfflineCacheToken(nsISupports **token) {
8248   NS_ENSURE_ARG_POINTER(token);
8249   if (!mOfflineCacheEntry) return NS_ERROR_NOT_AVAILABLE;
8250   return CallQueryInterface(mOfflineCacheEntry, token);
8253 NS_IMETHODIMP
8254 nsHttpChannel::SetOfflineCacheToken(nsISupports *token) {
8255   return NS_ERROR_NOT_IMPLEMENTED;
8258 NS_IMETHODIMP
8259 nsHttpChannel::GetCacheKey(uint32_t *key) {
8260   NS_ENSURE_ARG_POINTER(key);
8262   LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this));
8264   *key = mPostID;
8265   return NS_OK;
8268 NS_IMETHODIMP
8269 nsHttpChannel::SetCacheKey(uint32_t key) {
8270   LOG(("nsHttpChannel::SetCacheKey [this=%p key=%u]\n", this, key));
8272   ENSURE_CALLED_BEFORE_CONNECT();
8274   mPostID = key;
8275   return NS_OK;
8278 NS_IMETHODIMP
8279 nsHttpChannel::GetCacheOnlyMetadata(bool *aOnlyMetadata) {
8280   NS_ENSURE_ARG(aOnlyMetadata);
8281   *aOnlyMetadata = mCacheOnlyMetadata;
8282   return NS_OK;
8285 NS_IMETHODIMP
8286 nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata) {
8287   LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n", this,
8288        aOnlyMetadata));
8290   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
8292   mCacheOnlyMetadata = aOnlyMetadata;
8293   if (aOnlyMetadata) {
8294     mLoadFlags |= LOAD_ONLY_IF_MODIFIED;
8295   }
8297   return NS_OK;
8300 NS_IMETHODIMP
8301 nsHttpChannel::GetPin(bool *aPin) {
8302   NS_ENSURE_ARG(aPin);
8303   *aPin = mPinCacheContent;
8304   return NS_OK;
8307 NS_IMETHODIMP
8308 nsHttpChannel::SetPin(bool aPin) {
8309   LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n", this, aPin));
8311   ENSURE_CALLED_BEFORE_CONNECT();
8313   mPinCacheContent = aPin;
8314   return NS_OK;
8317 NS_IMETHODIMP
8318 nsHttpChannel::ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture) {
8319   if (!mCacheEntry) {
8320     LOG(
8321         ("nsHttpChannel::ForceCacheEntryValidFor found no cache entry "
8322          "for this channel [this=%p].",
8323          this));
8324   } else {
8325     mCacheEntry->ForceValidFor(aSecondsToTheFuture);
8327     nsAutoCString key;
8328     mCacheEntry->GetKey(key);
8330     LOG(
8331         ("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid "
8332          "entry with key %s for %d seconds. [this=%p]",
8333          key.get(), aSecondsToTheFuture, this));
8334   }
8336   return NS_OK;
8339 //-----------------------------------------------------------------------------
8340 // nsHttpChannel::nsIResumableChannel
8341 //-----------------------------------------------------------------------------
8343 NS_IMETHODIMP
8344 nsHttpChannel::ResumeAt(uint64_t aStartPos, const nsACString &aEntityID) {
8345   LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%" PRIu64 " id='%s']\n", this,
8346        aStartPos, PromiseFlatCString(aEntityID).get()));
8347   mEntityID = aEntityID;
8348   mStartPos = aStartPos;
8349   mResuming = true;
8350   return NS_OK;
8353 nsresult nsHttpChannel::DoAuthRetry(
8354     nsHttpTransaction *aTransWithStickyConn,
8355     const std::function<nsresult(nsHttpChannel *, nsresult)>
8356         &aContinueOnStopRequestFunc) {
8357   LOG(("nsHttpChannel::DoAuthRetry [this=%p, aTransWithStickyConn=%p]\n", this,
8358        aTransWithStickyConn));
8360   MOZ_ASSERT(!mTransaction, "should not have a transaction");
8362   // Note that we don't have to toggle |mIsPending| anymore. See the reasons
8363   // below.
8364   // 1. We can't suspend the channel during "http-on-modify-request"
8365   // when |mIsPending| is false.
8366   // 2. We don't check |mIsPending| in SetRequestHeader now.
8368   // Reset mRequestObserversCalled because we've probably called the request
8369   // observers once already.
8370   mRequestObserversCalled = false;
8372   // fetch cookies, and add them to the request header.
8373   // the server response could have included cookies that must be sent with
8374   // this authentication attempt (bug 84794).
8375   // TODO: save cookies from auth response and send them here (bug 572151).
8376   AddCookiesToRequest();
8378   // notify "http-on-modify-request" observers
8379   CallOnModifyRequestObservers();
8381   RefPtr<nsHttpTransaction> trans(aTransWithStickyConn);
8382   return CallOrWaitForResume(
8383       [trans{std::move(trans)}, aContinueOnStopRequestFunc](auto *self) {
8384         return self->ContinueDoAuthRetry(trans, aContinueOnStopRequestFunc);
8385       });
8388 nsresult nsHttpChannel::ContinueDoAuthRetry(
8389     nsHttpTransaction *aTransWithStickyConn,
8390     const std::function<nsresult(nsHttpChannel *, nsresult)>
8391         &aContinueOnStopRequestFunc) {
8392   LOG(("nsHttpChannel::ContinueDoAuthRetry [this=%p]\n", this));
8394   mIsPending = true;
8396   // get rid of the old response headers
8397   mResponseHead = nullptr;
8399   // rewind the upload stream
8400   if (mUploadStream) {
8401     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
8402     if (seekable) {
8403       seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
8404     }
8405   }
8407   // always set sticky connection flag
8408   mCaps |= NS_HTTP_STICKY_CONNECTION;
8409   // and when needed, allow restart regardless the sticky flag
8410   if (mAuthConnectionRestartable) {
8411     LOG(("  connection made restartable"));
8412     mCaps |= NS_HTTP_CONNECTION_RESTARTABLE;
8413     mAuthConnectionRestartable = false;
8414   } else {
8415     LOG(("  connection made non-restartable"));
8416     mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE;
8417   }
8419   // notify "http-on-before-connect" observers
8420   gHttpHandler->OnBeforeConnect(this);
8422   RefPtr<nsHttpTransaction> trans(aTransWithStickyConn);
8423   return CallOrWaitForResume(
8424       [trans{std::move(trans)}, aContinueOnStopRequestFunc](auto *self) {
8425         nsresult rv = self->DoConnect(trans);
8426         return aContinueOnStopRequestFunc(self, rv);
8427       });
8430 //-----------------------------------------------------------------------------
8431 // nsHttpChannel::nsIApplicationCacheChannel
8432 //-----------------------------------------------------------------------------
8434 NS_IMETHODIMP
8435 nsHttpChannel::GetApplicationCache(nsIApplicationCache **out) {
8436   NS_IF_ADDREF(*out = mApplicationCache);
8437   return NS_OK;
8440 NS_IMETHODIMP
8441 nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache) {
8442   ENSURE_CALLED_BEFORE_CONNECT();
8444   mApplicationCache = appCache;
8445   return NS_OK;
8448 NS_IMETHODIMP
8449 nsHttpChannel::GetApplicationCacheForWrite(nsIApplicationCache **out) {
8450   NS_IF_ADDREF(*out = mApplicationCacheForWrite);
8451   return NS_OK;
8454 NS_IMETHODIMP
8455 nsHttpChannel::SetApplicationCacheForWrite(nsIApplicationCache *appCache) {
8456   ENSURE_CALLED_BEFORE_CONNECT();
8458   mApplicationCacheForWrite = appCache;
8459   return NS_OK;
8462 NS_IMETHODIMP
8463 nsHttpChannel::GetLoadedFromApplicationCache(
8464     bool *aLoadedFromApplicationCache) {
8465   *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
8466   return NS_OK;
8469 NS_IMETHODIMP
8470 nsHttpChannel::GetInheritApplicationCache(bool *aInherit) {
8471   *aInherit = mInheritApplicationCache;
8472   return NS_OK;
8475 NS_IMETHODIMP
8476 nsHttpChannel::SetInheritApplicationCache(bool aInherit) {
8477   ENSURE_CALLED_BEFORE_CONNECT();
8479   mInheritApplicationCache = aInherit;
8480   return NS_OK;
8483 NS_IMETHODIMP
8484 nsHttpChannel::GetChooseApplicationCache(bool *aChoose) {
8485   *aChoose = mChooseApplicationCache;
8486   return NS_OK;
8489 NS_IMETHODIMP
8490 nsHttpChannel::SetChooseApplicationCache(bool aChoose) {
8491   ENSURE_CALLED_BEFORE_CONNECT();
8493   mChooseApplicationCache = aChoose;
8494   return NS_OK;
8497 nsHttpChannel::OfflineCacheEntryAsForeignMarker *
8498 nsHttpChannel::GetOfflineCacheEntryAsForeignMarker() {
8499   if (!mApplicationCache) return nullptr;
8501   return new OfflineCacheEntryAsForeignMarker(mApplicationCache, mURI);
8504 nsresult nsHttpChannel::OfflineCacheEntryAsForeignMarker::MarkAsForeign() {
8505   nsresult rv;
8507   nsCOMPtr<nsIURI> noRefURI;
8508   rv = NS_GetURIWithoutRef(mCacheURI, getter_AddRefs(noRefURI));
8509   NS_ENSURE_SUCCESS(rv, rv);
8511   nsAutoCString spec;
8512   rv = noRefURI->GetAsciiSpec(spec);
8513   NS_ENSURE_SUCCESS(rv, rv);
8515   return mApplicationCache->MarkEntry(spec, nsIApplicationCache::ITEM_FOREIGN);
8518 NS_IMETHODIMP
8519 nsHttpChannel::MarkOfflineCacheEntryAsForeign() {
8520   nsresult rv;
8522   nsAutoPtr<OfflineCacheEntryAsForeignMarker> marker(
8523       GetOfflineCacheEntryAsForeignMarker());
8525   if (!marker) return NS_ERROR_NOT_AVAILABLE;
8527   rv = marker->MarkAsForeign();
8528   NS_ENSURE_SUCCESS(rv, rv);
8530   return NS_OK;
8533 //-----------------------------------------------------------------------------
8534 // nsHttpChannel::nsIAsyncVerifyRedirectCallback
8535 //-----------------------------------------------------------------------------
8537 nsresult nsHttpChannel::WaitForRedirectCallback() {
8538   nsresult rv;
8539   LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this));
8541   if (mTransactionPump) {
8542     rv = mTransactionPump->Suspend();
8543     NS_ENSURE_SUCCESS(rv, rv);
8544   }
8545   if (mCachePump) {
8546     rv = mCachePump->Suspend();
8547     if (NS_FAILED(rv) && mTransactionPump) {
8548 #ifdef DEBUG
8549       nsresult resume =
8550 #endif
8551           mTransactionPump->Resume();
8552       MOZ_ASSERT(NS_SUCCEEDED(resume), "Failed to resume transaction pump");
8553     }
8554     NS_ENSURE_SUCCESS(rv, rv);
8555   }
8557   mWaitingForRedirectCallback = true;
8558   return NS_OK;
8561 NS_IMETHODIMP
8562 nsHttpChannel::OnRedirectVerifyCallback(nsresult result) {
8563   LOG(
8564       ("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
8565        "result=%" PRIx32 " stack=%zu mWaitingForRedirectCallback=%u\n",
8566        this, static_cast<uint32_t>(result), mRedirectFuncStack.Length(),
8567        mWaitingForRedirectCallback));
8568   MOZ_ASSERT(mWaitingForRedirectCallback,
8569              "Someone forgot to call WaitForRedirectCallback() ?!");
8570   mWaitingForRedirectCallback = false;
8572   if (mCanceled && NS_SUCCEEDED(result)) result = NS_BINDING_ABORTED;
8574   for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) {
8575     --i;
8576     // Pop the last function pushed to the stack
8577     nsContinueRedirectionFunc func = mRedirectFuncStack.PopLastElement();
8579     // Call it with the result we got from the callback or the deeper
8580     // function call.
8581     result = (this->*func)(result);
8583     // If a new function has been pushed to the stack and placed us in the
8584     // waiting state, we need to break the chain and wait for the callback
8585     // again.
8586     if (mWaitingForRedirectCallback) break;
8587   }
8589   if (NS_FAILED(result) && !mCanceled) {
8590     // First, cancel this channel if we are in failure state to set mStatus
8591     // and let it be propagated to pumps.
8592     Cancel(result);
8593   }
8595   if (!mWaitingForRedirectCallback) {
8596     // We are not waiting for the callback. At this moment we must release
8597     // reference to the redirect target channel, otherwise we may leak.
8598     mRedirectChannel = nullptr;
8599   }
8601   // We always resume the pumps here. If all functions on stack have been
8602   // called we need OnStopRequest to be triggered, and if we broke out of the
8603   // loop above (and are thus waiting for a new callback) the suspension
8604   // count must be balanced in the pumps.
8605   if (mTransactionPump) mTransactionPump->Resume();
8606   if (mCachePump) mCachePump->Resume();
8608   return result;
8611 void nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func) {
8612   mRedirectFuncStack.AppendElement(func);
8615 void nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func) {
8616   MOZ_ASSERT(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
8617              "Trying to pop wrong method from redirect async stack!");
8619   mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
8622 //-----------------------------------------------------------------------------
8623 // nsIDNSListener functions
8624 //-----------------------------------------------------------------------------
8626 NS_IMETHODIMP
8627 nsHttpChannel::OnLookupComplete(nsICancelable *request, nsIDNSRecord *rec,
8628                                 nsresult status) {
8629   MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
8631   LOG(
8632       ("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: "
8633        "%s status[0x%" PRIx32 "]\n",
8634        this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
8635        NS_SUCCEEDED(status) ? "success" : "failure",
8636        static_cast<uint32_t>(status)));
8638   // We no longer need the dns prefetch object. Note: mDNSPrefetch could be
8639   // validly null if OnStopRequest has already been called.
8640   // We only need the domainLookup timestamps when not loading from cache
8641   if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
8642     TimeStamp connectStart = mTransaction->GetConnectStart();
8643     TimeStamp requestStart = mTransaction->GetRequestStart();
8644     // We only set the domainLookup timestamps if we're not using a
8645     // persistent connection.
8646     if (requestStart.IsNull() && connectStart.IsNull()) {
8647       mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
8648       mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
8649     }
8650   }
8651   mDNSPrefetch = nullptr;
8653   // Unset DNS cache refresh if it was requested,
8654   if (mCaps & NS_HTTP_REFRESH_DNS) {
8655     mCaps &= ~NS_HTTP_REFRESH_DNS;
8656     if (mTransaction) {
8657       mTransaction->SetDNSWasRefreshed();
8658     }
8659   }
8661   return NS_OK;
8664 NS_IMETHODIMP
8665 nsHttpChannel::OnLookupByTypeComplete(nsICancelable *aRequest,
8666                                       nsIDNSByTypeRecord *aRes,
8667                                       nsresult aStatus) {
8668   return NS_OK;
8671 //-----------------------------------------------------------------------------
8672 // nsHttpChannel internal functions
8673 //-----------------------------------------------------------------------------
8675 // Creates an URI to the given location using current URI for base and charset
8676 nsresult nsHttpChannel::CreateNewURI(const char *loc, nsIURI **newURI) {
8677   nsCOMPtr<nsIIOService> ioService;
8678   nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
8679   if (NS_FAILED(rv)) return rv;
8681   return ioService->NewURI(nsDependentCString(loc), nullptr, mURI, newURI);
8684 void nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet() {
8685   // See RFC 2616 section 5.1.1. These are considered valid
8686   // methods which DO NOT invalidate cache-entries for the
8687   // referred resource. POST, PUT and DELETE as well as any
8688   // other method not listed here will potentially invalidate
8689   // any cached copy of the resource
8690   if (mRequestHead.IsGet() || mRequestHead.IsOptions() ||
8691       mRequestHead.IsHead() || mRequestHead.IsTrace() ||
8692       mRequestHead.IsConnect()) {
8693     return;
8694   }
8696   // Invalidate the request-uri.
8697   if (LOG_ENABLED()) {
8698     nsAutoCString key;
8699     mURI->GetAsciiSpec(key);
8700     LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n", this,
8701          key.get()));
8702   }
8704   DoInvalidateCacheEntry(mURI);
8706   // Invalidate Location-header if set
8707   nsAutoCString location;
8708   Unused << mResponseHead->GetHeader(nsHttp::Location, location);
8709   if (!location.IsEmpty()) {
8710     LOG(("  Location-header=%s\n", location.get()));
8711     InvalidateCacheEntryForLocation(location.get());
8712   }
8714   // Invalidate Content-Location-header if set
8715   Unused << mResponseHead->GetHeader(nsHttp::Content_Location, location);
8716   if (!location.IsEmpty()) {
8717     LOG(("  Content-Location-header=%s\n", location.get()));
8718     InvalidateCacheEntryForLocation(location.get());
8719   }
8722 void nsHttpChannel::InvalidateCacheEntryForLocation(const char *location) {
8723   nsAutoCString tmpCacheKey, tmpSpec;
8724   nsCOMPtr<nsIURI> resultingURI;
8725   nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI));
8726   if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) {
8727     DoInvalidateCacheEntry(resultingURI);
8728   } else {
8729     LOG(("  hosts not matching\n"));
8730   }
8733 void nsHttpChannel::DoInvalidateCacheEntry(nsIURI *aURI) {
8734   // NOTE:
8735   // Following comments 24,32 and 33 in bug #327765, we only care about
8736   // the cache in the protocol-handler, not the application cache.
8737   // The logic below deviates from the original logic in OpenCacheEntry on
8738   // one point by using only READ_ONLY access-policy. I think this is safe.
8740   nsresult rv;
8742   nsAutoCString key;
8743   if (LOG_ENABLED()) {
8744     aURI->GetAsciiSpec(key);
8745   }
8747   LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get()));
8749   nsCOMPtr<nsICacheStorageService> cacheStorageService(
8750       services::GetCacheStorageService());
8751   rv = cacheStorageService ? NS_OK : NS_ERROR_FAILURE;
8753   nsCOMPtr<nsICacheStorage> cacheStorage;
8754   if (NS_SUCCEEDED(rv)) {
8755     RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
8756     rv = cacheStorageService->DiskCacheStorage(info, false,
8757                                                getter_AddRefs(cacheStorage));
8758   }
8760   if (NS_SUCCEEDED(rv)) {
8761     rv = cacheStorage->AsyncDoomURI(aURI, EmptyCString(), nullptr);
8762   }
8764   LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(),
8765        int(rv)));
8768 void nsHttpChannel::AsyncOnExamineCachedResponse() {
8769   gHttpHandler->OnExamineCachedResponse(this);
8772 void nsHttpChannel::UpdateAggregateCallbacks() {
8773   if (!mTransaction) {
8774     return;
8775   }
8776   nsCOMPtr<nsIInterfaceRequestor> callbacks;
8777   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
8778                                          GetCurrentThreadEventTarget(),
8779                                          getter_AddRefs(callbacks));
8780   mTransaction->SetSecurityCallbacks(callbacks);
8783 NS_IMETHODIMP
8784 nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) {
8785   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
8787   nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup);
8788   if (NS_SUCCEEDED(rv)) {
8789     UpdateAggregateCallbacks();
8790   }
8791   return rv;
8794 NS_IMETHODIMP
8795 nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) {
8796   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
8798   nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks);
8799   if (NS_SUCCEEDED(rv)) {
8800     UpdateAggregateCallbacks();
8801   }
8802   return rv;
8805 bool nsHttpChannel::AwaitingCacheCallbacks() {
8806   return mCacheEntriesToWaitFor != 0;
8809 void nsHttpChannel::SetPushedStream(Http2PushedStream *stream) {
8810   MOZ_ASSERT(stream);
8811   MOZ_ASSERT(!mPushedStream);
8812   mPushedStream = stream;
8815 nsresult nsHttpChannel::OnPush(const nsACString &url,
8816                                Http2PushedStream *pushedStream) {
8817   MOZ_ASSERT(NS_IsMainThread());
8818   LOG(("nsHttpChannel::OnPush [this=%p]\n", this));
8820   MOZ_ASSERT(mCaps & NS_HTTP_ONPUSH_LISTENER);
8821   nsCOMPtr<nsIHttpPushListener> pushListener;
8822   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
8823                                 NS_GET_IID(nsIHttpPushListener),
8824                                 getter_AddRefs(pushListener));
8826   MOZ_ASSERT(pushListener);
8827   if (!pushListener) {
8828     LOG(
8829         ("nsHttpChannel::OnPush [this=%p] notification callbacks do not "
8830          "implement nsIHttpPushListener\n",
8831          this));
8832     return NS_ERROR_UNEXPECTED;
8833   }
8835   nsCOMPtr<nsIURI> pushResource;
8836   nsresult rv;
8838   // Create a Channel for the Push Resource
8839   rv = NS_NewURI(getter_AddRefs(pushResource), url);
8840   if (NS_FAILED(rv)) {
8841     return NS_ERROR_FAILURE;
8842   }
8844   nsCOMPtr<nsIIOService> ioService;
8845   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
8846   NS_ENSURE_SUCCESS(rv, rv);
8848   nsCOMPtr<nsIChannel> pushChannel;
8849   rv = NS_NewChannelInternal(getter_AddRefs(pushChannel), pushResource,
8850                              mLoadInfo,
8851                              nullptr,  // PerformanceStorage
8852                              nullptr,  // aLoadGroup
8853                              nullptr,  // aCallbacks
8854                              nsIRequest::LOAD_NORMAL, ioService);
8855   NS_ENSURE_SUCCESS(rv, rv);
8857   nsCOMPtr<nsIHttpChannel> pushHttpChannel = do_QueryInterface(pushChannel);
8858   MOZ_ASSERT(pushHttpChannel);
8859   if (!pushHttpChannel) {
8860     return NS_ERROR_UNEXPECTED;
8861   }
8863   RefPtr<nsHttpChannel> channel;
8864   CallQueryInterface(pushHttpChannel, channel.StartAssignment());
8865   MOZ_ASSERT(channel);
8866   if (!channel) {
8867     return NS_ERROR_UNEXPECTED;
8868   }
8870   // new channel needs mrqeuesthead and headers from pushedStream
8871   channel->mRequestHead.ParseHeaderSet(
8872       pushedStream->GetRequestString().BeginWriting());
8874   channel->mLoadGroup = mLoadGroup;
8875   channel->mLoadInfo = mLoadInfo;
8876   channel->mCallbacks = mCallbacks;
8878   // Link the pushed stream with the new channel and call listener
8879   channel->SetPushedStream(pushedStream);
8880   rv = pushListener->OnPush(this, pushHttpChannel);
8881   return rv;
8884 // static
8885 bool nsHttpChannel::IsRedirectStatus(uint32_t status) {
8886   // 305 disabled as a security measure (see bug 187996).
8887   return status == 300 || status == 301 || status == 302 || status == 303 ||
8888          status == 307 || status == 308;
8891 void nsHttpChannel::SetCouldBeSynthesized() {
8892   MOZ_ASSERT(!BypassServiceWorker());
8893   mResponseCouldBeSynthesized = true;
8896 void nsHttpChannel::SetConnectionInfo(nsHttpConnectionInfo *aCI) {
8897   mConnectionInfo = aCI ? aCI->Clone() : nullptr;
8900 NS_IMETHODIMP
8901 nsHttpChannel::OnPreflightSucceeded() {
8902   MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
8903   mIsCorsPreflightDone = 1;
8904   mPreflightChannel = nullptr;
8906   return ContinueConnect();
8909 NS_IMETHODIMP
8910 nsHttpChannel::OnPreflightFailed(nsresult aError) {
8911   MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
8912   mIsCorsPreflightDone = 1;
8913   mPreflightChannel = nullptr;
8915   CloseCacheEntry(false);
8916   Unused << AsyncAbort(aError);
8917   return NS_OK;
8920 //-----------------------------------------------------------------------------
8921 // AChannelHasDivertableParentChannelAsListener internal functions
8922 //-----------------------------------------------------------------------------
8924 NS_IMETHODIMP
8925 nsHttpChannel::MessageDiversionStarted(
8926     ADivertableParentChannel *aParentChannel) {
8927   LOG(("nsHttpChannel::MessageDiversionStarted [this=%p]", this));
8928   MOZ_ASSERT(!mParentChannel);
8929   mParentChannel = aParentChannel;
8930   // If the channel is suspended, propagate that info to the parent's mEventQ.
8931   uint32_t suspendCount = mSuspendCount;
8932   while (suspendCount--) {
8933     mParentChannel->SuspendMessageDiversion();
8934   }
8935   return NS_OK;
8938 NS_IMETHODIMP
8939 nsHttpChannel::MessageDiversionStop() {
8940   LOG(("nsHttpChannel::MessageDiversionStop [this=%p]", this));
8941   MOZ_ASSERT(mParentChannel);
8942   mParentChannel = nullptr;
8943   return NS_OK;
8946 NS_IMETHODIMP
8947 nsHttpChannel::SuspendInternal() {
8948   NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE);
8950   LOG(("nsHttpChannel::SuspendInternal [this=%p]\n", this));
8952   ++mSuspendCount;
8954   if (mSuspendCount == 1) {
8955     mSuspendTimestamp = TimeStamp::NowLoRes();
8956   }
8958   nsresult rvTransaction = NS_OK;
8959   if (mTransactionPump) {
8960     rvTransaction = mTransactionPump->Suspend();
8961   }
8962   nsresult rvCache = NS_OK;
8963   if (mCachePump) {
8964     rvCache = mCachePump->Suspend();
8965   }
8967   return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
8970 nsresult nsHttpChannel::CallOrWaitForResume(
8971     const std::function<nsresult(nsHttpChannel *)> &aFunc) {
8972   if (mCanceled) {
8973     MOZ_ASSERT(NS_FAILED(mStatus));
8974     return mStatus;
8975   }
8977   if (mSuspendCount) {
8978     LOG(("Waiting until resume [this=%p]\n", this));
8979     MOZ_ASSERT(!mCallOnResume);
8980     mCallOnResume = aFunc;
8981     return NS_OK;
8982   }
8984   return aFunc(this);
8987 NS_IMETHODIMP
8988 nsHttpChannel::ResumeInternal() {
8989   NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
8991   LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this));
8993   if (--mSuspendCount == 0) {
8994     mSuspendTotalTime +=
8995         (TimeStamp::NowLoRes() - mSuspendTimestamp).ToMilliseconds();
8997     if (mCallOnResume) {
8998       // Resume the interrupted procedure first, then resume
8999       // the pump to continue process the input stream.
9000       // Any newly created pump MUST be suspended to prevent calling
9001       // its OnStartRequest before OnStopRequest of any pre-existing
9002       // pump.  mAsyncResumePending ensures that.
9003       MOZ_ASSERT(!mAsyncResumePending);
9004       mAsyncResumePending = 1;
9006       std::function<nsresult(nsHttpChannel *)> callOnResume = nullptr;
9007       std::swap(callOnResume, mCallOnResume);
9009       RefPtr<nsHttpChannel> self(this);
9010       RefPtr<nsInputStreamPump> transactionPump = mTransactionPump;
9011       RefPtr<nsInputStreamPump> cachePump = mCachePump;
9013       nsresult rv = NS_DispatchToCurrentThread(NS_NewRunnableFunction(
9014           "nsHttpChannel::CallOnResume",
9015           [callOnResume{std::move(callOnResume)}, self{std::move(self)},
9016            transactionPump{std::move(transactionPump)},
9017            cachePump{std::move(cachePump)}]() {
9018             MOZ_ASSERT(self->mAsyncResumePending);
9019             nsresult rv = self->CallOrWaitForResume(callOnResume);
9020             if (NS_FAILED(rv)) {
9021               self->CloseCacheEntry(false);
9022               Unused << self->AsyncAbort(rv);
9023             }
9024             MOZ_ASSERT(self->mAsyncResumePending);
9026             self->mAsyncResumePending = 0;
9028             // And now actually resume the previously existing pumps.
9029             if (transactionPump) {
9030               LOG(
9031                   ("nsHttpChannel::CallOnResume resuming previous transaction "
9032                    "pump %p, this=%p",
9033                    transactionPump.get(), self.get()));
9034               transactionPump->Resume();
9035             }
9036             if (cachePump) {
9037               LOG(
9038                   ("nsHttpChannel::CallOnResume resuming previous cache pump "
9039                    "%p, this=%p",
9040                    cachePump.get(), self.get()));
9041               cachePump->Resume();
9042             }
9044             // Any newly created pumps were suspended once because of
9045             // mAsyncResumePending. Problem is that the stream listener
9046             // notification is already pending in the queue right now, because
9047             // AsyncRead doesn't (regardless if called after Suspend) respect
9048             // the suspend coutner and the right order would not be preserved.
9049             // Hence, we do another dispatch round to actually Resume after the
9050             // notification from the original pump.
9051             if (transactionPump != self->mTransactionPump &&
9052                 self->mTransactionPump) {
9053               LOG(
9054                   ("nsHttpChannel::CallOnResume async-resuming new transaction "
9055                    "pump %p, this=%p",
9056                    self->mTransactionPump.get(), self.get()));
9058               RefPtr<nsInputStreamPump> pump = self->mTransactionPump;
9059               NS_DispatchToCurrentThread(NS_NewRunnableFunction(
9060                   "nsHttpChannel::CallOnResume new transaction",
9061                   [pump{std::move(pump)}]() { pump->Resume(); }));
9062             }
9063             if (cachePump != self->mCachePump && self->mCachePump) {
9064               LOG(
9065                   ("nsHttpChannel::CallOnResume async-resuming new cache pump "
9066                    "%p, this=%p",
9067                    self->mCachePump.get(), self.get()));
9069               RefPtr<nsInputStreamPump> pump = self->mCachePump;
9070               NS_DispatchToCurrentThread(NS_NewRunnableFunction(
9071                   "nsHttpChannel::CallOnResume new pump",
9072                   [pump{std::move(pump)}]() { pump->Resume(); }));
9073             }
9074           }));
9075       NS_ENSURE_SUCCESS(rv, rv);
9076       return rv;
9077     }
9078   }
9080   nsresult rvTransaction = NS_OK;
9081   if (mTransactionPump) {
9082     rvTransaction = mTransactionPump->Resume();
9083   }
9085   nsresult rvCache = NS_OK;
9086   if (mCachePump) {
9087     rvCache = mCachePump->Resume();
9088   }
9090   return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
9093 void nsHttpChannel::MaybeWarnAboutAppCache() {
9094   // First, accumulate a telemetry ping about appcache usage.
9095   Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, true);
9097   // Then, issue a deprecation warning.
9098   nsCOMPtr<nsIDeprecationWarner> warner;
9099   GetCallback(warner);
9100   if (warner) {
9101     warner->IssueWarning(Document::eAppCache, false);
9102     // When the page is insecure and the API is still enabled
9103     // provide an additional warning for developers of removal
9104     if (!IsHTTPS() &&
9105         Preferences::GetBool("browser.cache.offline.insecure.enable")) {
9106       warner->IssueWarning(Document::eAppCacheInsecure, true);
9107     }
9108   }
9111 void nsHttpChannel::SetLoadGroupUserAgentOverride() {
9112   nsCOMPtr<nsIURI> uri;
9113   GetURI(getter_AddRefs(uri));
9114   nsAutoCString uriScheme;
9115   if (uri) {
9116     uri->GetScheme(uriScheme);
9117   }
9119   // We don't need a UA for file: protocols.
9120   if (uriScheme.EqualsLiteral("file")) {
9121     gHttpHandler->OnUserAgentRequest(this);
9122     return;
9123   }
9125   nsIRequestContextService *rcsvc = gHttpHandler->GetRequestContextService();
9126   nsCOMPtr<nsIRequestContext> rc;
9127   if (rcsvc) {
9128     rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(rc));
9129   }
9131   nsAutoCString ua;
9132   if (nsContentUtils::IsNonSubresourceRequest(this)) {
9133     gHttpHandler->OnUserAgentRequest(this);
9134     if (rc) {
9135       GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
9136       rc->SetUserAgentOverride(ua);
9137     }
9138   } else {
9139     GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
9140     // Don't overwrite the UA if it is already set (eg by an XHR with explicit
9141     // UA).
9142     if (ua.IsEmpty()) {
9143       if (rc) {
9144         SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
9145                          rc->GetUserAgentOverride(), false);
9146       } else {
9147         gHttpHandler->OnUserAgentRequest(this);
9148       }
9149     }
9150   }
9153 // Step 10 of HTTP-network-or-cache fetch
9154 void nsHttpChannel::SetOriginHeader() {
9155   if (mRequestHead.IsGet() || mRequestHead.IsHead()) {
9156     return;
9157   }
9158   nsAutoCString existingHeader;
9159   Unused << mRequestHead.GetHeader(nsHttp::Origin, existingHeader);
9160   if (!existingHeader.IsEmpty()) {
9161     LOG(("nsHttpChannel::SetOriginHeader Origin header already present"));
9162     return;
9163   }
9165   DebugOnly<nsresult> rv;
9167   // Instead of consulting Preferences::GetInt() all the time we
9168   // can cache the result to speed things up.
9169   static int32_t sSendOriginHeader = 0;
9170   static bool sIsInited = false;
9171   if (!sIsInited) {
9172     sIsInited = true;
9173     Preferences::AddIntVarCache(&sSendOriginHeader,
9174                                 "network.http.sendOriginHeader");
9175   }
9176   if (sSendOriginHeader == 0) {
9177     // Origin header suppressed by user setting
9178     return;
9179   }
9181   nsCOMPtr<nsIURI> referrer;
9182   mLoadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(referrer));
9184   nsAutoCString origin("null");
9185   if (referrer && IsReferrerSchemeAllowed(referrer)) {
9186     nsContentUtils::GetASCIIOrigin(referrer, origin);
9187   }
9189   // Restrict Origin to same-origin loads if requested by user or leaving from
9190   // .onion
9191   if (sSendOriginHeader == 1) {
9192     nsAutoCString currentOrigin;
9193     nsContentUtils::GetASCIIOrigin(mURI, currentOrigin);
9194     if (!origin.EqualsIgnoreCase(currentOrigin.get())) {
9195       // Origin header suppressed by user setting
9196       return;
9197     }
9198   } else if (gHttpHandler->HideOnionReferrerSource()) {
9199     nsAutoCString host;
9200     if (referrer && NS_SUCCEEDED(referrer->GetAsciiHost(host)) &&
9201         StringEndsWith(host, NS_LITERAL_CSTRING(".onion"))) {
9202       nsAutoCString currentOrigin;
9203       nsContentUtils::GetASCIIOrigin(mURI, currentOrigin);
9204       if (!origin.EqualsIgnoreCase(currentOrigin.get())) {
9205         // Origin header is suppressed by .onion
9206         return;
9207       }
9208     }
9209   }
9211   rv = mRequestHead.SetHeader(nsHttp::Origin, origin, false /* merge */);
9212   MOZ_ASSERT(NS_SUCCEEDED(rv));
9215 void nsHttpChannel::SetDoNotTrack() {
9216   /**
9217    * 'DoNotTrack' header should be added if 'privacy.donottrackheader.enabled'
9218    * is true or tracking protection is enabled. See bug 1258033.
9219    */
9220   nsCOMPtr<nsILoadContext> loadContext;
9221   NS_QueryNotificationCallbacks(this, loadContext);
9223   if ((loadContext && loadContext->UseTrackingProtection()) ||
9224       nsContentUtils::DoNotTrackEnabled()) {
9225     DebugOnly<nsresult> rv = mRequestHead.SetHeader(
9226         nsHttp::DoNotTrack, NS_LITERAL_CSTRING("1"), false);
9227     MOZ_ASSERT(NS_SUCCEEDED(rv));
9228   }
9231 void nsHttpChannel::ReportRcwnStats(bool isFromNet) {
9232   if (!sRCWNEnabled) {
9233     return;
9234   }
9236   if (isFromNet) {
9237     if (mRaceCacheWithNetwork) {
9238       gIOService->IncrementNetWonRequestNumber();
9239       Telemetry::Accumulate(
9240           Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_NETWORK_WIN,
9241           mTransferSize);
9242       if (mRaceDelay) {
9243         AccumulateCategorical(
9244             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
9245                 NetworkDelayedRace);
9246       } else {
9247         AccumulateCategorical(
9248             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
9249                 NetworkRace);
9250       }
9251     } else {
9252       Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE,
9253                             mTransferSize);
9254       AccumulateCategorical(
9255           Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
9256               NetworkNoRace);
9257     }
9258   } else {
9259     if (mRaceCacheWithNetwork || mRaceDelay) {
9260       gIOService->IncrementCacheWonRequestNumber();
9261       Telemetry::Accumulate(
9262           Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_CACHE_WIN,
9263           mTransferSize);
9264       if (mRaceDelay) {
9265         AccumulateCategorical(
9266             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
9267                 CacheDelayedRace);
9268       } else {
9269         AccumulateCategorical(
9270             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
9271                 CacheRace);
9272       }
9273     } else {
9274       Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE,
9275                             mTransferSize);
9276       AccumulateCategorical(
9277           Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
9278               CacheNoRace);
9279     }
9280   }
9282   gIOService->IncrementRequestNumber();
9285 static const size_t kPositiveBucketNumbers = 34;
9286 static const int64_t kPositiveBucketLevels[kPositiveBucketNumbers] = {
9287     0,    10,   20,   30,   40,    50,    60,    70,    80,    90,   100,  200,
9288     300,  400,  500,  600,  700,   800,   900,   1000,  2000,  3000, 4000, 5000,
9289     6000, 7000, 8000, 9000, 10000, 20000, 30000, 40000, 50000, 60000};
9292  * For space efficiency, we collect finer resolution for small difference
9293  * between net and cache time, coarser for larger.
9294  * Bucket #40 for a tie.
9295  * #41 to #50 indicates cache wins by 1ms to 100ms, split equally.
9296  * #51 to #59 indicates cache wins by 101ms to 1000ms.
9297  * #60 to #68 indicates cache wins by 1s to 10s.
9298  * #69 to #73 indicates cache wins by 11s to 60s.
9299  * #74 indicates cache wins by more than 1 minute.
9301  * #39 to #30 indicates network wins by 1ms to 100ms, split equally.
9302  * #29 to #21 indicates network wins by 101ms to 1000ms.
9303  * #20 to #12 indicates network wins by 1s to 10s.
9304  * #11 to #7 indicates network wins by 11s to 60s.
9305  * #6 indicates network wins by more than 1 minute.
9307  * Other bucket numbers are reserved.
9308  */
9309 inline int64_t nsHttpChannel::ComputeTelemetryBucketNumber(
9310     int64_t difftime_ms) {
9311   int64_t absBucketIndex =
9312       std::lower_bound(kPositiveBucketLevels,
9313                        kPositiveBucketLevels + kPositiveBucketNumbers,
9314                        static_cast<int64_t>(mozilla::Abs(difftime_ms))) -
9315       kPositiveBucketLevels;
9317   return difftime_ms >= 0 ? 40 + absBucketIndex : 40 - absBucketIndex;
9320 void nsHttpChannel::ReportNetVSCacheTelemetry() {
9321   nsresult rv;
9322   if (!mCacheEntry) {
9323     return;
9324   }
9326   // We only report telemetry if the entry is persistent (on disk)
9327   bool persistent;
9328   rv = mCacheEntry->GetPersistent(&persistent);
9329   if (NS_FAILED(rv) || !persistent) {
9330     return;
9331   }
9333   uint64_t onStartNetTime = 0;
9334   if (NS_FAILED(mCacheEntry->GetOnStartTime(&onStartNetTime))) {
9335     return;
9336   }
9338   uint64_t onStopNetTime = 0;
9339   if (NS_FAILED(mCacheEntry->GetOnStopTime(&onStopNetTime))) {
9340     return;
9341   }
9343   uint64_t onStartCacheTime =
9344       (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
9345   int64_t onStartDiff = onStartNetTime - onStartCacheTime;
9346   onStartDiff = ComputeTelemetryBucketNumber(onStartDiff);
9348   uint64_t onStopCacheTime = (mCacheReadEnd - mAsyncOpenTime).ToMilliseconds();
9349   int64_t onStopDiff = onStopNetTime - onStopCacheTime;
9350   onStopDiff = ComputeTelemetryBucketNumber(onStopDiff);
9352   if (mDidReval) {
9353     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_REVALIDATED_V2,
9354                           onStartDiff);
9355     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_REVALIDATED_V2,
9356                           onStopDiff);
9357   } else {
9358     Telemetry::Accumulate(
9359         Telemetry::HTTP_NET_VS_CACHE_ONSTART_NOTREVALIDATED_V2, onStartDiff);
9360     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_NOTREVALIDATED_V2,
9361                           onStopDiff);
9362   }
9364   if (mDidReval) {
9365     // We don't report revalidated probes as the data would be skewed.
9366     return;
9367   }
9369   if (mCacheOpenWithPriority) {
9370     if (mCacheQueueSizeWhenOpen < 5) {
9371       Telemetry::Accumulate(
9372           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_HIGHPRI_V2, onStartDiff);
9373       Telemetry::Accumulate(
9374           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_HIGHPRI_V2, onStopDiff);
9375     } else if (mCacheQueueSizeWhenOpen < 10) {
9376       Telemetry::Accumulate(
9377           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_HIGHPRI_V2, onStartDiff);
9378       Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_HIGHPRI_V2,
9379                             onStopDiff);
9380     } else {
9381       Telemetry::Accumulate(
9382           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_HIGHPRI_V2, onStartDiff);
9383       Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_HIGHPRI_V2,
9384                             onStopDiff);
9385     }
9386   } else {  // The limits are higher for normal priority cache queues
9387     if (mCacheQueueSizeWhenOpen < 10) {
9388       Telemetry::Accumulate(
9389           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_NORMALPRI_V2,
9390           onStartDiff);
9391       Telemetry::Accumulate(
9392           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_NORMALPRI_V2, onStopDiff);
9393     } else if (mCacheQueueSizeWhenOpen < 50) {
9394       Telemetry::Accumulate(
9395           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_NORMALPRI_V2, onStartDiff);
9396       Telemetry::Accumulate(
9397           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_NORMALPRI_V2, onStopDiff);
9398     } else {
9399       Telemetry::Accumulate(
9400           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_NORMALPRI_V2, onStartDiff);
9401       Telemetry::Accumulate(
9402           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_NORMALPRI_V2, onStopDiff);
9403     }
9404   }
9406   uint32_t diskStorageSizeK = 0;
9407   rv = mCacheEntry->GetDiskStorageSizeInKB(&diskStorageSizeK);
9408   if (NS_FAILED(rv)) {
9409     return;
9410   }
9412   // No significant difference was observed between different sizes for
9413   // |onStartDiff|
9414   if (diskStorageSizeK < 256) {
9415     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_SMALL_V2,
9416                           onStopDiff);
9417   } else {
9418     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_LARGE_V2,
9419                           onStopDiff);
9420   }
9423 NS_IMETHODIMP
9424 nsHttpChannel::Test_delayCacheEntryOpeningBy(int32_t aTimeout) {
9425   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
9426   mCacheOpenDelay = aTimeout;
9427   return NS_OK;
9430 NS_IMETHODIMP
9431 nsHttpChannel::Test_triggerDelayedOpenCacheEntry() {
9432   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
9433   nsresult rv;
9434   if (!mCacheOpenDelay) {
9435     // No delay was set.
9436     return NS_ERROR_NOT_AVAILABLE;
9437   }
9438   if (!mCacheOpenFunc) {
9439     // There should be a runnable.
9440     return NS_ERROR_FAILURE;
9441   }
9442   if (mCacheOpenTimer) {
9443     rv = mCacheOpenTimer->Cancel();
9444     if (NS_FAILED(rv)) {
9445       return rv;
9446     }
9447     mCacheOpenTimer = nullptr;
9448   }
9449   mCacheOpenDelay = 0;
9450   // Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it.
9451   std::function<void(nsHttpChannel *)> cacheOpenFunc = nullptr;
9452   std::swap(cacheOpenFunc, mCacheOpenFunc);
9453   cacheOpenFunc(this);
9455   return NS_OK;
9458 nsresult nsHttpChannel::TriggerNetworkWithDelay(uint32_t aDelay) {
9459   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
9461   LOG(("nsHttpChannel::TriggerNetworkWithDelay [this=%p, delay=%u]\n", this,
9462        aDelay));
9464   if (mCanceled) {
9465     LOG(("  channel was canceled.\n"));
9466     return mStatus;
9467   }
9469   // If a network request has already gone out, there is no point in
9470   // doing this again.
9471   if (mNetworkTriggered) {
9472     LOG(("  network already triggered. Returning.\n"));
9473     return NS_OK;
9474   }
9476   if (!aDelay) {
9477     // We cannot call TriggerNetwork() directly here, because it would
9478     // cause performance regression in tp6 tests, see bug 1398847.
9479     return NS_DispatchToMainThread(
9480         NewRunnableMethod("net::nsHttpChannel::TriggerNetworkWithDelay", this,
9481                           &nsHttpChannel::TriggerNetwork),
9482         NS_DISPATCH_NORMAL);
9483   }
9485   if (!mNetworkTriggerTimer) {
9486     mNetworkTriggerTimer = NS_NewTimer();
9487   }
9488   mNetworkTriggerTimer->InitWithCallback(this, aDelay, nsITimer::TYPE_ONE_SHOT);
9489   return NS_OK;
9492 nsresult nsHttpChannel::TriggerNetwork() {
9493   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
9495   LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n", this));
9497   if (mCanceled) {
9498     LOG(("  channel was canceled.\n"));
9499     return mStatus;
9500   }
9502   // If a network request has already gone out, there is no point in
9503   // doing this again.
9504   if (mNetworkTriggered) {
9505     LOG(("  network already triggered. Returning.\n"));
9506     return NS_OK;
9507   }
9509   mNetworkTriggered = true;
9510   if (mNetworkTriggerTimer) {
9511     mNetworkTriggerTimer->Cancel();
9512     mNetworkTriggerTimer = nullptr;
9513   }
9515   // If we are waiting for a proxy request, that means we can't trigger
9516   // the next step just yet. We need for mConnectionInfo to be non-null
9517   // before we call ContinueConnect. OnProxyAvailable will trigger
9518   // BeginConnect, and Connect will call ContinueConnect even if it's
9519   // for the cache callbacks.
9520   if (mProxyRequest) {
9521     LOG(("  proxy request in progress. Delaying network trigger.\n"));
9522     mWaitingForProxy = true;
9523     return NS_OK;
9524   }
9526   if (AwaitingCacheCallbacks()) {
9527     mRaceCacheWithNetwork = sRCWNEnabled;
9528   }
9530   LOG(("  triggering network\n"));
9531   return ContinueConnect();
9534 nsresult nsHttpChannel::MaybeRaceCacheWithNetwork() {
9535   nsresult rv;
9537   nsCOMPtr<nsINetworkLinkService> netLinkSvc =
9538       do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
9539   NS_ENSURE_SUCCESS(rv, rv);
9541   uint32_t linkType;
9542   rv = netLinkSvc->GetLinkType(&linkType);
9543   NS_ENSURE_SUCCESS(rv, rv);
9545   if (!(linkType == nsINetworkLinkService::LINK_TYPE_UNKNOWN ||
9546         linkType == nsINetworkLinkService::LINK_TYPE_ETHERNET ||
9547         linkType == nsINetworkLinkService::LINK_TYPE_USB ||
9548         linkType == nsINetworkLinkService::LINK_TYPE_WIFI)) {
9549     return NS_OK;
9550   }
9552   // Don't trigger the network if the load flags say so.
9553   if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
9554     return NS_OK;
9555   }
9557   // We must not race if the channel has a failure status code.
9558   if (NS_FAILED(mStatus)) {
9559     return NS_OK;
9560   }
9562   // If a CORS Preflight is required we must not race.
9563   if (mRequireCORSPreflight && !mIsCorsPreflightDone) {
9564     return NS_OK;
9565   }
9567   if (CacheFileUtils::CachePerfStats::IsCacheSlow()) {
9568     // If the cache is slow, trigger the network request immediately.
9569     mRaceDelay = 0;
9570   } else {
9571     // Give cache a headstart of 3 times the average cache entry open time.
9572     mRaceDelay = CacheFileUtils::CachePerfStats::GetAverage(
9573                      CacheFileUtils::CachePerfStats::ENTRY_OPEN, true) *
9574                  3;
9575     // We use microseconds in CachePerfStats but we need milliseconds
9576     // for TriggerNetwork.
9577     mRaceDelay /= 1000;
9578   }
9580   mRaceDelay = clamped<uint32_t>(mRaceDelay, sRCWNMinWaitMs, sRCWNMaxWaitMs);
9582   MOZ_ASSERT(sRCWNEnabled, "The pref must be turned on.");
9583   LOG(("nsHttpChannel::MaybeRaceCacheWithNetwork [this=%p, delay=%u]\n", this,
9584        mRaceDelay));
9586   return TriggerNetworkWithDelay(mRaceDelay);
9589 NS_IMETHODIMP
9590 nsHttpChannel::Test_triggerNetwork(int32_t aTimeout) {
9591   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
9592   return TriggerNetworkWithDelay(aTimeout);
9595 NS_IMETHODIMP
9596 nsHttpChannel::Notify(nsITimer *aTimer) {
9597   RefPtr<nsHttpChannel> self(this);
9598   if (aTimer == mCacheOpenTimer) {
9599     return Test_triggerDelayedOpenCacheEntry();
9600   } else if (aTimer == mNetworkTriggerTimer) {
9601     return TriggerNetwork();
9602   } else {
9603     MOZ_CRASH("Unknown timer");
9604   }
9606   return NS_OK;
9609 bool nsHttpChannel::EligibleForTailing() {
9610   if (!(mClassOfService & nsIClassOfService::Tail)) {
9611     return false;
9612   }
9614   if (mClassOfService &
9615       (nsIClassOfService::UrgentStart | nsIClassOfService::Leader |
9616        nsIClassOfService::TailForbidden)) {
9617     return false;
9618   }
9620   if (mClassOfService & nsIClassOfService::Unblocked &&
9621       !(mClassOfService & nsIClassOfService::TailAllowed)) {
9622     return false;
9623   }
9625   if (IsNavigation()) {
9626     return false;
9627   }
9629   return true;
9632 bool nsHttpChannel::WaitingForTailUnblock() {
9633   nsresult rv;
9635   if (!gHttpHandler->IsTailBlockingEnabled()) {
9636     LOG(("nsHttpChannel %p tail-blocking disabled", this));
9637     return false;
9638   }
9640   if (!EligibleForTailing()) {
9641     LOG(("nsHttpChannel %p not eligible for tail-blocking", this));
9642     AddAsNonTailRequest();
9643     return false;
9644   }
9646   if (!EnsureRequestContext()) {
9647     LOG(("nsHttpChannel %p no request context", this));
9648     return false;
9649   }
9651   LOG(("nsHttpChannel::WaitingForTailUnblock this=%p, rc=%p", this,
9652        mRequestContext.get()));
9654   bool blocked;
9655   rv = mRequestContext->IsContextTailBlocked(this, &blocked);
9656   if (NS_FAILED(rv)) {
9657     return false;
9658   }
9660   LOG(("  blocked=%d", blocked));
9662   return blocked;
9665 //-----------------------------------------------------------------------------
9666 // nsHttpChannel::nsIRequestTailUnblockCallback
9667 //-----------------------------------------------------------------------------
9669 // Must be implemented in the leaf class because we don't have
9670 // AsyncAbort in HttpBaseChannel.
9671 NS_IMETHODIMP
9672 nsHttpChannel::OnTailUnblock(nsresult rv) {
9673   LOG(("nsHttpChannel::OnTailUnblock this=%p rv=%" PRIx32 " rc=%p", this,
9674        static_cast<uint32_t>(rv), mRequestContext.get()));
9676   MOZ_RELEASE_ASSERT(mOnTailUnblock);
9678   if (NS_FAILED(mStatus)) {
9679     rv = mStatus;
9680   }
9682   if (NS_SUCCEEDED(rv)) {
9683     auto callback = mOnTailUnblock;
9684     mOnTailUnblock = nullptr;
9685     rv = (this->*callback)();
9686   }
9688   if (NS_FAILED(rv)) {
9689     CloseCacheEntry(false);
9690     return AsyncAbort(rv);
9691   }
9693   return NS_OK;
9696 void nsHttpChannel::SetWarningReporter(
9697     HttpChannelSecurityWarningReporter *aReporter) {
9698   LOG(("nsHttpChannel [this=%p] SetWarningReporter [%p]", this, aReporter));
9699   mWarningReporter = aReporter;
9702 HttpChannelSecurityWarningReporter *nsHttpChannel::GetWarningReporter() {
9703   LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this,
9704        mWarningReporter.get()));
9705   return mWarningReporter.get();
9708 namespace {
9710 class CopyNonDefaultHeaderVisitor final : public nsIHttpHeaderVisitor {
9711   nsCOMPtr<nsIHttpChannel> mTarget;
9713   ~CopyNonDefaultHeaderVisitor() = default;
9715   NS_IMETHOD
9716   VisitHeader(const nsACString &aHeader, const nsACString &aValue) override {
9717     if (aValue.IsEmpty()) {
9718       return mTarget->SetEmptyRequestHeader(aHeader);
9719     } else {
9720       return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
9721     }
9722   }
9724  public:
9725   explicit CopyNonDefaultHeaderVisitor(nsIHttpChannel *aTarget)
9726       : mTarget(aTarget) {
9727     MOZ_DIAGNOSTIC_ASSERT(mTarget);
9728   }
9730   NS_DECL_ISUPPORTS
9733 NS_IMPL_ISUPPORTS(CopyNonDefaultHeaderVisitor, nsIHttpHeaderVisitor)
9735 }  // anonymous namespace
9737 nsresult nsHttpChannel::RedirectToInterceptedChannel() {
9738   nsCOMPtr<nsINetworkInterceptController> controller;
9739   GetCallback(controller);
9741   RefPtr<InterceptedHttpChannel> intercepted =
9742       InterceptedHttpChannel::CreateForInterception(
9743           mChannelCreationTime, mChannelCreationTimestamp, mAsyncOpenTime);
9745   nsContentPolicyType type = mLoadInfo
9746                                  ? mLoadInfo->GetExternalContentPolicyType()
9747                                  : nsIContentPolicy::TYPE_OTHER;
9749   nsresult rv = intercepted->Init(
9750       mURI, mCaps, static_cast<nsProxyInfo *>(mProxyInfo.get()),
9751       mProxyResolveFlags, mProxyURI, mChannelId, type);
9753   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
9754       CloneLoadInfoForRedirect(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
9755   intercepted->SetLoadInfo(redirectLoadInfo);
9757   rv = SetupReplacementChannel(mURI, intercepted, true,
9758                                nsIChannelEventSink::REDIRECT_INTERNAL);
9759   NS_ENSURE_SUCCESS(rv, rv);
9761   // Some APIs, like fetch(), allow content to set non-standard headers.
9762   // Normally these APIs are responsible for copying these headers across
9763   // redirects.  In the e10s parent-side intercept case, though, we currently
9764   // "hide" the internal redirect to the InterceptedHttpChannel.  So the
9765   // fetch() API does not have the opportunity to move headers over.
9766   // Therefore, we do it automatically here.
9767   //
9768   // Once child-side interception is removed and the internal redirect no
9769   // longer needs to be "hidden", then this header copying code can be
9770   // removed.
9771   if (ServiceWorkerParentInterceptEnabled()) {
9772     nsCOMPtr<nsIHttpHeaderVisitor> visitor =
9773         new CopyNonDefaultHeaderVisitor(intercepted);
9774     rv = VisitNonDefaultRequestHeaders(visitor);
9775     NS_ENSURE_SUCCESS(rv, rv);
9776   }
9778   mRedirectChannel = intercepted;
9780   PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
9782   rv = gHttpHandler->AsyncOnChannelRedirect(
9783       this, intercepted, nsIChannelEventSink::REDIRECT_INTERNAL);
9785   if (NS_SUCCEEDED(rv)) {
9786     rv = WaitForRedirectCallback();
9787   }
9789   if (NS_FAILED(rv)) {
9790     AutoRedirectVetoNotifier notifier(this);
9792     PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
9793   }
9795   return rv;
9798 }  // namespace net
9799 }  // namespace mozilla