1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 sw=4 sts=4 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/. */
6 
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
9 
10 #include <inttypes.h>
11 
12 #include "mozilla/dom/nsCSPContext.h"
13 #include "mozilla/ScopeExit.h"
14 #include "mozilla/Sprintf.h"
15 
16 #include "nsHttp.h"
17 #include "nsHttpChannel.h"
18 #include "nsHttpHandler.h"
19 #include "nsIApplicationCacheService.h"
20 #include "nsIApplicationCacheContainer.h"
21 #include "nsICacheStorageService.h"
22 #include "nsICacheStorage.h"
23 #include "nsICacheEntry.h"
24 #include "nsICaptivePortalService.h"
25 #include "nsICryptoHash.h"
26 #include "nsINetworkInterceptController.h"
27 #include "nsINSSErrorsService.h"
28 #include "nsISecurityReporter.h"
29 #include "nsIStringBundle.h"
30 #include "nsIStreamListenerTee.h"
31 #include "nsISeekableStream.h"
32 #include "nsILoadGroupChild.h"
33 #include "nsIProtocolProxyService2.h"
34 #include "nsIURIClassifier.h"
35 #include "nsMimeTypes.h"
36 #include "nsNetCID.h"
37 #include "nsNetUtil.h"
38 #include "nsIURL.h"
39 #include "nsIURIMutator.h"
40 #include "nsIStreamTransportService.h"
41 #include "prnetdb.h"
42 #include "nsEscape.h"
43 #include "nsStreamUtils.h"
44 #include "nsIOService.h"
45 #include "nsDNSPrefetch.h"
46 #include "nsChannelClassifier.h"
47 #include "nsIRedirectResultListener.h"
48 #include "mozilla/dom/ContentVerifier.h"
49 #include "mozilla/TimeStamp.h"
50 #include "nsError.h"
51 #include "nsPrintfCString.h"
52 #include "nsAlgorithm.h"
53 #include "nsQueryObject.h"
54 #include "nsThreadUtils.h"
55 #include "GeckoProfiler.h"
56 #include "nsIConsoleService.h"
57 #include "mozilla/Attributes.h"
58 #include "mozilla/DebugOnly.h"
59 #include "mozilla/Preferences.h"
60 #include "nsISSLSocketControl.h"
61 #include "sslt.h"
62 #include "nsContentUtils.h"
63 #include "nsContentSecurityManager.h"
64 #include "nsIClassOfService.h"
65 #include "nsIPermissionManager.h"
66 #include "nsIPrincipal.h"
67 #include "nsIScriptError.h"
68 #include "nsIScriptSecurityManager.h"
69 #include "nsISSLStatus.h"
70 #include "nsISSLStatusProvider.h"
71 #include "nsITransportSecurityInfo.h"
72 #include "nsIWebProgressListener.h"
73 #include "LoadContextInfo.h"
74 #include "netCore.h"
75 #include "nsHttpTransaction.h"
76 #include "nsICacheEntryDescriptor.h"
77 #include "nsICancelable.h"
78 #include "nsIHttpChannelAuthProvider.h"
79 #include "nsIHttpChannelInternal.h"
80 #include "nsIPrompt.h"
81 #include "nsInputStreamPump.h"
82 #include "nsURLHelper.h"
83 #include "nsISocketTransport.h"
84 #include "nsIStreamConverterService.h"
85 #include "nsISiteSecurityService.h"
86 #include "nsString.h"
87 #include "nsCRT.h"
88 #include "CacheObserver.h"
89 #include "mozilla/dom/PerformanceStorage.h"
90 #include "mozilla/Telemetry.h"
91 #include "AlternateServices.h"
92 #include "InterceptedChannel.h"
93 #include "nsIHttpPushListener.h"
94 #include "nsIX509Cert.h"
95 #include "ScopedNSSTypes.h"
96 #include "NullPrincipal.h"
97 #include "nsIDeprecationWarner.h"
98 #include "nsIDocument.h"
99 #include "nsIDOMDocument.h"
100 #include "nsICompressConvStats.h"
101 #include "nsCORSListenerProxy.h"
102 #include "nsISocketProvider.h"
103 #include "mozilla/extensions/StreamFilterParent.h"
104 #include "mozilla/net/Predictor.h"
105 #include "mozilla/MathAlgorithms.h"
106 #include "CacheControlParser.h"
107 #include "nsMixedContentBlocker.h"
108 #include "CacheStorageService.h"
109 #include "HttpChannelParent.h"
110 #include "InterceptedHttpChannel.h"
111 #include "nsIBufferedStreams.h"
112 #include "nsIFileStreams.h"
113 #include "nsIMIMEInputStream.h"
114 #include "nsIMultiplexInputStream.h"
115 #include "../../cache2/CacheFileUtils.h"
116 
117 #ifdef MOZ_TASK_TRACER
118 #include "GeckoTaskTracer.h"
119 #endif
120 
121 namespace mozilla {
122 namespace net {
123 
124 namespace {
125 
126 static bool sRCWNEnabled = false;
127 static uint32_t sRCWNQueueSizeNormal = 50;
128 static uint32_t sRCWNQueueSizePriority = 10;
129 static uint32_t sRCWNSmallResourceSizeKB = 256;
130 static uint32_t sRCWNMinWaitMs = 0;
131 static uint32_t sRCWNMaxWaitMs = 500;
132 
133 // True if the local cache should be bypassed when processing a request.
134 #define BYPASS_LOCAL_CACHE(loadFlags)           \
135   (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
136                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
137 
138 #define RECOVER_FROM_CACHE_FILE_ERROR(result) \
139   ((result) == NS_ERROR_FILE_NOT_FOUND ||     \
140    (result) == NS_ERROR_FILE_CORRUPTED || (result) == NS_ERROR_OUT_OF_MEMORY)
141 
142 #define WRONG_RACING_RESPONSE_SOURCE(req)                                     \
143   (mRaceCacheWithNetwork &&                                                   \
144    (((mFirstResponseSource == RESPONSE_FROM_CACHE) && (req != mCachePump)) || \
145     ((mFirstResponseSource == RESPONSE_FROM_NETWORK) &&                       \
146      (req != mTransactionPump))))
147 
148 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
149 
150 enum CacheDisposition {
151   kCacheHit = 1,
152   kCacheHitViaReval = 2,
153   kCacheMissedViaReval = 3,
154   kCacheMissed = 4
155 };
156 
AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss)157 void AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss) {
158   Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2, hitOrMiss);
159 }
160 
161 // Computes and returns a SHA1 hash of the input buffer. The input buffer
162 // must be a null-terminated string.
Hash(const char * buf,nsACString & hash)163 nsresult Hash(const char *buf, nsACString &hash) {
164   nsresult rv;
165 
166   nsCOMPtr<nsICryptoHash> hasher =
167       do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
168   NS_ENSURE_SUCCESS(rv, rv);
169 
170   rv = hasher->Init(nsICryptoHash::SHA1);
171   NS_ENSURE_SUCCESS(rv, rv);
172 
173   rv =
174       hasher->Update(reinterpret_cast<unsigned const char *>(buf), strlen(buf));
175   NS_ENSURE_SUCCESS(rv, rv);
176 
177   rv = hasher->Finish(true, hash);
178   NS_ENSURE_SUCCESS(rv, rv);
179 
180   return NS_OK;
181 }
182 
IsInSubpathOfAppCacheManifest(nsIApplicationCache * cache,nsACString const & uriSpec)183 bool IsInSubpathOfAppCacheManifest(nsIApplicationCache *cache,
184                                    nsACString const &uriSpec) {
185   MOZ_ASSERT(cache);
186 
187   nsresult rv;
188 
189   nsCOMPtr<nsIURI> uri;
190   rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
191   if (NS_FAILED(rv)) {
192     return false;
193   }
194 
195   nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv));
196   if (NS_FAILED(rv)) {
197     return false;
198   }
199 
200   nsAutoCString directory;
201   rv = url->GetDirectory(directory);
202   if (NS_FAILED(rv)) {
203     return false;
204   }
205 
206   nsCOMPtr<nsIURI> manifestURI;
207   rv = cache->GetManifestURI(getter_AddRefs(manifestURI));
208   if (NS_FAILED(rv)) {
209     return false;
210   }
211 
212   nsCOMPtr<nsIURL> manifestURL(do_QueryInterface(manifestURI, &rv));
213   if (NS_FAILED(rv)) {
214     return false;
215   }
216 
217   nsAutoCString manifestDirectory;
218   rv = manifestURL->GetDirectory(manifestDirectory);
219   if (NS_FAILED(rv)) {
220     return false;
221   }
222 
223   return StringBeginsWith(directory, manifestDirectory);
224 }
225 
226 }  // unnamed namespace
227 
228 // We only treat 3xx responses as redirects if they have a Location header and
229 // the status code is in a whitelist.
WillRedirect(nsHttpResponseHead * response)230 bool nsHttpChannel::WillRedirect(nsHttpResponseHead *response) {
231   return IsRedirectStatus(response->Status()) &&
232          response->HasHeader(nsHttp::Location);
233 }
234 
235 nsresult StoreAuthorizationMetaData(nsICacheEntry *entry,
236                                     nsHttpRequestHead *requestHead);
237 
238 class AutoRedirectVetoNotifier {
239  public:
AutoRedirectVetoNotifier(nsHttpChannel * channel)240   explicit AutoRedirectVetoNotifier(nsHttpChannel *channel)
241       : mChannel(channel) {
242     if (mChannel->mHasAutoRedirectVetoNotifier) {
243       MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");
244       mChannel = nullptr;
245       return;
246     }
247 
248     mChannel->mHasAutoRedirectVetoNotifier = true;
249   }
~AutoRedirectVetoNotifier()250   ~AutoRedirectVetoNotifier() { ReportRedirectResult(false); }
RedirectSucceeded()251   void RedirectSucceeded() { ReportRedirectResult(true); }
252 
253  private:
254   nsHttpChannel *mChannel;
255   void ReportRedirectResult(bool succeeded);
256 };
257 
ReportRedirectResult(bool succeeded)258 void AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded) {
259   if (!mChannel) return;
260 
261   mChannel->mRedirectChannel = nullptr;
262 
263   if (succeeded) {
264     mChannel->RemoveAsNonTailRequest();
265   }
266 
267   nsCOMPtr<nsIRedirectResultListener> vetoHook;
268   NS_QueryNotificationCallbacks(mChannel, NS_GET_IID(nsIRedirectResultListener),
269                                 getter_AddRefs(vetoHook));
270 
271   nsHttpChannel *channel = mChannel;
272   mChannel = nullptr;
273 
274   if (vetoHook) vetoHook->OnRedirectResult(succeeded);
275 
276   // Drop after the notification
277   channel->mHasAutoRedirectVetoNotifier = false;
278 }
279 
280 //-----------------------------------------------------------------------------
281 // nsHttpChannel <public>
282 //-----------------------------------------------------------------------------
283 
nsHttpChannel()284 nsHttpChannel::nsHttpChannel()
285     : HttpAsyncAborter<nsHttpChannel>(this),
286       mLogicalOffset(0),
287       mPostID(0),
288       mRequestTime(0),
289       mOfflineCacheLastModifiedTime(0),
290       mSuspendTotalTime(0),
291       mCacheOpenWithPriority(false),
292       mCacheQueueSizeWhenOpen(0),
293       mCachedContentIsValid(false),
294       mCachedContentIsPartial(false),
295       mCacheOnlyMetadata(false),
296       mTransactionReplaced(false),
297       mAuthRetryPending(false),
298       mProxyAuthPending(false),
299       mCustomAuthHeader(false),
300       mResuming(false),
301       mInitedCacheEntry(false),
302       mFallbackChannel(false),
303       mCustomConditionalRequest(false),
304       mFallingBack(false),
305       mWaitingForRedirectCallback(false),
306       mRequestTimeInitialized(false),
307       mCacheEntryIsReadOnly(false),
308       mCacheEntryIsWriteOnly(false),
309       mCacheEntriesToWaitFor(0),
310       mHasQueryString(0),
311       mConcurrentCacheAccess(0),
312       mIsPartialRequest(0),
313       mHasAutoRedirectVetoNotifier(0),
314       mPinCacheContent(0),
315       mIsCorsPreflightDone(0),
316       mStronglyFramed(false),
317       mUsedNetwork(0),
318       mAuthConnectionRestartable(0),
319       mPushedStream(nullptr),
320       mOnTailUnblock(nullptr),
321       mWarningReporter(nullptr),
322       mIsReadingFromCache(false),
323       mFirstResponseSource(RESPONSE_PENDING),
324       mRaceCacheWithNetwork(false),
325       mRaceDelay(0),
326       mIgnoreCacheEntry(false),
327       mRCWNLock("nsHttpChannel.mRCWNLock"),
328       mDidReval(false) {
329   LOG(("Creating nsHttpChannel [this=%p]\n", this));
330   mChannelCreationTime = PR_Now();
331   mChannelCreationTimestamp = TimeStamp::Now();
332 }
333 
~nsHttpChannel()334 nsHttpChannel::~nsHttpChannel() {
335   LOG(("Destroying nsHttpChannel [this=%p]\n", this));
336 
337   if (mAuthProvider) {
338     DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
339     MOZ_ASSERT(NS_SUCCEEDED(rv));
340   }
341 
342   ReleaseMainThreadOnlyReferences();
343 }
344 
ReleaseMainThreadOnlyReferences()345 void nsHttpChannel::ReleaseMainThreadOnlyReferences() {
346   if (NS_IsMainThread()) {
347     // Already on main thread, let dtor to
348     // take care of releasing references
349     return;
350   }
351 
352   nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
353   arrayToRelease.AppendElement(mApplicationCacheForWrite.forget());
354   arrayToRelease.AppendElement(mAuthProvider.forget());
355   arrayToRelease.AppendElement(mRedirectURI.forget());
356   arrayToRelease.AppendElement(mRedirectChannel.forget());
357   arrayToRelease.AppendElement(mPreflightChannel.forget());
358 
359   NS_DispatchToMainThread(new ProxyReleaseRunnable(Move(arrayToRelease)));
360 }
361 
Init(nsIURI * uri,uint32_t caps,nsProxyInfo * proxyInfo,uint32_t proxyResolveFlags,nsIURI * proxyURI,uint64_t channelId)362 nsresult nsHttpChannel::Init(nsIURI *uri, uint32_t caps, nsProxyInfo *proxyInfo,
363                              uint32_t proxyResolveFlags, nsIURI *proxyURI,
364                              uint64_t channelId) {
365   nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, proxyResolveFlags,
366                                       proxyURI, channelId);
367   if (NS_FAILED(rv)) return rv;
368 
369   LOG(("nsHttpChannel::Init [this=%p]\n", this));
370 
371   return rv;
372 }
373 
AddSecurityMessage(const nsAString & aMessageTag,const nsAString & aMessageCategory)374 nsresult nsHttpChannel::AddSecurityMessage(const nsAString &aMessageTag,
375                                            const nsAString &aMessageCategory) {
376   if (mWarningReporter) {
377     return mWarningReporter->ReportSecurityMessage(aMessageTag,
378                                                    aMessageCategory);
379   }
380   return HttpBaseChannel::AddSecurityMessage(aMessageTag, aMessageCategory);
381 }
382 
383 NS_IMETHODIMP
LogBlockedCORSRequest(const nsAString & aMessage)384 nsHttpChannel::LogBlockedCORSRequest(const nsAString &aMessage) {
385   if (mWarningReporter) {
386     return mWarningReporter->LogBlockedCORSRequest(aMessage);
387   }
388   return NS_ERROR_UNEXPECTED;
389 }
390 
391 //-----------------------------------------------------------------------------
392 // nsHttpChannel <private>
393 //-----------------------------------------------------------------------------
394 
OnBeforeConnect()395 nsresult nsHttpChannel::OnBeforeConnect() {
396   nsresult rv;
397 
398   // Note that we are only setting the "Upgrade-Insecure-Requests" request
399   // header for *all* navigational requests instead of all requests as
400   // defined in the spec, see:
401   // https://www.w3.org/TR/upgrade-insecure-requests/#preference
402   nsContentPolicyType type = mLoadInfo
403                                  ? mLoadInfo->GetExternalContentPolicyType()
404                                  : nsIContentPolicy::TYPE_OTHER;
405 
406   if (type == nsIContentPolicy::TYPE_DOCUMENT ||
407       type == nsIContentPolicy::TYPE_SUBDOCUMENT) {
408     rv = SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
409                           NS_LITERAL_CSTRING("1"), false);
410     NS_ENSURE_SUCCESS(rv, rv);
411   }
412 
413   bool isHttps = false;
414   rv = mURI->SchemeIs("https", &isHttps);
415   NS_ENSURE_SUCCESS(rv, rv);
416   nsCOMPtr<nsIPrincipal> resultPrincipal;
417   if (!isHttps && mLoadInfo) {
418     nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
419         this, getter_AddRefs(resultPrincipal));
420   }
421   OriginAttributes originAttributes;
422   if (!NS_GetOriginAttributes(this, originAttributes)) {
423     return NS_ERROR_FAILURE;
424   }
425   bool isHttp = false;
426   rv = mURI->SchemeIs("http", &isHttp);
427   NS_ENSURE_SUCCESS(rv, rv);
428 
429   // At this point it is no longer possible to call
430   // HttpBaseChannel::UpgradeToSecure.
431   mUpgradableToSecure = false;
432   if (isHttp) {
433     bool shouldUpgrade = mUpgradeToSecure;
434     if (!shouldUpgrade) {
435       rv = NS_ShouldSecureUpgrade(mURI, mLoadInfo, resultPrincipal,
436                                   mPrivateBrowsing, mAllowSTS, originAttributes,
437                                   shouldUpgrade);
438       NS_ENSURE_SUCCESS(rv, rv);
439     }
440     if (shouldUpgrade) {
441       return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
442     }
443   }
444 
445   // ensure that we are using a valid hostname
446   if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Origin())))
447     return NS_ERROR_UNKNOWN_HOST;
448 
449   if (mUpgradeProtocolCallback) {
450     mCaps |= NS_HTTP_DISALLOW_SPDY;
451   }
452 
453   // Finalize ConnectionInfo flags before SpeculativeConnect
454   mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
455   mConnectionInfo->SetPrivate(mPrivateBrowsing);
456   mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
457   mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
458                                      mBeConservative);
459   mConnectionInfo->SetTlsFlags(mTlsFlags);
460 
461   // notify "http-on-before-connect" observers
462   gHttpHandler->OnBeforeConnect(this);
463 
464   // Check if request was cancelled during http-on-before-connect.
465   if (mCanceled) {
466     return mStatus;
467   }
468 
469   if (mSuspendCount) {
470     // We abandon the connection here if there was one.
471     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
472     MOZ_ASSERT(!mCallOnResume);
473     mCallOnResume = &nsHttpChannel::OnBeforeConnectContinue;
474     return NS_OK;
475   }
476 
477   return Connect();
478 }
479 
OnBeforeConnectContinue()480 void nsHttpChannel::OnBeforeConnectContinue() {
481   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
482   nsresult rv;
483 
484   if (mSuspendCount) {
485     LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
486     mCallOnResume = &nsHttpChannel::OnBeforeConnectContinue;
487     return;
488   }
489 
490   LOG(("nsHttpChannel::OnBeforeConnectContinue [this=%p]\n", this));
491   rv = Connect();
492   if (NS_FAILED(rv)) {
493     CloseCacheEntry(false);
494     Unused << AsyncAbort(rv);
495   }
496 }
497 
Connect()498 nsresult nsHttpChannel::Connect() {
499   LOG(("nsHttpChannel::Connect [this=%p]\n", this));
500 
501   // Don't allow resuming when cache must be used
502   if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
503     LOG(("Resuming from cache is not supported yet"));
504     return NS_ERROR_DOCUMENT_NOT_CACHED;
505   }
506 
507   if (ShouldIntercept()) {
508     return RedirectToInterceptedChannel();
509   }
510 
511   bool isTrackingResource = mIsTrackingResource;  // is atomic
512   LOG(("nsHttpChannel %p tracking resource=%d, cos=%u", this,
513        isTrackingResource, mClassOfService));
514 
515   if (isTrackingResource) {
516     AddClassFlags(nsIClassOfService::Tail);
517   }
518 
519   if (WaitingForTailUnblock()) {
520     MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
521     mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock;
522     return NS_OK;
523   }
524 
525   return ConnectOnTailUnblock();
526 }
527 
ConnectOnTailUnblock()528 nsresult nsHttpChannel::ConnectOnTailUnblock() {
529   nsresult rv;
530 
531   LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this));
532 
533   // Consider opening a TCP connection right away.
534   SpeculativeConnect();
535 
536   // open a cache entry for this channel...
537   bool isHttps = false;
538   rv = mURI->SchemeIs("https", &isHttps);
539   NS_ENSURE_SUCCESS(rv, rv);
540   rv = OpenCacheEntry(isHttps);
541 
542   // do not continue if asyncOpenCacheEntry is in progress
543   if (AwaitingCacheCallbacks()) {
544     LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n",
545          this));
546     MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
547 
548     if (mNetworkTriggered && mWaitingForProxy) {
549       // Someone has called TriggerNetwork(), meaning we are racing the
550       // network with the cache.
551       mWaitingForProxy = false;
552       return ContinueConnect();
553     }
554 
555     return NS_OK;
556   }
557 
558   if (NS_FAILED(rv)) {
559     LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n",
560          static_cast<uint32_t>(rv)));
561     // if this channel is only allowed to pull from the cache, then
562     // we must fail if we were unable to open a cache entry.
563     if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
564       // If we have a fallback URI (and we're not already
565       // falling back), process the fallback asynchronously.
566       if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
567         return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
568       }
569       return NS_ERROR_DOCUMENT_NOT_CACHED;
570     }
571     // otherwise, let's just proceed without using the cache.
572   }
573 
574   if (mRaceCacheWithNetwork && ((mCacheEntry && !mCachedContentIsValid &&
575                                  (mDidReval || mCachedContentIsPartial)) ||
576                                 mIgnoreCacheEntry)) {
577     // We won't send the conditional request because the unconditional
578     // request was already sent (see bug 1377223).
579     AccumulateCategorical(
580         Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent);
581   }
582 
583   // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI
584   // returns, then we may not have started reading from the cache.
585   // If the content is valid, we should attempt to do so, as technically the
586   // cache has won the race.
587   if (mRaceCacheWithNetwork && mCachedContentIsValid) {
588     Unused << ReadFromCache(true);
589   }
590 
591   return TriggerNetwork();
592 }
593 
594 // nsIInputAvailableCallback (nsIStreamTransportService.idl)
595 NS_IMETHODIMP
OnInputAvailableComplete(uint64_t size,nsresult status)596 nsHttpChannel::OnInputAvailableComplete(uint64_t size, nsresult status) {
597   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
598   LOG(("nsHttpChannel::OnInputAvailableComplete %p %" PRIx32 "\n", this,
599        static_cast<uint32_t>(status)));
600   if (NS_SUCCEEDED(status)) {
601     mReqContentLength = size;
602   } else {
603     // fall back to synchronous on the error path. should not happen.
604     if (NS_SUCCEEDED(mUploadStream->Available(&size))) {
605       mReqContentLength = size;
606     }
607   }
608 
609   LOG(("nsHttpChannel::DetermineContentLength %p from sts\n", this));
610   mReqContentLengthDetermined = 1;
611   nsresult rv = mCanceled ? mStatus : ContinueConnect();
612   if (NS_FAILED(rv)) {
613     CloseCacheEntry(false);
614     Unused << AsyncAbort(rv);
615   }
616   return NS_OK;
617 }
618 
DetermineContentLength()619 void nsHttpChannel::DetermineContentLength() {
620   nsCOMPtr<nsIStreamTransportService> sts(
621       services::GetStreamTransportService());
622 
623   if (!mUploadStream || !sts) {
624     LOG(("nsHttpChannel::DetermineContentLength %p no body\n", this));
625     mReqContentLength = 0U;
626     mReqContentLengthDetermined = 1;
627     return;
628   }
629 
630   // If this is a stream is blocking, it needs to be sent to a worker thread
631   // to do Available() as it may cause disk/IO.
632   bool nonBlocking = false;
633   if (NS_FAILED(mUploadStream->IsNonBlocking(&nonBlocking)) || nonBlocking) {
634     mUploadStream->Available(&mReqContentLength);
635     LOG(("nsHttpChannel::DetermineContentLength %p from mem\n", this));
636     mReqContentLengthDetermined = 1;
637     return;
638   }
639 
640   LOG(("nsHttpChannel::DetermineContentLength Async [this=%p]\n", this));
641   sts->InputAvailable(mUploadStream, this);
642 }
643 
ContinueConnect()644 nsresult nsHttpChannel::ContinueConnect() {
645   // If we have a request body that is going to require bouncing to the STS
646   // in order to determine the content-length as doing it on the main thread
647   // will incur file IO some of the time.
648   if (!mReqContentLengthDetermined) {
649     // C-L might be determined sync or async. Sync will set
650     // mReqContentLengthDetermined to true in DetermineContentLength()
651     DetermineContentLength();
652   }
653   if (!mReqContentLengthDetermined) {
654     return NS_OK;
655   }
656 
657   // If we need to start a CORS preflight, do it now!
658   // Note that it is important to do this before the early returns below.
659   if (!mIsCorsPreflightDone && mRequireCORSPreflight) {
660     MOZ_ASSERT(!mPreflightChannel);
661     nsresult rv = nsCORSListenerProxy::StartCORSPreflight(
662         this, this, mUnsafeHeaders, getter_AddRefs(mPreflightChannel));
663     return rv;
664   }
665 
666   MOZ_RELEASE_ASSERT(!mRequireCORSPreflight || mIsCorsPreflightDone,
667                      "CORS preflight must have been finished by the time we "
668                      "do the rest of ContinueConnect");
669 
670   // we may or may not have a cache entry at this point
671   if (mCacheEntry) {
672     // read straight from the cache if possible...
673     if (mCachedContentIsValid) {
674       nsRunnableMethod<nsHttpChannel> *event = nullptr;
675       nsresult rv;
676       if (!mCachedContentIsPartial) {
677         rv = AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
678         if (NS_FAILED(rv)) {
679           LOG(("  AsyncCall failed (%08x)", static_cast<uint32_t>(rv)));
680         }
681       }
682       rv = ReadFromCache(true);
683       if (NS_FAILED(rv) && event) {
684         event->Revoke();
685       }
686 
687       AccumulateCacheHitTelemetry(kCacheHit);
688 
689       return rv;
690     } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
691       // the cache contains the requested resource, but it must be
692       // validated before we can reuse it.  since we are not allowed
693       // to hit the net, there's nothing more to do.  the document
694       // is effectively not in the cache.
695       LOG(("  !mCachedContentIsValid && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
696       return NS_ERROR_DOCUMENT_NOT_CACHED;
697     }
698   } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
699     // If we have a fallback URI (and we're not already
700     // falling back), process the fallback asynchronously.
701     if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
702       return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
703     }
704     LOG(("  !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
705     return NS_ERROR_DOCUMENT_NOT_CACHED;
706   }
707 
708   if (mLoadFlags & LOAD_NO_NETWORK_IO) {
709     LOG(("  mLoadFlags & LOAD_NO_NETWORK_IO"));
710     return NS_ERROR_DOCUMENT_NOT_CACHED;
711   }
712 
713   // hit the net...
714   nsresult rv = SetupTransaction();
715   if (NS_FAILED(rv)) return rv;
716 
717   rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
718   if (NS_FAILED(rv)) return rv;
719 
720   rv = mTransactionPump->AsyncRead(this, nullptr);
721   if (NS_FAILED(rv)) return rv;
722 
723   uint32_t suspendCount = mSuspendCount;
724   while (suspendCount--) mTransactionPump->Suspend();
725 
726   return NS_OK;
727 }
728 
SpeculativeConnect()729 void nsHttpChannel::SpeculativeConnect() {
730   // Before we take the latency hit of dealing with the cache, try and
731   // get the TCP (and SSL) handshakes going so they can overlap.
732 
733   // don't speculate if we are on uses of the offline application cache,
734   // if we are offline, when doing http upgrade (i.e.
735   // websockets bootstrap), or if we can't do keep-alive (because then we
736   // couldn't reuse the speculative connection anyhow).
737   if (mApplicationCache || gIOService->IsOffline() ||
738       mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE))
739     return;
740 
741   // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
742   // LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network,
743   // so skip preconnects for them.
744   if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE |
745                     LOAD_NO_NETWORK_IO | LOAD_CHECK_OFFLINE_CACHE))
746     return;
747 
748   if (mAllowStaleCacheContent) {
749     return;
750   }
751 
752   nsCOMPtr<nsIInterfaceRequestor> callbacks;
753   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
754                                          getter_AddRefs(callbacks));
755   if (!callbacks) return;
756 
757   Unused << gHttpHandler->SpeculativeConnect(mConnectionInfo, callbacks,
758                                              mCaps & NS_HTTP_DISALLOW_SPDY);
759 }
760 
DoNotifyListenerCleanup()761 void nsHttpChannel::DoNotifyListenerCleanup() {
762   // We don't need this info anymore
763   CleanRedirectCacheChainIfNecessary();
764 }
765 
ReleaseListeners()766 void nsHttpChannel::ReleaseListeners() {
767   HttpBaseChannel::ReleaseListeners();
768   mChannelClassifier = nullptr;
769   mWarningReporter = nullptr;
770 }
771 
HandleAsyncRedirect()772 void nsHttpChannel::HandleAsyncRedirect() {
773   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
774 
775   if (mSuspendCount) {
776     LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
777     mCallOnResume = &nsHttpChannel::HandleAsyncRedirect;
778     return;
779   }
780 
781   nsresult rv = NS_OK;
782 
783   LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this));
784 
785   // since this event is handled asynchronously, it is possible that this
786   // channel could have been canceled, in which case there would be no point
787   // in processing the redirect.
788   if (NS_SUCCEEDED(mStatus)) {
789     PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
790     rv = AsyncProcessRedirection(mResponseHead->Status());
791     if (NS_FAILED(rv)) {
792       PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
793       // TODO: if !DoNotRender3xxBody(), render redirect body instead.
794       // But first we need to cache 3xx bodies (bug 748510)
795       rv = ContinueHandleAsyncRedirect(rv);
796       MOZ_ASSERT(NS_SUCCEEDED(rv));
797     }
798   } else {
799     rv = ContinueHandleAsyncRedirect(mStatus);
800     MOZ_ASSERT(NS_SUCCEEDED(rv));
801   }
802 }
803 
ContinueHandleAsyncRedirect(nsresult rv)804 nsresult nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) {
805   if (NS_FAILED(rv)) {
806     // If AsyncProcessRedirection fails, then we have to send out the
807     // OnStart/OnStop notifications.
808     LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n",
809          static_cast<uint32_t>(rv)));
810 
811     bool redirectsEnabled = !mLoadInfo || !mLoadInfo->GetDontFollowRedirects();
812 
813     if (redirectsEnabled) {
814       // TODO: stop failing original channel if redirect vetoed?
815       mStatus = rv;
816 
817       DoNotifyListener();
818 
819       // Blow away cache entry if we couldn't process the redirect
820       // for some reason (the cache entry might be corrupt).
821       if (mCacheEntry) {
822         mCacheEntry->AsyncDoom(nullptr);
823       }
824     } else {
825       DoNotifyListener();
826     }
827   }
828 
829   CloseCacheEntry(true);
830 
831   mIsPending = false;
832 
833   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
834 
835   return NS_OK;
836 }
837 
HandleAsyncNotModified()838 void nsHttpChannel::HandleAsyncNotModified() {
839   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
840 
841   if (mSuspendCount) {
842     LOG(("Waiting until resume to do async not-modified [this=%p]\n", this));
843     mCallOnResume = &nsHttpChannel::HandleAsyncNotModified;
844     return;
845   }
846 
847   LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
848 
849   DoNotifyListener();
850 
851   CloseCacheEntry(false);
852 
853   mIsPending = false;
854 
855   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
856 }
857 
HandleAsyncFallback()858 void nsHttpChannel::HandleAsyncFallback() {
859   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
860 
861   if (mSuspendCount) {
862     LOG(("Waiting until resume to do async fallback [this=%p]\n", this));
863     mCallOnResume = &nsHttpChannel::HandleAsyncFallback;
864     return;
865   }
866 
867   nsresult rv = NS_OK;
868 
869   LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n", this));
870 
871   // since this event is handled asynchronously, it is possible that this
872   // channel could have been canceled, in which case there would be no point
873   // in processing the fallback.
874   if (!mCanceled) {
875     PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
876     bool waitingForRedirectCallback;
877     rv = ProcessFallback(&waitingForRedirectCallback);
878     if (waitingForRedirectCallback) return;
879     PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
880   }
881 
882   rv = ContinueHandleAsyncFallback(rv);
883   MOZ_ASSERT(NS_SUCCEEDED(rv));
884 }
885 
ContinueHandleAsyncFallback(nsresult rv)886 nsresult nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv) {
887   if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) {
888     // If ProcessFallback fails, then we have to send out the
889     // OnStart/OnStop notifications.
890     LOG(("ProcessFallback failed [rv=%" PRIx32 ", %d]\n",
891          static_cast<uint32_t>(rv), mFallingBack));
892     mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED;
893     DoNotifyListener();
894   }
895 
896   mIsPending = false;
897 
898   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
899 
900   return rv;
901 }
902 
SetupTransaction()903 nsresult nsHttpChannel::SetupTransaction() {
904   LOG(("nsHttpChannel::SetupTransaction [this=%p, cos=%u, prio=%d]\n", this,
905        mClassOfService, mPriority));
906 
907   NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
908 
909   nsresult rv;
910 
911   mozilla::MutexAutoLock lock(mRCWNLock);
912 
913   // If we're racing cache with network, conditional or byte range header
914   // could be added in OnCacheEntryCheck. We cannot send conditional request
915   // without having the entry, so we need to remove the headers here and
916   // ignore the cache entry in OnCacheEntryAvailable.
917   if (mRaceCacheWithNetwork && AwaitingCacheCallbacks()) {
918     if (mDidReval) {
919       LOG(("  Removing conditional request headers"));
920       UntieValidationRequest();
921       mDidReval = false;
922       mIgnoreCacheEntry = true;
923     }
924 
925     if (mCachedContentIsPartial) {
926       LOG(("  Removing byte range request headers"));
927       UntieByteRangeRequest();
928       mCachedContentIsPartial = false;
929       mIgnoreCacheEntry = true;
930     }
931 
932     if (mIgnoreCacheEntry) {
933       if (!mAvailableCachedAltDataType.IsEmpty()) {
934         mAvailableCachedAltDataType.Truncate();
935         mAltDataLength = 0;
936       }
937       mCacheInputStream.CloseAndRelease();
938     }
939   }
940 
941   mUsedNetwork = 1;
942 
943   if (mTRR) {
944     mCaps |= NS_HTTP_LARGE_KEEPALIVE;
945   }
946   if (!mAllowSpdy) {
947     mCaps |= NS_HTTP_DISALLOW_SPDY;
948   }
949   if (mBeConservative) {
950     mCaps |= NS_HTTP_BE_CONSERVATIVE;
951   }
952 
953   // Use the URI path if not proxying (transparent proxying such as proxy
954   // CONNECT does not count here). Also figure out what HTTP version to use.
955   nsAutoCString buf, path;
956   nsCString *requestURI;
957 
958   // This is the normal e2e H1 path syntax "/index.html"
959   rv = mURI->GetPathQueryRef(path);
960   if (NS_FAILED(rv)) {
961     return rv;
962   }
963 
964   // path may contain UTF-8 characters, so ensure that they're escaped.
965   if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf)) {
966     requestURI = &buf;
967   } else {
968     requestURI = &path;
969   }
970 
971   // trim off the #ref portion if any...
972   int32_t ref1 = requestURI->FindChar('#');
973   if (ref1 != kNotFound) {
974     requestURI->SetLength(ref1);
975   }
976 
977   if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
978     mRequestHead.SetVersion(gHttpHandler->HttpVersion());
979   } else {
980     mRequestHead.SetPath(*requestURI);
981 
982     // RequestURI should be the absolute uri H1 proxy syntax
983     // "http://foo/index.html" so we will overwrite the relative version in
984     // requestURI
985     rv = mURI->GetUserPass(buf);
986     if (NS_FAILED(rv)) return rv;
987     if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
988                            strncmp(mSpec.get(), "https:", 6) == 0)) {
989       nsCOMPtr<nsIURI> tempURI;
990       rv = NS_MutateURI(mURI).SetUserPass(EmptyCString()).Finalize(tempURI);
991       if (NS_FAILED(rv)) return rv;
992       rv = tempURI->GetAsciiSpec(path);
993       if (NS_FAILED(rv)) return rv;
994       requestURI = &path;
995     } else {
996       requestURI = &mSpec;
997     }
998 
999     // trim off the #ref portion if any...
1000     int32_t ref2 = requestURI->FindChar('#');
1001     if (ref2 != kNotFound) {
1002       requestURI->SetLength(ref2);
1003     }
1004 
1005     mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
1006   }
1007 
1008   mRequestHead.SetRequestURI(*requestURI);
1009 
1010   // set the request time for cache expiration calculations
1011   mRequestTime = NowInSeconds();
1012   mRequestTimeInitialized = true;
1013 
1014   // if doing a reload, force end-to-end
1015   if (mLoadFlags & LOAD_BYPASS_CACHE) {
1016     // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
1017     // no proxy is configured since we might be talking with a transparent
1018     // proxy, i.e. one that operates at the network level.  See bug #14772.
1019     rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
1020     MOZ_ASSERT(NS_SUCCEEDED(rv));
1021     // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
1022     // no-cache'
1023     if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1) {
1024       rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
1025       MOZ_ASSERT(NS_SUCCEEDED(rv));
1026     }
1027   } else if ((mLoadFlags & VALIDATE_ALWAYS) && !mCacheEntryIsWriteOnly) {
1028     // We need to send 'Cache-Control: max-age=0' to force each cache along
1029     // the path to the origin server to revalidate its own entry, if any,
1030     // with the next cache or server.  See bug #84847.
1031     //
1032     // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
1033     if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
1034       rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true);
1035     else
1036       rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
1037     MOZ_ASSERT(NS_SUCCEEDED(rv));
1038   }
1039 
1040   if (mResuming) {
1041     char byteRange[32];
1042     SprintfLiteral(byteRange, "bytes=%" PRIu64 "-", mStartPos);
1043     rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
1044     MOZ_ASSERT(NS_SUCCEEDED(rv));
1045 
1046     if (!mEntityID.IsEmpty()) {
1047       // Also, we want an error if this resource changed in the meantime
1048       // Format of the entity id is: escaped_etag/size/lastmod
1049       nsCString::const_iterator start, end, slash;
1050       mEntityID.BeginReading(start);
1051       mEntityID.EndReading(end);
1052       mEntityID.BeginReading(slash);
1053 
1054       if (FindCharInReadable('/', slash, end)) {
1055         nsAutoCString ifMatch;
1056         rv = mRequestHead.SetHeader(
1057             nsHttp::If_Match,
1058             NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
1059         MOZ_ASSERT(NS_SUCCEEDED(rv));
1060 
1061         ++slash;  // Incrementing, so that searching for '/' won't find
1062                   // the same slash again
1063       }
1064 
1065       if (FindCharInReadable('/', slash, end)) {
1066         rv = mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
1067                                     Substring(++slash, end));
1068         MOZ_ASSERT(NS_SUCCEEDED(rv));
1069       }
1070     }
1071   }
1072 
1073   // create wrapper for this channel's notification callbacks
1074   nsCOMPtr<nsIInterfaceRequestor> callbacks;
1075   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
1076                                          getter_AddRefs(callbacks));
1077 
1078   // create the transaction object
1079   mTransaction = new nsHttpTransaction();
1080   LOG(("nsHttpChannel %p created nsHttpTransaction %p\n", this,
1081        mTransaction.get()));
1082   mTransaction->SetTransactionObserver(mTransactionObserver);
1083   mTransactionObserver = nullptr;
1084 
1085   // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
1086   if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS;
1087 
1088   if (mTimingEnabled) mCaps |= NS_HTTP_TIMING_ENABLED;
1089 
1090   if (mUpgradeProtocolCallback) {
1091     rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
1092     MOZ_ASSERT(NS_SUCCEEDED(rv));
1093     rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(),
1094                                     true);
1095     MOZ_ASSERT(NS_SUCCEEDED(rv));
1096     mCaps |= NS_HTTP_STICKY_CONNECTION;
1097     mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
1098   }
1099 
1100   if (mPushedStream) {
1101     mTransaction->SetPushedStream(mPushedStream);
1102     mPushedStream = nullptr;
1103   }
1104 
1105   nsCOMPtr<nsIHttpPushListener> pushListener;
1106   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
1107                                 NS_GET_IID(nsIHttpPushListener),
1108                                 getter_AddRefs(pushListener));
1109   if (pushListener) {
1110     mCaps |= NS_HTTP_ONPUSH_LISTENER;
1111   }
1112 
1113   EnsureTopLevelOuterContentWindowId();
1114 
1115   nsCOMPtr<nsIAsyncInputStream> responseStream;
1116   rv = mTransaction->Init(
1117       mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength,
1118       mUploadStreamHasHeaders, GetCurrentThreadEventTarget(), callbacks, this,
1119       mTopLevelOuterContentWindowId, getter_AddRefs(responseStream));
1120   if (NS_FAILED(rv)) {
1121     mTransaction = nullptr;
1122     return rv;
1123   }
1124 
1125   mTransaction->SetClassOfService(mClassOfService);
1126   if (EnsureRequestContext()) {
1127     mTransaction->SetRequestContext(mRequestContext);
1128   }
1129 
1130   rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
1131                                  responseStream);
1132   return rv;
1133 }
1134 
1135 // Helper Function to report messages to the console when loading
1136 // a resource was blocked due to a MIME type mismatch.
ReportTypeBlocking(nsIURI * aURI,nsILoadInfo * aLoadInfo,const char * aMessageName)1137 void ReportTypeBlocking(nsIURI *aURI, nsILoadInfo *aLoadInfo,
1138                         const char *aMessageName) {
1139   NS_ConvertUTF8toUTF16 specUTF16(aURI->GetSpecOrDefault());
1140   const char16_t *params[] = {specUTF16.get()};
1141   nsCOMPtr<nsIDocument> doc;
1142   if (aLoadInfo) {
1143     nsCOMPtr<nsIDOMDocument> domDoc;
1144     aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
1145     if (domDoc) {
1146       doc = do_QueryInterface(domDoc);
1147     }
1148   }
1149   nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
1150                                   NS_LITERAL_CSTRING("MIMEMISMATCH"), doc,
1151                                   nsContentUtils::eSECURITY_PROPERTIES,
1152                                   aMessageName, params, ArrayLength(params));
1153 }
1154 
1155 // Check and potentially enforce X-Content-Type-Options: nosniff
ProcessXCTO(nsIURI * aURI,nsHttpResponseHead * aResponseHead,nsILoadInfo * aLoadInfo)1156 nsresult ProcessXCTO(nsIURI *aURI, nsHttpResponseHead *aResponseHead,
1157                      nsILoadInfo *aLoadInfo) {
1158   if (!aURI || !aResponseHead || !aLoadInfo) {
1159     // if there is no uri, no response head or no loadInfo, then there is
1160     // nothing to do
1161     return NS_OK;
1162   }
1163 
1164   // 1) Query the XCTO header and check if 'nosniff' is the first value.
1165   nsAutoCString contentTypeOptionsHeader;
1166   Unused << aResponseHead->GetHeader(nsHttp::X_Content_Type_Options,
1167                                      contentTypeOptionsHeader);
1168   if (contentTypeOptionsHeader.IsEmpty()) {
1169     // if there is no XCTO header, then there is nothing to do.
1170     return NS_OK;
1171   }
1172   // XCTO header might contain multiple values which are comma separated, so:
1173   // a) let's skip all subsequent values
1174   //     e.g. "   NoSniFF   , foo " will be "   NoSniFF   "
1175   int32_t idx = contentTypeOptionsHeader.Find(",");
1176   if (idx > 0) {
1177     contentTypeOptionsHeader = Substring(contentTypeOptionsHeader, 0, idx);
1178   }
1179   // b) let's trim all surrounding whitespace
1180   //    e.g. "   NoSniFF   " -> "NoSniFF"
1181   contentTypeOptionsHeader.StripWhitespace();
1182   // c) let's compare the header (ignoring case)
1183   //    e.g. "NoSniFF" -> "nosniff"
1184   //    if it's not 'nosniff' then there is nothing to do here
1185   if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) {
1186     // since we are getting here, the XCTO header was sent;
1187     // a non matching value most likely means a mistake happenend;
1188     // e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.
1189     NS_ConvertUTF8toUTF16 char16_header(contentTypeOptionsHeader);
1190     const char16_t *params[] = {char16_header.get()};
1191     nsCOMPtr<nsIDocument> doc;
1192     nsCOMPtr<nsIDOMDocument> domDoc;
1193     aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
1194     if (domDoc) {
1195       doc = do_QueryInterface(domDoc);
1196     }
1197     nsContentUtils::ReportToConsole(
1198         nsIScriptError::warningFlag, NS_LITERAL_CSTRING("XCTO"), doc,
1199         nsContentUtils::eSECURITY_PROPERTIES, "XCTOHeaderValueMissing", params,
1200         ArrayLength(params));
1201     return NS_OK;
1202   }
1203 
1204   // 2) Query the content type from the channel
1205   nsAutoCString contentType;
1206   aResponseHead->ContentType(contentType);
1207 
1208   // 3) Compare the expected MIME type with the actual type
1209   if (aLoadInfo->GetExternalContentPolicyType() ==
1210       nsIContentPolicy::TYPE_STYLESHEET) {
1211     if (contentType.EqualsLiteral(TEXT_CSS)) {
1212       return NS_OK;
1213     }
1214     ReportTypeBlocking(aURI, aLoadInfo, "MimeTypeMismatch");
1215     return NS_ERROR_CORRUPTED_CONTENT;
1216   }
1217 
1218   if (aLoadInfo->GetExternalContentPolicyType() ==
1219       nsIContentPolicy::TYPE_SCRIPT) {
1220     if (nsContentUtils::IsJavascriptMIMEType(
1221             NS_ConvertUTF8toUTF16(contentType))) {
1222       return NS_OK;
1223     }
1224     ReportTypeBlocking(aURI, aLoadInfo, "MimeTypeMismatch");
1225     return NS_ERROR_CORRUPTED_CONTENT;
1226   }
1227   return NS_OK;
1228 }
1229 
1230 // Ensure that a load of type script has correct MIME type
EnsureMIMEOfScript(nsIURI * aURI,nsHttpResponseHead * aResponseHead,nsILoadInfo * aLoadInfo)1231 nsresult EnsureMIMEOfScript(nsIURI *aURI, nsHttpResponseHead *aResponseHead,
1232                             nsILoadInfo *aLoadInfo) {
1233   if (!aURI || !aResponseHead || !aLoadInfo) {
1234     // if there is no uri, no response head or no loadInfo, then there is
1235     // nothing to do
1236     return NS_OK;
1237   }
1238 
1239   if (aLoadInfo->GetExternalContentPolicyType() !=
1240       nsIContentPolicy::TYPE_SCRIPT) {
1241     // if this is not a script load, then there is nothing to do
1242     return NS_OK;
1243   }
1244 
1245   nsAutoCString contentType;
1246   aResponseHead->ContentType(contentType);
1247   NS_ConvertUTF8toUTF16 typeString(contentType);
1248 
1249   if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
1250     // script load has type script
1251     AccumulateCategorical(
1252         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::javaScript);
1253     return NS_OK;
1254   }
1255 
1256   nsCOMPtr<nsIURI> requestURI;
1257   aLoadInfo->LoadingPrincipal()->GetURI(getter_AddRefs(requestURI));
1258 
1259   nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
1260   nsresult rv = ssm->CheckSameOriginURI(requestURI, aURI, false);
1261   if (NS_SUCCEEDED(rv)) {
1262     // same origin
1263     AccumulateCategorical(
1264         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::same_origin);
1265   } else {
1266     bool cors = false;
1267     nsAutoCString corsOrigin;
1268     rv = aResponseHead->GetHeader(
1269         nsHttp::ResolveAtom("Access-Control-Allow-Origin"), corsOrigin);
1270     if (NS_SUCCEEDED(rv)) {
1271       if (corsOrigin.Equals("*")) {
1272         cors = true;
1273       } else {
1274         nsCOMPtr<nsIURI> corsOriginURI;
1275         rv = NS_NewURI(getter_AddRefs(corsOriginURI), corsOrigin);
1276         if (NS_SUCCEEDED(rv)) {
1277           rv = ssm->CheckSameOriginURI(requestURI, corsOriginURI, false);
1278           if (NS_SUCCEEDED(rv)) {
1279             cors = true;
1280           }
1281         }
1282       }
1283     }
1284     if (cors) {
1285       // cors origin
1286       AccumulateCategorical(
1287           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::CORS_origin);
1288     } else {
1289       // cross origin
1290       AccumulateCategorical(
1291           Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::cross_origin);
1292     }
1293   }
1294 
1295   bool block = false;
1296   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
1297     // script load has type image
1298     AccumulateCategorical(
1299         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::image);
1300     block = true;
1301   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("audio/"))) {
1302     // script load has type audio
1303     AccumulateCategorical(
1304         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::audio);
1305     block = true;
1306   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("video/"))) {
1307     // script load has type video
1308     AccumulateCategorical(
1309         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::video);
1310     block = true;
1311   } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/csv"))) {
1312     // script load has type text/csv
1313     AccumulateCategorical(
1314         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::text_csv);
1315     block = true;
1316   }
1317 
1318   if (block) {
1319     // Instead of consulting Preferences::GetBool() all the time we
1320     // can cache the result to speed things up.
1321     static bool sCachedBlockScriptWithWrongMime = false;
1322     static bool sIsInited = false;
1323     if (!sIsInited) {
1324       sIsInited = true;
1325       Preferences::AddBoolVarCache(&sCachedBlockScriptWithWrongMime,
1326                                    "security.block_script_with_wrong_mime");
1327     }
1328 
1329     // Do not block the load if the feature is not enabled.
1330     if (!sCachedBlockScriptWithWrongMime) {
1331       return NS_OK;
1332     }
1333 
1334     ReportTypeBlocking(aURI, aLoadInfo, "BlockScriptWithWrongMimeType");
1335     return NS_ERROR_CORRUPTED_CONTENT;
1336   }
1337 
1338   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/plain"))) {
1339     // script load has type text/plain
1340     AccumulateCategorical(
1341         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::text_plain);
1342     return NS_OK;
1343   }
1344 
1345   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/xml"))) {
1346     // script load has type text/xml
1347     AccumulateCategorical(
1348         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::text_xml);
1349     return NS_OK;
1350   }
1351 
1352   if (StringBeginsWith(contentType,
1353                        NS_LITERAL_CSTRING("application/octet-stream"))) {
1354     // script load has type application/octet-stream
1355     AccumulateCategorical(
1356         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::app_octet_stream);
1357     return NS_OK;
1358   }
1359 
1360   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("application/xml"))) {
1361     // script load has type application/xml
1362     AccumulateCategorical(
1363         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::app_xml);
1364     return NS_OK;
1365   }
1366 
1367   if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/html"))) {
1368     // script load has type text/html
1369     AccumulateCategorical(
1370         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::text_html);
1371     return NS_OK;
1372   }
1373 
1374   if (contentType.IsEmpty()) {
1375     // script load has no type
1376     AccumulateCategorical(
1377         Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::empty);
1378     return NS_OK;
1379   }
1380 
1381   // script load has unknown type
1382   AccumulateCategorical(
1383       Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_2::unknown);
1384   return NS_OK;
1385 }
1386 
CallOnStartRequest()1387 nsresult nsHttpChannel::CallOnStartRequest() {
1388   LOG(("nsHttpChannel::CallOnStartRequest [this=%p]", this));
1389 
1390   MOZ_RELEASE_ASSERT(!mRequireCORSPreflight || mIsCorsPreflightDone,
1391                      "CORS preflight must have been finished by the time we "
1392                      "call OnStartRequest");
1393 
1394   if (mOnStartRequestCalled) {
1395     // This can only happen when a range request loading rest of the data
1396     // after interrupted concurrent cache read asynchronously failed, e.g.
1397     // the response range bytes are not as expected or this channel has
1398     // been externally canceled.
1399     //
1400     // It's legal to bypass CallOnStartRequest for that case since we've
1401     // already called OnStartRequest on our listener and also added all
1402     // content converters before.
1403     MOZ_ASSERT(mConcurrentCacheAccess);
1404     LOG(("CallOnStartRequest already invoked before"));
1405     return mStatus;
1406   }
1407 
1408   mTracingEnabled = false;
1409 
1410   // Ensure mListener->OnStartRequest will be invoked before exiting
1411   // this function.
1412   auto onStartGuard = MakeScopeExit([&] {
1413     LOG(
1414         ("  calling mListener->OnStartRequest by ScopeExit [this=%p, "
1415          "listener=%p]\n",
1416          this, mListener.get()));
1417     MOZ_ASSERT(!mOnStartRequestCalled);
1418 
1419     if (mListener) {
1420       nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
1421       deleteProtector->OnStartRequest(this, mListenerContext);
1422     }
1423 
1424     mOnStartRequestCalled = true;
1425   });
1426 
1427   nsresult rv = EnsureMIMEOfScript(mURI, mResponseHead, mLoadInfo);
1428   NS_ENSURE_SUCCESS(rv, rv);
1429 
1430   rv = ProcessXCTO(mURI, mResponseHead, mLoadInfo);
1431   NS_ENSURE_SUCCESS(rv, rv);
1432 
1433   // Allow consumers to override our content type
1434   if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
1435     // NOTE: We can have both a txn pump and a cache pump when the cache
1436     // content is partial. In that case, we need to read from the cache,
1437     // because that's the one that has the initial contents. If that fails
1438     // then give the transaction pump a shot.
1439 
1440     nsIChannel *thisChannel = static_cast<nsIChannel *>(this);
1441 
1442     bool typeSniffersCalled = false;
1443     if (mCachePump) {
1444       typeSniffersCalled =
1445           NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel));
1446     }
1447 
1448     if (!typeSniffersCalled && mTransactionPump) {
1449       mTransactionPump->PeekStream(CallTypeSniffers, thisChannel);
1450     }
1451   }
1452 
1453   bool unknownDecoderStarted = false;
1454   if (mResponseHead && !mResponseHead->HasContentType()) {
1455     MOZ_ASSERT(mConnectionInfo, "Should have connection info here");
1456     if (!mContentTypeHint.IsEmpty())
1457       mResponseHead->SetContentType(mContentTypeHint);
1458     else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 &&
1459              mConnectionInfo->OriginPort() != mConnectionInfo->DefaultPort())
1460       mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
1461     else {
1462       // Uh-oh.  We had better find out what type we are!
1463       nsCOMPtr<nsIStreamConverterService> serv;
1464       rv = gHttpHandler->GetStreamConverterService(getter_AddRefs(serv));
1465       // If we failed, we just fall through to the "normal" case
1466       if (NS_SUCCEEDED(rv)) {
1467         nsCOMPtr<nsIStreamListener> converter;
1468         rv =
1469             serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, "*/*", mListener,
1470                                    mListenerContext, getter_AddRefs(converter));
1471         if (NS_SUCCEEDED(rv)) {
1472           mListener = converter;
1473           unknownDecoderStarted = true;
1474         }
1475       }
1476     }
1477   }
1478 
1479   if (mResponseHead && !mResponseHead->HasContentCharset())
1480     mResponseHead->SetContentCharset(mContentCharsetHint);
1481 
1482   if (mResponseHead && mCacheEntry) {
1483     // If we have a cache entry, set its predicted size to TotalEntitySize to
1484     // avoid caching an entry that will exceed the max size limit.
1485     rv = mCacheEntry->SetPredictedDataSize(mResponseHead->TotalEntitySize());
1486     if (NS_ERROR_FILE_TOO_BIG == rv) {
1487       // Don't throw the entry away, we will need it later.
1488       LOG(("  entry too big"));
1489     } else {
1490       NS_ENSURE_SUCCESS(rv, rv);
1491     }
1492   }
1493 
1494   LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
1495        mListener.get()));
1496 
1497   // About to call OnStartRequest, dismiss the guard object.
1498   onStartGuard.release();
1499 
1500   if (mListener) {
1501     MOZ_ASSERT(!mOnStartRequestCalled,
1502                "We should not call OsStartRequest twice");
1503     nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
1504     rv = deleteProtector->OnStartRequest(this, mListenerContext);
1505     mOnStartRequestCalled = true;
1506     if (NS_FAILED(rv)) return rv;
1507   } else {
1508     NS_WARNING("OnStartRequest skipped because of null listener");
1509     mOnStartRequestCalled = true;
1510   }
1511 
1512   // Install stream converter if required.
1513   // If we use unknownDecoder, stream converters will be installed later (in
1514   // nsUnknownDecoder) after OnStartRequest is called for the real listener.
1515   if (!unknownDecoderStarted) {
1516     nsCOMPtr<nsIStreamListener> listener;
1517     nsISupports *ctxt = mListenerContext;
1518     rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), ctxt);
1519     if (NS_FAILED(rv)) {
1520       return rv;
1521     }
1522     if (listener) {
1523       mListener = listener;
1524       mCompressListener = listener;
1525     }
1526   }
1527 
1528   // if this channel is for a download, close off access to the cache.
1529   if (mCacheEntry && mChannelIsForDownload) {
1530     mCacheEntry->AsyncDoom(nullptr);
1531 
1532     // We must keep the cache entry in case of partial request.
1533     // Concurrent access is the same, we need the entry in
1534     // OnStopRequest.
1535     // We also need the cache entry when racing cache with network to find
1536     // out what is the source of the data.
1537     if (!mCachedContentIsPartial && !mConcurrentCacheAccess &&
1538         !(mRaceCacheWithNetwork &&
1539           mFirstResponseSource == RESPONSE_FROM_CACHE)) {
1540       CloseCacheEntry(false);
1541     }
1542   }
1543 
1544   if (!mCanceled) {
1545     // create offline cache entry if offline caching was requested
1546     if (ShouldUpdateOfflineCacheEntry()) {
1547       LOG(("writing to the offline cache"));
1548       rv = InitOfflineCacheEntry();
1549       if (NS_FAILED(rv)) return rv;
1550 
1551       // InitOfflineCacheEntry may have closed mOfflineCacheEntry
1552       if (mOfflineCacheEntry) {
1553         rv = InstallOfflineCacheListener();
1554         if (NS_FAILED(rv)) return rv;
1555       }
1556     } else if (mApplicationCacheForWrite) {
1557       LOG(("offline cache is up to date, not updating"));
1558       CloseOfflineCacheEntry();
1559     }
1560   }
1561 
1562   // Check for a Content-Signature header and inject mediator if the header is
1563   // requested and available.
1564   // If requested (mLoadInfo->GetVerifySignedContent), but not present, or
1565   // present but not valid, fail this channel and return
1566   // NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a
1567   // fallback load in nsDocShell.
1568   // Note that OnStartRequest has already been called on the target stream
1569   // listener at this point. We have to add the listener here that late to
1570   // ensure that it's the last listener and can thus block the load in
1571   // OnStopRequest.
1572   if (!mCanceled) {
1573     rv = ProcessContentSignatureHeader(mResponseHead);
1574     if (NS_FAILED(rv)) {
1575       LOG(("Content-signature verification failed.\n"));
1576       return rv;
1577     }
1578   }
1579 
1580   return NS_OK;
1581 }
1582 
ProcessFailedProxyConnect(uint32_t httpStatus)1583 nsresult nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) {
1584   // Failure to set up a proxy tunnel via CONNECT means one of the following:
1585   // 1) Proxy wants authorization, or forbids.
1586   // 2) DNS at proxy couldn't resolve target URL.
1587   // 3) Proxy connection to target failed or timed out.
1588   // 4) Eve intercepted our CONNECT, and is replying with malicious HTML.
1589   //
1590   // Our current architecture would parse the proxy's response content with
1591   // the permission of the target URL.  Given #4, we must avoid rendering the
1592   // body of the reply, and instead give the user a (hopefully helpful)
1593   // boilerplate error page, based on just the HTTP status of the reply.
1594 
1595   MOZ_ASSERT(mConnectionInfo->UsingConnect(),
1596              "proxy connect failed but not using CONNECT?");
1597   nsresult rv;
1598   switch (httpStatus) {
1599     case 300:
1600     case 301:
1601     case 302:
1602     case 303:
1603     case 307:
1604     case 308:
1605       // Bad redirect: not top-level, or it's a POST, bad/missing Location,
1606       // or ProcessRedirect() failed for some other reason.  Legal
1607       // redirects that fail because site not available, etc., are handled
1608       // elsewhere, in the regular codepath.
1609       rv = NS_ERROR_CONNECTION_REFUSED;
1610       break;
1611     case 403:  // HTTP/1.1: "Forbidden"
1612     case 407:  // ProcessAuthentication() failed
1613     case 501:  // HTTP/1.1: "Not Implemented"
1614       // user sees boilerplate Mozilla "Proxy Refused Connection" page.
1615       rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1616       break;
1617     // Squid sends 404 if DNS fails (regular 404 from target is tunneled)
1618     case 404:  // HTTP/1.1: "Not Found"
1619     // RFC 2616: "some deployed proxies are known to return 400 or 500 when
1620     // DNS lookups time out."  (Squid uses 500 if it runs out of sockets: so
1621     // we have a conflict here).
1622     case 400:  // HTTP/1.1 "Bad Request"
1623     case 500:  // HTTP/1.1: "Internal Server Error"
1624       /* User sees: "Address Not Found: Firefox can't find the server at
1625        * www.foo.com."
1626        */
1627       rv = NS_ERROR_UNKNOWN_HOST;
1628       break;
1629     case 502:  // HTTP/1.1: "Bad Gateway" (invalid resp from target server)
1630     // Squid returns 503 if target request fails for anything but DNS.
1631     case 503:  // HTTP/1.1: "Service Unavailable"
1632       /* User sees: "Failed to Connect:
1633        *  Firefox can't establish a connection to the server at
1634        *  www.foo.com.  Though the site seems valid, the browser
1635        *  was unable to establish a connection."
1636        */
1637       rv = NS_ERROR_CONNECTION_REFUSED;
1638       break;
1639     // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to
1640     // do here: picking target timeout, as DNS covered by 400/404/500
1641     case 504:  // HTTP/1.1: "Gateway Timeout"
1642       // user sees: "Network Timeout: The server at www.foo.com
1643       //              is taking too long to respond."
1644       rv = NS_ERROR_NET_TIMEOUT;
1645       break;
1646     // Confused proxy server or malicious response
1647     default:
1648       rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1649       break;
1650   }
1651   LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n", this,
1652        httpStatus));
1653   Cancel(rv);
1654   {
1655     nsresult rv = CallOnStartRequest();
1656     if (NS_FAILED(rv)) {
1657       LOG(("CallOnStartRequest failed [this=%p httpStatus=%u rv=%08x]\n", this,
1658            httpStatus, static_cast<uint32_t>(rv)));
1659     }
1660   }
1661   return rv;
1662 }
1663 
GetSTSConsoleErrorTag(uint32_t failureResult,nsAString & consoleErrorTag)1664 static void GetSTSConsoleErrorTag(uint32_t failureResult,
1665                                   nsAString &consoleErrorTag) {
1666   switch (failureResult) {
1667     case nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:
1668       consoleErrorTag = NS_LITERAL_STRING("STSUntrustworthyConnection");
1669       break;
1670     case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
1671       consoleErrorTag = NS_LITERAL_STRING("STSCouldNotParseHeader");
1672       break;
1673     case nsISiteSecurityService::ERROR_NO_MAX_AGE:
1674       consoleErrorTag = NS_LITERAL_STRING("STSNoMaxAge");
1675       break;
1676     case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
1677       consoleErrorTag = NS_LITERAL_STRING("STSMultipleMaxAges");
1678       break;
1679     case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
1680       consoleErrorTag = NS_LITERAL_STRING("STSInvalidMaxAge");
1681       break;
1682     case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
1683       consoleErrorTag = NS_LITERAL_STRING("STSMultipleIncludeSubdomains");
1684       break;
1685     case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
1686       consoleErrorTag = NS_LITERAL_STRING("STSInvalidIncludeSubdomains");
1687       break;
1688     case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
1689       consoleErrorTag = NS_LITERAL_STRING("STSCouldNotSaveState");
1690       break;
1691     default:
1692       consoleErrorTag = NS_LITERAL_STRING("STSUnknownError");
1693       break;
1694   }
1695 }
1696 
GetPKPConsoleErrorTag(uint32_t failureResult,nsAString & consoleErrorTag)1697 static void GetPKPConsoleErrorTag(uint32_t failureResult,
1698                                   nsAString &consoleErrorTag) {
1699   switch (failureResult) {
1700     case nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:
1701       consoleErrorTag = NS_LITERAL_STRING("PKPUntrustworthyConnection");
1702       break;
1703     case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
1704       consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotParseHeader");
1705       break;
1706     case nsISiteSecurityService::ERROR_NO_MAX_AGE:
1707       consoleErrorTag = NS_LITERAL_STRING("PKPNoMaxAge");
1708       break;
1709     case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
1710       consoleErrorTag = NS_LITERAL_STRING("PKPMultipleMaxAges");
1711       break;
1712     case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
1713       consoleErrorTag = NS_LITERAL_STRING("PKPInvalidMaxAge");
1714       break;
1715     case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
1716       consoleErrorTag = NS_LITERAL_STRING("PKPMultipleIncludeSubdomains");
1717       break;
1718     case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
1719       consoleErrorTag = NS_LITERAL_STRING("PKPInvalidIncludeSubdomains");
1720       break;
1721     case nsISiteSecurityService::ERROR_INVALID_PIN:
1722       consoleErrorTag = NS_LITERAL_STRING("PKPInvalidPin");
1723       break;
1724     case nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS:
1725       consoleErrorTag = NS_LITERAL_STRING("PKPMultipleReportURIs");
1726       break;
1727     case nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN:
1728       consoleErrorTag = NS_LITERAL_STRING("PKPPinsetDoesNotMatch");
1729       break;
1730     case nsISiteSecurityService::ERROR_NO_BACKUP_PIN:
1731       consoleErrorTag = NS_LITERAL_STRING("PKPNoBackupPin");
1732       break;
1733     case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
1734       consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotSaveState");
1735       break;
1736     case nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN:
1737       consoleErrorTag = NS_LITERAL_STRING("PKPRootNotBuiltIn");
1738       break;
1739     default:
1740       consoleErrorTag = NS_LITERAL_STRING("PKPUnknownError");
1741       break;
1742   }
1743 }
1744 
1745 /**
1746  * Process a single security header. Only two types are supported: HSTS and
1747  * HPKP.
1748  */
ProcessSingleSecurityHeader(uint32_t aType,nsISSLStatus * aSSLStatus,uint32_t aFlags)1749 nsresult nsHttpChannel::ProcessSingleSecurityHeader(uint32_t aType,
1750                                                     nsISSLStatus *aSSLStatus,
1751                                                     uint32_t aFlags) {
1752   nsHttpAtom atom;
1753   switch (aType) {
1754     case nsISiteSecurityService::HEADER_HSTS:
1755       atom = nsHttp::ResolveAtom("Strict-Transport-Security");
1756       break;
1757     case nsISiteSecurityService::HEADER_HPKP:
1758       atom = nsHttp::ResolveAtom("Public-Key-Pins");
1759       break;
1760     default:
1761       NS_NOTREACHED("Invalid security header type");
1762       return NS_ERROR_FAILURE;
1763   }
1764 
1765   nsAutoCString securityHeader;
1766   nsresult rv = mResponseHead->GetHeader(atom, securityHeader);
1767   if (NS_SUCCEEDED(rv)) {
1768     nsISiteSecurityService *sss = gHttpHandler->GetSSService();
1769     NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
1770     // Process header will now discard the headers itself if the channel
1771     // wasn't secure (whereas before it had to be checked manually)
1772     OriginAttributes originAttributes;
1773     NS_GetOriginAttributes(this, originAttributes);
1774     uint32_t failureResult;
1775     uint32_t headerSource = nsISiteSecurityService::SOURCE_ORGANIC_REQUEST;
1776     rv = sss->ProcessHeader(aType, mURI, securityHeader, aSSLStatus, aFlags,
1777                             headerSource, originAttributes, nullptr, nullptr,
1778                             &failureResult);
1779     if (NS_FAILED(rv)) {
1780       nsAutoString consoleErrorCategory;
1781       nsAutoString consoleErrorTag;
1782       switch (aType) {
1783         case nsISiteSecurityService::HEADER_HSTS:
1784           GetSTSConsoleErrorTag(failureResult, consoleErrorTag);
1785           consoleErrorCategory = NS_LITERAL_STRING("Invalid HSTS Headers");
1786           break;
1787         case nsISiteSecurityService::HEADER_HPKP:
1788           GetPKPConsoleErrorTag(failureResult, consoleErrorTag);
1789           consoleErrorCategory = NS_LITERAL_STRING("Invalid HPKP Headers");
1790           break;
1791         default:
1792           return NS_ERROR_FAILURE;
1793       }
1794       Unused << AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
1795       LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n",
1796            atom.get()));
1797     }
1798   } else {
1799     if (rv != NS_ERROR_NOT_AVAILABLE) {
1800       // All other errors are fatal
1801       NS_ENSURE_SUCCESS(rv, rv);
1802     }
1803     LOG(("nsHttpChannel: No %s header, continuing load.\n", atom.get()));
1804   }
1805   return NS_OK;
1806 }
1807 
1808 /**
1809  * Decide whether or not to remember Strict-Transport-Security, and whether
1810  * or not to enforce channel integrity.
1811  *
1812  * @return NS_ERROR_FAILURE if there's security information missing even though
1813  *             it's an HTTPS connection.
1814  */
ProcessSecurityHeaders()1815 nsresult nsHttpChannel::ProcessSecurityHeaders() {
1816   nsresult rv;
1817   bool isHttps = false;
1818   rv = mURI->SchemeIs("https", &isHttps);
1819   NS_ENSURE_SUCCESS(rv, rv);
1820 
1821   // If this channel is not loading securely, STS or PKP doesn't do anything.
1822   // In the case of HSTS, the upgrade to HTTPS takes place earlier in the
1823   // channel load process.
1824   if (!isHttps) return NS_OK;
1825 
1826   nsAutoCString asciiHost;
1827   rv = mURI->GetAsciiHost(asciiHost);
1828   NS_ENSURE_SUCCESS(rv, NS_OK);
1829 
1830   // If the channel is not a hostname, but rather an IP, do not process STS
1831   // or PKP headers
1832   PRNetAddr hostAddr;
1833   if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr))
1834     return NS_OK;
1835 
1836   // mSecurityInfo may not always be present, and if it's not then it is okay
1837   // to just disregard any security headers since we know nothing about the
1838   // security of the connection.
1839   NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
1840 
1841   uint32_t flags =
1842       NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
1843 
1844   // Get the SSLStatus
1845   nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(mSecurityInfo);
1846   NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
1847   nsCOMPtr<nsISSLStatus> sslStatus;
1848   rv = sslprov->GetSSLStatus(getter_AddRefs(sslStatus));
1849   NS_ENSURE_SUCCESS(rv, rv);
1850   NS_ENSURE_TRUE(sslStatus, NS_ERROR_FAILURE);
1851 
1852   rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HSTS,
1853                                    sslStatus, flags);
1854   NS_ENSURE_SUCCESS(rv, rv);
1855 
1856   rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP,
1857                                    sslStatus, flags);
1858   NS_ENSURE_SUCCESS(rv, rv);
1859 
1860   return NS_OK;
1861 }
1862 
ProcessContentSignatureHeader(nsHttpResponseHead * aResponseHead)1863 nsresult nsHttpChannel::ProcessContentSignatureHeader(
1864     nsHttpResponseHead *aResponseHead) {
1865   nsresult rv = NS_OK;
1866 
1867   // we only do this if we require it in loadInfo
1868   if (!mLoadInfo || !mLoadInfo->GetVerifySignedContent()) {
1869     return NS_OK;
1870   }
1871 
1872   NS_ENSURE_TRUE(aResponseHead, NS_ERROR_ABORT);
1873   nsAutoCString contentSignatureHeader;
1874   nsHttpAtom atom = nsHttp::ResolveAtom("Content-Signature");
1875   rv = aResponseHead->GetHeader(atom, contentSignatureHeader);
1876   if (NS_FAILED(rv)) {
1877     LOG(("Content-Signature header is missing but expected."));
1878     DoInvalidateCacheEntry(mURI);
1879     return NS_ERROR_INVALID_SIGNATURE;
1880   }
1881 
1882   // if we require a signature but it is empty, fail
1883   if (contentSignatureHeader.IsEmpty()) {
1884     DoInvalidateCacheEntry(mURI);
1885     LOG(("An expected content-signature header is missing.\n"));
1886     return NS_ERROR_INVALID_SIGNATURE;
1887   }
1888 
1889   // we ensure a content type here to avoid running into problems with
1890   // content sniffing, which might sniff parts of the content before we can
1891   // verify the signature
1892   if (!aResponseHead->HasContentType()) {
1893     NS_WARNING(
1894         "Empty content type can get us in trouble when verifying "
1895         "content signatures");
1896     return NS_ERROR_INVALID_SIGNATURE;
1897   }
1898   // create a new listener that meadiates the content
1899   RefPtr<ContentVerifier> contentVerifyingMediator =
1900       new ContentVerifier(mListener, mListenerContext);
1901   rv = contentVerifyingMediator->Init(contentSignatureHeader, this,
1902                                       mListenerContext);
1903   NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
1904   mListener = contentVerifyingMediator;
1905 
1906   return NS_OK;
1907 }
1908 
1909 /**
1910  * Decide whether or not to send a security report and, if so, give the
1911  * SecurityReporter the information required to send such a report.
1912  */
ProcessSecurityReport(nsresult status)1913 void nsHttpChannel::ProcessSecurityReport(nsresult status) {
1914   uint32_t errorClass;
1915   nsCOMPtr<nsINSSErrorsService> errSvc =
1916       do_GetService("@mozilla.org/nss_errors_service;1");
1917   // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
1918   // not in the set of errors covered by the NSS errors service.
1919   nsresult rv = errSvc->GetErrorClass(status, &errorClass);
1920   if (!NS_SUCCEEDED(rv)) {
1921     return;
1922   }
1923 
1924   // if the content was not loaded succesfully and we have security info,
1925   // send a TLS error report - we must do this early as other parts of
1926   // OnStopRequest can return early
1927   bool reportingEnabled =
1928       Preferences::GetBool("security.ssl.errorReporting.enabled");
1929   bool reportingAutomatic =
1930       Preferences::GetBool("security.ssl.errorReporting.automatic");
1931   if (!mSecurityInfo || !reportingEnabled || !reportingAutomatic) {
1932     return;
1933   }
1934 
1935   nsCOMPtr<nsITransportSecurityInfo> secInfo = do_QueryInterface(mSecurityInfo);
1936   nsCOMPtr<nsISecurityReporter> errorReporter =
1937       do_GetService("@mozilla.org/securityreporter;1");
1938 
1939   if (!secInfo || !mURI) {
1940     return;
1941   }
1942 
1943   nsAutoCString hostStr;
1944   int32_t port;
1945   rv = mURI->GetHost(hostStr);
1946   if (!NS_SUCCEEDED(rv)) {
1947     return;
1948   }
1949 
1950   rv = mURI->GetPort(&port);
1951 
1952   if (NS_SUCCEEDED(rv)) {
1953     errorReporter->ReportTLSError(secInfo, hostStr, port);
1954   }
1955 }
1956 
IsHTTPS()1957 bool nsHttpChannel::IsHTTPS() {
1958   bool isHttps;
1959   if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps) return false;
1960   return true;
1961 }
1962 
ProcessSSLInformation()1963 void nsHttpChannel::ProcessSSLInformation() {
1964   // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
1965   // can be whitelisted for TLS False Start in future sessions. We could
1966   // do the same for DH but its rarity doesn't justify the lookup.
1967 
1968   if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo || !IsHTTPS() ||
1969       mPrivateBrowsing)
1970     return;
1971 
1972   nsCOMPtr<nsISSLStatusProvider> statusProvider =
1973       do_QueryInterface(mSecurityInfo);
1974   if (!statusProvider) return;
1975   nsCOMPtr<nsISSLStatus> sslstat;
1976   statusProvider->GetSSLStatus(getter_AddRefs(sslstat));
1977   if (!sslstat) return;
1978 
1979   nsCOMPtr<nsITransportSecurityInfo> securityInfo =
1980       do_QueryInterface(mSecurityInfo);
1981   uint32_t state;
1982   if (securityInfo && NS_SUCCEEDED(securityInfo->GetSecurityState(&state)) &&
1983       (state & nsIWebProgressListener::STATE_IS_BROKEN)) {
1984     // Send weak crypto warnings to the web console
1985     if (state & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
1986       nsString consoleErrorTag = NS_LITERAL_STRING("WeakCipherSuiteWarning");
1987       nsString consoleErrorCategory = NS_LITERAL_STRING("SSL");
1988       Unused << AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
1989     }
1990   }
1991 
1992   // Send (SHA-1) signature algorithm errors to the web console
1993   nsCOMPtr<nsIX509Cert> cert;
1994   sslstat->GetServerCert(getter_AddRefs(cert));
1995   if (cert) {
1996     UniqueCERTCertificate nssCert(cert->GetCert());
1997     if (nssCert) {
1998       SECOidTag tag = SECOID_GetAlgorithmTag(&nssCert->signature);
1999       LOG(("Checking certificate signature: The OID tag is %i [this=%p]\n", tag,
2000            this));
2001       // Check to see if the signature is sha-1 based.
2002       // Not including checks for SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE
2003       // from http://tools.ietf.org/html/rfc2437#section-8 since I
2004       // can't see reference to it outside this spec
2005       if (tag == SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION ||
2006           tag == SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST ||
2007           tag == SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE) {
2008         nsString consoleErrorTag = NS_LITERAL_STRING("SHA1Sig");
2009         nsString consoleErrorMessage = NS_LITERAL_STRING("SHA-1 Signature");
2010         Unused << AddSecurityMessage(consoleErrorTag, consoleErrorMessage);
2011       }
2012     }
2013   }
2014 }
2015 
ProcessAltService()2016 void nsHttpChannel::ProcessAltService() {
2017   // e.g. Alt-Svc: h2=":443"; ma=60
2018   // e.g. Alt-Svc: h2="otherhost:443"
2019   // Alt-Svc       = 1#( alternative *( OWS ";" OWS parameter ) )
2020   // alternative   = protocol-id "=" alt-authority
2021   // protocol-id   = token ; percent-encoded ALPN protocol identifier
2022   // alt-authority = quoted-string ;  containing [ uri-host ] ":" port
2023 
2024   if (!mAllowAltSvc) {  // per channel opt out
2025     return;
2026   }
2027 
2028   if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) {
2029     return;
2030   }
2031 
2032   nsAutoCString scheme;
2033   mURI->GetScheme(scheme);
2034   bool isHttp = scheme.EqualsLiteral("http");
2035   if (!isHttp && !scheme.EqualsLiteral("https")) {
2036     return;
2037   }
2038 
2039   nsAutoCString altSvc;
2040   Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc);
2041   if (altSvc.IsEmpty()) {
2042     return;
2043   }
2044 
2045   if (!nsHttp::IsReasonableHeaderValue(altSvc)) {
2046     LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));
2047     return;
2048   }
2049 
2050   nsAutoCString originHost;
2051   int32_t originPort = 80;
2052   mURI->GetPort(&originPort);
2053   if (NS_FAILED(mURI->GetHost(originHost))) {
2054     return;
2055   }
2056 
2057   nsCOMPtr<nsIInterfaceRequestor> callbacks;
2058   nsCOMPtr<nsProxyInfo> proxyInfo;
2059   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
2060                                          getter_AddRefs(callbacks));
2061   if (mProxyInfo) {
2062     proxyInfo = do_QueryInterface(mProxyInfo);
2063   }
2064 
2065   OriginAttributes originAttributes;
2066   NS_GetOriginAttributes(this, originAttributes);
2067 
2068   AltSvcMapping::ProcessHeader(
2069       altSvc, scheme, originHost, originPort, mUsername, mPrivateBrowsing,
2070       callbacks, proxyInfo, mCaps & NS_HTTP_DISALLOW_SPDY, originAttributes);
2071 }
2072 
ProcessResponse()2073 nsresult nsHttpChannel::ProcessResponse() {
2074   uint32_t httpStatus = mResponseHead->Status();
2075 
2076   LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", this,
2077        httpStatus));
2078 
2079   // Gather data on whether the transaction and page (if this is
2080   // the initial page load) is being loaded with SSL.
2081   Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_IS_SSL,
2082                         mConnectionInfo->EndToEndSSL());
2083   if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
2084     Telemetry::Accumulate(Telemetry::HTTP_PAGELOAD_IS_SSL,
2085                           mConnectionInfo->EndToEndSSL());
2086   }
2087 
2088   if (gHttpHandler->IsTelemetryEnabled()) {
2089     // how often do we see something like Alt-Svc: "443:quic,p=1"
2090     nsAutoCString alt_service;
2091     Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, alt_service);
2092     bool saw_quic =
2093         (!alt_service.IsEmpty() && PL_strstr(alt_service.get(), "quic")) ? 1
2094                                                                          : 0;
2095     Telemetry::Accumulate(Telemetry::HTTP_SAW_QUIC_ALT_PROTOCOL, saw_quic);
2096 
2097     // Gather data on how many URLS get redirected
2098     switch (httpStatus) {
2099       case 200:
2100         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 0);
2101         break;
2102       case 301:
2103         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 1);
2104         break;
2105       case 302:
2106         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 2);
2107         break;
2108       case 304:
2109         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 3);
2110         break;
2111       case 307:
2112         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 4);
2113         break;
2114       case 308:
2115         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 5);
2116         break;
2117       case 400:
2118         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 6);
2119         break;
2120       case 401:
2121         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 7);
2122         break;
2123       case 403:
2124         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 8);
2125         break;
2126       case 404:
2127         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 9);
2128         break;
2129       case 500:
2130         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 10);
2131         break;
2132       default:
2133         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 11);
2134         break;
2135     }
2136   }
2137 
2138   // Let the predictor know whether this was a cacheable response or not so
2139   // that it knows whether or not to possibly prefetch this resource in the
2140   // future.
2141   // We use GetReferringPage because mReferrer may not be set at all, or may
2142   // not be a full URI (HttpBaseChannel::SetReferrer has the gorey details).
2143   // If that's null, though, we'll fall back to mReferrer just in case (this
2144   // is especially useful in xpcshell tests, where we don't have an actual
2145   // pageload to get a referrer from).
2146   nsCOMPtr<nsIURI> referrer = GetReferringPage();
2147   if (!referrer) {
2148     referrer = mReferrer;
2149   }
2150   if (referrer) {
2151     nsCOMPtr<nsILoadContextInfo> lci = GetLoadContextInfo(this);
2152     mozilla::net::Predictor::UpdateCacheability(referrer, mURI, httpStatus,
2153                                                 mRequestHead, mResponseHead,
2154                                                 lci, mIsTrackingResource);
2155   }
2156 
2157   // Only allow 407 (authentication required) to continue
2158   if (mTransaction && mTransaction->ProxyConnectFailed() && httpStatus != 407) {
2159     return ProcessFailedProxyConnect(httpStatus);
2160   }
2161 
2162   MOZ_ASSERT(!mCachedContentIsValid || mRaceCacheWithNetwork,
2163              "We should not be hitting the network if we have valid cached "
2164              "content unless we are racing the network and cache");
2165 
2166   ProcessSSLInformation();
2167 
2168   // notify "http-on-examine-response" observers
2169   gHttpHandler->OnExamineResponse(this);
2170 
2171   return ContinueProcessResponse1();
2172 }
2173 
AsyncContinueProcessResponse()2174 void nsHttpChannel::AsyncContinueProcessResponse() {
2175   nsresult rv;
2176   rv = ContinueProcessResponse1();
2177   if (NS_FAILED(rv)) {
2178     // A synchronous failure here would normally be passed as the return
2179     // value from OnStartRequest, which would in turn cancel the request.
2180     // If we're continuing asynchronously, we need to cancel the request
2181     // ourselves.
2182     Unused << Cancel(rv);
2183   }
2184 }
2185 
ContinueProcessResponse1()2186 nsresult nsHttpChannel::ContinueProcessResponse1() {
2187   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
2188   nsresult rv;
2189 
2190   if (mSuspendCount) {
2191     LOG(("Waiting until resume to finish processing response [this=%p]\n",
2192          this));
2193     mCallOnResume = &nsHttpChannel::AsyncContinueProcessResponse;
2194     return NS_OK;
2195   }
2196 
2197   // Check if request was cancelled during http-on-examine-response.
2198   if (mCanceled) {
2199     return CallOnStartRequest();
2200   }
2201 
2202   uint32_t httpStatus = mResponseHead->Status();
2203 
2204   // STS, Cookies and Alt-Service should not be handled on proxy failure.
2205   // If proxy CONNECT response needs to complete, wait to process connection
2206   // for Strict-Transport-Security.
2207   if (!(mTransaction && mTransaction->ProxyConnectFailed()) &&
2208       (httpStatus != 407)) {
2209     nsAutoCString cookie;
2210     if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Cookie, cookie))) {
2211       SetCookie(cookie.get());
2212     }
2213 
2214     // Given a successful connection, process any STS or PKP data that's
2215     // relevant.
2216     DebugOnly<nsresult> rv = ProcessSecurityHeaders();
2217     MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
2218 
2219     if ((httpStatus < 500) && (httpStatus != 421)) {
2220       ProcessAltService();
2221     }
2222   }
2223 
2224   if (mConcurrentCacheAccess && mCachedContentIsPartial && httpStatus != 206) {
2225     LOG(
2226         ("  only expecting 206 when doing partial request during "
2227          "interrupted cache concurrent read"));
2228     return NS_ERROR_CORRUPTED_CONTENT;
2229   }
2230 
2231   // handle unused username and password in url (see bug 232567)
2232   if (httpStatus != 401 && httpStatus != 407) {
2233     if (!mAuthRetryPending) {
2234       rv = mAuthProvider->CheckForSuperfluousAuth();
2235       if (NS_FAILED(rv)) {
2236         LOG(("  CheckForSuperfluousAuth failed (%08x)",
2237              static_cast<uint32_t>(rv)));
2238       }
2239     }
2240     if (mCanceled) return CallOnStartRequest();
2241 
2242     // reset the authentication's current continuation state because our
2243     // last authentication attempt has been completed successfully
2244     rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
2245     if (NS_FAILED(rv)) {
2246       LOG(("  Disconnect failed (%08x)", static_cast<uint32_t>(rv)));
2247     }
2248     mAuthProvider = nullptr;
2249     LOG(("  continuation state has been reset"));
2250   }
2251 
2252   if (mAPIRedirectToURI && !mCanceled) {
2253     MOZ_ASSERT(!mOnStartRequestCalled);
2254     nsCOMPtr<nsIURI> redirectTo;
2255     mAPIRedirectToURI.swap(redirectTo);
2256 
2257     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
2258     rv = StartRedirectChannelToURI(redirectTo,
2259                                    nsIChannelEventSink::REDIRECT_TEMPORARY);
2260     if (NS_SUCCEEDED(rv)) {
2261       return NS_OK;
2262     }
2263     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
2264   }
2265 
2266   // Hack: ContinueProcessResponse2 uses NS_OK to detect successful
2267   // redirects, so we distinguish this codepath (a non-redirect that's
2268   // processing normally) by passing in a bogus error code.
2269   return ContinueProcessResponse2(NS_BINDING_FAILED);
2270 }
2271 
ContinueProcessResponse2(nsresult rv)2272 nsresult nsHttpChannel::ContinueProcessResponse2(nsresult rv) {
2273   LOG(("nsHttpChannel::ContinueProcessResponse1 [this=%p, rv=%" PRIx32 "]",
2274        this, static_cast<uint32_t>(rv)));
2275 
2276   if (NS_SUCCEEDED(rv)) {
2277     // redirectTo() has passed through, we don't want to go on with
2278     // this channel.  It will now be canceled by the redirect handling
2279     // code that called this function.
2280     return NS_OK;
2281   }
2282 
2283   rv = NS_OK;
2284 
2285   uint32_t httpStatus = mResponseHead->Status();
2286 
2287   bool successfulReval = false;
2288   bool partialContentUsed = false;
2289 
2290   // handle different server response categories.  Note that we handle
2291   // caching or not caching of error pages in
2292   // nsHttpResponseHead::MustValidate; if you change this switch, update that
2293   // one
2294   switch (httpStatus) {
2295     case 200:
2296     case 203:
2297       // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
2298       // So if a server does that and sends 200 instead of 206 that we
2299       // expect, notify our caller.
2300       // However, if we wanted to start from the beginning, let it go through
2301       if (mResuming && mStartPos != 0) {
2302         LOG(("Server ignored our Range header, cancelling [this=%p]\n", this));
2303         Cancel(NS_ERROR_NOT_RESUMABLE);
2304         rv = CallOnStartRequest();
2305         break;
2306       }
2307       // these can normally be cached
2308       rv = ProcessNormal();
2309       MaybeInvalidateCacheEntryForSubsequentGet();
2310       break;
2311     case 206:
2312       if (mCachedContentIsPartial) {  // an internal byte range request...
2313         rv = ProcessPartialContent();
2314         if (NS_SUCCEEDED(rv)) {
2315           partialContentUsed = true;
2316         }
2317       } else {
2318         mCacheInputStream.CloseAndRelease();
2319         rv = ProcessNormal();
2320       }
2321       break;
2322     case 300:
2323     case 301:
2324     case 302:
2325     case 307:
2326     case 308:
2327     case 303:
2328 #if 0
2329     case 305: // disabled as a security measure (see bug 187996).
2330 #endif
2331       // don't store the response body for redirects
2332       MaybeInvalidateCacheEntryForSubsequentGet();
2333       PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
2334       rv = AsyncProcessRedirection(httpStatus);
2335       if (NS_FAILED(rv)) {
2336         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
2337         LOG(("AsyncProcessRedirection failed [rv=%" PRIx32 "]\n",
2338              static_cast<uint32_t>(rv)));
2339         // don't cache failed redirect responses.
2340         if (mCacheEntry) mCacheEntry->AsyncDoom(nullptr);
2341         if (DoNotRender3xxBody(rv)) {
2342           mStatus = rv;
2343           DoNotifyListener();
2344         } else {
2345           rv = ContinueProcessResponse3(rv);
2346         }
2347       }
2348       break;
2349     case 304:
2350       if (!ShouldBypassProcessNotModified()) {
2351         rv = ProcessNotModified();
2352         if (NS_SUCCEEDED(rv)) {
2353           successfulReval = true;
2354           break;
2355         }
2356 
2357         LOG(("ProcessNotModified failed [rv=%" PRIx32 "]\n",
2358              static_cast<uint32_t>(rv)));
2359 
2360         // We cannot read from the cache entry, it might be in an
2361         // incosistent state.  Doom it and redirect the channel
2362         // to the same URI to reload from the network.
2363         mCacheInputStream.CloseAndRelease();
2364         if (mCacheEntry) {
2365           mCacheEntry->AsyncDoom(nullptr);
2366           mCacheEntry = nullptr;
2367         }
2368 
2369         rv = StartRedirectChannelToURI(mURI,
2370                                        nsIChannelEventSink::REDIRECT_INTERNAL);
2371         if (NS_SUCCEEDED(rv)) {
2372           return NS_OK;
2373         }
2374       }
2375 
2376       // Don't cache uninformative 304
2377       if (mCustomConditionalRequest) {
2378         CloseCacheEntry(false);
2379       }
2380 
2381       if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) {
2382         rv = ProcessNormal();
2383       }
2384       break;
2385     case 401:
2386     case 407:
2387       if (MOZ_UNLIKELY(mCustomAuthHeader) && httpStatus == 401) {
2388         // When a custom auth header fails, we don't want to try
2389         // any cached credentials, nor we want to ask the user.
2390         // It's up to the consumer to re-try w/o setting a custom
2391         // auth header if cached credentials should be attempted.
2392         rv = NS_ERROR_FAILURE;
2393       } else {
2394         rv = mAuthProvider->ProcessAuthentication(
2395             httpStatus, mConnectionInfo->EndToEndSSL() && mTransaction &&
2396                             mTransaction->ProxyConnectFailed());
2397       }
2398       if (rv == NS_ERROR_IN_PROGRESS) {
2399         // authentication prompt has been invoked and result
2400         // is expected asynchronously
2401         mAuthRetryPending = true;
2402         if (httpStatus == 407 ||
2403             (mTransaction && mTransaction->ProxyConnectFailed()))
2404           mProxyAuthPending = true;
2405 
2406         // suspend the transaction pump to stop receiving the
2407         // unauthenticated content data. We will throw that data
2408         // away when user provides credentials or resume the pump
2409         // when user refuses to authenticate.
2410         LOG(
2411             ("Suspending the transaction, asynchronously prompting for "
2412              "credentials"));
2413         mTransactionPump->Suspend();
2414         rv = NS_OK;
2415       } else if (NS_FAILED(rv)) {
2416         LOG(("ProcessAuthentication failed [rv=%" PRIx32 "]\n",
2417              static_cast<uint32_t>(rv)));
2418         if (mTransaction && mTransaction->ProxyConnectFailed()) {
2419           return ProcessFailedProxyConnect(httpStatus);
2420         }
2421         if (!mAuthRetryPending) {
2422           rv = mAuthProvider->CheckForSuperfluousAuth();
2423           if (NS_FAILED(rv)) {
2424             LOG(("CheckForSuperfluousAuth failed [rv=%x]\n",
2425                  static_cast<uint32_t>(rv)));
2426           }
2427         }
2428         rv = ProcessNormal();
2429       } else {
2430         mAuthRetryPending = true;  // see DoAuthRetry
2431       }
2432       break;
2433 
2434     case 425:
2435       // Do not cache 425.
2436       CloseCacheEntry(false);
2437       MOZ_FALLTHROUGH;  // process normally
2438     default:
2439       rv = ProcessNormal();
2440       MaybeInvalidateCacheEntryForSubsequentGet();
2441       break;
2442   }
2443 
2444   if (mRaceDelay && !mRaceCacheWithNetwork &&
2445       (mCachedContentIsPartial || mDidReval)) {
2446     if (successfulReval || partialContentUsed) {
2447       AccumulateCategorical(
2448           Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::CachedContentUsed);
2449     } else {
2450       AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::
2451                                 CachedContentNotUsed);
2452     }
2453   }
2454 
2455   if (gHttpHandler->IsTelemetryEnabled()) {
2456     CacheDisposition cacheDisposition;
2457     if (!mDidReval) {
2458       cacheDisposition = kCacheMissed;
2459     } else if (successfulReval) {
2460       cacheDisposition = kCacheHitViaReval;
2461     } else {
2462       cacheDisposition = kCacheMissedViaReval;
2463     }
2464     AccumulateCacheHitTelemetry(cacheDisposition);
2465 
2466     Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_VERSION,
2467                           mResponseHead->Version());
2468 
2469     if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
2470       // DefaultPortTopLevel = 0, DefaultPortSubResource = 1,
2471       // NonDefaultPortTopLevel = 2, NonDefaultPortSubResource = 3
2472       uint32_t v09Info = 0;
2473       if (!(mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)) {
2474         v09Info += 1;
2475       }
2476       if (mConnectionInfo->OriginPort() != mConnectionInfo->DefaultPort()) {
2477         v09Info += 2;
2478       }
2479       Telemetry::Accumulate(Telemetry::HTTP_09_INFO, v09Info);
2480     }
2481   }
2482   return rv;
2483 }
2484 
ContinueProcessResponse3(nsresult rv)2485 nsresult nsHttpChannel::ContinueProcessResponse3(nsresult rv) {
2486   bool doNotRender = DoNotRender3xxBody(rv);
2487 
2488   if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) {
2489     bool isHTTP = false;
2490     if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP))) isHTTP = false;
2491     if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP)))
2492       isHTTP = false;
2493 
2494     if (!isHTTP) {
2495       // This was a blocked attempt to redirect and subvert the system by
2496       // redirecting to another protocol (perhaps javascript:)
2497       // In that case we want to throw an error instead of displaying the
2498       // non-redirected response body.
2499       LOG(("ContinueProcessResponse3 detected rejected Non-HTTP Redirection"));
2500       doNotRender = true;
2501       rv = NS_ERROR_CORRUPTED_CONTENT;
2502     }
2503   }
2504 
2505   if (doNotRender) {
2506     Cancel(rv);
2507     DoNotifyListener();
2508     return rv;
2509   }
2510 
2511   if (NS_SUCCEEDED(rv)) {
2512     UpdateInhibitPersistentCachingFlag();
2513 
2514     rv = InitCacheEntry();
2515     if (NS_FAILED(rv)) {
2516       LOG(
2517           ("ContinueProcessResponse3 "
2518            "failed to init cache entry [rv=%x]\n",
2519            static_cast<uint32_t>(rv)));
2520     }
2521     CloseCacheEntry(false);
2522 
2523     if (mApplicationCacheForWrite) {
2524       // Store response in the offline cache
2525       Unused << InitOfflineCacheEntry();
2526       CloseOfflineCacheEntry();
2527     }
2528     return NS_OK;
2529   }
2530 
2531   LOG(("ContinueProcessResponse3 got failure result [rv=%" PRIx32 "]\n",
2532        static_cast<uint32_t>(rv)));
2533   if (mTransaction && mTransaction->ProxyConnectFailed()) {
2534     return ProcessFailedProxyConnect(mRedirectType);
2535   }
2536   return ProcessNormal();
2537 }
2538 
ProcessNormal()2539 nsresult nsHttpChannel::ProcessNormal() {
2540   nsresult rv;
2541 
2542   LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
2543 
2544   bool succeeded;
2545   rv = GetRequestSucceeded(&succeeded);
2546   if (NS_SUCCEEDED(rv) && !succeeded) {
2547     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
2548     bool waitingForRedirectCallback;
2549     Unused << ProcessFallback(&waitingForRedirectCallback);
2550     if (waitingForRedirectCallback) {
2551       // The transaction has been suspended by ProcessFallback.
2552       return NS_OK;
2553     }
2554     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
2555   }
2556 
2557   return ContinueProcessNormal(NS_OK);
2558 }
2559 
ContinueProcessNormal(nsresult rv)2560 nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) {
2561   LOG(("nsHttpChannel::ContinueProcessNormal [this=%p]", this));
2562 
2563   if (NS_FAILED(rv)) {
2564     // Fill the failure status here, we have failed to fall back, thus we
2565     // have to report our status as failed.
2566     mStatus = rv;
2567     DoNotifyListener();
2568     return rv;
2569   }
2570 
2571   if (mFallingBack) {
2572     // Do not continue with normal processing, fallback is in
2573     // progress now.
2574     return NS_OK;
2575   }
2576 
2577   // if we're here, then any byte-range requests failed to result in a partial
2578   // response.  we must clear this flag to prevent BufferPartialContent from
2579   // being called inside our OnDataAvailable (see bug 136678).
2580   mCachedContentIsPartial = false;
2581 
2582   ClearBogusContentEncodingIfNeeded();
2583 
2584   UpdateInhibitPersistentCachingFlag();
2585 
2586   // this must be called before firing OnStartRequest, since http clients,
2587   // such as imagelib, expect our cache entry to already have the correct
2588   // expiration time (bug 87710).
2589   if (mCacheEntry) {
2590     rv = InitCacheEntry();
2591     if (NS_FAILED(rv)) CloseCacheEntry(true);
2592   }
2593 
2594   // Check that the server sent us what we were asking for
2595   if (mResuming) {
2596     // Create an entity id from the response
2597     nsAutoCString id;
2598     rv = GetEntityID(id);
2599     if (NS_FAILED(rv)) {
2600       // If creating an entity id is not possible -> error
2601       Cancel(NS_ERROR_NOT_RESUMABLE);
2602     } else if (mResponseHead->Status() != 206 &&
2603                mResponseHead->Status() != 200) {
2604       // Probably 404 Not Found, 412 Precondition Failed or
2605       // 416 Invalid Range -> error
2606       LOG(("Unexpected response status while resuming, aborting [this=%p]\n",
2607            this));
2608       Cancel(NS_ERROR_ENTITY_CHANGED);
2609     }
2610     // If we were passed an entity id, verify it's equal to the server's
2611     else if (!mEntityID.IsEmpty()) {
2612       if (!mEntityID.Equals(id)) {
2613         LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",
2614              mEntityID.get(), id.get(), this));
2615         Cancel(NS_ERROR_ENTITY_CHANGED);
2616       }
2617     }
2618   }
2619 
2620   rv = CallOnStartRequest();
2621   if (NS_FAILED(rv)) return rv;
2622 
2623   // install cache listener if we still have a cache entry open
2624   if (mCacheEntry && !mCacheEntryIsReadOnly) {
2625     rv = InstallCacheListener();
2626     if (NS_FAILED(rv)) return rv;
2627   }
2628 
2629   return NS_OK;
2630 }
2631 
PromptTempRedirect()2632 nsresult nsHttpChannel::PromptTempRedirect() {
2633   if (!gHttpHandler->PromptTempRedirect()) {
2634     return NS_OK;
2635   }
2636   nsresult rv;
2637   nsCOMPtr<nsIStringBundleService> bundleService =
2638       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
2639   if (NS_FAILED(rv)) return rv;
2640 
2641   nsCOMPtr<nsIStringBundle> stringBundle;
2642   rv =
2643       bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle));
2644   if (NS_FAILED(rv)) return rv;
2645 
2646   nsAutoString messageString;
2647   rv = stringBundle->GetStringFromName("RepostFormData", messageString);
2648   if (NS_SUCCEEDED(rv)) {
2649     bool repost = false;
2650 
2651     nsCOMPtr<nsIPrompt> prompt;
2652     GetCallback(prompt);
2653     if (!prompt) return NS_ERROR_NO_INTERFACE;
2654 
2655     prompt->Confirm(nullptr, messageString.get(), &repost);
2656     if (!repost) return NS_ERROR_FAILURE;
2657   }
2658 
2659   return rv;
2660 }
2661 
ProxyFailover()2662 nsresult nsHttpChannel::ProxyFailover() {
2663   LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this));
2664 
2665   nsresult rv;
2666 
2667   nsCOMPtr<nsIProtocolProxyService> pps =
2668       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
2669   if (NS_FAILED(rv)) return rv;
2670 
2671   nsCOMPtr<nsIProxyInfo> pi;
2672   rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus,
2673                                 getter_AddRefs(pi));
2674   if (NS_FAILED(rv)) return rv;
2675 
2676   // XXXbz so where does this codepath remove us from the loadgroup,
2677   // exactly?
2678   return AsyncDoReplaceWithProxy(pi);
2679 }
2680 
HandleAsyncRedirectChannelToHttps()2681 void nsHttpChannel::HandleAsyncRedirectChannelToHttps() {
2682   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
2683 
2684   if (mSuspendCount) {
2685     LOG(("Waiting until resume to do async redirect to https [this=%p]\n",
2686          this));
2687     mCallOnResume = &nsHttpChannel::HandleAsyncRedirectChannelToHttps;
2688     return;
2689   }
2690 
2691   nsresult rv = StartRedirectChannelToHttps();
2692   if (NS_FAILED(rv)) {
2693     rv = ContinueAsyncRedirectChannelToURI(rv);
2694     if (NS_FAILED(rv)) {
2695       LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
2696            static_cast<uint32_t>(rv), this));
2697     }
2698   }
2699 }
2700 
StartRedirectChannelToHttps()2701 nsresult nsHttpChannel::StartRedirectChannelToHttps() {
2702   LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
2703 
2704   nsCOMPtr<nsIURI> upgradedURI;
2705   nsresult rv = NS_GetSecureUpgradedURI(mURI, getter_AddRefs(upgradedURI));
2706   NS_ENSURE_SUCCESS(rv, rv);
2707 
2708   return StartRedirectChannelToURI(
2709       upgradedURI, nsIChannelEventSink::REDIRECT_PERMANENT |
2710                        nsIChannelEventSink::REDIRECT_STS_UPGRADE);
2711 }
2712 
HandleAsyncAPIRedirect()2713 void nsHttpChannel::HandleAsyncAPIRedirect() {
2714   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
2715   NS_PRECONDITION(mAPIRedirectToURI, "How did that happen?");
2716 
2717   if (mSuspendCount) {
2718     LOG(("Waiting until resume to do async API redirect [this=%p]\n", this));
2719     mCallOnResume = &nsHttpChannel::HandleAsyncAPIRedirect;
2720     return;
2721   }
2722 
2723   nsresult rv = StartRedirectChannelToURI(
2724       mAPIRedirectToURI, nsIChannelEventSink::REDIRECT_PERMANENT);
2725   if (NS_FAILED(rv)) {
2726     rv = ContinueAsyncRedirectChannelToURI(rv);
2727     if (NS_FAILED(rv)) {
2728       LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
2729            static_cast<uint32_t>(rv), this));
2730     }
2731   }
2732 }
2733 
StartRedirectChannelToURI(nsIURI * upgradedURI,uint32_t flags)2734 nsresult nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI,
2735                                                   uint32_t flags) {
2736   nsresult rv = NS_OK;
2737   LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
2738 
2739   nsCOMPtr<nsIChannel> newChannel;
2740   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
2741       CloneLoadInfoForRedirect(upgradedURI, flags);
2742 
2743   nsCOMPtr<nsIIOService> ioService;
2744   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
2745   NS_ENSURE_SUCCESS(rv, rv);
2746 
2747   rv = NS_NewChannelInternal(getter_AddRefs(newChannel), upgradedURI,
2748                              redirectLoadInfo,
2749                              nullptr,  // PerformanceStorage
2750                              nullptr,  // aLoadGroup
2751                              nullptr,  // aCallbacks
2752                              nsIRequest::LOAD_NORMAL, ioService);
2753   NS_ENSURE_SUCCESS(rv, rv);
2754 
2755   rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
2756   NS_ENSURE_SUCCESS(rv, rv);
2757 
2758   // Inform consumers about this fake redirect
2759   mRedirectChannel = newChannel;
2760 
2761   PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
2762   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
2763 
2764   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
2765 
2766   if (NS_FAILED(rv)) {
2767     AutoRedirectVetoNotifier notifier(this);
2768 
2769     /* Remove the async call to ContinueAsyncRedirectChannelToURI().
2770      * It is called directly by our callers upon return (to clean up
2771      * the failed redirect). */
2772     PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
2773   }
2774 
2775   return rv;
2776 }
2777 
ContinueAsyncRedirectChannelToURI(nsresult rv)2778 nsresult nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv) {
2779   LOG(("nsHttpChannel::ContinueAsyncRedirectChannelToURI [this=%p]", this));
2780 
2781   // Since we handle mAPIRedirectToURI also after on-examine-response handler
2782   // rather drop it here to avoid any redirect loops, even just hypothetical.
2783   mAPIRedirectToURI = nullptr;
2784 
2785   if (NS_SUCCEEDED(rv)) {
2786     rv = OpenRedirectChannel(rv);
2787   }
2788 
2789   if (NS_FAILED(rv)) {
2790     // Cancel the channel here, the update to https had been vetoed
2791     // but from the security reasons we have to discard the whole channel
2792     // load.
2793     Cancel(rv);
2794   }
2795 
2796   if (mLoadGroup) {
2797     mLoadGroup->RemoveRequest(this, nullptr, mStatus);
2798   }
2799 
2800   if (NS_FAILED(rv) && !mCachePump && !mTransactionPump) {
2801     // We have to manually notify the listener because there is not any pump
2802     // that would call our OnStart/StopRequest after resume from waiting for
2803     // the redirect callback.
2804     DoNotifyListener();
2805   }
2806 
2807   return rv;
2808 }
2809 
OpenRedirectChannel(nsresult rv)2810 nsresult nsHttpChannel::OpenRedirectChannel(nsresult rv) {
2811   AutoRedirectVetoNotifier notifier(this);
2812 
2813   // Make sure to do this after we received redirect veto answer,
2814   // i.e. after all sinks had been notified
2815   mRedirectChannel->SetOriginalURI(mOriginalURI);
2816 
2817   // open new channel
2818   if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
2819     MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
2820     rv = mRedirectChannel->AsyncOpen2(mListener);
2821   } else {
2822     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
2823   }
2824   NS_ENSURE_SUCCESS(rv, rv);
2825 
2826   mStatus = NS_BINDING_REDIRECTED;
2827 
2828   notifier.RedirectSucceeded();
2829 
2830   ReleaseListeners();
2831 
2832   return NS_OK;
2833 }
2834 
AsyncDoReplaceWithProxy(nsIProxyInfo * pi)2835 nsresult nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo *pi) {
2836   LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
2837   nsresult rv;
2838 
2839   nsCOMPtr<nsIChannel> newChannel;
2840   rv = gHttpHandler->NewProxiedChannel2(mURI, pi, mProxyResolveFlags, mProxyURI,
2841                                         mLoadInfo, getter_AddRefs(newChannel));
2842   if (NS_FAILED(rv)) return rv;
2843 
2844   uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
2845 
2846   rv = SetupReplacementChannel(mURI, newChannel, true, flags);
2847   if (NS_FAILED(rv)) return rv;
2848 
2849   // Inform consumers about this fake redirect
2850   mRedirectChannel = newChannel;
2851 
2852   PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
2853   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
2854 
2855   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
2856 
2857   if (NS_FAILED(rv)) {
2858     AutoRedirectVetoNotifier notifier(this);
2859     PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
2860   }
2861 
2862   return rv;
2863 }
2864 
ContinueDoReplaceWithProxy(nsresult rv)2865 nsresult nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv) {
2866   AutoRedirectVetoNotifier notifier(this);
2867 
2868   if (NS_FAILED(rv)) return rv;
2869 
2870   NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
2871 
2872   // Make sure to do this after we received redirect veto answer,
2873   // i.e. after all sinks had been notified
2874   mRedirectChannel->SetOriginalURI(mOriginalURI);
2875 
2876   // open new channel
2877   if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
2878     MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
2879     rv = mRedirectChannel->AsyncOpen2(mListener);
2880   } else {
2881     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
2882   }
2883   NS_ENSURE_SUCCESS(rv, rv);
2884 
2885   mStatus = NS_BINDING_REDIRECTED;
2886 
2887   notifier.RedirectSucceeded();
2888 
2889   ReleaseListeners();
2890 
2891   return rv;
2892 }
2893 
ResolveProxy()2894 nsresult nsHttpChannel::ResolveProxy() {
2895   LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this));
2896 
2897   nsresult rv;
2898 
2899   nsCOMPtr<nsIProtocolProxyService> pps =
2900       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
2901   if (NS_FAILED(rv)) return rv;
2902 
2903   // using the nsIProtocolProxyService2 allows a minor performance
2904   // optimization, but if an add-on has only provided the original interface
2905   // then it is ok to use that version.
2906   nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
2907   if (pps2) {
2908     rv = pps2->AsyncResolve2(this, mProxyResolveFlags, this, nullptr,
2909                              getter_AddRefs(mProxyRequest));
2910   } else {
2911     rv = pps->AsyncResolve(static_cast<nsIChannel *>(this), mProxyResolveFlags,
2912                            this, nullptr, getter_AddRefs(mProxyRequest));
2913   }
2914 
2915   return rv;
2916 }
2917 
ResponseWouldVary(nsICacheEntry * entry)2918 bool nsHttpChannel::ResponseWouldVary(nsICacheEntry *entry) {
2919   nsresult rv;
2920   nsAutoCString buf, metaKey;
2921   Unused << mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
2922   if (!buf.IsEmpty()) {
2923     NS_NAMED_LITERAL_CSTRING(prefix, "request-");
2924 
2925     // enumerate the elements of the Vary header...
2926     char *val = buf.BeginWriting();  // going to munge buf
2927     char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
2928     while (token) {
2929       LOG(
2930           ("nsHttpChannel::ResponseWouldVary [channel=%p] "
2931            "processing %s\n",
2932            this, token));
2933       //
2934       // if "*", then assume response would vary.  technically speaking,
2935       // "Vary: header, *" is not permitted, but we allow it anyways.
2936       //
2937       // We hash values of cookie-headers for the following reasons:
2938       //
2939       //   1- cookies can be very large in size
2940       //
2941       //   2- cookies may contain sensitive information.  (for parity with
2942       //      out policy of not storing Set-cookie headers in the cache
2943       //      meta data, we likewise do not want to store cookie headers
2944       //      here.)
2945       //
2946       if (*token == '*')
2947         return true;  // if we encounter this, just get out of here
2948 
2949       // build cache meta data key...
2950       metaKey = prefix + nsDependentCString(token);
2951 
2952       // check the last value of the given request header to see if it has
2953       // since changed.  if so, then indeed the cached response is invalid.
2954       nsCString lastVal;
2955       entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
2956       LOG(
2957           ("nsHttpChannel::ResponseWouldVary [channel=%p] "
2958            "stored value = \"%s\"\n",
2959            this, lastVal.get()));
2960 
2961       // Look for value of "Cookie" in the request headers
2962       nsHttpAtom atom = nsHttp::ResolveAtom(token);
2963       nsAutoCString newVal;
2964       bool hasHeader = NS_SUCCEEDED(mRequestHead.GetHeader(atom, newVal));
2965       if (!lastVal.IsEmpty()) {
2966         // value for this header in cache, but no value in request
2967         if (!hasHeader) {
2968           return true;  // yes - response would vary
2969         }
2970 
2971         // If this is a cookie-header, stored metadata is not
2972         // the value itself but the hash. So we also hash the
2973         // outgoing value here in order to compare the hashes
2974         nsAutoCString hash;
2975         if (atom == nsHttp::Cookie) {
2976           rv = Hash(newVal.get(), hash);
2977           // If hash failed, be conservative (the cached hash
2978           // exists at this point) and claim response would vary
2979           if (NS_FAILED(rv)) return true;
2980           newVal = hash;
2981 
2982           LOG(
2983               ("nsHttpChannel::ResponseWouldVary [this=%p] "
2984                "set-cookie value hashed to %s\n",
2985                this, newVal.get()));
2986         }
2987 
2988         if (!newVal.Equals(lastVal)) {
2989           return true;  // yes, response would vary
2990         }
2991 
2992       } else if (hasHeader) {  // old value is empty, but newVal is set
2993         return true;
2994       }
2995 
2996       // next token...
2997       token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
2998     }
2999   }
3000   return false;
3001 }
3002 
3003 // We need to have an implementation of this function just so that we can keep
3004 // all references to mCallOnResume of type nsHttpChannel:  it's not OK in C++
3005 // to set a member function ptr to  a base class function.
HandleAsyncAbort()3006 void nsHttpChannel::HandleAsyncAbort() {
3007   HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
3008 }
3009 
3010 //-----------------------------------------------------------------------------
3011 // nsHttpChannel <byte-range>
3012 //-----------------------------------------------------------------------------
3013 
IsResumable(int64_t partialLen,int64_t contentLength,bool ignoreMissingPartialLen) const3014 bool nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength,
3015                                 bool ignoreMissingPartialLen) const {
3016   bool hasContentEncoding =
3017       mCachedResponseHead->HasHeader(nsHttp::Content_Encoding);
3018 
3019   nsAutoCString etag;
3020   Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, etag);
3021   bool hasWeakEtag =
3022       !etag.IsEmpty() && StringBeginsWith(etag, NS_LITERAL_CSTRING("W/"));
3023 
3024   return (partialLen < contentLength) &&
3025          (partialLen > 0 || ignoreMissingPartialLen) && !hasContentEncoding &&
3026          !hasWeakEtag && mCachedResponseHead->IsResumable() &&
3027          !mCustomConditionalRequest && !mCachedResponseHead->NoStore();
3028 }
3029 
MaybeSetupByteRangeRequest(int64_t partialLen,int64_t contentLength,bool ignoreMissingPartialLen)3030 nsresult nsHttpChannel::MaybeSetupByteRangeRequest(
3031     int64_t partialLen, int64_t contentLength, bool ignoreMissingPartialLen) {
3032   // Be pesimistic
3033   mIsPartialRequest = false;
3034 
3035   if (!IsResumable(partialLen, contentLength, ignoreMissingPartialLen))
3036     return NS_ERROR_NOT_RESUMABLE;
3037 
3038   // looks like a partial entry we can reuse; add If-Range
3039   // and Range headers.
3040   nsresult rv = SetupByteRangeRequest(partialLen);
3041   if (NS_FAILED(rv)) {
3042     // Make the request unconditional again.
3043     UntieByteRangeRequest();
3044   }
3045 
3046   return rv;
3047 }
3048 
SetupByteRangeRequest(int64_t partialLen)3049 nsresult nsHttpChannel::SetupByteRangeRequest(int64_t partialLen) {
3050   // cached content has been found to be partial, add necessary request
3051   // headers to complete cache entry.
3052 
3053   // use strongest validator available...
3054   nsAutoCString val;
3055   Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, val);
3056   if (val.IsEmpty())
3057     Unused << mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
3058   if (val.IsEmpty()) {
3059     // if we hit this code it means mCachedResponseHead->IsResumable() is
3060     // either broken or not being called.
3061     NS_NOTREACHED("no cache validator");
3062     mIsPartialRequest = false;
3063     return NS_ERROR_FAILURE;
3064   }
3065 
3066   char buf[64];
3067   SprintfLiteral(buf, "bytes=%" PRId64 "-", partialLen);
3068 
3069   DebugOnly<nsresult> rv;
3070   rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
3071   MOZ_ASSERT(NS_SUCCEEDED(rv));
3072   rv = mRequestHead.SetHeader(nsHttp::If_Range, val);
3073   MOZ_ASSERT(NS_SUCCEEDED(rv));
3074   mIsPartialRequest = true;
3075 
3076   return NS_OK;
3077 }
3078 
UntieByteRangeRequest()3079 void nsHttpChannel::UntieByteRangeRequest() {
3080   DebugOnly<nsresult> rv;
3081   rv = mRequestHead.ClearHeader(nsHttp::Range);
3082   MOZ_ASSERT(NS_SUCCEEDED(rv));
3083   rv = mRequestHead.ClearHeader(nsHttp::If_Range);
3084   MOZ_ASSERT(NS_SUCCEEDED(rv));
3085 }
3086 
ProcessPartialContent()3087 nsresult nsHttpChannel::ProcessPartialContent() {
3088   // ok, we've just received a 206
3089   //
3090   // we need to stream whatever data is in the cache out first, and then
3091   // pick up whatever data is on the wire, writing it into the cache.
3092 
3093   LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this));
3094 
3095   NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
3096   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
3097 
3098   // Make sure to clear bogus content-encodings before looking at the header
3099   ClearBogusContentEncodingIfNeeded();
3100 
3101   // Check if the content-encoding we now got is different from the one we
3102   // got before
3103   nsAutoCString contentEncoding, cachedContentEncoding;
3104   // It is possible that there is not such headers
3105   Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
3106   Unused << mCachedResponseHead->GetHeader(nsHttp::Content_Encoding,
3107                                            cachedContentEncoding);
3108   if (PL_strcasecmp(contentEncoding.get(), cachedContentEncoding.get()) != 0) {
3109     Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);
3110     return CallOnStartRequest();
3111   }
3112 
3113   nsresult rv;
3114 
3115   int64_t cachedContentLength = mCachedResponseHead->ContentLength();
3116   int64_t entitySize = mResponseHead->TotalEntitySize();
3117 
3118   nsAutoCString contentRange;
3119   Unused << mResponseHead->GetHeader(nsHttp::Content_Range, contentRange);
3120   LOG(
3121       ("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] "
3122        "original content-length %" PRId64 ", entity-size %" PRId64
3123        ", content-range %s\n",
3124        this, mTransaction.get(), cachedContentLength, entitySize,
3125        contentRange.get()));
3126 
3127   if ((entitySize >= 0) && (cachedContentLength >= 0) &&
3128       (entitySize != cachedContentLength)) {
3129     LOG(
3130         ("nsHttpChannel::ProcessPartialContent [this=%p] "
3131          "206 has different total entity size than the content length "
3132          "of the original partially cached entity.\n",
3133          this));
3134 
3135     mCacheEntry->AsyncDoom(nullptr);
3136     Cancel(NS_ERROR_CORRUPTED_CONTENT);
3137     return CallOnStartRequest();
3138   }
3139 
3140   if (mConcurrentCacheAccess) {
3141     // We started to read cached data sooner than its write has been done.
3142     // But the concurrent write has not finished completely, so we had to
3143     // do a range request.  Now let the content coming from the network
3144     // be presented to consumers and also stored to the cache entry.
3145 
3146     rv = InstallCacheListener(mLogicalOffset);
3147     if (NS_FAILED(rv)) return rv;
3148 
3149     if (mOfflineCacheEntry) {
3150       rv = InstallOfflineCacheListener(mLogicalOffset);
3151       if (NS_FAILED(rv)) return rv;
3152     }
3153   } else {
3154     // suspend the current transaction
3155     rv = mTransactionPump->Suspend();
3156     if (NS_FAILED(rv)) return rv;
3157   }
3158 
3159   // merge any new headers with the cached response headers
3160   rv = mCachedResponseHead->UpdateHeaders(mResponseHead);
3161   if (NS_FAILED(rv)) return rv;
3162 
3163   // update the cached response head
3164   nsAutoCString head;
3165   mCachedResponseHead->Flatten(head, true);
3166   rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
3167   if (NS_FAILED(rv)) return rv;
3168 
3169   // make the cached response be the current response
3170   mResponseHead = Move(mCachedResponseHead);
3171 
3172   UpdateInhibitPersistentCachingFlag();
3173 
3174   rv = UpdateExpirationTime();
3175   if (NS_FAILED(rv)) return rv;
3176 
3177   // notify observers interested in looking at a response that has been
3178   // merged with any cached headers (http-on-examine-merged-response).
3179   gHttpHandler->OnExamineMergedResponse(this);
3180 
3181   if (mConcurrentCacheAccess) {
3182     mCachedContentIsPartial = false;
3183     // Leave the mConcurrentCacheAccess flag set, we want to use it
3184     // to prevent duplicate OnStartRequest call on the target listener
3185     // in case this channel is canceled before it gets its OnStartRequest
3186     // from the http transaction.
3187 
3188     // Now we continue reading the network response.
3189   } else {
3190     // the cached content is valid, although incomplete.
3191     mCachedContentIsValid = true;
3192     rv = ReadFromCache(false);
3193   }
3194 
3195   return rv;
3196 }
3197 
OnDoneReadingPartialCacheEntry(bool * streamDone)3198 nsresult nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone) {
3199   nsresult rv;
3200 
3201   LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
3202 
3203   // by default, assume we would have streamed all data or failed...
3204   *streamDone = true;
3205 
3206   // setup cache listener to append to cache entry
3207   int64_t size;
3208   rv = mCacheEntry->GetDataSize(&size);
3209   if (NS_FAILED(rv)) return rv;
3210 
3211   rv = InstallCacheListener(size);
3212   if (NS_FAILED(rv)) return rv;
3213 
3214   // Entry is valid, do it now, after the output stream has been opened,
3215   // otherwise when done earlier, pending readers would consider the cache
3216   // entry still as partial (CacheEntry::GetDataSize would return the partial
3217   // data size) and consumers would do the conditional request again.
3218   rv = mCacheEntry->SetValid();
3219   if (NS_FAILED(rv)) return rv;
3220 
3221   // need to track the logical offset of the data being sent to our listener
3222   mLogicalOffset = size;
3223 
3224   // we're now completing the cached content, so we can clear this flag.
3225   // this puts us in the state of a regular download.
3226   mCachedContentIsPartial = false;
3227   // The cache input stream pump is finished, we do not need it any more.
3228   // (see bug 1313923)
3229   mCachePump = nullptr;
3230 
3231   // resume the transaction if it exists, otherwise the pipe contained the
3232   // remaining part of the document and we've now streamed all of the data.
3233   if (mTransactionPump) {
3234     rv = mTransactionPump->Resume();
3235     if (NS_SUCCEEDED(rv)) *streamDone = false;
3236   } else
3237     NS_NOTREACHED("no transaction");
3238   return rv;
3239 }
3240 
3241 //-----------------------------------------------------------------------------
3242 // nsHttpChannel <cache>
3243 //-----------------------------------------------------------------------------
3244 
ShouldBypassProcessNotModified()3245 bool nsHttpChannel::ShouldBypassProcessNotModified() {
3246   if (mCustomConditionalRequest) {
3247     LOG(("Bypassing ProcessNotModified due to custom conditional headers"));
3248     return true;
3249   }
3250 
3251   if (!mDidReval) {
3252     LOG(
3253         ("Server returned a 304 response even though we did not send a "
3254          "conditional request"));
3255     return true;
3256   }
3257 
3258   return false;
3259 }
3260 
ProcessNotModified()3261 nsresult nsHttpChannel::ProcessNotModified() {
3262   nsresult rv;
3263 
3264   LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this));
3265 
3266   // Assert ShouldBypassProcessNotModified() has been checked before call to
3267   // ProcessNotModified().
3268   MOZ_ASSERT(!ShouldBypassProcessNotModified());
3269 
3270   MOZ_ASSERT(mCachedResponseHead);
3271   MOZ_ASSERT(mCacheEntry);
3272   NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED);
3273 
3274   // If the 304 response contains a Last-Modified different than the
3275   // one in our cache that is pretty suspicious and is, in at least the
3276   // case of bug 716840, a sign of the server having previously corrupted
3277   // our cache with a bad response. Take the minor step here of just dooming
3278   // that cache entry so there is a fighting chance of getting things on the
3279   // right track.
3280 
3281   nsAutoCString lastModifiedCached;
3282   nsAutoCString lastModified304;
3283 
3284   rv =
3285       mCachedResponseHead->GetHeader(nsHttp::Last_Modified, lastModifiedCached);
3286   if (NS_SUCCEEDED(rv)) {
3287     rv = mResponseHead->GetHeader(nsHttp::Last_Modified, lastModified304);
3288   }
3289 
3290   if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) {
3291     LOG(
3292         ("Cache Entry and 304 Last-Modified Headers Do Not Match "
3293          "[%s] and [%s]\n",
3294          lastModifiedCached.get(), lastModified304.get()));
3295 
3296     mCacheEntry->AsyncDoom(nullptr);
3297     Telemetry::Accumulate(Telemetry::CACHE_LM_INCONSISTENT, true);
3298   }
3299 
3300   // merge any new headers with the cached response headers
3301   rv = mCachedResponseHead->UpdateHeaders(mResponseHead);
3302   if (NS_FAILED(rv)) return rv;
3303 
3304   // update the cached response head
3305   nsAutoCString head;
3306   mCachedResponseHead->Flatten(head, true);
3307   rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
3308   if (NS_FAILED(rv)) return rv;
3309 
3310   // make the cached response be the current response
3311   mResponseHead = Move(mCachedResponseHead);
3312 
3313   UpdateInhibitPersistentCachingFlag();
3314 
3315   rv = UpdateExpirationTime();
3316   if (NS_FAILED(rv)) return rv;
3317 
3318   rv = AddCacheEntryHeaders(mCacheEntry);
3319   if (NS_FAILED(rv)) return rv;
3320 
3321   // notify observers interested in looking at a reponse that has been
3322   // merged with any cached headers
3323   gHttpHandler->OnExamineMergedResponse(this);
3324 
3325   mCachedContentIsValid = true;
3326 
3327   // Tell other consumers the entry is OK to use
3328   rv = mCacheEntry->SetValid();
3329   if (NS_FAILED(rv)) return rv;
3330 
3331   rv = ReadFromCache(false);
3332   if (NS_FAILED(rv)) return rv;
3333 
3334   mTransactionReplaced = true;
3335   return NS_OK;
3336 }
3337 
ProcessFallback(bool * waitingForRedirectCallback)3338 nsresult nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback) {
3339   LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
3340   nsresult rv;
3341 
3342   *waitingForRedirectCallback = false;
3343   mFallingBack = false;
3344 
3345   // At this point a load has failed (either due to network problems
3346   // or an error returned on the server).  Perform an application
3347   // cache fallback if we have a URI to fall back to.
3348   if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) {
3349     LOG(("  choosing not to fallback [%p,%s,%d]", mApplicationCache.get(),
3350          mFallbackKey.get(), mFallbackChannel));
3351     return NS_OK;
3352   }
3353 
3354   // Make sure the fallback entry hasn't been marked as a foreign
3355   // entry.
3356   uint32_t fallbackEntryType;
3357   rv = mApplicationCache->GetTypes(mFallbackKey, &fallbackEntryType);
3358   NS_ENSURE_SUCCESS(rv, rv);
3359 
3360   if (fallbackEntryType & nsIApplicationCache::ITEM_FOREIGN) {
3361     // This cache points to a fallback that refers to a different
3362     // manifest.  Refuse to fall back.
3363     return NS_OK;
3364   }
3365 
3366   if (!IsInSubpathOfAppCacheManifest(mApplicationCache, mFallbackKey)) {
3367     // Refuse to fallback if the fallback key is not contained in the same
3368     // path as the cache manifest.
3369     return NS_OK;
3370   }
3371 
3372   MOZ_ASSERT(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK,
3373              "Fallback entry not marked correctly!");
3374 
3375   // Kill any offline cache entry, and disable offline caching for the
3376   // fallback.
3377   if (mOfflineCacheEntry) {
3378     mOfflineCacheEntry->AsyncDoom(nullptr);
3379     mOfflineCacheEntry = nullptr;
3380   }
3381 
3382   mApplicationCacheForWrite = nullptr;
3383   mOfflineCacheEntry = nullptr;
3384 
3385   // Close the current cache entry.
3386   CloseCacheEntry(true);
3387 
3388   // Create a new channel to load the fallback entry.
3389   RefPtr<nsIChannel> newChannel;
3390   rv = gHttpHandler->NewChannel2(mURI, mLoadInfo, getter_AddRefs(newChannel));
3391   NS_ENSURE_SUCCESS(rv, rv);
3392 
3393   uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
3394   rv = SetupReplacementChannel(mURI, newChannel, true, redirectFlags);
3395   NS_ENSURE_SUCCESS(rv, rv);
3396 
3397   // Make sure the new channel loads from the fallback key.
3398   nsCOMPtr<nsIHttpChannelInternal> httpInternal =
3399       do_QueryInterface(newChannel, &rv);
3400   NS_ENSURE_SUCCESS(rv, rv);
3401 
3402   rv = httpInternal->SetupFallbackChannel(mFallbackKey.get());
3403   NS_ENSURE_SUCCESS(rv, rv);
3404 
3405   // ... and fallbacks should only load from the cache.
3406   uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE | LOAD_ONLY_FROM_CACHE;
3407   rv = newChannel->SetLoadFlags(newLoadFlags);
3408 
3409   // Inform consumers about this fake redirect
3410   mRedirectChannel = newChannel;
3411 
3412   PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
3413   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
3414 
3415   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
3416 
3417   if (NS_FAILED(rv)) {
3418     AutoRedirectVetoNotifier notifier(this);
3419     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
3420     return rv;
3421   }
3422 
3423   // Indicate we are now waiting for the asynchronous redirect callback
3424   // if all went OK.
3425   *waitingForRedirectCallback = true;
3426   return NS_OK;
3427 }
3428 
ContinueProcessFallback(nsresult rv)3429 nsresult nsHttpChannel::ContinueProcessFallback(nsresult rv) {
3430   AutoRedirectVetoNotifier notifier(this);
3431 
3432   if (NS_FAILED(rv)) return rv;
3433 
3434   NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
3435 
3436   // Make sure to do this after we received redirect veto answer,
3437   // i.e. after all sinks had been notified
3438   mRedirectChannel->SetOriginalURI(mOriginalURI);
3439 
3440   if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
3441     MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
3442     rv = mRedirectChannel->AsyncOpen2(mListener);
3443   } else {
3444     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
3445   }
3446   NS_ENSURE_SUCCESS(rv, rv);
3447 
3448   if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
3449     MaybeWarnAboutAppCache();
3450   }
3451 
3452   // close down this channel
3453   Cancel(NS_BINDING_REDIRECTED);
3454 
3455   notifier.RedirectSucceeded();
3456 
3457   ReleaseListeners();
3458 
3459   mFallingBack = true;
3460 
3461   return NS_OK;
3462 }
3463 
3464 // Determines if a request is a byte range request for a subrange,
3465 // i.e. is a byte range request, but not a 0- byte range request.
IsSubRangeRequest(nsHttpRequestHead & aRequestHead)3466 static bool IsSubRangeRequest(nsHttpRequestHead &aRequestHead) {
3467   nsAutoCString byteRange;
3468   if (NS_FAILED(aRequestHead.GetHeader(nsHttp::Range, byteRange))) {
3469     return false;
3470   }
3471   return !byteRange.EqualsLiteral("bytes=0-");
3472 }
3473 
OpenCacheEntry(bool isHttps)3474 nsresult nsHttpChannel::OpenCacheEntry(bool isHttps) {
3475   // Drop this flag here
3476   mConcurrentCacheAccess = 0;
3477 
3478   mLoadedFromApplicationCache = false;
3479   mHasQueryString = HasQueryString(mRequestHead.ParsedMethod(), mURI);
3480 
3481   LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
3482 
3483   // make sure we're not abusing this function
3484   NS_PRECONDITION(!mCacheEntry, "cache entry already open");
3485 
3486   if (mRequestHead.IsPost()) {
3487     // If the post id is already set then this is an attempt to replay
3488     // a post transaction via the cache.  Otherwise, we need a unique
3489     // post id for this transaction.
3490     if (mPostID == 0) mPostID = gHttpHandler->GenerateUniqueID();
3491   } else if (!mRequestHead.IsGet() && !mRequestHead.IsHead()) {
3492     // don't use the cache for other types of requests
3493     return NS_OK;
3494   }
3495 
3496   // Pick up an application cache from the notification
3497   // callbacks if available and if we are not an intercepted channel.
3498   if (!mApplicationCache && mInheritApplicationCache) {
3499     nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
3500     GetCallback(appCacheContainer);
3501 
3502     if (appCacheContainer) {
3503       appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
3504     }
3505   }
3506 
3507   return OpenCacheEntryInternal(isHttps, mApplicationCache, true);
3508 }
3509 
OpenCacheEntryInternal(bool isHttps,nsIApplicationCache * applicationCache,bool allowApplicationCache)3510 nsresult nsHttpChannel::OpenCacheEntryInternal(
3511     bool isHttps, nsIApplicationCache *applicationCache,
3512     bool allowApplicationCache) {
3513   MOZ_ASSERT_IF(!allowApplicationCache, !applicationCache);
3514 
3515   nsresult rv;
3516 
3517   if (mResuming) {
3518     // We don't support caching for requests initiated
3519     // via nsIResumableChannel.
3520     return NS_OK;
3521   }
3522 
3523   // Don't cache byte range requests which are subranges, only cache 0-
3524   // byte range requests.
3525   if (IsSubRangeRequest(mRequestHead)) {
3526     return NS_OK;
3527   }
3528 
3529   // Handle correctly mCacheEntriesToWaitFor
3530   AutoCacheWaitFlags waitFlags(this);
3531 
3532   nsAutoCString cacheKey;
3533   nsAutoCString extension;
3534 
3535   nsCOMPtr<nsICacheStorageService> cacheStorageService(
3536       services::GetCacheStorageService());
3537   if (!cacheStorageService) {
3538     return NS_ERROR_NOT_AVAILABLE;
3539   }
3540 
3541   nsCOMPtr<nsICacheStorage> cacheStorage;
3542   nsCOMPtr<nsIURI> openURI;
3543   if (!mFallbackKey.IsEmpty() && mFallbackChannel) {
3544     // This is a fallback channel, open fallback URI instead
3545     rv = NS_NewURI(getter_AddRefs(openURI), mFallbackKey);
3546     NS_ENSURE_SUCCESS(rv, rv);
3547   } else {
3548     openURI = mURI;
3549   }
3550 
3551   RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
3552   if (!info) {
3553     return NS_ERROR_FAILURE;
3554   }
3555 
3556   uint32_t cacheEntryOpenFlags;
3557   bool offline = gIOService->IsOffline();
3558 
3559   bool maybeRCWN = false;
3560 
3561   nsAutoCString cacheControlRequestHeader;
3562   Unused << mRequestHead.GetHeader(nsHttp::Cache_Control,
3563                                    cacheControlRequestHeader);
3564   CacheControlParser cacheControlRequest(cacheControlRequestHeader);
3565   if (cacheControlRequest.NoStore()) {
3566     goto bypassCacheEntryOpen;
3567   }
3568 
3569   if (offline || (mLoadFlags & INHIBIT_CACHING)) {
3570     if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) {
3571       goto bypassCacheEntryOpen;
3572     }
3573     cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY;
3574     mCacheEntryIsReadOnly = true;
3575   } else if (BYPASS_LOCAL_CACHE(mLoadFlags) && !applicationCache) {
3576     cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
3577   } else {
3578     cacheEntryOpenFlags =
3579         nsICacheStorage::OPEN_NORMALLY | nsICacheStorage::CHECK_MULTITHREADED;
3580   }
3581 
3582   // Remember the request is a custom conditional request so that we can
3583   // process any 304 response correctly.
3584   mCustomConditionalRequest =
3585       mRequestHead.HasHeader(nsHttp::If_Modified_Since) ||
3586       mRequestHead.HasHeader(nsHttp::If_None_Match) ||
3587       mRequestHead.HasHeader(nsHttp::If_Unmodified_Since) ||
3588       mRequestHead.HasHeader(nsHttp::If_Match) ||
3589       mRequestHead.HasHeader(nsHttp::If_Range);
3590 
3591   if (!mPostID && applicationCache) {
3592     rv = cacheStorageService->AppCacheStorage(info, applicationCache,
3593                                               getter_AddRefs(cacheStorage));
3594   } else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
3595     rv = cacheStorageService->MemoryCacheStorage(
3596         info,  // ? choose app cache as well...
3597         getter_AddRefs(cacheStorage));
3598   } else if (mPinCacheContent) {
3599     rv = cacheStorageService->PinningCacheStorage(info,
3600                                                   getter_AddRefs(cacheStorage));
3601   } else {
3602     bool lookupAppCache =
3603         (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE)) &&
3604         !mPostID && MOZ_LIKELY(allowApplicationCache);
3605     // Try to race only if we use disk cache storage and we don't lookup
3606     // app cache first
3607     maybeRCWN = (!lookupAppCache) && mRequestHead.IsSafeMethod();
3608     rv = cacheStorageService->DiskCacheStorage(info, lookupAppCache,
3609                                                getter_AddRefs(cacheStorage));
3610   }
3611   NS_ENSURE_SUCCESS(rv, rv);
3612 
3613   if ((mClassOfService & nsIClassOfService::Leader) ||
3614       (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI))
3615     cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
3616 
3617   // Only for backward compatibility with the old cache back end.
3618   // When removed, remove the flags and related code snippets.
3619   if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY)
3620     cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY;
3621 
3622   if (mPostID) {
3623     extension.Append(nsPrintfCString("%d", mPostID));
3624   }
3625   if (mTRR) {
3626     extension.Append("TRR");
3627   }
3628 
3629   mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
3630   mCacheQueueSizeWhenOpen =
3631       CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
3632 
3633   if (sRCWNEnabled && maybeRCWN && !mApplicationCacheForWrite) {
3634     bool hasAltData = false;
3635     uint32_t sizeInKb = 0;
3636     rv = cacheStorage->GetCacheIndexEntryAttrs(openURI, extension, &hasAltData,
3637                                                &sizeInKb);
3638 
3639     // We will attempt to race the network vs the cache if we've found
3640     // this entry in the cache index, and it has appropriate attributes
3641     // (doesn't have alt-data, and has a small size)
3642     if (NS_SUCCEEDED(rv) && !hasAltData &&
3643         sizeInKb < sRCWNSmallResourceSizeKB) {
3644       MaybeRaceCacheWithNetwork();
3645     }
3646   }
3647 
3648   if (!mCacheOpenDelay) {
3649     MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
3650     if (mNetworkTriggered) {
3651       mRaceCacheWithNetwork = sRCWNEnabled;
3652     }
3653     rv = cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags,
3654                                     this);
3655   } else {
3656     // We pass `this` explicitly as a parameter due to the raw pointer
3657     // to refcounted object in lambda analysis.
3658     mCacheOpenFunc = [openURI, extension, cacheEntryOpenFlags,
3659                       cacheStorage](nsHttpChannel *self) -> void {
3660       MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
3661       if (self->mNetworkTriggered) {
3662         self->mRaceCacheWithNetwork = true;
3663       }
3664       cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, self);
3665     };
3666 
3667     // calls nsHttpChannel::Notify after `mCacheOpenDelay` milliseconds
3668     NS_NewTimerWithCallback(getter_AddRefs(mCacheOpenTimer), this,
3669                             mCacheOpenDelay, nsITimer::TYPE_ONE_SHOT);
3670   }
3671   NS_ENSURE_SUCCESS(rv, rv);
3672 
3673   waitFlags.Keep(WAIT_FOR_CACHE_ENTRY);
3674 
3675 bypassCacheEntryOpen:
3676   if (!mApplicationCacheForWrite || !allowApplicationCache) return NS_OK;
3677 
3678   // If there is an app cache to write to, open the entry right now in parallel.
3679 
3680   // make sure we're not abusing this function
3681   NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open");
3682 
3683   if (offline) {
3684     // only put things in the offline cache while online
3685     return NS_OK;
3686   }
3687 
3688   if (mLoadFlags & INHIBIT_CACHING) {
3689     // respect demand not to cache
3690     return NS_OK;
3691   }
3692 
3693   if (!mRequestHead.IsGet()) {
3694     // only cache complete documents offline
3695     return NS_OK;
3696   }
3697 
3698   rv = cacheStorageService->AppCacheStorage(info, mApplicationCacheForWrite,
3699                                             getter_AddRefs(cacheStorage));
3700   NS_ENSURE_SUCCESS(rv, rv);
3701 
3702   rv = cacheStorage->AsyncOpenURI(mURI, EmptyCString(),
3703                                   nsICacheStorage::OPEN_TRUNCATE, this);
3704   NS_ENSURE_SUCCESS(rv, rv);
3705 
3706   waitFlags.Keep(WAIT_FOR_OFFLINE_CACHE_ENTRY);
3707 
3708   return NS_OK;
3709 }
3710 
CheckPartial(nsICacheEntry * aEntry,int64_t * aSize,int64_t * aContentLength)3711 nsresult nsHttpChannel::CheckPartial(nsICacheEntry *aEntry, int64_t *aSize,
3712                                      int64_t *aContentLength) {
3713   return nsHttp::CheckPartial(
3714       aEntry, aSize, aContentLength,
3715       mCachedResponseHead ? mCachedResponseHead : mResponseHead);
3716 }
3717 
UntieValidationRequest()3718 void nsHttpChannel::UntieValidationRequest() {
3719   DebugOnly<nsresult> rv;
3720   // Make the request unconditional again.
3721   rv = mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
3722   MOZ_ASSERT(NS_SUCCEEDED(rv));
3723   rv = mRequestHead.ClearHeader(nsHttp::If_None_Match);
3724   MOZ_ASSERT(NS_SUCCEEDED(rv));
3725   rv = mRequestHead.ClearHeader(nsHttp::ETag);
3726   MOZ_ASSERT(NS_SUCCEEDED(rv));
3727 }
3728 
3729 NS_IMETHODIMP
OnCacheEntryCheck(nsICacheEntry * entry,nsIApplicationCache * appCache,uint32_t * aResult)3730 nsHttpChannel::OnCacheEntryCheck(nsICacheEntry *entry,
3731                                  nsIApplicationCache *appCache,
3732                                  uint32_t *aResult) {
3733   nsresult rv = NS_OK;
3734 
3735   LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]", this,
3736        entry));
3737 
3738   mozilla::MutexAutoLock lock(mRCWNLock);
3739 
3740   if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_NETWORK) {
3741     LOG(
3742         ("Not using cached response because we've already got one from the "
3743          "network\n"));
3744     *aResult = ENTRY_NOT_WANTED;
3745 
3746     // Net-win indicates that mOnStartRequestTimestamp is from net.
3747     int64_t savedTime =
3748         (TimeStamp::Now() - mOnStartRequestTimestamp).ToMilliseconds();
3749     Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME,
3750                           savedTime);
3751     return NS_OK;
3752   } else if (mRaceCacheWithNetwork &&
3753              mFirstResponseSource == RESPONSE_PENDING) {
3754     mOnCacheEntryCheckTimestamp = TimeStamp::Now();
3755   }
3756 
3757   nsAutoCString cacheControlRequestHeader;
3758   Unused << mRequestHead.GetHeader(nsHttp::Cache_Control,
3759                                    cacheControlRequestHeader);
3760   CacheControlParser cacheControlRequest(cacheControlRequestHeader);
3761 
3762   if (cacheControlRequest.NoStore()) {
3763     LOG(
3764         ("Not using cached response based on no-store request cache "
3765          "directive\n"));
3766     *aResult = ENTRY_NOT_WANTED;
3767     return NS_OK;
3768   }
3769 
3770   // Be pessimistic: assume the cache entry has no useful data.
3771   *aResult = ENTRY_WANTED;
3772   mCachedContentIsValid = false;
3773 
3774   nsCString buf;
3775 
3776   // Get the method that was used to generate the cached response
3777   rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
3778   NS_ENSURE_SUCCESS(rv, rv);
3779 
3780   bool methodWasHead = buf.EqualsLiteral("HEAD");
3781   bool methodWasGet = buf.EqualsLiteral("GET");
3782 
3783   if (methodWasHead) {
3784     // The cached response does not contain an entity.  We can only reuse
3785     // the response if the current request is also HEAD.
3786     if (!mRequestHead.IsHead()) {
3787       return NS_OK;
3788     }
3789   }
3790   buf.Adopt(0);
3791 
3792   // We'll need this value in later computations...
3793   uint32_t lastModifiedTime;
3794   rv = entry->GetLastModified(&lastModifiedTime);
3795   NS_ENSURE_SUCCESS(rv, rv);
3796 
3797   // Determine if this is the first time that this cache entry
3798   // has been accessed during this session.
3799   bool fromPreviousSession =
3800       (gHttpHandler->SessionStartTime() > lastModifiedTime);
3801 
3802   // Get the cached HTTP response headers
3803   mCachedResponseHead = new nsHttpResponseHead();
3804 
3805   rv = nsHttp::GetHttpResponseHeadFromCacheEntry(entry, mCachedResponseHead);
3806   NS_ENSURE_SUCCESS(rv, rv);
3807 
3808   bool isCachedRedirect = WillRedirect(mCachedResponseHead);
3809 
3810   // Do not return 304 responses from the cache, and also do not return
3811   // any other non-redirect 3xx responses from the cache (see bug 759043).
3812   NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) || isCachedRedirect,
3813                  NS_ERROR_ABORT);
3814 
3815   if (mCachedResponseHead->NoStore() && mCacheEntryIsReadOnly) {
3816     // This prevents loading no-store responses when navigating back
3817     // while the browser is set to work offline.
3818     LOG(("  entry loading as read-only but is no-store, set INHIBIT_CACHING"));
3819     mLoadFlags |= nsIRequest::INHIBIT_CACHING;
3820   }
3821 
3822   // Don't bother to validate items that are read-only,
3823   // unless they are read-only because of INHIBIT_CACHING or because
3824   // we're updating the offline cache.
3825   // Don't bother to validate if this is a fallback entry.
3826   if (!mApplicationCacheForWrite &&
3827       (appCache ||
3828        (mCacheEntryIsReadOnly && !(mLoadFlags & nsIRequest::INHIBIT_CACHING)) ||
3829        mFallbackChannel)) {
3830     rv = OpenCacheInputStream(entry, true, !!appCache);
3831     if (NS_SUCCEEDED(rv)) {
3832       mCachedContentIsValid = true;
3833       entry->MaybeMarkValid();
3834     }
3835     return rv;
3836   }
3837 
3838   bool wantCompleteEntry = false;
3839 
3840   if (!methodWasHead && !isCachedRedirect) {
3841     // If the cached content-length is set and it does not match the data
3842     // size of the cached content, then the cached response is partial...
3843     // either we need to issue a byte range request or we need to refetch
3844     // the entire document.
3845     //
3846     // We exclude redirects from this check because we (usually) strip the
3847     // entity when we store the cache entry, and even if we didn't, we
3848     // always ignore a cached redirect's entity anyway. See bug 759043.
3849     int64_t size, contentLength;
3850     rv = CheckPartial(entry, &size, &contentLength);
3851     NS_ENSURE_SUCCESS(rv, rv);
3852 
3853     if (size == int64_t(-1)) {
3854       LOG(("  write is in progress"));
3855       if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
3856         LOG(
3857             ("  not interested in the entry, "
3858              "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified"));
3859 
3860         *aResult = ENTRY_NOT_WANTED;
3861         return NS_OK;
3862       }
3863 
3864       // Ignore !(size > 0) from the resumability condition
3865       if (!IsResumable(size, contentLength, true)) {
3866         if (IsNavigation()) {
3867           LOG(
3868               ("  bypassing wait for the entry, "
3869                "this is a navigational load"));
3870           *aResult = ENTRY_NOT_WANTED;
3871           return NS_OK;
3872         }
3873 
3874         LOG(
3875             ("  wait for entry completion, "
3876              "response is not resumable"));
3877 
3878         wantCompleteEntry = true;
3879       } else {
3880         mConcurrentCacheAccess = 1;
3881       }
3882     } else if (contentLength != int64_t(-1) && contentLength != size) {
3883       LOG(
3884           ("Cached data size does not match the Content-Length header "
3885            "[content-length=%" PRId64 " size=%" PRId64 "]\n",
3886            contentLength, size));
3887 
3888       rv = MaybeSetupByteRangeRequest(size, contentLength);
3889       mCachedContentIsPartial = NS_SUCCEEDED(rv) && mIsPartialRequest;
3890       if (mCachedContentIsPartial) {
3891         rv = OpenCacheInputStream(entry, false, !!appCache);
3892         if (NS_FAILED(rv)) {
3893           UntieByteRangeRequest();
3894           return rv;
3895         }
3896 
3897         *aResult = ENTRY_NEEDS_REVALIDATION;
3898         return NS_OK;
3899       }
3900 
3901       if (size == 0 && mCacheOnlyMetadata) {
3902         // Don't break cache entry load when the entry's data size
3903         // is 0 and mCacheOnlyMetadata flag is set. In that case we
3904         // want to proceed since the LOAD_ONLY_IF_MODIFIED flag is
3905         // also set.
3906         MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
3907       } else {
3908         return rv;
3909       }
3910     }
3911   }
3912 
3913   bool isHttps = false;
3914   rv = mURI->SchemeIs("https", &isHttps);
3915   NS_ENSURE_SUCCESS(rv, rv);
3916 
3917   bool doValidation = false;
3918   bool canAddImsHeader = true;
3919 
3920   bool isForcedValid = false;
3921   entry->GetIsForcedValid(&isForcedValid);
3922 
3923   bool weaklyFramed, isImmutable;
3924   nsHttp::DetermineFramingAndImmutability(entry, mCachedResponseHead, isHttps,
3925                                           &weaklyFramed, &isImmutable);
3926 
3927   // Cached entry is not the entity we request (see bug #633743)
3928   if (ResponseWouldVary(entry)) {
3929     LOG(("Validating based on Vary headers returning TRUE\n"));
3930     canAddImsHeader = false;
3931     doValidation = true;
3932   } else {
3933     doValidation = nsHttp::ValidationRequired(
3934         isForcedValid, mCachedResponseHead, mLoadFlags, mAllowStaleCacheContent,
3935         isImmutable, mCustomConditionalRequest, mRequestHead, entry,
3936         cacheControlRequest, fromPreviousSession);
3937   }
3938 
3939   // If a content signature is expected to be valid in this load,
3940   // set doValidation to force a signature check.
3941   if (!doValidation && mLoadInfo && mLoadInfo->GetVerifySignedContent()) {
3942     doValidation = true;
3943   }
3944 
3945   nsAutoCString requestedETag;
3946   if (!doValidation &&
3947       NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag)) &&
3948       (methodWasGet || methodWasHead)) {
3949     nsAutoCString cachedETag;
3950     Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, cachedETag);
3951     if (!cachedETag.IsEmpty() &&
3952         (StringBeginsWith(cachedETag, NS_LITERAL_CSTRING("W/")) ||
3953          !requestedETag.Equals(cachedETag))) {
3954       // User has defined If-Match header, if the cached entry is not
3955       // matching the provided header value or the cached ETag is weak,
3956       // force validation.
3957       doValidation = true;
3958     }
3959   }
3960 
3961   // Previous error should not be propagated.
3962   rv = NS_OK;
3963 
3964   if (!doValidation) {
3965     //
3966     // Check the authorization headers used to generate the cache entry.
3967     // We must validate the cache entry if:
3968     //
3969     // 1) the cache entry was generated prior to this session w/
3970     //    credentials (see bug 103402).
3971     // 2) the cache entry was generated w/o credentials, but would now
3972     //    require credentials (see bug 96705).
3973     //
3974     // NOTE: this does not apply to proxy authentication.
3975     //
3976     entry->GetMetaDataElement("auth", getter_Copies(buf));
3977     doValidation =
3978         (fromPreviousSession && !buf.IsEmpty()) ||
3979         (buf.IsEmpty() && mRequestHead.HasHeader(nsHttp::Authorization));
3980   }
3981 
3982   // Bug #561276: We maintain a chain of cache-keys which returns cached
3983   // 3xx-responses (redirects) in order to detect cycles. If a cycle is
3984   // found, ignore the cached response and hit the net. Otherwise, use
3985   // the cached response and add the cache-key to the chain. Note that
3986   // a limited number of redirects (cached or not) is allowed and is
3987   // enforced independently of this mechanism
3988   if (!doValidation && isCachedRedirect) {
3989     nsAutoCString cacheKey;
3990     rv = GenerateCacheKey(mPostID, cacheKey);
3991     MOZ_ASSERT(NS_SUCCEEDED(rv));
3992 
3993     if (!mRedirectedCachekeys)
3994       mRedirectedCachekeys = new nsTArray<nsCString>();
3995     else if (mRedirectedCachekeys->Contains(cacheKey))
3996       doValidation = true;
3997 
3998     LOG(("Redirection-chain %s key %s\n",
3999          doValidation ? "contains" : "does not contain", cacheKey.get()));
4000 
4001     // Append cacheKey if not in the chain already
4002     if (!doValidation) mRedirectedCachekeys->AppendElement(cacheKey);
4003   }
4004 
4005   mCachedContentIsValid = !doValidation;
4006 
4007   if (doValidation) {
4008     //
4009     // now, we are definitely going to issue a HTTP request to the server.
4010     // make it conditional if possible.
4011     //
4012     // do not attempt to validate no-store content, since servers will not
4013     // expect it to be cached.  (we only keep it in our cache for the
4014     // purposes of back/forward, etc.)
4015     //
4016     // the request method MUST be either GET or HEAD (see bug 175641) and
4017     // the cached response code must be < 400
4018     //
4019     // the cached content must not be weakly framed or marked immutable
4020     //
4021     // do not override conditional headers when consumer has defined its own
4022     if (!mCachedResponseHead->NoStore() &&
4023         (mRequestHead.IsGet() || mRequestHead.IsHead()) &&
4024         !mCustomConditionalRequest && !weaklyFramed && !isImmutable &&
4025         (mCachedResponseHead->Status() < 400)) {
4026       if (mConcurrentCacheAccess) {
4027         // In case of concurrent read and also validation request we
4028         // must wait for the current writer to close the output stream
4029         // first.  Otherwise, when the writer's job would have been interrupted
4030         // before all the data were downloaded, we'd have to do a range request
4031         // which would be a second request in line during this channel's
4032         // life-time.  nsHttpChannel is not designed to do that, so rather
4033         // turn off concurrent read and wait for entry's completion.
4034         // Then only re-validation or range-re-validation request will go out.
4035         mConcurrentCacheAccess = 0;
4036         // This will cause that OnCacheEntryCheck is called again with the same
4037         // entry after the writer is done.
4038         wantCompleteEntry = true;
4039       } else {
4040         nsAutoCString val;
4041         // Add If-Modified-Since header if a Last-Modified was given
4042         // and we are allowed to do this (see bugs 510359 and 269303)
4043         if (canAddImsHeader) {
4044           Unused << mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
4045           if (!val.IsEmpty()) {
4046             rv = mRequestHead.SetHeader(nsHttp::If_Modified_Since, val);
4047             MOZ_ASSERT(NS_SUCCEEDED(rv));
4048           }
4049         }
4050         // Add If-None-Match header if an ETag was given in the response
4051         Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, val);
4052         if (!val.IsEmpty()) {
4053           rv = mRequestHead.SetHeader(nsHttp::If_None_Match, val);
4054           MOZ_ASSERT(NS_SUCCEEDED(rv));
4055         }
4056         mDidReval = true;
4057       }
4058     }
4059   }
4060 
4061   if (mCachedContentIsValid || mDidReval) {
4062     rv = OpenCacheInputStream(entry, mCachedContentIsValid, !!appCache);
4063     if (NS_FAILED(rv)) {
4064       // If we can't get the entity then we have to act as though we
4065       // don't have the cache entry.
4066       if (mDidReval) {
4067         UntieValidationRequest();
4068         mDidReval = false;
4069       }
4070       mCachedContentIsValid = false;
4071     }
4072   }
4073 
4074   if (mDidReval)
4075     *aResult = ENTRY_NEEDS_REVALIDATION;
4076   else if (wantCompleteEntry)
4077     *aResult = RECHECK_AFTER_WRITE_FINISHED;
4078   else {
4079     *aResult = ENTRY_WANTED;
4080   }
4081 
4082   if (mCachedContentIsValid) {
4083     entry->MaybeMarkValid();
4084   }
4085 
4086   LOG(
4087       ("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d "
4088        "result=%d]\n",
4089        this, doValidation, *aResult));
4090   return rv;
4091 }
4092 
4093 NS_IMETHODIMP
OnCacheEntryAvailable(nsICacheEntry * entry,bool aNew,nsIApplicationCache * aAppCache,nsresult status)4094 nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry *entry, bool aNew,
4095                                      nsIApplicationCache *aAppCache,
4096                                      nsresult status) {
4097   MOZ_ASSERT(NS_IsMainThread());
4098 
4099   nsresult rv;
4100 
4101   LOG(
4102       ("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
4103        "new=%d appcache=%p status=%" PRIx32
4104        " mAppCache=%p mAppCacheForWrite=%p]\n",
4105        this, entry, aNew, aAppCache, static_cast<uint32_t>(status),
4106        mApplicationCache.get(), mApplicationCacheForWrite.get()));
4107 
4108   // if the channel's already fired onStopRequest, then we should ignore
4109   // this event.
4110   if (!mIsPending) {
4111     mCacheInputStream.CloseAndRelease();
4112     return NS_OK;
4113   }
4114 
4115   rv = OnCacheEntryAvailableInternal(entry, aNew, aAppCache, status);
4116   if (NS_FAILED(rv)) {
4117     CloseCacheEntry(false);
4118     if (mRaceCacheWithNetwork && mNetworkTriggered &&
4119         mFirstResponseSource != RESPONSE_FROM_CACHE) {
4120       // Ignore the error if we're racing cache with network and the cache
4121       // didn't win, The network part will handle cancelation or any other
4122       // error. Otherwise we could end up calling the listener twice, see
4123       // bug 1397593.
4124       LOG(
4125           ("  not calling AsyncAbort() because we're racing cache with "
4126            "network"));
4127     } else {
4128       Unused << AsyncAbort(rv);
4129     }
4130   }
4131 
4132   return NS_OK;
4133 }
4134 
OnCacheEntryAvailableInternal(nsICacheEntry * entry,bool aNew,nsIApplicationCache * aAppCache,nsresult status)4135 nsresult nsHttpChannel::OnCacheEntryAvailableInternal(
4136     nsICacheEntry *entry, bool aNew, nsIApplicationCache *aAppCache,
4137     nsresult status) {
4138   nsresult rv;
4139 
4140   if (mCanceled) {
4141     LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n", this,
4142          static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
4143     return mStatus;
4144   }
4145 
4146   if (mIgnoreCacheEntry) {
4147     if (!entry || aNew) {
4148       // We use this flag later to decide whether to report
4149       // LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent. We didn't have
4150       // an usable entry, so drop the flag.
4151       mIgnoreCacheEntry = false;
4152     }
4153     entry = nullptr;
4154     status = NS_ERROR_NOT_AVAILABLE;
4155   }
4156 
4157   if (aAppCache) {
4158     if (mApplicationCache == aAppCache && !mCacheEntry) {
4159       rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
4160     } else if (mApplicationCacheForWrite == aAppCache && aNew &&
4161                !mOfflineCacheEntry) {
4162       rv = OnOfflineCacheEntryForWritingAvailable(entry, aAppCache, status);
4163     } else {
4164       rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
4165     }
4166   } else {
4167     rv = OnNormalCacheEntryAvailable(entry, aNew, status);
4168   }
4169 
4170   if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
4171     // If we have a fallback URI (and we're not already
4172     // falling back), process the fallback asynchronously.
4173     if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
4174       return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
4175     }
4176 
4177     return NS_ERROR_DOCUMENT_NOT_CACHED;
4178   }
4179 
4180   if (NS_FAILED(rv)) {
4181     return rv;
4182   }
4183 
4184   // We may be waiting for more callbacks...
4185   if (AwaitingCacheCallbacks()) {
4186     return NS_OK;
4187   }
4188 
4189   if (mRaceCacheWithNetwork && ((mCacheEntry && !mCachedContentIsValid &&
4190                                  (mDidReval || mCachedContentIsPartial)) ||
4191                                 mIgnoreCacheEntry)) {
4192     // We won't send the conditional request because the unconditional
4193     // request was already sent (see bug 1377223).
4194     AccumulateCategorical(
4195         Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent);
4196   }
4197 
4198   if (mRaceCacheWithNetwork && mCachedContentIsValid) {
4199     Unused << ReadFromCache(true);
4200   }
4201 
4202   return TriggerNetwork();
4203 }
4204 
OnNormalCacheEntryAvailable(nsICacheEntry * aEntry,bool aNew,nsresult aEntryStatus)4205 nsresult nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry,
4206                                                     bool aNew,
4207                                                     nsresult aEntryStatus) {
4208   mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
4209 
4210   if (NS_FAILED(aEntryStatus) || aNew) {
4211     // Make sure this flag is dropped.  It may happen the entry is doomed
4212     // between OnCacheEntryCheck and OnCacheEntryAvailable.
4213     mCachedContentIsValid = false;
4214 
4215     // From the same reason remove any conditional headers added
4216     // in OnCacheEntryCheck.
4217     if (mDidReval) {
4218       LOG(("  Removing conditional request headers"));
4219       UntieValidationRequest();
4220       mDidReval = false;
4221     }
4222 
4223     if (mCachedContentIsPartial) {
4224       LOG(("  Removing byte range request headers"));
4225       UntieByteRangeRequest();
4226       mCachedContentIsPartial = false;
4227     }
4228 
4229     if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
4230       // if this channel is only allowed to pull from the cache, then
4231       // we must fail if we were unable to open a cache entry for read.
4232       return NS_ERROR_DOCUMENT_NOT_CACHED;
4233     }
4234   }
4235 
4236   if (NS_SUCCEEDED(aEntryStatus)) {
4237     mCacheEntry = aEntry;
4238     mCacheEntryIsWriteOnly = aNew;
4239 
4240     if (!aNew && !mAsyncOpenTime.IsNull()) {
4241       // We use microseconds for IO operations. For consistency let's use
4242       // microseconds here too.
4243       uint32_t duration = (TimeStamp::Now() - mAsyncOpenTime).ToMicroseconds();
4244       bool isSlow = false;
4245       if ((mCacheOpenWithPriority &&
4246            mCacheQueueSizeWhenOpen >= sRCWNQueueSizePriority) ||
4247           (!mCacheOpenWithPriority &&
4248            mCacheQueueSizeWhenOpen >= sRCWNQueueSizeNormal)) {
4249         isSlow = true;
4250       }
4251       CacheFileUtils::CachePerfStats::AddValue(
4252           CacheFileUtils::CachePerfStats::ENTRY_OPEN, duration, isSlow);
4253     }
4254 
4255     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
4256       Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, false);
4257     }
4258   }
4259 
4260   return NS_OK;
4261 }
4262 
OnOfflineCacheEntryAvailable(nsICacheEntry * aEntry,bool aNew,nsIApplicationCache * aAppCache,nsresult aEntryStatus)4263 nsresult nsHttpChannel::OnOfflineCacheEntryAvailable(
4264     nsICacheEntry *aEntry, bool aNew, nsIApplicationCache *aAppCache,
4265     nsresult aEntryStatus) {
4266   MOZ_ASSERT(!mApplicationCache || aAppCache == mApplicationCache);
4267   MOZ_ASSERT(!aNew || !aEntry || mApplicationCacheForWrite);
4268 
4269   mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
4270 
4271   nsresult rv;
4272 
4273   if (NS_SUCCEEDED(aEntryStatus)) {
4274     if (!mApplicationCache) {
4275       mApplicationCache = aAppCache;
4276     }
4277 
4278     // We successfully opened an offline cache session and the entry,
4279     // so indicate we will load from the offline cache.
4280     mLoadedFromApplicationCache = true;
4281     mCacheEntryIsReadOnly = true;
4282     mCacheEntry = aEntry;
4283     mCacheEntryIsWriteOnly = false;
4284 
4285     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI && !mApplicationCacheForWrite) {
4286       MaybeWarnAboutAppCache();
4287     }
4288 
4289     return NS_OK;
4290   }
4291 
4292   if (!mApplicationCacheForWrite && !mFallbackChannel) {
4293     if (!mApplicationCache) {
4294       mApplicationCache = aAppCache;
4295     }
4296 
4297     // Check for namespace match.
4298     nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
4299     rv = mApplicationCache->GetMatchingNamespace(
4300         mSpec, getter_AddRefs(namespaceEntry));
4301     NS_ENSURE_SUCCESS(rv, rv);
4302 
4303     uint32_t namespaceType = 0;
4304     if (!namespaceEntry ||
4305         NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
4306         (namespaceType & (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
4307                           nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) ==
4308             0) {
4309       // When loading from an application cache, only items
4310       // on the whitelist or matching a
4311       // fallback namespace should hit the network...
4312       mLoadFlags |= LOAD_ONLY_FROM_CACHE;
4313 
4314       // ... and if there were an application cache entry,
4315       // we would have found it earlier.
4316       return NS_ERROR_CACHE_KEY_NOT_FOUND;
4317     }
4318 
4319     if (namespaceType & nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
4320       nsAutoCString namespaceSpec;
4321       rv = namespaceEntry->GetNamespaceSpec(namespaceSpec);
4322       NS_ENSURE_SUCCESS(rv, rv);
4323 
4324       // This prevents fallback attacks injected by an insecure subdirectory
4325       // for the whole origin (or a parent directory).
4326       if (!IsInSubpathOfAppCacheManifest(mApplicationCache, namespaceSpec)) {
4327         return NS_OK;
4328       }
4329 
4330       rv = namespaceEntry->GetData(mFallbackKey);
4331       NS_ENSURE_SUCCESS(rv, rv);
4332     }
4333 
4334     if (namespaceType & nsIApplicationCacheNamespace::NAMESPACE_BYPASS) {
4335       LOG(
4336           ("nsHttpChannel::OnOfflineCacheEntryAvailable this=%p, URL matches "
4337            "NETWORK,"
4338            " looking for a regular cache entry",
4339            this));
4340 
4341       bool isHttps = false;
4342       rv = mURI->SchemeIs("https", &isHttps);
4343       NS_ENSURE_SUCCESS(rv, rv);
4344 
4345       rv = OpenCacheEntryInternal(isHttps, nullptr,
4346                                   false /* don't allow appcache lookups */);
4347       if (NS_FAILED(rv)) {
4348         // Don't let this fail when cache entry can't be synchronously open.
4349         // We want to go forward even without a regular cache entry.
4350         return NS_OK;
4351       }
4352     }
4353   }
4354 
4355   return NS_OK;
4356 }
4357 
OnOfflineCacheEntryForWritingAvailable(nsICacheEntry * aEntry,nsIApplicationCache * aAppCache,nsresult aEntryStatus)4358 nsresult nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(
4359     nsICacheEntry *aEntry, nsIApplicationCache *aAppCache,
4360     nsresult aEntryStatus) {
4361   MOZ_ASSERT(mApplicationCacheForWrite &&
4362              aAppCache == mApplicationCacheForWrite);
4363 
4364   mCacheEntriesToWaitFor &= ~WAIT_FOR_OFFLINE_CACHE_ENTRY;
4365 
4366   if (NS_SUCCEEDED(aEntryStatus)) {
4367     mOfflineCacheEntry = aEntry;
4368     if (NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))) {
4369       mOfflineCacheLastModifiedTime = 0;
4370     }
4371   }
4372 
4373   return aEntryStatus;
4374 }
4375 
4376 // Generates the proper cache-key for this instance of nsHttpChannel
GenerateCacheKey(uint32_t postID,nsACString & cacheKey)4377 nsresult nsHttpChannel::GenerateCacheKey(uint32_t postID,
4378                                          nsACString &cacheKey) {
4379   AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(), postID,
4380                    cacheKey);
4381   return NS_OK;
4382 }
4383 
4384 // Assembles a cache-key from the given pieces of information and |mLoadFlags|
AssembleCacheKey(const char * spec,uint32_t postID,nsACString & cacheKey)4385 void nsHttpChannel::AssembleCacheKey(const char *spec, uint32_t postID,
4386                                      nsACString &cacheKey) {
4387   cacheKey.Truncate();
4388 
4389   if (mLoadFlags & LOAD_ANONYMOUS) {
4390     cacheKey.AssignLiteral("anon&");
4391   }
4392 
4393   if (postID) {
4394     char buf[32];
4395     SprintfLiteral(buf, "id=%x&", postID);
4396     cacheKey.Append(buf);
4397   }
4398 
4399   if (!cacheKey.IsEmpty()) {
4400     cacheKey.AppendLiteral("uri=");
4401   }
4402 
4403   // Strip any trailing #ref from the URL before using it as the key
4404   const char *p = strchr(spec, '#');
4405   if (p)
4406     cacheKey.Append(spec, p - spec);
4407   else
4408     cacheKey.Append(spec);
4409 }
4410 
DoUpdateExpirationTime(nsHttpChannel * aSelf,nsICacheEntry * aCacheEntry,nsHttpResponseHead * aResponseHead,uint32_t & aExpirationTime)4411 nsresult DoUpdateExpirationTime(nsHttpChannel *aSelf,
4412                                 nsICacheEntry *aCacheEntry,
4413                                 nsHttpResponseHead *aResponseHead,
4414                                 uint32_t &aExpirationTime) {
4415   MOZ_ASSERT(aExpirationTime == 0);
4416   NS_ENSURE_TRUE(aResponseHead, NS_ERROR_FAILURE);
4417 
4418   nsresult rv;
4419 
4420   if (!aResponseHead->MustValidate()) {
4421     uint32_t freshnessLifetime = 0;
4422 
4423     rv = aResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
4424     if (NS_FAILED(rv)) return rv;
4425 
4426     if (freshnessLifetime > 0) {
4427       uint32_t now = NowInSeconds(), currentAge = 0;
4428 
4429       rv = aResponseHead->ComputeCurrentAge(now, aSelf->GetRequestTime(),
4430                                             &currentAge);
4431       if (NS_FAILED(rv)) return rv;
4432 
4433       LOG(("freshnessLifetime = %u, currentAge = %u\n", freshnessLifetime,
4434            currentAge));
4435 
4436       if (freshnessLifetime > currentAge) {
4437         uint32_t timeRemaining = freshnessLifetime - currentAge;
4438         // be careful... now + timeRemaining may overflow
4439         if (now + timeRemaining < now)
4440           aExpirationTime = uint32_t(-1);
4441         else
4442           aExpirationTime = now + timeRemaining;
4443       } else
4444         aExpirationTime = 0;
4445     }
4446   }
4447 
4448   rv = aCacheEntry->SetExpirationTime(aExpirationTime);
4449   NS_ENSURE_SUCCESS(rv, rv);
4450 
4451   return rv;
4452 }
4453 
4454 // UpdateExpirationTime is called when a new response comes in from the server.
4455 // It updates the stored response-time and sets the expiration time on the
4456 // cache entry.
4457 //
4458 // From section 13.2.4 of RFC2616, we compute expiration time as follows:
4459 //
4460 //    timeRemaining = freshnessLifetime - currentAge
4461 //    expirationTime = now + timeRemaining
4462 //
UpdateExpirationTime()4463 nsresult nsHttpChannel::UpdateExpirationTime() {
4464   uint32_t expirationTime = 0;
4465   nsresult rv =
4466       DoUpdateExpirationTime(this, mCacheEntry, mResponseHead, expirationTime);
4467   NS_ENSURE_SUCCESS(rv, rv);
4468 
4469   if (mOfflineCacheEntry) {
4470     rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
4471     NS_ENSURE_SUCCESS(rv, rv);
4472   }
4473 
4474   return NS_OK;
4475 }
4476 
HasQueryString(nsHttpRequestHead::ParsedMethodType method,nsIURI * uri)4477 /*static*/ inline bool nsHttpChannel::HasQueryString(
4478     nsHttpRequestHead::ParsedMethodType method, nsIURI *uri) {
4479   // Must be called on the main thread because nsIURI does not implement
4480   // thread-safe QueryInterface.
4481   MOZ_ASSERT(NS_IsMainThread());
4482 
4483   if (method != nsHttpRequestHead::kMethod_Get &&
4484       method != nsHttpRequestHead::kMethod_Head)
4485     return false;
4486 
4487   nsAutoCString query;
4488   nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
4489   nsresult rv = url->GetQuery(query);
4490   return NS_SUCCEEDED(rv) && !query.IsEmpty();
4491 }
4492 
ShouldUpdateOfflineCacheEntry()4493 bool nsHttpChannel::ShouldUpdateOfflineCacheEntry() {
4494   if (!mApplicationCacheForWrite || !mOfflineCacheEntry) {
4495     return false;
4496   }
4497 
4498   // if we're updating the cache entry, update the offline cache entry too
4499   if (mCacheEntry && mCacheEntryIsWriteOnly) {
4500     return true;
4501   }
4502 
4503   // if there's nothing in the offline cache, add it
4504   if (mOfflineCacheEntry) {
4505     return true;
4506   }
4507 
4508   // if the document is newer than the offline entry, update it
4509   uint32_t docLastModifiedTime;
4510   nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
4511   if (NS_FAILED(rv)) {
4512     return true;
4513   }
4514 
4515   if (mOfflineCacheLastModifiedTime == 0) {
4516     return false;
4517   }
4518 
4519   if (docLastModifiedTime > mOfflineCacheLastModifiedTime) {
4520     return true;
4521   }
4522 
4523   return false;
4524 }
4525 
OpenCacheInputStream(nsICacheEntry * cacheEntry,bool startBuffering,bool checkingAppCacheEntry)4526 nsresult nsHttpChannel::OpenCacheInputStream(nsICacheEntry *cacheEntry,
4527                                              bool startBuffering,
4528                                              bool checkingAppCacheEntry) {
4529   nsresult rv;
4530 
4531   bool isHttps = false;
4532   rv = mURI->SchemeIs("https", &isHttps);
4533   NS_ENSURE_SUCCESS(rv, rv);
4534 
4535   if (isHttps) {
4536     rv = cacheEntry->GetSecurityInfo(getter_AddRefs(mCachedSecurityInfo));
4537     if (NS_FAILED(rv)) {
4538       LOG(("failed to parse security-info [channel=%p, entry=%p]", this,
4539            cacheEntry));
4540       NS_WARNING("failed to parse security-info");
4541       cacheEntry->AsyncDoom(nullptr);
4542       return rv;
4543     }
4544 
4545     // XXX: We should not be skilling this check in the offline cache
4546     // case, but we have to do so now to work around bug 794507.
4547     bool mustHaveSecurityInfo =
4548         !mLoadedFromApplicationCache && !checkingAppCacheEntry;
4549     MOZ_ASSERT(mCachedSecurityInfo || !mustHaveSecurityInfo);
4550     if (!mCachedSecurityInfo && mustHaveSecurityInfo) {
4551       LOG(
4552           ("mCacheEntry->GetSecurityInfo returned success but did not "
4553            "return the security info [channel=%p, entry=%p]",
4554            this, cacheEntry));
4555       cacheEntry->AsyncDoom(nullptr);
4556       return NS_ERROR_UNEXPECTED;  // XXX error code
4557     }
4558   }
4559 
4560   // Keep the conditions below in sync with the conditions in ReadFromCache.
4561 
4562   rv = NS_OK;
4563 
4564   if (WillRedirect(mCachedResponseHead)) {
4565     // Do not even try to read the entity for a redirect because we do not
4566     // return an entity to the application when we process redirects.
4567     LOG(("Will skip read of cached redirect entity\n"));
4568     return NS_OK;
4569   }
4570 
4571   if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
4572       !mCachedContentIsPartial) {
4573     // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
4574     // cached entity.
4575     if (!mApplicationCacheForWrite) {
4576       LOG(
4577           ("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
4578            "load flag\n"));
4579       return NS_OK;
4580     }
4581 
4582     // If offline caching has been requested and the offline cache needs
4583     // updating, we must complete the call even if the main cache entry
4584     // is up to date. We don't know yet for sure whether the offline
4585     // cache needs updating because at this point we haven't opened it
4586     // for writing yet, so we have to start reading the cached entity now
4587     // just in case.
4588     LOG(
4589         ("May skip read from cache based on LOAD_ONLY_IF_MODIFIED "
4590          "load flag\n"));
4591   }
4592 
4593   // Open an input stream for the entity, so that the call to OpenInputStream
4594   // happens off the main thread.
4595   nsCOMPtr<nsIInputStream> stream;
4596 
4597   // If an alternate representation was requested, try to open the alt
4598   // input stream.
4599   // If the entry has a "is-from-child" metadata, then only open the altdata
4600   // stream if the consumer is also from child.
4601   bool altDataFromChild = false;
4602   {
4603     nsCString value;
4604     rv = cacheEntry->GetMetaDataElement("alt-data-from-child",
4605                                         getter_Copies(value));
4606     altDataFromChild = !value.IsEmpty();
4607   }
4608 
4609   if (!mPreferredCachedAltDataType.IsEmpty() &&
4610       (altDataFromChild == mAltDataForChild)) {
4611     rv = cacheEntry->OpenAlternativeInputStream(mPreferredCachedAltDataType,
4612                                                 getter_AddRefs(stream));
4613     if (NS_SUCCEEDED(rv)) {
4614       // We have succeeded.
4615       mAvailableCachedAltDataType = mPreferredCachedAltDataType;
4616       // Set the correct data size on the channel.
4617       int64_t altDataSize;
4618       if (NS_SUCCEEDED(cacheEntry->GetAltDataSize(&altDataSize))) {
4619         mAltDataLength = altDataSize;
4620       }
4621     }
4622   }
4623 
4624   if (!stream) {
4625     rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream));
4626   }
4627 
4628   if (NS_FAILED(rv)) {
4629     LOG(
4630         ("Failed to open cache input stream [channel=%p, "
4631          "mCacheEntry=%p]",
4632          this, cacheEntry));
4633     return rv;
4634   }
4635 
4636   if (startBuffering) {
4637     bool nonBlocking;
4638     rv = stream->IsNonBlocking(&nonBlocking);
4639     if (NS_SUCCEEDED(rv) && nonBlocking) startBuffering = false;
4640   }
4641 
4642   if (!startBuffering) {
4643     // Bypass wrapping the input stream for the new cache back-end since
4644     // nsIStreamTransportService expects a blocking stream.  Preloading of
4645     // the data must be done on the level of the cache backend, internally.
4646     //
4647     // We do not connect the stream to the stream transport service if we
4648     // have to validate the entry with the server. If we did, we would get
4649     // into a race condition between the stream transport service reading
4650     // the existing contents and the opening of the cache entry's output
4651     // stream to write the new contents in the case where we get a non-304
4652     // response.
4653     LOG(
4654         ("Opened cache input stream without buffering [channel=%p, "
4655          "mCacheEntry=%p, stream=%p]",
4656          this, cacheEntry, stream.get()));
4657     mCacheInputStream.takeOver(stream);
4658     return rv;
4659   }
4660 
4661   // Have the stream transport service start reading the entity on one of its
4662   // background threads.
4663 
4664   nsCOMPtr<nsITransport> transport;
4665   nsCOMPtr<nsIInputStream> wrapper;
4666 
4667   nsCOMPtr<nsIStreamTransportService> sts(
4668       services::GetStreamTransportService());
4669   rv = sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
4670   if (NS_SUCCEEDED(rv)) {
4671     rv = sts->CreateInputTransport(stream, true, getter_AddRefs(transport));
4672   }
4673   if (NS_SUCCEEDED(rv)) {
4674     rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
4675   }
4676   if (NS_SUCCEEDED(rv)) {
4677     LOG(
4678         ("Opened cache input stream [channel=%p, wrapper=%p, "
4679          "transport=%p, stream=%p]",
4680          this, wrapper.get(), transport.get(), stream.get()));
4681   } else {
4682     LOG(
4683         ("Failed to open cache input stream [channel=%p, "
4684          "wrapper=%p, transport=%p, stream=%p]",
4685          this, wrapper.get(), transport.get(), stream.get()));
4686 
4687     stream->Close();
4688     return rv;
4689   }
4690 
4691   mCacheInputStream.takeOver(wrapper);
4692 
4693   return NS_OK;
4694 }
4695 
4696 // Actually process the cached response that we started to handle in CheckCache
4697 // and/or StartBufferingCachedEntity.
ReadFromCache(bool alreadyMarkedValid)4698 nsresult nsHttpChannel::ReadFromCache(bool alreadyMarkedValid) {
4699   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
4700   NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE);
4701   NS_ENSURE_TRUE(!mCachePump, NS_OK);  // already opened
4702 
4703   LOG(
4704       ("nsHttpChannel::ReadFromCache [this=%p] "
4705        "Using cached copy of: %s\n",
4706        this, mSpec.get()));
4707 
4708   // When racing the cache with the network with a timer, and we get data from
4709   // the cache, we should prevent the timer from triggering a network request.
4710   if (mNetworkTriggerTimer) {
4711     mNetworkTriggerTimer->Cancel();
4712     mNetworkTriggerTimer = nullptr;
4713   }
4714 
4715   if (mRaceCacheWithNetwork) {
4716     MOZ_ASSERT(mFirstResponseSource != RESPONSE_FROM_CACHE);
4717     if (mFirstResponseSource == RESPONSE_PENDING) {
4718       LOG(("First response from cache\n"));
4719       mFirstResponseSource = RESPONSE_FROM_CACHE;
4720 
4721       // Cancel the transaction because we will serve the request from the cache
4722       CancelNetworkRequest(NS_BINDING_ABORTED);
4723       if (mTransactionPump && mSuspendCount) {
4724         uint32_t suspendCount = mSuspendCount;
4725         while (suspendCount--) {
4726           mTransactionPump->Resume();
4727         }
4728       }
4729       mTransaction = nullptr;
4730       mTransactionPump = nullptr;
4731     } else {
4732       MOZ_ASSERT(mFirstResponseSource == RESPONSE_FROM_NETWORK);
4733       LOG(
4734           ("Skipping read from cache because first response was from "
4735            "network\n"));
4736 
4737       if (!mOnCacheEntryCheckTimestamp.IsNull()) {
4738         TimeStamp currentTime = TimeStamp::Now();
4739         int64_t savedTime =
4740             (currentTime - mOnStartRequestTimestamp).ToMilliseconds();
4741         Telemetry::Accumulate(
4742             Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME, savedTime);
4743 
4744         int64_t diffTime =
4745             (currentTime - mOnCacheEntryCheckTimestamp).ToMilliseconds();
4746         Telemetry::Accumulate(
4747             Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_OCEC_ON_START_DIFF,
4748             diffTime);
4749       }
4750       return NS_OK;
4751     }
4752   }
4753 
4754   if (mCachedResponseHead) mResponseHead = Move(mCachedResponseHead);
4755 
4756   UpdateInhibitPersistentCachingFlag();
4757 
4758   // if we don't already have security info, try to get it from the cache
4759   // entry. there are two cases to consider here: 1) we are just reading
4760   // from the cache, or 2) this may be due to a 304 not modified response,
4761   // in which case we could have security info from a socket transport.
4762   if (!mSecurityInfo) mSecurityInfo = mCachedSecurityInfo;
4763 
4764   if (!alreadyMarkedValid && !mCachedContentIsPartial) {
4765     // We validated the entry, and we have write access to the cache, so
4766     // mark the cache entry as valid in order to allow others access to
4767     // this cache entry.
4768     //
4769     // TODO: This should be done asynchronously so we don't take the cache
4770     // service lock on the main thread.
4771     mCacheEntry->MaybeMarkValid();
4772   }
4773 
4774   nsresult rv;
4775 
4776   // Keep the conditions below in sync with the conditions in
4777   // StartBufferingCachedEntity.
4778 
4779   if (WillRedirect(mResponseHead)) {
4780     // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
4781     // to avoid event dispatching latency.
4782     MOZ_ASSERT(!mCacheInputStream);
4783     LOG(("Skipping skip read of cached redirect entity\n"));
4784     return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
4785   }
4786 
4787   if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
4788     if (!mApplicationCacheForWrite) {
4789       LOG(
4790           ("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
4791            "load flag\n"));
4792       MOZ_ASSERT(!mCacheInputStream);
4793       // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
4794       // here, to avoid event dispatching latency.
4795       return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
4796     }
4797 
4798     if (!ShouldUpdateOfflineCacheEntry()) {
4799       LOG(
4800           ("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
4801            "load flag (mApplicationCacheForWrite not null case)\n"));
4802       mCacheInputStream.CloseAndRelease();
4803       // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
4804       // here, to avoid event dispatching latency.
4805       return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
4806     }
4807   }
4808 
4809   MOZ_ASSERT(mCacheInputStream);
4810   if (!mCacheInputStream) {
4811     NS_ERROR(
4812         "mCacheInputStream is null but we're expecting to "
4813         "be able to read from it.");
4814     return NS_ERROR_UNEXPECTED;
4815   }
4816 
4817   nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget();
4818 
4819   rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream, 0, 0,
4820                                  true);
4821   if (NS_FAILED(rv)) {
4822     inputStream->Close();
4823     return rv;
4824   }
4825 
4826   rv = mCachePump->AsyncRead(this, mListenerContext);
4827   if (NS_FAILED(rv)) return rv;
4828 
4829   if (mTimingEnabled) mCacheReadStart = TimeStamp::Now();
4830 
4831   uint32_t suspendCount = mSuspendCount;
4832   while (suspendCount--) mCachePump->Suspend();
4833 
4834   return NS_OK;
4835 }
4836 
CloseCacheEntry(bool doomOnFailure)4837 void nsHttpChannel::CloseCacheEntry(bool doomOnFailure) {
4838   mCacheInputStream.CloseAndRelease();
4839 
4840   if (!mCacheEntry) return;
4841 
4842   LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32
4843        " mCacheEntryIsWriteOnly=%x",
4844        this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)),
4845        mCacheEntryIsWriteOnly));
4846 
4847   // If we have begun to create or replace a cache entry, and that cache
4848   // entry is not complete and not resumable, then it needs to be doomed.
4849   // Otherwise, CheckCache will make the mistake of thinking that the
4850   // partial cache entry is complete.
4851 
4852   bool doom = false;
4853   if (mInitedCacheEntry) {
4854     MOZ_ASSERT(mResponseHead, "oops");
4855     if (NS_FAILED(mStatus) && doomOnFailure && mCacheEntryIsWriteOnly &&
4856         !mResponseHead->IsResumable())
4857       doom = true;
4858   } else if (mCacheEntryIsWriteOnly)
4859     doom = true;
4860 
4861   if (doom) {
4862     LOG(("  dooming cache entry!!"));
4863     mCacheEntry->AsyncDoom(nullptr);
4864   } else {
4865     // Store updated security info, makes cached EV status race less likely
4866     // (see bug 1040086)
4867     if (mSecurityInfo) mCacheEntry->SetSecurityInfo(mSecurityInfo);
4868   }
4869 
4870   mCachedResponseHead = nullptr;
4871 
4872   mCachePump = nullptr;
4873   // This releases the entry for other consumers to use.
4874   // We call Dismiss() in case someone still keeps a reference
4875   // to this entry handle.
4876   mCacheEntry->Dismiss();
4877   mCacheEntry = nullptr;
4878   mCacheEntryIsWriteOnly = false;
4879   mInitedCacheEntry = false;
4880 }
4881 
CloseOfflineCacheEntry()4882 void nsHttpChannel::CloseOfflineCacheEntry() {
4883   if (!mOfflineCacheEntry) return;
4884 
4885   LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]", this));
4886 
4887   if (NS_FAILED(mStatus)) {
4888     mOfflineCacheEntry->AsyncDoom(nullptr);
4889   } else {
4890     bool succeeded;
4891     if (NS_SUCCEEDED(GetRequestSucceeded(&succeeded)) && !succeeded)
4892       mOfflineCacheEntry->AsyncDoom(nullptr);
4893   }
4894 
4895   mOfflineCacheEntry = nullptr;
4896 }
4897 
4898 // Initialize the cache entry for writing.
4899 //  - finalize storage policy
4900 //  - store security info
4901 //  - update expiration time
4902 //  - store headers and other meta data
InitCacheEntry()4903 nsresult nsHttpChannel::InitCacheEntry() {
4904   nsresult rv;
4905 
4906   NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED);
4907   // if only reading, nothing to be done here.
4908   if (mCacheEntryIsReadOnly) return NS_OK;
4909 
4910   // Don't cache the response again if already cached...
4911   if (mCachedContentIsValid) return NS_OK;
4912 
4913   LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n", this,
4914        mCacheEntry.get()));
4915 
4916   bool recreate = !mCacheEntryIsWriteOnly;
4917   bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING;
4918 
4919   if (!recreate && dontPersist) {
4920     // If the current entry is persistent but we inhibit peristence
4921     // then force recreation of the entry as memory/only.
4922     rv = mCacheEntry->GetPersistent(&recreate);
4923     if (NS_FAILED(rv)) return rv;
4924   }
4925 
4926   if (recreate) {
4927     LOG(
4928         ("  we have a ready entry, but reading it again from the server -> "
4929          "recreating cache entry\n"));
4930     // clean the altData cache and reset this to avoid wrong content length
4931     mAvailableCachedAltDataType.Truncate();
4932 
4933     nsCOMPtr<nsICacheEntry> currentEntry;
4934     currentEntry.swap(mCacheEntry);
4935     rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry));
4936     if (NS_FAILED(rv)) {
4937       LOG(("  recreation failed, the response will not be cached"));
4938       return NS_OK;
4939     }
4940 
4941     mCacheEntryIsWriteOnly = true;
4942   }
4943 
4944   // Set the expiration time for this cache entry
4945   rv = UpdateExpirationTime();
4946   if (NS_FAILED(rv)) return rv;
4947 
4948   // mark this weakly framed until a response body is seen
4949   mCacheEntry->SetMetaDataElement("strongly-framed", "0");
4950 
4951   rv = AddCacheEntryHeaders(mCacheEntry);
4952   if (NS_FAILED(rv)) return rv;
4953 
4954   mInitedCacheEntry = true;
4955 
4956   // Don't perform the check when writing (doesn't make sense)
4957   mConcurrentCacheAccess = 0;
4958 
4959   return NS_OK;
4960 }
4961 
UpdateInhibitPersistentCachingFlag()4962 void nsHttpChannel::UpdateInhibitPersistentCachingFlag() {
4963   // The no-store directive within the 'Cache-Control:' header indicates
4964   // that we must not store the response in a persistent cache.
4965   if (mResponseHead->NoStore()) mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
4966 
4967   // Only cache SSL content on disk if the pref is set
4968   bool isHttps;
4969   if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
4970       NS_SUCCEEDED(mURI->SchemeIs("https", &isHttps)) && isHttps) {
4971     mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
4972   }
4973 }
4974 
InitOfflineCacheEntry()4975 nsresult nsHttpChannel::InitOfflineCacheEntry() {
4976   // This function can be called even when we fail to connect (bug 551990)
4977 
4978   if (!mOfflineCacheEntry) {
4979     return NS_OK;
4980   }
4981 
4982   if (!mResponseHead || mResponseHead->NoStore()) {
4983     if (mResponseHead && mResponseHead->NoStore()) {
4984       mOfflineCacheEntry->AsyncDoom(nullptr);
4985     }
4986 
4987     CloseOfflineCacheEntry();
4988 
4989     if (mResponseHead && mResponseHead->NoStore()) {
4990       return NS_ERROR_NOT_AVAILABLE;
4991     }
4992 
4993     return NS_OK;
4994   }
4995 
4996   // This entry's expiration time should match the main entry's expiration
4997   // time.  UpdateExpirationTime() will keep it in sync once the offline
4998   // cache entry has been created.
4999   if (mCacheEntry) {
5000     uint32_t expirationTime;
5001     nsresult rv = mCacheEntry->GetExpirationTime(&expirationTime);
5002     NS_ENSURE_SUCCESS(rv, rv);
5003 
5004     mOfflineCacheEntry->SetExpirationTime(expirationTime);
5005   }
5006 
5007   return AddCacheEntryHeaders(mOfflineCacheEntry);
5008 }
5009 
DoAddCacheEntryHeaders(nsHttpChannel * self,nsICacheEntry * entry,nsHttpRequestHead * requestHead,nsHttpResponseHead * responseHead,nsISupports * securityInfo)5010 nsresult DoAddCacheEntryHeaders(nsHttpChannel *self, nsICacheEntry *entry,
5011                                 nsHttpRequestHead *requestHead,
5012                                 nsHttpResponseHead *responseHead,
5013                                 nsISupports *securityInfo) {
5014   nsresult rv;
5015 
5016   LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", self));
5017   // Store secure data in memory only
5018   if (securityInfo) entry->SetSecurityInfo(securityInfo);
5019 
5020   // Store the HTTP request method with the cache entry so we can distinguish
5021   // for example GET and HEAD responses.
5022   nsAutoCString method;
5023   requestHead->Method(method);
5024   rv = entry->SetMetaDataElement("request-method", method.get());
5025   if (NS_FAILED(rv)) return rv;
5026 
5027   // Store the HTTP authorization scheme used if any...
5028   rv = StoreAuthorizationMetaData(entry, requestHead);
5029   if (NS_FAILED(rv)) return rv;
5030 
5031   // Iterate over the headers listed in the Vary response header, and
5032   // store the value of the corresponding request header so we can verify
5033   // that it has not varied when we try to re-use the cached response at
5034   // a later time.  Take care to store "Cookie" headers only as hashes
5035   // due to security considerations and the fact that they can be pretty
5036   // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.
5037   //
5038   // NOTE: if "Vary: accept, cookie", then we will store the "accept" header
5039   // in the cache.  we could try to avoid needlessly storing the "accept"
5040   // header in this case, but it doesn't seem worth the extra code to perform
5041   // the check.
5042   {
5043     nsAutoCString buf, metaKey;
5044     Unused << responseHead->GetHeader(nsHttp::Vary, buf);
5045     if (!buf.IsEmpty()) {
5046       NS_NAMED_LITERAL_CSTRING(prefix, "request-");
5047 
5048       char *bufData = buf.BeginWriting();  // going to munge buf
5049       char *token = nsCRT::strtok(bufData, NS_HTTP_HEADER_SEPS, &bufData);
5050       while (token) {
5051         LOG(
5052             ("nsHttpChannel::AddCacheEntryHeaders [this=%p] "
5053              "processing %s",
5054              self, token));
5055         if (*token != '*') {
5056           nsHttpAtom atom = nsHttp::ResolveAtom(token);
5057           nsAutoCString val;
5058           nsAutoCString hash;
5059           if (NS_SUCCEEDED(requestHead->GetHeader(atom, val))) {
5060             // If cookie-header, store a hash of the value
5061             if (atom == nsHttp::Cookie) {
5062               LOG(
5063                   ("nsHttpChannel::AddCacheEntryHeaders [this=%p] "
5064                    "cookie-value %s",
5065                    self, val.get()));
5066               rv = Hash(val.get(), hash);
5067               // If hash failed, store a string not very likely
5068               // to be the result of subsequent hashes
5069               if (NS_FAILED(rv)) {
5070                 val = NS_LITERAL_CSTRING("<hash failed>");
5071               } else {
5072                 val = hash;
5073               }
5074 
5075               LOG(("   hashed to %s\n", val.get()));
5076             }
5077 
5078             // build cache meta data key and set meta data element...
5079             metaKey = prefix + nsDependentCString(token);
5080             entry->SetMetaDataElement(metaKey.get(), val.get());
5081           } else {
5082             LOG(
5083                 ("nsHttpChannel::AddCacheEntryHeaders [this=%p] "
5084                  "clearing metadata for %s",
5085                  self, token));
5086             metaKey = prefix + nsDependentCString(token);
5087             entry->SetMetaDataElement(metaKey.get(), nullptr);
5088           }
5089         }
5090         token = nsCRT::strtok(bufData, NS_HTTP_HEADER_SEPS, &bufData);
5091       }
5092     }
5093   }
5094 
5095   // Store the received HTTP head with the cache entry as an element of
5096   // the meta data.
5097   nsAutoCString head;
5098   responseHead->Flatten(head, true);
5099   rv = entry->SetMetaDataElement("response-head", head.get());
5100   if (NS_FAILED(rv)) return rv;
5101   head.Truncate();
5102   responseHead->FlattenNetworkOriginalHeaders(head);
5103   rv = entry->SetMetaDataElement("original-response-headers", head.get());
5104   if (NS_FAILED(rv)) return rv;
5105 
5106   // Indicate we have successfully finished setting metadata on the cache entry.
5107   rv = entry->MetaDataReady();
5108 
5109   return rv;
5110 }
5111 
AddCacheEntryHeaders(nsICacheEntry * entry)5112 nsresult nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry *entry) {
5113   return DoAddCacheEntryHeaders(this, entry, &mRequestHead, mResponseHead,
5114                                 mSecurityInfo);
5115 }
5116 
GetAuthType(const char * challenge,nsCString & authType)5117 inline void GetAuthType(const char *challenge, nsCString &authType) {
5118   const char *p;
5119 
5120   // get the challenge type
5121   if ((p = strchr(challenge, ' ')) != nullptr)
5122     authType.Assign(challenge, p - challenge);
5123   else
5124     authType.Assign(challenge);
5125 }
5126 
StoreAuthorizationMetaData(nsICacheEntry * entry,nsHttpRequestHead * requestHead)5127 nsresult StoreAuthorizationMetaData(nsICacheEntry *entry,
5128                                     nsHttpRequestHead *requestHead) {
5129   // Not applicable to proxy authorization...
5130   nsAutoCString val;
5131   if (NS_FAILED(requestHead->GetHeader(nsHttp::Authorization, val))) {
5132     return NS_OK;
5133   }
5134 
5135   // eg. [Basic realm="wally world"]
5136   nsAutoCString buf;
5137   GetAuthType(val.get(), buf);
5138   return entry->SetMetaDataElement("auth", buf.get());
5139 }
5140 
5141 // Finalize the cache entry
5142 //  - may need to rewrite response headers if any headers changed
5143 //  - may need to recalculate the expiration time if any headers changed
5144 //  - called only for freshly written cache entries
FinalizeCacheEntry()5145 nsresult nsHttpChannel::FinalizeCacheEntry() {
5146   LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this));
5147 
5148   // Don't update this meta-data on 304
5149   if (mStronglyFramed && !mCachedContentIsValid && mCacheEntry) {
5150     LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p] Is Strongly Framed\n",
5151          this));
5152     mCacheEntry->SetMetaDataElement("strongly-framed", "1");
5153   }
5154 
5155   if (mResponseHead && mResponseHeadersModified) {
5156     // Set the expiration time for this cache entry
5157     nsresult rv = UpdateExpirationTime();
5158     if (NS_FAILED(rv)) return rv;
5159   }
5160   return NS_OK;
5161 }
5162 
5163 // Open an output stream to the cache entry and insert a listener tee into
5164 // the chain of response listeners.
InstallCacheListener(int64_t offset)5165 nsresult nsHttpChannel::InstallCacheListener(int64_t offset) {
5166   nsresult rv;
5167 
5168   LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
5169 
5170   MOZ_ASSERT(mCacheEntry);
5171   MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial ||
5172              mRaceCacheWithNetwork);
5173   MOZ_ASSERT(mListener);
5174 
5175   nsAutoCString contentEncoding, contentType;
5176   Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
5177   mResponseHead->ContentType(contentType);
5178   // If the content is compressible and the server has not compressed it,
5179   // mark the cache entry for compression.
5180   if (contentEncoding.IsEmpty() &&
5181       (contentType.EqualsLiteral(TEXT_HTML) ||
5182        contentType.EqualsLiteral(TEXT_PLAIN) ||
5183        contentType.EqualsLiteral(TEXT_CSS) ||
5184        contentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
5185        contentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
5186        contentType.EqualsLiteral(TEXT_XML) ||
5187        contentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
5188        contentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
5189        contentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
5190        contentType.EqualsLiteral(APPLICATION_XHTML_XML))) {
5191     rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0");
5192     if (NS_FAILED(rv)) {
5193       LOG(("unable to mark cache entry for compression"));
5194     }
5195   }
5196 
5197   LOG(("Trading cache input stream for output stream [channel=%p]", this));
5198 
5199   // We must close the input stream first because cache entries do not
5200   // correctly handle having an output stream and input streams open at
5201   // the same time.
5202   mCacheInputStream.CloseAndRelease();
5203 
5204   nsCOMPtr<nsIOutputStream> out;
5205   rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
5206   if (rv == NS_ERROR_NOT_AVAILABLE) {
5207     LOG(("  entry doomed, not writing it [channel=%p]", this));
5208     // Entry is already doomed.
5209     // This may happen when expiration time is set to past and the entry
5210     // has been removed by the background eviction logic.
5211     return NS_OK;
5212   }
5213   if (NS_FAILED(rv)) return rv;
5214 
5215   if (mCacheOnlyMetadata) {
5216     LOG(("Not storing content, cacheOnlyMetadata set"));
5217     // We must open and then close the output stream of the cache entry.
5218     // This way we indicate the content has been written (despite with zero
5219     // length) and the entry is now in the ready state with "having data".
5220 
5221     out->Close();
5222     return NS_OK;
5223   }
5224 
5225     // XXX disk cache does not support overlapped i/o yet
5226 #if 0
5227     // Mark entry valid inorder to allow simultaneous reading...
5228     rv = mCacheEntry->MarkValid();
5229     if (NS_FAILED(rv)) return rv;
5230 #endif
5231 
5232   nsCOMPtr<nsIStreamListenerTee> tee =
5233       do_CreateInstance(kStreamListenerTeeCID, &rv);
5234   if (NS_FAILED(rv)) return rv;
5235 
5236   LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%" PRIx32, tee.get(),
5237        static_cast<uint32_t>(rv)));
5238   rv = tee->Init(mListener, out, nullptr);
5239   if (NS_FAILED(rv)) return rv;
5240 
5241   mListener = tee;
5242   return NS_OK;
5243 }
5244 
InstallOfflineCacheListener(int64_t offset)5245 nsresult nsHttpChannel::InstallOfflineCacheListener(int64_t offset) {
5246   nsresult rv;
5247 
5248   LOG(("Preparing to write data into the offline cache [uri=%s]\n",
5249        mSpec.get()));
5250 
5251   MOZ_ASSERT(mOfflineCacheEntry);
5252   MOZ_ASSERT(mListener);
5253 
5254   nsCOMPtr<nsIOutputStream> out;
5255   rv = mOfflineCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
5256   if (NS_FAILED(rv)) return rv;
5257 
5258   nsCOMPtr<nsIStreamListenerTee> tee =
5259       do_CreateInstance(kStreamListenerTeeCID, &rv);
5260   if (NS_FAILED(rv)) return rv;
5261 
5262   rv = tee->Init(mListener, out, nullptr);
5263   if (NS_FAILED(rv)) return rv;
5264 
5265   mListener = tee;
5266 
5267   return NS_OK;
5268 }
5269 
ClearBogusContentEncodingIfNeeded()5270 void nsHttpChannel::ClearBogusContentEncodingIfNeeded() {
5271   // For .gz files, apache sends both a Content-Type: application/x-gzip
5272   // as well as Content-Encoding: gzip, which is completely wrong.  In
5273   // this case, we choose to ignore the rogue Content-Encoding header. We
5274   // must do this early on so as to prevent it from being seen up stream.
5275   // The same problem exists for Content-Encoding: compress in default
5276   // Apache installs.
5277   nsAutoCString contentType;
5278   mResponseHead->ContentType(contentType);
5279   if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") &&
5280       (contentType.EqualsLiteral(APPLICATION_GZIP) ||
5281        contentType.EqualsLiteral(APPLICATION_GZIP2) ||
5282        contentType.EqualsLiteral(APPLICATION_GZIP3))) {
5283     // clear the Content-Encoding header
5284     mResponseHead->ClearHeader(nsHttp::Content_Encoding);
5285   } else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding,
5286                                            "compress") &&
5287              (contentType.EqualsLiteral(APPLICATION_COMPRESS) ||
5288               contentType.EqualsLiteral(APPLICATION_COMPRESS2))) {
5289     // clear the Content-Encoding header
5290     mResponseHead->ClearHeader(nsHttp::Content_Encoding);
5291   }
5292 }
5293 
5294 //-----------------------------------------------------------------------------
5295 // nsHttpChannel <redirect>
5296 //-----------------------------------------------------------------------------
5297 
SetupReplacementChannel(nsIURI * newURI,nsIChannel * newChannel,bool preserveMethod,uint32_t redirectFlags)5298 nsresult nsHttpChannel::SetupReplacementChannel(nsIURI *newURI,
5299                                                 nsIChannel *newChannel,
5300                                                 bool preserveMethod,
5301                                                 uint32_t redirectFlags) {
5302   LOG(
5303       ("nsHttpChannel::SetupReplacementChannel "
5304        "[this=%p newChannel=%p preserveMethod=%d]",
5305        this, newChannel, preserveMethod));
5306 
5307   nsresult rv = HttpBaseChannel::SetupReplacementChannel(
5308       newURI, newChannel, preserveMethod, redirectFlags);
5309   if (NS_FAILED(rv)) return rv;
5310 
5311   rv = CheckRedirectLimit(redirectFlags);
5312   NS_ENSURE_SUCCESS(rv, rv);
5313 
5314   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
5315   if (!httpChannel) return NS_OK;  // no other options to set
5316 
5317   // convey the mApplyConversion flag (bug 91862)
5318   nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
5319   if (encodedChannel) encodedChannel->SetApplyConversion(mApplyConversion);
5320 
5321   // transfer the resume information
5322   if (mResuming) {
5323     nsCOMPtr<nsIResumableChannel> resumableChannel(
5324         do_QueryInterface(newChannel));
5325     if (!resumableChannel) {
5326       NS_WARNING(
5327           "Got asked to resume, but redirected to non-resumable channel!");
5328       return NS_ERROR_NOT_RESUMABLE;
5329     }
5330     resumableChannel->ResumeAt(mStartPos, mEntityID);
5331   }
5332 
5333   return NS_OK;
5334 }
5335 
AsyncProcessRedirection(uint32_t redirectType)5336 nsresult nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType) {
5337   LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n", this,
5338        redirectType));
5339 
5340   nsAutoCString location;
5341 
5342   // if a location header was not given, then we can't perform the redirect,
5343   // so just carry on as though this were a normal response.
5344   if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location)))
5345     return NS_ERROR_FAILURE;
5346 
5347   // make sure non-ASCII characters in the location header are escaped.
5348   nsAutoCString locationBuf;
5349   if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII, locationBuf))
5350     location = locationBuf;
5351 
5352   mRedirectType = redirectType;
5353 
5354   LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(),
5355        uint32_t(mRedirectionLimit)));
5356 
5357   nsresult rv = CreateNewURI(location.get(), getter_AddRefs(mRedirectURI));
5358 
5359   if (NS_FAILED(rv)) {
5360     LOG(("Invalid URI for redirect: Location: %s\n", location.get()));
5361     return NS_ERROR_CORRUPTED_CONTENT;
5362   }
5363 
5364   if (mApplicationCache) {
5365     // if we are redirected to a different origin check if there is a fallback
5366     // cache entry to fall back to. we don't care about file strict
5367     // checking, at least mURI is not a file URI.
5368     if (!NS_SecurityCompareURIs(mURI, mRedirectURI, false)) {
5369       PushRedirectAsyncFunc(
5370           &nsHttpChannel::ContinueProcessRedirectionAfterFallback);
5371       bool waitingForRedirectCallback;
5372       Unused << ProcessFallback(&waitingForRedirectCallback);
5373       if (waitingForRedirectCallback) return NS_OK;
5374       PopRedirectAsyncFunc(
5375           &nsHttpChannel::ContinueProcessRedirectionAfterFallback);
5376     }
5377   }
5378 
5379   return ContinueProcessRedirectionAfterFallback(NS_OK);
5380 }
5381 
ContinueProcessRedirectionAfterFallback(nsresult rv)5382 nsresult nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) {
5383   if (NS_SUCCEEDED(rv) && mFallingBack) {
5384     // do not continue with redirect processing, fallback is in
5385     // progress now.
5386     return NS_OK;
5387   }
5388 
5389   // Kill the current cache entry if we are redirecting
5390   // back to ourself.
5391   bool redirectingBackToSameURI = false;
5392   if (mCacheEntry && mCacheEntryIsWriteOnly &&
5393       NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
5394       redirectingBackToSameURI)
5395     mCacheEntry->AsyncDoom(nullptr);
5396 
5397   // move the reference of the old location to the new one if the new
5398   // one has none.
5399   PropagateReferenceIfNeeded(mURI, mRedirectURI);
5400 
5401   bool rewriteToGET =
5402       ShouldRewriteRedirectToGET(mRedirectType, mRequestHead.ParsedMethod());
5403 
5404   // prompt if the method is not safe (such as POST, PUT, DELETE, ...)
5405   if (!rewriteToGET && !mRequestHead.IsSafeMethod()) {
5406     rv = PromptTempRedirect();
5407     if (NS_FAILED(rv)) return rv;
5408   }
5409 
5410   nsCOMPtr<nsIIOService> ioService;
5411   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
5412   if (NS_FAILED(rv)) return rv;
5413 
5414   uint32_t redirectFlags;
5415   if (nsHttp::IsPermanentRedirect(mRedirectType))
5416     redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
5417   else
5418     redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
5419 
5420   nsCOMPtr<nsIChannel> newChannel;
5421   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
5422       CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
5423   rv = NS_NewChannelInternal(getter_AddRefs(newChannel), mRedirectURI,
5424                              redirectLoadInfo,
5425                              nullptr,  // PerformanceStorage
5426                              nullptr,  // aLoadGroup
5427                              nullptr,  // aCallbacks
5428                              nsIRequest::LOAD_NORMAL, ioService);
5429   NS_ENSURE_SUCCESS(rv, rv);
5430 
5431   rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET,
5432                                redirectFlags);
5433   if (NS_FAILED(rv)) return rv;
5434 
5435   // verify that this is a legal redirect
5436   mRedirectChannel = newChannel;
5437 
5438   PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
5439   rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
5440 
5441   if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
5442 
5443   if (NS_FAILED(rv)) {
5444     AutoRedirectVetoNotifier notifier(this);
5445     PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
5446   }
5447 
5448   return rv;
5449 }
5450 
ContinueProcessRedirection(nsresult rv)5451 nsresult nsHttpChannel::ContinueProcessRedirection(nsresult rv) {
5452   AutoRedirectVetoNotifier notifier(this);
5453 
5454   LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%" PRIx32 ",this=%p]\n",
5455        static_cast<uint32_t>(rv), this));
5456   if (NS_FAILED(rv)) return rv;
5457 
5458   NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
5459 
5460   // Make sure to do this after we received redirect veto answer,
5461   // i.e. after all sinks had been notified
5462   mRedirectChannel->SetOriginalURI(mOriginalURI);
5463 
5464   // XXX we used to talk directly with the script security manager, but that
5465   // should really be handled by the event sink implementation.
5466 
5467   // begin loading the new channel
5468   if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
5469     MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
5470     rv = mRedirectChannel->AsyncOpen2(mListener);
5471   } else {
5472     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
5473   }
5474   NS_ENSURE_SUCCESS(rv, rv);
5475 
5476   // close down this channel
5477   Cancel(NS_BINDING_REDIRECTED);
5478 
5479   notifier.RedirectSucceeded();
5480 
5481   ReleaseListeners();
5482 
5483   return NS_OK;
5484 }
5485 
5486 //-----------------------------------------------------------------------------
5487 // nsHttpChannel <auth>
5488 //-----------------------------------------------------------------------------
5489 
OnAuthAvailable()5490 NS_IMETHODIMP nsHttpChannel::OnAuthAvailable() {
5491   LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this));
5492 
5493   // setting mAuthRetryPending flag and resuming the transaction
5494   // triggers process of throwing away the unauthenticated data already
5495   // coming from the network
5496   mAuthRetryPending = true;
5497   mProxyAuthPending = false;
5498   LOG(("Resuming the transaction, we got credentials from user"));
5499   if (mTransactionPump) {
5500     mTransactionPump->Resume();
5501   }
5502 
5503   return NS_OK;
5504 }
5505 
OnAuthCancelled(bool userCancel)5506 NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel) {
5507   LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this));
5508 
5509   if (mTransactionPump) {
5510     // If the channel is trying to authenticate to a proxy and
5511     // that was canceled we cannot show the http response body
5512     // from the 40x as that might mislead the user into thinking
5513     // it was a end host response instead of a proxy reponse.
5514     // This must check explicitly whether a proxy auth was being done
5515     // because we do want to show the content if this is an error from
5516     // the origin server.
5517     if (mProxyAuthPending) Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED);
5518 
5519     // ensure call of OnStartRequest of the current listener here,
5520     // it would not be called otherwise at all
5521     nsresult rv = CallOnStartRequest();
5522 
5523     // drop mAuthRetryPending flag and resume the transaction
5524     // this resumes load of the unauthenticated content data (which
5525     // may have been canceled if we don't want to show it)
5526     mAuthRetryPending = false;
5527     LOG(("Resuming the transaction, user cancelled the auth dialog"));
5528     mTransactionPump->Resume();
5529 
5530     if (NS_FAILED(rv)) mTransactionPump->Cancel(rv);
5531   }
5532 
5533   mProxyAuthPending = false;
5534   return NS_OK;
5535 }
5536 
CloseStickyConnection()5537 NS_IMETHODIMP nsHttpChannel::CloseStickyConnection() {
5538   LOG(("nsHttpChannel::CloseStickyConnection this=%p", this));
5539 
5540   // Require we are between OnStartRequest and OnStopRequest, because
5541   // what we do here takes effect in OnStopRequest (not reusing the
5542   // connection for next authentication round).
5543   if (!mIsPending) {
5544     LOG(("  channel not pending"));
5545     NS_ERROR(
5546         "CloseStickyConnection not called before OnStopRequest, won't have any "
5547         "effect");
5548     return NS_ERROR_UNEXPECTED;
5549   }
5550 
5551   MOZ_ASSERT(mTransaction);
5552   if (!mTransaction) {
5553     return NS_ERROR_UNEXPECTED;
5554   }
5555 
5556   if (!(mCaps & NS_HTTP_STICKY_CONNECTION ||
5557         mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
5558     LOG(("  not sticky"));
5559     return NS_OK;
5560   }
5561 
5562   RefPtr<nsAHttpConnection> conn = mTransaction->GetConnectionReference();
5563   if (!conn) {
5564     LOG(("  no connection"));
5565     return NS_OK;
5566   }
5567 
5568   // This turns the IsPersistent() indicator on the connection to false,
5569   // and makes us throw it away in OnStopRequest.
5570   conn->DontReuse();
5571   return NS_OK;
5572 }
5573 
ConnectionRestartable(bool aRestartable)5574 NS_IMETHODIMP nsHttpChannel::ConnectionRestartable(bool aRestartable) {
5575   LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d", this,
5576        aRestartable));
5577   mAuthConnectionRestartable = aRestartable;
5578   return NS_OK;
5579 }
5580 
5581 //-----------------------------------------------------------------------------
5582 // nsHttpChannel::nsISupports
5583 //-----------------------------------------------------------------------------
5584 
5585 NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
5586 NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
5587 
5588 NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
5589   NS_INTERFACE_MAP_ENTRY(nsIRequest)
5590   NS_INTERFACE_MAP_ENTRY(nsIChannel)
5591   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
5592   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
5593   NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
5594   NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
5595   NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
5596   NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
5597   NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
5598   NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
5599   NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
5600   NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)
5601   NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
5602   NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
5603   NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
5604   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
5605   NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
5606   NS_INTERFACE_MAP_ENTRY(nsIInputAvailableCallback)
5607   NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
5608   NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
5609   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
5610   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
5611   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
5612   NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
5613   NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
5614   NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
5615   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
5616   NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
5617   NS_INTERFACE_MAP_ENTRY(nsIRaceCacheWithNetwork)
5618   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
5619   NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
5620   NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback)
5621   // we have no macro that covers this case.
5622   if (aIID.Equals(NS_GET_IID(nsHttpChannel))) {
5623     AddRef();
5624     *aInstancePtr = this;
5625     return NS_OK;
5626   } else
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)5627 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
5628 
5629 //-----------------------------------------------------------------------------
5630 // nsHttpChannel::nsIRequest
5631 //-----------------------------------------------------------------------------
5632 
5633 NS_IMETHODIMP
5634 nsHttpChannel::Cancel(nsresult status) {
5635   MOZ_ASSERT(NS_IsMainThread());
5636   // We should never have a pump open while a CORS preflight is in progress.
5637   MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
5638 
5639   LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 "]\n", this,
5640        static_cast<uint32_t>(status)));
5641   if (mCanceled) {
5642     LOG(("  ignoring; already canceled\n"));
5643     return NS_OK;
5644   }
5645 
5646   if (mWaitingForRedirectCallback) {
5647     LOG(("channel canceled during wait for redirect callback"));
5648   }
5649   mCanceled = true;
5650   mStatus = status;
5651   if (mProxyRequest) mProxyRequest->Cancel(status);
5652   CancelNetworkRequest(status);
5653   mCacheInputStream.CloseAndRelease();
5654   if (mCachePump) mCachePump->Cancel(status);
5655   if (mAuthProvider) mAuthProvider->Cancel(status);
5656   if (mPreflightChannel) mPreflightChannel->Cancel(status);
5657   if (mRequestContext && mOnTailUnblock) {
5658     mOnTailUnblock = nullptr;
5659     mRequestContext->CancelTailedRequest(this);
5660     CloseCacheEntry(false);
5661     Unused << AsyncAbort(status);
5662   }
5663   return NS_OK;
5664 }
5665 
CancelNetworkRequest(nsresult aStatus)5666 void nsHttpChannel::CancelNetworkRequest(nsresult aStatus) {
5667   if (mTransaction) {
5668     nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus);
5669     if (NS_FAILED(rv)) {
5670       LOG(("failed to cancel the transaction\n"));
5671     }
5672   }
5673   if (mTransactionPump) mTransactionPump->Cancel(aStatus);
5674 }
5675 
5676 NS_IMETHODIMP
Suspend()5677 nsHttpChannel::Suspend() {
5678   nsresult rv = SuspendInternal();
5679 
5680   nsresult rvParentChannel = NS_OK;
5681   if (mParentChannel) {
5682     rvParentChannel = mParentChannel->SuspendMessageDiversion();
5683   }
5684 
5685   return NS_FAILED(rv) ? rv : rvParentChannel;
5686 }
5687 
5688 NS_IMETHODIMP
Resume()5689 nsHttpChannel::Resume() {
5690   nsresult rv = ResumeInternal();
5691 
5692   nsresult rvParentChannel = NS_OK;
5693   if (mParentChannel) {
5694     rvParentChannel = mParentChannel->ResumeMessageDiversion();
5695   }
5696 
5697   return NS_FAILED(rv) ? rv : rvParentChannel;
5698 }
5699 
5700 //-----------------------------------------------------------------------------
5701 // nsHttpChannel::nsIChannel
5702 //-----------------------------------------------------------------------------
5703 
5704 NS_IMETHODIMP
GetSecurityInfo(nsISupports ** securityInfo)5705 nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo) {
5706   NS_ENSURE_ARG_POINTER(securityInfo);
5707   *securityInfo = mSecurityInfo;
5708   NS_IF_ADDREF(*securityInfo);
5709   return NS_OK;
5710 }
5711 
5712 // If any of the functions that AsyncOpen calls returns immediately an error
5713 // AsyncAbort(which calls onStart/onStopRequest) does not need to be call.
5714 // To be sure that they are not call ReleaseListeners() is called.
5715 // If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on
5716 // any error.
5717 NS_IMETHODIMP
AsyncOpen(nsIStreamListener * listener,nsISupports * context)5718 nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) {
5719   MOZ_ASSERT(
5720       !mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
5721           mLoadInfo->GetInitialSecurityCheckDone() ||
5722           (mLoadInfo->GetSecurityMode() ==
5723                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
5724            nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
5725       "security flags in loadInfo but asyncOpen2() not called");
5726 
5727   LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
5728 
5729 #ifdef MOZ_TASK_TRACER
5730   if (tasktracer::IsStartLogging()) {
5731     uint64_t sourceEventId, parentTaskId;
5732     tasktracer::SourceEventType sourceEventType;
5733     GetCurTraceInfo(&sourceEventId, &parentTaskId, &sourceEventType);
5734     nsCOMPtr<nsIURI> uri;
5735     GetURI(getter_AddRefs(uri));
5736     nsAutoCString urispec;
5737     uri->GetSpec(urispec);
5738     tasktracer::AddLabel("nsHttpChannel::AsyncOpen %s", urispec.get());
5739   }
5740 #endif
5741 
5742   NS_CompareLoadInfoAndLoadContext(this);
5743 
5744 #ifdef DEBUG
5745   AssertPrivateBrowsingId();
5746 #endif
5747 
5748   NS_ENSURE_ARG_POINTER(listener);
5749   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
5750   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
5751 
5752   nsresult rv;
5753 
5754   MOZ_ASSERT(NS_IsMainThread());
5755 
5756   if (!gHttpHandler->Active()) {
5757     LOG(("  after HTTP shutdown..."));
5758     ReleaseListeners();
5759     return NS_ERROR_NOT_AVAILABLE;
5760   }
5761 
5762   static bool sRCWNInited = false;
5763   if (!sRCWNInited) {
5764     sRCWNInited = true;
5765     Preferences::AddBoolVarCache(&sRCWNEnabled, "network.http.rcwn.enabled");
5766     Preferences::AddUintVarCache(
5767         &sRCWNQueueSizeNormal,
5768         "network.http.rcwn.cache_queue_normal_threshold");
5769     Preferences::AddUintVarCache(
5770         &sRCWNQueueSizePriority,
5771         "network.http.rcwn.cache_queue_priority_threshold");
5772     Preferences::AddUintVarCache(&sRCWNSmallResourceSizeKB,
5773                                  "network.http.rcwn.small_resource_size_kb");
5774     Preferences::AddUintVarCache(&sRCWNMinWaitMs,
5775                                  "network.http.rcwn.min_wait_before_racing_ms");
5776     Preferences::AddUintVarCache(&sRCWNMaxWaitMs,
5777                                  "network.http.rcwn.max_wait_before_racing_ms");
5778   }
5779 
5780   rv = NS_CheckPortSafety(mURI);
5781   if (NS_FAILED(rv)) {
5782     ReleaseListeners();
5783     return rv;
5784   }
5785 
5786   if (WaitingForTailUnblock()) {
5787     // This channel is marked as Tail and is part of a request context
5788     // that has positive number of non-tailed requestst, hence this channel
5789     // has been put to a queue.
5790     // When tail is unblocked, OnTailUnblock on this channel will be called
5791     // to continue AsyncOpen.
5792     mListener = listener;
5793     mListenerContext = context;
5794     MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
5795     mOnTailUnblock = &nsHttpChannel::AsyncOpenOnTailUnblock;
5796 
5797     LOG(("  put on hold until tail is unblocked"));
5798     return NS_OK;
5799   }
5800 
5801   // Remember the cookie header that was set, if any
5802   nsAutoCString cookieHeader;
5803   if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookieHeader))) {
5804     mUserSetCookieHeader = cookieHeader;
5805   }
5806 
5807   AddCookiesToRequest();
5808 
5809   // Set user agent override, do so before OnOpeningRequest notification
5810   // since we want to allow consumers of that notification change or remove
5811   // the User-Agent request header.
5812   HttpBaseChannel::SetDocshellUserAgentOverride();
5813 
5814   // After we notify any observers (on-opening-request, loadGroup, etc) we
5815   // must return NS_OK and return any errors asynchronously via
5816   // OnStart/OnStopRequest.  Observers may add a reference to the channel
5817   // and expect to get OnStopRequest so they know when to drop the reference,
5818   // etc.
5819 
5820   // notify "http-on-opening-request" observers, but not if this is a redirect
5821   if (!(mLoadFlags & LOAD_REPLACE)) {
5822     gHttpHandler->OnOpeningRequest(this);
5823   }
5824 
5825   mIsPending = true;
5826   mWasOpened = true;
5827 
5828   mListener = listener;
5829   mListenerContext = context;
5830 
5831   if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
5832 
5833   // record asyncopen time unconditionally and clear it if we
5834   // don't want it after OnModifyRequest() weighs in. But waiting for
5835   // that to complete would mean we don't include proxy resolution in the
5836   // timing.
5837   mAsyncOpenTime = TimeStamp::Now();
5838 
5839   // Remember we have Authorization header set here.  We need to check on it
5840   // just once and early, AsyncOpen is the best place.
5841   mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization);
5842 
5843   // The common case for HTTP channels is to begin proxy resolution and return
5844   // at this point. The only time we know mProxyInfo already is if we're
5845   // proxying a non-http protocol like ftp.
5846   if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) {
5847     return NS_OK;
5848   }
5849 
5850   rv = BeginConnect();
5851   if (NS_FAILED(rv)) {
5852     CloseCacheEntry(false);
5853     Unused << AsyncAbort(rv);
5854   }
5855 
5856   return NS_OK;
5857 }
5858 
AsyncOpenOnTailUnblock()5859 nsresult nsHttpChannel::AsyncOpenOnTailUnblock() {
5860   return AsyncOpen(mListener, mListenerContext);
5861 }
5862 
5863 already_AddRefed<nsChannelClassifier>
GetOrCreateChannelClassifier()5864 nsHttpChannel::GetOrCreateChannelClassifier() {
5865   if (!mChannelClassifier) {
5866     mChannelClassifier = new nsChannelClassifier(this);
5867     LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n", this,
5868          mChannelClassifier.get()));
5869   }
5870 
5871   RefPtr<nsChannelClassifier> classifier = mChannelClassifier;
5872   return classifier.forget();
5873 }
5874 
5875 NS_IMETHODIMP
AsyncOpen2(nsIStreamListener * aListener)5876 nsHttpChannel::AsyncOpen2(nsIStreamListener *aListener) {
5877   nsCOMPtr<nsIStreamListener> listener = aListener;
5878   nsresult rv =
5879       nsContentSecurityManager::doContentSecurityCheck(this, listener);
5880   if (NS_WARN_IF(NS_FAILED(rv))) {
5881     ReleaseListeners();
5882     return rv;
5883   }
5884   return AsyncOpen(listener, nullptr);
5885 }
5886 
5887 // BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by
5888 // functions that called BeginConnect if needed. Only AsyncOpen and
5889 // OnProxyAvailable ever call BeginConnect.
BeginConnect()5890 nsresult nsHttpChannel::BeginConnect() {
5891   LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
5892   nsresult rv;
5893 
5894   // Construct connection info object
5895   nsAutoCString host;
5896   nsAutoCString scheme;
5897   int32_t port = -1;
5898   bool isHttps = false;
5899 
5900   rv = mURI->GetScheme(scheme);
5901   if (NS_SUCCEEDED(rv)) rv = mURI->SchemeIs("https", &isHttps);
5902   if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host);
5903   if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port);
5904   if (NS_SUCCEEDED(rv)) mURI->GetUsername(mUsername);
5905   if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec);
5906   if (NS_FAILED(rv)) {
5907     return rv;
5908   }
5909 
5910   // Reject the URL if it doesn't specify a host
5911   if (host.IsEmpty()) {
5912     rv = NS_ERROR_MALFORMED_URI;
5913     return rv;
5914   }
5915   LOG(("host=%s port=%d\n", host.get(), port));
5916   LOG(("uri=%s\n", mSpec.get()));
5917 
5918   nsCOMPtr<nsProxyInfo> proxyInfo;
5919   if (mProxyInfo) proxyInfo = do_QueryInterface(mProxyInfo);
5920 
5921   mRequestHead.SetHTTPS(isHttps);
5922   mRequestHead.SetOrigin(scheme, host, port);
5923 
5924   SetOriginHeader();
5925   SetDoNotTrack();
5926 
5927   OriginAttributes originAttributes;
5928   NS_GetOriginAttributes(this, originAttributes);
5929 
5930   RefPtr<AltSvcMapping> mapping;
5931   if (!mConnectionInfo && mAllowAltSvc &&  // per channel
5932       !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
5933       (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
5934       (!proxyInfo || proxyInfo->IsDirect()) &&
5935       (mapping = gHttpHandler->GetAltServiceMapping(
5936            scheme, host, port, mPrivateBrowsing, originAttributes))) {
5937     LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", this,
5938          scheme.get(), mapping->AlternateHost().get(), mapping->AlternatePort(),
5939          mapping->HashKey().get()));
5940 
5941     if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) {
5942       nsAutoCString altUsedLine(mapping->AlternateHost());
5943       bool defaultPort =
5944           mapping->AlternatePort() ==
5945           (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT);
5946       if (!defaultPort) {
5947         altUsedLine.AppendLiteral(":");
5948         altUsedLine.AppendInt(mapping->AlternatePort());
5949       }
5950       rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine);
5951       MOZ_ASSERT(NS_SUCCEEDED(rv));
5952     }
5953 
5954     nsCOMPtr<nsIConsoleService> consoleService =
5955         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
5956     if (consoleService) {
5957       nsAutoString message(
5958           NS_LITERAL_STRING("Alternate Service Mapping found: "));
5959       AppendASCIItoUTF16(scheme.get(), message);
5960       message.AppendLiteral(u"://");
5961       AppendASCIItoUTF16(host.get(), message);
5962       message.AppendLiteral(u":");
5963       message.AppendInt(port);
5964       message.AppendLiteral(u" to ");
5965       AppendASCIItoUTF16(scheme.get(), message);
5966       message.AppendLiteral(u"://");
5967       AppendASCIItoUTF16(mapping->AlternateHost().get(), message);
5968       message.AppendLiteral(u":");
5969       message.AppendInt(mapping->AlternatePort());
5970       consoleService->LogStringMessage(message.get());
5971     }
5972 
5973     LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this));
5974     mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo,
5975                                originAttributes);
5976     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
5977     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE, !isHttps);
5978   } else if (mConnectionInfo) {
5979     LOG(("nsHttpChannel %p Using channel supplied connection info", this));
5980     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
5981   } else {
5982     LOG(("nsHttpChannel %p Using default connection info", this));
5983 
5984     mConnectionInfo =
5985         new nsHttpConnectionInfo(host, port, EmptyCString(), mUsername,
5986                                  proxyInfo, originAttributes, isHttps);
5987     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
5988   }
5989 
5990   // Set network interface id only when it's not empty to avoid
5991   // rebuilding hash key.
5992   if (!mNetworkInterfaceId.IsEmpty()) {
5993     mConnectionInfo->SetNetworkInterfaceId(mNetworkInterfaceId);
5994   }
5995 
5996   mAuthProvider = do_CreateInstance(
5997       "@mozilla.org/network/http-channel-auth-provider;1", &rv);
5998   if (NS_SUCCEEDED(rv)) rv = mAuthProvider->Init(this);
5999   if (NS_FAILED(rv)) {
6000     return rv;
6001   }
6002 
6003   // check to see if authorization headers should be included
6004   // mCustomAuthHeader is set in AsyncOpen if we find Authorization header
6005   rv = mAuthProvider->AddAuthorizationHeaders(mCustomAuthHeader);
6006   if (NS_FAILED(rv)) {
6007     LOG(("nsHttpChannel %p AddAuthorizationHeaders failed (%08x)", this,
6008          static_cast<uint32_t>(rv)));
6009   }
6010 
6011   // notify "http-on-modify-request" observers
6012   CallOnModifyRequestObservers();
6013 
6014   SetLoadGroupUserAgentOverride();
6015 
6016   // Check if request was cancelled during on-modify-request or on-useragent.
6017   if (mCanceled) {
6018     return mStatus;
6019   }
6020 
6021   if (mSuspendCount) {
6022     LOG(("Waiting until resume BeginConnect [this=%p]\n", this));
6023     MOZ_ASSERT(!mCallOnResume);
6024     mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue;
6025     return NS_OK;
6026   }
6027 
6028   return BeginConnectContinue();
6029 }
6030 
HandleBeginConnectContinue()6031 void nsHttpChannel::HandleBeginConnectContinue() {
6032   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
6033   nsresult rv;
6034 
6035   if (mSuspendCount) {
6036     LOG(("Waiting until resume BeginConnect [this=%p]\n", this));
6037     mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue;
6038     return;
6039   }
6040 
6041   LOG(("nsHttpChannel::HandleBeginConnectContinue [this=%p]\n", this));
6042   rv = BeginConnectContinue();
6043   if (NS_FAILED(rv)) {
6044     CloseCacheEntry(false);
6045     Unused << AsyncAbort(rv);
6046   }
6047 }
6048 
BeginConnectContinue()6049 nsresult nsHttpChannel::BeginConnectContinue() {
6050   nsresult rv;
6051 
6052   // Check if request was cancelled during suspend AFTER on-modify-request or
6053   // on-useragent.
6054   if (mCanceled) {
6055     return mStatus;
6056   }
6057 
6058   // Check to see if we should redirect this channel elsewhere by
6059   // nsIHttpChannel.redirectTo API request
6060   if (mAPIRedirectToURI) {
6061     return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
6062   }
6063 
6064   // If mTimingEnabled flag is not set after OnModifyRequest() then
6065   // clear the already recorded AsyncOpen value for consistency.
6066   if (!mTimingEnabled) mAsyncOpenTime = TimeStamp();
6067 
6068   // if this somehow fails we can go on without it
6069   Unused << gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
6070 
6071   if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
6072     mCaps |= NS_HTTP_REFRESH_DNS;
6073 
6074   // Adjust mCaps according to our request headers:
6075   //  - If "Connection: close" is set as a request header, then do not bother
6076   //    trying to establish a keep-alive connection.
6077   if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close"))
6078     mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE);
6079 
6080   if (gHttpHandler->CriticalRequestPrioritization()) {
6081     if (mClassOfService & nsIClassOfService::Leader) {
6082       mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
6083     }
6084     if (mClassOfService & nsIClassOfService::Unblocked) {
6085       mCaps |= NS_HTTP_LOAD_UNBLOCKED;
6086     }
6087     if (mClassOfService & nsIClassOfService::UrgentStart &&
6088         gHttpHandler->IsUrgentStartEnabled()) {
6089       mCaps |= NS_HTTP_URGENT_START;
6090       SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);
6091     }
6092   }
6093 
6094   // Force-Reload should reset the persistent connection pool for this host
6095   if (mLoadFlags & LOAD_FRESH_CONNECTION) {
6096     // just the initial document resets the whole pool
6097     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
6098       gHttpHandler->ConnMgr()->ClearAltServiceMappings();
6099       rv = gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(
6100           mConnectionInfo);
6101       if (NS_FAILED(rv)) {
6102         LOG(
6103             ("nsHttpChannel::BeginConnect "
6104              "DoShiftReloadConnectionCleanup failed: %08x [this=%p]",
6105              static_cast<uint32_t>(rv), this));
6106       }
6107     }
6108   }
6109 
6110   // We may have been cancelled already, either by on-modify-request
6111   // listeners or load group observers; in that case, we should not send the
6112   // request to the server
6113   if (mCanceled) {
6114     return mStatus;
6115   }
6116 
6117   if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
6118     return ContinueBeginConnectWithResult();
6119   }
6120 
6121   // We are about to do a sync lookup to check if the URI is a
6122   // tracker. If yes, this channel will be canceled by channel classifier.
6123   // Chances are the lookup is not needed so CheckIsTrackerWithLocalTable()
6124   // will return an error and then we can BeginConnectActual() right away.
6125   RefPtr<nsChannelClassifier> channelClassifier =
6126       GetOrCreateChannelClassifier();
6127   RefPtr<nsHttpChannel> self = this;
6128   bool willCallback = NS_SUCCEEDED(
6129       channelClassifier->CheckIsTrackerWithLocalTable([self]() -> void {
6130         nsresult rv = self->BeginConnectActual();
6131         if (NS_FAILED(rv)) {
6132           // Since this error is thrown asynchronously so that the caller
6133           // of BeginConnect() will not do clean up for us. We have to do
6134           // it on our own.
6135           self->CloseCacheEntry(false);
6136           Unused << self->AsyncAbort(rv);
6137         }
6138       }));
6139 
6140   if (!willCallback) {
6141     // We can do BeginConnectActual immediately if CheckIsTrackerWithLocalTable
6142     // is failed. Note that we don't need to handle the failure because
6143     // BeginConnect() will return synchronously and the caller will be
6144     // responsible for handling it.
6145     return BeginConnectActual();
6146   }
6147 
6148   return NS_OK;
6149 }
6150 
BeginConnectActual()6151 nsresult nsHttpChannel::BeginConnectActual() {
6152   if (mCanceled) {
6153     return mStatus;
6154   }
6155 
6156   if (!mConnectionInfo->UsingHttpProxy() &&
6157       !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
6158     // Start a DNS lookup very early in case the real open is queued the DNS can
6159     // happen in parallel. Do not do so in the presence of an HTTP proxy as
6160     // all lookups other than for the proxy itself are done by the proxy.
6161     // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
6162     // LOAD_ONLY_FROM_CACHE flags are set.
6163     //
6164     // We keep the DNS prefetch object around so that we can retrieve
6165     // timing information from it. There is no guarantee that we actually
6166     // use the DNS prefetch data for the real connection, but as we keep
6167     // this data around for 3 minutes by default, this should almost always
6168     // be correct, and even when it isn't, the timing still represents _a_
6169     // valid DNS lookup timing for the site, even if it is not _the_
6170     // timing we used.
6171     LOG(("nsHttpChannel::BeginConnect [this=%p] prefetching%s\n", this,
6172          mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
6173     OriginAttributes originAttributes;
6174     NS_GetOriginAttributes(this, originAttributes);
6175     mDNSPrefetch =
6176         new nsDNSPrefetch(mURI, originAttributes, this, mTimingEnabled);
6177     mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
6178   }
6179 
6180   nsresult rv = ContinueBeginConnectWithResult();
6181   if (NS_FAILED(rv)) {
6182     return rv;
6183   }
6184 
6185   // Start nsChannelClassifier to catch phishing and malware URIs.
6186   RefPtr<nsChannelClassifier> channelClassifier =
6187       GetOrCreateChannelClassifier();
6188   LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
6189        channelClassifier.get(), this));
6190   channelClassifier->Start();
6191 
6192   return NS_OK;
6193 }
6194 
6195 NS_IMETHODIMP
GetEncodedBodySize(uint64_t * aEncodedBodySize)6196 nsHttpChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize) {
6197   if (mCacheEntry && !mCacheEntryIsWriteOnly) {
6198     int64_t dataSize = 0;
6199     mCacheEntry->GetDataSize(&dataSize);
6200     *aEncodedBodySize = dataSize;
6201   } else {
6202     *aEncodedBodySize = mLogicalOffset;
6203   }
6204   return NS_OK;
6205 }
6206 
6207 //-----------------------------------------------------------------------------
6208 // nsHttpChannel::nsIHttpChannelInternal
6209 //-----------------------------------------------------------------------------
6210 
6211 NS_IMETHODIMP
SetupFallbackChannel(const char * aFallbackKey)6212 nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey) {
6213   ENSURE_CALLED_BEFORE_CONNECT();
6214 
6215   LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]\n", this,
6216        aFallbackKey));
6217   mFallbackChannel = true;
6218   mFallbackKey = aFallbackKey;
6219 
6220   return NS_OK;
6221 }
6222 
6223 NS_IMETHODIMP
SetChannelIsForDownload(bool aChannelIsForDownload)6224 nsHttpChannel::SetChannelIsForDownload(bool aChannelIsForDownload) {
6225   if (aChannelIsForDownload) {
6226     AddClassFlags(nsIClassOfService::Throttleable);
6227   } else {
6228     ClearClassFlags(nsIClassOfService::Throttleable);
6229   }
6230 
6231   return HttpBaseChannel::SetChannelIsForDownload(aChannelIsForDownload);
6232 }
6233 
ProcessId()6234 base::ProcessId nsHttpChannel::ProcessId() {
6235   nsCOMPtr<nsIParentChannel> parentChannel;
6236   NS_QueryNotificationCallbacks(this, parentChannel);
6237   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
6238   if (httpParent) {
6239     return httpParent->OtherPid();
6240   }
6241   return base::GetCurrentProcId();
6242 }
6243 
AttachStreamFilter(ipc::Endpoint<extensions::PStreamFilterParent> && aEndpoint)6244 bool nsHttpChannel::AttachStreamFilter(
6245     ipc::Endpoint<extensions::PStreamFilterParent> &&aEndpoint)
6246 
6247 {
6248   nsCOMPtr<nsIParentChannel> parentChannel;
6249   NS_QueryNotificationCallbacks(this, parentChannel);
6250   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
6251   if (httpParent) {
6252     return httpParent->SendAttachStreamFilter(Move(aEndpoint));
6253   }
6254 
6255   extensions::StreamFilterParent::Attach(this, Move(aEndpoint));
6256   return true;
6257 }
6258 
6259 //-----------------------------------------------------------------------------
6260 // nsHttpChannel::nsISupportsPriority
6261 //-----------------------------------------------------------------------------
6262 
6263 NS_IMETHODIMP
SetPriority(int32_t value)6264 nsHttpChannel::SetPriority(int32_t value) {
6265   int16_t newValue = clamped<int32_t>(value, INT16_MIN, INT16_MAX);
6266   if (mPriority == newValue) return NS_OK;
6267 
6268   LOG(("nsHttpChannel::SetPriority %p p=%d", this, newValue));
6269 
6270   mPriority = newValue;
6271   if (mTransaction) {
6272     nsresult rv = gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
6273     if (NS_FAILED(rv)) {
6274       LOG(
6275           ("nsHttpChannel::SetPriority [this=%p] "
6276            "RescheduleTransaction failed (%08x)",
6277            this, static_cast<uint32_t>(rv)));
6278     }
6279   }
6280 
6281   // If this channel is the real channel for an e10s channel, notify the
6282   // child side about the priority change as well.
6283   nsCOMPtr<nsIParentChannel> parentChannel;
6284   NS_QueryNotificationCallbacks(this, parentChannel);
6285   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
6286   if (httpParent) {
6287     httpParent->DoSendSetPriority(newValue);
6288   }
6289 
6290   return NS_OK;
6291 }
6292 
ContinueBeginConnectWithResult()6293 nsresult nsHttpChannel::ContinueBeginConnectWithResult() {
6294   LOG(("nsHttpChannel::ContinueBeginConnectWithResult [this=%p]", this));
6295   NS_PRECONDITION(!mCallOnResume, "How did that happen?");
6296 
6297   nsresult rv;
6298 
6299   if (mSuspendCount) {
6300     LOG(("Waiting until resume to do async connect [this=%p]\n", this));
6301     mCallOnResume = &nsHttpChannel::ContinueBeginConnect;
6302     rv = NS_OK;
6303   } else if (mCanceled) {
6304     // We may have been cancelled already, by nsChannelClassifier in that
6305     // case, we should not send the request to the server
6306     rv = mStatus;
6307   } else {
6308     rv = OnBeforeConnect();
6309   }
6310 
6311   LOG(
6312       ("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p "
6313        "rv=%" PRIx32 " mCanceled=%u]\n",
6314        this, static_cast<uint32_t>(rv), static_cast<bool>(mCanceled)));
6315   return rv;
6316 }
6317 
ContinueBeginConnect()6318 void nsHttpChannel::ContinueBeginConnect() {
6319   LOG(("nsHttpChannel::ContinueBeginConnect this=%p", this));
6320 
6321   nsresult rv = ContinueBeginConnectWithResult();
6322   if (NS_FAILED(rv)) {
6323     CloseCacheEntry(false);
6324     Unused << AsyncAbort(rv);
6325   }
6326 }
6327 
6328 //-----------------------------------------------------------------------------
6329 // HttpChannel::nsIClassOfService
6330 //-----------------------------------------------------------------------------
6331 
OnClassOfServiceUpdated()6332 void nsHttpChannel::OnClassOfServiceUpdated() {
6333   LOG(("nsHttpChannel::OnClassOfServiceUpdated this=%p, cos=%u", this,
6334        mClassOfService));
6335 
6336   if (mTransaction) {
6337     gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction,
6338                                                     mClassOfService);
6339   }
6340   if (EligibleForTailing()) {
6341     RemoveAsNonTailRequest();
6342   } else {
6343     AddAsNonTailRequest();
6344   }
6345 }
6346 
6347 NS_IMETHODIMP
SetClassFlags(uint32_t inFlags)6348 nsHttpChannel::SetClassFlags(uint32_t inFlags) {
6349   uint32_t previous = mClassOfService;
6350   mClassOfService = inFlags;
6351   if (previous != mClassOfService) {
6352     OnClassOfServiceUpdated();
6353   }
6354   return NS_OK;
6355 }
6356 
6357 NS_IMETHODIMP
AddClassFlags(uint32_t inFlags)6358 nsHttpChannel::AddClassFlags(uint32_t inFlags) {
6359   uint32_t previous = mClassOfService;
6360   mClassOfService |= inFlags;
6361   if (previous != mClassOfService) {
6362     OnClassOfServiceUpdated();
6363   }
6364   return NS_OK;
6365 }
6366 
6367 NS_IMETHODIMP
ClearClassFlags(uint32_t inFlags)6368 nsHttpChannel::ClearClassFlags(uint32_t inFlags) {
6369   uint32_t previous = mClassOfService;
6370   mClassOfService &= ~inFlags;
6371   if (previous != mClassOfService) {
6372     OnClassOfServiceUpdated();
6373   }
6374   return NS_OK;
6375 }
6376 
6377 //-----------------------------------------------------------------------------
6378 // nsHttpChannel::nsIProtocolProxyCallback
6379 //-----------------------------------------------------------------------------
6380 
6381 NS_IMETHODIMP
OnProxyAvailable(nsICancelable * request,nsIChannel * channel,nsIProxyInfo * pi,nsresult status)6382 nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
6383                                 nsIProxyInfo *pi, nsresult status) {
6384   LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
6385        " mStatus=%" PRIx32 "]\n",
6386        this, pi, static_cast<uint32_t>(status),
6387        static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
6388   mProxyRequest = nullptr;
6389 
6390   nsresult rv;
6391 
6392   // If status is a failure code, then it means that we failed to resolve
6393   // proxy info.  That is a non-fatal error assuming it wasn't because the
6394   // request was canceled.  We just failover to DIRECT when proxy resolution
6395   // fails (failure can mean that the PAC URL could not be loaded).
6396 
6397   if (NS_SUCCEEDED(status)) mProxyInfo = pi;
6398 
6399   if (!gHttpHandler->Active()) {
6400     LOG(
6401         ("nsHttpChannel::OnProxyAvailable [this=%p] "
6402          "Handler no longer active.\n",
6403          this));
6404     rv = NS_ERROR_NOT_AVAILABLE;
6405   } else {
6406     rv = BeginConnect();
6407   }
6408 
6409   if (NS_FAILED(rv)) {
6410     CloseCacheEntry(false);
6411     Unused << AsyncAbort(rv);
6412   }
6413   return rv;
6414 }
6415 
6416 //-----------------------------------------------------------------------------
6417 // nsHttpChannel::nsIProxiedChannel
6418 //-----------------------------------------------------------------------------
6419 
6420 NS_IMETHODIMP
GetProxyInfo(nsIProxyInfo ** result)6421 nsHttpChannel::GetProxyInfo(nsIProxyInfo **result) {
6422   if (!mConnectionInfo)
6423     *result = mProxyInfo;
6424   else
6425     *result = mConnectionInfo->ProxyInfo();
6426   NS_IF_ADDREF(*result);
6427   return NS_OK;
6428 }
6429 
6430 //-----------------------------------------------------------------------------
6431 // nsHttpChannel::nsITimedChannel
6432 //-----------------------------------------------------------------------------
6433 
6434 NS_IMETHODIMP
GetDomainLookupStart(TimeStamp * _retval)6435 nsHttpChannel::GetDomainLookupStart(TimeStamp *_retval) {
6436   if (mTransaction)
6437     *_retval = mTransaction->GetDomainLookupStart();
6438   else
6439     *_retval = mTransactionTimings.domainLookupStart;
6440   return NS_OK;
6441 }
6442 
6443 NS_IMETHODIMP
GetDomainLookupEnd(TimeStamp * _retval)6444 nsHttpChannel::GetDomainLookupEnd(TimeStamp *_retval) {
6445   if (mTransaction)
6446     *_retval = mTransaction->GetDomainLookupEnd();
6447   else
6448     *_retval = mTransactionTimings.domainLookupEnd;
6449   return NS_OK;
6450 }
6451 
6452 NS_IMETHODIMP
GetConnectStart(TimeStamp * _retval)6453 nsHttpChannel::GetConnectStart(TimeStamp *_retval) {
6454   if (mTransaction)
6455     *_retval = mTransaction->GetConnectStart();
6456   else
6457     *_retval = mTransactionTimings.connectStart;
6458   return NS_OK;
6459 }
6460 
6461 NS_IMETHODIMP
GetTcpConnectEnd(TimeStamp * _retval)6462 nsHttpChannel::GetTcpConnectEnd(TimeStamp *_retval) {
6463   if (mTransaction)
6464     *_retval = mTransaction->GetTcpConnectEnd();
6465   else
6466     *_retval = mTransactionTimings.tcpConnectEnd;
6467   return NS_OK;
6468 }
6469 
6470 NS_IMETHODIMP
GetSecureConnectionStart(TimeStamp * _retval)6471 nsHttpChannel::GetSecureConnectionStart(TimeStamp *_retval) {
6472   if (mTransaction)
6473     *_retval = mTransaction->GetSecureConnectionStart();
6474   else
6475     *_retval = mTransactionTimings.secureConnectionStart;
6476   return NS_OK;
6477 }
6478 
6479 NS_IMETHODIMP
GetConnectEnd(TimeStamp * _retval)6480 nsHttpChannel::GetConnectEnd(TimeStamp *_retval) {
6481   if (mTransaction)
6482     *_retval = mTransaction->GetConnectEnd();
6483   else
6484     *_retval = mTransactionTimings.connectEnd;
6485   return NS_OK;
6486 }
6487 
6488 NS_IMETHODIMP
GetRequestStart(TimeStamp * _retval)6489 nsHttpChannel::GetRequestStart(TimeStamp *_retval) {
6490   if (mTransaction)
6491     *_retval = mTransaction->GetRequestStart();
6492   else
6493     *_retval = mTransactionTimings.requestStart;
6494   return NS_OK;
6495 }
6496 
6497 NS_IMETHODIMP
GetResponseStart(TimeStamp * _retval)6498 nsHttpChannel::GetResponseStart(TimeStamp *_retval) {
6499   if (mTransaction)
6500     *_retval = mTransaction->GetResponseStart();
6501   else
6502     *_retval = mTransactionTimings.responseStart;
6503   return NS_OK;
6504 }
6505 
6506 NS_IMETHODIMP
GetResponseEnd(TimeStamp * _retval)6507 nsHttpChannel::GetResponseEnd(TimeStamp *_retval) {
6508   if (mTransaction)
6509     *_retval = mTransaction->GetResponseEnd();
6510   else
6511     *_retval = mTransactionTimings.responseEnd;
6512   return NS_OK;
6513 }
6514 
6515 //-----------------------------------------------------------------------------
6516 // nsHttpChannel::nsIHttpAuthenticableChannel
6517 //-----------------------------------------------------------------------------
6518 
6519 NS_IMETHODIMP
GetIsSSL(bool * aIsSSL)6520 nsHttpChannel::GetIsSSL(bool *aIsSSL) {
6521   // this attribute is really misnamed - it wants to know if
6522   // https:// is being used. SSL might be used to cover http://
6523   // in some circumstances (proxies, http/2, etc..)
6524   return mURI->SchemeIs("https", aIsSSL);
6525 }
6526 
6527 NS_IMETHODIMP
GetProxyMethodIsConnect(bool * aProxyMethodIsConnect)6528 nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect) {
6529   *aProxyMethodIsConnect = mConnectionInfo->UsingConnect();
6530   return NS_OK;
6531 }
6532 
6533 NS_IMETHODIMP
GetServerResponseHeader(nsACString & value)6534 nsHttpChannel::GetServerResponseHeader(nsACString &value) {
6535   if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
6536   return mResponseHead->GetHeader(nsHttp::Server, value);
6537 }
6538 
6539 NS_IMETHODIMP
GetProxyChallenges(nsACString & value)6540 nsHttpChannel::GetProxyChallenges(nsACString &value) {
6541   if (!mResponseHead) return NS_ERROR_UNEXPECTED;
6542   return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value);
6543 }
6544 
6545 NS_IMETHODIMP
GetWWWChallenges(nsACString & value)6546 nsHttpChannel::GetWWWChallenges(nsACString &value) {
6547   if (!mResponseHead) return NS_ERROR_UNEXPECTED;
6548   return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value);
6549 }
6550 
6551 NS_IMETHODIMP
SetProxyCredentials(const nsACString & value)6552 nsHttpChannel::SetProxyCredentials(const nsACString &value) {
6553   return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value);
6554 }
6555 
6556 NS_IMETHODIMP
SetWWWCredentials(const nsACString & value)6557 nsHttpChannel::SetWWWCredentials(const nsACString &value) {
6558   // This method is called when various browser initiated authorization
6559   // code sets the credentials.  We need to flag this header as the
6560   // "browser default" so it does not show up in the ServiceWorker
6561   // FetchEvent.  This may actually get called more than once, though,
6562   // so we clear the header first since "default" headers are not
6563   // allowed to overwrite normally.
6564   Unused << mRequestHead.ClearHeader(nsHttp::Authorization);
6565   return mRequestHead.SetHeader(nsHttp::Authorization, value, false,
6566                                 nsHttpHeaderArray::eVarietyRequestDefault);
6567 }
6568 
6569 //-----------------------------------------------------------------------------
6570 // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we
6571 // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.
6572 //
6573 
6574 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * aLoadFlags)6575 nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) {
6576   return HttpBaseChannel::GetLoadFlags(aLoadFlags);
6577 }
6578 
6579 NS_IMETHODIMP
GetURI(nsIURI ** aURI)6580 nsHttpChannel::GetURI(nsIURI **aURI) { return HttpBaseChannel::GetURI(aURI); }
6581 
6582 NS_IMETHODIMP
GetNotificationCallbacks(nsIInterfaceRequestor ** aCallbacks)6583 nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) {
6584   return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
6585 }
6586 
6587 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** aLoadGroup)6588 nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) {
6589   return HttpBaseChannel::GetLoadGroup(aLoadGroup);
6590 }
6591 
6592 NS_IMETHODIMP
GetRequestMethod(nsACString & aMethod)6593 nsHttpChannel::GetRequestMethod(nsACString &aMethod) {
6594   return HttpBaseChannel::GetRequestMethod(aMethod);
6595 }
6596 
6597 //-----------------------------------------------------------------------------
6598 // nsHttpChannel::nsIRequestObserver
6599 //-----------------------------------------------------------------------------
6600 
6601 NS_IMETHODIMP
OnStartRequest(nsIRequest * request,nsISupports * ctxt)6602 nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
6603   nsresult rv;
6604 
6605   AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
6606 
6607   if (!(mCanceled || NS_FAILED(mStatus)) &&
6608       !WRONG_RACING_RESPONSE_SOURCE(request)) {
6609     // capture the request's status, so our consumers will know ASAP of any
6610     // connection failures, etc - bug 93581
6611     nsresult status;
6612     request->GetStatus(&status);
6613     mStatus = status;
6614   }
6615 
6616   LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32
6617        "]\n",
6618        this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
6619 
6620   if (mRaceCacheWithNetwork) {
6621     LOG(
6622         ("  racingNetAndCache - mFirstResponseSource:%d fromCache:%d "
6623          "fromNet:%d\n",
6624          static_cast<int32_t>(mFirstResponseSource), request == mCachePump,
6625          request == mTransactionPump));
6626     if (mFirstResponseSource == RESPONSE_PENDING) {
6627       // When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE
6628       // earlier in ReadFromCache, so this must be a response from the network.
6629       MOZ_ASSERT(request == mTransactionPump);
6630       LOG(("  First response from network\n"));
6631       {
6632         // Race condition with OnCacheEntryCheck, which is not limited
6633         // to main thread.
6634         mozilla::MutexAutoLock lock(mRCWNLock);
6635         mFirstResponseSource = RESPONSE_FROM_NETWORK;
6636         mOnStartRequestTimestamp = TimeStamp::Now();
6637 
6638         // Conditional or byte range header could be added in
6639         // OnCacheEntryCheck. We need to remove them because the
6640         // request might be sent again due to auth retry and we must
6641         // not send these headers without having the entry.
6642         if (mDidReval) {
6643           LOG(("  Removing conditional request headers"));
6644           UntieValidationRequest();
6645           mDidReval = false;
6646         }
6647         if (mCachedContentIsPartial) {
6648           LOG(("  Removing byte range request headers"));
6649           UntieByteRangeRequest();
6650           mCachedContentIsPartial = false;
6651         }
6652       }
6653       mAvailableCachedAltDataType.Truncate();
6654     } else if (WRONG_RACING_RESPONSE_SOURCE(request)) {
6655       LOG(("  Early return when racing. This response not needed."));
6656       return NS_OK;
6657     }
6658   }
6659 
6660   // Make sure things are what we expect them to be...
6661   MOZ_ASSERT(request == mCachePump || request == mTransactionPump,
6662              "Unexpected request");
6663 
6664   MOZ_ASSERT(mRaceCacheWithNetwork || !(mTransactionPump && mCachePump) ||
6665                  mCachedContentIsPartial,
6666              "If we have both pumps, the cache content must be partial");
6667 
6668   mAfterOnStartRequestBegun = true;
6669   if (mOnStartRequestTimestamp.IsNull()) {
6670     mOnStartRequestTimestamp = TimeStamp::Now();
6671   }
6672 
6673   Telemetry::Accumulate(Telemetry::HTTP_ONSTART_SUSPEND_TOTAL_TIME,
6674                         mSuspendTotalTime);
6675 
6676   if (!mSecurityInfo && !mCachePump && mTransaction) {
6677     // grab the security info from the connection object; the transaction
6678     // is guaranteed to own a reference to the connection.
6679     mSecurityInfo = mTransaction->SecurityInfo();
6680   }
6681 
6682   // don't enter this block if we're reading from the cache...
6683   if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
6684     // mTransactionPump doesn't hit OnInputStreamReady and call this until
6685     // all of the response headers have been acquired, so we can take ownership
6686     // of them from the transaction.
6687     mResponseHead = mTransaction->TakeResponseHead();
6688     // the response head may be null if the transaction was cancelled.  in
6689     // which case we just need to call OnStartRequest/OnStopRequest.
6690     if (mResponseHead) return ProcessResponse();
6691 
6692     NS_WARNING("No response head in OnStartRequest");
6693   }
6694 
6695   // cache file could be deleted on our behalf, it could contain errors or
6696   // it failed to allocate memory, reload from network here.
6697   if (mCacheEntry && mCachePump && RECOVER_FROM_CACHE_FILE_ERROR(mStatus)) {
6698     LOG(("  cache file error, reloading from server"));
6699     mCacheEntry->AsyncDoom(nullptr);
6700     rv =
6701         StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
6702     if (NS_SUCCEEDED(rv)) return NS_OK;
6703   }
6704 
6705   // avoid crashing if mListener happens to be null...
6706   if (!mListener) {
6707     NS_NOTREACHED("mListener is null");
6708     return NS_OK;
6709   }
6710 
6711   // before we start any content load, check for redirectTo being called
6712   // this code is executed mainly before we start load from the cache
6713   if (mAPIRedirectToURI && !mCanceled) {
6714     nsAutoCString redirectToSpec;
6715     mAPIRedirectToURI->GetAsciiSpec(redirectToSpec);
6716     LOG(("  redirectTo called with uri=%s", redirectToSpec.BeginReading()));
6717 
6718     MOZ_ASSERT(!mOnStartRequestCalled);
6719 
6720     nsCOMPtr<nsIURI> redirectTo;
6721     mAPIRedirectToURI.swap(redirectTo);
6722 
6723     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
6724     rv = StartRedirectChannelToURI(redirectTo,
6725                                    nsIChannelEventSink::REDIRECT_TEMPORARY);
6726     if (NS_SUCCEEDED(rv)) {
6727       return NS_OK;
6728     }
6729     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
6730   }
6731 
6732   // Hack: ContinueOnStartRequest1 uses NS_OK to detect successful redirects,
6733   // so we distinguish this codepath (a non-redirect that's processing
6734   // normally) by passing in a bogus error code.
6735   return ContinueOnStartRequest1(NS_BINDING_FAILED);
6736 }
6737 
ContinueOnStartRequest1(nsresult result)6738 nsresult nsHttpChannel::ContinueOnStartRequest1(nsresult result) {
6739   if (NS_SUCCEEDED(result)) {
6740     // Redirect has passed through, we don't want to go on with this
6741     // channel.  It will now be canceled by the redirect handling code
6742     // that called this function.
6743     return NS_OK;
6744   }
6745 
6746   // on proxy errors, try to failover
6747   if (mConnectionInfo->ProxyInfo() &&
6748       (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6749        mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6750        mStatus == NS_ERROR_NET_TIMEOUT)) {
6751     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
6752     if (NS_SUCCEEDED(ProxyFailover())) return NS_OK;
6753     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
6754   }
6755 
6756   // Hack: ContinueOnStartRequest2 uses NS_OK to detect successful redirects,
6757   // so we distinguish this codepath (a non-redirect that's processing
6758   // normally) by passing in a bogus error code.
6759   return ContinueOnStartRequest2(NS_BINDING_FAILED);
6760 }
6761 
ContinueOnStartRequest2(nsresult result)6762 nsresult nsHttpChannel::ContinueOnStartRequest2(nsresult result) {
6763   if (NS_SUCCEEDED(result)) {
6764     // Redirect has passed through, we don't want to go on with this
6765     // channel.  It will now be canceled by the redirect handling code
6766     // that called this function.
6767     return NS_OK;
6768   }
6769 
6770   // on other request errors, try to fall back
6771   if (NS_FAILED(mStatus)) {
6772     PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
6773     bool waitingForRedirectCallback;
6774     Unused << ProcessFallback(&waitingForRedirectCallback);
6775     if (waitingForRedirectCallback) return NS_OK;
6776     PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
6777   }
6778 
6779   return ContinueOnStartRequest3(NS_OK);
6780 }
6781 
ContinueOnStartRequest3(nsresult result)6782 nsresult nsHttpChannel::ContinueOnStartRequest3(nsresult result) {
6783   LOG(("nsHttpChannel::ContinueOnStartRequest3 [this=%p]", this));
6784 
6785   if (mFallingBack) return NS_OK;
6786 
6787   return CallOnStartRequest();
6788 }
6789 
6790 NS_IMETHODIMP
OnStopRequest(nsIRequest * request,nsISupports * ctxt,nsresult status)6791 nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
6792                              nsresult status) {
6793   AUTO_PROFILER_LABEL("nsHttpChannel::OnStopRequest", NETWORK);
6794 
6795   LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%" PRIx32 "]\n",
6796        this, request, static_cast<uint32_t>(status)));
6797 
6798   LOG(("OnStopRequest %p requestFromCache: %d mFirstResponseSource: %d\n", this,
6799        request == mCachePump, static_cast<int32_t>(mFirstResponseSource)));
6800 
6801   MOZ_ASSERT(NS_IsMainThread(),
6802              "OnStopRequest should only be called from the main thread");
6803 
6804   if (WRONG_RACING_RESPONSE_SOURCE(request)) {
6805     return NS_OK;
6806   }
6807 
6808   if (NS_FAILED(status)) {
6809     ProcessSecurityReport(status);
6810   }
6811 
6812   // If this load failed because of a security error, it may be because we
6813   // are in a captive portal - trigger an async check to make sure.
6814   int32_t nsprError = -1 * NS_ERROR_GET_CODE(status);
6815   if (mozilla::psm::IsNSSErrorCode(nsprError)) {
6816     gIOService->RecheckCaptivePortal();
6817   }
6818 
6819   if (mTimingEnabled && request == mCachePump) {
6820     mCacheReadEnd = TimeStamp::Now();
6821 
6822     ReportNetVSCacheTelemetry();
6823   }
6824 
6825   // allow content to be cached if it was loaded successfully (bug #482935)
6826   bool contentComplete = NS_SUCCEEDED(status);
6827 
6828   // honor the cancelation status even if the underlying transaction completed.
6829   if (mCanceled || NS_FAILED(mStatus)) status = mStatus;
6830 
6831   if (mCachedContentIsPartial) {
6832     if (NS_SUCCEEDED(status)) {
6833       // mTransactionPump should be suspended
6834       MOZ_ASSERT(request != mTransactionPump,
6835                  "byte-range transaction finished prematurely");
6836 
6837       if (request == mCachePump) {
6838         bool streamDone;
6839         status = OnDoneReadingPartialCacheEntry(&streamDone);
6840         if (NS_SUCCEEDED(status) && !streamDone) return status;
6841         // otherwise, fall through and fire OnStopRequest...
6842       } else if (request == mTransactionPump) {
6843         MOZ_ASSERT(mConcurrentCacheAccess);
6844       } else
6845         NS_NOTREACHED("unexpected request");
6846     }
6847     // Do not to leave the transaction in a suspended state in error cases.
6848     if (NS_FAILED(status) && mTransaction) {
6849       nsresult rv = gHttpHandler->CancelTransaction(mTransaction, status);
6850       if (NS_FAILED(rv)) {
6851         LOG(("  CancelTransaction failed (%08x)", static_cast<uint32_t>(rv)));
6852       }
6853     }
6854   }
6855 
6856   nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
6857   if (conv) {
6858     conv->GetDecodedDataLength(&mDecodedBodySize);
6859   }
6860 
6861   bool isFromNet = request == mTransactionPump;
6862 
6863   if (mTransaction) {
6864     // determine if we should call DoAuthRetry
6865     bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
6866     mStronglyFramed = mTransaction->ResponseIsComplete();
6867     LOG(("nsHttpChannel %p has a strongly framed transaction: %d", this,
6868          mStronglyFramed));
6869 
6870     //
6871     // grab references to connection in case we need to retry an
6872     // authentication request over it or use it for an upgrade
6873     // to another protocol.
6874     //
6875     // this code relies on the code in nsHttpTransaction::Close, which
6876     // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
6877     // keep the connection around after the transaction is finished.
6878     //
6879     RefPtr<nsAHttpConnection> conn;
6880     LOG(("  mAuthRetryPending=%d, status=%" PRIx32 ", sticky conn cap=%d",
6881          mAuthRetryPending, static_cast<uint32_t>(status),
6882          mCaps & NS_HTTP_STICKY_CONNECTION));
6883     // We must check caps for stickinness also on the transaction because it
6884     // might have been updated by the transaction itself during inspection of
6885     // the reposnse headers yet on the socket thread (found connection based
6886     // auth schema).
6887     if ((mAuthRetryPending || NS_FAILED(status)) &&
6888         (mCaps & NS_HTTP_STICKY_CONNECTION ||
6889          mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
6890       conn = mTransaction->GetConnectionReference();
6891       LOG(("  transaction %p provides connection %p", mTransaction.get(),
6892            conn.get()));
6893 
6894       if (conn) {
6895         if (NS_FAILED(status)) {
6896           // Close (don't reuse) the sticky connection if it's in the middle
6897           // of an NTLM negotiation and this channel has been cancelled.
6898           // There are proxy servers known to get confused when we send
6899           // a new request over such a half-stated connection.
6900           if (!mAuthConnectionRestartable) {
6901             LOG(("  not reusing a half-authenticated sticky connection"));
6902             conn->DontReuse();
6903           }
6904           conn = nullptr;
6905         } else if (!conn->IsPersistent()) {
6906           // This is so far a workaround to fix leak when reusing unpersistent
6907           // connection for authentication retry. See bug 459620 comment 4
6908           // for details.
6909           LOG(("  connection is not persistent, not reusing it"));
6910           conn = nullptr;
6911         }
6912       }
6913     }
6914 
6915     RefPtr<nsAHttpConnection> stickyConn;
6916     if (mCaps & NS_HTTP_STICKY_CONNECTION) {
6917       stickyConn = mTransaction->GetConnectionReference();
6918     }
6919 
6920     mTransferSize = mTransaction->GetTransferSize();
6921 
6922     // If we are using the transaction to serve content, we also save the
6923     // time since async open in the cache entry so we can compare telemetry
6924     // between cache and net response.
6925     // Do not store the time of conditional requests because even if we
6926     // fetch the data from the server, the time includes loading of the old
6927     // cache entry which would skew the network load time.
6928     if (request == mTransactionPump && mCacheEntry && !mDidReval &&
6929         !mCustomConditionalRequest && !mAsyncOpenTime.IsNull() &&
6930         !mOnStartRequestTimestamp.IsNull()) {
6931       uint64_t onStartTime =
6932           (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
6933       uint64_t onStopTime =
6934           (TimeStamp::Now() - mAsyncOpenTime).ToMilliseconds();
6935       Unused << mCacheEntry->SetNetworkTimes(onStartTime, onStopTime);
6936     }
6937 
6938     mResponseTrailers = mTransaction->TakeResponseTrailers();
6939 
6940     // at this point, we're done with the transaction
6941     mTransactionTimings = mTransaction->Timings();
6942     mTransaction = nullptr;
6943     mTransactionPump = nullptr;
6944 
6945     // We no longer need the dns prefetch object
6946     if (mDNSPrefetch && mDNSPrefetch->TimingsValid() &&
6947         !mTransactionTimings.requestStart.IsNull() &&
6948         !mTransactionTimings.connectStart.IsNull() &&
6949         mDNSPrefetch->EndTimestamp() <= mTransactionTimings.connectStart) {
6950       // We only need the domainLookup timestamps when not using a
6951       // persistent connection, meaning if the endTimestamp < connectStart
6952       mTransactionTimings.domainLookupStart = mDNSPrefetch->StartTimestamp();
6953       mTransactionTimings.domainLookupEnd = mDNSPrefetch->EndTimestamp();
6954     }
6955     mDNSPrefetch = nullptr;
6956 
6957     // handle auth retry...
6958     if (authRetry) {
6959       mAuthRetryPending = false;
6960       status = DoAuthRetry(conn);
6961       if (NS_SUCCEEDED(status)) return NS_OK;
6962     }
6963 
6964     // If DoAuthRetry failed, or if we have been cancelled since showing
6965     // the auth. dialog, then we need to send OnStartRequest now
6966     if (authRetry || (mAuthRetryPending && NS_FAILED(status))) {
6967       MOZ_ASSERT(NS_FAILED(status), "should have a failure code here");
6968       // NOTE: since we have a failure status, we can ignore the return
6969       // value from onStartRequest.
6970       LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
6971            mListener.get()));
6972       if (mListener) {
6973         MOZ_ASSERT(!mOnStartRequestCalled,
6974                    "We should not call OnStartRequest twice.");
6975         nsCOMPtr<nsIStreamListener> listener(mListener);
6976         listener->OnStartRequest(this, mListenerContext);
6977         mOnStartRequestCalled = true;
6978       } else {
6979         NS_WARNING("OnStartRequest skipped because of null listener");
6980       }
6981     }
6982 
6983     // if this transaction has been replaced, then bail.
6984     if (mTransactionReplaced) {
6985       LOG(("Transaction replaced\n"));
6986       // This was just the network check for a 304 response.
6987       mFirstResponseSource = RESPONSE_PENDING;
6988       return NS_OK;
6989     }
6990 
6991     if (mUpgradeProtocolCallback && stickyConn && mResponseHead &&
6992         mResponseHead->Status() == 101) {
6993       nsresult rv = gHttpHandler->ConnMgr()->CompleteUpgrade(
6994           stickyConn, mUpgradeProtocolCallback);
6995       if (NS_FAILED(rv)) {
6996         LOG(("  CompleteUpgrade failed with %08x", static_cast<uint32_t>(rv)));
6997       }
6998     }
6999   }
7000 
7001   // HTTP_CHANNEL_DISPOSITION TELEMETRY
7002   enum ChannelDisposition {
7003     kHttpCanceled = 0,
7004     kHttpDisk = 1,
7005     kHttpNetOK = 2,
7006     kHttpNetEarlyFail = 3,
7007     kHttpNetLateFail = 4,
7008     kHttpsCanceled = 8,
7009     kHttpsDisk = 9,
7010     kHttpsNetOK = 10,
7011     kHttpsNetEarlyFail = 11,
7012     kHttpsNetLateFail = 12
7013   } chanDisposition = kHttpCanceled;
7014 
7015   // HTTP 0.9 is more likely to be an error than really 0.9, so count it that
7016   // way
7017   if (mCanceled) {
7018     chanDisposition = kHttpCanceled;
7019   } else if (!mUsedNetwork || (mRaceCacheWithNetwork &&
7020                                mFirstResponseSource == RESPONSE_FROM_CACHE)) {
7021     chanDisposition = kHttpDisk;
7022   } else if (NS_SUCCEEDED(status) && mResponseHead &&
7023              mResponseHead->Version() != NS_HTTP_VERSION_0_9) {
7024     chanDisposition = kHttpNetOK;
7025   } else if (!mTransferSize) {
7026     chanDisposition = kHttpNetEarlyFail;
7027   } else {
7028     chanDisposition = kHttpNetLateFail;
7029   }
7030   if (IsHTTPS()) {
7031     // shift http to https disposition enums
7032     chanDisposition =
7033         static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled);
7034   }
7035   LOG(("  nsHttpChannel::OnStopRequest ChannelDisposition %d\n",
7036        chanDisposition));
7037   Telemetry::Accumulate(Telemetry::HTTP_CHANNEL_DISPOSITION, chanDisposition);
7038 
7039   // if needed, check cache entry has all data we expect
7040   if (mCacheEntry && mCachePump && mConcurrentCacheAccess && contentComplete) {
7041     int64_t size, contentLength;
7042     nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength);
7043     if (NS_SUCCEEDED(rv)) {
7044       if (size == int64_t(-1)) {
7045         // mayhemer TODO - we have to restart read from cache here at the size
7046         // offset
7047         MOZ_ASSERT(false);
7048         LOG(
7049             ("  cache entry write is still in progress, but we just "
7050              "finished reading the cache entry"));
7051       } else if (contentLength != int64_t(-1) && contentLength != size) {
7052         LOG(("  concurrent cache entry write has been interrupted"));
7053         mCachedResponseHead = Move(mResponseHead);
7054         // Ignore zero partial length because we also want to resume when
7055         // no data at all has been read from the cache.
7056         rv = MaybeSetupByteRangeRequest(size, contentLength, true);
7057         if (NS_SUCCEEDED(rv) && mIsPartialRequest) {
7058           // Prevent read from cache again
7059           mCachedContentIsValid = 0;
7060           mCachedContentIsPartial = 1;
7061 
7062           // Perform the range request
7063           rv = ContinueConnect();
7064           if (NS_SUCCEEDED(rv)) {
7065             LOG(("  performing range request"));
7066             mCachePump = nullptr;
7067             return NS_OK;
7068           } else {
7069             LOG(("  but range request perform failed 0x%08" PRIx32,
7070                  static_cast<uint32_t>(rv)));
7071             status = NS_ERROR_NET_INTERRUPT;
7072           }
7073         } else {
7074           LOG(("  but range request setup failed rv=0x%08" PRIx32
7075                ", failing load",
7076                static_cast<uint32_t>(rv)));
7077         }
7078       }
7079     }
7080   }
7081 
7082   mIsPending = false;
7083   mStatus = status;
7084 
7085   // perform any final cache operations before we close the cache entry.
7086   if (mCacheEntry && mRequestTimeInitialized) {
7087     bool writeAccess;
7088     // New implementation just returns value of the !mCacheEntryIsReadOnly flag
7089     // passed in. Old implementation checks on nsICache::ACCESS_WRITE flag.
7090     mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly, &writeAccess);
7091     if (writeAccess) {
7092       nsresult rv = FinalizeCacheEntry();
7093       if (NS_FAILED(rv)) {
7094         LOG(("FinalizeCacheEntry failed (%08x)", static_cast<uint32_t>(rv)));
7095       }
7096     }
7097   }
7098 
7099   ReportRcwnStats(isFromNet);
7100 
7101   // Register entry to the PerformanceStorage resource timing
7102   mozilla::dom::PerformanceStorage *performanceStorage =
7103       GetPerformanceStorage();
7104   if (performanceStorage) {
7105     performanceStorage->AddEntry(this, this);
7106   }
7107 
7108   if (mListener) {
7109     LOG(("nsHttpChannel %p calling OnStopRequest\n", this));
7110     MOZ_ASSERT(mOnStartRequestCalled,
7111                "OnStartRequest should be called before OnStopRequest");
7112     MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
7113     mListener->OnStopRequest(this, mListenerContext, status);
7114     mOnStopRequestCalled = true;
7115   }
7116 
7117   // notify "http-on-stop-connect" observers
7118   gHttpHandler->OnStopRequest(this);
7119 
7120   RemoveAsNonTailRequest();
7121 
7122   // If a preferred alt-data type was set, this signals the consumer is
7123   // interested in reading and/or writing the alt-data representation.
7124   // We need to hold a reference to the cache entry in case the listener calls
7125   // openAlternativeOutputStream() after CloseCacheEntry() clears mCacheEntry.
7126   if (!mPreferredCachedAltDataType.IsEmpty()) {
7127     mAltDataCacheEntry = mCacheEntry;
7128   }
7129 
7130   CloseCacheEntry(!contentComplete);
7131 
7132   if (mOfflineCacheEntry) CloseOfflineCacheEntry();
7133 
7134   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, status);
7135 
7136   // We don't need this info anymore
7137   CleanRedirectCacheChainIfNecessary();
7138 
7139   ReleaseListeners();
7140 
7141   return NS_OK;
7142 }
7143 
7144 //-----------------------------------------------------------------------------
7145 // nsHttpChannel::nsIStreamListener
7146 //-----------------------------------------------------------------------------
7147 
7148 class OnTransportStatusAsyncEvent : public Runnable {
7149  public:
OnTransportStatusAsyncEvent(nsITransportEventSink * aEventSink,nsresult aTransportStatus,int64_t aProgress,int64_t aProgressMax)7150   OnTransportStatusAsyncEvent(nsITransportEventSink *aEventSink,
7151                               nsresult aTransportStatus, int64_t aProgress,
7152                               int64_t aProgressMax)
7153       : Runnable("net::OnTransportStatusAsyncEvent"),
7154         mEventSink(aEventSink),
7155         mTransportStatus(aTransportStatus),
7156         mProgress(aProgress),
7157         mProgressMax(aProgressMax) {
7158     MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
7159   }
7160 
Run()7161   NS_IMETHOD Run() override {
7162     MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
7163     if (mEventSink) {
7164       mEventSink->OnTransportStatus(nullptr, mTransportStatus, mProgress,
7165                                     mProgressMax);
7166     }
7167     return NS_OK;
7168   }
7169 
7170  private:
7171   nsCOMPtr<nsITransportEventSink> mEventSink;
7172   nsresult mTransportStatus;
7173   int64_t mProgress;
7174   int64_t mProgressMax;
7175 };
7176 
7177 NS_IMETHODIMP
OnDataAvailable(nsIRequest * request,nsISupports * ctxt,nsIInputStream * input,uint64_t offset,uint32_t count)7178 nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
7179                                nsIInputStream *input, uint64_t offset,
7180                                uint32_t count) {
7181   nsresult rv;
7182   AUTO_PROFILER_LABEL("nsHttpChannel::OnDataAvailable", NETWORK);
7183 
7184   LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64
7185        " count=%" PRIu32 "]\n",
7186        this, request, offset, count));
7187 
7188   LOG(("  requestFromCache: %d mFirstResponseSource: %d\n",
7189        request == mCachePump, static_cast<int32_t>(mFirstResponseSource)));
7190 
7191   // don't send out OnDataAvailable notifications if we've been canceled.
7192   if (mCanceled) return mStatus;
7193 
7194   if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) ||
7195       (request == mTransactionPump && mTransactionReplaced)) {
7196     uint32_t n;
7197     return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
7198   }
7199 
7200   MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
7201 
7202   MOZ_ASSERT(!(mCachedContentIsPartial && (request == mTransactionPump)),
7203              "transaction pump not suspended");
7204 
7205   mIsReadingFromCache = (request == mCachePump);
7206 
7207   if (mListener) {
7208     //
7209     // synthesize transport progress event.  we do this here since we want
7210     // to delay OnProgress events until we start streaming data.  this is
7211     // crucially important since it impacts the lock icon (see bug 240053).
7212     //
7213     nsresult transportStatus;
7214     if (request == mCachePump)
7215       transportStatus = NS_NET_STATUS_READING;
7216     else
7217       transportStatus = NS_NET_STATUS_RECEIVING_FROM;
7218 
7219     // mResponseHead may reference new or cached headers, but either way it
7220     // holds our best estimate of the total content length.  Even in the case
7221     // of a byte range request, the content length stored in the cached
7222     // response headers is what we want to use here.
7223 
7224     int64_t progressMax = -1;
7225     rv = GetContentLength(&progressMax);
7226     if (NS_FAILED(rv)) {
7227       NS_WARNING("GetContentLength failed");
7228     }
7229     int64_t progress = mLogicalOffset + count;
7230 
7231     if ((progress > progressMax) && (progressMax != -1)) {
7232       NS_WARNING(
7233           "unexpected progress values - "
7234           "is server exceeding content length?");
7235     }
7236 
7237     // make sure params are in range for js
7238     if (!InScriptableRange(progressMax)) {
7239       progressMax = -1;
7240     }
7241 
7242     if (!InScriptableRange(progress)) {
7243       progress = -1;
7244     }
7245 
7246     if (NS_IsMainThread()) {
7247       OnTransportStatus(nullptr, transportStatus, progress, progressMax);
7248     } else {
7249       rv = NS_DispatchToMainThread(new OnTransportStatusAsyncEvent(
7250           this, transportStatus, progress, progressMax));
7251       NS_ENSURE_SUCCESS(rv, rv);
7252     }
7253 
7254     //
7255     // we have to manually keep the logical offset of the stream up-to-date.
7256     // we cannot depend solely on the offset provided, since we may have
7257     // already streamed some data from another source (see, for example,
7258     // OnDoneReadingPartialCacheEntry).
7259     //
7260     int64_t offsetBefore = 0;
7261     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
7262     if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
7263       seekable = nullptr;
7264     }
7265 
7266     nsresult rv = mListener->OnDataAvailable(this, mListenerContext, input,
7267                                              mLogicalOffset, count);
7268     if (NS_SUCCEEDED(rv)) {
7269       // by contract mListener must read all of "count" bytes, but
7270       // nsInputStreamPump is tolerant to seekable streams that violate that
7271       // and it will redeliver incompletely read data. So we need to do
7272       // the same thing when updating the progress counter to stay in sync.
7273       int64_t offsetAfter, delta;
7274       if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) {
7275         delta = offsetAfter - offsetBefore;
7276         if (delta != count) {
7277           count = delta;
7278 
7279           NS_WARNING("Listener OnDataAvailable contract violation");
7280           nsCOMPtr<nsIConsoleService> consoleService =
7281               do_GetService(NS_CONSOLESERVICE_CONTRACTID);
7282           nsAutoString message(NS_LITERAL_STRING(
7283               "http channel Listener OnDataAvailable contract violation"));
7284           if (consoleService) {
7285             consoleService->LogStringMessage(message.get());
7286           }
7287         }
7288       }
7289       mLogicalOffset += count;
7290     }
7291 
7292     return rv;
7293   }
7294 
7295   return NS_ERROR_ABORT;
7296 }
7297 
7298 //-----------------------------------------------------------------------------
7299 // nsHttpChannel::nsIThreadRetargetableRequest
7300 //-----------------------------------------------------------------------------
7301 
7302 NS_IMETHODIMP
RetargetDeliveryTo(nsIEventTarget * aNewTarget)7303 nsHttpChannel::RetargetDeliveryTo(nsIEventTarget *aNewTarget) {
7304   MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
7305 
7306   NS_ENSURE_ARG(aNewTarget);
7307   if (aNewTarget->IsOnCurrentThread()) {
7308     NS_WARNING("Retargeting delivery to same thread");
7309     return NS_OK;
7310   }
7311   if (!mTransactionPump && !mCachePump) {
7312     LOG(("nsHttpChannel::RetargetDeliveryTo %p %p no pump available\n", this,
7313          aNewTarget));
7314     return NS_ERROR_NOT_AVAILABLE;
7315   }
7316 
7317   nsresult rv = NS_OK;
7318   // If both cache pump and transaction pump exist, we're probably dealing
7319   // with partially cached content. So, we must be able to retarget both.
7320   nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump;
7321   nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
7322   if (mCachePump) {
7323     retargetableCachePump = do_QueryObject(mCachePump);
7324     // nsInputStreamPump should implement this interface.
7325     MOZ_ASSERT(retargetableCachePump);
7326     rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget);
7327   }
7328   if (NS_SUCCEEDED(rv) && mTransactionPump) {
7329     retargetableTransactionPump = do_QueryObject(mTransactionPump);
7330     // nsInputStreamPump should implement this interface.
7331     MOZ_ASSERT(retargetableTransactionPump);
7332     rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);
7333 
7334     // If retarget fails for transaction pump, we must restore mCachePump.
7335     if (NS_FAILED(rv) && retargetableCachePump) {
7336       nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
7337       NS_ENSURE_TRUE(main, NS_ERROR_UNEXPECTED);
7338       rv = retargetableCachePump->RetargetDeliveryTo(main);
7339     }
7340   }
7341   return rv;
7342 }
7343 
7344 NS_IMETHODIMP
GetDeliveryTarget(nsIEventTarget ** aEventTarget)7345 nsHttpChannel::GetDeliveryTarget(nsIEventTarget **aEventTarget) {
7346   if (mCachePump) {
7347     return mCachePump->GetDeliveryTarget(aEventTarget);
7348   }
7349   if (mTransactionPump) {
7350     return mTransactionPump->GetDeliveryTarget(aEventTarget);
7351   }
7352   return NS_ERROR_NOT_AVAILABLE;
7353 }
7354 
7355 //-----------------------------------------------------------------------------
7356 // nsHttpChannel::nsThreadRetargetableStreamListener
7357 //-----------------------------------------------------------------------------
7358 
7359 NS_IMETHODIMP
CheckListenerChain()7360 nsHttpChannel::CheckListenerChain() {
7361   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
7362   nsresult rv = NS_OK;
7363   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
7364       do_QueryInterface(mListener, &rv);
7365   if (retargetableListener) {
7366     rv = retargetableListener->CheckListenerChain();
7367   }
7368   return rv;
7369 }
7370 
7371 //-----------------------------------------------------------------------------
7372 // nsHttpChannel::nsITransportEventSink
7373 //-----------------------------------------------------------------------------
7374 
7375 NS_IMETHODIMP
OnTransportStatus(nsITransport * trans,nsresult status,int64_t progress,int64_t progressMax)7376 nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
7377                                  int64_t progress, int64_t progressMax) {
7378   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only");
7379   // cache the progress sink so we don't have to query for it each time.
7380   if (!mProgressSink) GetCallback(mProgressSink);
7381 
7382   if (status == NS_NET_STATUS_CONNECTED_TO ||
7383       status == NS_NET_STATUS_WAITING_FOR) {
7384     if (mTransaction) {
7385       mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr);
7386     } else {
7387       nsCOMPtr<nsISocketTransport> socketTransport = do_QueryInterface(trans);
7388       if (socketTransport) {
7389         socketTransport->GetSelfAddr(&mSelfAddr);
7390         socketTransport->GetPeerAddr(&mPeerAddr);
7391       }
7392     }
7393   }
7394 
7395   // block socket status event after Cancel or OnStopRequest has been called.
7396   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
7397     LOG(("sending progress%s notification [this=%p status=%" PRIx32
7398          " progress=%" PRId64 "/%" PRId64 "]\n",
7399          (mLoadFlags & LOAD_BACKGROUND) ? "" : " and status", this,
7400          static_cast<uint32_t>(status), progress, progressMax));
7401 
7402     if (!(mLoadFlags & LOAD_BACKGROUND)) {
7403       nsAutoCString host;
7404       mURI->GetHost(host);
7405       mProgressSink->OnStatus(this, nullptr, status,
7406                               NS_ConvertUTF8toUTF16(host).get());
7407     }
7408 
7409     if (progress > 0) {
7410       if ((progress > progressMax) && (progressMax != -1)) {
7411         NS_WARNING("unexpected progress values");
7412       }
7413 
7414       // Try to get mProgressSink if it was nulled out during OnStatus.
7415       if (!mProgressSink) {
7416         GetCallback(mProgressSink);
7417       }
7418       if (mProgressSink) {
7419         mProgressSink->OnProgress(this, nullptr, progress, progressMax);
7420       }
7421     }
7422   }
7423 
7424   return NS_OK;
7425 }
7426 
7427 //-----------------------------------------------------------------------------
7428 // nsHttpChannel::nsICacheInfoChannel
7429 //-----------------------------------------------------------------------------
7430 
7431 NS_IMETHODIMP
IsFromCache(bool * value)7432 nsHttpChannel::IsFromCache(bool *value) {
7433   if (!mIsPending) return NS_ERROR_NOT_AVAILABLE;
7434 
7435   if (!mRaceCacheWithNetwork) {
7436     // return false if reading a partial cache entry; the data isn't
7437     // entirely from the cache!
7438     *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
7439              mCachedContentIsValid && !mCachedContentIsPartial;
7440     return NS_OK;
7441   }
7442 
7443   // If we are racing network and cache (or skipping the cache)
7444   // we just return the first response source.
7445   *value = mFirstResponseSource == RESPONSE_FROM_CACHE;
7446 
7447   return NS_OK;
7448 }
7449 
7450 NS_IMETHODIMP
GetCacheEntryId(uint64_t * aCacheEntryId)7451 nsHttpChannel::GetCacheEntryId(uint64_t *aCacheEntryId) {
7452   bool fromCache = false;
7453   if (NS_FAILED(IsFromCache(&fromCache)) || !fromCache || !mCacheEntry ||
7454       NS_FAILED(mCacheEntry->GetCacheEntryId(aCacheEntryId))) {
7455     return NS_ERROR_NOT_AVAILABLE;
7456   }
7457 
7458   return NS_OK;
7459 }
7460 
7461 NS_IMETHODIMP
GetCacheTokenFetchCount(int32_t * _retval)7462 nsHttpChannel::GetCacheTokenFetchCount(int32_t *_retval) {
7463   NS_ENSURE_ARG_POINTER(_retval);
7464   nsCOMPtr<nsICacheEntry> cacheEntry =
7465       mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
7466   if (!cacheEntry) {
7467     return NS_ERROR_NOT_AVAILABLE;
7468   }
7469 
7470   return cacheEntry->GetFetchCount(_retval);
7471 }
7472 
7473 NS_IMETHODIMP
GetCacheTokenExpirationTime(uint32_t * _retval)7474 nsHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval) {
7475   NS_ENSURE_ARG_POINTER(_retval);
7476   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
7477 
7478   return mCacheEntry->GetExpirationTime(_retval);
7479 }
7480 
7481 NS_IMETHODIMP
GetCacheTokenCachedCharset(nsACString & _retval)7482 nsHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval) {
7483   nsresult rv;
7484 
7485   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
7486 
7487   nsCString cachedCharset;
7488   rv = mCacheEntry->GetMetaDataElement("charset", getter_Copies(cachedCharset));
7489   if (NS_SUCCEEDED(rv)) _retval = cachedCharset;
7490 
7491   return rv;
7492 }
7493 
7494 NS_IMETHODIMP
SetCacheTokenCachedCharset(const nsACString & aCharset)7495 nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset) {
7496   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
7497 
7498   return mCacheEntry->SetMetaDataElement("charset",
7499                                          PromiseFlatCString(aCharset).get());
7500 }
7501 
7502 NS_IMETHODIMP
SetAllowStaleCacheContent(bool aAllowStaleCacheContent)7503 nsHttpChannel::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) {
7504   LOG(("nsHttpChannel::SetAllowStaleCacheContent [this=%p, allow=%d]", this,
7505        aAllowStaleCacheContent));
7506   mAllowStaleCacheContent = aAllowStaleCacheContent;
7507   return NS_OK;
7508 }
7509 NS_IMETHODIMP
GetAllowStaleCacheContent(bool * aAllowStaleCacheContent)7510 nsHttpChannel::GetAllowStaleCacheContent(bool *aAllowStaleCacheContent) {
7511   NS_ENSURE_ARG(aAllowStaleCacheContent);
7512   *aAllowStaleCacheContent = mAllowStaleCacheContent;
7513   return NS_OK;
7514 }
7515 
7516 NS_IMETHODIMP
PreferAlternativeDataType(const nsACString & aType)7517 nsHttpChannel::PreferAlternativeDataType(const nsACString &aType) {
7518   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
7519   mPreferredCachedAltDataType = aType;
7520   return NS_OK;
7521 }
7522 
7523 NS_IMETHODIMP
GetPreferredAlternativeDataType(nsACString & aType)7524 nsHttpChannel::GetPreferredAlternativeDataType(nsACString &aType) {
7525   aType = mPreferredCachedAltDataType;
7526   return NS_OK;
7527 }
7528 
7529 NS_IMETHODIMP
GetAlternativeDataType(nsACString & aType)7530 nsHttpChannel::GetAlternativeDataType(nsACString &aType) {
7531   // must be called during or after OnStartRequest
7532   if (!mAfterOnStartRequestBegun) {
7533     return NS_ERROR_NOT_AVAILABLE;
7534   }
7535   aType = mAvailableCachedAltDataType;
7536   return NS_OK;
7537 }
7538 
7539 NS_IMETHODIMP
OpenAlternativeOutputStream(const nsACString & type,nsIOutputStream ** _retval)7540 nsHttpChannel::OpenAlternativeOutputStream(const nsACString &type,
7541                                            nsIOutputStream **_retval) {
7542   // OnStopRequest will clear mCacheEntry, but we may use mAltDataCacheEntry
7543   // if the consumer called PreferAlternativeDataType()
7544   nsCOMPtr<nsICacheEntry> cacheEntry =
7545       mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
7546   if (!cacheEntry) {
7547     return NS_ERROR_NOT_AVAILABLE;
7548   }
7549   nsresult rv = cacheEntry->OpenAlternativeOutputStream(type, _retval);
7550   if (NS_SUCCEEDED(rv)) {
7551     // Clear this metadata flag in case it exists.
7552     // The caller of this method may set it again.
7553     cacheEntry->SetMetaDataElement("alt-data-from-child", nullptr);
7554   }
7555   return rv;
7556 }
7557 
7558 //-----------------------------------------------------------------------------
7559 // nsHttpChannel::nsICachingChannel
7560 //-----------------------------------------------------------------------------
7561 
7562 NS_IMETHODIMP
GetCacheToken(nsISupports ** token)7563 nsHttpChannel::GetCacheToken(nsISupports **token) {
7564   NS_ENSURE_ARG_POINTER(token);
7565   if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
7566   return CallQueryInterface(mCacheEntry, token);
7567 }
7568 
7569 NS_IMETHODIMP
SetCacheToken(nsISupports * token)7570 nsHttpChannel::SetCacheToken(nsISupports *token) {
7571   return NS_ERROR_NOT_IMPLEMENTED;
7572 }
7573 
7574 NS_IMETHODIMP
GetOfflineCacheToken(nsISupports ** token)7575 nsHttpChannel::GetOfflineCacheToken(nsISupports **token) {
7576   NS_ENSURE_ARG_POINTER(token);
7577   if (!mOfflineCacheEntry) return NS_ERROR_NOT_AVAILABLE;
7578   return CallQueryInterface(mOfflineCacheEntry, token);
7579 }
7580 
7581 NS_IMETHODIMP
SetOfflineCacheToken(nsISupports * token)7582 nsHttpChannel::SetOfflineCacheToken(nsISupports *token) {
7583   return NS_ERROR_NOT_IMPLEMENTED;
7584 }
7585 
7586 NS_IMETHODIMP
GetCacheKey(nsISupports ** key)7587 nsHttpChannel::GetCacheKey(nsISupports **key) {
7588   nsresult rv;
7589   NS_ENSURE_ARG_POINTER(key);
7590 
7591   LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this));
7592 
7593   *key = nullptr;
7594 
7595   nsCOMPtr<nsISupportsPRUint32> container =
7596       do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
7597 
7598   if (!container) return NS_ERROR_OUT_OF_MEMORY;
7599 
7600   rv = container->SetData(mPostID);
7601   if (NS_FAILED(rv)) return rv;
7602 
7603   return CallQueryInterface(container.get(), key);
7604 }
7605 
7606 NS_IMETHODIMP
SetCacheKey(nsISupports * key)7607 nsHttpChannel::SetCacheKey(nsISupports *key) {
7608   nsresult rv;
7609 
7610   LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n", this, key));
7611 
7612   ENSURE_CALLED_BEFORE_CONNECT();
7613 
7614   if (!key)
7615     mPostID = 0;
7616   else {
7617     // extract the post id
7618     nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(key, &rv);
7619     if (NS_FAILED(rv)) return rv;
7620 
7621     rv = container->GetData(&mPostID);
7622     if (NS_FAILED(rv)) return rv;
7623   }
7624   return NS_OK;
7625 }
7626 
7627 NS_IMETHODIMP
GetCacheOnlyMetadata(bool * aOnlyMetadata)7628 nsHttpChannel::GetCacheOnlyMetadata(bool *aOnlyMetadata) {
7629   NS_ENSURE_ARG(aOnlyMetadata);
7630   *aOnlyMetadata = mCacheOnlyMetadata;
7631   return NS_OK;
7632 }
7633 
7634 NS_IMETHODIMP
SetCacheOnlyMetadata(bool aOnlyMetadata)7635 nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata) {
7636   LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n", this,
7637        aOnlyMetadata));
7638 
7639   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
7640 
7641   mCacheOnlyMetadata = aOnlyMetadata;
7642   if (aOnlyMetadata) {
7643     mLoadFlags |= LOAD_ONLY_IF_MODIFIED;
7644   }
7645 
7646   return NS_OK;
7647 }
7648 
7649 NS_IMETHODIMP
GetPin(bool * aPin)7650 nsHttpChannel::GetPin(bool *aPin) {
7651   NS_ENSURE_ARG(aPin);
7652   *aPin = mPinCacheContent;
7653   return NS_OK;
7654 }
7655 
7656 NS_IMETHODIMP
SetPin(bool aPin)7657 nsHttpChannel::SetPin(bool aPin) {
7658   LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n", this, aPin));
7659 
7660   ENSURE_CALLED_BEFORE_CONNECT();
7661 
7662   mPinCacheContent = aPin;
7663   return NS_OK;
7664 }
7665 
7666 NS_IMETHODIMP
ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture)7667 nsHttpChannel::ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture) {
7668   if (!mCacheEntry) {
7669     LOG(
7670         ("nsHttpChannel::ForceCacheEntryValidFor found no cache entry "
7671          "for this channel [this=%p].",
7672          this));
7673   } else {
7674     mCacheEntry->ForceValidFor(aSecondsToTheFuture);
7675 
7676     nsAutoCString key;
7677     mCacheEntry->GetKey(key);
7678 
7679     LOG(
7680         ("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid "
7681          "entry with key %s for %d seconds. [this=%p]",
7682          key.get(), aSecondsToTheFuture, this));
7683   }
7684 
7685   return NS_OK;
7686 }
7687 
7688 //-----------------------------------------------------------------------------
7689 // nsHttpChannel::nsIResumableChannel
7690 //-----------------------------------------------------------------------------
7691 
7692 NS_IMETHODIMP
ResumeAt(uint64_t aStartPos,const nsACString & aEntityID)7693 nsHttpChannel::ResumeAt(uint64_t aStartPos, const nsACString &aEntityID) {
7694   LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%" PRIu64 " id='%s']\n", this,
7695        aStartPos, PromiseFlatCString(aEntityID).get()));
7696   mEntityID = aEntityID;
7697   mStartPos = aStartPos;
7698   mResuming = true;
7699   return NS_OK;
7700 }
7701 
DoAuthRetry(nsAHttpConnection * conn)7702 nsresult nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn) {
7703   LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
7704 
7705   MOZ_ASSERT(!mTransaction, "should not have a transaction");
7706   nsresult rv;
7707 
7708   // toggle mIsPending to allow nsIObserver implementations to modify
7709   // the request headers (bug 95044).
7710   mIsPending = false;
7711 
7712   // fetch cookies, and add them to the request header.
7713   // the server response could have included cookies that must be sent with
7714   // this authentication attempt (bug 84794).
7715   // TODO: save cookies from auth response and send them here (bug 572151).
7716   AddCookiesToRequest();
7717 
7718   // notify "http-on-modify-request" observers
7719   CallOnModifyRequestObservers();
7720 
7721   mIsPending = true;
7722 
7723   // get rid of the old response headers
7724   mResponseHead = nullptr;
7725 
7726   // rewind the upload stream
7727   if (mUploadStream) {
7728     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
7729     if (seekable) seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
7730   }
7731 
7732   // always set sticky connection flag
7733   mCaps |= NS_HTTP_STICKY_CONNECTION;
7734   // and when needed, allow restart regardless the sticky flag
7735   if (mAuthConnectionRestartable) {
7736     LOG(("  connection made restartable"));
7737     mCaps |= NS_HTTP_CONNECTION_RESTARTABLE;
7738     mAuthConnectionRestartable = false;
7739   } else {
7740     LOG(("  connection made non-restartable"));
7741     mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE;
7742   }
7743 
7744   // and create a new one...
7745   rv = SetupTransaction();
7746   if (NS_FAILED(rv)) return rv;
7747 
7748   // transfer ownership of connection to transaction
7749   if (conn) mTransaction->SetConnection(conn);
7750 
7751   rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
7752   if (NS_FAILED(rv)) return rv;
7753 
7754   rv = mTransactionPump->AsyncRead(this, nullptr);
7755   if (NS_FAILED(rv)) return rv;
7756 
7757   uint32_t suspendCount = mSuspendCount;
7758   while (suspendCount--) mTransactionPump->Suspend();
7759 
7760   return NS_OK;
7761 }
7762 
7763 //-----------------------------------------------------------------------------
7764 // nsHttpChannel::nsIApplicationCacheChannel
7765 //-----------------------------------------------------------------------------
7766 
7767 NS_IMETHODIMP
GetApplicationCache(nsIApplicationCache ** out)7768 nsHttpChannel::GetApplicationCache(nsIApplicationCache **out) {
7769   NS_IF_ADDREF(*out = mApplicationCache);
7770   return NS_OK;
7771 }
7772 
7773 NS_IMETHODIMP
SetApplicationCache(nsIApplicationCache * appCache)7774 nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache) {
7775   ENSURE_CALLED_BEFORE_CONNECT();
7776 
7777   mApplicationCache = appCache;
7778   return NS_OK;
7779 }
7780 
7781 NS_IMETHODIMP
GetApplicationCacheForWrite(nsIApplicationCache ** out)7782 nsHttpChannel::GetApplicationCacheForWrite(nsIApplicationCache **out) {
7783   NS_IF_ADDREF(*out = mApplicationCacheForWrite);
7784   return NS_OK;
7785 }
7786 
7787 NS_IMETHODIMP
SetApplicationCacheForWrite(nsIApplicationCache * appCache)7788 nsHttpChannel::SetApplicationCacheForWrite(nsIApplicationCache *appCache) {
7789   ENSURE_CALLED_BEFORE_CONNECT();
7790 
7791   mApplicationCacheForWrite = appCache;
7792   return NS_OK;
7793 }
7794 
7795 NS_IMETHODIMP
GetLoadedFromApplicationCache(bool * aLoadedFromApplicationCache)7796 nsHttpChannel::GetLoadedFromApplicationCache(
7797     bool *aLoadedFromApplicationCache) {
7798   *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
7799   return NS_OK;
7800 }
7801 
7802 NS_IMETHODIMP
GetInheritApplicationCache(bool * aInherit)7803 nsHttpChannel::GetInheritApplicationCache(bool *aInherit) {
7804   *aInherit = mInheritApplicationCache;
7805   return NS_OK;
7806 }
7807 
7808 NS_IMETHODIMP
SetInheritApplicationCache(bool aInherit)7809 nsHttpChannel::SetInheritApplicationCache(bool aInherit) {
7810   ENSURE_CALLED_BEFORE_CONNECT();
7811 
7812   mInheritApplicationCache = aInherit;
7813   return NS_OK;
7814 }
7815 
7816 NS_IMETHODIMP
GetChooseApplicationCache(bool * aChoose)7817 nsHttpChannel::GetChooseApplicationCache(bool *aChoose) {
7818   *aChoose = mChooseApplicationCache;
7819   return NS_OK;
7820 }
7821 
7822 NS_IMETHODIMP
SetChooseApplicationCache(bool aChoose)7823 nsHttpChannel::SetChooseApplicationCache(bool aChoose) {
7824   ENSURE_CALLED_BEFORE_CONNECT();
7825 
7826   mChooseApplicationCache = aChoose;
7827   return NS_OK;
7828 }
7829 
7830 nsHttpChannel::OfflineCacheEntryAsForeignMarker *
GetOfflineCacheEntryAsForeignMarker()7831 nsHttpChannel::GetOfflineCacheEntryAsForeignMarker() {
7832   if (!mApplicationCache) return nullptr;
7833 
7834   return new OfflineCacheEntryAsForeignMarker(mApplicationCache, mURI);
7835 }
7836 
MarkAsForeign()7837 nsresult nsHttpChannel::OfflineCacheEntryAsForeignMarker::MarkAsForeign() {
7838   nsresult rv;
7839 
7840   nsCOMPtr<nsIURI> noRefURI;
7841   rv = mCacheURI->CloneIgnoringRef(getter_AddRefs(noRefURI));
7842   NS_ENSURE_SUCCESS(rv, rv);
7843 
7844   nsAutoCString spec;
7845   rv = noRefURI->GetAsciiSpec(spec);
7846   NS_ENSURE_SUCCESS(rv, rv);
7847 
7848   return mApplicationCache->MarkEntry(spec, nsIApplicationCache::ITEM_FOREIGN);
7849 }
7850 
7851 NS_IMETHODIMP
MarkOfflineCacheEntryAsForeign()7852 nsHttpChannel::MarkOfflineCacheEntryAsForeign() {
7853   nsresult rv;
7854 
7855   nsAutoPtr<OfflineCacheEntryAsForeignMarker> marker(
7856       GetOfflineCacheEntryAsForeignMarker());
7857 
7858   if (!marker) return NS_ERROR_NOT_AVAILABLE;
7859 
7860   rv = marker->MarkAsForeign();
7861   NS_ENSURE_SUCCESS(rv, rv);
7862 
7863   return NS_OK;
7864 }
7865 
7866 //-----------------------------------------------------------------------------
7867 // nsHttpChannel::nsIAsyncVerifyRedirectCallback
7868 //-----------------------------------------------------------------------------
7869 
WaitForRedirectCallback()7870 nsresult nsHttpChannel::WaitForRedirectCallback() {
7871   nsresult rv;
7872   LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this));
7873 
7874   if (mTransactionPump) {
7875     rv = mTransactionPump->Suspend();
7876     NS_ENSURE_SUCCESS(rv, rv);
7877   }
7878   if (mCachePump) {
7879     rv = mCachePump->Suspend();
7880     if (NS_FAILED(rv) && mTransactionPump) {
7881 #ifdef DEBUG
7882       nsresult resume =
7883 #endif
7884           mTransactionPump->Resume();
7885       MOZ_ASSERT(NS_SUCCEEDED(resume), "Failed to resume transaction pump");
7886     }
7887     NS_ENSURE_SUCCESS(rv, rv);
7888   }
7889 
7890   mWaitingForRedirectCallback = true;
7891   return NS_OK;
7892 }
7893 
7894 NS_IMETHODIMP
OnRedirectVerifyCallback(nsresult result)7895 nsHttpChannel::OnRedirectVerifyCallback(nsresult result) {
7896   LOG(
7897       ("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
7898        "result=%" PRIx32 " stack=%zu mWaitingForRedirectCallback=%u\n",
7899        this, static_cast<uint32_t>(result), mRedirectFuncStack.Length(),
7900        mWaitingForRedirectCallback));
7901   MOZ_ASSERT(mWaitingForRedirectCallback,
7902              "Someone forgot to call WaitForRedirectCallback() ?!");
7903   mWaitingForRedirectCallback = false;
7904 
7905   if (mCanceled && NS_SUCCEEDED(result)) result = NS_BINDING_ABORTED;
7906 
7907   for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) {
7908     --i;
7909     // Pop the last function pushed to the stack
7910     nsContinueRedirectionFunc func = mRedirectFuncStack[i];
7911     mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length() - 1);
7912 
7913     // Call it with the result we got from the callback or the deeper
7914     // function call.
7915     result = (this->*func)(result);
7916 
7917     // If a new function has been pushed to the stack and placed us in the
7918     // waiting state, we need to break the chain and wait for the callback
7919     // again.
7920     if (mWaitingForRedirectCallback) break;
7921   }
7922 
7923   if (NS_FAILED(result) && !mCanceled) {
7924     // First, cancel this channel if we are in failure state to set mStatus
7925     // and let it be propagated to pumps.
7926     Cancel(result);
7927   }
7928 
7929   if (!mWaitingForRedirectCallback) {
7930     // We are not waiting for the callback. At this moment we must release
7931     // reference to the redirect target channel, otherwise we may leak.
7932     mRedirectChannel = nullptr;
7933   }
7934 
7935   // We always resume the pumps here. If all functions on stack have been
7936   // called we need OnStopRequest to be triggered, and if we broke out of the
7937   // loop above (and are thus waiting for a new callback) the suspension
7938   // count must be balanced in the pumps.
7939   if (mTransactionPump) mTransactionPump->Resume();
7940   if (mCachePump) mCachePump->Resume();
7941 
7942   return result;
7943 }
7944 
PushRedirectAsyncFunc(nsContinueRedirectionFunc func)7945 void nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func) {
7946   mRedirectFuncStack.AppendElement(func);
7947 }
7948 
PopRedirectAsyncFunc(nsContinueRedirectionFunc func)7949 void nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func) {
7950   MOZ_ASSERT(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
7951              "Trying to pop wrong method from redirect async stack!");
7952 
7953   mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
7954 }
7955 
7956 //-----------------------------------------------------------------------------
7957 // nsIDNSListener functions
7958 //-----------------------------------------------------------------------------
7959 
7960 NS_IMETHODIMP
OnLookupComplete(nsICancelable * request,nsIDNSRecord * rec,nsresult status)7961 nsHttpChannel::OnLookupComplete(nsICancelable *request, nsIDNSRecord *rec,
7962                                 nsresult status) {
7963   MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
7964 
7965   LOG(
7966       ("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: "
7967        "%s status[0x%" PRIx32 "]\n",
7968        this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
7969        NS_SUCCEEDED(status) ? "success" : "failure",
7970        static_cast<uint32_t>(status)));
7971 
7972   // We no longer need the dns prefetch object. Note: mDNSPrefetch could be
7973   // validly null if OnStopRequest has already been called.
7974   // We only need the domainLookup timestamps when not loading from cache
7975   if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
7976     TimeStamp connectStart = mTransaction->GetConnectStart();
7977     TimeStamp requestStart = mTransaction->GetRequestStart();
7978     // We only set the domainLookup timestamps if we're not using a
7979     // persistent connection.
7980     if (requestStart.IsNull() && connectStart.IsNull()) {
7981       mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
7982       mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
7983     }
7984   }
7985   mDNSPrefetch = nullptr;
7986 
7987   // Unset DNS cache refresh if it was requested,
7988   if (mCaps & NS_HTTP_REFRESH_DNS) {
7989     mCaps &= ~NS_HTTP_REFRESH_DNS;
7990     if (mTransaction) {
7991       mTransaction->SetDNSWasRefreshed();
7992     }
7993   }
7994 
7995   return NS_OK;
7996 }
7997 
7998 //-----------------------------------------------------------------------------
7999 // nsHttpChannel internal functions
8000 //-----------------------------------------------------------------------------
8001 
8002 // Creates an URI to the given location using current URI for base and charset
CreateNewURI(const char * loc,nsIURI ** newURI)8003 nsresult nsHttpChannel::CreateNewURI(const char *loc, nsIURI **newURI) {
8004   nsCOMPtr<nsIIOService> ioService;
8005   nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
8006   if (NS_FAILED(rv)) return rv;
8007 
8008   return ioService->NewURI(nsDependentCString(loc), nullptr, mURI, newURI);
8009 }
8010 
MaybeInvalidateCacheEntryForSubsequentGet()8011 void nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet() {
8012   // See RFC 2616 section 5.1.1. These are considered valid
8013   // methods which DO NOT invalidate cache-entries for the
8014   // referred resource. POST, PUT and DELETE as well as any
8015   // other method not listed here will potentially invalidate
8016   // any cached copy of the resource
8017   if (mRequestHead.IsGet() || mRequestHead.IsOptions() ||
8018       mRequestHead.IsHead() || mRequestHead.IsTrace() ||
8019       mRequestHead.IsConnect()) {
8020     return;
8021   }
8022 
8023   // Invalidate the request-uri.
8024   if (LOG_ENABLED()) {
8025     nsAutoCString key;
8026     mURI->GetAsciiSpec(key);
8027     LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n", this,
8028          key.get()));
8029   }
8030 
8031   DoInvalidateCacheEntry(mURI);
8032 
8033   // Invalidate Location-header if set
8034   nsAutoCString location;
8035   Unused << mResponseHead->GetHeader(nsHttp::Location, location);
8036   if (!location.IsEmpty()) {
8037     LOG(("  Location-header=%s\n", location.get()));
8038     InvalidateCacheEntryForLocation(location.get());
8039   }
8040 
8041   // Invalidate Content-Location-header if set
8042   Unused << mResponseHead->GetHeader(nsHttp::Content_Location, location);
8043   if (!location.IsEmpty()) {
8044     LOG(("  Content-Location-header=%s\n", location.get()));
8045     InvalidateCacheEntryForLocation(location.get());
8046   }
8047 }
8048 
InvalidateCacheEntryForLocation(const char * location)8049 void nsHttpChannel::InvalidateCacheEntryForLocation(const char *location) {
8050   nsAutoCString tmpCacheKey, tmpSpec;
8051   nsCOMPtr<nsIURI> resultingURI;
8052   nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI));
8053   if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) {
8054     DoInvalidateCacheEntry(resultingURI);
8055   } else {
8056     LOG(("  hosts not matching\n"));
8057   }
8058 }
8059 
DoInvalidateCacheEntry(nsIURI * aURI)8060 void nsHttpChannel::DoInvalidateCacheEntry(nsIURI *aURI) {
8061   // NOTE:
8062   // Following comments 24,32 and 33 in bug #327765, we only care about
8063   // the cache in the protocol-handler, not the application cache.
8064   // The logic below deviates from the original logic in OpenCacheEntry on
8065   // one point by using only READ_ONLY access-policy. I think this is safe.
8066 
8067   nsresult rv;
8068 
8069   nsAutoCString key;
8070   if (LOG_ENABLED()) {
8071     aURI->GetAsciiSpec(key);
8072   }
8073 
8074   LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get()));
8075 
8076   nsCOMPtr<nsICacheStorageService> cacheStorageService(
8077       services::GetCacheStorageService());
8078   rv = cacheStorageService ? NS_OK : NS_ERROR_FAILURE;
8079 
8080   nsCOMPtr<nsICacheStorage> cacheStorage;
8081   if (NS_SUCCEEDED(rv)) {
8082     RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
8083     rv = cacheStorageService->DiskCacheStorage(info, false,
8084                                                getter_AddRefs(cacheStorage));
8085   }
8086 
8087   if (NS_SUCCEEDED(rv)) {
8088     rv = cacheStorage->AsyncDoomURI(aURI, EmptyCString(), nullptr);
8089   }
8090 
8091   LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(),
8092        int(rv)));
8093 }
8094 
AsyncOnExamineCachedResponse()8095 void nsHttpChannel::AsyncOnExamineCachedResponse() {
8096   gHttpHandler->OnExamineCachedResponse(this);
8097 }
8098 
UpdateAggregateCallbacks()8099 void nsHttpChannel::UpdateAggregateCallbacks() {
8100   if (!mTransaction) {
8101     return;
8102   }
8103   nsCOMPtr<nsIInterfaceRequestor> callbacks;
8104   NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
8105                                          GetCurrentThreadEventTarget(),
8106                                          getter_AddRefs(callbacks));
8107   mTransaction->SetSecurityCallbacks(callbacks);
8108 }
8109 
8110 NS_IMETHODIMP
SetLoadGroup(nsILoadGroup * aLoadGroup)8111 nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) {
8112   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
8113 
8114   nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup);
8115   if (NS_SUCCEEDED(rv)) {
8116     UpdateAggregateCallbacks();
8117   }
8118   return rv;
8119 }
8120 
8121 NS_IMETHODIMP
SetNotificationCallbacks(nsIInterfaceRequestor * aCallbacks)8122 nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) {
8123   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
8124 
8125   nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks);
8126   if (NS_SUCCEEDED(rv)) {
8127     UpdateAggregateCallbacks();
8128   }
8129   return rv;
8130 }
8131 
8132 NS_IMETHODIMP
GetResponseSynthesized(bool * aSynthesized)8133 nsHttpChannel::GetResponseSynthesized(bool *aSynthesized) {
8134   NS_ENSURE_ARG_POINTER(aSynthesized);
8135   *aSynthesized = false;
8136   return NS_OK;
8137 }
8138 
AwaitingCacheCallbacks()8139 bool nsHttpChannel::AwaitingCacheCallbacks() {
8140   return mCacheEntriesToWaitFor != 0;
8141 }
8142 
SetPushedStream(Http2PushedStreamWrapper * stream)8143 void nsHttpChannel::SetPushedStream(Http2PushedStreamWrapper *stream) {
8144   MOZ_ASSERT(stream);
8145   MOZ_ASSERT(!mPushedStream);
8146   mPushedStream = stream;
8147 }
8148 
OnPush(const nsACString & url,Http2PushedStreamWrapper * pushedStream)8149 nsresult nsHttpChannel::OnPush(const nsACString &url,
8150                                Http2PushedStreamWrapper *pushedStream) {
8151   MOZ_ASSERT(NS_IsMainThread());
8152   LOG(("nsHttpChannel::OnPush [this=%p]\n", this));
8153 
8154   MOZ_ASSERT(mCaps & NS_HTTP_ONPUSH_LISTENER);
8155   nsCOMPtr<nsIHttpPushListener> pushListener;
8156   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
8157                                 NS_GET_IID(nsIHttpPushListener),
8158                                 getter_AddRefs(pushListener));
8159 
8160   MOZ_ASSERT(pushListener);
8161   if (!pushListener) {
8162     LOG(
8163         ("nsHttpChannel::OnPush [this=%p] notification callbacks do not "
8164          "implement nsIHttpPushListener\n",
8165          this));
8166     return NS_ERROR_UNEXPECTED;
8167   }
8168 
8169   nsCOMPtr<nsIURI> pushResource;
8170   nsresult rv;
8171 
8172   // Create a Channel for the Push Resource
8173   rv = NS_NewURI(getter_AddRefs(pushResource), url);
8174   if (NS_FAILED(rv)) {
8175     return NS_ERROR_FAILURE;
8176   }
8177 
8178   nsCOMPtr<nsIIOService> ioService;
8179   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
8180   NS_ENSURE_SUCCESS(rv, rv);
8181 
8182   nsCOMPtr<nsIChannel> pushChannel;
8183   rv = NS_NewChannelInternal(getter_AddRefs(pushChannel), pushResource,
8184                              mLoadInfo,
8185                              nullptr,  // PerformanceStorage
8186                              nullptr,  // aLoadGroup
8187                              nullptr,  // aCallbacks
8188                              nsIRequest::LOAD_NORMAL, ioService);
8189   NS_ENSURE_SUCCESS(rv, rv);
8190 
8191   nsCOMPtr<nsIHttpChannel> pushHttpChannel = do_QueryInterface(pushChannel);
8192   MOZ_ASSERT(pushHttpChannel);
8193   if (!pushHttpChannel) {
8194     return NS_ERROR_UNEXPECTED;
8195   }
8196 
8197   RefPtr<nsHttpChannel> channel;
8198   CallQueryInterface(pushHttpChannel, channel.StartAssignment());
8199   MOZ_ASSERT(channel);
8200   if (!channel) {
8201     return NS_ERROR_UNEXPECTED;
8202   }
8203 
8204   // new channel needs mrqeuesthead and headers from pushedStream
8205   channel->mRequestHead.ParseHeaderSet(
8206       pushedStream->GetRequestString().BeginWriting());
8207 
8208   channel->mLoadGroup = mLoadGroup;
8209   channel->mLoadInfo = mLoadInfo;
8210   channel->mCallbacks = mCallbacks;
8211 
8212   // Link the pushed stream with the new channel and call listener
8213   channel->SetPushedStream(pushedStream);
8214   rv = pushListener->OnPush(this, pushHttpChannel);
8215   return rv;
8216 }
8217 
8218 // static
IsRedirectStatus(uint32_t status)8219 bool nsHttpChannel::IsRedirectStatus(uint32_t status) {
8220   // 305 disabled as a security measure (see bug 187996).
8221   return status == 300 || status == 301 || status == 302 || status == 303 ||
8222          status == 307 || status == 308;
8223 }
8224 
SetCouldBeSynthesized()8225 void nsHttpChannel::SetCouldBeSynthesized() {
8226   MOZ_ASSERT(!BypassServiceWorker());
8227   mResponseCouldBeSynthesized = true;
8228 }
8229 
SetConnectionInfo(nsHttpConnectionInfo * aCI)8230 void nsHttpChannel::SetConnectionInfo(nsHttpConnectionInfo *aCI) {
8231   mConnectionInfo = aCI ? aCI->Clone() : nullptr;
8232 }
8233 
8234 NS_IMETHODIMP
OnPreflightSucceeded()8235 nsHttpChannel::OnPreflightSucceeded() {
8236   MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
8237   mIsCorsPreflightDone = 1;
8238   mPreflightChannel = nullptr;
8239 
8240   return ContinueConnect();
8241 }
8242 
8243 NS_IMETHODIMP
OnPreflightFailed(nsresult aError)8244 nsHttpChannel::OnPreflightFailed(nsresult aError) {
8245   MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
8246   mIsCorsPreflightDone = 1;
8247   mPreflightChannel = nullptr;
8248 
8249   CloseCacheEntry(false);
8250   Unused << AsyncAbort(aError);
8251   return NS_OK;
8252 }
8253 
8254 //-----------------------------------------------------------------------------
8255 // AChannelHasDivertableParentChannelAsListener internal functions
8256 //-----------------------------------------------------------------------------
8257 
8258 NS_IMETHODIMP
MessageDiversionStarted(ADivertableParentChannel * aParentChannel)8259 nsHttpChannel::MessageDiversionStarted(
8260     ADivertableParentChannel *aParentChannel) {
8261   LOG(("nsHttpChannel::MessageDiversionStarted [this=%p]", this));
8262   MOZ_ASSERT(!mParentChannel);
8263   mParentChannel = aParentChannel;
8264   // If the channel is suspended, propagate that info to the parent's mEventQ.
8265   uint32_t suspendCount = mSuspendCount;
8266   while (suspendCount--) {
8267     mParentChannel->SuspendMessageDiversion();
8268   }
8269   return NS_OK;
8270 }
8271 
8272 NS_IMETHODIMP
MessageDiversionStop()8273 nsHttpChannel::MessageDiversionStop() {
8274   LOG(("nsHttpChannel::MessageDiversionStop [this=%p]", this));
8275   MOZ_ASSERT(mParentChannel);
8276   mParentChannel = nullptr;
8277   return NS_OK;
8278 }
8279 
8280 NS_IMETHODIMP
SuspendInternal()8281 nsHttpChannel::SuspendInternal() {
8282   NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE);
8283 
8284   LOG(("nsHttpChannel::SuspendInternal [this=%p]\n", this));
8285 
8286   ++mSuspendCount;
8287 
8288   if (mSuspendCount == 1) {
8289     mSuspendTimestamp = TimeStamp::NowLoRes();
8290   }
8291 
8292   nsresult rvTransaction = NS_OK;
8293   if (mTransactionPump) {
8294     rvTransaction = mTransactionPump->Suspend();
8295   }
8296   nsresult rvCache = NS_OK;
8297   if (mCachePump) {
8298     rvCache = mCachePump->Suspend();
8299   }
8300 
8301   return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
8302 }
8303 
8304 NS_IMETHODIMP
ResumeInternal()8305 nsHttpChannel::ResumeInternal() {
8306   NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
8307 
8308   LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this));
8309 
8310   if (--mSuspendCount == 0) {
8311     mSuspendTotalTime +=
8312         (TimeStamp::NowLoRes() - mSuspendTimestamp).ToMilliseconds();
8313 
8314     if (mCallOnResume) {
8315       // Resume the interrupted procedure first, then resume
8316       // the pump to continue process the input stream.
8317       RefPtr<nsRunnableMethod<nsHttpChannel>> callOnResume =
8318           NewRunnableMethod("CallOnResume", this, mCallOnResume);
8319       // Should not resume pump that created after resumption.
8320       RefPtr<nsInputStreamPump> transactionPump = mTransactionPump;
8321       RefPtr<nsInputStreamPump> cachePump = mCachePump;
8322 
8323       nsresult rv = NS_DispatchToCurrentThread(
8324           NS_NewRunnableFunction("nsHttpChannel::CallOnResume",
8325                                  [callOnResume, transactionPump, cachePump]() {
8326                                    callOnResume->Run();
8327 
8328                                    if (transactionPump) {
8329                                      transactionPump->Resume();
8330                                    }
8331 
8332                                    if (cachePump) {
8333                                      cachePump->Resume();
8334                                    }
8335                                  }));
8336       mCallOnResume = nullptr;
8337       NS_ENSURE_SUCCESS(rv, rv);
8338       return rv;
8339     }
8340   }
8341 
8342   nsresult rvTransaction = NS_OK;
8343   if (mTransactionPump) {
8344     rvTransaction = mTransactionPump->Resume();
8345   }
8346 
8347   nsresult rvCache = NS_OK;
8348   if (mCachePump) {
8349     rvCache = mCachePump->Resume();
8350   }
8351 
8352   return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
8353 }
8354 
MaybeWarnAboutAppCache()8355 void nsHttpChannel::MaybeWarnAboutAppCache() {
8356   // First, accumulate a telemetry ping about appcache usage.
8357   Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, true);
8358 
8359   // Then, issue a deprecation warning.
8360   nsCOMPtr<nsIDeprecationWarner> warner;
8361   GetCallback(warner);
8362   if (warner) {
8363     warner->IssueWarning(nsIDocument::eAppCache, false);
8364     // When the page is insecure and the API is still enabled
8365     // provide an additional warning for developers of removal
8366     if (!IsHTTPS() &&
8367         Preferences::GetBool("browser.cache.offline.insecure.enable")) {
8368       warner->IssueWarning(nsIDocument::eAppCacheInsecure, true);
8369     }
8370   }
8371 }
8372 
SetLoadGroupUserAgentOverride()8373 void nsHttpChannel::SetLoadGroupUserAgentOverride() {
8374   nsCOMPtr<nsIURI> uri;
8375   GetURI(getter_AddRefs(uri));
8376   nsAutoCString uriScheme;
8377   if (uri) {
8378     uri->GetScheme(uriScheme);
8379   }
8380 
8381   // We don't need a UA for file: protocols.
8382   if (uriScheme.EqualsLiteral("file")) {
8383     gHttpHandler->OnUserAgentRequest(this);
8384     return;
8385   }
8386 
8387   nsIRequestContextService *rcsvc = gHttpHandler->GetRequestContextService();
8388   nsCOMPtr<nsIRequestContext> rc;
8389   if (rcsvc) {
8390     rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(rc));
8391   }
8392 
8393   nsAutoCString ua;
8394   if (nsContentUtils::IsNonSubresourceRequest(this)) {
8395     gHttpHandler->OnUserAgentRequest(this);
8396     if (rc) {
8397       GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
8398       rc->SetUserAgentOverride(ua);
8399     }
8400   } else {
8401     GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
8402     // Don't overwrite the UA if it is already set (eg by an XHR with explicit
8403     // UA).
8404     if (ua.IsEmpty()) {
8405       if (rc) {
8406         rc->GetUserAgentOverride(ua);
8407         SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua, false);
8408       } else {
8409         gHttpHandler->OnUserAgentRequest(this);
8410       }
8411     }
8412   }
8413 }
8414 
8415 // Step 10 of HTTP-network-or-cache fetch
SetOriginHeader()8416 void nsHttpChannel::SetOriginHeader() {
8417   if (mRequestHead.IsGet() || mRequestHead.IsHead()) {
8418     return;
8419   }
8420   nsAutoCString existingHeader;
8421   Unused << mRequestHead.GetHeader(nsHttp::Origin, existingHeader);
8422   if (!existingHeader.IsEmpty()) {
8423     LOG(("nsHttpChannel::SetOriginHeader Origin header already present"));
8424     return;
8425   }
8426 
8427   DebugOnly<nsresult> rv;
8428 
8429   // Instead of consulting Preferences::GetInt() all the time we
8430   // can cache the result to speed things up.
8431   static int32_t sSendOriginHeader = 0;
8432   static bool sIsInited = false;
8433   if (!sIsInited) {
8434     sIsInited = true;
8435     Preferences::AddIntVarCache(&sSendOriginHeader,
8436                                 "network.http.sendOriginHeader");
8437   }
8438   if (sSendOriginHeader == 0) {
8439     // Origin header suppressed by user setting
8440     return;
8441   }
8442 
8443   nsCOMPtr<nsIURI> referrer;
8444   mLoadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(referrer));
8445 
8446   nsAutoCString origin("null");
8447   if (referrer && IsReferrerSchemeAllowed(referrer)) {
8448     nsContentUtils::GetASCIIOrigin(referrer, origin);
8449   }
8450 
8451   // Restrict Origin to same-origin loads if requested by user
8452   if (sSendOriginHeader == 1) {
8453     nsAutoCString currentOrigin;
8454     nsContentUtils::GetASCIIOrigin(mURI, currentOrigin);
8455     if (!origin.EqualsIgnoreCase(currentOrigin.get())) {
8456       // Origin header suppressed by user setting
8457       return;
8458     }
8459   }
8460 
8461   rv = mRequestHead.SetHeader(nsHttp::Origin, origin, false /* merge */);
8462   MOZ_ASSERT(NS_SUCCEEDED(rv));
8463 }
8464 
SetDoNotTrack()8465 void nsHttpChannel::SetDoNotTrack() {
8466   /**
8467    * 'DoNotTrack' header should be added if 'privacy.donottrackheader.enabled'
8468    * is true or tracking protection is enabled. See bug 1258033.
8469    */
8470   nsCOMPtr<nsILoadContext> loadContext;
8471   NS_QueryNotificationCallbacks(this, loadContext);
8472 
8473   if ((loadContext && loadContext->UseTrackingProtection()) ||
8474       nsContentUtils::DoNotTrackEnabled()) {
8475     DebugOnly<nsresult> rv = mRequestHead.SetHeader(
8476         nsHttp::DoNotTrack, NS_LITERAL_CSTRING("1"), false);
8477     MOZ_ASSERT(NS_SUCCEEDED(rv));
8478   }
8479 }
8480 
ReportRcwnStats(bool isFromNet)8481 void nsHttpChannel::ReportRcwnStats(bool isFromNet) {
8482   if (!sRCWNEnabled) {
8483     return;
8484   }
8485 
8486   if (isFromNet) {
8487     if (mRaceCacheWithNetwork) {
8488       gIOService->IncrementNetWonRequestNumber();
8489       Telemetry::Accumulate(
8490           Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_NETWORK_WIN,
8491           mTransferSize);
8492       if (mRaceDelay) {
8493         AccumulateCategorical(
8494             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
8495                 NetworkDelayedRace);
8496       } else {
8497         AccumulateCategorical(
8498             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
8499                 NetworkRace);
8500       }
8501     } else {
8502       Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE,
8503                             mTransferSize);
8504       AccumulateCategorical(
8505           Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
8506               NetworkNoRace);
8507     }
8508   } else {
8509     if (mRaceCacheWithNetwork || mRaceDelay) {
8510       gIOService->IncrementCacheWonRequestNumber();
8511       Telemetry::Accumulate(
8512           Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_CACHE_WIN,
8513           mTransferSize);
8514       if (mRaceDelay) {
8515         AccumulateCategorical(
8516             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
8517                 CacheDelayedRace);
8518       } else {
8519         AccumulateCategorical(
8520             Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
8521                 CacheRace);
8522       }
8523     } else {
8524       Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE,
8525                             mTransferSize);
8526       AccumulateCategorical(
8527           Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::
8528               CacheNoRace);
8529     }
8530   }
8531 
8532   gIOService->IncrementRequestNumber();
8533 }
8534 
8535 static const size_t kPositiveBucketNumbers = 34;
8536 static const int64_t kPositiveBucketLevels[kPositiveBucketNumbers] = {
8537     0,    10,   20,   30,   40,    50,    60,    70,    80,    90,   100,  200,
8538     300,  400,  500,  600,  700,   800,   900,   1000,  2000,  3000, 4000, 5000,
8539     6000, 7000, 8000, 9000, 10000, 20000, 30000, 40000, 50000, 60000};
8540 
8541 /**
8542  * For space efficiency, we collect finer resolution for small difference
8543  * between net and cache time, coarser for larger.
8544  * Bucket #40 for a tie.
8545  * #41 to #50 indicates cache wins by 1ms to 100ms, split equally.
8546  * #51 to #59 indicates cache wins by 101ms to 1000ms.
8547  * #60 to #68 indicates cache wins by 1s to 10s.
8548  * #69 to #73 indicates cache wins by 11s to 60s.
8549  * #74 indicates cache wins by more than 1 minute.
8550  *
8551  * #39 to #30 indicates network wins by 1ms to 100ms, split equally.
8552  * #29 to #21 indicates network wins by 101ms to 1000ms.
8553  * #20 to #12 indicates network wins by 1s to 10s.
8554  * #11 to #7 indicates network wins by 11s to 60s.
8555  * #6 indicates network wins by more than 1 minute.
8556  *
8557  * Other bucket numbers are reserved.
8558  */
ComputeTelemetryBucketNumber(int64_t difftime_ms)8559 inline int64_t nsHttpChannel::ComputeTelemetryBucketNumber(
8560     int64_t difftime_ms) {
8561   int64_t absBucketIndex =
8562       std::lower_bound(kPositiveBucketLevels,
8563                        kPositiveBucketLevels + kPositiveBucketNumbers,
8564                        static_cast<int64_t>(mozilla::Abs(difftime_ms))) -
8565       kPositiveBucketLevels;
8566 
8567   return difftime_ms >= 0 ? 40 + absBucketIndex : 40 - absBucketIndex;
8568 }
8569 
ReportNetVSCacheTelemetry()8570 void nsHttpChannel::ReportNetVSCacheTelemetry() {
8571   nsresult rv;
8572   if (!mCacheEntry) {
8573     return;
8574   }
8575 
8576   // We only report telemetry if the entry is persistent (on disk)
8577   bool persistent;
8578   rv = mCacheEntry->GetPersistent(&persistent);
8579   if (NS_FAILED(rv) || !persistent) {
8580     return;
8581   }
8582 
8583   uint64_t onStartNetTime = 0;
8584   if (NS_FAILED(mCacheEntry->GetOnStartTime(&onStartNetTime))) {
8585     return;
8586   }
8587 
8588   uint64_t onStopNetTime = 0;
8589   if (NS_FAILED(mCacheEntry->GetOnStopTime(&onStopNetTime))) {
8590     return;
8591   }
8592 
8593   uint64_t onStartCacheTime =
8594       (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
8595   int64_t onStartDiff = onStartNetTime - onStartCacheTime;
8596   onStartDiff = ComputeTelemetryBucketNumber(onStartDiff);
8597 
8598   uint64_t onStopCacheTime = (mCacheReadEnd - mAsyncOpenTime).ToMilliseconds();
8599   int64_t onStopDiff = onStopNetTime - onStopCacheTime;
8600   onStopDiff = ComputeTelemetryBucketNumber(onStopDiff);
8601 
8602   if (mDidReval) {
8603     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_REVALIDATED_V2,
8604                           onStartDiff);
8605     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_REVALIDATED_V2,
8606                           onStopDiff);
8607   } else {
8608     Telemetry::Accumulate(
8609         Telemetry::HTTP_NET_VS_CACHE_ONSTART_NOTREVALIDATED_V2, onStartDiff);
8610     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_NOTREVALIDATED_V2,
8611                           onStopDiff);
8612   }
8613 
8614   if (mDidReval) {
8615     // We don't report revalidated probes as the data would be skewed.
8616     return;
8617   }
8618 
8619   if (mCacheOpenWithPriority) {
8620     if (mCacheQueueSizeWhenOpen < 5) {
8621       Telemetry::Accumulate(
8622           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_HIGHPRI_V2, onStartDiff);
8623       Telemetry::Accumulate(
8624           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_HIGHPRI_V2, onStopDiff);
8625     } else if (mCacheQueueSizeWhenOpen < 10) {
8626       Telemetry::Accumulate(
8627           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_HIGHPRI_V2, onStartDiff);
8628       Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_HIGHPRI_V2,
8629                             onStopDiff);
8630     } else {
8631       Telemetry::Accumulate(
8632           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_HIGHPRI_V2, onStartDiff);
8633       Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_HIGHPRI_V2,
8634                             onStopDiff);
8635     }
8636   } else {  // The limits are higher for normal priority cache queues
8637     if (mCacheQueueSizeWhenOpen < 10) {
8638       Telemetry::Accumulate(
8639           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_NORMALPRI_V2,
8640           onStartDiff);
8641       Telemetry::Accumulate(
8642           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_NORMALPRI_V2, onStopDiff);
8643     } else if (mCacheQueueSizeWhenOpen < 50) {
8644       Telemetry::Accumulate(
8645           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_NORMALPRI_V2, onStartDiff);
8646       Telemetry::Accumulate(
8647           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_NORMALPRI_V2, onStopDiff);
8648     } else {
8649       Telemetry::Accumulate(
8650           Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_NORMALPRI_V2, onStartDiff);
8651       Telemetry::Accumulate(
8652           Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_NORMALPRI_V2, onStopDiff);
8653     }
8654   }
8655 
8656   uint32_t diskStorageSizeK = 0;
8657   rv = mCacheEntry->GetDiskStorageSizeInKB(&diskStorageSizeK);
8658   if (NS_FAILED(rv)) {
8659     return;
8660   }
8661 
8662   // No significant difference was observed between different sizes for
8663   // |onStartDiff|
8664   if (diskStorageSizeK < 256) {
8665     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_SMALL_V2,
8666                           onStopDiff);
8667   } else {
8668     Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_LARGE_V2,
8669                           onStopDiff);
8670   }
8671 }
8672 
8673 NS_IMETHODIMP
Test_delayCacheEntryOpeningBy(int32_t aTimeout)8674 nsHttpChannel::Test_delayCacheEntryOpeningBy(int32_t aTimeout) {
8675   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
8676   mCacheOpenDelay = aTimeout;
8677   return NS_OK;
8678 }
8679 
8680 NS_IMETHODIMP
Test_triggerDelayedOpenCacheEntry()8681 nsHttpChannel::Test_triggerDelayedOpenCacheEntry() {
8682   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
8683   nsresult rv;
8684   if (!mCacheOpenDelay) {
8685     // No delay was set.
8686     return NS_ERROR_NOT_AVAILABLE;
8687   }
8688   if (!mCacheOpenFunc) {
8689     // There should be a runnable.
8690     return NS_ERROR_FAILURE;
8691   }
8692   if (mCacheOpenTimer) {
8693     rv = mCacheOpenTimer->Cancel();
8694     if (NS_FAILED(rv)) {
8695       return rv;
8696     }
8697     mCacheOpenTimer = nullptr;
8698   }
8699   mCacheOpenDelay = 0;
8700   // Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it.
8701   std::function<void(nsHttpChannel *)> cacheOpenFunc = nullptr;
8702   std::swap(cacheOpenFunc, mCacheOpenFunc);
8703   cacheOpenFunc(this);
8704 
8705   return NS_OK;
8706 }
8707 
TriggerNetworkWithDelay(uint32_t aDelay)8708 nsresult nsHttpChannel::TriggerNetworkWithDelay(uint32_t aDelay) {
8709   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
8710 
8711   LOG(("nsHttpChannel::TriggerNetworkWithDelay [this=%p, delay=%u]\n", this,
8712        aDelay));
8713 
8714   if (mCanceled) {
8715     LOG(("  channel was canceled.\n"));
8716     return mStatus;
8717   }
8718 
8719   // If a network request has already gone out, there is no point in
8720   // doing this again.
8721   if (mNetworkTriggered) {
8722     LOG(("  network already triggered. Returning.\n"));
8723     return NS_OK;
8724   }
8725 
8726   if (!aDelay) {
8727     // We cannot call TriggerNetwork() directly here, because it would
8728     // cause performance regression in tp6 tests, see bug 1398847.
8729     return NS_DispatchToMainThread(
8730         NewRunnableMethod("net::nsHttpChannel::TriggerNetworkWithDelay", this,
8731                           &nsHttpChannel::TriggerNetwork),
8732         NS_DISPATCH_NORMAL);
8733   }
8734 
8735   if (!mNetworkTriggerTimer) {
8736     mNetworkTriggerTimer = NS_NewTimer();
8737   }
8738   mNetworkTriggerTimer->InitWithCallback(this, aDelay, nsITimer::TYPE_ONE_SHOT);
8739   return NS_OK;
8740 }
8741 
TriggerNetwork()8742 nsresult nsHttpChannel::TriggerNetwork() {
8743   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
8744 
8745   LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n", this));
8746 
8747   if (mCanceled) {
8748     LOG(("  channel was canceled.\n"));
8749     return mStatus;
8750   }
8751 
8752   // If a network request has already gone out, there is no point in
8753   // doing this again.
8754   if (mNetworkTriggered) {
8755     LOG(("  network already triggered. Returning.\n"));
8756     return NS_OK;
8757   }
8758 
8759   mNetworkTriggered = true;
8760   if (mNetworkTriggerTimer) {
8761     mNetworkTriggerTimer->Cancel();
8762     mNetworkTriggerTimer = nullptr;
8763   }
8764 
8765   // If we are waiting for a proxy request, that means we can't trigger
8766   // the next step just yet. We need for mConnectionInfo to be non-null
8767   // before we call ContinueConnect. OnProxyAvailable will trigger
8768   // BeginConnect, and Connect will call ContinueConnect even if it's
8769   // for the cache callbacks.
8770   if (mProxyRequest) {
8771     LOG(("  proxy request in progress. Delaying network trigger.\n"));
8772     mWaitingForProxy = true;
8773     return NS_OK;
8774   }
8775 
8776   if (AwaitingCacheCallbacks()) {
8777     mRaceCacheWithNetwork = sRCWNEnabled;
8778   }
8779 
8780   LOG(("  triggering network\n"));
8781   return ContinueConnect();
8782 }
8783 
MaybeRaceCacheWithNetwork()8784 nsresult nsHttpChannel::MaybeRaceCacheWithNetwork() {
8785   // Don't trigger the network if the load flags say so.
8786   if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
8787     return NS_OK;
8788   }
8789 
8790   // We must not race if the channel has a failure status code.
8791   if (NS_FAILED(mStatus)) {
8792     return NS_OK;
8793   }
8794 
8795   // If a CORS Preflight is required we must not race.
8796   if (mRequireCORSPreflight && !mIsCorsPreflightDone) {
8797     return NS_OK;
8798   }
8799 
8800   if (CacheFileUtils::CachePerfStats::IsCacheSlow()) {
8801     // If the cache is slow, trigger the network request immediately.
8802     mRaceDelay = 0;
8803   } else {
8804     // Give cache a headstart of 3 times the average cache entry open time.
8805     mRaceDelay = CacheFileUtils::CachePerfStats::GetAverage(
8806                      CacheFileUtils::CachePerfStats::ENTRY_OPEN, true) *
8807                  3;
8808     // We use microseconds in CachePerfStats but we need milliseconds
8809     // for TriggerNetwork.
8810     mRaceDelay /= 1000;
8811   }
8812 
8813   mRaceDelay = clamped<uint32_t>(mRaceDelay, sRCWNMinWaitMs, sRCWNMaxWaitMs);
8814 
8815   MOZ_ASSERT(sRCWNEnabled, "The pref must be turned on.");
8816   LOG(("nsHttpChannel::MaybeRaceCacheWithNetwork [this=%p, delay=%u]\n", this,
8817        mRaceDelay));
8818 
8819   return TriggerNetworkWithDelay(mRaceDelay);
8820 }
8821 
8822 NS_IMETHODIMP
Test_triggerNetwork(int32_t aTimeout)8823 nsHttpChannel::Test_triggerNetwork(int32_t aTimeout) {
8824   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
8825   return TriggerNetworkWithDelay(aTimeout);
8826 }
8827 
8828 NS_IMETHODIMP
Notify(nsITimer * aTimer)8829 nsHttpChannel::Notify(nsITimer *aTimer) {
8830   RefPtr<nsHttpChannel> self(this);
8831   if (aTimer == mCacheOpenTimer) {
8832     return Test_triggerDelayedOpenCacheEntry();
8833   } else if (aTimer == mNetworkTriggerTimer) {
8834     return TriggerNetwork();
8835   } else {
8836     MOZ_CRASH("Unknown timer");
8837   }
8838 
8839   return NS_OK;
8840 }
8841 
EligibleForTailing()8842 bool nsHttpChannel::EligibleForTailing() {
8843   if (!(mClassOfService & nsIClassOfService::Tail)) {
8844     return false;
8845   }
8846 
8847   if (mClassOfService &
8848       (nsIClassOfService::UrgentStart | nsIClassOfService::Leader |
8849        nsIClassOfService::TailForbidden)) {
8850     return false;
8851   }
8852 
8853   if (mClassOfService & nsIClassOfService::Unblocked &&
8854       !(mClassOfService & nsIClassOfService::TailAllowed)) {
8855     return false;
8856   }
8857 
8858   if (IsNavigation()) {
8859     return false;
8860   }
8861 
8862   return true;
8863 }
8864 
WaitingForTailUnblock()8865 bool nsHttpChannel::WaitingForTailUnblock() {
8866   nsresult rv;
8867 
8868   if (!gHttpHandler->IsTailBlockingEnabled()) {
8869     LOG(("nsHttpChannel %p tail-blocking disabled", this));
8870     return false;
8871   }
8872 
8873   if (!EligibleForTailing()) {
8874     LOG(("nsHttpChannel %p not eligible for tail-blocking", this));
8875     AddAsNonTailRequest();
8876     return false;
8877   }
8878 
8879   if (!EnsureRequestContext()) {
8880     LOG(("nsHttpChannel %p no request context", this));
8881     return false;
8882   }
8883 
8884   LOG(("nsHttpChannel::WaitingForTailUnblock this=%p, rc=%p", this,
8885        mRequestContext.get()));
8886 
8887   bool blocked;
8888   rv = mRequestContext->IsContextTailBlocked(this, &blocked);
8889   if (NS_FAILED(rv)) {
8890     return false;
8891   }
8892 
8893   LOG(("  blocked=%d", blocked));
8894 
8895   return blocked;
8896 }
8897 
8898 //-----------------------------------------------------------------------------
8899 // nsHttpChannel::nsIRequestTailUnblockCallback
8900 //-----------------------------------------------------------------------------
8901 
8902 // Must be implemented in the leaf class because we don't have
8903 // AsyncAbort in HttpBaseChannel.
8904 NS_IMETHODIMP
OnTailUnblock(nsresult rv)8905 nsHttpChannel::OnTailUnblock(nsresult rv) {
8906   LOG(("nsHttpChannel::OnTailUnblock this=%p rv=%" PRIx32 " rc=%p", this,
8907        static_cast<uint32_t>(rv), mRequestContext.get()));
8908 
8909   MOZ_RELEASE_ASSERT(mOnTailUnblock);
8910 
8911   if (NS_FAILED(mStatus)) {
8912     rv = mStatus;
8913   }
8914 
8915   if (NS_SUCCEEDED(rv)) {
8916     auto callback = mOnTailUnblock;
8917     mOnTailUnblock = nullptr;
8918     rv = (this->*callback)();
8919   }
8920 
8921   if (NS_FAILED(rv)) {
8922     CloseCacheEntry(false);
8923     return AsyncAbort(rv);
8924   }
8925 
8926   return NS_OK;
8927 }
8928 
SetWarningReporter(HttpChannelSecurityWarningReporter * aReporter)8929 void nsHttpChannel::SetWarningReporter(
8930     HttpChannelSecurityWarningReporter *aReporter) {
8931   LOG(("nsHttpChannel [this=%p] SetWarningReporter [%p]", this, aReporter));
8932   mWarningReporter = aReporter;
8933 }
8934 
GetWarningReporter()8935 HttpChannelSecurityWarningReporter *nsHttpChannel::GetWarningReporter() {
8936   LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this,
8937        mWarningReporter.get()));
8938   return mWarningReporter.get();
8939 }
8940 
RedirectToInterceptedChannel()8941 nsresult nsHttpChannel::RedirectToInterceptedChannel() {
8942   nsCOMPtr<nsINetworkInterceptController> controller;
8943   GetCallback(controller);
8944 
8945   RefPtr<InterceptedHttpChannel> intercepted =
8946       InterceptedHttpChannel::CreateForInterception(
8947           mChannelCreationTime, mChannelCreationTimestamp, mAsyncOpenTime);
8948 
8949   nsresult rv = intercepted->Init(mURI, mCaps,
8950                                   static_cast<nsProxyInfo *>(mProxyInfo.get()),
8951                                   mProxyResolveFlags, mProxyURI, mChannelId);
8952 
8953   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
8954       CloneLoadInfoForRedirect(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
8955   intercepted->SetLoadInfo(redirectLoadInfo);
8956 
8957   rv = SetupReplacementChannel(mURI, intercepted, true,
8958                                nsIChannelEventSink::REDIRECT_INTERNAL);
8959   NS_ENSURE_SUCCESS(rv, rv);
8960 
8961   mRedirectChannel = intercepted;
8962 
8963   PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
8964 
8965   rv = gHttpHandler->AsyncOnChannelRedirect(
8966       this, intercepted, nsIChannelEventSink::REDIRECT_INTERNAL);
8967 
8968   if (NS_SUCCEEDED(rv)) {
8969     rv = WaitForRedirectCallback();
8970   }
8971 
8972   if (NS_FAILED(rv)) {
8973     AutoRedirectVetoNotifier notifier(this);
8974 
8975     PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
8976   }
8977 
8978   return rv;
8979 }
8980 
8981 }  // namespace net
8982 }  // namespace mozilla
8983