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