1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/nsHttpChannel.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,6277 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:set expandtab ts=4 sw=4 sts=4 cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +#include "nsHttp.h" 1.14 +#include "nsHttpChannel.h" 1.15 +#include "nsHttpHandler.h" 1.16 +#include "nsIApplicationCacheService.h" 1.17 +#include "nsIApplicationCacheContainer.h" 1.18 +#include "nsICacheStorageService.h" 1.19 +#include "nsICacheStorage.h" 1.20 +#include "nsICacheEntry.h" 1.21 +#include "nsICryptoHash.h" 1.22 +#include "nsIStringBundle.h" 1.23 +#include "nsIStreamListenerTee.h" 1.24 +#include "nsISeekableStream.h" 1.25 +#include "nsILoadGroupChild.h" 1.26 +#include "nsIProtocolProxyService2.h" 1.27 +#include "nsMimeTypes.h" 1.28 +#include "nsNetUtil.h" 1.29 +#include "prprf.h" 1.30 +#include "prnetdb.h" 1.31 +#include "nsEscape.h" 1.32 +#include "nsStreamUtils.h" 1.33 +#include "nsIOService.h" 1.34 +#include "nsDNSPrefetch.h" 1.35 +#include "nsChannelClassifier.h" 1.36 +#include "nsIRedirectResultListener.h" 1.37 +#include "mozilla/TimeStamp.h" 1.38 +#include "nsError.h" 1.39 +#include "nsPrintfCString.h" 1.40 +#include "nsAlgorithm.h" 1.41 +#include "GeckoProfiler.h" 1.42 +#include "nsIConsoleService.h" 1.43 +#include "mozilla/Attributes.h" 1.44 +#include "mozilla/VisualEventTracer.h" 1.45 +#include "nsISSLSocketControl.h" 1.46 +#include "sslt.h" 1.47 +#include "nsContentUtils.h" 1.48 +#include "nsIPermissionManager.h" 1.49 +#include "nsIPrincipal.h" 1.50 +#include "nsIScriptSecurityManager.h" 1.51 +#include "nsISSLStatus.h" 1.52 +#include "nsISSLStatusProvider.h" 1.53 +#include "LoadContextInfo.h" 1.54 +#include "netCore.h" 1.55 +#include "nsHttpTransaction.h" 1.56 +#include "nsICacheEntryDescriptor.h" 1.57 +#include "nsICancelable.h" 1.58 +#include "nsIHttpChannelAuthProvider.h" 1.59 +#include "nsIHttpEventSink.h" 1.60 +#include "nsIPrompt.h" 1.61 +#include "nsInputStreamPump.h" 1.62 +#include "nsURLHelper.h" 1.63 +#include "nsISocketTransport.h" 1.64 +#include "nsICacheSession.h" 1.65 +#include "nsIStreamConverterService.h" 1.66 +#include "nsISiteSecurityService.h" 1.67 +#include "nsCRT.h" 1.68 +#include "nsPIDOMWindow.h" 1.69 +#include "nsPerformance.h" 1.70 +#include "CacheObserver.h" 1.71 +#include "mozilla/Telemetry.h" 1.72 +#include "mozIThirdPartyUtil.h" 1.73 + 1.74 +namespace mozilla { namespace net { 1.75 + 1.76 +namespace { 1.77 + 1.78 +// True if the local cache should be bypassed when processing a request. 1.79 +#define BYPASS_LOCAL_CACHE(loadFlags) \ 1.80 + (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \ 1.81 + nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE)) 1.82 + 1.83 +#define CACHE_FILE_GONE(result) \ 1.84 + ((result) == NS_ERROR_FILE_NOT_FOUND || \ 1.85 + (result) == NS_ERROR_FILE_CORRUPTED) 1.86 + 1.87 +static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); 1.88 +static NS_DEFINE_CID(kStreamTransportServiceCID, 1.89 + NS_STREAMTRANSPORTSERVICE_CID); 1.90 + 1.91 +enum CacheDisposition { 1.92 + kCacheHit = 1, 1.93 + kCacheHitViaReval = 2, 1.94 + kCacheMissedViaReval = 3, 1.95 + kCacheMissed = 4 1.96 +}; 1.97 + 1.98 +void 1.99 +AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss) 1.100 +{ 1.101 + if (!CacheObserver::UseNewCache()) { 1.102 + Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2, hitOrMiss); 1.103 + } 1.104 + else { 1.105 + Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2, hitOrMiss); 1.106 + 1.107 + int32_t experiment = CacheObserver::HalfLifeExperiment(); 1.108 + if (experiment > 0 && hitOrMiss == kCacheMissed) { 1.109 + Telemetry::Accumulate(Telemetry::HTTP_CACHE_MISS_HALFLIFE_EXPERIMENT, 1.110 + experiment - 1); 1.111 + } 1.112 + } 1.113 +} 1.114 + 1.115 +// Computes and returns a SHA1 hash of the input buffer. The input buffer 1.116 +// must be a null-terminated string. 1.117 +nsresult 1.118 +Hash(const char *buf, nsACString &hash) 1.119 +{ 1.120 + nsresult rv; 1.121 + 1.122 + nsCOMPtr<nsICryptoHash> hasher 1.123 + = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 1.124 + NS_ENSURE_SUCCESS(rv, rv); 1.125 + 1.126 + rv = hasher->Init(nsICryptoHash::SHA1); 1.127 + NS_ENSURE_SUCCESS(rv, rv); 1.128 + 1.129 + rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf), 1.130 + strlen(buf)); 1.131 + NS_ENSURE_SUCCESS(rv, rv); 1.132 + 1.133 + rv = hasher->Finish(true, hash); 1.134 + NS_ENSURE_SUCCESS(rv, rv); 1.135 + 1.136 + return NS_OK; 1.137 +} 1.138 + 1.139 +bool IsRedirectStatus(uint32_t status) 1.140 +{ 1.141 + // 305 disabled as a security measure (see bug 187996). 1.142 + return status == 300 || status == 301 || status == 302 || status == 303 || 1.143 + status == 307 || status == 308; 1.144 +} 1.145 + 1.146 +// We only treat 3xx responses as redirects if they have a Location header and 1.147 +// the status code is in a whitelist. 1.148 +bool 1.149 +WillRedirect(const nsHttpResponseHead * response) 1.150 +{ 1.151 + return IsRedirectStatus(response->Status()) && 1.152 + response->PeekHeader(nsHttp::Location); 1.153 +} 1.154 + 1.155 +} // unnamed namespace 1.156 + 1.157 +class AutoRedirectVetoNotifier 1.158 +{ 1.159 +public: 1.160 + AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel) 1.161 + { 1.162 + if (mChannel->mHasAutoRedirectVetoNotifier) { 1.163 + MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack"); 1.164 + mChannel = nullptr; 1.165 + return; 1.166 + } 1.167 + 1.168 + mChannel->mHasAutoRedirectVetoNotifier = true; 1.169 + } 1.170 + ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);} 1.171 + void RedirectSucceeded() {ReportRedirectResult(true);} 1.172 + 1.173 +private: 1.174 + nsHttpChannel* mChannel; 1.175 + void ReportRedirectResult(bool succeeded); 1.176 +}; 1.177 + 1.178 +void 1.179 +AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded) 1.180 +{ 1.181 + if (!mChannel) 1.182 + return; 1.183 + 1.184 + mChannel->mRedirectChannel = nullptr; 1.185 + 1.186 + nsCOMPtr<nsIRedirectResultListener> vetoHook; 1.187 + NS_QueryNotificationCallbacks(mChannel, 1.188 + NS_GET_IID(nsIRedirectResultListener), 1.189 + getter_AddRefs(vetoHook)); 1.190 + 1.191 + nsHttpChannel* channel = mChannel; 1.192 + mChannel = nullptr; 1.193 + 1.194 + if (vetoHook) 1.195 + vetoHook->OnRedirectResult(succeeded); 1.196 + 1.197 + // Drop after the notification 1.198 + channel->mHasAutoRedirectVetoNotifier = false; 1.199 + 1.200 + MOZ_EVENT_TRACER_DONE(channel, "net::http::redirect-callbacks"); 1.201 +} 1.202 + 1.203 +//----------------------------------------------------------------------------- 1.204 +// nsHttpChannel <public> 1.205 +//----------------------------------------------------------------------------- 1.206 + 1.207 +nsHttpChannel::nsHttpChannel() 1.208 + : HttpAsyncAborter<nsHttpChannel>(MOZ_THIS_IN_INITIALIZER_LIST()) 1.209 + , mLogicalOffset(0) 1.210 + , mPostID(0) 1.211 + , mRequestTime(0) 1.212 + , mOfflineCacheLastModifiedTime(0) 1.213 + , mCachedContentIsValid(false) 1.214 + , mCachedContentIsPartial(false) 1.215 + , mTransactionReplaced(false) 1.216 + , mAuthRetryPending(false) 1.217 + , mProxyAuthPending(false) 1.218 + , mResuming(false) 1.219 + , mInitedCacheEntry(false) 1.220 + , mFallbackChannel(false) 1.221 + , mCustomConditionalRequest(false) 1.222 + , mFallingBack(false) 1.223 + , mWaitingForRedirectCallback(false) 1.224 + , mRequestTimeInitialized(false) 1.225 + , mCacheEntryIsReadOnly(false) 1.226 + , mCacheEntryIsWriteOnly(false) 1.227 + , mCacheEntriesToWaitFor(0) 1.228 + , mHasQueryString(0) 1.229 + , mConcurentCacheAccess(0) 1.230 + , mIsPartialRequest(0) 1.231 + , mHasAutoRedirectVetoNotifier(0) 1.232 + , mDidReval(false) 1.233 + , mForcePending(false) 1.234 +{ 1.235 + LOG(("Creating nsHttpChannel [this=%p]\n", this)); 1.236 + mChannelCreationTime = PR_Now(); 1.237 + mChannelCreationTimestamp = TimeStamp::Now(); 1.238 +} 1.239 + 1.240 +nsHttpChannel::~nsHttpChannel() 1.241 +{ 1.242 + LOG(("Destroying nsHttpChannel [this=%p]\n", this)); 1.243 + 1.244 + if (mAuthProvider) 1.245 + mAuthProvider->Disconnect(NS_ERROR_ABORT); 1.246 +} 1.247 + 1.248 +nsresult 1.249 +nsHttpChannel::Init(nsIURI *uri, 1.250 + uint32_t caps, 1.251 + nsProxyInfo *proxyInfo, 1.252 + uint32_t proxyResolveFlags, 1.253 + nsIURI *proxyURI) 1.254 +{ 1.255 +#ifdef MOZ_VISUAL_EVENT_TRACER 1.256 + nsAutoCString url; 1.257 + uri->GetAsciiSpec(url); 1.258 + MOZ_EVENT_TRACER_NAME_OBJECT(this, url.get()); 1.259 +#endif 1.260 + 1.261 + nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, 1.262 + proxyResolveFlags, proxyURI); 1.263 + if (NS_FAILED(rv)) 1.264 + return rv; 1.265 + 1.266 + LOG(("nsHttpChannel::Init [this=%p]\n", this)); 1.267 + 1.268 + return rv; 1.269 +} 1.270 +//----------------------------------------------------------------------------- 1.271 +// nsHttpChannel <private> 1.272 +//----------------------------------------------------------------------------- 1.273 + 1.274 +nsresult 1.275 +nsHttpChannel::Connect() 1.276 +{ 1.277 + nsresult rv; 1.278 + 1.279 + LOG(("nsHttpChannel::Connect [this=%p]\n", this)); 1.280 + 1.281 + // Even if we're in private browsing mode, we still enforce existing STS 1.282 + // data (it is read-only). 1.283 + // if the connection is not using SSL and either the exact host matches or 1.284 + // a superdomain wants to force HTTPS, do it. 1.285 + bool usingSSL = false; 1.286 + rv = mURI->SchemeIs("https", &usingSSL); 1.287 + NS_ENSURE_SUCCESS(rv,rv); 1.288 + 1.289 + if (!usingSSL) { 1.290 + // enforce Strict-Transport-Security 1.291 + nsISiteSecurityService* sss = gHttpHandler->GetSSService(); 1.292 + NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); 1.293 + 1.294 + bool isStsHost = false; 1.295 + uint32_t flags = mPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; 1.296 + rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags, 1.297 + &isStsHost); 1.298 + 1.299 + // if the SSS check fails, it's likely because this load is on a 1.300 + // malformed URI or something else in the setup is wrong, so any error 1.301 + // should be reported. 1.302 + NS_ENSURE_SUCCESS(rv, rv); 1.303 + 1.304 + if (isStsHost) { 1.305 + LOG(("nsHttpChannel::Connect() STS permissions found\n")); 1.306 + return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps); 1.307 + } 1.308 + } 1.309 + 1.310 + // ensure that we are using a valid hostname 1.311 + if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host()))) 1.312 + return NS_ERROR_UNKNOWN_HOST; 1.313 + 1.314 + // Finalize ConnectionInfo flags before SpeculativeConnect 1.315 + mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0); 1.316 + mConnectionInfo->SetPrivate(mPrivateBrowsing); 1.317 + 1.318 + // Consider opening a TCP connection right away 1.319 + RetrieveSSLOptions(); 1.320 + SpeculativeConnect(); 1.321 + 1.322 + // Don't allow resuming when cache must be used 1.323 + if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) { 1.324 + LOG(("Resuming from cache is not supported yet")); 1.325 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.326 + } 1.327 + 1.328 + if (!gHttpHandler->UseCache()) { 1.329 + return ContinueConnect(); 1.330 + } 1.331 + 1.332 + // open a cache entry for this channel... 1.333 + rv = OpenCacheEntry(usingSSL); 1.334 + 1.335 + // do not continue if asyncOpenCacheEntry is in progress 1.336 + if (mCacheEntriesToWaitFor) { 1.337 + MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state"); 1.338 + return NS_OK; 1.339 + } 1.340 + 1.341 + if (NS_FAILED(rv)) { 1.342 + LOG(("OpenCacheEntry failed [rv=%x]\n", rv)); 1.343 + // if this channel is only allowed to pull from the cache, then 1.344 + // we must fail if we were unable to open a cache entry. 1.345 + if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1.346 + // If we have a fallback URI (and we're not already 1.347 + // falling back), process the fallback asynchronously. 1.348 + if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { 1.349 + return AsyncCall(&nsHttpChannel::HandleAsyncFallback); 1.350 + } 1.351 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.352 + } 1.353 + // otherwise, let's just proceed without using the cache. 1.354 + } 1.355 + 1.356 + return ContinueConnect(); 1.357 +} 1.358 + 1.359 +nsresult 1.360 +nsHttpChannel::ContinueConnect() 1.361 +{ 1.362 + // we may or may not have a cache entry at this point 1.363 + if (mCacheEntry) { 1.364 + // read straight from the cache if possible... 1.365 + if (mCachedContentIsValid) { 1.366 + nsRunnableMethod<nsHttpChannel> *event = nullptr; 1.367 + if (!mCachedContentIsPartial) { 1.368 + AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event); 1.369 + } 1.370 + nsresult rv = ReadFromCache(true); 1.371 + if (NS_FAILED(rv) && event) { 1.372 + event->Revoke(); 1.373 + } 1.374 + 1.375 + AccumulateCacheHitTelemetry(kCacheHit); 1.376 + 1.377 + return rv; 1.378 + } 1.379 + else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1.380 + // the cache contains the requested resource, but it must be 1.381 + // validated before we can reuse it. since we are not allowed 1.382 + // to hit the net, there's nothing more to do. the document 1.383 + // is effectively not in the cache. 1.384 + LOG((" !mCachedContentIsValid && mLoadFlags & LOAD_ONLY_FROM_CACHE")); 1.385 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.386 + } 1.387 + } 1.388 + else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1.389 + // If we have a fallback URI (and we're not already 1.390 + // falling back), process the fallback asynchronously. 1.391 + if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { 1.392 + return AsyncCall(&nsHttpChannel::HandleAsyncFallback); 1.393 + } 1.394 + LOG((" !mCachedEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE")); 1.395 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.396 + } 1.397 + 1.398 + if (mLoadFlags & LOAD_NO_NETWORK_IO) { 1.399 + LOG((" mLoadFlags & LOAD_NO_NETWORK_IO")); 1.400 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.401 + } 1.402 + 1.403 + // hit the net... 1.404 + nsresult rv = SetupTransaction(); 1.405 + if (NS_FAILED(rv)) return rv; 1.406 + 1.407 + rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); 1.408 + if (NS_FAILED(rv)) return rv; 1.409 + 1.410 + rv = mTransactionPump->AsyncRead(this, nullptr); 1.411 + if (NS_FAILED(rv)) return rv; 1.412 + 1.413 + uint32_t suspendCount = mSuspendCount; 1.414 + while (suspendCount--) 1.415 + mTransactionPump->Suspend(); 1.416 + 1.417 + return NS_OK; 1.418 +} 1.419 + 1.420 +void 1.421 +nsHttpChannel::SpeculativeConnect() 1.422 +{ 1.423 + // Before we take the latency hit of dealing with the cache, try and 1.424 + // get the TCP (and SSL) handshakes going so they can overlap. 1.425 + 1.426 + // don't speculate on uses of the offline application cache, 1.427 + // if we are offline, when doing http upgrade (i.e. websockets bootstrap), 1.428 + // or if we can't do keep-alive (because then we couldn't reuse 1.429 + // the speculative connection anyhow). 1.430 + if (mApplicationCache || gIOService->IsOffline() || 1.431 + mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE)) 1.432 + return; 1.433 + 1.434 + // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network. 1.435 + // LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network, 1.436 + // so skip preconnects for them. 1.437 + if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE | 1.438 + LOAD_NO_NETWORK_IO | LOAD_CHECK_OFFLINE_CACHE)) 1.439 + return; 1.440 + 1.441 + nsCOMPtr<nsIInterfaceRequestor> callbacks; 1.442 + NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 1.443 + getter_AddRefs(callbacks)); 1.444 + if (!callbacks) 1.445 + return; 1.446 + 1.447 + gHttpHandler->SpeculativeConnect( 1.448 + mConnectionInfo, callbacks, 1.449 + mCaps & (NS_HTTP_ALLOW_RSA_FALSESTART | NS_HTTP_DISALLOW_SPDY)); 1.450 +} 1.451 + 1.452 +void 1.453 +nsHttpChannel::DoNotifyListenerCleanup() 1.454 +{ 1.455 + // We don't need this info anymore 1.456 + CleanRedirectCacheChainIfNecessary(); 1.457 +} 1.458 + 1.459 +void 1.460 +nsHttpChannel::HandleAsyncRedirect() 1.461 +{ 1.462 + NS_PRECONDITION(!mCallOnResume, "How did that happen?"); 1.463 + 1.464 + if (mSuspendCount) { 1.465 + LOG(("Waiting until resume to do async redirect [this=%p]\n", this)); 1.466 + mCallOnResume = &nsHttpChannel::HandleAsyncRedirect; 1.467 + return; 1.468 + } 1.469 + 1.470 + nsresult rv = NS_OK; 1.471 + 1.472 + LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this)); 1.473 + 1.474 + // since this event is handled asynchronously, it is possible that this 1.475 + // channel could have been canceled, in which case there would be no point 1.476 + // in processing the redirect. 1.477 + if (NS_SUCCEEDED(mStatus)) { 1.478 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect); 1.479 + rv = AsyncProcessRedirection(mResponseHead->Status()); 1.480 + if (NS_FAILED(rv)) { 1.481 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect); 1.482 + // TODO: if !DoNotRender3xxBody(), render redirect body instead. 1.483 + // But first we need to cache 3xx bodies (bug 748510) 1.484 + ContinueHandleAsyncRedirect(rv); 1.485 + } 1.486 + } 1.487 + else { 1.488 + ContinueHandleAsyncRedirect(NS_OK); 1.489 + } 1.490 +} 1.491 + 1.492 +nsresult 1.493 +nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) 1.494 +{ 1.495 + if (NS_FAILED(rv)) { 1.496 + // If AsyncProcessRedirection fails, then we have to send out the 1.497 + // OnStart/OnStop notifications. 1.498 + LOG(("ContinueHandleAsyncRedirect got failure result [rv=%x]\n", rv)); 1.499 + mStatus = rv; 1.500 + DoNotifyListener(); 1.501 + } 1.502 + 1.503 + // close the cache entry. Blow it away if we couldn't process the redirect 1.504 + // for some reason (the cache entry might be corrupt). 1.505 + if (mCacheEntry) { 1.506 + if (NS_FAILED(rv)) 1.507 + mCacheEntry->AsyncDoom(nullptr); 1.508 + } 1.509 + CloseCacheEntry(false); 1.510 + 1.511 + mIsPending = false; 1.512 + 1.513 + if (mLoadGroup) 1.514 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.515 + 1.516 + return NS_OK; 1.517 +} 1.518 + 1.519 +void 1.520 +nsHttpChannel::HandleAsyncNotModified() 1.521 +{ 1.522 + NS_PRECONDITION(!mCallOnResume, "How did that happen?"); 1.523 + 1.524 + if (mSuspendCount) { 1.525 + LOG(("Waiting until resume to do async not-modified [this=%p]\n", 1.526 + this)); 1.527 + mCallOnResume = &nsHttpChannel::HandleAsyncNotModified; 1.528 + return; 1.529 + } 1.530 + 1.531 + LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this)); 1.532 + 1.533 + DoNotifyListener(); 1.534 + 1.535 + CloseCacheEntry(true); 1.536 + 1.537 + mIsPending = false; 1.538 + 1.539 + if (mLoadGroup) 1.540 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.541 +} 1.542 + 1.543 +void 1.544 +nsHttpChannel::HandleAsyncFallback() 1.545 +{ 1.546 + NS_PRECONDITION(!mCallOnResume, "How did that happen?"); 1.547 + 1.548 + if (mSuspendCount) { 1.549 + LOG(("Waiting until resume to do async fallback [this=%p]\n", this)); 1.550 + mCallOnResume = &nsHttpChannel::HandleAsyncFallback; 1.551 + return; 1.552 + } 1.553 + 1.554 + nsresult rv = NS_OK; 1.555 + 1.556 + LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n", this)); 1.557 + 1.558 + // since this event is handled asynchronously, it is possible that this 1.559 + // channel could have been canceled, in which case there would be no point 1.560 + // in processing the fallback. 1.561 + if (!mCanceled) { 1.562 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback); 1.563 + bool waitingForRedirectCallback; 1.564 + rv = ProcessFallback(&waitingForRedirectCallback); 1.565 + if (waitingForRedirectCallback) 1.566 + return; 1.567 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback); 1.568 + } 1.569 + 1.570 + ContinueHandleAsyncFallback(rv); 1.571 +} 1.572 + 1.573 +nsresult 1.574 +nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv) 1.575 +{ 1.576 + if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) { 1.577 + // If ProcessFallback fails, then we have to send out the 1.578 + // OnStart/OnStop notifications. 1.579 + LOG(("ProcessFallback failed [rv=%x, %d]\n", rv, mFallingBack)); 1.580 + mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED; 1.581 + DoNotifyListener(); 1.582 + } 1.583 + 1.584 + mIsPending = false; 1.585 + 1.586 + if (mLoadGroup) 1.587 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.588 + 1.589 + return rv; 1.590 +} 1.591 + 1.592 +void 1.593 +nsHttpChannel::SetupTransactionLoadGroupInfo() 1.594 +{ 1.595 + // Find the loadgroup at the end of the chain in order 1.596 + // to make sure all channels derived from the load group 1.597 + // use the same connection scope. 1.598 + nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(mLoadGroup); 1.599 + if (!childLoadGroup) 1.600 + return; 1.601 + 1.602 + nsCOMPtr<nsILoadGroup> rootLoadGroup; 1.603 + childLoadGroup->GetRootLoadGroup(getter_AddRefs(rootLoadGroup)); 1.604 + if (!rootLoadGroup) 1.605 + return; 1.606 + 1.607 + // Set the load group connection scope on the transaction 1.608 + nsCOMPtr<nsILoadGroupConnectionInfo> ci; 1.609 + rootLoadGroup->GetConnectionInfo(getter_AddRefs(ci)); 1.610 + if (ci) 1.611 + mTransaction->SetLoadGroupConnectionInfo(ci); 1.612 +} 1.613 + 1.614 +void 1.615 +nsHttpChannel::RetrieveSSLOptions() 1.616 +{ 1.617 + if (!IsHTTPS() || mPrivateBrowsing) 1.618 + return; 1.619 + 1.620 + nsIPrincipal *principal = GetPrincipal(); 1.621 + if (!principal) 1.622 + return; 1.623 + 1.624 + nsCOMPtr<nsIPermissionManager> permMgr = 1.625 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.626 + if (!permMgr) 1.627 + return; 1.628 + 1.629 + uint32_t perm; 1.630 + nsresult rv = permMgr->TestPermissionFromPrincipal(principal, 1.631 + "falsestart-rsa", &perm); 1.632 + if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) { 1.633 + LOG(("nsHttpChannel::RetrieveSSLOptions [this=%p] " 1.634 + "falsestart-rsa permission found\n", this)); 1.635 + mCaps |= NS_HTTP_ALLOW_RSA_FALSESTART; 1.636 + } 1.637 +} 1.638 + 1.639 +static bool 1.640 +SafeForPipelining(nsHttpRequestHead::ParsedMethodType method, 1.641 + const nsCString &methodString) 1.642 +{ 1.643 + if (method == nsHttpRequestHead::kMethod_Get || 1.644 + method == nsHttpRequestHead::kMethod_Head || 1.645 + method == nsHttpRequestHead::kMethod_Options) { 1.646 + return true; 1.647 + } 1.648 + 1.649 + if (method != nsHttpRequestHead::kMethod_Custom) { 1.650 + return false; 1.651 + } 1.652 + 1.653 + return (!strcmp(methodString.get(), "PROPFIND") || 1.654 + !strcmp(methodString.get(), "PROPPATCH")); 1.655 +} 1.656 + 1.657 +nsresult 1.658 +nsHttpChannel::SetupTransaction() 1.659 +{ 1.660 + LOG(("nsHttpChannel::SetupTransaction [this=%p]\n", this)); 1.661 + 1.662 + NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED); 1.663 + 1.664 + nsresult rv; 1.665 + 1.666 + if (mCaps & NS_HTTP_ALLOW_PIPELINING) { 1.667 + // 1.668 + // disable pipelining if: 1.669 + // (1) pipelining has been disabled by config 1.670 + // (2) pipelining has been disabled by connection mgr info 1.671 + // (3) request corresponds to a top-level document load (link click) 1.672 + // (4) request method is non-idempotent 1.673 + // (5) request is marked slow (e.g XHR) 1.674 + // 1.675 + if (!mAllowPipelining || 1.676 + (mLoadFlags & (LOAD_INITIAL_DOCUMENT_URI | INHIBIT_PIPELINE)) || 1.677 + !SafeForPipelining(mRequestHead.ParsedMethod(), mRequestHead.Method())) { 1.678 + LOG((" pipelining disallowed\n")); 1.679 + mCaps &= ~NS_HTTP_ALLOW_PIPELINING; 1.680 + } 1.681 + } 1.682 + 1.683 + if (!mAllowSpdy) 1.684 + mCaps |= NS_HTTP_DISALLOW_SPDY; 1.685 + 1.686 + // Use the URI path if not proxying (transparent proxying such as proxy 1.687 + // CONNECT does not count here). Also figure out what HTTP version to use. 1.688 + nsAutoCString buf, path; 1.689 + nsCString* requestURI; 1.690 + if (mConnectionInfo->UsingConnect() || 1.691 + !mConnectionInfo->UsingHttpProxy()) { 1.692 + rv = mURI->GetPath(path); 1.693 + if (NS_FAILED(rv)) return rv; 1.694 + // path may contain UTF-8 characters, so ensure that they're escaped. 1.695 + if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf)) 1.696 + requestURI = &buf; 1.697 + else 1.698 + requestURI = &path; 1.699 + mRequestHead.SetVersion(gHttpHandler->HttpVersion()); 1.700 + } 1.701 + else { 1.702 + rv = mURI->GetUserPass(buf); 1.703 + if (NS_FAILED(rv)) return rv; 1.704 + if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) || 1.705 + strncmp(mSpec.get(), "https:", 6) == 0)) { 1.706 + nsCOMPtr<nsIURI> tempURI; 1.707 + rv = mURI->Clone(getter_AddRefs(tempURI)); 1.708 + if (NS_FAILED(rv)) return rv; 1.709 + rv = tempURI->SetUserPass(EmptyCString()); 1.710 + if (NS_FAILED(rv)) return rv; 1.711 + rv = tempURI->GetAsciiSpec(path); 1.712 + if (NS_FAILED(rv)) return rv; 1.713 + requestURI = &path; 1.714 + } 1.715 + else 1.716 + requestURI = &mSpec; 1.717 + mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion()); 1.718 + } 1.719 + 1.720 + // trim off the #ref portion if any... 1.721 + int32_t ref = requestURI->FindChar('#'); 1.722 + if (ref != kNotFound) 1.723 + requestURI->SetLength(ref); 1.724 + 1.725 + mRequestHead.SetRequestURI(*requestURI); 1.726 + 1.727 + // set the request time for cache expiration calculations 1.728 + mRequestTime = NowInSeconds(); 1.729 + mRequestTimeInitialized = true; 1.730 + 1.731 + // if doing a reload, force end-to-end 1.732 + if (mLoadFlags & LOAD_BYPASS_CACHE) { 1.733 + // We need to send 'Pragma:no-cache' to inhibit proxy caching even if 1.734 + // no proxy is configured since we might be talking with a transparent 1.735 + // proxy, i.e. one that operates at the network level. See bug #14772. 1.736 + mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); 1.737 + // If we're configured to speak HTTP/1.1 then also send 'Cache-control: 1.738 + // no-cache' 1.739 + if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1) 1.740 + mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true); 1.741 + } 1.742 + else if ((mLoadFlags & VALIDATE_ALWAYS) && !mCacheEntryIsWriteOnly) { 1.743 + // We need to send 'Cache-Control: max-age=0' to force each cache along 1.744 + // the path to the origin server to revalidate its own entry, if any, 1.745 + // with the next cache or server. See bug #84847. 1.746 + // 1.747 + // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache' 1.748 + if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1) 1.749 + mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true); 1.750 + else 1.751 + mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); 1.752 + } 1.753 + 1.754 + if (mResuming) { 1.755 + char byteRange[32]; 1.756 + PR_snprintf(byteRange, sizeof(byteRange), "bytes=%llu-", mStartPos); 1.757 + mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange)); 1.758 + 1.759 + if (!mEntityID.IsEmpty()) { 1.760 + // Also, we want an error if this resource changed in the meantime 1.761 + // Format of the entity id is: escaped_etag/size/lastmod 1.762 + nsCString::const_iterator start, end, slash; 1.763 + mEntityID.BeginReading(start); 1.764 + mEntityID.EndReading(end); 1.765 + mEntityID.BeginReading(slash); 1.766 + 1.767 + if (FindCharInReadable('/', slash, end)) { 1.768 + nsAutoCString ifMatch; 1.769 + mRequestHead.SetHeader(nsHttp::If_Match, 1.770 + NS_UnescapeURL(Substring(start, slash), 0, ifMatch)); 1.771 + 1.772 + ++slash; // Incrementing, so that searching for '/' won't find 1.773 + // the same slash again 1.774 + } 1.775 + 1.776 + if (FindCharInReadable('/', slash, end)) { 1.777 + mRequestHead.SetHeader(nsHttp::If_Unmodified_Since, 1.778 + Substring(++slash, end)); 1.779 + } 1.780 + } 1.781 + } 1.782 + 1.783 + // create wrapper for this channel's notification callbacks 1.784 + nsCOMPtr<nsIInterfaceRequestor> callbacks; 1.785 + NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 1.786 + getter_AddRefs(callbacks)); 1.787 + if (!callbacks) 1.788 + return NS_ERROR_OUT_OF_MEMORY; 1.789 + 1.790 + // create the transaction object 1.791 + mTransaction = new nsHttpTransaction(); 1.792 + if (!mTransaction) 1.793 + return NS_ERROR_OUT_OF_MEMORY; 1.794 + 1.795 + // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. 1.796 + if (mLoadFlags & LOAD_ANONYMOUS) 1.797 + mCaps |= NS_HTTP_LOAD_ANONYMOUS; 1.798 + 1.799 + if (mTimingEnabled) 1.800 + mCaps |= NS_HTTP_TIMING_ENABLED; 1.801 + 1.802 + if (mUpgradeProtocolCallback) { 1.803 + mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false); 1.804 + mRequestHead.SetHeaderOnce(nsHttp::Connection, 1.805 + nsHttp::Upgrade.get(), 1.806 + true); 1.807 + mCaps |= NS_HTTP_STICKY_CONNECTION; 1.808 + mCaps &= ~NS_HTTP_ALLOW_PIPELINING; 1.809 + mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE; 1.810 + mCaps |= NS_HTTP_DISALLOW_SPDY; 1.811 + } 1.812 + 1.813 + nsCOMPtr<nsIAsyncInputStream> responseStream; 1.814 + rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead, 1.815 + mUploadStream, mUploadStreamHasHeaders, 1.816 + NS_GetCurrentThread(), callbacks, this, 1.817 + getter_AddRefs(responseStream)); 1.818 + if (NS_FAILED(rv)) { 1.819 + mTransaction = nullptr; 1.820 + return rv; 1.821 + } 1.822 + 1.823 + SetupTransactionLoadGroupInfo(); 1.824 + 1.825 + rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump), 1.826 + responseStream); 1.827 + return rv; 1.828 +} 1.829 + 1.830 +// NOTE: This function duplicates code from nsBaseChannel. This will go away 1.831 +// once HTTP uses nsBaseChannel (part of bug 312760) 1.832 +static void 1.833 +CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount) 1.834 +{ 1.835 + nsIChannel *chan = static_cast<nsIChannel*>(aClosure); 1.836 + 1.837 + nsAutoCString newType; 1.838 + NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType); 1.839 + if (!newType.IsEmpty()) { 1.840 + chan->SetContentType(newType); 1.841 + } 1.842 +} 1.843 + 1.844 +nsresult 1.845 +nsHttpChannel::CallOnStartRequest() 1.846 +{ 1.847 + nsresult rv; 1.848 + 1.849 + mTracingEnabled = false; 1.850 + 1.851 + // Allow consumers to override our content type 1.852 + if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) { 1.853 + // NOTE: We can have both a txn pump and a cache pump when the cache 1.854 + // content is partial. In that case, we need to read from the cache, 1.855 + // because that's the one that has the initial contents. If that fails 1.856 + // then give the transaction pump a shot. 1.857 + 1.858 + nsIChannel* thisChannel = static_cast<nsIChannel*>(this); 1.859 + 1.860 + bool typeSniffersCalled = false; 1.861 + if (mCachePump) { 1.862 + typeSniffersCalled = 1.863 + NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel)); 1.864 + } 1.865 + 1.866 + if (!typeSniffersCalled && mTransactionPump) { 1.867 + mTransactionPump->PeekStream(CallTypeSniffers, thisChannel); 1.868 + } 1.869 + } 1.870 + 1.871 + bool shouldSniff = mResponseHead && (mResponseHead->ContentType().IsEmpty() || 1.872 + ((mResponseHead->ContentType().EqualsLiteral(APPLICATION_OCTET_STREAM) && 1.873 + (mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN)))); 1.874 + 1.875 + if (shouldSniff) { 1.876 + MOZ_ASSERT(mConnectionInfo, "Should have connection info here"); 1.877 + if (!mContentTypeHint.IsEmpty()) 1.878 + mResponseHead->SetContentType(mContentTypeHint); 1.879 + else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 && 1.880 + mConnectionInfo->Port() != mConnectionInfo->DefaultPort()) 1.881 + mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); 1.882 + else { 1.883 + // Uh-oh. We had better find out what type we are! 1.884 + 1.885 + // XXX This does not work with content-encodings... but 1.886 + // neither does applying the conversion from the URILoader 1.887 + 1.888 + nsCOMPtr<nsIStreamConverterService> serv; 1.889 + rv = gHttpHandler-> 1.890 + GetStreamConverterService(getter_AddRefs(serv)); 1.891 + // If we failed, we just fall through to the "normal" case 1.892 + if (NS_SUCCEEDED(rv)) { 1.893 + nsCOMPtr<nsIStreamListener> converter; 1.894 + rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, 1.895 + "*/*", 1.896 + mListener, 1.897 + mListenerContext, 1.898 + getter_AddRefs(converter)); 1.899 + if (NS_SUCCEEDED(rv)) { 1.900 + mListener = converter; 1.901 + } 1.902 + } 1.903 + } 1.904 + } 1.905 + 1.906 + if (mResponseHead && mResponseHead->ContentCharset().IsEmpty()) 1.907 + mResponseHead->SetContentCharset(mContentCharsetHint); 1.908 + 1.909 + if (mResponseHead && mCacheEntry) { 1.910 + // If we have a cache entry, set its predicted size to ContentLength to 1.911 + // avoid caching an entry that will exceed the max size limit. 1.912 + rv = mCacheEntry->SetPredictedDataSize( 1.913 + mResponseHead->ContentLength()); 1.914 + if (NS_ERROR_FILE_TOO_BIG == rv) { 1.915 + mCacheEntry = nullptr; 1.916 + LOG((" entry too big, throwing away")); 1.917 + } else { 1.918 + NS_ENSURE_SUCCESS(rv, rv); 1.919 + } 1.920 + } 1.921 + 1.922 + LOG((" calling mListener->OnStartRequest\n")); 1.923 + if (mListener) { 1.924 + rv = mListener->OnStartRequest(this, mListenerContext); 1.925 + if (NS_FAILED(rv)) 1.926 + return rv; 1.927 + } else { 1.928 + NS_WARNING("OnStartRequest skipped because of null listener"); 1.929 + } 1.930 + 1.931 + // install stream converter if required 1.932 + rv = ApplyContentConversions(); 1.933 + if (NS_FAILED(rv)) return rv; 1.934 + 1.935 + rv = EnsureAssocReq(); 1.936 + if (NS_FAILED(rv)) 1.937 + return rv; 1.938 + 1.939 + // if this channel is for a download, close off access to the cache. 1.940 + if (mCacheEntry && mChannelIsForDownload) { 1.941 + mCacheEntry->AsyncDoom(nullptr); 1.942 + 1.943 + // We must keep the cache entry in case of partial request. 1.944 + // Concurrent access is the same, we need the entry in 1.945 + // OnStopRequest. 1.946 + if (!mCachedContentIsPartial && !mConcurentCacheAccess) 1.947 + CloseCacheEntry(false); 1.948 + } 1.949 + 1.950 + if (!mCanceled) { 1.951 + // create offline cache entry if offline caching was requested 1.952 + if (ShouldUpdateOfflineCacheEntry()) { 1.953 + LOG(("writing to the offline cache")); 1.954 + rv = InitOfflineCacheEntry(); 1.955 + if (NS_FAILED(rv)) return rv; 1.956 + 1.957 + // InitOfflineCacheEntry may have closed mOfflineCacheEntry 1.958 + if (mOfflineCacheEntry) { 1.959 + rv = InstallOfflineCacheListener(); 1.960 + if (NS_FAILED(rv)) return rv; 1.961 + } 1.962 + } else if (mApplicationCacheForWrite) { 1.963 + LOG(("offline cache is up to date, not updating")); 1.964 + CloseOfflineCacheEntry(); 1.965 + } 1.966 + } 1.967 + 1.968 + return NS_OK; 1.969 +} 1.970 + 1.971 +nsresult 1.972 +nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) 1.973 +{ 1.974 + // Failure to set up a proxy tunnel via CONNECT means one of the following: 1.975 + // 1) Proxy wants authorization, or forbids. 1.976 + // 2) DNS at proxy couldn't resolve target URL. 1.977 + // 3) Proxy connection to target failed or timed out. 1.978 + // 4) Eve intercepted our CONNECT, and is replying with malicious HTML. 1.979 + // 1.980 + // Our current architecture would parse the proxy's response content with 1.981 + // the permission of the target URL. Given #4, we must avoid rendering the 1.982 + // body of the reply, and instead give the user a (hopefully helpful) 1.983 + // boilerplate error page, based on just the HTTP status of the reply. 1.984 + 1.985 + MOZ_ASSERT(mConnectionInfo->UsingConnect(), 1.986 + "proxy connect failed but not using CONNECT?"); 1.987 + nsresult rv; 1.988 + switch (httpStatus) 1.989 + { 1.990 + case 300: case 301: case 302: case 303: case 307: case 308: 1.991 + // Bad redirect: not top-level, or it's a POST, bad/missing Location, 1.992 + // or ProcessRedirect() failed for some other reason. Legal 1.993 + // redirects that fail because site not available, etc., are handled 1.994 + // elsewhere, in the regular codepath. 1.995 + rv = NS_ERROR_CONNECTION_REFUSED; 1.996 + break; 1.997 + case 403: // HTTP/1.1: "Forbidden" 1.998 + case 407: // ProcessAuthentication() failed 1.999 + case 501: // HTTP/1.1: "Not Implemented" 1.1000 + // user sees boilerplate Mozilla "Proxy Refused Connection" page. 1.1001 + rv = NS_ERROR_PROXY_CONNECTION_REFUSED; 1.1002 + break; 1.1003 + // Squid sends 404 if DNS fails (regular 404 from target is tunneled) 1.1004 + case 404: // HTTP/1.1: "Not Found" 1.1005 + // RFC 2616: "some deployed proxies are known to return 400 or 500 when 1.1006 + // DNS lookups time out." (Squid uses 500 if it runs out of sockets: so 1.1007 + // we have a conflict here). 1.1008 + case 400: // HTTP/1.1 "Bad Request" 1.1009 + case 500: // HTTP/1.1: "Internal Server Error" 1.1010 + /* User sees: "Address Not Found: Firefox can't find the server at 1.1011 + * www.foo.com." 1.1012 + */ 1.1013 + rv = NS_ERROR_UNKNOWN_HOST; 1.1014 + break; 1.1015 + case 502: // HTTP/1.1: "Bad Gateway" (invalid resp from target server) 1.1016 + // Squid returns 503 if target request fails for anything but DNS. 1.1017 + case 503: // HTTP/1.1: "Service Unavailable" 1.1018 + /* User sees: "Failed to Connect: 1.1019 + * Firefox can't establish a connection to the server at 1.1020 + * www.foo.com. Though the site seems valid, the browser 1.1021 + * was unable to establish a connection." 1.1022 + */ 1.1023 + rv = NS_ERROR_CONNECTION_REFUSED; 1.1024 + break; 1.1025 + // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to 1.1026 + // do here: picking target timeout, as DNS covered by 400/404/500 1.1027 + case 504: // HTTP/1.1: "Gateway Timeout" 1.1028 + // user sees: "Network Timeout: The server at www.foo.com 1.1029 + // is taking too long to respond." 1.1030 + rv = NS_ERROR_NET_TIMEOUT; 1.1031 + break; 1.1032 + // Confused proxy server or malicious response 1.1033 + default: 1.1034 + rv = NS_ERROR_PROXY_CONNECTION_REFUSED; 1.1035 + break; 1.1036 + } 1.1037 + LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n", 1.1038 + this, httpStatus)); 1.1039 + Cancel(rv); 1.1040 + CallOnStartRequest(); 1.1041 + return rv; 1.1042 +} 1.1043 + 1.1044 +/** 1.1045 + * Decide whether or not to remember Strict-Transport-Security, and whether 1.1046 + * or not to enforce channel integrity. 1.1047 + * 1.1048 + * @return NS_ERROR_FAILURE if there's security information missing even though 1.1049 + * it's an HTTPS connection. 1.1050 + */ 1.1051 +nsresult 1.1052 +nsHttpChannel::ProcessSTSHeader() 1.1053 +{ 1.1054 + nsresult rv; 1.1055 + bool isHttps = false; 1.1056 + rv = mURI->SchemeIs("https", &isHttps); 1.1057 + NS_ENSURE_SUCCESS(rv, rv); 1.1058 + 1.1059 + // If this channel is not loading securely, STS doesn't do anything. 1.1060 + // The upgrade to HTTPS takes place earlier in the channel load process. 1.1061 + if (!isHttps) 1.1062 + return NS_OK; 1.1063 + 1.1064 + nsAutoCString asciiHost; 1.1065 + rv = mURI->GetAsciiHost(asciiHost); 1.1066 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.1067 + 1.1068 + // If the channel is not a hostname, but rather an IP, STS doesn't do 1.1069 + // anything. 1.1070 + PRNetAddr hostAddr; 1.1071 + if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr)) 1.1072 + return NS_OK; 1.1073 + 1.1074 + nsISiteSecurityService* sss = gHttpHandler->GetSSService(); 1.1075 + NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); 1.1076 + 1.1077 + // mSecurityInfo may not always be present, and if it's not then it is okay 1.1078 + // to just disregard any STS headers since we know nothing about the 1.1079 + // security of the connection. 1.1080 + NS_ENSURE_TRUE(mSecurityInfo, NS_OK); 1.1081 + 1.1082 + // Check the trustworthiness of the channel (are there any cert errors?) 1.1083 + // If there are certificate errors, we still load the data, we just ignore 1.1084 + // any STS headers that are present. 1.1085 + bool tlsIsBroken = false; 1.1086 + rv = sss->ShouldIgnoreHeaders(mSecurityInfo, &tlsIsBroken); 1.1087 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.1088 + 1.1089 + // If this was already an STS host, the connection should have been aborted 1.1090 + // by the bad cert handler in the case of cert errors. If it didn't abort the connection, 1.1091 + // there's probably something funny going on. 1.1092 + // If this wasn't an STS host, errors are allowed, but no more STS processing 1.1093 + // will happen during the session. 1.1094 + bool wasAlreadySTSHost; 1.1095 + uint32_t flags = 1.1096 + NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; 1.1097 + rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags, 1.1098 + &wasAlreadySTSHost); 1.1099 + // Failure here means STS is broken. Don't prevent the load, but this 1.1100 + // shouldn't fail. 1.1101 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.1102 + MOZ_ASSERT(!(wasAlreadySTSHost && tlsIsBroken), 1.1103 + "connection should have been aborted by nss-bad-cert-handler"); 1.1104 + 1.1105 + // Any STS header is ignored if the channel is not trusted due to 1.1106 + // certificate errors (STS Spec 7.1) -- there is nothing else to do, and 1.1107 + // the load may progress. 1.1108 + if (tlsIsBroken) { 1.1109 + LOG(("STS: Transport layer is not trustworthy, ignoring " 1.1110 + "STS headers and continuing load\n")); 1.1111 + return NS_OK; 1.1112 + } 1.1113 + 1.1114 + // If there's a STS header, process it (STS Spec 7.1). At this point in 1.1115 + // processing, the channel is trusted, so the header should not be ignored. 1.1116 + const nsHttpAtom atom = nsHttp::ResolveAtom("Strict-Transport-Security"); 1.1117 + nsAutoCString stsHeader; 1.1118 + rv = mResponseHead->GetHeader(atom, stsHeader); 1.1119 + if (rv == NS_ERROR_NOT_AVAILABLE) { 1.1120 + LOG(("STS: No STS header, continuing load.\n")); 1.1121 + return NS_OK; 1.1122 + } 1.1123 + // All other failures are fatal. 1.1124 + NS_ENSURE_SUCCESS(rv, rv); 1.1125 + 1.1126 + rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, mURI, 1.1127 + stsHeader.get(), flags, nullptr, nullptr); 1.1128 + if (NS_FAILED(rv)) { 1.1129 + AddSecurityMessage(NS_LITERAL_STRING("InvalidSTSHeaders"), 1.1130 + NS_LITERAL_STRING("Invalid HSTS Headers")); 1.1131 + LOG(("STS: Failed to parse STS header, continuing load.\n")); 1.1132 + } 1.1133 + 1.1134 + return NS_OK; 1.1135 +} 1.1136 + 1.1137 +bool 1.1138 +nsHttpChannel::IsHTTPS() 1.1139 +{ 1.1140 + bool isHttps; 1.1141 + if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps) 1.1142 + return false; 1.1143 + return true; 1.1144 +} 1.1145 + 1.1146 +void 1.1147 +nsHttpChannel::ProcessSSLInformation() 1.1148 +{ 1.1149 + // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm 1.1150 + // can be whitelisted for TLS False Start in future sessions. We could 1.1151 + // do the same for DH but its rarity doesn't justify the lookup. 1.1152 + 1.1153 + if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo || 1.1154 + !IsHTTPS() || mPrivateBrowsing) 1.1155 + return; 1.1156 + 1.1157 + nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(mSecurityInfo); 1.1158 + nsCOMPtr<nsISSLStatusProvider> statusProvider = 1.1159 + do_QueryInterface(mSecurityInfo); 1.1160 + if (!ssl || !statusProvider) 1.1161 + return; 1.1162 + nsCOMPtr<nsISSLStatus> sslstat; 1.1163 + statusProvider->GetSSLStatus(getter_AddRefs(sslstat)); 1.1164 + if (!sslstat) 1.1165 + return; 1.1166 + 1.1167 + // If certificate exceptions are being used don't record this information 1.1168 + // in the permission manager. 1.1169 + bool trustCheck; 1.1170 + if (NS_FAILED(sslstat->GetIsDomainMismatch(&trustCheck)) || trustCheck) 1.1171 + return; 1.1172 + if (NS_FAILED(sslstat->GetIsNotValidAtThisTime(&trustCheck)) || trustCheck) 1.1173 + return; 1.1174 + if (NS_FAILED(sslstat->GetIsUntrusted(&trustCheck)) || trustCheck) 1.1175 + return; 1.1176 + 1.1177 + int16_t kea = ssl->GetKEAUsed(); 1.1178 + 1.1179 + nsIPrincipal *principal = GetPrincipal(); 1.1180 + if (!principal) 1.1181 + return; 1.1182 + 1.1183 + // set a permission manager flag that future transactions can 1.1184 + // use via RetrieveSSLOptions(() 1.1185 + 1.1186 + nsCOMPtr<nsIPermissionManager> permMgr = 1.1187 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.1188 + if (!permMgr) 1.1189 + return; 1.1190 + 1.1191 + // Allow this to stand for a week 1.1192 + int64_t expireTime = (PR_Now() / PR_USEC_PER_MSEC) + 1.1193 + (86400 * 7 * PR_MSEC_PER_SEC); 1.1194 + 1.1195 + if (kea == ssl_kea_rsa) { 1.1196 + permMgr->AddFromPrincipal(principal, "falsestart-rsa", 1.1197 + nsIPermissionManager::ALLOW_ACTION, 1.1198 + nsIPermissionManager::EXPIRE_TIME, 1.1199 + expireTime); 1.1200 + LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] " 1.1201 + "falsestart-rsa permission granted for this host\n", this)); 1.1202 + } else { 1.1203 + permMgr->RemoveFromPrincipal(principal, "falsestart-rsa"); 1.1204 + } 1.1205 +} 1.1206 + 1.1207 +nsresult 1.1208 +nsHttpChannel::ProcessResponse() 1.1209 +{ 1.1210 + nsresult rv; 1.1211 + uint32_t httpStatus = mResponseHead->Status(); 1.1212 + 1.1213 + // Gather data on whether the transaction and page (if this is 1.1214 + // the initial page load) is being loaded with SSL. 1.1215 + Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_IS_SSL, 1.1216 + mConnectionInfo->UsingSSL()); 1.1217 + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { 1.1218 + Telemetry::Accumulate(Telemetry::HTTP_PAGELOAD_IS_SSL, 1.1219 + mConnectionInfo->UsingSSL()); 1.1220 + } 1.1221 + 1.1222 + LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", 1.1223 + this, httpStatus)); 1.1224 + 1.1225 + if (mTransaction->ProxyConnectFailed()) { 1.1226 + // Only allow 407 (authentication required) to continue 1.1227 + if (httpStatus != 407) 1.1228 + return ProcessFailedProxyConnect(httpStatus); 1.1229 + // If proxy CONNECT response needs to complete, wait to process connection 1.1230 + // for Strict-Transport-Security. 1.1231 + } else { 1.1232 + // Given a successful connection, process any STS data that's relevant. 1.1233 + rv = ProcessSTSHeader(); 1.1234 + MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load."); 1.1235 + } 1.1236 + 1.1237 + MOZ_ASSERT(!mCachedContentIsValid); 1.1238 + 1.1239 + ProcessSSLInformation(); 1.1240 + 1.1241 + // notify "http-on-examine-response" observers 1.1242 + gHttpHandler->OnExamineResponse(this); 1.1243 + 1.1244 + SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); 1.1245 + 1.1246 + // handle unused username and password in url (see bug 232567) 1.1247 + if (httpStatus != 401 && httpStatus != 407) { 1.1248 + if (!mAuthRetryPending) 1.1249 + mAuthProvider->CheckForSuperfluousAuth(); 1.1250 + if (mCanceled) 1.1251 + return CallOnStartRequest(); 1.1252 + 1.1253 + // reset the authentication's current continuation state because our 1.1254 + // last authentication attempt has been completed successfully 1.1255 + mAuthProvider->Disconnect(NS_ERROR_ABORT); 1.1256 + mAuthProvider = nullptr; 1.1257 + LOG((" continuation state has been reset")); 1.1258 + } 1.1259 + 1.1260 + bool successfulReval = false; 1.1261 + 1.1262 + // handle different server response categories. Note that we handle 1.1263 + // caching or not caching of error pages in 1.1264 + // nsHttpResponseHead::MustValidate; if you change this switch, update that 1.1265 + // one 1.1266 + switch (httpStatus) { 1.1267 + case 200: 1.1268 + case 203: 1.1269 + // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header". 1.1270 + // So if a server does that and sends 200 instead of 206 that we 1.1271 + // expect, notify our caller. 1.1272 + // However, if we wanted to start from the beginning, let it go through 1.1273 + if (mResuming && mStartPos != 0) { 1.1274 + LOG(("Server ignored our Range header, cancelling [this=%p]\n", this)); 1.1275 + Cancel(NS_ERROR_NOT_RESUMABLE); 1.1276 + rv = CallOnStartRequest(); 1.1277 + break; 1.1278 + } 1.1279 + // these can normally be cached 1.1280 + rv = ProcessNormal(); 1.1281 + MaybeInvalidateCacheEntryForSubsequentGet(); 1.1282 + break; 1.1283 + case 206: 1.1284 + if (mCachedContentIsPartial) // an internal byte range request... 1.1285 + rv = ProcessPartialContent(); 1.1286 + else { 1.1287 + mCacheInputStream.CloseAndRelease(); 1.1288 + rv = ProcessNormal(); 1.1289 + } 1.1290 + break; 1.1291 + case 300: 1.1292 + case 301: 1.1293 + case 302: 1.1294 + case 307: 1.1295 + case 308: 1.1296 + case 303: 1.1297 +#if 0 1.1298 + case 305: // disabled as a security measure (see bug 187996). 1.1299 +#endif 1.1300 + // don't store the response body for redirects 1.1301 + MaybeInvalidateCacheEntryForSubsequentGet(); 1.1302 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse); 1.1303 + rv = AsyncProcessRedirection(httpStatus); 1.1304 + if (NS_FAILED(rv)) { 1.1305 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse); 1.1306 + LOG(("AsyncProcessRedirection failed [rv=%x]\n", rv)); 1.1307 + // don't cache failed redirect responses. 1.1308 + if (mCacheEntry) 1.1309 + mCacheEntry->AsyncDoom(nullptr); 1.1310 + if (DoNotRender3xxBody(rv)) { 1.1311 + mStatus = rv; 1.1312 + DoNotifyListener(); 1.1313 + } else { 1.1314 + rv = ContinueProcessResponse(rv); 1.1315 + } 1.1316 + } 1.1317 + break; 1.1318 + case 304: 1.1319 + rv = ProcessNotModified(); 1.1320 + if (NS_FAILED(rv)) { 1.1321 + LOG(("ProcessNotModified failed [rv=%x]\n", rv)); 1.1322 + mCacheInputStream.CloseAndRelease(); 1.1323 + rv = ProcessNormal(); 1.1324 + } 1.1325 + else { 1.1326 + successfulReval = true; 1.1327 + } 1.1328 + break; 1.1329 + case 401: 1.1330 + case 407: 1.1331 + rv = mAuthProvider->ProcessAuthentication( 1.1332 + httpStatus, mConnectionInfo->UsingSSL() && 1.1333 + mTransaction->ProxyConnectFailed()); 1.1334 + if (rv == NS_ERROR_IN_PROGRESS) { 1.1335 + // authentication prompt has been invoked and result 1.1336 + // is expected asynchronously 1.1337 + mAuthRetryPending = true; 1.1338 + if (httpStatus == 407 || mTransaction->ProxyConnectFailed()) 1.1339 + mProxyAuthPending = true; 1.1340 + 1.1341 + // suspend the transaction pump to stop receiving the 1.1342 + // unauthenticated content data. We will throw that data 1.1343 + // away when user provides credentials or resume the pump 1.1344 + // when user refuses to authenticate. 1.1345 + LOG(("Suspending the transaction, asynchronously prompting for credentials")); 1.1346 + mTransactionPump->Suspend(); 1.1347 + rv = NS_OK; 1.1348 + } 1.1349 + else if (NS_FAILED(rv)) { 1.1350 + LOG(("ProcessAuthentication failed [rv=%x]\n", rv)); 1.1351 + if (mTransaction->ProxyConnectFailed()) 1.1352 + return ProcessFailedProxyConnect(httpStatus); 1.1353 + if (!mAuthRetryPending) 1.1354 + mAuthProvider->CheckForSuperfluousAuth(); 1.1355 + rv = ProcessNormal(); 1.1356 + } 1.1357 + else 1.1358 + mAuthRetryPending = true; // see DoAuthRetry 1.1359 + break; 1.1360 + default: 1.1361 + rv = ProcessNormal(); 1.1362 + MaybeInvalidateCacheEntryForSubsequentGet(); 1.1363 + break; 1.1364 + } 1.1365 + 1.1366 + CacheDisposition cacheDisposition; 1.1367 + if (!mDidReval) 1.1368 + cacheDisposition = kCacheMissed; 1.1369 + else if (successfulReval) 1.1370 + cacheDisposition = kCacheHitViaReval; 1.1371 + else 1.1372 + cacheDisposition = kCacheMissedViaReval; 1.1373 + 1.1374 + AccumulateCacheHitTelemetry(cacheDisposition); 1.1375 + 1.1376 + return rv; 1.1377 +} 1.1378 + 1.1379 +nsresult 1.1380 +nsHttpChannel::ContinueProcessResponse(nsresult rv) 1.1381 +{ 1.1382 + bool doNotRender = DoNotRender3xxBody(rv); 1.1383 + 1.1384 + if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) { 1.1385 + bool isHTTP = false; 1.1386 + if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP))) 1.1387 + isHTTP = false; 1.1388 + if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP))) 1.1389 + isHTTP = false; 1.1390 + 1.1391 + if (!isHTTP) { 1.1392 + // This was a blocked attempt to redirect and subvert the system by 1.1393 + // redirecting to another protocol (perhaps javascript:) 1.1394 + // In that case we want to throw an error instead of displaying the 1.1395 + // non-redirected response body. 1.1396 + LOG(("ContinueProcessResponse detected rejected Non-HTTP Redirection")); 1.1397 + doNotRender = true; 1.1398 + rv = NS_ERROR_CORRUPTED_CONTENT; 1.1399 + } 1.1400 + } 1.1401 + 1.1402 + if (doNotRender) { 1.1403 + Cancel(rv); 1.1404 + DoNotifyListener(); 1.1405 + return rv; 1.1406 + } 1.1407 + 1.1408 + if (NS_SUCCEEDED(rv)) { 1.1409 + UpdateInhibitPersistentCachingFlag(); 1.1410 + 1.1411 + InitCacheEntry(); 1.1412 + CloseCacheEntry(false); 1.1413 + 1.1414 + if (mApplicationCacheForWrite) { 1.1415 + // Store response in the offline cache 1.1416 + InitOfflineCacheEntry(); 1.1417 + CloseOfflineCacheEntry(); 1.1418 + } 1.1419 + return NS_OK; 1.1420 + } 1.1421 + 1.1422 + LOG(("ContinueProcessResponse got failure result [rv=%x]\n", rv)); 1.1423 + if (mTransaction->ProxyConnectFailed()) { 1.1424 + return ProcessFailedProxyConnect(mRedirectType); 1.1425 + } 1.1426 + return ProcessNormal(); 1.1427 +} 1.1428 + 1.1429 +nsresult 1.1430 +nsHttpChannel::ProcessNormal() 1.1431 +{ 1.1432 + nsresult rv; 1.1433 + 1.1434 + LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this)); 1.1435 + 1.1436 + bool succeeded; 1.1437 + rv = GetRequestSucceeded(&succeeded); 1.1438 + if (NS_SUCCEEDED(rv) && !succeeded) { 1.1439 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal); 1.1440 + bool waitingForRedirectCallback; 1.1441 + (void)ProcessFallback(&waitingForRedirectCallback); 1.1442 + if (waitingForRedirectCallback) { 1.1443 + // The transaction has been suspended by ProcessFallback. 1.1444 + return NS_OK; 1.1445 + } 1.1446 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal); 1.1447 + } 1.1448 + 1.1449 + return ContinueProcessNormal(NS_OK); 1.1450 +} 1.1451 + 1.1452 +nsresult 1.1453 +nsHttpChannel::ContinueProcessNormal(nsresult rv) 1.1454 +{ 1.1455 + if (NS_FAILED(rv)) { 1.1456 + // Fill the failure status here, we have failed to fall back, thus we 1.1457 + // have to report our status as failed. 1.1458 + mStatus = rv; 1.1459 + DoNotifyListener(); 1.1460 + return rv; 1.1461 + } 1.1462 + 1.1463 + if (mFallingBack) { 1.1464 + // Do not continue with normal processing, fallback is in 1.1465 + // progress now. 1.1466 + return NS_OK; 1.1467 + } 1.1468 + 1.1469 + // if we're here, then any byte-range requests failed to result in a partial 1.1470 + // response. we must clear this flag to prevent BufferPartialContent from 1.1471 + // being called inside our OnDataAvailable (see bug 136678). 1.1472 + mCachedContentIsPartial = false; 1.1473 + 1.1474 + ClearBogusContentEncodingIfNeeded(); 1.1475 + 1.1476 + UpdateInhibitPersistentCachingFlag(); 1.1477 + 1.1478 + // this must be called before firing OnStartRequest, since http clients, 1.1479 + // such as imagelib, expect our cache entry to already have the correct 1.1480 + // expiration time (bug 87710). 1.1481 + if (mCacheEntry) { 1.1482 + rv = InitCacheEntry(); 1.1483 + if (NS_FAILED(rv)) 1.1484 + CloseCacheEntry(true); 1.1485 + } 1.1486 + 1.1487 + // Check that the server sent us what we were asking for 1.1488 + if (mResuming) { 1.1489 + // Create an entity id from the response 1.1490 + nsAutoCString id; 1.1491 + rv = GetEntityID(id); 1.1492 + if (NS_FAILED(rv)) { 1.1493 + // If creating an entity id is not possible -> error 1.1494 + Cancel(NS_ERROR_NOT_RESUMABLE); 1.1495 + } 1.1496 + else if (mResponseHead->Status() != 206 && 1.1497 + mResponseHead->Status() != 200) { 1.1498 + // Probably 404 Not Found, 412 Precondition Failed or 1.1499 + // 416 Invalid Range -> error 1.1500 + LOG(("Unexpected response status while resuming, aborting [this=%p]\n", 1.1501 + this)); 1.1502 + Cancel(NS_ERROR_ENTITY_CHANGED); 1.1503 + } 1.1504 + // If we were passed an entity id, verify it's equal to the server's 1.1505 + else if (!mEntityID.IsEmpty()) { 1.1506 + if (!mEntityID.Equals(id)) { 1.1507 + LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]", 1.1508 + mEntityID.get(), id.get(), this)); 1.1509 + Cancel(NS_ERROR_ENTITY_CHANGED); 1.1510 + } 1.1511 + } 1.1512 + } 1.1513 + 1.1514 + rv = CallOnStartRequest(); 1.1515 + if (NS_FAILED(rv)) return rv; 1.1516 + 1.1517 + // install cache listener if we still have a cache entry open 1.1518 + if (mCacheEntry && !mLoadedFromApplicationCache) { 1.1519 + rv = InstallCacheListener(); 1.1520 + if (NS_FAILED(rv)) return rv; 1.1521 + } 1.1522 + 1.1523 + return NS_OK; 1.1524 +} 1.1525 + 1.1526 +nsresult 1.1527 +nsHttpChannel::PromptTempRedirect() 1.1528 +{ 1.1529 + if (!gHttpHandler->PromptTempRedirect()) { 1.1530 + return NS_OK; 1.1531 + } 1.1532 + nsresult rv; 1.1533 + nsCOMPtr<nsIStringBundleService> bundleService = 1.1534 + do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); 1.1535 + if (NS_FAILED(rv)) return rv; 1.1536 + 1.1537 + nsCOMPtr<nsIStringBundle> stringBundle; 1.1538 + rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle)); 1.1539 + if (NS_FAILED(rv)) return rv; 1.1540 + 1.1541 + nsXPIDLString messageString; 1.1542 + rv = stringBundle->GetStringFromName(MOZ_UTF16("RepostFormData"), getter_Copies(messageString)); 1.1543 + // GetStringFromName can return NS_OK and nullptr messageString. 1.1544 + if (NS_SUCCEEDED(rv) && messageString) { 1.1545 + bool repost = false; 1.1546 + 1.1547 + nsCOMPtr<nsIPrompt> prompt; 1.1548 + GetCallback(prompt); 1.1549 + if (!prompt) 1.1550 + return NS_ERROR_NO_INTERFACE; 1.1551 + 1.1552 + prompt->Confirm(nullptr, messageString, &repost); 1.1553 + if (!repost) 1.1554 + return NS_ERROR_FAILURE; 1.1555 + } 1.1556 + 1.1557 + return rv; 1.1558 +} 1.1559 + 1.1560 +nsresult 1.1561 +nsHttpChannel::ProxyFailover() 1.1562 +{ 1.1563 + LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this)); 1.1564 + 1.1565 + nsresult rv; 1.1566 + 1.1567 + nsCOMPtr<nsIProtocolProxyService> pps = 1.1568 + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); 1.1569 + if (NS_FAILED(rv)) 1.1570 + return rv; 1.1571 + 1.1572 + nsCOMPtr<nsIProxyInfo> pi; 1.1573 + rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus, 1.1574 + getter_AddRefs(pi)); 1.1575 + if (NS_FAILED(rv)) 1.1576 + return rv; 1.1577 + 1.1578 + // XXXbz so where does this codepath remove us from the loadgroup, 1.1579 + // exactly? 1.1580 + return AsyncDoReplaceWithProxy(pi); 1.1581 +} 1.1582 + 1.1583 +void 1.1584 +nsHttpChannel::HandleAsyncRedirectChannelToHttps() 1.1585 +{ 1.1586 + NS_PRECONDITION(!mCallOnResume, "How did that happen?"); 1.1587 + 1.1588 + if (mSuspendCount) { 1.1589 + LOG(("Waiting until resume to do async redirect to https [this=%p]\n", this)); 1.1590 + mCallOnResume = &nsHttpChannel::HandleAsyncRedirectChannelToHttps; 1.1591 + return; 1.1592 + } 1.1593 + 1.1594 + nsresult rv = StartRedirectChannelToHttps(); 1.1595 + if (NS_FAILED(rv)) 1.1596 + ContinueAsyncRedirectChannelToURI(rv); 1.1597 +} 1.1598 + 1.1599 +nsresult 1.1600 +nsHttpChannel::StartRedirectChannelToHttps() 1.1601 +{ 1.1602 + nsresult rv = NS_OK; 1.1603 + LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n")); 1.1604 + 1.1605 + nsCOMPtr<nsIURI> upgradedURI; 1.1606 + 1.1607 + rv = mURI->Clone(getter_AddRefs(upgradedURI)); 1.1608 + NS_ENSURE_SUCCESS(rv,rv); 1.1609 + 1.1610 + upgradedURI->SetScheme(NS_LITERAL_CSTRING("https")); 1.1611 + 1.1612 + int32_t oldPort = -1; 1.1613 + rv = mURI->GetPort(&oldPort); 1.1614 + if (NS_FAILED(rv)) return rv; 1.1615 + 1.1616 + // Keep any nonstandard ports so only the scheme is changed. 1.1617 + // For example: 1.1618 + // http://foo.com:80 -> https://foo.com:443 1.1619 + // http://foo.com:81 -> https://foo.com:81 1.1620 + 1.1621 + if (oldPort == 80 || oldPort == -1) 1.1622 + upgradedURI->SetPort(-1); 1.1623 + else 1.1624 + upgradedURI->SetPort(oldPort); 1.1625 + 1.1626 + return StartRedirectChannelToURI(upgradedURI, 1.1627 + nsIChannelEventSink::REDIRECT_PERMANENT); 1.1628 +} 1.1629 + 1.1630 +void 1.1631 +nsHttpChannel::HandleAsyncAPIRedirect() 1.1632 +{ 1.1633 + NS_PRECONDITION(!mCallOnResume, "How did that happen?"); 1.1634 + NS_PRECONDITION(mAPIRedirectToURI, "How did that happen?"); 1.1635 + 1.1636 + if (mSuspendCount) { 1.1637 + LOG(("Waiting until resume to do async API redirect [this=%p]\n", this)); 1.1638 + mCallOnResume = &nsHttpChannel::HandleAsyncAPIRedirect; 1.1639 + return; 1.1640 + } 1.1641 + 1.1642 + nsresult rv = StartRedirectChannelToURI(mAPIRedirectToURI, 1.1643 + nsIChannelEventSink::REDIRECT_PERMANENT); 1.1644 + if (NS_FAILED(rv)) 1.1645 + ContinueAsyncRedirectChannelToURI(rv); 1.1646 + 1.1647 + return; 1.1648 +} 1.1649 + 1.1650 +nsresult 1.1651 +nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags) 1.1652 +{ 1.1653 + nsresult rv = NS_OK; 1.1654 + LOG(("nsHttpChannel::StartRedirectChannelToURI()\n")); 1.1655 + 1.1656 + nsCOMPtr<nsIChannel> newChannel; 1.1657 + 1.1658 + nsCOMPtr<nsIIOService> ioService; 1.1659 + rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 1.1660 + NS_ENSURE_SUCCESS(rv, rv); 1.1661 + 1.1662 + rv = ioService->NewChannelFromURI(upgradedURI, getter_AddRefs(newChannel)); 1.1663 + NS_ENSURE_SUCCESS(rv, rv); 1.1664 + 1.1665 + rv = SetupReplacementChannel(upgradedURI, newChannel, true); 1.1666 + NS_ENSURE_SUCCESS(rv, rv); 1.1667 + 1.1668 + // Inform consumers about this fake redirect 1.1669 + mRedirectChannel = newChannel; 1.1670 + 1.1671 + PushRedirectAsyncFunc( 1.1672 + &nsHttpChannel::ContinueAsyncRedirectChannelToURI); 1.1673 + rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags); 1.1674 + 1.1675 + if (NS_SUCCEEDED(rv)) 1.1676 + rv = WaitForRedirectCallback(); 1.1677 + 1.1678 + if (NS_FAILED(rv)) { 1.1679 + AutoRedirectVetoNotifier notifier(this); 1.1680 + 1.1681 + /* Remove the async call to ContinueAsyncRedirectChannelToURI(). 1.1682 + * It is called directly by our callers upon return (to clean up 1.1683 + * the failed redirect). */ 1.1684 + PopRedirectAsyncFunc( 1.1685 + &nsHttpChannel::ContinueAsyncRedirectChannelToURI); 1.1686 + } 1.1687 + 1.1688 + return rv; 1.1689 +} 1.1690 + 1.1691 +nsresult 1.1692 +nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv) 1.1693 +{ 1.1694 + if (NS_SUCCEEDED(rv)) 1.1695 + rv = OpenRedirectChannel(rv); 1.1696 + 1.1697 + if (NS_FAILED(rv)) { 1.1698 + // Fill the failure status here, the update to https had been vetoed 1.1699 + // but from the security reasons we have to discard the whole channel 1.1700 + // load. 1.1701 + mStatus = rv; 1.1702 + } 1.1703 + 1.1704 + if (mLoadGroup) 1.1705 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.1706 + 1.1707 + if (NS_FAILED(rv)) { 1.1708 + // We have to manually notify the listener because there is not any pump 1.1709 + // that would call our OnStart/StopRequest after resume from waiting for 1.1710 + // the redirect callback. 1.1711 + DoNotifyListener(); 1.1712 + } 1.1713 + 1.1714 + return rv; 1.1715 +} 1.1716 + 1.1717 +nsresult 1.1718 +nsHttpChannel::OpenRedirectChannel(nsresult rv) 1.1719 +{ 1.1720 + AutoRedirectVetoNotifier notifier(this); 1.1721 + 1.1722 + // Make sure to do this _after_ calling OnChannelRedirect 1.1723 + mRedirectChannel->SetOriginalURI(mOriginalURI); 1.1724 + 1.1725 + // And now, notify observers the deprecated way 1.1726 + nsCOMPtr<nsIHttpEventSink> httpEventSink; 1.1727 + GetCallback(httpEventSink); 1.1728 + if (httpEventSink) { 1.1729 + // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8 1.1730 + // versions. 1.1731 + rv = httpEventSink->OnRedirect(this, mRedirectChannel); 1.1732 + if (NS_FAILED(rv)) { 1.1733 + return rv; 1.1734 + } 1.1735 + } 1.1736 + 1.1737 + // open new channel 1.1738 + rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); 1.1739 + if (NS_FAILED(rv)) { 1.1740 + return rv; 1.1741 + } 1.1742 + 1.1743 + mStatus = NS_BINDING_REDIRECTED; 1.1744 + 1.1745 + notifier.RedirectSucceeded(); 1.1746 + 1.1747 + ReleaseListeners(); 1.1748 + 1.1749 + return NS_OK; 1.1750 +} 1.1751 + 1.1752 +nsresult 1.1753 +nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi) 1.1754 +{ 1.1755 + LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi)); 1.1756 + nsresult rv; 1.1757 + 1.1758 + nsCOMPtr<nsIChannel> newChannel; 1.1759 + rv = gHttpHandler->NewProxiedChannel(mURI, pi, mProxyResolveFlags, 1.1760 + mProxyURI, getter_AddRefs(newChannel)); 1.1761 + if (NS_FAILED(rv)) 1.1762 + return rv; 1.1763 + 1.1764 + rv = SetupReplacementChannel(mURI, newChannel, true); 1.1765 + if (NS_FAILED(rv)) 1.1766 + return rv; 1.1767 + 1.1768 + // Inform consumers about this fake redirect 1.1769 + mRedirectChannel = newChannel; 1.1770 + uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL; 1.1771 + 1.1772 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy); 1.1773 + rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags); 1.1774 + 1.1775 + if (NS_SUCCEEDED(rv)) 1.1776 + rv = WaitForRedirectCallback(); 1.1777 + 1.1778 + if (NS_FAILED(rv)) { 1.1779 + AutoRedirectVetoNotifier notifier(this); 1.1780 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy); 1.1781 + } 1.1782 + 1.1783 + return rv; 1.1784 +} 1.1785 + 1.1786 +nsresult 1.1787 +nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv) 1.1788 +{ 1.1789 + AutoRedirectVetoNotifier notifier(this); 1.1790 + 1.1791 + if (NS_FAILED(rv)) 1.1792 + return rv; 1.1793 + 1.1794 + NS_PRECONDITION(mRedirectChannel, "No redirect channel?"); 1.1795 + 1.1796 + // Make sure to do this _after_ calling OnChannelRedirect 1.1797 + mRedirectChannel->SetOriginalURI(mOriginalURI); 1.1798 + 1.1799 + // open new channel 1.1800 + rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); 1.1801 + if (NS_FAILED(rv)) 1.1802 + return rv; 1.1803 + 1.1804 + mStatus = NS_BINDING_REDIRECTED; 1.1805 + 1.1806 + notifier.RedirectSucceeded(); 1.1807 + 1.1808 + ReleaseListeners(); 1.1809 + 1.1810 + return rv; 1.1811 +} 1.1812 + 1.1813 +nsresult 1.1814 +nsHttpChannel::ResolveProxy() 1.1815 +{ 1.1816 + LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this)); 1.1817 + 1.1818 + nsresult rv; 1.1819 + 1.1820 + nsCOMPtr<nsIProtocolProxyService> pps = 1.1821 + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); 1.1822 + if (NS_FAILED(rv)) 1.1823 + return rv; 1.1824 + 1.1825 + // using the nsIProtocolProxyService2 allows a minor performance 1.1826 + // optimization, but if an add-on has only provided the original interface 1.1827 + // then it is ok to use that version. 1.1828 + nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps); 1.1829 + if (pps2) { 1.1830 + rv = pps2->AsyncResolve2(this, mProxyResolveFlags, 1.1831 + this, getter_AddRefs(mProxyRequest)); 1.1832 + } else { 1.1833 + rv = pps->AsyncResolve(this, mProxyResolveFlags, 1.1834 + this, getter_AddRefs(mProxyRequest)); 1.1835 + } 1.1836 + 1.1837 + return rv; 1.1838 +} 1.1839 + 1.1840 +bool 1.1841 +nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry) const 1.1842 +{ 1.1843 + nsresult rv; 1.1844 + nsAutoCString buf, metaKey; 1.1845 + mCachedResponseHead->GetHeader(nsHttp::Vary, buf); 1.1846 + if (!buf.IsEmpty()) { 1.1847 + NS_NAMED_LITERAL_CSTRING(prefix, "request-"); 1.1848 + 1.1849 + // enumerate the elements of the Vary header... 1.1850 + char *val = buf.BeginWriting(); // going to munge buf 1.1851 + char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val); 1.1852 + while (token) { 1.1853 + LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] " \ 1.1854 + "processing %s\n", 1.1855 + this, token)); 1.1856 + // 1.1857 + // if "*", then assume response would vary. technically speaking, 1.1858 + // "Vary: header, *" is not permitted, but we allow it anyways. 1.1859 + // 1.1860 + // We hash values of cookie-headers for the following reasons: 1.1861 + // 1.1862 + // 1- cookies can be very large in size 1.1863 + // 1.1864 + // 2- cookies may contain sensitive information. (for parity with 1.1865 + // out policy of not storing Set-cookie headers in the cache 1.1866 + // meta data, we likewise do not want to store cookie headers 1.1867 + // here.) 1.1868 + // 1.1869 + if (*token == '*') 1.1870 + return true; // if we encounter this, just get out of here 1.1871 + 1.1872 + // build cache meta data key... 1.1873 + metaKey = prefix + nsDependentCString(token); 1.1874 + 1.1875 + // check the last value of the given request header to see if it has 1.1876 + // since changed. if so, then indeed the cached response is invalid. 1.1877 + nsXPIDLCString lastVal; 1.1878 + entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal)); 1.1879 + LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] " 1.1880 + "stored value = \"%s\"\n", 1.1881 + this, lastVal.get())); 1.1882 + 1.1883 + // Look for value of "Cookie" in the request headers 1.1884 + nsHttpAtom atom = nsHttp::ResolveAtom(token); 1.1885 + const char *newVal = mRequestHead.PeekHeader(atom); 1.1886 + if (!lastVal.IsEmpty()) { 1.1887 + // value for this header in cache, but no value in request 1.1888 + if (!newVal) 1.1889 + return true; // yes - response would vary 1.1890 + 1.1891 + // If this is a cookie-header, stored metadata is not 1.1892 + // the value itself but the hash. So we also hash the 1.1893 + // outgoing value here in order to compare the hashes 1.1894 + nsAutoCString hash; 1.1895 + if (atom == nsHttp::Cookie) { 1.1896 + rv = Hash(newVal, hash); 1.1897 + // If hash failed, be conservative (the cached hash 1.1898 + // exists at this point) and claim response would vary 1.1899 + if (NS_FAILED(rv)) 1.1900 + return true; 1.1901 + newVal = hash.get(); 1.1902 + 1.1903 + LOG(("nsHttpChannel::ResponseWouldVary [this=%p] " \ 1.1904 + "set-cookie value hashed to %s\n", 1.1905 + this, newVal)); 1.1906 + } 1.1907 + 1.1908 + if (strcmp(newVal, lastVal)) 1.1909 + return true; // yes, response would vary 1.1910 + 1.1911 + } else if (newVal) { // old value is empty, but newVal is set 1.1912 + return true; 1.1913 + } 1.1914 + 1.1915 + // next token... 1.1916 + token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val); 1.1917 + } 1.1918 + } 1.1919 + return false; 1.1920 +} 1.1921 + 1.1922 +// We need to have an implementation of this function just so that we can keep 1.1923 +// all references to mCallOnResume of type nsHttpChannel: it's not OK in C++ 1.1924 +// to set a member function ptr to a base class function. 1.1925 +void 1.1926 +nsHttpChannel::HandleAsyncAbort() 1.1927 +{ 1.1928 + HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort(); 1.1929 +} 1.1930 + 1.1931 + 1.1932 +nsresult 1.1933 +nsHttpChannel::EnsureAssocReq() 1.1934 +{ 1.1935 + // Confirm Assoc-Req response header on pipelined transactions 1.1936 + // per draft-nottingham-http-pipeline-01.txt 1.1937 + // of the form: GET http://blah.com/foo/bar?qv 1.1938 + // return NS_OK as long as we don't find a violation 1.1939 + // (i.e. no header is ok, as are malformed headers, as are 1.1940 + // transactions that have not been pipelined (unless those have been 1.1941 + // opted in via pragma)) 1.1942 + 1.1943 + if (!mResponseHead) 1.1944 + return NS_OK; 1.1945 + 1.1946 + const char *assoc_val = mResponseHead->PeekHeader(nsHttp::Assoc_Req); 1.1947 + if (!assoc_val) 1.1948 + return NS_OK; 1.1949 + 1.1950 + if (!mTransaction || !mURI) 1.1951 + return NS_OK; 1.1952 + 1.1953 + if (!mTransaction->PipelinePosition()) { 1.1954 + // "Pragma: X-Verify-Assoc-Req" can be used to verify even non pipelined 1.1955 + // transactions. It is used by test harness. 1.1956 + 1.1957 + const char *pragma_val = mResponseHead->PeekHeader(nsHttp::Pragma); 1.1958 + if (!pragma_val || 1.1959 + !nsHttp::FindToken(pragma_val, "X-Verify-Assoc-Req", 1.1960 + HTTP_HEADER_VALUE_SEPS)) 1.1961 + return NS_OK; 1.1962 + } 1.1963 + 1.1964 + char *method = net_FindCharNotInSet(assoc_val, HTTP_LWS); 1.1965 + if (!method) 1.1966 + return NS_OK; 1.1967 + 1.1968 + bool equals; 1.1969 + char *endofmethod; 1.1970 + 1.1971 + assoc_val = nullptr; 1.1972 + endofmethod = net_FindCharInSet(method, HTTP_LWS); 1.1973 + if (endofmethod) 1.1974 + assoc_val = net_FindCharNotInSet(endofmethod, HTTP_LWS); 1.1975 + if (!assoc_val) 1.1976 + return NS_OK; 1.1977 + 1.1978 + // check the method 1.1979 + int32_t methodlen = strlen(mRequestHead.Method().get()); 1.1980 + if ((methodlen != (endofmethod - method)) || 1.1981 + PL_strncmp(method, 1.1982 + mRequestHead.Method().get(), 1.1983 + endofmethod - method)) { 1.1984 + LOG((" Assoc-Req failure Method %s", method)); 1.1985 + if (mConnectionInfo) 1.1986 + gHttpHandler->ConnMgr()-> 1.1987 + PipelineFeedbackInfo(mConnectionInfo, 1.1988 + nsHttpConnectionMgr::RedCorruptedContent, 1.1989 + nullptr, 0); 1.1990 + 1.1991 + nsCOMPtr<nsIConsoleService> consoleService = 1.1992 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.1993 + if (consoleService) { 1.1994 + nsAutoString message 1.1995 + (NS_LITERAL_STRING("Failed Assoc-Req. Received ")); 1.1996 + AppendASCIItoUTF16( 1.1997 + mResponseHead->PeekHeader(nsHttp::Assoc_Req), 1.1998 + message); 1.1999 + message += NS_LITERAL_STRING(" expected method "); 1.2000 + AppendASCIItoUTF16(mRequestHead.Method().get(), message); 1.2001 + consoleService->LogStringMessage(message.get()); 1.2002 + } 1.2003 + 1.2004 + if (gHttpHandler->EnforceAssocReq()) 1.2005 + return NS_ERROR_CORRUPTED_CONTENT; 1.2006 + return NS_OK; 1.2007 + } 1.2008 + 1.2009 + // check the URL 1.2010 + nsCOMPtr<nsIURI> assoc_url; 1.2011 + if (NS_FAILED(NS_NewURI(getter_AddRefs(assoc_url), assoc_val)) || 1.2012 + !assoc_url) 1.2013 + return NS_OK; 1.2014 + 1.2015 + mURI->Equals(assoc_url, &equals); 1.2016 + if (!equals) { 1.2017 + LOG((" Assoc-Req failure URL %s", assoc_val)); 1.2018 + if (mConnectionInfo) 1.2019 + gHttpHandler->ConnMgr()-> 1.2020 + PipelineFeedbackInfo(mConnectionInfo, 1.2021 + nsHttpConnectionMgr::RedCorruptedContent, 1.2022 + nullptr, 0); 1.2023 + 1.2024 + nsCOMPtr<nsIConsoleService> consoleService = 1.2025 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.2026 + if (consoleService) { 1.2027 + nsAutoString message 1.2028 + (NS_LITERAL_STRING("Failed Assoc-Req. Received ")); 1.2029 + AppendASCIItoUTF16( 1.2030 + mResponseHead->PeekHeader(nsHttp::Assoc_Req), 1.2031 + message); 1.2032 + message += NS_LITERAL_STRING(" expected URL "); 1.2033 + AppendASCIItoUTF16(mSpec.get(), message); 1.2034 + consoleService->LogStringMessage(message.get()); 1.2035 + } 1.2036 + 1.2037 + if (gHttpHandler->EnforceAssocReq()) 1.2038 + return NS_ERROR_CORRUPTED_CONTENT; 1.2039 + } 1.2040 + return NS_OK; 1.2041 +} 1.2042 + 1.2043 +//----------------------------------------------------------------------------- 1.2044 +// nsHttpChannel <byte-range> 1.2045 +//----------------------------------------------------------------------------- 1.2046 + 1.2047 +bool 1.2048 +nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength, 1.2049 + bool ignoreMissingPartialLen) const 1.2050 +{ 1.2051 + bool hasContentEncoding = 1.2052 + mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding) 1.2053 + != nullptr; 1.2054 + 1.2055 + return (partialLen < contentLength) && 1.2056 + (partialLen > 0 || ignoreMissingPartialLen) && 1.2057 + !hasContentEncoding && 1.2058 + mCachedResponseHead->IsResumable() && 1.2059 + !mCustomConditionalRequest && 1.2060 + !mCachedResponseHead->NoStore(); 1.2061 +} 1.2062 + 1.2063 +nsresult 1.2064 +nsHttpChannel::MaybeSetupByteRangeRequest(int64_t partialLen, int64_t contentLength) 1.2065 +{ 1.2066 + // Be pesimistic 1.2067 + mIsPartialRequest = false; 1.2068 + 1.2069 + if (!IsResumable(partialLen, contentLength)) 1.2070 + return NS_ERROR_NOT_RESUMABLE; 1.2071 + 1.2072 + // looks like a partial entry we can reuse; add If-Range 1.2073 + // and Range headers. 1.2074 + nsresult rv = SetupByteRangeRequest(partialLen); 1.2075 + if (NS_FAILED(rv)) { 1.2076 + // Make the request unconditional again. 1.2077 + mRequestHead.ClearHeader(nsHttp::Range); 1.2078 + mRequestHead.ClearHeader(nsHttp::If_Range); 1.2079 + } 1.2080 + 1.2081 + return rv; 1.2082 +} 1.2083 + 1.2084 +nsresult 1.2085 +nsHttpChannel::SetupByteRangeRequest(int64_t partialLen) 1.2086 +{ 1.2087 + // cached content has been found to be partial, add necessary request 1.2088 + // headers to complete cache entry. 1.2089 + 1.2090 + // use strongest validator available... 1.2091 + const char *val = mCachedResponseHead->PeekHeader(nsHttp::ETag); 1.2092 + if (!val) 1.2093 + val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified); 1.2094 + if (!val) { 1.2095 + // if we hit this code it means mCachedResponseHead->IsResumable() is 1.2096 + // either broken or not being called. 1.2097 + NS_NOTREACHED("no cache validator"); 1.2098 + mIsPartialRequest = false; 1.2099 + return NS_ERROR_FAILURE; 1.2100 + } 1.2101 + 1.2102 + char buf[64]; 1.2103 + PR_snprintf(buf, sizeof(buf), "bytes=%lld-", partialLen); 1.2104 + 1.2105 + mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf)); 1.2106 + mRequestHead.SetHeader(nsHttp::If_Range, nsDependentCString(val)); 1.2107 + mIsPartialRequest = true; 1.2108 + 1.2109 + return NS_OK; 1.2110 +} 1.2111 + 1.2112 +nsresult 1.2113 +nsHttpChannel::ProcessPartialContent() 1.2114 +{ 1.2115 + // ok, we've just received a 206 1.2116 + // 1.2117 + // we need to stream whatever data is in the cache out first, and then 1.2118 + // pick up whatever data is on the wire, writing it into the cache. 1.2119 + 1.2120 + LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this)); 1.2121 + 1.2122 + NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED); 1.2123 + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED); 1.2124 + 1.2125 + // Make sure to clear bogus content-encodings before looking at the header 1.2126 + ClearBogusContentEncodingIfNeeded(); 1.2127 + 1.2128 + // Check if the content-encoding we now got is different from the one we 1.2129 + // got before 1.2130 + if (PL_strcasecmp(mResponseHead->PeekHeader(nsHttp::Content_Encoding), 1.2131 + mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding)) 1.2132 + != 0) { 1.2133 + Cancel(NS_ERROR_INVALID_CONTENT_ENCODING); 1.2134 + return CallOnStartRequest(); 1.2135 + } 1.2136 + 1.2137 + nsresult rv; 1.2138 + 1.2139 + int64_t cachedContentLength = mCachedResponseHead->ContentLength(); 1.2140 + int64_t entitySize = mResponseHead->TotalEntitySize(); 1.2141 + 1.2142 + LOG(("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] " 1.2143 + "original content-length %lld, entity-size %lld, content-range %s\n", 1.2144 + this, mTransaction.get(), cachedContentLength, entitySize, 1.2145 + mResponseHead->PeekHeader(nsHttp::Content_Range))); 1.2146 + 1.2147 + if ((entitySize >= 0) && (cachedContentLength >= 0) && 1.2148 + (entitySize != cachedContentLength)) { 1.2149 + LOG(("nsHttpChannel::ProcessPartialContent [this=%p] " 1.2150 + "206 has different total entity size than the content length " 1.2151 + "of the original partially cached entity.\n", this)); 1.2152 + 1.2153 + mCacheEntry->AsyncDoom(nullptr); 1.2154 + Cancel(NS_ERROR_CORRUPTED_CONTENT); 1.2155 + return CallOnStartRequest(); 1.2156 + } 1.2157 + 1.2158 + if (mConcurentCacheAccess) { 1.2159 + // We started to read cached data sooner than its write has been done. 1.2160 + // But the concurrent write has not finished completely, so we had to 1.2161 + // do a range request. Now let the content coming from the network 1.2162 + // be presented to consumers and also stored to the cache entry. 1.2163 + 1.2164 + rv = InstallCacheListener(mLogicalOffset); 1.2165 + if (NS_FAILED(rv)) return rv; 1.2166 + 1.2167 + if (mOfflineCacheEntry) { 1.2168 + rv = InstallOfflineCacheListener(mLogicalOffset); 1.2169 + if (NS_FAILED(rv)) return rv; 1.2170 + } 1.2171 + 1.2172 + // merge any new headers with the cached response headers 1.2173 + rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers()); 1.2174 + if (NS_FAILED(rv)) return rv; 1.2175 + 1.2176 + // update the cached response head 1.2177 + nsAutoCString head; 1.2178 + mCachedResponseHead->Flatten(head, true); 1.2179 + rv = mCacheEntry->SetMetaDataElement("response-head", head.get()); 1.2180 + if (NS_FAILED(rv)) return rv; 1.2181 + 1.2182 + UpdateInhibitPersistentCachingFlag(); 1.2183 + 1.2184 + rv = UpdateExpirationTime(); 1.2185 + if (NS_FAILED(rv)) return rv; 1.2186 + 1.2187 + mCachedContentIsPartial = false; 1.2188 + 1.2189 + // notify observers interested in looking at a response that has been 1.2190 + // merged with any cached headers (http-on-examine-merged-response). 1.2191 + gHttpHandler->OnExamineMergedResponse(this); 1.2192 + } 1.2193 + else { 1.2194 + // suspend the current transaction 1.2195 + rv = mTransactionPump->Suspend(); 1.2196 + if (NS_FAILED(rv)) return rv; 1.2197 + 1.2198 + // merge any new headers with the cached response headers 1.2199 + rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers()); 1.2200 + if (NS_FAILED(rv)) return rv; 1.2201 + 1.2202 + // update the cached response head 1.2203 + nsAutoCString head; 1.2204 + mCachedResponseHead->Flatten(head, true); 1.2205 + rv = mCacheEntry->SetMetaDataElement("response-head", head.get()); 1.2206 + if (NS_FAILED(rv)) return rv; 1.2207 + 1.2208 + // make the cached response be the current response 1.2209 + mResponseHead = mCachedResponseHead; 1.2210 + 1.2211 + UpdateInhibitPersistentCachingFlag(); 1.2212 + 1.2213 + rv = UpdateExpirationTime(); 1.2214 + if (NS_FAILED(rv)) return rv; 1.2215 + 1.2216 + // notify observers interested in looking at a response that has been 1.2217 + // merged with any cached headers (http-on-examine-merged-response). 1.2218 + gHttpHandler->OnExamineMergedResponse(this); 1.2219 + 1.2220 + // the cached content is valid, although incomplete. 1.2221 + mCachedContentIsValid = true; 1.2222 + rv = ReadFromCache(false); 1.2223 + } 1.2224 + 1.2225 + return rv; 1.2226 +} 1.2227 + 1.2228 +nsresult 1.2229 +nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone) 1.2230 +{ 1.2231 + nsresult rv; 1.2232 + 1.2233 + LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this)); 1.2234 + 1.2235 + // by default, assume we would have streamed all data or failed... 1.2236 + *streamDone = true; 1.2237 + 1.2238 + // setup cache listener to append to cache entry 1.2239 + int64_t size; 1.2240 + rv = mCacheEntry->GetDataSize(&size); 1.2241 + if (NS_FAILED(rv)) return rv; 1.2242 + 1.2243 + rv = InstallCacheListener(size); 1.2244 + if (NS_FAILED(rv)) return rv; 1.2245 + 1.2246 + // Entry is valid, do it now, after the output stream has been opened, 1.2247 + // otherwise when done earlier, pending readers would consider the cache 1.2248 + // entry still as partial (CacheEntry::GetDataSize would return the partial 1.2249 + // data size) and consumers would do the conditional request again. 1.2250 + rv = mCacheEntry->SetValid(); 1.2251 + if (NS_FAILED(rv)) return rv; 1.2252 + 1.2253 + // need to track the logical offset of the data being sent to our listener 1.2254 + mLogicalOffset = size; 1.2255 + 1.2256 + // we're now completing the cached content, so we can clear this flag. 1.2257 + // this puts us in the state of a regular download. 1.2258 + mCachedContentIsPartial = false; 1.2259 + 1.2260 + // resume the transaction if it exists, otherwise the pipe contained the 1.2261 + // remaining part of the document and we've now streamed all of the data. 1.2262 + if (mTransactionPump) { 1.2263 + rv = mTransactionPump->Resume(); 1.2264 + if (NS_SUCCEEDED(rv)) 1.2265 + *streamDone = false; 1.2266 + } 1.2267 + else 1.2268 + NS_NOTREACHED("no transaction"); 1.2269 + return rv; 1.2270 +} 1.2271 + 1.2272 +//----------------------------------------------------------------------------- 1.2273 +// nsHttpChannel <cache> 1.2274 +//----------------------------------------------------------------------------- 1.2275 + 1.2276 +nsresult 1.2277 +nsHttpChannel::ProcessNotModified() 1.2278 +{ 1.2279 + nsresult rv; 1.2280 + 1.2281 + LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this)); 1.2282 + 1.2283 + if (mCustomConditionalRequest) { 1.2284 + LOG(("Bypassing ProcessNotModified due to custom conditional headers")); 1.2285 + return NS_ERROR_FAILURE; 1.2286 + } 1.2287 + 1.2288 + if (!mDidReval) { 1.2289 + LOG(("Server returned a 304 response even though we did not send a " 1.2290 + "conditional request")); 1.2291 + return NS_ERROR_FAILURE; 1.2292 + } 1.2293 + 1.2294 + MOZ_ASSERT(mCachedResponseHead); 1.2295 + MOZ_ASSERT(mCacheEntry); 1.2296 + NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED); 1.2297 + 1.2298 + // If the 304 response contains a Last-Modified different than the 1.2299 + // one in our cache that is pretty suspicious and is, in at least the 1.2300 + // case of bug 716840, a sign of the server having previously corrupted 1.2301 + // our cache with a bad response. Take the minor step here of just dooming 1.2302 + // that cache entry so there is a fighting chance of getting things on the 1.2303 + // right track as well as disabling pipelining for that host. 1.2304 + 1.2305 + nsAutoCString lastModifiedCached; 1.2306 + nsAutoCString lastModified304; 1.2307 + 1.2308 + rv = mCachedResponseHead->GetHeader(nsHttp::Last_Modified, 1.2309 + lastModifiedCached); 1.2310 + if (NS_SUCCEEDED(rv)) { 1.2311 + rv = mResponseHead->GetHeader(nsHttp::Last_Modified, 1.2312 + lastModified304); 1.2313 + } 1.2314 + 1.2315 + if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) { 1.2316 + LOG(("Cache Entry and 304 Last-Modified Headers Do Not Match " 1.2317 + "[%s] and [%s]\n", 1.2318 + lastModifiedCached.get(), lastModified304.get())); 1.2319 + 1.2320 + mCacheEntry->AsyncDoom(nullptr); 1.2321 + if (mConnectionInfo) 1.2322 + gHttpHandler->ConnMgr()-> 1.2323 + PipelineFeedbackInfo(mConnectionInfo, 1.2324 + nsHttpConnectionMgr::RedCorruptedContent, 1.2325 + nullptr, 0); 1.2326 + Telemetry::Accumulate(Telemetry::CACHE_LM_INCONSISTENT, true); 1.2327 + } 1.2328 + 1.2329 + // merge any new headers with the cached response headers 1.2330 + rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers()); 1.2331 + if (NS_FAILED(rv)) return rv; 1.2332 + 1.2333 + // update the cached response head 1.2334 + nsAutoCString head; 1.2335 + mCachedResponseHead->Flatten(head, true); 1.2336 + rv = mCacheEntry->SetMetaDataElement("response-head", head.get()); 1.2337 + if (NS_FAILED(rv)) return rv; 1.2338 + 1.2339 + // make the cached response be the current response 1.2340 + mResponseHead = mCachedResponseHead; 1.2341 + 1.2342 + UpdateInhibitPersistentCachingFlag(); 1.2343 + 1.2344 + rv = UpdateExpirationTime(); 1.2345 + if (NS_FAILED(rv)) return rv; 1.2346 + 1.2347 + rv = AddCacheEntryHeaders(mCacheEntry); 1.2348 + if (NS_FAILED(rv)) return rv; 1.2349 + 1.2350 + // notify observers interested in looking at a reponse that has been 1.2351 + // merged with any cached headers 1.2352 + gHttpHandler->OnExamineMergedResponse(this); 1.2353 + 1.2354 + mCachedContentIsValid = true; 1.2355 + 1.2356 + // Tell other consumers the entry is OK to use 1.2357 + rv = mCacheEntry->SetValid(); 1.2358 + if (NS_FAILED(rv)) return rv; 1.2359 + 1.2360 + rv = ReadFromCache(false); 1.2361 + if (NS_FAILED(rv)) return rv; 1.2362 + 1.2363 + mTransactionReplaced = true; 1.2364 + return NS_OK; 1.2365 +} 1.2366 + 1.2367 +nsresult 1.2368 +nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback) 1.2369 +{ 1.2370 + LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this)); 1.2371 + nsresult rv; 1.2372 + 1.2373 + *waitingForRedirectCallback = false; 1.2374 + mFallingBack = false; 1.2375 + 1.2376 + // At this point a load has failed (either due to network problems 1.2377 + // or an error returned on the server). Perform an application 1.2378 + // cache fallback if we have a URI to fall back to. 1.2379 + if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) { 1.2380 + LOG((" choosing not to fallback [%p,%s,%d]", 1.2381 + mApplicationCache.get(), mFallbackKey.get(), mFallbackChannel)); 1.2382 + return NS_OK; 1.2383 + } 1.2384 + 1.2385 + // Make sure the fallback entry hasn't been marked as a foreign 1.2386 + // entry. 1.2387 + uint32_t fallbackEntryType; 1.2388 + rv = mApplicationCache->GetTypes(mFallbackKey, &fallbackEntryType); 1.2389 + NS_ENSURE_SUCCESS(rv, rv); 1.2390 + 1.2391 + if (fallbackEntryType & nsIApplicationCache::ITEM_FOREIGN) { 1.2392 + // This cache points to a fallback that refers to a different 1.2393 + // manifest. Refuse to fall back. 1.2394 + return NS_OK; 1.2395 + } 1.2396 + 1.2397 + MOZ_ASSERT(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK, 1.2398 + "Fallback entry not marked correctly!"); 1.2399 + 1.2400 + // Kill any offline cache entry, and disable offline caching for the 1.2401 + // fallback. 1.2402 + if (mOfflineCacheEntry) { 1.2403 + mOfflineCacheEntry->AsyncDoom(nullptr); 1.2404 + mOfflineCacheEntry = nullptr; 1.2405 + } 1.2406 + 1.2407 + mApplicationCacheForWrite = nullptr; 1.2408 + mOfflineCacheEntry = nullptr; 1.2409 + 1.2410 + // Close the current cache entry. 1.2411 + CloseCacheEntry(true); 1.2412 + 1.2413 + // Create a new channel to load the fallback entry. 1.2414 + nsRefPtr<nsIChannel> newChannel; 1.2415 + rv = gHttpHandler->NewChannel(mURI, getter_AddRefs(newChannel)); 1.2416 + NS_ENSURE_SUCCESS(rv, rv); 1.2417 + 1.2418 + rv = SetupReplacementChannel(mURI, newChannel, true); 1.2419 + NS_ENSURE_SUCCESS(rv, rv); 1.2420 + 1.2421 + // Make sure the new channel loads from the fallback key. 1.2422 + nsCOMPtr<nsIHttpChannelInternal> httpInternal = 1.2423 + do_QueryInterface(newChannel, &rv); 1.2424 + NS_ENSURE_SUCCESS(rv, rv); 1.2425 + 1.2426 + rv = httpInternal->SetupFallbackChannel(mFallbackKey.get()); 1.2427 + NS_ENSURE_SUCCESS(rv, rv); 1.2428 + 1.2429 + // ... and fallbacks should only load from the cache. 1.2430 + uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE | LOAD_ONLY_FROM_CACHE; 1.2431 + rv = newChannel->SetLoadFlags(newLoadFlags); 1.2432 + 1.2433 + // Inform consumers about this fake redirect 1.2434 + mRedirectChannel = newChannel; 1.2435 + uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL; 1.2436 + 1.2437 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback); 1.2438 + rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags); 1.2439 + 1.2440 + if (NS_SUCCEEDED(rv)) 1.2441 + rv = WaitForRedirectCallback(); 1.2442 + 1.2443 + if (NS_FAILED(rv)) { 1.2444 + AutoRedirectVetoNotifier notifier(this); 1.2445 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback); 1.2446 + return rv; 1.2447 + } 1.2448 + 1.2449 + // Indicate we are now waiting for the asynchronous redirect callback 1.2450 + // if all went OK. 1.2451 + *waitingForRedirectCallback = true; 1.2452 + return NS_OK; 1.2453 +} 1.2454 + 1.2455 +nsresult 1.2456 +nsHttpChannel::ContinueProcessFallback(nsresult rv) 1.2457 +{ 1.2458 + AutoRedirectVetoNotifier notifier(this); 1.2459 + 1.2460 + if (NS_FAILED(rv)) 1.2461 + return rv; 1.2462 + 1.2463 + NS_PRECONDITION(mRedirectChannel, "No redirect channel?"); 1.2464 + 1.2465 + // Make sure to do this _after_ calling OnChannelRedirect 1.2466 + mRedirectChannel->SetOriginalURI(mOriginalURI); 1.2467 + 1.2468 + rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); 1.2469 + if (NS_FAILED(rv)) 1.2470 + return rv; 1.2471 + 1.2472 + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { 1.2473 + Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, 1.2474 + true); 1.2475 + } 1.2476 + 1.2477 + // close down this channel 1.2478 + Cancel(NS_BINDING_REDIRECTED); 1.2479 + 1.2480 + notifier.RedirectSucceeded(); 1.2481 + 1.2482 + ReleaseListeners(); 1.2483 + 1.2484 + mFallingBack = true; 1.2485 + 1.2486 + return NS_OK; 1.2487 +} 1.2488 + 1.2489 +// Determines if a request is a byte range request for a subrange, 1.2490 +// i.e. is a byte range request, but not a 0- byte range request. 1.2491 +static bool 1.2492 +IsSubRangeRequest(nsHttpRequestHead &aRequestHead) 1.2493 +{ 1.2494 + if (!aRequestHead.PeekHeader(nsHttp::Range)) 1.2495 + return false; 1.2496 + nsAutoCString byteRange; 1.2497 + aRequestHead.GetHeader(nsHttp::Range, byteRange); 1.2498 + return !byteRange.EqualsLiteral("bytes=0-"); 1.2499 +} 1.2500 + 1.2501 +nsresult 1.2502 +nsHttpChannel::OpenCacheEntry(bool usingSSL) 1.2503 +{ 1.2504 + MOZ_EVENT_TRACER_EXEC(this, "net::http::OpenCacheEntry"); 1.2505 + 1.2506 + // Handle correctly mCacheEntriesToWaitFor 1.2507 + AutoCacheWaitFlags waitFlags(this); 1.2508 + 1.2509 + // Drop this flag here 1.2510 + mConcurentCacheAccess = 0; 1.2511 + 1.2512 + nsresult rv; 1.2513 + 1.2514 + mLoadedFromApplicationCache = false; 1.2515 + mHasQueryString = HasQueryString(mRequestHead.ParsedMethod(), mURI); 1.2516 + 1.2517 + LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this)); 1.2518 + 1.2519 + // make sure we're not abusing this function 1.2520 + NS_PRECONDITION(!mCacheEntry, "cache entry already open"); 1.2521 + 1.2522 + nsAutoCString cacheKey; 1.2523 + 1.2524 + if (mRequestHead.IsPost()) { 1.2525 + // If the post id is already set then this is an attempt to replay 1.2526 + // a post transaction via the cache. Otherwise, we need a unique 1.2527 + // post id for this transaction. 1.2528 + if (mPostID == 0) 1.2529 + mPostID = gHttpHandler->GenerateUniqueID(); 1.2530 + } 1.2531 + else if (!mRequestHead.IsGet() && !mRequestHead.IsHead()) { 1.2532 + // don't use the cache for other types of requests 1.2533 + return NS_OK; 1.2534 + } 1.2535 + 1.2536 + if (mResuming) { 1.2537 + // We don't support caching for requests initiated 1.2538 + // via nsIResumableChannel. 1.2539 + return NS_OK; 1.2540 + } 1.2541 + 1.2542 + // Don't cache byte range requests which are subranges, only cache 0- 1.2543 + // byte range requests. 1.2544 + if (IsSubRangeRequest(mRequestHead)) 1.2545 + return NS_OK; 1.2546 + 1.2547 + // Pick up an application cache from the notification 1.2548 + // callbacks if available 1.2549 + if (!mApplicationCache && mInheritApplicationCache) { 1.2550 + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer; 1.2551 + GetCallback(appCacheContainer); 1.2552 + 1.2553 + if (appCacheContainer) { 1.2554 + appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache)); 1.2555 + } 1.2556 + } 1.2557 + 1.2558 + nsCOMPtr<nsICacheStorageService> cacheStorageService = 1.2559 + do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); 1.2560 + NS_ENSURE_SUCCESS(rv, rv); 1.2561 + 1.2562 + nsRefPtr<LoadContextInfo> info = GetLoadContextInfo(this); 1.2563 + nsCOMPtr<nsICacheStorage> cacheStorage; 1.2564 + nsCOMPtr<nsIURI> openURI; 1.2565 + 1.2566 + /* Obtain optional third party isolation domain */ 1.2567 + nsAutoCString cacheDomain; 1.2568 + nsCOMPtr<nsIURI> firstPartyIsolationURI; 1.2569 + nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc 1.2570 + = do_GetService(THIRDPARTYUTIL_CONTRACTID); 1.2571 + rv = thirdPartySvc->GetFirstPartyIsolationURI(this, nullptr, 1.2572 + getter_AddRefs(firstPartyIsolationURI)); 1.2573 + if (NS_SUCCEEDED(rv) && firstPartyIsolationURI) { 1.2574 + thirdPartySvc->GetFirstPartyHostForIsolation(firstPartyIsolationURI, 1.2575 + cacheDomain); 1.2576 + } 1.2577 + 1.2578 + if (!mFallbackKey.IsEmpty() && mFallbackChannel) { 1.2579 + // This is a fallback channel, open fallback URI instead 1.2580 + rv = NS_NewURI(getter_AddRefs(openURI), mFallbackKey); 1.2581 + NS_ENSURE_SUCCESS(rv, rv); 1.2582 + } 1.2583 + else { 1.2584 + openURI = mURI; 1.2585 + } 1.2586 + 1.2587 + uint32_t cacheEntryOpenFlags; 1.2588 + bool offline = gIOService->IsOffline(); 1.2589 + if (offline || (mLoadFlags & INHIBIT_CACHING)) { 1.2590 + if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) { 1.2591 + goto bypassCacheEntryOpen; 1.2592 + } 1.2593 + cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY; 1.2594 + mCacheEntryIsReadOnly = true; 1.2595 + } 1.2596 + else if (BYPASS_LOCAL_CACHE(mLoadFlags) && !mApplicationCache) { 1.2597 + cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE; 1.2598 + } 1.2599 + else { 1.2600 + cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY 1.2601 + | nsICacheStorage::CHECK_MULTITHREADED; 1.2602 + } 1.2603 + 1.2604 + if (mApplicationCache) { 1.2605 + rv = cacheStorageService->AppCacheStorage(info, 1.2606 + mApplicationCache, 1.2607 + getter_AddRefs(cacheStorage)); 1.2608 + } 1.2609 + else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) { 1.2610 + rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well... 1.2611 + getter_AddRefs(cacheStorage)); 1.2612 + } 1.2613 + else { 1.2614 + rv = cacheStorageService->DiskCacheStorage(info, 1.2615 + mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE), 1.2616 + getter_AddRefs(cacheStorage)); 1.2617 + } 1.2618 + NS_ENSURE_SUCCESS(rv, rv); 1.2619 + 1.2620 + // Don't consider mLoadUnblocked here, since it's not indication of a demand 1.2621 + // to load prioritly. It's mostly used to load XHR requests, but those should 1.2622 + // not be considered as influencing the page load performance. 1.2623 + if (mLoadAsBlocking || (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)) 1.2624 + cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY; 1.2625 + 1.2626 + // Only for backward compatibility with the old cache back end. 1.2627 + // When removed, remove the flags and related code snippets. 1.2628 + if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) 1.2629 + cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY; 1.2630 + 1.2631 + rv = cacheStorage->AsyncOpenURI( 1.2632 + openURI, nsPrintfCString("%s@%d", cacheDomain.get(), mPostID), 1.2633 + cacheEntryOpenFlags, this); 1.2634 + NS_ENSURE_SUCCESS(rv, rv); 1.2635 + 1.2636 + waitFlags.Keep(WAIT_FOR_CACHE_ENTRY); 1.2637 + 1.2638 +bypassCacheEntryOpen: 1.2639 + if (!mApplicationCacheForWrite) 1.2640 + return NS_OK; 1.2641 + 1.2642 + // If there is an app cache to write to, open the entry right now in parallel. 1.2643 + 1.2644 + // make sure we're not abusing this function 1.2645 + NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open"); 1.2646 + 1.2647 + if (offline) { 1.2648 + // only put things in the offline cache while online 1.2649 + return NS_OK; 1.2650 + } 1.2651 + 1.2652 + if (mLoadFlags & INHIBIT_CACHING) { 1.2653 + // respect demand not to cache 1.2654 + return NS_OK; 1.2655 + } 1.2656 + 1.2657 + if (!mRequestHead.IsGet()) { 1.2658 + // only cache complete documents offline 1.2659 + return NS_OK; 1.2660 + } 1.2661 + 1.2662 + rv = cacheStorageService->AppCacheStorage(info, mApplicationCacheForWrite, 1.2663 + getter_AddRefs(cacheStorage)); 1.2664 + NS_ENSURE_SUCCESS(rv, rv); 1.2665 + 1.2666 + rv = cacheStorage->AsyncOpenURI( 1.2667 + mURI, cacheDomain, nsICacheStorage::OPEN_TRUNCATE, this); 1.2668 + NS_ENSURE_SUCCESS(rv, rv); 1.2669 + 1.2670 + waitFlags.Keep(WAIT_FOR_OFFLINE_CACHE_ENTRY); 1.2671 + 1.2672 + return NS_OK; 1.2673 +} 1.2674 + 1.2675 +nsresult 1.2676 +nsHttpChannel::CheckPartial(nsICacheEntry* aEntry, int64_t *aSize, int64_t *aContentLength) 1.2677 +{ 1.2678 + nsresult rv; 1.2679 + 1.2680 + rv = aEntry->GetDataSize(aSize); 1.2681 + 1.2682 + if (NS_ERROR_IN_PROGRESS == rv) { 1.2683 + *aSize = -1; 1.2684 + rv = NS_OK; 1.2685 + } 1.2686 + 1.2687 + NS_ENSURE_SUCCESS(rv, rv); 1.2688 + 1.2689 + nsHttpResponseHead* responseHead = mCachedResponseHead 1.2690 + ? mCachedResponseHead 1.2691 + : mResponseHead; 1.2692 + 1.2693 + if (!responseHead) 1.2694 + return NS_ERROR_UNEXPECTED; 1.2695 + 1.2696 + *aContentLength = responseHead->ContentLength(); 1.2697 + 1.2698 + return NS_OK; 1.2699 +} 1.2700 + 1.2701 +NS_IMETHODIMP 1.2702 +nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appCache, 1.2703 + uint32_t* aResult) 1.2704 +{ 1.2705 + nsresult rv = NS_OK; 1.2706 + 1.2707 + LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]", 1.2708 + this, entry)); 1.2709 + 1.2710 + // Remember the request is a custom conditional request so that we can 1.2711 + // process any 304 response correctly. 1.2712 + mCustomConditionalRequest = 1.2713 + mRequestHead.PeekHeader(nsHttp::If_Modified_Since) || 1.2714 + mRequestHead.PeekHeader(nsHttp::If_None_Match) || 1.2715 + mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) || 1.2716 + mRequestHead.PeekHeader(nsHttp::If_Match) || 1.2717 + mRequestHead.PeekHeader(nsHttp::If_Range); 1.2718 + 1.2719 + // Be pessimistic: assume the cache entry has no useful data. 1.2720 + *aResult = ENTRY_WANTED; 1.2721 + mCachedContentIsValid = false; 1.2722 + 1.2723 + nsXPIDLCString buf; 1.2724 + 1.2725 + // Get the method that was used to generate the cached response 1.2726 + rv = entry->GetMetaDataElement("request-method", getter_Copies(buf)); 1.2727 + NS_ENSURE_SUCCESS(rv, rv); 1.2728 + 1.2729 + bool methodWasHead = buf.EqualsLiteral("HEAD"); 1.2730 + bool methodWasGet = buf.EqualsLiteral("GET"); 1.2731 + 1.2732 + if (methodWasHead) { 1.2733 + // The cached response does not contain an entity. We can only reuse 1.2734 + // the response if the current request is also HEAD. 1.2735 + if (!mRequestHead.IsHead()) { 1.2736 + return NS_OK; 1.2737 + } 1.2738 + } 1.2739 + buf.Adopt(0); 1.2740 + 1.2741 + // We'll need this value in later computations... 1.2742 + uint32_t lastModifiedTime; 1.2743 + rv = entry->GetLastModified(&lastModifiedTime); 1.2744 + NS_ENSURE_SUCCESS(rv, rv); 1.2745 + 1.2746 + // Determine if this is the first time that this cache entry 1.2747 + // has been accessed during this session. 1.2748 + bool fromPreviousSession = 1.2749 + (gHttpHandler->SessionStartTime() > lastModifiedTime); 1.2750 + 1.2751 + // Get the cached HTTP response headers 1.2752 + rv = entry->GetMetaDataElement("response-head", getter_Copies(buf)); 1.2753 + NS_ENSURE_SUCCESS(rv, rv); 1.2754 + 1.2755 + // Parse the cached HTTP response headers 1.2756 + mCachedResponseHead = new nsHttpResponseHead(); 1.2757 + rv = mCachedResponseHead->Parse((char *) buf.get()); 1.2758 + NS_ENSURE_SUCCESS(rv, rv); 1.2759 + buf.Adopt(0); 1.2760 + 1.2761 + bool isCachedRedirect = WillRedirect(mCachedResponseHead); 1.2762 + 1.2763 + // Do not return 304 responses from the cache, and also do not return 1.2764 + // any other non-redirect 3xx responses from the cache (see bug 759043). 1.2765 + NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) || 1.2766 + isCachedRedirect, NS_ERROR_ABORT); 1.2767 + 1.2768 + // Don't bother to validate items that are read-only, 1.2769 + // unless they are read-only because of INHIBIT_CACHING or because 1.2770 + // we're updating the offline cache. 1.2771 + // Don't bother to validate if this is a fallback entry. 1.2772 + if (!mApplicationCacheForWrite && 1.2773 + (appCache || 1.2774 + (mCacheEntryIsReadOnly && !(mLoadFlags & nsIRequest::INHIBIT_CACHING)) || 1.2775 + mFallbackChannel)) { 1.2776 + rv = OpenCacheInputStream(entry, true); 1.2777 + if (NS_SUCCEEDED(rv)) { 1.2778 + mCachedContentIsValid = true; 1.2779 + entry->MaybeMarkValid(); 1.2780 + } 1.2781 + return rv; 1.2782 + } 1.2783 + 1.2784 + bool wantCompleteEntry = false; 1.2785 + 1.2786 + if (!methodWasHead && !isCachedRedirect) { 1.2787 + // If the cached content-length is set and it does not match the data 1.2788 + // size of the cached content, then the cached response is partial... 1.2789 + // either we need to issue a byte range request or we need to refetch 1.2790 + // the entire document. 1.2791 + // 1.2792 + // We exclude redirects from this check because we (usually) strip the 1.2793 + // entity when we store the cache entry, and even if we didn't, we 1.2794 + // always ignore a cached redirect's entity anyway. See bug 759043. 1.2795 + int64_t size, contentLength; 1.2796 + rv = CheckPartial(entry, &size, &contentLength); 1.2797 + NS_ENSURE_SUCCESS(rv,rv); 1.2798 + 1.2799 + if (size == int64_t(-1)) { 1.2800 + LOG((" write is in progress")); 1.2801 + if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { 1.2802 + LOG((" not interested in the entry, " 1.2803 + "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified")); 1.2804 + 1.2805 + *aResult = ENTRY_NOT_WANTED; 1.2806 + return NS_OK; 1.2807 + } 1.2808 + 1.2809 + // Ignore !(size > 0) from the resumability condition 1.2810 + if (!IsResumable(size, contentLength, true)) { 1.2811 + LOG((" wait for entry completion, " 1.2812 + "response is not resumable")); 1.2813 + 1.2814 + wantCompleteEntry = true; 1.2815 + } 1.2816 + else { 1.2817 + mConcurentCacheAccess = 1; 1.2818 + } 1.2819 + } 1.2820 + else if (contentLength != int64_t(-1) && contentLength != size) { 1.2821 + LOG(("Cached data size does not match the Content-Length header " 1.2822 + "[content-length=%lld size=%lld]\n", contentLength, size)); 1.2823 + 1.2824 + rv = MaybeSetupByteRangeRequest(size, contentLength); 1.2825 + mCachedContentIsPartial = NS_SUCCEEDED(rv) && mIsPartialRequest; 1.2826 + if (mCachedContentIsPartial) { 1.2827 + rv = OpenCacheInputStream(entry, false); 1.2828 + *aResult = ENTRY_NEEDS_REVALIDATION; 1.2829 + } 1.2830 + return rv; 1.2831 + } 1.2832 + } 1.2833 + 1.2834 + bool usingSSL = false; 1.2835 + rv = mURI->SchemeIs("https", &usingSSL); 1.2836 + NS_ENSURE_SUCCESS(rv,rv); 1.2837 + 1.2838 + bool doValidation = false; 1.2839 + bool canAddImsHeader = true; 1.2840 + 1.2841 + // Cached entry is not the entity we request (see bug #633743) 1.2842 + if (ResponseWouldVary(entry)) { 1.2843 + LOG(("Validating based on Vary headers returning TRUE\n")); 1.2844 + canAddImsHeader = false; 1.2845 + doValidation = true; 1.2846 + } 1.2847 + // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used 1.2848 + else if (mLoadFlags & nsIRequest::LOAD_FROM_CACHE) { 1.2849 + LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n")); 1.2850 + doValidation = false; 1.2851 + } 1.2852 + // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until 1.2853 + // it's revalidated with the server. 1.2854 + else if (mLoadFlags & nsIRequest::VALIDATE_ALWAYS) { 1.2855 + LOG(("Validating based on VALIDATE_ALWAYS load flag\n")); 1.2856 + doValidation = true; 1.2857 + } 1.2858 + // Even if the VALIDATE_NEVER flag is set, there are still some cases in 1.2859 + // which we must validate the cached response with the server. 1.2860 + else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) { 1.2861 + LOG(("VALIDATE_NEVER set\n")); 1.2862 + // if no-store or if no-cache and ssl, validate cached response (see 1.2863 + // bug 112564 for an explanation of this logic) 1.2864 + if (mCachedResponseHead->NoStore() || 1.2865 + (mCachedResponseHead->NoCache() && usingSSL)) { 1.2866 + LOG(("Validating based on (no-store || (no-cache && ssl)) logic\n")); 1.2867 + doValidation = true; 1.2868 + } 1.2869 + else { 1.2870 + LOG(("NOT validating based on VALIDATE_NEVER load flag\n")); 1.2871 + doValidation = false; 1.2872 + } 1.2873 + } 1.2874 + // check if validation is strictly required... 1.2875 + else if (mCachedResponseHead->MustValidate()) { 1.2876 + LOG(("Validating based on MustValidate() returning TRUE\n")); 1.2877 + doValidation = true; 1.2878 + } 1.2879 + else if (MustValidateBasedOnQueryUrl()) { 1.2880 + LOG(("Validating based on RFC 2616 section 13.9 " 1.2881 + "(query-url w/o explicit expiration-time)\n")); 1.2882 + doValidation = true; 1.2883 + } 1.2884 + // Check if the cache entry has expired... 1.2885 + else { 1.2886 + uint32_t time = 0; // a temporary variable for storing time values... 1.2887 + 1.2888 + rv = entry->GetExpirationTime(&time); 1.2889 + NS_ENSURE_SUCCESS(rv, rv); 1.2890 + 1.2891 + LOG((" NowInSeconds()=%u, time=%u", NowInSeconds(), time)); 1.2892 + if (NowInSeconds() <= time) 1.2893 + doValidation = false; 1.2894 + else if (mCachedResponseHead->MustValidateIfExpired()) 1.2895 + doValidation = true; 1.2896 + else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) { 1.2897 + // If the cached response does not include expiration infor- 1.2898 + // mation, then we must validate the response, despite whether 1.2899 + // or not this is the first access this session. This behavior 1.2900 + // is consistent with existing browsers and is generally expected 1.2901 + // by web authors. 1.2902 + rv = mCachedResponseHead->ComputeFreshnessLifetime(&time); 1.2903 + NS_ENSURE_SUCCESS(rv, rv); 1.2904 + 1.2905 + if (time == 0) 1.2906 + doValidation = true; 1.2907 + else 1.2908 + doValidation = fromPreviousSession; 1.2909 + } 1.2910 + else 1.2911 + doValidation = true; 1.2912 + 1.2913 + LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v")); 1.2914 + } 1.2915 + 1.2916 + if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) && 1.2917 + (methodWasGet || methodWasHead)) { 1.2918 + const char *requestedETag, *cachedETag; 1.2919 + cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag); 1.2920 + requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match); 1.2921 + if (cachedETag && (!strncmp(cachedETag, "W/", 2) || 1.2922 + strcmp(requestedETag, cachedETag))) { 1.2923 + // User has defined If-Match header, if the cached entry is not 1.2924 + // matching the provided header value or the cached ETag is weak, 1.2925 + // force validation. 1.2926 + doValidation = true; 1.2927 + } 1.2928 + } 1.2929 + 1.2930 + if (!doValidation) { 1.2931 + // 1.2932 + // Check the authorization headers used to generate the cache entry. 1.2933 + // We must validate the cache entry if: 1.2934 + // 1.2935 + // 1) the cache entry was generated prior to this session w/ 1.2936 + // credentials (see bug 103402). 1.2937 + // 2) the cache entry was generated w/o credentials, but would now 1.2938 + // require credentials (see bug 96705). 1.2939 + // 1.2940 + // NOTE: this does not apply to proxy authentication. 1.2941 + // 1.2942 + entry->GetMetaDataElement("auth", getter_Copies(buf)); 1.2943 + doValidation = 1.2944 + (fromPreviousSession && !buf.IsEmpty()) || 1.2945 + (buf.IsEmpty() && mRequestHead.PeekHeader(nsHttp::Authorization)); 1.2946 + } 1.2947 + 1.2948 + // Bug #561276: We maintain a chain of cache-keys which returns cached 1.2949 + // 3xx-responses (redirects) in order to detect cycles. If a cycle is 1.2950 + // found, ignore the cached response and hit the net. Otherwise, use 1.2951 + // the cached response and add the cache-key to the chain. Note that 1.2952 + // a limited number of redirects (cached or not) is allowed and is 1.2953 + // enforced independently of this mechanism 1.2954 + if (!doValidation && isCachedRedirect) { 1.2955 + nsAutoCString cacheKey; 1.2956 + GenerateCacheKey(mPostID, cacheKey); 1.2957 + 1.2958 + if (!mRedirectedCachekeys) 1.2959 + mRedirectedCachekeys = new nsTArray<nsCString>(); 1.2960 + else if (mRedirectedCachekeys->Contains(cacheKey)) 1.2961 + doValidation = true; 1.2962 + 1.2963 + LOG(("Redirection-chain %s key %s\n", 1.2964 + doValidation ? "contains" : "does not contain", cacheKey.get())); 1.2965 + 1.2966 + // Append cacheKey if not in the chain already 1.2967 + if (!doValidation) 1.2968 + mRedirectedCachekeys->AppendElement(cacheKey); 1.2969 + } 1.2970 + 1.2971 + mCachedContentIsValid = !doValidation; 1.2972 + 1.2973 + if (doValidation) { 1.2974 + // 1.2975 + // now, we are definitely going to issue a HTTP request to the server. 1.2976 + // make it conditional if possible. 1.2977 + // 1.2978 + // do not attempt to validate no-store content, since servers will not 1.2979 + // expect it to be cached. (we only keep it in our cache for the 1.2980 + // purposes of back/forward, etc.) 1.2981 + // 1.2982 + // the request method MUST be either GET or HEAD (see bug 175641). 1.2983 + // 1.2984 + // do not override conditional headers when consumer has defined its own 1.2985 + if (!mCachedResponseHead->NoStore() && 1.2986 + (mRequestHead.IsGet() || mRequestHead.IsHead()) && 1.2987 + !mCustomConditionalRequest) { 1.2988 + 1.2989 + if (mConcurentCacheAccess) { 1.2990 + // In case of concurrent read and also validation request we 1.2991 + // must wait for the current writer to close the output stream 1.2992 + // first. Otherwise, when the writer's job would have been interrupted 1.2993 + // before all the data were downloaded, we'd have to do a range request 1.2994 + // which would be a second request in line during this channel's 1.2995 + // life-time. nsHttpChannel is not designed to do that, so rather 1.2996 + // turn off concurrent read and wait for entry's completion. 1.2997 + // Then only re-validation or range-re-validation request will go out. 1.2998 + mConcurentCacheAccess = 0; 1.2999 + // This will cause that OnCacheEntryCheck is called again with the same 1.3000 + // entry after the writer is done. 1.3001 + wantCompleteEntry = true; 1.3002 + } else { 1.3003 + const char *val; 1.3004 + // Add If-Modified-Since header if a Last-Modified was given 1.3005 + // and we are allowed to do this (see bugs 510359 and 269303) 1.3006 + if (canAddImsHeader) { 1.3007 + val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified); 1.3008 + if (val) 1.3009 + mRequestHead.SetHeader(nsHttp::If_Modified_Since, 1.3010 + nsDependentCString(val)); 1.3011 + } 1.3012 + // Add If-None-Match header if an ETag was given in the response 1.3013 + val = mCachedResponseHead->PeekHeader(nsHttp::ETag); 1.3014 + if (val) 1.3015 + mRequestHead.SetHeader(nsHttp::If_None_Match, 1.3016 + nsDependentCString(val)); 1.3017 + mDidReval = true; 1.3018 + } 1.3019 + } 1.3020 + } 1.3021 + 1.3022 + if (mCachedContentIsValid || mDidReval) { 1.3023 + rv = OpenCacheInputStream(entry, mCachedContentIsValid); 1.3024 + if (NS_FAILED(rv)) { 1.3025 + // If we can't get the entity then we have to act as though we 1.3026 + // don't have the cache entry. 1.3027 + if (mDidReval) { 1.3028 + // Make the request unconditional again. 1.3029 + mRequestHead.ClearHeader(nsHttp::If_Modified_Since); 1.3030 + mRequestHead.ClearHeader(nsHttp::If_None_Match); 1.3031 + mRequestHead.ClearHeader(nsHttp::ETag); 1.3032 + mDidReval = false; 1.3033 + } 1.3034 + mCachedContentIsValid = false; 1.3035 + } 1.3036 + } 1.3037 + 1.3038 + if (mDidReval) 1.3039 + *aResult = ENTRY_NEEDS_REVALIDATION; 1.3040 + else if (wantCompleteEntry) 1.3041 + *aResult = RECHECK_AFTER_WRITE_FINISHED; 1.3042 + else 1.3043 + *aResult = ENTRY_WANTED; 1.3044 + 1.3045 + if (mCachedContentIsValid) { 1.3046 + entry->MaybeMarkValid(); 1.3047 + } 1.3048 + 1.3049 + LOG(("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d result=%d]\n", 1.3050 + this, doValidation, *aResult)); 1.3051 + return rv; 1.3052 +} 1.3053 + 1.3054 +NS_IMETHODIMP 1.3055 +nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry *entry, 1.3056 + bool aNew, 1.3057 + nsIApplicationCache* aAppCache, 1.3058 + nsresult status) 1.3059 +{ 1.3060 + MOZ_ASSERT(NS_IsMainThread()); 1.3061 + 1.3062 + nsresult rv; 1.3063 + 1.3064 + LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p " 1.3065 + "new=%d appcache=%p status=%x]\n", this, entry, aNew, aAppCache, status)); 1.3066 + 1.3067 + // if the channel's already fired onStopRequest, then we should ignore 1.3068 + // this event. 1.3069 + if (!mIsPending) { 1.3070 + mCacheInputStream.CloseAndRelease(); 1.3071 + return NS_OK; 1.3072 + } 1.3073 + 1.3074 + rv = OnCacheEntryAvailableInternal(entry, aNew, aAppCache, status); 1.3075 + if (NS_FAILED(rv)) { 1.3076 + CloseCacheEntry(true); 1.3077 + AsyncAbort(rv); 1.3078 + } 1.3079 + 1.3080 + return NS_OK; 1.3081 +} 1.3082 + 1.3083 +nsresult 1.3084 +nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry *entry, 1.3085 + bool aNew, 1.3086 + nsIApplicationCache* aAppCache, 1.3087 + nsresult status) 1.3088 +{ 1.3089 + nsresult rv; 1.3090 + 1.3091 + if (mCanceled) { 1.3092 + LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); 1.3093 + return mStatus; 1.3094 + } 1.3095 + 1.3096 + if (aAppCache) { 1.3097 + if (mApplicationCache == aAppCache && !mCacheEntry) { 1.3098 + rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status); 1.3099 + } 1.3100 + else if (mApplicationCacheForWrite == aAppCache && aNew && !mOfflineCacheEntry) { 1.3101 + rv = OnOfflineCacheEntryForWritingAvailable(entry, aAppCache, status); 1.3102 + } 1.3103 + else { 1.3104 + rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status); 1.3105 + } 1.3106 + } 1.3107 + else { 1.3108 + rv = OnNormalCacheEntryAvailable(entry, aNew, status); 1.3109 + } 1.3110 + 1.3111 + if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) { 1.3112 + // If we have a fallback URI (and we're not already 1.3113 + // falling back), process the fallback asynchronously. 1.3114 + if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { 1.3115 + return AsyncCall(&nsHttpChannel::HandleAsyncFallback); 1.3116 + } 1.3117 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.3118 + } 1.3119 + 1.3120 + if (NS_FAILED(rv)) 1.3121 + return rv; 1.3122 + 1.3123 + // We may be waiting for more callbacks... 1.3124 + if (mCacheEntriesToWaitFor) 1.3125 + return NS_OK; 1.3126 + 1.3127 + return ContinueConnect(); 1.3128 +} 1.3129 + 1.3130 +nsresult 1.3131 +nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry, 1.3132 + bool aNew, 1.3133 + nsresult aEntryStatus) 1.3134 +{ 1.3135 + mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY; 1.3136 + 1.3137 + if (NS_FAILED(aEntryStatus) || aNew) { 1.3138 + // Make sure this flag is dropped. It may happen the entry is doomed 1.3139 + // between OnCacheEntryCheck and OnCacheEntryAvailable. 1.3140 + mCachedContentIsValid = false; 1.3141 + 1.3142 + if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1.3143 + // if this channel is only allowed to pull from the cache, then 1.3144 + // we must fail if we were unable to open a cache entry for read. 1.3145 + return NS_ERROR_DOCUMENT_NOT_CACHED; 1.3146 + } 1.3147 + } 1.3148 + 1.3149 + if (NS_SUCCEEDED(aEntryStatus)) { 1.3150 + mCacheEntry = aEntry; 1.3151 + mCacheEntryIsWriteOnly = aNew; 1.3152 + 1.3153 + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { 1.3154 + Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, 1.3155 + false); 1.3156 + } 1.3157 + } 1.3158 + 1.3159 + return NS_OK; 1.3160 +} 1.3161 + 1.3162 +nsresult 1.3163 +nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntry *aEntry, 1.3164 + bool aNew, 1.3165 + nsIApplicationCache* aAppCache, 1.3166 + nsresult aEntryStatus) 1.3167 +{ 1.3168 + MOZ_ASSERT(!mApplicationCache || aAppCache == mApplicationCache); 1.3169 + MOZ_ASSERT(!aNew || !aEntry || mApplicationCacheForWrite); 1.3170 + 1.3171 + mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY; 1.3172 + 1.3173 + nsresult rv; 1.3174 + 1.3175 + if (!mApplicationCache) 1.3176 + mApplicationCache = aAppCache; 1.3177 + 1.3178 + if (NS_SUCCEEDED(aEntryStatus)) { 1.3179 + // We successfully opened an offline cache session and the entry, 1.3180 + // so indicate we will load from the offline cache. 1.3181 + mLoadedFromApplicationCache = true; 1.3182 + mCacheEntryIsReadOnly = true; 1.3183 + mCacheEntry = aEntry; 1.3184 + mCacheEntryIsWriteOnly = false; 1.3185 + 1.3186 + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI && !mApplicationCacheForWrite) { 1.3187 + Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD, 1.3188 + true); 1.3189 + } 1.3190 + 1.3191 + return NS_OK; 1.3192 + } 1.3193 + 1.3194 + if (!mApplicationCacheForWrite && !mFallbackChannel) { 1.3195 + // Check for namespace match. 1.3196 + nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry; 1.3197 + rv = mApplicationCache->GetMatchingNamespace(mSpec, 1.3198 + getter_AddRefs(namespaceEntry)); 1.3199 + NS_ENSURE_SUCCESS(rv, rv); 1.3200 + 1.3201 + uint32_t namespaceType = 0; 1.3202 + if (!namespaceEntry || 1.3203 + NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) || 1.3204 + (namespaceType & 1.3205 + (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK | 1.3206 + nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) { 1.3207 + // When loading from an application cache, only items 1.3208 + // on the whitelist or matching a 1.3209 + // fallback namespace should hit the network... 1.3210 + mLoadFlags |= LOAD_ONLY_FROM_CACHE; 1.3211 + 1.3212 + // ... and if there were an application cache entry, 1.3213 + // we would have found it earlier. 1.3214 + return NS_ERROR_CACHE_KEY_NOT_FOUND; 1.3215 + } 1.3216 + 1.3217 + if (namespaceType & 1.3218 + nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) { 1.3219 + rv = namespaceEntry->GetData(mFallbackKey); 1.3220 + NS_ENSURE_SUCCESS(rv, rv); 1.3221 + } 1.3222 + } 1.3223 + 1.3224 + return NS_OK; 1.3225 +} 1.3226 + 1.3227 +nsresult 1.3228 +nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(nsICacheEntry *aEntry, 1.3229 + nsIApplicationCache* aAppCache, 1.3230 + nsresult aEntryStatus) 1.3231 +{ 1.3232 + MOZ_ASSERT(mApplicationCacheForWrite && aAppCache == mApplicationCacheForWrite); 1.3233 + 1.3234 + mCacheEntriesToWaitFor &= ~WAIT_FOR_OFFLINE_CACHE_ENTRY; 1.3235 + 1.3236 + if (NS_SUCCEEDED(aEntryStatus)) { 1.3237 + mOfflineCacheEntry = aEntry; 1.3238 + if (NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))) { 1.3239 + mOfflineCacheLastModifiedTime = 0; 1.3240 + } 1.3241 + } 1.3242 + 1.3243 + return aEntryStatus; 1.3244 +} 1.3245 + 1.3246 +// Generates the proper cache-key for this instance of nsHttpChannel 1.3247 +nsresult 1.3248 +nsHttpChannel::GenerateCacheKey(uint32_t postID, nsACString &cacheKey) 1.3249 +{ 1.3250 + AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(), 1.3251 + postID, cacheKey); 1.3252 + return NS_OK; 1.3253 +} 1.3254 + 1.3255 +// Assembles a cache-key from the given pieces of information and |mLoadFlags| 1.3256 +void 1.3257 +nsHttpChannel::AssembleCacheKey(const char *spec, uint32_t postID, 1.3258 + nsACString &cacheKey) 1.3259 +{ 1.3260 + cacheKey.Truncate(); 1.3261 + 1.3262 + if (mLoadFlags & LOAD_ANONYMOUS) { 1.3263 + cacheKey.AssignLiteral("anon&"); 1.3264 + } 1.3265 + 1.3266 + if (postID) { 1.3267 + char buf[32]; 1.3268 + PR_snprintf(buf, sizeof(buf), "id=%x&", postID); 1.3269 + cacheKey.Append(buf); 1.3270 + } 1.3271 + 1.3272 + if (strlen(mCacheDomain.get()) > 0) { 1.3273 + cacheKey.AppendLiteral("domain="); 1.3274 + cacheKey.Append(mCacheDomain.get()); 1.3275 + cacheKey.AppendLiteral("&"); 1.3276 + } 1.3277 + 1.3278 + if (!cacheKey.IsEmpty()) { 1.3279 + cacheKey.AppendLiteral("uri="); 1.3280 + } 1.3281 + 1.3282 + // Strip any trailing #ref from the URL before using it as the key 1.3283 + const char *p = strchr(spec, '#'); 1.3284 + if (p) 1.3285 + cacheKey.Append(spec, p - spec); 1.3286 + else 1.3287 + cacheKey.Append(spec); 1.3288 +} 1.3289 + 1.3290 +// UpdateExpirationTime is called when a new response comes in from the server. 1.3291 +// It updates the stored response-time and sets the expiration time on the 1.3292 +// cache entry. 1.3293 +// 1.3294 +// From section 13.2.4 of RFC2616, we compute expiration time as follows: 1.3295 +// 1.3296 +// timeRemaining = freshnessLifetime - currentAge 1.3297 +// expirationTime = now + timeRemaining 1.3298 +// 1.3299 +nsresult 1.3300 +nsHttpChannel::UpdateExpirationTime() 1.3301 +{ 1.3302 + NS_ENSURE_TRUE(mResponseHead, NS_ERROR_FAILURE); 1.3303 + 1.3304 + nsresult rv; 1.3305 + 1.3306 + uint32_t expirationTime = 0; 1.3307 + if (!mResponseHead->MustValidate()) { 1.3308 + uint32_t freshnessLifetime = 0; 1.3309 + 1.3310 + rv = mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime); 1.3311 + if (NS_FAILED(rv)) return rv; 1.3312 + 1.3313 + if (freshnessLifetime > 0) { 1.3314 + uint32_t now = NowInSeconds(), currentAge = 0; 1.3315 + 1.3316 + rv = mResponseHead->ComputeCurrentAge(now, mRequestTime, ¤tAge); 1.3317 + if (NS_FAILED(rv)) return rv; 1.3318 + 1.3319 + LOG(("freshnessLifetime = %u, currentAge = %u\n", 1.3320 + freshnessLifetime, currentAge)); 1.3321 + 1.3322 + if (freshnessLifetime > currentAge) { 1.3323 + uint32_t timeRemaining = freshnessLifetime - currentAge; 1.3324 + // be careful... now + timeRemaining may overflow 1.3325 + if (now + timeRemaining < now) 1.3326 + expirationTime = uint32_t(-1); 1.3327 + else 1.3328 + expirationTime = now + timeRemaining; 1.3329 + } 1.3330 + else 1.3331 + expirationTime = now; 1.3332 + } 1.3333 + } 1.3334 + 1.3335 + rv = mCacheEntry->SetExpirationTime(expirationTime); 1.3336 + NS_ENSURE_SUCCESS(rv, rv); 1.3337 + 1.3338 + if (mOfflineCacheEntry) { 1.3339 + rv = mOfflineCacheEntry->SetExpirationTime(expirationTime); 1.3340 + NS_ENSURE_SUCCESS(rv, rv); 1.3341 + } 1.3342 + 1.3343 + return NS_OK; 1.3344 +} 1.3345 + 1.3346 +/*static*/ inline bool 1.3347 +nsHttpChannel::HasQueryString(nsHttpRequestHead::ParsedMethodType method, nsIURI * uri) 1.3348 +{ 1.3349 + // Must be called on the main thread because nsIURI does not implement 1.3350 + // thread-safe QueryInterface. 1.3351 + MOZ_ASSERT(NS_IsMainThread()); 1.3352 + 1.3353 + if (method != nsHttpRequestHead::kMethod_Get && 1.3354 + method != nsHttpRequestHead::kMethod_Head) 1.3355 + return false; 1.3356 + 1.3357 + nsAutoCString query; 1.3358 + nsCOMPtr<nsIURL> url = do_QueryInterface(uri); 1.3359 + nsresult rv = url->GetQuery(query); 1.3360 + return NS_SUCCEEDED(rv) && !query.IsEmpty(); 1.3361 +} 1.3362 + 1.3363 +bool 1.3364 +nsHttpChannel::MustValidateBasedOnQueryUrl() const 1.3365 +{ 1.3366 + // RFC 2616, section 13.9 states that GET-requests with a query-url 1.3367 + // MUST NOT be treated as fresh unless the server explicitly provides 1.3368 + // an expiration-time in the response. See bug #468594 1.3369 + // Section 13.2.1 (6th paragraph) defines "explicit expiration time" 1.3370 + if (mHasQueryString) 1.3371 + { 1.3372 + uint32_t tmp; // we don't need the value, just whether it's set 1.3373 + nsresult rv = mCachedResponseHead->GetExpiresValue(&tmp); 1.3374 + if (NS_FAILED(rv)) { 1.3375 + rv = mCachedResponseHead->GetMaxAgeValue(&tmp); 1.3376 + if (NS_FAILED(rv)) { 1.3377 + return true; 1.3378 + } 1.3379 + } 1.3380 + } 1.3381 + return false; 1.3382 +} 1.3383 + 1.3384 + 1.3385 +bool 1.3386 +nsHttpChannel::ShouldUpdateOfflineCacheEntry() 1.3387 +{ 1.3388 + if (!mApplicationCacheForWrite || !mOfflineCacheEntry) { 1.3389 + return false; 1.3390 + } 1.3391 + 1.3392 + // if we're updating the cache entry, update the offline cache entry too 1.3393 + if (mCacheEntry && mCacheEntryIsWriteOnly) { 1.3394 + return true; 1.3395 + } 1.3396 + 1.3397 + // if there's nothing in the offline cache, add it 1.3398 + if (mOfflineCacheEntry) { 1.3399 + return true; 1.3400 + } 1.3401 + 1.3402 + // if the document is newer than the offline entry, update it 1.3403 + uint32_t docLastModifiedTime; 1.3404 + nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime); 1.3405 + if (NS_FAILED(rv)) { 1.3406 + return true; 1.3407 + } 1.3408 + 1.3409 + if (mOfflineCacheLastModifiedTime == 0) { 1.3410 + return false; 1.3411 + } 1.3412 + 1.3413 + if (docLastModifiedTime > mOfflineCacheLastModifiedTime) { 1.3414 + return true; 1.3415 + } 1.3416 + 1.3417 + return false; 1.3418 +} 1.3419 + 1.3420 +nsresult 1.3421 +nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering) 1.3422 +{ 1.3423 + nsresult rv; 1.3424 + 1.3425 + bool usingSSL = false; 1.3426 + rv = mURI->SchemeIs("https", &usingSSL); 1.3427 + NS_ENSURE_SUCCESS(rv,rv); 1.3428 + 1.3429 + if (usingSSL) { 1.3430 + rv = cacheEntry->GetSecurityInfo( 1.3431 + getter_AddRefs(mCachedSecurityInfo)); 1.3432 + if (NS_FAILED(rv)) { 1.3433 + LOG(("failed to parse security-info [channel=%p, entry=%p]", 1.3434 + this, cacheEntry)); 1.3435 + NS_WARNING("failed to parse security-info"); 1.3436 + return rv; 1.3437 + } 1.3438 + 1.3439 + // XXX: We should not be skilling this check in the offline cache 1.3440 + // case, but we have to do so now to work around bug 794507. 1.3441 + MOZ_ASSERT(mCachedSecurityInfo || mLoadedFromApplicationCache); 1.3442 + if (!mCachedSecurityInfo && !mLoadedFromApplicationCache) { 1.3443 + LOG(("mCacheEntry->GetSecurityInfo returned success but did not " 1.3444 + "return the security info [channel=%p, entry=%p]", 1.3445 + this, cacheEntry)); 1.3446 + return NS_ERROR_UNEXPECTED; // XXX error code 1.3447 + } 1.3448 + } 1.3449 + 1.3450 + // Keep the conditions below in sync with the conditions in ReadFromCache. 1.3451 + 1.3452 + rv = NS_OK; 1.3453 + 1.3454 + if (WillRedirect(mCachedResponseHead)) { 1.3455 + // Do not even try to read the entity for a redirect because we do not 1.3456 + // return an entity to the application when we process redirects. 1.3457 + LOG(("Will skip read of cached redirect entity\n")); 1.3458 + return NS_OK; 1.3459 + } 1.3460 + 1.3461 + if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) && 1.3462 + !mCachedContentIsPartial) { 1.3463 + // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the 1.3464 + // cached entity. 1.3465 + if (!mApplicationCacheForWrite) { 1.3466 + LOG(("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED " 1.3467 + "load flag\n")); 1.3468 + return NS_OK; 1.3469 + } 1.3470 + 1.3471 + // If offline caching has been requested and the offline cache needs 1.3472 + // updating, we must complete the call even if the main cache entry 1.3473 + // is up to date. We don't know yet for sure whether the offline 1.3474 + // cache needs updating because at this point we haven't opened it 1.3475 + // for writing yet, so we have to start reading the cached entity now 1.3476 + // just in case. 1.3477 + LOG(("May skip read from cache based on LOAD_ONLY_IF_MODIFIED " 1.3478 + "load flag\n")); 1.3479 + } 1.3480 + 1.3481 + // Open an input stream for the entity, so that the call to OpenInputStream 1.3482 + // happens off the main thread. 1.3483 + nsCOMPtr<nsIInputStream> stream; 1.3484 + rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream)); 1.3485 + 1.3486 + if (NS_FAILED(rv)) { 1.3487 + LOG(("Failed to open cache input stream [channel=%p, " 1.3488 + "mCacheEntry=%p]", this, cacheEntry)); 1.3489 + return rv; 1.3490 + } 1.3491 + 1.3492 + if (startBuffering) { 1.3493 + bool nonBlocking; 1.3494 + rv = stream->IsNonBlocking(&nonBlocking); 1.3495 + if (NS_SUCCEEDED(rv) && nonBlocking) 1.3496 + startBuffering = false; 1.3497 + } 1.3498 + 1.3499 + if (!startBuffering) { 1.3500 + // Bypass wrapping the input stream for the new cache back-end since 1.3501 + // nsIStreamTransportService expects a blocking stream. Preloading of 1.3502 + // the data must be done on the level of the cache backend, internally. 1.3503 + // 1.3504 + // We do not connect the stream to the stream transport service if we 1.3505 + // have to validate the entry with the server. If we did, we would get 1.3506 + // into a race condition between the stream transport service reading 1.3507 + // the existing contents and the opening of the cache entry's output 1.3508 + // stream to write the new contents in the case where we get a non-304 1.3509 + // response. 1.3510 + LOG(("Opened cache input stream without buffering [channel=%p, " 1.3511 + "mCacheEntry=%p, stream=%p]", this, 1.3512 + cacheEntry, stream.get())); 1.3513 + mCacheInputStream.takeOver(stream); 1.3514 + return rv; 1.3515 + } 1.3516 + 1.3517 + // Have the stream transport service start reading the entity on one of its 1.3518 + // background threads. 1.3519 + 1.3520 + nsCOMPtr<nsITransport> transport; 1.3521 + nsCOMPtr<nsIInputStream> wrapper; 1.3522 + 1.3523 + nsCOMPtr<nsIStreamTransportService> sts = 1.3524 + do_GetService(kStreamTransportServiceCID, &rv); 1.3525 + if (NS_SUCCEEDED(rv)) { 1.3526 + rv = sts->CreateInputTransport(stream, int64_t(-1), int64_t(-1), 1.3527 + true, getter_AddRefs(transport)); 1.3528 + } 1.3529 + if (NS_SUCCEEDED(rv)) { 1.3530 + rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper)); 1.3531 + } 1.3532 + if (NS_SUCCEEDED(rv)) { 1.3533 + LOG(("Opened cache input stream [channel=%p, wrapper=%p, " 1.3534 + "transport=%p, stream=%p]", this, wrapper.get(), 1.3535 + transport.get(), stream.get())); 1.3536 + } else { 1.3537 + LOG(("Failed to open cache input stream [channel=%p, " 1.3538 + "wrapper=%p, transport=%p, stream=%p]", this, 1.3539 + wrapper.get(), transport.get(), stream.get())); 1.3540 + 1.3541 + stream->Close(); 1.3542 + return rv; 1.3543 + } 1.3544 + 1.3545 + mCacheInputStream.takeOver(wrapper); 1.3546 + 1.3547 + return NS_OK; 1.3548 +} 1.3549 + 1.3550 +// Actually process the cached response that we started to handle in CheckCache 1.3551 +// and/or StartBufferingCachedEntity. 1.3552 +nsresult 1.3553 +nsHttpChannel::ReadFromCache(bool alreadyMarkedValid) 1.3554 +{ 1.3555 + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE); 1.3556 + NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE); 1.3557 + 1.3558 + LOG(("nsHttpChannel::ReadFromCache [this=%p] " 1.3559 + "Using cached copy of: %s\n", this, mSpec.get())); 1.3560 + 1.3561 + if (mCachedResponseHead) 1.3562 + mResponseHead = mCachedResponseHead; 1.3563 + 1.3564 + UpdateInhibitPersistentCachingFlag(); 1.3565 + 1.3566 + // if we don't already have security info, try to get it from the cache 1.3567 + // entry. there are two cases to consider here: 1) we are just reading 1.3568 + // from the cache, or 2) this may be due to a 304 not modified response, 1.3569 + // in which case we could have security info from a socket transport. 1.3570 + if (!mSecurityInfo) 1.3571 + mSecurityInfo = mCachedSecurityInfo; 1.3572 + 1.3573 + if (!alreadyMarkedValid && !mCachedContentIsPartial) { 1.3574 + // We validated the entry, and we have write access to the cache, so 1.3575 + // mark the cache entry as valid in order to allow others access to 1.3576 + // this cache entry. 1.3577 + // 1.3578 + // TODO: This should be done asynchronously so we don't take the cache 1.3579 + // service lock on the main thread. 1.3580 + mCacheEntry->MaybeMarkValid(); 1.3581 + } 1.3582 + 1.3583 + nsresult rv; 1.3584 + 1.3585 + // Keep the conditions below in sync with the conditions in 1.3586 + // StartBufferingCachedEntity. 1.3587 + 1.3588 + if (WillRedirect(mResponseHead)) { 1.3589 + // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here, 1.3590 + // to avoid event dispatching latency. 1.3591 + MOZ_ASSERT(!mCacheInputStream); 1.3592 + LOG(("Skipping skip read of cached redirect entity\n")); 1.3593 + return AsyncCall(&nsHttpChannel::HandleAsyncRedirect); 1.3594 + } 1.3595 + 1.3596 + if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) { 1.3597 + if (!mApplicationCacheForWrite) { 1.3598 + LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED " 1.3599 + "load flag\n")); 1.3600 + MOZ_ASSERT(!mCacheInputStream); 1.3601 + // TODO: Bug 759040 - We should call HandleAsyncNotModified directly 1.3602 + // here, to avoid event dispatching latency. 1.3603 + return AsyncCall(&nsHttpChannel::HandleAsyncNotModified); 1.3604 + } 1.3605 + 1.3606 + if (!ShouldUpdateOfflineCacheEntry()) { 1.3607 + LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED " 1.3608 + "load flag (mApplicationCacheForWrite not null case)\n")); 1.3609 + mCacheInputStream.CloseAndRelease(); 1.3610 + // TODO: Bug 759040 - We should call HandleAsyncNotModified directly 1.3611 + // here, to avoid event dispatching latency. 1.3612 + return AsyncCall(&nsHttpChannel::HandleAsyncNotModified); 1.3613 + } 1.3614 + } 1.3615 + 1.3616 + MOZ_ASSERT(mCacheInputStream); 1.3617 + if (!mCacheInputStream) { 1.3618 + NS_ERROR("mCacheInputStream is null but we're expecting to " 1.3619 + "be able to read from it."); 1.3620 + return NS_ERROR_UNEXPECTED; 1.3621 + } 1.3622 + 1.3623 + 1.3624 + nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget(); 1.3625 + 1.3626 + rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream, 1.3627 + int64_t(-1), int64_t(-1), 0, 0, true); 1.3628 + if (NS_FAILED(rv)) { 1.3629 + inputStream->Close(); 1.3630 + return rv; 1.3631 + } 1.3632 + 1.3633 + rv = mCachePump->AsyncRead(this, mListenerContext); 1.3634 + if (NS_FAILED(rv)) return rv; 1.3635 + 1.3636 + if (mTimingEnabled) 1.3637 + mCacheReadStart = TimeStamp::Now(); 1.3638 + 1.3639 + uint32_t suspendCount = mSuspendCount; 1.3640 + while (suspendCount--) 1.3641 + mCachePump->Suspend(); 1.3642 + 1.3643 + return NS_OK; 1.3644 +} 1.3645 + 1.3646 +void 1.3647 +nsHttpChannel::CloseCacheEntry(bool doomOnFailure) 1.3648 +{ 1.3649 + mCacheInputStream.CloseAndRelease(); 1.3650 + 1.3651 + if (!mCacheEntry) 1.3652 + return; 1.3653 + 1.3654 + LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%x mCacheEntryIsWriteOnly=%x", 1.3655 + this, mStatus, mCacheEntryIsWriteOnly)); 1.3656 + 1.3657 + // If we have begun to create or replace a cache entry, and that cache 1.3658 + // entry is not complete and not resumable, then it needs to be doomed. 1.3659 + // Otherwise, CheckCache will make the mistake of thinking that the 1.3660 + // partial cache entry is complete. 1.3661 + 1.3662 + bool doom = false; 1.3663 + if (mInitedCacheEntry) { 1.3664 + MOZ_ASSERT(mResponseHead, "oops"); 1.3665 + if (NS_FAILED(mStatus) && doomOnFailure && 1.3666 + mCacheEntryIsWriteOnly && !mResponseHead->IsResumable()) 1.3667 + doom = true; 1.3668 + } 1.3669 + else if (mCacheEntryIsWriteOnly) 1.3670 + doom = true; 1.3671 + 1.3672 + if (doom) { 1.3673 + LOG((" dooming cache entry!!")); 1.3674 + mCacheEntry->AsyncDoom(nullptr); 1.3675 + } 1.3676 + 1.3677 + mCachedResponseHead = nullptr; 1.3678 + 1.3679 + mCachePump = nullptr; 1.3680 + mCacheEntry = nullptr; 1.3681 + mCacheEntryIsWriteOnly = false; 1.3682 + mInitedCacheEntry = false; 1.3683 +} 1.3684 + 1.3685 + 1.3686 +void 1.3687 +nsHttpChannel::CloseOfflineCacheEntry() 1.3688 +{ 1.3689 + if (!mOfflineCacheEntry) 1.3690 + return; 1.3691 + 1.3692 + LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]", this)); 1.3693 + 1.3694 + if (NS_FAILED(mStatus)) { 1.3695 + mOfflineCacheEntry->AsyncDoom(nullptr); 1.3696 + } 1.3697 + else { 1.3698 + bool succeeded; 1.3699 + if (NS_SUCCEEDED(GetRequestSucceeded(&succeeded)) && !succeeded) 1.3700 + mOfflineCacheEntry->AsyncDoom(nullptr); 1.3701 + } 1.3702 + 1.3703 + mOfflineCacheEntry = nullptr; 1.3704 +} 1.3705 + 1.3706 + 1.3707 +// Initialize the cache entry for writing. 1.3708 +// - finalize storage policy 1.3709 +// - store security info 1.3710 +// - update expiration time 1.3711 +// - store headers and other meta data 1.3712 +nsresult 1.3713 +nsHttpChannel::InitCacheEntry() 1.3714 +{ 1.3715 + nsresult rv; 1.3716 + 1.3717 + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED); 1.3718 + // if only reading, nothing to be done here. 1.3719 + if (mCacheEntryIsReadOnly) 1.3720 + return NS_OK; 1.3721 + 1.3722 + // Don't cache the response again if already cached... 1.3723 + if (mCachedContentIsValid) 1.3724 + return NS_OK; 1.3725 + 1.3726 + LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n", 1.3727 + this, mCacheEntry.get())); 1.3728 + 1.3729 + bool recreate = !mCacheEntryIsWriteOnly; 1.3730 + bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING; 1.3731 + 1.3732 + if (!recreate && dontPersist) { 1.3733 + // If the current entry is persistent but we inhibit peristence 1.3734 + // then force recreation of the entry as memory/only. 1.3735 + rv = mCacheEntry->GetPersistent(&recreate); 1.3736 + if (NS_FAILED(rv)) 1.3737 + return rv; 1.3738 + } 1.3739 + 1.3740 + if (recreate) { 1.3741 + LOG((" we have a ready entry, but reading it again from the server -> recreating cache entry\n")); 1.3742 + nsCOMPtr<nsICacheEntry> currentEntry; 1.3743 + currentEntry.swap(mCacheEntry); 1.3744 + rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry)); 1.3745 + if (NS_FAILED(rv)) { 1.3746 + LOG((" recreation failed, the response will not be cached")); 1.3747 + return NS_OK; 1.3748 + } 1.3749 + 1.3750 + mCacheEntryIsWriteOnly = true; 1.3751 + } 1.3752 + 1.3753 + // Set the expiration time for this cache entry 1.3754 + rv = UpdateExpirationTime(); 1.3755 + if (NS_FAILED(rv)) return rv; 1.3756 + 1.3757 + rv = AddCacheEntryHeaders(mCacheEntry); 1.3758 + if (NS_FAILED(rv)) return rv; 1.3759 + 1.3760 + mInitedCacheEntry = true; 1.3761 + 1.3762 + // Don't perform the check when writing (doesn't make sense) 1.3763 + mConcurentCacheAccess = 0; 1.3764 + 1.3765 + return NS_OK; 1.3766 +} 1.3767 + 1.3768 +void 1.3769 +nsHttpChannel::UpdateInhibitPersistentCachingFlag() 1.3770 +{ 1.3771 + // The no-store directive within the 'Cache-Control:' header indicates 1.3772 + // that we must not store the response in a persistent cache. 1.3773 + if (mResponseHead->NoStore()) 1.3774 + mLoadFlags |= INHIBIT_PERSISTENT_CACHING; 1.3775 + 1.3776 + // Only cache SSL content on disk if the pref is set 1.3777 + if (!gHttpHandler->IsPersistentHttpsCachingEnabled() && 1.3778 + mConnectionInfo->UsingSSL()) 1.3779 + mLoadFlags |= INHIBIT_PERSISTENT_CACHING; 1.3780 +} 1.3781 + 1.3782 +nsresult 1.3783 +nsHttpChannel::InitOfflineCacheEntry() 1.3784 +{ 1.3785 + // This function can be called even when we fail to connect (bug 551990) 1.3786 + 1.3787 + if (!mOfflineCacheEntry) { 1.3788 + return NS_OK; 1.3789 + } 1.3790 + 1.3791 + if (!mResponseHead || mResponseHead->NoStore()) { 1.3792 + if (mResponseHead && mResponseHead->NoStore()) { 1.3793 + mOfflineCacheEntry->AsyncDoom(nullptr); 1.3794 + } 1.3795 + 1.3796 + CloseOfflineCacheEntry(); 1.3797 + 1.3798 + if (mResponseHead && mResponseHead->NoStore()) { 1.3799 + return NS_ERROR_NOT_AVAILABLE; 1.3800 + } 1.3801 + 1.3802 + return NS_OK; 1.3803 + } 1.3804 + 1.3805 + // This entry's expiration time should match the main entry's expiration 1.3806 + // time. UpdateExpirationTime() will keep it in sync once the offline 1.3807 + // cache entry has been created. 1.3808 + if (mCacheEntry) { 1.3809 + uint32_t expirationTime; 1.3810 + nsresult rv = mCacheEntry->GetExpirationTime(&expirationTime); 1.3811 + NS_ENSURE_SUCCESS(rv, rv); 1.3812 + 1.3813 + mOfflineCacheEntry->SetExpirationTime(expirationTime); 1.3814 + } 1.3815 + 1.3816 + return AddCacheEntryHeaders(mOfflineCacheEntry); 1.3817 +} 1.3818 + 1.3819 + 1.3820 +nsresult 1.3821 +nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry *entry) 1.3822 +{ 1.3823 + nsresult rv; 1.3824 + 1.3825 + LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", this)); 1.3826 + // Store secure data in memory only 1.3827 + if (mSecurityInfo) 1.3828 + entry->SetSecurityInfo(mSecurityInfo); 1.3829 + 1.3830 + // Store the HTTP request method with the cache entry so we can distinguish 1.3831 + // for example GET and HEAD responses. 1.3832 + rv = entry->SetMetaDataElement("request-method", 1.3833 + mRequestHead.Method().get()); 1.3834 + if (NS_FAILED(rv)) return rv; 1.3835 + 1.3836 + // Store the HTTP authorization scheme used if any... 1.3837 + rv = StoreAuthorizationMetaData(entry); 1.3838 + if (NS_FAILED(rv)) return rv; 1.3839 + 1.3840 + // Iterate over the headers listed in the Vary response header, and 1.3841 + // store the value of the corresponding request header so we can verify 1.3842 + // that it has not varied when we try to re-use the cached response at 1.3843 + // a later time. Take care to store "Cookie" headers only as hashes 1.3844 + // due to security considerations and the fact that they can be pretty 1.3845 + // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary. 1.3846 + // 1.3847 + // NOTE: if "Vary: accept, cookie", then we will store the "accept" header 1.3848 + // in the cache. we could try to avoid needlessly storing the "accept" 1.3849 + // header in this case, but it doesn't seem worth the extra code to perform 1.3850 + // the check. 1.3851 + { 1.3852 + nsAutoCString buf, metaKey; 1.3853 + mResponseHead->GetHeader(nsHttp::Vary, buf); 1.3854 + if (!buf.IsEmpty()) { 1.3855 + NS_NAMED_LITERAL_CSTRING(prefix, "request-"); 1.3856 + 1.3857 + char *val = buf.BeginWriting(); // going to munge buf 1.3858 + char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val); 1.3859 + while (token) { 1.3860 + LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \ 1.3861 + "processing %s", this, token)); 1.3862 + if (*token != '*') { 1.3863 + nsHttpAtom atom = nsHttp::ResolveAtom(token); 1.3864 + const char *val = mRequestHead.PeekHeader(atom); 1.3865 + nsAutoCString hash; 1.3866 + if (val) { 1.3867 + // If cookie-header, store a hash of the value 1.3868 + if (atom == nsHttp::Cookie) { 1.3869 + LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \ 1.3870 + "cookie-value %s", this, val)); 1.3871 + rv = Hash(val, hash); 1.3872 + // If hash failed, store a string not very likely 1.3873 + // to be the result of subsequent hashes 1.3874 + if (NS_FAILED(rv)) 1.3875 + val = "<hash failed>"; 1.3876 + else 1.3877 + val = hash.get(); 1.3878 + 1.3879 + LOG((" hashed to %s\n", val)); 1.3880 + } 1.3881 + 1.3882 + // build cache meta data key and set meta data element... 1.3883 + metaKey = prefix + nsDependentCString(token); 1.3884 + entry->SetMetaDataElement(metaKey.get(), val); 1.3885 + } else { 1.3886 + LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \ 1.3887 + "clearing metadata for %s", this, token)); 1.3888 + metaKey = prefix + nsDependentCString(token); 1.3889 + entry->SetMetaDataElement(metaKey.get(), nullptr); 1.3890 + } 1.3891 + } 1.3892 + token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val); 1.3893 + } 1.3894 + } 1.3895 + } 1.3896 + 1.3897 + 1.3898 + // Store the received HTTP head with the cache entry as an element of 1.3899 + // the meta data. 1.3900 + nsAutoCString head; 1.3901 + mResponseHead->Flatten(head, true); 1.3902 + rv = entry->SetMetaDataElement("response-head", head.get()); 1.3903 + if (NS_FAILED(rv)) return rv; 1.3904 + 1.3905 + // Indicate we have successfully finished setting metadata on the cache entry. 1.3906 + rv = entry->MetaDataReady(); 1.3907 + 1.3908 + return rv; 1.3909 +} 1.3910 + 1.3911 +inline void 1.3912 +GetAuthType(const char *challenge, nsCString &authType) 1.3913 +{ 1.3914 + const char *p; 1.3915 + 1.3916 + // get the challenge type 1.3917 + if ((p = strchr(challenge, ' ')) != nullptr) 1.3918 + authType.Assign(challenge, p - challenge); 1.3919 + else 1.3920 + authType.Assign(challenge); 1.3921 +} 1.3922 + 1.3923 +nsresult 1.3924 +nsHttpChannel::StoreAuthorizationMetaData(nsICacheEntry *entry) 1.3925 +{ 1.3926 + // Not applicable to proxy authorization... 1.3927 + const char *val = mRequestHead.PeekHeader(nsHttp::Authorization); 1.3928 + if (!val) 1.3929 + return NS_OK; 1.3930 + 1.3931 + // eg. [Basic realm="wally world"] 1.3932 + nsAutoCString buf; 1.3933 + GetAuthType(val, buf); 1.3934 + return entry->SetMetaDataElement("auth", buf.get()); 1.3935 +} 1.3936 + 1.3937 +// Finalize the cache entry 1.3938 +// - may need to rewrite response headers if any headers changed 1.3939 +// - may need to recalculate the expiration time if any headers changed 1.3940 +// - called only for freshly written cache entries 1.3941 +nsresult 1.3942 +nsHttpChannel::FinalizeCacheEntry() 1.3943 +{ 1.3944 + LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this)); 1.3945 + 1.3946 + if (mResponseHead && mResponseHeadersModified) { 1.3947 + // Set the expiration time for this cache entry 1.3948 + nsresult rv = UpdateExpirationTime(); 1.3949 + if (NS_FAILED(rv)) return rv; 1.3950 + } 1.3951 + return NS_OK; 1.3952 +} 1.3953 + 1.3954 +// Open an output stream to the cache entry and insert a listener tee into 1.3955 +// the chain of response listeners. 1.3956 +nsresult 1.3957 +nsHttpChannel::InstallCacheListener(int64_t offset) 1.3958 +{ 1.3959 + nsresult rv; 1.3960 + 1.3961 + LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get())); 1.3962 + 1.3963 + MOZ_ASSERT(mCacheEntry); 1.3964 + MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial); 1.3965 + MOZ_ASSERT(mListener); 1.3966 + 1.3967 + // If the content is compressible and the server has not compressed it, 1.3968 + // mark the cache entry for compression. 1.3969 + if ((mResponseHead->PeekHeader(nsHttp::Content_Encoding) == nullptr) && ( 1.3970 + mResponseHead->ContentType().EqualsLiteral(TEXT_HTML) || 1.3971 + mResponseHead->ContentType().EqualsLiteral(TEXT_PLAIN) || 1.3972 + mResponseHead->ContentType().EqualsLiteral(TEXT_CSS) || 1.3973 + mResponseHead->ContentType().EqualsLiteral(TEXT_JAVASCRIPT) || 1.3974 + mResponseHead->ContentType().EqualsLiteral(TEXT_ECMASCRIPT) || 1.3975 + mResponseHead->ContentType().EqualsLiteral(TEXT_XML) || 1.3976 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_JAVASCRIPT) || 1.3977 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_ECMASCRIPT) || 1.3978 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_XJAVASCRIPT) || 1.3979 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_XHTML_XML))) { 1.3980 + rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0"); 1.3981 + if (NS_FAILED(rv)) { 1.3982 + LOG(("unable to mark cache entry for compression")); 1.3983 + } 1.3984 + } 1.3985 + 1.3986 + LOG(("Trading cache input stream for output stream [channel=%p]", this)); 1.3987 + 1.3988 + // We must close the input stream first because cache entries do not 1.3989 + // correctly handle having an output stream and input streams open at 1.3990 + // the same time. 1.3991 + mCacheInputStream.CloseAndRelease(); 1.3992 + 1.3993 + nsCOMPtr<nsIOutputStream> out; 1.3994 + rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out)); 1.3995 + if (rv == NS_ERROR_NOT_AVAILABLE) { 1.3996 + LOG((" entry doomed, not writing it [channel=%p]", this)); 1.3997 + // Entry is already doomed. 1.3998 + // This may happen when expiration time is set to past and the entry 1.3999 + // has been removed by the background eviction logic. 1.4000 + return NS_OK; 1.4001 + } 1.4002 + if (NS_FAILED(rv)) return rv; 1.4003 + 1.4004 + // XXX disk cache does not support overlapped i/o yet 1.4005 +#if 0 1.4006 + // Mark entry valid inorder to allow simultaneous reading... 1.4007 + rv = mCacheEntry->MarkValid(); 1.4008 + if (NS_FAILED(rv)) return rv; 1.4009 +#endif 1.4010 + 1.4011 + nsCOMPtr<nsIStreamListenerTee> tee = 1.4012 + do_CreateInstance(kStreamListenerTeeCID, &rv); 1.4013 + if (NS_FAILED(rv)) return rv; 1.4014 + 1.4015 + nsCOMPtr<nsICacheStorageService> serv = 1.4016 + do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); 1.4017 + NS_ENSURE_SUCCESS(rv, rv); 1.4018 + 1.4019 + nsCOMPtr<nsIEventTarget> cacheIOTarget; 1.4020 + serv->GetIoTarget(getter_AddRefs(cacheIOTarget)); 1.4021 + 1.4022 + if (!cacheIOTarget) { 1.4023 + LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%x " 1.4024 + "cacheIOTarget=%p", tee.get(), rv, cacheIOTarget.get())); 1.4025 + rv = tee->Init(mListener, out, nullptr); 1.4026 + } else { 1.4027 + LOG(("nsHttpChannel::InstallCacheListener async tee %p", tee.get())); 1.4028 + rv = tee->InitAsync(mListener, cacheIOTarget, out, nullptr); 1.4029 + } 1.4030 + 1.4031 + if (NS_FAILED(rv)) return rv; 1.4032 + mListener = tee; 1.4033 + return NS_OK; 1.4034 +} 1.4035 + 1.4036 +nsresult 1.4037 +nsHttpChannel::InstallOfflineCacheListener(int64_t offset) 1.4038 +{ 1.4039 + nsresult rv; 1.4040 + 1.4041 + LOG(("Preparing to write data into the offline cache [uri=%s]\n", 1.4042 + mSpec.get())); 1.4043 + 1.4044 + MOZ_ASSERT(mOfflineCacheEntry); 1.4045 + MOZ_ASSERT(mListener); 1.4046 + 1.4047 + nsCOMPtr<nsIOutputStream> out; 1.4048 + rv = mOfflineCacheEntry->OpenOutputStream(offset, getter_AddRefs(out)); 1.4049 + if (NS_FAILED(rv)) return rv; 1.4050 + 1.4051 + nsCOMPtr<nsIStreamListenerTee> tee = 1.4052 + do_CreateInstance(kStreamListenerTeeCID, &rv); 1.4053 + if (NS_FAILED(rv)) return rv; 1.4054 + 1.4055 + rv = tee->Init(mListener, out, nullptr); 1.4056 + if (NS_FAILED(rv)) return rv; 1.4057 + 1.4058 + mListener = tee; 1.4059 + 1.4060 + return NS_OK; 1.4061 +} 1.4062 + 1.4063 +void 1.4064 +nsHttpChannel::ClearBogusContentEncodingIfNeeded() 1.4065 +{ 1.4066 + // For .gz files, apache sends both a Content-Type: application/x-gzip 1.4067 + // as well as Content-Encoding: gzip, which is completely wrong. In 1.4068 + // this case, we choose to ignore the rogue Content-Encoding header. We 1.4069 + // must do this early on so as to prevent it from being seen up stream. 1.4070 + // The same problem exists for Content-Encoding: compress in default 1.4071 + // Apache installs. 1.4072 + if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") && ( 1.4073 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP) || 1.4074 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP2) || 1.4075 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP3))) { 1.4076 + // clear the Content-Encoding header 1.4077 + mResponseHead->ClearHeader(nsHttp::Content_Encoding); 1.4078 + } 1.4079 + else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "compress") && ( 1.4080 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS) || 1.4081 + mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS2))) { 1.4082 + // clear the Content-Encoding header 1.4083 + mResponseHead->ClearHeader(nsHttp::Content_Encoding); 1.4084 + } 1.4085 +} 1.4086 + 1.4087 +//----------------------------------------------------------------------------- 1.4088 +// nsHttpChannel <redirect> 1.4089 +//----------------------------------------------------------------------------- 1.4090 + 1.4091 +nsresult 1.4092 +nsHttpChannel::SetupReplacementChannel(nsIURI *newURI, 1.4093 + nsIChannel *newChannel, 1.4094 + bool preserveMethod) 1.4095 +{ 1.4096 + LOG(("nsHttpChannel::SetupReplacementChannel " 1.4097 + "[this=%p newChannel=%p preserveMethod=%d]", 1.4098 + this, newChannel, preserveMethod)); 1.4099 + 1.4100 + nsresult rv = HttpBaseChannel::SetupReplacementChannel(newURI, newChannel, preserveMethod); 1.4101 + if (NS_FAILED(rv)) 1.4102 + return rv; 1.4103 + 1.4104 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); 1.4105 + if (!httpChannel) 1.4106 + return NS_OK; // no other options to set 1.4107 + 1.4108 + // convey the mApplyConversion flag (bug 91862) 1.4109 + nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel); 1.4110 + if (encodedChannel) 1.4111 + encodedChannel->SetApplyConversion(mApplyConversion); 1.4112 + 1.4113 + // transfer the resume information 1.4114 + if (mResuming) { 1.4115 + nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(newChannel)); 1.4116 + if (!resumableChannel) { 1.4117 + NS_WARNING("Got asked to resume, but redirected to non-resumable channel!"); 1.4118 + return NS_ERROR_NOT_RESUMABLE; 1.4119 + } 1.4120 + resumableChannel->ResumeAt(mStartPos, mEntityID); 1.4121 + } 1.4122 + 1.4123 + return NS_OK; 1.4124 +} 1.4125 + 1.4126 +nsresult 1.4127 +nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType) 1.4128 +{ 1.4129 + LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n", 1.4130 + this, redirectType)); 1.4131 + 1.4132 + // The channel is actually starting its operation now, at least because 1.4133 + // we want it to appear like being in the waiting phase until now. 1.4134 + MOZ_EVENT_TRACER_EXEC(this, "net::http::channel"); 1.4135 + MOZ_EVENT_TRACER_EXEC(this, "net::http::redirect-callbacks"); 1.4136 + 1.4137 + const char *location = mResponseHead->PeekHeader(nsHttp::Location); 1.4138 + 1.4139 + // if a location header was not given, then we can't perform the redirect, 1.4140 + // so just carry on as though this were a normal response. 1.4141 + if (!location) 1.4142 + return NS_ERROR_FAILURE; 1.4143 + 1.4144 + // make sure non-ASCII characters in the location header are escaped. 1.4145 + nsAutoCString locationBuf; 1.4146 + if (NS_EscapeURL(location, -1, esc_OnlyNonASCII, locationBuf)) 1.4147 + location = locationBuf.get(); 1.4148 + 1.4149 + if (mRedirectionLimit == 0) { 1.4150 + LOG(("redirection limit reached!\n")); 1.4151 + return NS_ERROR_REDIRECT_LOOP; 1.4152 + } 1.4153 + 1.4154 + mRedirectType = redirectType; 1.4155 + 1.4156 + LOG(("redirecting to: %s [redirection-limit=%u]\n", 1.4157 + location, uint32_t(mRedirectionLimit))); 1.4158 + 1.4159 + nsresult rv = CreateNewURI(location, getter_AddRefs(mRedirectURI)); 1.4160 + 1.4161 + if (NS_FAILED(rv)) { 1.4162 + LOG(("Invalid URI for redirect: Location: %s\n", location)); 1.4163 + return NS_ERROR_CORRUPTED_CONTENT; 1.4164 + } 1.4165 + 1.4166 + if (mApplicationCache) { 1.4167 + // if we are redirected to a different origin check if there is a fallback 1.4168 + // cache entry to fall back to. we don't care about file strict 1.4169 + // checking, at least mURI is not a file URI. 1.4170 + if (!NS_SecurityCompareURIs(mURI, mRedirectURI, false)) { 1.4171 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback); 1.4172 + bool waitingForRedirectCallback; 1.4173 + (void)ProcessFallback(&waitingForRedirectCallback); 1.4174 + if (waitingForRedirectCallback) 1.4175 + return NS_OK; 1.4176 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback); 1.4177 + } 1.4178 + } 1.4179 + 1.4180 + return ContinueProcessRedirectionAfterFallback(NS_OK); 1.4181 +} 1.4182 + 1.4183 +// Creates an URI to the given location using current URI for base and charset 1.4184 +nsresult 1.4185 +nsHttpChannel::CreateNewURI(const char *loc, nsIURI **newURI) 1.4186 +{ 1.4187 + nsCOMPtr<nsIIOService> ioService; 1.4188 + nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 1.4189 + if (NS_FAILED(rv)) return rv; 1.4190 + 1.4191 + // the new uri should inherit the origin charset of the current uri 1.4192 + nsAutoCString originCharset; 1.4193 + rv = mURI->GetOriginCharset(originCharset); 1.4194 + if (NS_FAILED(rv)) 1.4195 + originCharset.Truncate(); 1.4196 + 1.4197 + return ioService->NewURI(nsDependentCString(loc), 1.4198 + originCharset.get(), 1.4199 + mURI, 1.4200 + newURI); 1.4201 +} 1.4202 + 1.4203 +nsresult 1.4204 +nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) 1.4205 +{ 1.4206 + if (NS_SUCCEEDED(rv) && mFallingBack) { 1.4207 + // do not continue with redirect processing, fallback is in 1.4208 + // progress now. 1.4209 + return NS_OK; 1.4210 + } 1.4211 + 1.4212 + // Kill the current cache entry if we are redirecting 1.4213 + // back to ourself. 1.4214 + bool redirectingBackToSameURI = false; 1.4215 + if (mCacheEntry && mCacheEntryIsWriteOnly && 1.4216 + NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) && 1.4217 + redirectingBackToSameURI) 1.4218 + mCacheEntry->AsyncDoom(nullptr); 1.4219 + 1.4220 + // move the reference of the old location to the new one if the new 1.4221 + // one has none. 1.4222 + nsAutoCString ref; 1.4223 + rv = mRedirectURI->GetRef(ref); 1.4224 + if (NS_SUCCEEDED(rv) && ref.IsEmpty()) { 1.4225 + mURI->GetRef(ref); 1.4226 + if (!ref.IsEmpty()) { 1.4227 + // NOTE: SetRef will fail if mRedirectURI is immutable 1.4228 + // (e.g. an about: URI)... Oh well. 1.4229 + mRedirectURI->SetRef(ref); 1.4230 + } 1.4231 + } 1.4232 + 1.4233 + bool rewriteToGET = ShouldRewriteRedirectToGET(mRedirectType, 1.4234 + mRequestHead.ParsedMethod()); 1.4235 + 1.4236 + // prompt if the method is not safe (such as POST, PUT, DELETE, ...) 1.4237 + if (!rewriteToGET && !mRequestHead.IsSafeMethod()) { 1.4238 + rv = PromptTempRedirect(); 1.4239 + if (NS_FAILED(rv)) return rv; 1.4240 + } 1.4241 + 1.4242 + nsCOMPtr<nsIIOService> ioService; 1.4243 + rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 1.4244 + if (NS_FAILED(rv)) return rv; 1.4245 + 1.4246 + nsCOMPtr<nsIChannel> newChannel; 1.4247 + rv = ioService->NewChannelFromURI(mRedirectURI, getter_AddRefs(newChannel)); 1.4248 + if (NS_FAILED(rv)) return rv; 1.4249 + 1.4250 + rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET); 1.4251 + if (NS_FAILED(rv)) return rv; 1.4252 + 1.4253 + uint32_t redirectFlags; 1.4254 + if (nsHttp::IsPermanentRedirect(mRedirectType)) 1.4255 + redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT; 1.4256 + else 1.4257 + redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY; 1.4258 + 1.4259 + // verify that this is a legal redirect 1.4260 + mRedirectChannel = newChannel; 1.4261 + 1.4262 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection); 1.4263 + rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags); 1.4264 + 1.4265 + if (NS_SUCCEEDED(rv)) 1.4266 + rv = WaitForRedirectCallback(); 1.4267 + 1.4268 + if (NS_FAILED(rv)) { 1.4269 + AutoRedirectVetoNotifier notifier(this); 1.4270 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection); 1.4271 + } 1.4272 + 1.4273 + return rv; 1.4274 +} 1.4275 + 1.4276 +nsresult 1.4277 +nsHttpChannel::ContinueProcessRedirection(nsresult rv) 1.4278 +{ 1.4279 + AutoRedirectVetoNotifier notifier(this); 1.4280 + 1.4281 + LOG(("ContinueProcessRedirection [rv=%x]\n", rv)); 1.4282 + if (NS_FAILED(rv)) 1.4283 + return rv; 1.4284 + 1.4285 + NS_PRECONDITION(mRedirectChannel, "No redirect channel?"); 1.4286 + 1.4287 + // Make sure to do this _after_ calling OnChannelRedirect 1.4288 + mRedirectChannel->SetOriginalURI(mOriginalURI); 1.4289 + 1.4290 + // And now, the deprecated way 1.4291 + nsCOMPtr<nsIHttpEventSink> httpEventSink; 1.4292 + GetCallback(httpEventSink); 1.4293 + if (httpEventSink) { 1.4294 + // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8 1.4295 + // versions. 1.4296 + rv = httpEventSink->OnRedirect(this, mRedirectChannel); 1.4297 + if (NS_FAILED(rv)) 1.4298 + return rv; 1.4299 + } 1.4300 + // XXX we used to talk directly with the script security manager, but that 1.4301 + // should really be handled by the event sink implementation. 1.4302 + 1.4303 + // begin loading the new channel 1.4304 + rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); 1.4305 + 1.4306 + if (NS_FAILED(rv)) 1.4307 + return rv; 1.4308 + 1.4309 + // close down this channel 1.4310 + Cancel(NS_BINDING_REDIRECTED); 1.4311 + 1.4312 + notifier.RedirectSucceeded(); 1.4313 + 1.4314 + ReleaseListeners(); 1.4315 + 1.4316 + return NS_OK; 1.4317 +} 1.4318 + 1.4319 +//----------------------------------------------------------------------------- 1.4320 +// nsHttpChannel <auth> 1.4321 +//----------------------------------------------------------------------------- 1.4322 + 1.4323 +NS_IMETHODIMP nsHttpChannel::OnAuthAvailable() 1.4324 +{ 1.4325 + LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this)); 1.4326 + 1.4327 + // setting mAuthRetryPending flag and resuming the transaction 1.4328 + // triggers process of throwing away the unauthenticated data already 1.4329 + // coming from the network 1.4330 + mAuthRetryPending = true; 1.4331 + mProxyAuthPending = false; 1.4332 + LOG(("Resuming the transaction, we got credentials from user")); 1.4333 + mTransactionPump->Resume(); 1.4334 + 1.4335 + return NS_OK; 1.4336 +} 1.4337 + 1.4338 +NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel) 1.4339 +{ 1.4340 + LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this)); 1.4341 + 1.4342 + if (mTransactionPump) { 1.4343 + // If the channel is trying to authenticate to a proxy and 1.4344 + // that was canceled we cannot show the http response body 1.4345 + // from the 40x as that might mislead the user into thinking 1.4346 + // it was a end host response instead of a proxy reponse. 1.4347 + // This must check explicitly whether a proxy auth was being done 1.4348 + // because we do want to show the content if this is an error from 1.4349 + // the origin server. 1.4350 + if (mProxyAuthPending) 1.4351 + Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED); 1.4352 + 1.4353 + // ensure call of OnStartRequest of the current listener here, 1.4354 + // it would not be called otherwise at all 1.4355 + nsresult rv = CallOnStartRequest(); 1.4356 + 1.4357 + // drop mAuthRetryPending flag and resume the transaction 1.4358 + // this resumes load of the unauthenticated content data (which 1.4359 + // may have been canceled if we don't want to show it) 1.4360 + mAuthRetryPending = false; 1.4361 + LOG(("Resuming the transaction, user cancelled the auth dialog")); 1.4362 + mTransactionPump->Resume(); 1.4363 + 1.4364 + if (NS_FAILED(rv)) 1.4365 + mTransactionPump->Cancel(rv); 1.4366 + } 1.4367 + 1.4368 + mProxyAuthPending = false; 1.4369 + return NS_OK; 1.4370 +} 1.4371 + 1.4372 +//----------------------------------------------------------------------------- 1.4373 +// nsHttpChannel::nsISupports 1.4374 +//----------------------------------------------------------------------------- 1.4375 + 1.4376 +NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel) 1.4377 +NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel) 1.4378 + 1.4379 +NS_INTERFACE_MAP_BEGIN(nsHttpChannel) 1.4380 + NS_INTERFACE_MAP_ENTRY(nsIRequest) 1.4381 + NS_INTERFACE_MAP_ENTRY(nsIChannel) 1.4382 + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 1.4383 + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 1.4384 + NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) 1.4385 + NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel) 1.4386 + NS_INTERFACE_MAP_ENTRY(nsICachingChannel) 1.4387 + NS_INTERFACE_MAP_ENTRY(nsIUploadChannel) 1.4388 + NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2) 1.4389 + NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback) 1.4390 + NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) 1.4391 + NS_INTERFACE_MAP_ENTRY(nsIResumableChannel) 1.4392 + NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) 1.4393 + NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) 1.4394 + NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback) 1.4395 + NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) 1.4396 + NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel) 1.4397 + NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) 1.4398 + NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) 1.4399 + NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) 1.4400 + NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest) 1.4401 + NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener) 1.4402 + NS_INTERFACE_MAP_ENTRY(nsIDNSListener) 1.4403 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.4404 +NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) 1.4405 + 1.4406 +//----------------------------------------------------------------------------- 1.4407 +// nsHttpChannel::nsIRequest 1.4408 +//----------------------------------------------------------------------------- 1.4409 + 1.4410 +NS_IMETHODIMP 1.4411 +nsHttpChannel::Cancel(nsresult status) 1.4412 +{ 1.4413 + MOZ_ASSERT(NS_IsMainThread()); 1.4414 + 1.4415 + LOG(("nsHttpChannel::Cancel [this=%p status=%x]\n", this, status)); 1.4416 + if (mCanceled) { 1.4417 + LOG((" ignoring; already canceled\n")); 1.4418 + return NS_OK; 1.4419 + } 1.4420 + if (mWaitingForRedirectCallback) { 1.4421 + LOG(("channel canceled during wait for redirect callback")); 1.4422 + } 1.4423 + mCanceled = true; 1.4424 + mStatus = status; 1.4425 + if (mProxyRequest) 1.4426 + mProxyRequest->Cancel(status); 1.4427 + if (mTransaction) 1.4428 + gHttpHandler->CancelTransaction(mTransaction, status); 1.4429 + if (mTransactionPump) 1.4430 + mTransactionPump->Cancel(status); 1.4431 + mCacheInputStream.CloseAndRelease(); 1.4432 + if (mCachePump) 1.4433 + mCachePump->Cancel(status); 1.4434 + if (mAuthProvider) 1.4435 + mAuthProvider->Cancel(status); 1.4436 + return NS_OK; 1.4437 +} 1.4438 + 1.4439 +NS_IMETHODIMP 1.4440 +nsHttpChannel::Suspend() 1.4441 +{ 1.4442 + NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE); 1.4443 + 1.4444 + LOG(("nsHttpChannel::Suspend [this=%p]\n", this)); 1.4445 + 1.4446 + ++mSuspendCount; 1.4447 + 1.4448 + if (mTransactionPump) 1.4449 + return mTransactionPump->Suspend(); 1.4450 + if (mCachePump) 1.4451 + return mCachePump->Suspend(); 1.4452 + 1.4453 + return NS_OK; 1.4454 +} 1.4455 + 1.4456 +NS_IMETHODIMP 1.4457 +nsHttpChannel::Resume() 1.4458 +{ 1.4459 + NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); 1.4460 + 1.4461 + LOG(("nsHttpChannel::Resume [this=%p]\n", this)); 1.4462 + 1.4463 + if (--mSuspendCount == 0 && mCallOnResume) { 1.4464 + nsresult rv = AsyncCall(mCallOnResume); 1.4465 + mCallOnResume = nullptr; 1.4466 + NS_ENSURE_SUCCESS(rv, rv); 1.4467 + } 1.4468 + 1.4469 + if (mTransactionPump) 1.4470 + return mTransactionPump->Resume(); 1.4471 + if (mCachePump) 1.4472 + return mCachePump->Resume(); 1.4473 + 1.4474 + return NS_OK; 1.4475 +} 1.4476 + 1.4477 +//----------------------------------------------------------------------------- 1.4478 +// nsHttpChannel::nsIChannel 1.4479 +//----------------------------------------------------------------------------- 1.4480 + 1.4481 +NS_IMETHODIMP 1.4482 +nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo) 1.4483 +{ 1.4484 + NS_ENSURE_ARG_POINTER(securityInfo); 1.4485 + *securityInfo = mSecurityInfo; 1.4486 + NS_IF_ADDREF(*securityInfo); 1.4487 + return NS_OK; 1.4488 +} 1.4489 + 1.4490 +NS_IMETHODIMP 1.4491 +nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) 1.4492 +{ 1.4493 + MOZ_EVENT_TRACER_WAIT(this, "net::http::channel"); 1.4494 + 1.4495 + LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this)); 1.4496 + 1.4497 + NS_ENSURE_ARG_POINTER(listener); 1.4498 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.4499 + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); 1.4500 + 1.4501 + nsresult rv; 1.4502 + 1.4503 + rv = NS_CheckPortSafety(mURI); 1.4504 + if (NS_FAILED(rv)) { 1.4505 + ReleaseListeners(); 1.4506 + return rv; 1.4507 + } 1.4508 + 1.4509 + // Remember the cookie header that was set, if any 1.4510 + const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); 1.4511 + if (cookieHeader) { 1.4512 + mUserSetCookieHeader = cookieHeader; 1.4513 + } 1.4514 + 1.4515 + AddCookiesToRequest(); 1.4516 + 1.4517 + // notify "http-on-opening-request" observers, but not if this is a redirect 1.4518 + if (!(mLoadFlags & LOAD_REPLACE)) { 1.4519 + gHttpHandler->OnOpeningRequest(this); 1.4520 + } 1.4521 + 1.4522 + mIsPending = true; 1.4523 + mWasOpened = true; 1.4524 + 1.4525 + mListener = listener; 1.4526 + mListenerContext = context; 1.4527 + 1.4528 + // add ourselves to the load group. from this point forward, we'll report 1.4529 + // all failures asynchronously. 1.4530 + if (mLoadGroup) 1.4531 + mLoadGroup->AddRequest(this, nullptr); 1.4532 + 1.4533 + // record asyncopen time unconditionally and clear it if we 1.4534 + // don't want it after OnModifyRequest() weighs in. But waiting for 1.4535 + // that to complete would mean we don't include proxy resolution in the 1.4536 + // timing. 1.4537 + mAsyncOpenTime = TimeStamp::Now(); 1.4538 + 1.4539 + // the only time we would already know the proxy information at this 1.4540 + // point would be if we were proxying a non-http protocol like ftp 1.4541 + if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) 1.4542 + return NS_OK; 1.4543 + 1.4544 + rv = BeginConnect(); 1.4545 + if (NS_FAILED(rv)) 1.4546 + ReleaseListeners(); 1.4547 + 1.4548 + return rv; 1.4549 +} 1.4550 + 1.4551 +nsresult 1.4552 +nsHttpChannel::BeginConnect() 1.4553 +{ 1.4554 + LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this)); 1.4555 + nsresult rv; 1.4556 + 1.4557 + // Construct connection info object 1.4558 + nsAutoCString host; 1.4559 + int32_t port = -1; 1.4560 + nsAutoCString username; 1.4561 + bool usingSSL = false; 1.4562 + 1.4563 + rv = mURI->SchemeIs("https", &usingSSL); 1.4564 + if (NS_SUCCEEDED(rv)) 1.4565 + rv = mURI->GetAsciiHost(host); 1.4566 + if (NS_SUCCEEDED(rv)) 1.4567 + rv = mURI->GetPort(&port); 1.4568 + if (NS_SUCCEEDED(rv)) 1.4569 + mURI->GetUsername(username); 1.4570 + if (NS_SUCCEEDED(rv)) 1.4571 + rv = mURI->GetAsciiSpec(mSpec); 1.4572 + if (NS_FAILED(rv)) 1.4573 + return rv; 1.4574 + 1.4575 + // Reject the URL if it doesn't specify a host 1.4576 + if (host.IsEmpty()) 1.4577 + return NS_ERROR_MALFORMED_URI; 1.4578 + LOG(("host=%s port=%d\n", host.get(), port)); 1.4579 + LOG(("uri=%s\n", mSpec.get())); 1.4580 + 1.4581 + nsCOMPtr<nsProxyInfo> proxyInfo; 1.4582 + if (mProxyInfo) 1.4583 + proxyInfo = do_QueryInterface(mProxyInfo); 1.4584 + 1.4585 + mConnectionInfo = new nsHttpConnectionInfo(host, port, username, proxyInfo, usingSSL); 1.4586 + 1.4587 + mAuthProvider = 1.4588 + do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1", 1.4589 + &rv); 1.4590 + if (NS_SUCCEEDED(rv)) 1.4591 + rv = mAuthProvider->Init(this); 1.4592 + if (NS_FAILED(rv)) 1.4593 + return rv; 1.4594 + 1.4595 + // check to see if authorization headers should be included 1.4596 + mAuthProvider->AddAuthorizationHeaders(); 1.4597 + 1.4598 + // notify "http-on-modify-request" observers 1.4599 + CallOnModifyRequestObservers(); 1.4600 + 1.4601 + // Check to see if we should redirect this channel elsewhere by 1.4602 + // nsIHttpChannel.redirectTo API request 1.4603 + if (mAPIRedirectToURI) { 1.4604 + return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); 1.4605 + } 1.4606 + 1.4607 + // If mTimingEnabled flag is not set after OnModifyRequest() then 1.4608 + // clear the already recorded AsyncOpen value for consistency. 1.4609 + if (!mTimingEnabled) 1.4610 + mAsyncOpenTime = TimeStamp(); 1.4611 + 1.4612 + // when proxying only use the pipeline bit if ProxyPipelining() allows it. 1.4613 + if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) { 1.4614 + mCaps &= ~NS_HTTP_ALLOW_PIPELINING; 1.4615 + if (gHttpHandler->ProxyPipelining()) 1.4616 + mCaps |= NS_HTTP_ALLOW_PIPELINING; 1.4617 + } 1.4618 + 1.4619 + // if this somehow fails we can go on without it 1.4620 + gHttpHandler->AddConnectionHeader(&mRequestHead.Headers(), mCaps); 1.4621 + 1.4622 + if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags)) 1.4623 + mCaps |= NS_HTTP_REFRESH_DNS; 1.4624 + 1.4625 + if (!mConnectionInfo->UsingHttpProxy()) { 1.4626 + // Start a DNS lookup very early in case the real open is queued the DNS can 1.4627 + // happen in parallel. Do not do so in the presence of an HTTP proxy as 1.4628 + // all lookups other than for the proxy itself are done by the proxy. 1.4629 + // 1.4630 + // We keep the DNS prefetch object around so that we can retrieve 1.4631 + // timing information from it. There is no guarantee that we actually 1.4632 + // use the DNS prefetch data for the real connection, but as we keep 1.4633 + // this data around for 3 minutes by default, this should almost always 1.4634 + // be correct, and even when it isn't, the timing still represents _a_ 1.4635 + // valid DNS lookup timing for the site, even if it is not _the_ 1.4636 + // timing we used. 1.4637 + LOG(("nsHttpChannel::BeginConnect [this=%p] prefetching%s\n", 1.4638 + this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "")); 1.4639 + mDNSPrefetch = new nsDNSPrefetch(mURI, this, mTimingEnabled); 1.4640 + mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS); 1.4641 + } 1.4642 + 1.4643 + // Adjust mCaps according to our request headers: 1.4644 + // - If "Connection: close" is set as a request header, then do not bother 1.4645 + // trying to establish a keep-alive connection. 1.4646 + if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) 1.4647 + mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING); 1.4648 + 1.4649 + if (gHttpHandler->CriticalRequestPrioritization()) { 1.4650 + if (mLoadAsBlocking) 1.4651 + mCaps |= NS_HTTP_LOAD_AS_BLOCKING; 1.4652 + if (mLoadUnblocked) 1.4653 + mCaps |= NS_HTTP_LOAD_UNBLOCKED; 1.4654 + } 1.4655 + 1.4656 + // Force-Reload should reset the persistent connection pool for this host 1.4657 + if (mLoadFlags & LOAD_FRESH_CONNECTION) { 1.4658 + // just the initial document resets the whole pool 1.4659 + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { 1.4660 + gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(mConnectionInfo); 1.4661 + } 1.4662 + // each sub resource gets a fresh connection 1.4663 + mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING); 1.4664 + } 1.4665 + 1.4666 + // We may have been cancelled already, either by on-modify-request 1.4667 + // listeners or by load group observers; in that case, we should 1.4668 + // not send the request to the server 1.4669 + if (mCanceled) 1.4670 + rv = mStatus; 1.4671 + else 1.4672 + rv = Connect(); 1.4673 + if (NS_FAILED(rv)) { 1.4674 + LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled)); 1.4675 + CloseCacheEntry(true); 1.4676 + AsyncAbort(rv); 1.4677 + } else if (mLoadFlags & LOAD_CLASSIFY_URI) { 1.4678 + nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier(); 1.4679 + rv = classifier->Start(this); 1.4680 + if (NS_FAILED(rv)) { 1.4681 + Cancel(rv); 1.4682 + return rv; 1.4683 + } 1.4684 + } 1.4685 + 1.4686 + return NS_OK; 1.4687 +} 1.4688 + 1.4689 +//----------------------------------------------------------------------------- 1.4690 +// nsHttpChannel::nsIHttpChannelInternal 1.4691 +//----------------------------------------------------------------------------- 1.4692 + 1.4693 +NS_IMETHODIMP 1.4694 +nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey) 1.4695 +{ 1.4696 + ENSURE_CALLED_BEFORE_CONNECT(); 1.4697 + 1.4698 + LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]", 1.4699 + this, aFallbackKey)); 1.4700 + mFallbackChannel = true; 1.4701 + mFallbackKey = aFallbackKey; 1.4702 + 1.4703 + return NS_OK; 1.4704 +} 1.4705 + 1.4706 +//----------------------------------------------------------------------------- 1.4707 +// nsHttpChannel::nsISupportsPriority 1.4708 +//----------------------------------------------------------------------------- 1.4709 + 1.4710 +NS_IMETHODIMP 1.4711 +nsHttpChannel::SetPriority(int32_t value) 1.4712 +{ 1.4713 + int16_t newValue = clamped<int32_t>(value, INT16_MIN, INT16_MAX); 1.4714 + if (mPriority == newValue) 1.4715 + return NS_OK; 1.4716 + mPriority = newValue; 1.4717 + if (mTransaction) 1.4718 + gHttpHandler->RescheduleTransaction(mTransaction, mPriority); 1.4719 + return NS_OK; 1.4720 +} 1.4721 + 1.4722 +//----------------------------------------------------------------------------- 1.4723 +// nsHttpChannel::nsIProtocolProxyCallback 1.4724 +//----------------------------------------------------------------------------- 1.4725 + 1.4726 +NS_IMETHODIMP 1.4727 +nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel, 1.4728 + nsIProxyInfo *pi, nsresult status) 1.4729 +{ 1.4730 + LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%x mStatus=%x]\n", 1.4731 + this, pi, status, mStatus)); 1.4732 + mProxyRequest = nullptr; 1.4733 + 1.4734 + nsresult rv; 1.4735 + 1.4736 + // If status is a failure code, then it means that we failed to resolve 1.4737 + // proxy info. That is a non-fatal error assuming it wasn't because the 1.4738 + // request was canceled. We just failover to DIRECT when proxy resolution 1.4739 + // fails (failure can mean that the PAC URL could not be loaded). 1.4740 + 1.4741 + if (NS_SUCCEEDED(status)) 1.4742 + mProxyInfo = pi; 1.4743 + 1.4744 + if (!gHttpHandler->Active()) { 1.4745 + LOG(("nsHttpChannel::OnProxyAvailable [this=%p] " 1.4746 + "Handler no longer active.\n", this)); 1.4747 + rv = NS_ERROR_NOT_AVAILABLE; 1.4748 + } 1.4749 + else { 1.4750 + rv = BeginConnect(); 1.4751 + } 1.4752 + 1.4753 + if (NS_FAILED(rv)) { 1.4754 + Cancel(rv); 1.4755 + // Calling OnStart/OnStop synchronously here would mean doing it before 1.4756 + // returning from AsyncOpen which is a contract violation. Do it async. 1.4757 + nsRefPtr<nsRunnableMethod<HttpBaseChannel> > event = 1.4758 + NS_NewRunnableMethod(this, &nsHttpChannel::DoNotifyListener); 1.4759 + rv = NS_DispatchToCurrentThread(event); 1.4760 + if (NS_FAILED(rv)) { 1.4761 + NS_WARNING("Failed To Dispatch DoNotifyListener"); 1.4762 + } 1.4763 + } 1.4764 + return rv; 1.4765 +} 1.4766 + 1.4767 +//----------------------------------------------------------------------------- 1.4768 +// nsHttpChannel::nsIProxiedChannel 1.4769 +//----------------------------------------------------------------------------- 1.4770 + 1.4771 +NS_IMETHODIMP 1.4772 +nsHttpChannel::GetProxyInfo(nsIProxyInfo **result) 1.4773 +{ 1.4774 + if (!mConnectionInfo) 1.4775 + *result = mProxyInfo; 1.4776 + else 1.4777 + *result = mConnectionInfo->ProxyInfo(); 1.4778 + NS_IF_ADDREF(*result); 1.4779 + return NS_OK; 1.4780 +} 1.4781 + 1.4782 +//----------------------------------------------------------------------------- 1.4783 +// nsHttpChannel::nsITimedChannel 1.4784 +//----------------------------------------------------------------------------- 1.4785 + 1.4786 +NS_IMETHODIMP 1.4787 +nsHttpChannel::GetDomainLookupStart(TimeStamp* _retval) { 1.4788 + if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) 1.4789 + *_retval = mDNSPrefetch->StartTimestamp(); 1.4790 + else if (mTransaction) 1.4791 + *_retval = mTransaction->Timings().domainLookupStart; 1.4792 + else 1.4793 + *_retval = mTransactionTimings.domainLookupStart; 1.4794 + return NS_OK; 1.4795 +} 1.4796 + 1.4797 +NS_IMETHODIMP 1.4798 +nsHttpChannel::GetDomainLookupEnd(TimeStamp* _retval) { 1.4799 + if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) 1.4800 + *_retval = mDNSPrefetch->EndTimestamp(); 1.4801 + else if (mTransaction) 1.4802 + *_retval = mTransaction->Timings().domainLookupEnd; 1.4803 + else 1.4804 + *_retval = mTransactionTimings.domainLookupEnd; 1.4805 + return NS_OK; 1.4806 +} 1.4807 + 1.4808 +NS_IMETHODIMP 1.4809 +nsHttpChannel::GetConnectStart(TimeStamp* _retval) { 1.4810 + if (mTransaction) 1.4811 + *_retval = mTransaction->Timings().connectStart; 1.4812 + else 1.4813 + *_retval = mTransactionTimings.connectStart; 1.4814 + return NS_OK; 1.4815 +} 1.4816 + 1.4817 +NS_IMETHODIMP 1.4818 +nsHttpChannel::GetConnectEnd(TimeStamp* _retval) { 1.4819 + if (mTransaction) 1.4820 + *_retval = mTransaction->Timings().connectEnd; 1.4821 + else 1.4822 + *_retval = mTransactionTimings.connectEnd; 1.4823 + return NS_OK; 1.4824 +} 1.4825 + 1.4826 +NS_IMETHODIMP 1.4827 +nsHttpChannel::GetRequestStart(TimeStamp* _retval) { 1.4828 + if (mTransaction) 1.4829 + *_retval = mTransaction->Timings().requestStart; 1.4830 + else 1.4831 + *_retval = mTransactionTimings.requestStart; 1.4832 + return NS_OK; 1.4833 +} 1.4834 + 1.4835 +NS_IMETHODIMP 1.4836 +nsHttpChannel::GetResponseStart(TimeStamp* _retval) { 1.4837 + if (mTransaction) 1.4838 + *_retval = mTransaction->Timings().responseStart; 1.4839 + else 1.4840 + *_retval = mTransactionTimings.responseStart; 1.4841 + return NS_OK; 1.4842 +} 1.4843 + 1.4844 +NS_IMETHODIMP 1.4845 +nsHttpChannel::GetResponseEnd(TimeStamp* _retval) { 1.4846 + if (mTransaction) 1.4847 + *_retval = mTransaction->Timings().responseEnd; 1.4848 + else 1.4849 + *_retval = mTransactionTimings.responseEnd; 1.4850 + return NS_OK; 1.4851 +} 1.4852 + 1.4853 +//----------------------------------------------------------------------------- 1.4854 +// nsHttpChannel::nsIHttpAuthenticableChannel 1.4855 +//----------------------------------------------------------------------------- 1.4856 + 1.4857 +NS_IMETHODIMP 1.4858 +nsHttpChannel::GetIsSSL(bool *aIsSSL) 1.4859 +{ 1.4860 + *aIsSSL = mConnectionInfo->UsingSSL(); 1.4861 + return NS_OK; 1.4862 +} 1.4863 + 1.4864 +NS_IMETHODIMP 1.4865 +nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect) 1.4866 +{ 1.4867 + *aProxyMethodIsConnect = mConnectionInfo->UsingConnect(); 1.4868 + return NS_OK; 1.4869 +} 1.4870 + 1.4871 +NS_IMETHODIMP 1.4872 +nsHttpChannel::GetServerResponseHeader(nsACString &value) 1.4873 +{ 1.4874 + if (!mResponseHead) 1.4875 + return NS_ERROR_NOT_AVAILABLE; 1.4876 + return mResponseHead->GetHeader(nsHttp::Server, value); 1.4877 +} 1.4878 + 1.4879 +NS_IMETHODIMP 1.4880 +nsHttpChannel::GetProxyChallenges(nsACString &value) 1.4881 +{ 1.4882 + if (!mResponseHead) 1.4883 + return NS_ERROR_UNEXPECTED; 1.4884 + return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value); 1.4885 +} 1.4886 + 1.4887 +NS_IMETHODIMP 1.4888 +nsHttpChannel::GetWWWChallenges(nsACString &value) 1.4889 +{ 1.4890 + if (!mResponseHead) 1.4891 + return NS_ERROR_UNEXPECTED; 1.4892 + return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value); 1.4893 +} 1.4894 + 1.4895 +NS_IMETHODIMP 1.4896 +nsHttpChannel::SetProxyCredentials(const nsACString &value) 1.4897 +{ 1.4898 + return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value); 1.4899 +} 1.4900 + 1.4901 +NS_IMETHODIMP 1.4902 +nsHttpChannel::SetWWWCredentials(const nsACString &value) 1.4903 +{ 1.4904 + return mRequestHead.SetHeader(nsHttp::Authorization, value); 1.4905 +} 1.4906 + 1.4907 +//----------------------------------------------------------------------------- 1.4908 +// Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we 1.4909 +// get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks. 1.4910 +// 1.4911 + 1.4912 +NS_IMETHODIMP 1.4913 +nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.4914 +{ 1.4915 + return HttpBaseChannel::GetLoadFlags(aLoadFlags); 1.4916 +} 1.4917 + 1.4918 +NS_IMETHODIMP 1.4919 +nsHttpChannel::GetURI(nsIURI **aURI) 1.4920 +{ 1.4921 + return HttpBaseChannel::GetURI(aURI); 1.4922 +} 1.4923 + 1.4924 +NS_IMETHODIMP 1.4925 +nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) 1.4926 +{ 1.4927 + return HttpBaseChannel::GetNotificationCallbacks(aCallbacks); 1.4928 +} 1.4929 + 1.4930 +NS_IMETHODIMP 1.4931 +nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) 1.4932 +{ 1.4933 + return HttpBaseChannel::GetLoadGroup(aLoadGroup); 1.4934 +} 1.4935 + 1.4936 +NS_IMETHODIMP 1.4937 +nsHttpChannel::GetRequestMethod(nsACString& aMethod) 1.4938 +{ 1.4939 + return HttpBaseChannel::GetRequestMethod(aMethod); 1.4940 +} 1.4941 + 1.4942 + 1.4943 +//----------------------------------------------------------------------------- 1.4944 +// nsHttpChannel::nsIRequestObserver 1.4945 +//----------------------------------------------------------------------------- 1.4946 + 1.4947 +NS_IMETHODIMP 1.4948 +nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) 1.4949 +{ 1.4950 + PROFILER_LABEL("nsHttpChannel", "OnStartRequest"); 1.4951 + if (!(mCanceled || NS_FAILED(mStatus))) { 1.4952 + // capture the request's status, so our consumers will know ASAP of any 1.4953 + // connection failures, etc - bug 93581 1.4954 + request->GetStatus(&mStatus); 1.4955 + } 1.4956 + 1.4957 + LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%x]\n", 1.4958 + this, request, mStatus)); 1.4959 + 1.4960 + // Make sure things are what we expect them to be... 1.4961 + MOZ_ASSERT(request == mCachePump || request == mTransactionPump, 1.4962 + "Unexpected request"); 1.4963 + MOZ_ASSERT(!(mTransactionPump && mCachePump) || mCachedContentIsPartial, 1.4964 + "If we have both pumps, the cache content must be partial"); 1.4965 + 1.4966 + if (!mSecurityInfo && !mCachePump && mTransaction) { 1.4967 + // grab the security info from the connection object; the transaction 1.4968 + // is guaranteed to own a reference to the connection. 1.4969 + mSecurityInfo = mTransaction->SecurityInfo(); 1.4970 + } 1.4971 + 1.4972 + // don't enter this block if we're reading from the cache... 1.4973 + if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) { 1.4974 + // mTransactionPump doesn't hit OnInputStreamReady and call this until 1.4975 + // all of the response headers have been acquired, so we can take ownership 1.4976 + // of them from the transaction. 1.4977 + mResponseHead = mTransaction->TakeResponseHead(); 1.4978 + // the response head may be null if the transaction was cancelled. in 1.4979 + // which case we just need to call OnStartRequest/OnStopRequest. 1.4980 + if (mResponseHead) 1.4981 + return ProcessResponse(); 1.4982 + 1.4983 + NS_WARNING("No response head in OnStartRequest"); 1.4984 + } 1.4985 + 1.4986 + // cache file could be deleted on our behalf, reload from network here. 1.4987 + if (mCacheEntry && mCachePump && CACHE_FILE_GONE(mStatus)) { 1.4988 + LOG((" cache file gone, reloading from server")); 1.4989 + mCacheEntry->AsyncDoom(nullptr); 1.4990 + nsresult rv = StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL); 1.4991 + if (NS_SUCCEEDED(rv)) 1.4992 + return NS_OK; 1.4993 + } 1.4994 + 1.4995 + // avoid crashing if mListener happens to be null... 1.4996 + if (!mListener) { 1.4997 + NS_NOTREACHED("mListener is null"); 1.4998 + return NS_OK; 1.4999 + } 1.5000 + 1.5001 + // on proxy errors, try to failover 1.5002 + if (mConnectionInfo->ProxyInfo() && 1.5003 + (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED || 1.5004 + mStatus == NS_ERROR_UNKNOWN_PROXY_HOST || 1.5005 + mStatus == NS_ERROR_NET_TIMEOUT)) { 1.5006 + 1.5007 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1); 1.5008 + if (NS_SUCCEEDED(ProxyFailover())) 1.5009 + return NS_OK; 1.5010 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1); 1.5011 + } 1.5012 + 1.5013 + return ContinueOnStartRequest2(NS_OK); 1.5014 +} 1.5015 + 1.5016 +nsresult 1.5017 +nsHttpChannel::ContinueOnStartRequest1(nsresult result) 1.5018 +{ 1.5019 + // Success indicates we passed ProxyFailover, in that case we must not continue 1.5020 + // with this code chain. 1.5021 + if (NS_SUCCEEDED(result)) 1.5022 + return NS_OK; 1.5023 + 1.5024 + return ContinueOnStartRequest2(result); 1.5025 +} 1.5026 + 1.5027 +nsresult 1.5028 +nsHttpChannel::ContinueOnStartRequest2(nsresult result) 1.5029 +{ 1.5030 + // on other request errors, try to fall back 1.5031 + if (NS_FAILED(mStatus)) { 1.5032 + PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3); 1.5033 + bool waitingForRedirectCallback; 1.5034 + ProcessFallback(&waitingForRedirectCallback); 1.5035 + if (waitingForRedirectCallback) 1.5036 + return NS_OK; 1.5037 + PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3); 1.5038 + } 1.5039 + 1.5040 + return ContinueOnStartRequest3(NS_OK); 1.5041 +} 1.5042 + 1.5043 +nsresult 1.5044 +nsHttpChannel::ContinueOnStartRequest3(nsresult result) 1.5045 +{ 1.5046 + if (mFallingBack) 1.5047 + return NS_OK; 1.5048 + 1.5049 + return CallOnStartRequest(); 1.5050 +} 1.5051 + 1.5052 +NS_IMETHODIMP 1.5053 +nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) 1.5054 +{ 1.5055 + PROFILER_LABEL("network", "nsHttpChannel::OnStopRequest"); 1.5056 + LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n", 1.5057 + this, request, status)); 1.5058 + 1.5059 + if (mTimingEnabled && request == mCachePump) { 1.5060 + mCacheReadEnd = TimeStamp::Now(); 1.5061 + } 1.5062 + 1.5063 + // allow content to be cached if it was loaded successfully (bug #482935) 1.5064 + bool contentComplete = NS_SUCCEEDED(status); 1.5065 + 1.5066 + // honor the cancelation status even if the underlying transaction completed. 1.5067 + if (mCanceled || NS_FAILED(mStatus)) 1.5068 + status = mStatus; 1.5069 + 1.5070 + if (mCachedContentIsPartial) { 1.5071 + if (NS_SUCCEEDED(status)) { 1.5072 + // mTransactionPump should be suspended 1.5073 + MOZ_ASSERT(request != mTransactionPump, 1.5074 + "byte-range transaction finished prematurely"); 1.5075 + 1.5076 + if (request == mCachePump) { 1.5077 + bool streamDone; 1.5078 + status = OnDoneReadingPartialCacheEntry(&streamDone); 1.5079 + if (NS_SUCCEEDED(status) && !streamDone) 1.5080 + return status; 1.5081 + // otherwise, fall through and fire OnStopRequest... 1.5082 + } 1.5083 + else if (request == mTransactionPump) { 1.5084 + MOZ_ASSERT(mConcurentCacheAccess); 1.5085 + } 1.5086 + else 1.5087 + NS_NOTREACHED("unexpected request"); 1.5088 + } 1.5089 + // Do not to leave the transaction in a suspended state in error cases. 1.5090 + if (NS_FAILED(status) && mTransaction) 1.5091 + gHttpHandler->CancelTransaction(mTransaction, status); 1.5092 + } 1.5093 + 1.5094 + if (mTransaction) { 1.5095 + // determine if we should call DoAuthRetry 1.5096 + bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status); 1.5097 + 1.5098 + // 1.5099 + // grab references to connection in case we need to retry an 1.5100 + // authentication request over it or use it for an upgrade 1.5101 + // to another protocol. 1.5102 + // 1.5103 + // this code relies on the code in nsHttpTransaction::Close, which 1.5104 + // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to 1.5105 + // keep the connection around after the transaction is finished. 1.5106 + // 1.5107 + nsRefPtr<nsAHttpConnection> conn; 1.5108 + if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION)) { 1.5109 + conn = mTransaction->GetConnectionReference(); 1.5110 + // This is so far a workaround to fix leak when reusing unpersistent 1.5111 + // connection for authentication retry. See bug 459620 comment 4 1.5112 + // for details. 1.5113 + if (conn && !conn->IsPersistent()) 1.5114 + conn = nullptr; 1.5115 + } 1.5116 + 1.5117 + nsRefPtr<nsAHttpConnection> stickyConn; 1.5118 + if (mCaps & NS_HTTP_STICKY_CONNECTION) 1.5119 + stickyConn = mTransaction->GetConnectionReference(); 1.5120 + 1.5121 + // at this point, we're done with the transaction 1.5122 + mTransactionTimings = mTransaction->Timings(); 1.5123 + mTransaction = nullptr; 1.5124 + mTransactionPump = nullptr; 1.5125 + 1.5126 + // We no longer need the dns prefetch object 1.5127 + if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) { 1.5128 + mTransactionTimings.domainLookupStart = 1.5129 + mDNSPrefetch->StartTimestamp(); 1.5130 + mTransactionTimings.domainLookupEnd = 1.5131 + mDNSPrefetch->EndTimestamp(); 1.5132 + } 1.5133 + mDNSPrefetch = nullptr; 1.5134 + 1.5135 + // handle auth retry... 1.5136 + if (authRetry) { 1.5137 + mAuthRetryPending = false; 1.5138 + status = DoAuthRetry(conn); 1.5139 + if (NS_SUCCEEDED(status)) 1.5140 + return NS_OK; 1.5141 + } 1.5142 + 1.5143 + // If DoAuthRetry failed, or if we have been cancelled since showing 1.5144 + // the auth. dialog, then we need to send OnStartRequest now 1.5145 + if (authRetry || (mAuthRetryPending && NS_FAILED(status))) { 1.5146 + MOZ_ASSERT(NS_FAILED(status), "should have a failure code here"); 1.5147 + // NOTE: since we have a failure status, we can ignore the return 1.5148 + // value from onStartRequest. 1.5149 + if (mListener) { 1.5150 + mListener->OnStartRequest(this, mListenerContext); 1.5151 + } else { 1.5152 + NS_WARNING("OnStartRequest skipped because of null listener"); 1.5153 + } 1.5154 + } 1.5155 + 1.5156 + // if this transaction has been replaced, then bail. 1.5157 + if (mTransactionReplaced) 1.5158 + return NS_OK; 1.5159 + 1.5160 + if (mUpgradeProtocolCallback && stickyConn && 1.5161 + mResponseHead && mResponseHead->Status() == 101) { 1.5162 + gHttpHandler->ConnMgr()->CompleteUpgrade(stickyConn, 1.5163 + mUpgradeProtocolCallback); 1.5164 + } 1.5165 + } 1.5166 + 1.5167 + mIsPending = false; 1.5168 + 1.5169 + // if needed, check cache entry has all data we expect 1.5170 + if (mCacheEntry && mCachePump && 1.5171 + mConcurentCacheAccess && contentComplete) { 1.5172 + int64_t size, contentLength; 1.5173 + nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength); 1.5174 + if (NS_SUCCEEDED(rv)) { 1.5175 + if (size == int64_t(-1)) { 1.5176 + // mayhemer TODO - we have to restart read from cache here at the size offset 1.5177 + MOZ_ASSERT(false); 1.5178 + LOG((" cache entry write is still in progress, but we just " 1.5179 + "finished reading the cache entry")); 1.5180 + } 1.5181 + else if (contentLength != int64_t(-1) && contentLength != size) { 1.5182 + LOG((" concurrent cache entry write has been interrupted")); 1.5183 + mCachedResponseHead = mResponseHead; 1.5184 + rv = MaybeSetupByteRangeRequest(size, contentLength); 1.5185 + if (NS_SUCCEEDED(rv) && mIsPartialRequest) { 1.5186 + // Prevent read from cache again 1.5187 + mCachedContentIsValid = 0; 1.5188 + mCachedContentIsPartial = 1; 1.5189 + 1.5190 + // Perform the range request 1.5191 + rv = ContinueConnect(); 1.5192 + if (NS_SUCCEEDED(rv)) { 1.5193 + LOG((" performing range request")); 1.5194 + mCachePump = nullptr; 1.5195 + return NS_OK; 1.5196 + } else { 1.5197 + LOG((" but range request perform failed 0x%08x", rv)); 1.5198 + status = NS_ERROR_NET_INTERRUPT; 1.5199 + } 1.5200 + } 1.5201 + else { 1.5202 + LOG((" but range request setup failed rv=0x%08x, failing load", rv)); 1.5203 + } 1.5204 + } 1.5205 + } 1.5206 + } 1.5207 + 1.5208 + mStatus = status; 1.5209 + 1.5210 + // perform any final cache operations before we close the cache entry. 1.5211 + if (mCacheEntry && mRequestTimeInitialized) { 1.5212 + bool writeAccess; 1.5213 + // New implementation just returns value of the !mCacheEntryIsReadOnly flag passed in. 1.5214 + // Old implementation checks on nsICache::ACCESS_WRITE flag. 1.5215 + mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly, &writeAccess); 1.5216 + if (writeAccess) { 1.5217 + FinalizeCacheEntry(); 1.5218 + } 1.5219 + } 1.5220 + 1.5221 + // Register entry to the Performance resource timing 1.5222 + nsPerformance* documentPerformance = GetPerformance(); 1.5223 + if (documentPerformance) { 1.5224 + documentPerformance->AddEntry(this, this); 1.5225 + } 1.5226 + 1.5227 + if (mListener) { 1.5228 + LOG((" calling OnStopRequest\n")); 1.5229 + mListener->OnStopRequest(this, mListenerContext, status); 1.5230 + } 1.5231 + 1.5232 + MOZ_EVENT_TRACER_DONE(this, "net::http::channel"); 1.5233 + 1.5234 + CloseCacheEntry(!contentComplete); 1.5235 + 1.5236 + if (mOfflineCacheEntry) 1.5237 + CloseOfflineCacheEntry(); 1.5238 + 1.5239 + if (mLoadGroup) 1.5240 + mLoadGroup->RemoveRequest(this, nullptr, status); 1.5241 + 1.5242 + // We don't need this info anymore 1.5243 + CleanRedirectCacheChainIfNecessary(); 1.5244 + 1.5245 + ReleaseListeners(); 1.5246 + 1.5247 + return NS_OK; 1.5248 +} 1.5249 + 1.5250 +//----------------------------------------------------------------------------- 1.5251 +// nsHttpChannel::nsIStreamListener 1.5252 +//----------------------------------------------------------------------------- 1.5253 + 1.5254 +class OnTransportStatusAsyncEvent : public nsRunnable 1.5255 +{ 1.5256 +public: 1.5257 + OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink, 1.5258 + nsresult aTransportStatus, 1.5259 + uint64_t aProgress, 1.5260 + uint64_t aProgressMax) 1.5261 + : mEventSink(aEventSink) 1.5262 + , mTransportStatus(aTransportStatus) 1.5263 + , mProgress(aProgress) 1.5264 + , mProgressMax(aProgressMax) 1.5265 + { 1.5266 + MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread"); 1.5267 + } 1.5268 + 1.5269 + NS_IMETHOD Run() 1.5270 + { 1.5271 + MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread"); 1.5272 + if (mEventSink) { 1.5273 + mEventSink->OnTransportStatus(nullptr, mTransportStatus, 1.5274 + mProgress, mProgressMax); 1.5275 + } 1.5276 + return NS_OK; 1.5277 + } 1.5278 +private: 1.5279 + nsCOMPtr<nsITransportEventSink> mEventSink; 1.5280 + nsresult mTransportStatus; 1.5281 + uint64_t mProgress; 1.5282 + uint64_t mProgressMax; 1.5283 +}; 1.5284 + 1.5285 +NS_IMETHODIMP 1.5286 +nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, 1.5287 + nsIInputStream *input, 1.5288 + uint64_t offset, uint32_t count) 1.5289 +{ 1.5290 + PROFILER_LABEL("network", "nsHttpChannel::OnDataAvailable"); 1.5291 + LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%llu count=%u]\n", 1.5292 + this, request, offset, count)); 1.5293 + 1.5294 + // don't send out OnDataAvailable notifications if we've been canceled. 1.5295 + if (mCanceled) 1.5296 + return mStatus; 1.5297 + 1.5298 + MOZ_ASSERT(mResponseHead, "No response head in ODA!!"); 1.5299 + 1.5300 + MOZ_ASSERT(!(mCachedContentIsPartial && (request == mTransactionPump)), 1.5301 + "transaction pump not suspended"); 1.5302 + 1.5303 + if (mAuthRetryPending || (request == mTransactionPump && mTransactionReplaced)) { 1.5304 + uint32_t n; 1.5305 + return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n); 1.5306 + } 1.5307 + 1.5308 + if (mListener) { 1.5309 + // 1.5310 + // synthesize transport progress event. we do this here since we want 1.5311 + // to delay OnProgress events until we start streaming data. this is 1.5312 + // crucially important since it impacts the lock icon (see bug 240053). 1.5313 + // 1.5314 + nsresult transportStatus; 1.5315 + if (request == mCachePump) 1.5316 + transportStatus = NS_NET_STATUS_READING; 1.5317 + else 1.5318 + transportStatus = NS_NET_STATUS_RECEIVING_FROM; 1.5319 + 1.5320 + // mResponseHead may reference new or cached headers, but either way it 1.5321 + // holds our best estimate of the total content length. Even in the case 1.5322 + // of a byte range request, the content length stored in the cached 1.5323 + // response headers is what we want to use here. 1.5324 + 1.5325 + uint64_t progressMax(uint64_t(mResponseHead->ContentLength())); 1.5326 + uint64_t progress = mLogicalOffset + uint64_t(count); 1.5327 + 1.5328 + if (progress > progressMax) 1.5329 + NS_WARNING("unexpected progress values - " 1.5330 + "is server exceeding content length?"); 1.5331 + 1.5332 + if (NS_IsMainThread()) { 1.5333 + OnTransportStatus(nullptr, transportStatus, progress, progressMax); 1.5334 + } else { 1.5335 + nsresult rv = NS_DispatchToMainThread( 1.5336 + new OnTransportStatusAsyncEvent(this, transportStatus, 1.5337 + progress, progressMax)); 1.5338 + NS_ENSURE_SUCCESS(rv, rv); 1.5339 + } 1.5340 + 1.5341 + // 1.5342 + // we have to manually keep the logical offset of the stream up-to-date. 1.5343 + // we cannot depend solely on the offset provided, since we may have 1.5344 + // already streamed some data from another source (see, for example, 1.5345 + // OnDoneReadingPartialCacheEntry). 1.5346 + // 1.5347 + if (!mLogicalOffset) 1.5348 + MOZ_EVENT_TRACER_EXEC(this, "net::http::channel"); 1.5349 + 1.5350 + int64_t offsetBefore = 0; 1.5351 + nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input); 1.5352 + if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) { 1.5353 + seekable = nullptr; 1.5354 + } 1.5355 + 1.5356 + nsresult rv = mListener->OnDataAvailable(this, 1.5357 + mListenerContext, 1.5358 + input, 1.5359 + mLogicalOffset, 1.5360 + count); 1.5361 + if (NS_SUCCEEDED(rv)) { 1.5362 + // by contract mListener must read all of "count" bytes, but 1.5363 + // nsInputStreamPump is tolerant to seekable streams that violate that 1.5364 + // and it will redeliver incompletely read data. So we need to do 1.5365 + // the same thing when updating the progress counter to stay in sync. 1.5366 + int64_t offsetAfter, delta; 1.5367 + if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) { 1.5368 + delta = offsetAfter - offsetBefore; 1.5369 + if (delta != count) { 1.5370 + count = delta; 1.5371 + 1.5372 + NS_WARNING("Listener OnDataAvailable contract violation"); 1.5373 + nsCOMPtr<nsIConsoleService> consoleService = 1.5374 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.5375 + nsAutoString message 1.5376 + (NS_LITERAL_STRING( 1.5377 + "http channel Listener OnDataAvailable contract violation")); 1.5378 + if (consoleService) { 1.5379 + consoleService->LogStringMessage(message.get()); 1.5380 + } 1.5381 + } 1.5382 + } 1.5383 + mLogicalOffset += count; 1.5384 + } 1.5385 + 1.5386 + return rv; 1.5387 + } 1.5388 + 1.5389 + return NS_ERROR_ABORT; 1.5390 +} 1.5391 + 1.5392 +//----------------------------------------------------------------------------- 1.5393 +// nsHttpChannel::nsIThreadRetargetableRequest 1.5394 +//----------------------------------------------------------------------------- 1.5395 + 1.5396 +NS_IMETHODIMP 1.5397 +nsHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget) 1.5398 +{ 1.5399 + MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only"); 1.5400 + 1.5401 + NS_ENSURE_ARG(aNewTarget); 1.5402 + if (aNewTarget == NS_GetCurrentThread()) { 1.5403 + NS_WARNING("Retargeting delivery to same thread"); 1.5404 + return NS_OK; 1.5405 + } 1.5406 + NS_ENSURE_TRUE(mTransactionPump || mCachePump, NS_ERROR_NOT_AVAILABLE); 1.5407 + 1.5408 + nsresult rv = NS_OK; 1.5409 + // If both cache pump and transaction pump exist, we're probably dealing 1.5410 + // with partially cached content. So, we must be able to retarget both. 1.5411 + nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump; 1.5412 + nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump; 1.5413 + if (mCachePump) { 1.5414 + retargetableCachePump = do_QueryObject(mCachePump); 1.5415 + // nsInputStreamPump should implement this interface. 1.5416 + MOZ_ASSERT(retargetableCachePump); 1.5417 + rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget); 1.5418 + } 1.5419 + if (NS_SUCCEEDED(rv) && mTransactionPump) { 1.5420 + retargetableTransactionPump = do_QueryObject(mTransactionPump); 1.5421 + // nsInputStreamPump should implement this interface. 1.5422 + MOZ_ASSERT(retargetableTransactionPump); 1.5423 + rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget); 1.5424 + 1.5425 + // If retarget fails for transaction pump, we must restore mCachePump. 1.5426 + if (NS_FAILED(rv) && retargetableCachePump) { 1.5427 + nsCOMPtr<nsIThread> mainThread; 1.5428 + rv = NS_GetMainThread(getter_AddRefs(mainThread)); 1.5429 + NS_ENSURE_SUCCESS(rv, rv); 1.5430 + rv = retargetableCachePump->RetargetDeliveryTo(mainThread); 1.5431 + } 1.5432 + } 1.5433 + return rv; 1.5434 +} 1.5435 + 1.5436 +//----------------------------------------------------------------------------- 1.5437 +// nsHttpChannel::nsThreadRetargetableStreamListener 1.5438 +//----------------------------------------------------------------------------- 1.5439 + 1.5440 +NS_IMETHODIMP 1.5441 +nsHttpChannel::CheckListenerChain() 1.5442 +{ 1.5443 + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!"); 1.5444 + nsresult rv = NS_OK; 1.5445 + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = 1.5446 + do_QueryInterface(mListener, &rv); 1.5447 + if (retargetableListener) { 1.5448 + rv = retargetableListener->CheckListenerChain(); 1.5449 + } 1.5450 + return rv; 1.5451 +} 1.5452 + 1.5453 +//----------------------------------------------------------------------------- 1.5454 +// nsHttpChannel::nsITransportEventSink 1.5455 +//----------------------------------------------------------------------------- 1.5456 + 1.5457 +NS_IMETHODIMP 1.5458 +nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status, 1.5459 + uint64_t progress, uint64_t progressMax) 1.5460 +{ 1.5461 + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only"); 1.5462 + // cache the progress sink so we don't have to query for it each time. 1.5463 + if (!mProgressSink) 1.5464 + GetCallback(mProgressSink); 1.5465 + 1.5466 + if (status == NS_NET_STATUS_CONNECTED_TO || 1.5467 + status == NS_NET_STATUS_WAITING_FOR) { 1.5468 + nsCOMPtr<nsISocketTransport> socketTransport = 1.5469 + do_QueryInterface(trans); 1.5470 + if (socketTransport) { 1.5471 + socketTransport->GetSelfAddr(&mSelfAddr); 1.5472 + socketTransport->GetPeerAddr(&mPeerAddr); 1.5473 + } 1.5474 + } 1.5475 + 1.5476 + // block socket status event after Cancel or OnStopRequest has been called. 1.5477 + if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) { 1.5478 + LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n", 1.5479 + this, status, progress, progressMax)); 1.5480 + 1.5481 + nsAutoCString host; 1.5482 + mURI->GetHost(host); 1.5483 + mProgressSink->OnStatus(this, nullptr, status, 1.5484 + NS_ConvertUTF8toUTF16(host).get()); 1.5485 + 1.5486 + if (progress > 0) { 1.5487 + MOZ_ASSERT(progress <= progressMax, "unexpected progress values"); 1.5488 + // Try to get mProgressSink if it was nulled out during OnStatus. 1.5489 + if (!mProgressSink) { 1.5490 + GetCallback(mProgressSink); 1.5491 + } 1.5492 + if (mProgressSink) { 1.5493 + mProgressSink->OnProgress(this, nullptr, progress, progressMax); 1.5494 + } 1.5495 + } 1.5496 + } 1.5497 +#ifdef DEBUG 1.5498 + else 1.5499 + LOG(("skipping status notification [this=%p sink=%p pending=%u background=%x]\n", 1.5500 + this, mProgressSink.get(), mIsPending, (mLoadFlags & LOAD_BACKGROUND))); 1.5501 +#endif 1.5502 + 1.5503 + return NS_OK; 1.5504 +} 1.5505 + 1.5506 +//----------------------------------------------------------------------------- 1.5507 +// nsHttpChannel::nsICacheInfoChannel 1.5508 +//----------------------------------------------------------------------------- 1.5509 + 1.5510 +NS_IMETHODIMP 1.5511 +nsHttpChannel::IsFromCache(bool *value) 1.5512 +{ 1.5513 + if (!mIsPending) 1.5514 + return NS_ERROR_NOT_AVAILABLE; 1.5515 + 1.5516 + // return false if reading a partial cache entry; the data isn't entirely 1.5517 + // from the cache! 1.5518 + 1.5519 + *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) && 1.5520 + mCachedContentIsValid && !mCachedContentIsPartial; 1.5521 + 1.5522 + return NS_OK; 1.5523 +} 1.5524 + 1.5525 +NS_IMETHODIMP 1.5526 +nsHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval) 1.5527 +{ 1.5528 + NS_ENSURE_ARG_POINTER(_retval); 1.5529 + if (!mCacheEntry) 1.5530 + return NS_ERROR_NOT_AVAILABLE; 1.5531 + 1.5532 + return mCacheEntry->GetExpirationTime(_retval); 1.5533 +} 1.5534 + 1.5535 +NS_IMETHODIMP 1.5536 +nsHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval) 1.5537 +{ 1.5538 + nsresult rv; 1.5539 + 1.5540 + if (!mCacheEntry) 1.5541 + return NS_ERROR_NOT_AVAILABLE; 1.5542 + 1.5543 + nsXPIDLCString cachedCharset; 1.5544 + rv = mCacheEntry->GetMetaDataElement("charset", 1.5545 + getter_Copies(cachedCharset)); 1.5546 + if (NS_SUCCEEDED(rv)) 1.5547 + _retval = cachedCharset; 1.5548 + 1.5549 + return rv; 1.5550 +} 1.5551 + 1.5552 +NS_IMETHODIMP 1.5553 +nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset) 1.5554 +{ 1.5555 + if (!mCacheEntry) 1.5556 + return NS_ERROR_NOT_AVAILABLE; 1.5557 + 1.5558 + return mCacheEntry->SetMetaDataElement("charset", 1.5559 + PromiseFlatCString(aCharset).get()); 1.5560 +} 1.5561 + 1.5562 + 1.5563 +NS_IMETHODIMP 1.5564 +nsHttpChannel::GetCacheDomain(nsACString &value) 1.5565 +{ 1.5566 + value = mCacheDomain; 1.5567 + 1.5568 + return NS_OK; 1.5569 +} 1.5570 + 1.5571 +NS_IMETHODIMP 1.5572 +nsHttpChannel::SetCacheDomain(const nsACString &value) 1.5573 +{ 1.5574 + mCacheDomain = value; 1.5575 + 1.5576 + return NS_OK; 1.5577 +} 1.5578 + 1.5579 +//----------------------------------------------------------------------------- 1.5580 +// nsHttpChannel::nsICachingChannel 1.5581 +//----------------------------------------------------------------------------- 1.5582 + 1.5583 +NS_IMETHODIMP 1.5584 +nsHttpChannel::GetCacheToken(nsISupports **token) 1.5585 +{ 1.5586 + NS_ENSURE_ARG_POINTER(token); 1.5587 + if (!mCacheEntry) 1.5588 + return NS_ERROR_NOT_AVAILABLE; 1.5589 + return CallQueryInterface(mCacheEntry, token); 1.5590 +} 1.5591 + 1.5592 +NS_IMETHODIMP 1.5593 +nsHttpChannel::SetCacheToken(nsISupports *token) 1.5594 +{ 1.5595 + return NS_ERROR_NOT_IMPLEMENTED; 1.5596 +} 1.5597 + 1.5598 +NS_IMETHODIMP 1.5599 +nsHttpChannel::GetOfflineCacheToken(nsISupports **token) 1.5600 +{ 1.5601 + NS_ENSURE_ARG_POINTER(token); 1.5602 + if (!mOfflineCacheEntry) 1.5603 + return NS_ERROR_NOT_AVAILABLE; 1.5604 + return CallQueryInterface(mOfflineCacheEntry, token); 1.5605 +} 1.5606 + 1.5607 +NS_IMETHODIMP 1.5608 +nsHttpChannel::SetOfflineCacheToken(nsISupports *token) 1.5609 +{ 1.5610 + return NS_ERROR_NOT_IMPLEMENTED; 1.5611 +} 1.5612 + 1.5613 +class nsHttpChannelCacheKey MOZ_FINAL : public nsISupportsPRUint32, 1.5614 + public nsISupportsCString 1.5615 +{ 1.5616 + NS_DECL_ISUPPORTS 1.5617 + 1.5618 + NS_DECL_NSISUPPORTSPRIMITIVE 1.5619 + NS_FORWARD_NSISUPPORTSPRUINT32(mSupportsPRUint32->) 1.5620 + 1.5621 + // Both interfaces declares toString method with the same signature. 1.5622 + // Thus we have to delegate only to nsISupportsPRUint32 implementation. 1.5623 + NS_IMETHOD GetData(nsACString & aData) 1.5624 + { 1.5625 + return mSupportsCString->GetData(aData); 1.5626 + } 1.5627 + NS_IMETHOD SetData(const nsACString & aData) 1.5628 + { 1.5629 + return mSupportsCString->SetData(aData); 1.5630 + } 1.5631 + 1.5632 +public: 1.5633 + nsresult SetData(uint32_t aPostID, const nsACString& aKey); 1.5634 + 1.5635 +protected: 1.5636 + nsCOMPtr<nsISupportsPRUint32> mSupportsPRUint32; 1.5637 + nsCOMPtr<nsISupportsCString> mSupportsCString; 1.5638 +}; 1.5639 + 1.5640 +NS_IMPL_ADDREF(nsHttpChannelCacheKey) 1.5641 +NS_IMPL_RELEASE(nsHttpChannelCacheKey) 1.5642 +NS_INTERFACE_TABLE_HEAD(nsHttpChannelCacheKey) 1.5643 +NS_INTERFACE_TABLE_BEGIN 1.5644 +NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsHttpChannelCacheKey, 1.5645 + nsISupports, nsISupportsPRUint32) 1.5646 +NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsHttpChannelCacheKey, 1.5647 + nsISupportsPrimitive, nsISupportsPRUint32) 1.5648 +NS_INTERFACE_TABLE_ENTRY(nsHttpChannelCacheKey, 1.5649 + nsISupportsPRUint32) 1.5650 +NS_INTERFACE_TABLE_ENTRY(nsHttpChannelCacheKey, 1.5651 + nsISupportsCString) 1.5652 +NS_INTERFACE_TABLE_END 1.5653 +NS_INTERFACE_TABLE_TAIL 1.5654 + 1.5655 +NS_IMETHODIMP nsHttpChannelCacheKey::GetType(uint16_t *aType) 1.5656 +{ 1.5657 + NS_ENSURE_ARG_POINTER(aType); 1.5658 + 1.5659 + *aType = TYPE_PRUINT32; 1.5660 + return NS_OK; 1.5661 +} 1.5662 + 1.5663 +nsresult nsHttpChannelCacheKey::SetData(uint32_t aPostID, 1.5664 + const nsACString& aKey) 1.5665 +{ 1.5666 + nsresult rv; 1.5667 + 1.5668 + mSupportsCString = 1.5669 + do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv); 1.5670 + if (NS_FAILED(rv)) return rv; 1.5671 + 1.5672 + mSupportsCString->SetData(aKey); 1.5673 + if (NS_FAILED(rv)) return rv; 1.5674 + 1.5675 + mSupportsPRUint32 = 1.5676 + do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv); 1.5677 + if (NS_FAILED(rv)) return rv; 1.5678 + 1.5679 + mSupportsPRUint32->SetData(aPostID); 1.5680 + if (NS_FAILED(rv)) return rv; 1.5681 + 1.5682 + return NS_OK; 1.5683 +} 1.5684 + 1.5685 +NS_IMETHODIMP 1.5686 +nsHttpChannel::GetCacheKey(nsISupports **key) 1.5687 +{ 1.5688 + // mayhemer: TODO - do we need this API? 1.5689 + 1.5690 + nsresult rv; 1.5691 + NS_ENSURE_ARG_POINTER(key); 1.5692 + 1.5693 + LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this)); 1.5694 + 1.5695 + *key = nullptr; 1.5696 + 1.5697 + nsRefPtr<nsHttpChannelCacheKey> container = 1.5698 + new nsHttpChannelCacheKey(); 1.5699 + 1.5700 + if (!container) 1.5701 + return NS_ERROR_OUT_OF_MEMORY; 1.5702 + 1.5703 + nsAutoCString cacheKey; 1.5704 + rv = GenerateCacheKey(mPostID, cacheKey); 1.5705 + if (NS_FAILED(rv)) return rv; 1.5706 + 1.5707 + rv = container->SetData(mPostID, cacheKey); 1.5708 + if (NS_FAILED(rv)) return rv; 1.5709 + 1.5710 + return CallQueryInterface(container.get(), key); 1.5711 +} 1.5712 + 1.5713 +NS_IMETHODIMP 1.5714 +nsHttpChannel::SetCacheKey(nsISupports *key) 1.5715 +{ 1.5716 + nsresult rv; 1.5717 + 1.5718 + LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n", this, key)); 1.5719 + 1.5720 + ENSURE_CALLED_BEFORE_CONNECT(); 1.5721 + 1.5722 + if (!key) 1.5723 + mPostID = 0; 1.5724 + else { 1.5725 + // extract the post id 1.5726 + nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(key, &rv); 1.5727 + if (NS_FAILED(rv)) return rv; 1.5728 + 1.5729 + rv = container->GetData(&mPostID); 1.5730 + if (NS_FAILED(rv)) return rv; 1.5731 + } 1.5732 + return NS_OK; 1.5733 +} 1.5734 + 1.5735 +//----------------------------------------------------------------------------- 1.5736 +// nsHttpChannel::nsIResumableChannel 1.5737 +//----------------------------------------------------------------------------- 1.5738 + 1.5739 +NS_IMETHODIMP 1.5740 +nsHttpChannel::ResumeAt(uint64_t aStartPos, 1.5741 + const nsACString& aEntityID) 1.5742 +{ 1.5743 + LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%llu id='%s']\n", 1.5744 + this, aStartPos, PromiseFlatCString(aEntityID).get())); 1.5745 + mEntityID = aEntityID; 1.5746 + mStartPos = aStartPos; 1.5747 + mResuming = true; 1.5748 + return NS_OK; 1.5749 +} 1.5750 + 1.5751 +nsresult 1.5752 +nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn) 1.5753 +{ 1.5754 + LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this)); 1.5755 + 1.5756 + MOZ_ASSERT(!mTransaction, "should not have a transaction"); 1.5757 + nsresult rv; 1.5758 + 1.5759 + // toggle mIsPending to allow nsIObserver implementations to modify 1.5760 + // the request headers (bug 95044). 1.5761 + mIsPending = false; 1.5762 + 1.5763 + // fetch cookies, and add them to the request header. 1.5764 + // the server response could have included cookies that must be sent with 1.5765 + // this authentication attempt (bug 84794). 1.5766 + // TODO: save cookies from auth response and send them here (bug 572151). 1.5767 + AddCookiesToRequest(); 1.5768 + 1.5769 + // notify "http-on-modify-request" observers 1.5770 + CallOnModifyRequestObservers(); 1.5771 + 1.5772 + mIsPending = true; 1.5773 + 1.5774 + // get rid of the old response headers 1.5775 + mResponseHead = nullptr; 1.5776 + 1.5777 + // rewind the upload stream 1.5778 + if (mUploadStream) { 1.5779 + nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); 1.5780 + if (seekable) 1.5781 + seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); 1.5782 + } 1.5783 + 1.5784 + // set sticky connection flag and disable pipelining. 1.5785 + mCaps |= NS_HTTP_STICKY_CONNECTION; 1.5786 + mCaps &= ~NS_HTTP_ALLOW_PIPELINING; 1.5787 + 1.5788 + // and create a new one... 1.5789 + rv = SetupTransaction(); 1.5790 + if (NS_FAILED(rv)) return rv; 1.5791 + 1.5792 + // transfer ownership of connection to transaction 1.5793 + if (conn) 1.5794 + mTransaction->SetConnection(conn); 1.5795 + 1.5796 + rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); 1.5797 + if (NS_FAILED(rv)) return rv; 1.5798 + 1.5799 + rv = mTransactionPump->AsyncRead(this, nullptr); 1.5800 + if (NS_FAILED(rv)) return rv; 1.5801 + 1.5802 + uint32_t suspendCount = mSuspendCount; 1.5803 + while (suspendCount--) 1.5804 + mTransactionPump->Suspend(); 1.5805 + 1.5806 + return NS_OK; 1.5807 +} 1.5808 + 1.5809 +//----------------------------------------------------------------------------- 1.5810 +// nsHttpChannel::nsIApplicationCacheChannel 1.5811 +//----------------------------------------------------------------------------- 1.5812 + 1.5813 +NS_IMETHODIMP 1.5814 +nsHttpChannel::GetApplicationCache(nsIApplicationCache **out) 1.5815 +{ 1.5816 + NS_IF_ADDREF(*out = mApplicationCache); 1.5817 + return NS_OK; 1.5818 +} 1.5819 + 1.5820 +NS_IMETHODIMP 1.5821 +nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache) 1.5822 +{ 1.5823 + ENSURE_CALLED_BEFORE_CONNECT(); 1.5824 + 1.5825 + mApplicationCache = appCache; 1.5826 + return NS_OK; 1.5827 +} 1.5828 + 1.5829 +NS_IMETHODIMP 1.5830 +nsHttpChannel::GetApplicationCacheForWrite(nsIApplicationCache **out) 1.5831 +{ 1.5832 + NS_IF_ADDREF(*out = mApplicationCacheForWrite); 1.5833 + return NS_OK; 1.5834 +} 1.5835 + 1.5836 +NS_IMETHODIMP 1.5837 +nsHttpChannel::SetApplicationCacheForWrite(nsIApplicationCache *appCache) 1.5838 +{ 1.5839 + ENSURE_CALLED_BEFORE_CONNECT(); 1.5840 + 1.5841 + mApplicationCacheForWrite = appCache; 1.5842 + return NS_OK; 1.5843 +} 1.5844 + 1.5845 +NS_IMETHODIMP 1.5846 +nsHttpChannel::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache) 1.5847 +{ 1.5848 + *aLoadedFromApplicationCache = mLoadedFromApplicationCache; 1.5849 + return NS_OK; 1.5850 +} 1.5851 + 1.5852 +NS_IMETHODIMP 1.5853 +nsHttpChannel::GetInheritApplicationCache(bool *aInherit) 1.5854 +{ 1.5855 + *aInherit = mInheritApplicationCache; 1.5856 + return NS_OK; 1.5857 +} 1.5858 + 1.5859 +NS_IMETHODIMP 1.5860 +nsHttpChannel::SetInheritApplicationCache(bool aInherit) 1.5861 +{ 1.5862 + ENSURE_CALLED_BEFORE_CONNECT(); 1.5863 + 1.5864 + mInheritApplicationCache = aInherit; 1.5865 + return NS_OK; 1.5866 +} 1.5867 + 1.5868 +NS_IMETHODIMP 1.5869 +nsHttpChannel::GetChooseApplicationCache(bool *aChoose) 1.5870 +{ 1.5871 + *aChoose = mChooseApplicationCache; 1.5872 + return NS_OK; 1.5873 +} 1.5874 + 1.5875 +NS_IMETHODIMP 1.5876 +nsHttpChannel::SetChooseApplicationCache(bool aChoose) 1.5877 +{ 1.5878 + ENSURE_CALLED_BEFORE_CONNECT(); 1.5879 + 1.5880 + mChooseApplicationCache = aChoose; 1.5881 + return NS_OK; 1.5882 +} 1.5883 + 1.5884 +nsHttpChannel::OfflineCacheEntryAsForeignMarker* 1.5885 +nsHttpChannel::GetOfflineCacheEntryAsForeignMarker() 1.5886 +{ 1.5887 + if (!mApplicationCache) 1.5888 + return nullptr; 1.5889 + 1.5890 + return new OfflineCacheEntryAsForeignMarker(mApplicationCache, mURI); 1.5891 +} 1.5892 + 1.5893 +nsresult 1.5894 +nsHttpChannel::OfflineCacheEntryAsForeignMarker::MarkAsForeign() 1.5895 +{ 1.5896 + nsresult rv; 1.5897 + 1.5898 + nsCOMPtr<nsIURI> noRefURI; 1.5899 + rv = mCacheURI->CloneIgnoringRef(getter_AddRefs(noRefURI)); 1.5900 + NS_ENSURE_SUCCESS(rv, rv); 1.5901 + 1.5902 + nsAutoCString spec; 1.5903 + rv = noRefURI->GetAsciiSpec(spec); 1.5904 + NS_ENSURE_SUCCESS(rv, rv); 1.5905 + 1.5906 + return mApplicationCache->MarkEntry(spec, 1.5907 + nsIApplicationCache::ITEM_FOREIGN); 1.5908 +} 1.5909 + 1.5910 +NS_IMETHODIMP 1.5911 +nsHttpChannel::MarkOfflineCacheEntryAsForeign() 1.5912 +{ 1.5913 + nsresult rv; 1.5914 + 1.5915 + nsAutoPtr<OfflineCacheEntryAsForeignMarker> marker( 1.5916 + GetOfflineCacheEntryAsForeignMarker()); 1.5917 + 1.5918 + if (!marker) 1.5919 + return NS_ERROR_NOT_AVAILABLE; 1.5920 + 1.5921 + rv = marker->MarkAsForeign(); 1.5922 + NS_ENSURE_SUCCESS(rv, rv); 1.5923 + 1.5924 + return NS_OK; 1.5925 +} 1.5926 + 1.5927 +//----------------------------------------------------------------------------- 1.5928 +// nsHttpChannel::nsIAsyncVerifyRedirectCallback 1.5929 +//----------------------------------------------------------------------------- 1.5930 + 1.5931 +nsresult 1.5932 +nsHttpChannel::WaitForRedirectCallback() 1.5933 +{ 1.5934 + nsresult rv; 1.5935 + LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this)); 1.5936 + 1.5937 + if (mTransactionPump) { 1.5938 + rv = mTransactionPump->Suspend(); 1.5939 + NS_ENSURE_SUCCESS(rv, rv); 1.5940 + } 1.5941 + if (mCachePump) { 1.5942 + rv = mCachePump->Suspend(); 1.5943 + if (NS_FAILED(rv) && mTransactionPump) { 1.5944 +#ifdef DEBUG 1.5945 + nsresult resume = 1.5946 +#endif 1.5947 + mTransactionPump->Resume(); 1.5948 + MOZ_ASSERT(NS_SUCCEEDED(resume), 1.5949 + "Failed to resume transaction pump"); 1.5950 + } 1.5951 + NS_ENSURE_SUCCESS(rv, rv); 1.5952 + } 1.5953 + 1.5954 + mWaitingForRedirectCallback = true; 1.5955 + return NS_OK; 1.5956 +} 1.5957 + 1.5958 +NS_IMETHODIMP 1.5959 +nsHttpChannel::OnRedirectVerifyCallback(nsresult result) 1.5960 +{ 1.5961 + LOG(("nsHttpChannel::OnRedirectVerifyCallback [this=%p] " 1.5962 + "result=%x stack=%d mWaitingForRedirectCallback=%u\n", 1.5963 + this, result, mRedirectFuncStack.Length(), mWaitingForRedirectCallback)); 1.5964 + MOZ_ASSERT(mWaitingForRedirectCallback, 1.5965 + "Someone forgot to call WaitForRedirectCallback() ?!"); 1.5966 + mWaitingForRedirectCallback = false; 1.5967 + 1.5968 + if (mCanceled && NS_SUCCEEDED(result)) 1.5969 + result = NS_BINDING_ABORTED; 1.5970 + 1.5971 + for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) { 1.5972 + --i; 1.5973 + // Pop the last function pushed to the stack 1.5974 + nsContinueRedirectionFunc func = mRedirectFuncStack[i]; 1.5975 + mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length() - 1); 1.5976 + 1.5977 + // Call it with the result we got from the callback or the deeper 1.5978 + // function call. 1.5979 + result = (this->*func)(result); 1.5980 + 1.5981 + // If a new function has been pushed to the stack and placed us in the 1.5982 + // waiting state, we need to break the chain and wait for the callback 1.5983 + // again. 1.5984 + if (mWaitingForRedirectCallback) 1.5985 + break; 1.5986 + } 1.5987 + 1.5988 + if (NS_FAILED(result) && !mCanceled) { 1.5989 + // First, cancel this channel if we are in failure state to set mStatus 1.5990 + // and let it be propagated to pumps. 1.5991 + Cancel(result); 1.5992 + } 1.5993 + 1.5994 + if (!mWaitingForRedirectCallback) { 1.5995 + // We are not waiting for the callback. At this moment we must release 1.5996 + // reference to the redirect target channel, otherwise we may leak. 1.5997 + mRedirectChannel = nullptr; 1.5998 + MOZ_EVENT_TRACER_DONE(this, "net::http::channel"); 1.5999 + } 1.6000 + 1.6001 + // We always resume the pumps here. If all functions on stack have been 1.6002 + // called we need OnStopRequest to be triggered, and if we broke out of the 1.6003 + // loop above (and are thus waiting for a new callback) the suspension 1.6004 + // count must be balanced in the pumps. 1.6005 + if (mTransactionPump) 1.6006 + mTransactionPump->Resume(); 1.6007 + if (mCachePump) 1.6008 + mCachePump->Resume(); 1.6009 + 1.6010 + return result; 1.6011 +} 1.6012 + 1.6013 +void 1.6014 +nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func) 1.6015 +{ 1.6016 + mRedirectFuncStack.AppendElement(func); 1.6017 +} 1.6018 + 1.6019 +void 1.6020 +nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func) 1.6021 +{ 1.6022 + MOZ_ASSERT(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1], 1.6023 + "Trying to pop wrong method from redirect async stack!"); 1.6024 + 1.6025 + mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1); 1.6026 +} 1.6027 + 1.6028 +//----------------------------------------------------------------------------- 1.6029 +// nsIDNSListener functions 1.6030 +//----------------------------------------------------------------------------- 1.6031 + 1.6032 +NS_IMETHODIMP 1.6033 +nsHttpChannel::OnLookupComplete(nsICancelable *request, 1.6034 + nsIDNSRecord *rec, 1.6035 + nsresult status) 1.6036 +{ 1.6037 + MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread."); 1.6038 + 1.6039 + LOG(("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: " 1.6040 + "%s status[0x%x]\n", 1.6041 + this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "", 1.6042 + NS_SUCCEEDED(status) ? "success" : "failure", status)); 1.6043 + 1.6044 + // We no longer need the dns prefetch object. Note: mDNSPrefetch could be 1.6045 + // validly null if OnStopRequest has already been called. 1.6046 + if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) { 1.6047 + mTransactionTimings.domainLookupStart = 1.6048 + mDNSPrefetch->StartTimestamp(); 1.6049 + mTransactionTimings.domainLookupEnd = 1.6050 + mDNSPrefetch->EndTimestamp(); 1.6051 + } 1.6052 + mDNSPrefetch = nullptr; 1.6053 + 1.6054 + // Unset DNS cache refresh if it was requested, 1.6055 + if (mCaps & NS_HTTP_REFRESH_DNS) { 1.6056 + mCaps &= ~NS_HTTP_REFRESH_DNS; 1.6057 + if (mTransaction) { 1.6058 + mTransaction->SetDNSWasRefreshed(); 1.6059 + } 1.6060 + } 1.6061 + 1.6062 + return NS_OK; 1.6063 +} 1.6064 + 1.6065 +//----------------------------------------------------------------------------- 1.6066 +// nsHttpChannel internal functions 1.6067 +//----------------------------------------------------------------------------- 1.6068 + 1.6069 +void 1.6070 +nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet() 1.6071 +{ 1.6072 + // See RFC 2616 section 5.1.1. These are considered valid 1.6073 + // methods which DO NOT invalidate cache-entries for the 1.6074 + // referred resource. POST, PUT and DELETE as well as any 1.6075 + // other method not listed here will potentially invalidate 1.6076 + // any cached copy of the resource 1.6077 + if (mRequestHead.IsGet() || mRequestHead.IsOptions() || 1.6078 + mRequestHead.IsHead() || mRequestHead.IsTrace() || 1.6079 + mRequestHead.IsConnect()) { 1.6080 + return; 1.6081 + } 1.6082 + 1.6083 + // Invalidate the request-uri. 1.6084 +#ifdef PR_LOGGING 1.6085 + nsAutoCString key; 1.6086 + mURI->GetAsciiSpec(key); 1.6087 + LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n", 1.6088 + this, key.get())); 1.6089 +#endif 1.6090 + 1.6091 + DoInvalidateCacheEntry(mURI); 1.6092 + 1.6093 + // Invalidate Location-header if set 1.6094 + const char *location = mResponseHead->PeekHeader(nsHttp::Location); 1.6095 + if (location) { 1.6096 + LOG((" Location-header=%s\n", location)); 1.6097 + InvalidateCacheEntryForLocation(location); 1.6098 + } 1.6099 + 1.6100 + // Invalidate Content-Location-header if set 1.6101 + location = mResponseHead->PeekHeader(nsHttp::Content_Location); 1.6102 + if (location) { 1.6103 + LOG((" Content-Location-header=%s\n", location)); 1.6104 + InvalidateCacheEntryForLocation(location); 1.6105 + } 1.6106 +} 1.6107 + 1.6108 +void 1.6109 +nsHttpChannel::InvalidateCacheEntryForLocation(const char *location) 1.6110 +{ 1.6111 + nsAutoCString tmpCacheKey, tmpSpec; 1.6112 + nsCOMPtr<nsIURI> resultingURI; 1.6113 + nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI)); 1.6114 + if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) { 1.6115 + DoInvalidateCacheEntry(resultingURI); 1.6116 + } else { 1.6117 + LOG((" hosts not matching\n")); 1.6118 + } 1.6119 +} 1.6120 + 1.6121 +void 1.6122 +nsHttpChannel::DoInvalidateCacheEntry(nsIURI* aURI) 1.6123 +{ 1.6124 + // NOTE: 1.6125 + // Following comments 24,32 and 33 in bug #327765, we only care about 1.6126 + // the cache in the protocol-handler, not the application cache. 1.6127 + // The logic below deviates from the original logic in OpenCacheEntry on 1.6128 + // one point by using only READ_ONLY access-policy. I think this is safe. 1.6129 + 1.6130 + nsresult rv; 1.6131 + 1.6132 +#ifdef PR_LOGGING 1.6133 + nsAutoCString key; 1.6134 + aURI->GetAsciiSpec(key); 1.6135 + LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get())); 1.6136 +#endif 1.6137 + 1.6138 + nsCOMPtr<nsICacheStorageService> cacheStorageService = 1.6139 + do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); 1.6140 + 1.6141 + nsCOMPtr<nsICacheStorage> cacheStorage; 1.6142 + if (NS_SUCCEEDED(rv)) { 1.6143 + nsRefPtr<LoadContextInfo> info = GetLoadContextInfo(this); 1.6144 + rv = cacheStorageService->DiskCacheStorage(info, false, getter_AddRefs(cacheStorage)); 1.6145 + } 1.6146 + 1.6147 + if (NS_SUCCEEDED(rv)) { 1.6148 + rv = cacheStorage->AsyncDoomURI(aURI, EmptyCString(), nullptr); 1.6149 + } 1.6150 + 1.6151 + LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(), int(rv))); 1.6152 +} 1.6153 + 1.6154 +void 1.6155 +nsHttpChannel::AsyncOnExamineCachedResponse() 1.6156 +{ 1.6157 + gHttpHandler->OnExamineCachedResponse(this); 1.6158 + 1.6159 +} 1.6160 + 1.6161 +void 1.6162 +nsHttpChannel::UpdateAggregateCallbacks() 1.6163 +{ 1.6164 + if (!mTransaction) { 1.6165 + return; 1.6166 + } 1.6167 + nsCOMPtr<nsIInterfaceRequestor> callbacks; 1.6168 + NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 1.6169 + NS_GetCurrentThread(), 1.6170 + getter_AddRefs(callbacks)); 1.6171 + mTransaction->SetSecurityCallbacks(callbacks); 1.6172 +} 1.6173 + 1.6174 +nsIPrincipal * 1.6175 +nsHttpChannel::GetPrincipal() 1.6176 +{ 1.6177 + if (mPrincipal) 1.6178 + return mPrincipal; 1.6179 + 1.6180 + nsIScriptSecurityManager *securityManager = 1.6181 + nsContentUtils::GetSecurityManager(); 1.6182 + 1.6183 + if (!securityManager) 1.6184 + return nullptr; 1.6185 + 1.6186 + securityManager->GetChannelPrincipal(this, getter_AddRefs(mPrincipal)); 1.6187 + if (!mPrincipal) 1.6188 + return nullptr; 1.6189 + 1.6190 + // principals with unknown app ids do not work with the permission manager 1.6191 + if (mPrincipal->GetUnknownAppId()) 1.6192 + mPrincipal = nullptr; 1.6193 + 1.6194 + return mPrincipal; 1.6195 +} 1.6196 + 1.6197 + 1.6198 +NS_IMETHODIMP 1.6199 +nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) 1.6200 +{ 1.6201 + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread."); 1.6202 + 1.6203 + nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup); 1.6204 + if (NS_SUCCEEDED(rv)) { 1.6205 + UpdateAggregateCallbacks(); 1.6206 + } 1.6207 + return rv; 1.6208 +} 1.6209 + 1.6210 +NS_IMETHODIMP 1.6211 +nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) 1.6212 +{ 1.6213 + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread."); 1.6214 + 1.6215 + nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks); 1.6216 + if (NS_SUCCEEDED(rv)) { 1.6217 + UpdateAggregateCallbacks(); 1.6218 + } 1.6219 + return rv; 1.6220 +} 1.6221 + 1.6222 +nsPerformance* 1.6223 +nsHttpChannel::GetPerformance() 1.6224 +{ 1.6225 + // If performance timing is disabled, there is no need for the nsPerformance 1.6226 + // object anymore. 1.6227 + if (!mTimingEnabled) { 1.6228 + return nullptr; 1.6229 + } 1.6230 + nsCOMPtr<nsILoadContext> loadContext; 1.6231 + NS_QueryNotificationCallbacks(this, loadContext); 1.6232 + if (!loadContext) { 1.6233 + return nullptr; 1.6234 + } 1.6235 + nsCOMPtr<nsIDOMWindow> domWindow; 1.6236 + loadContext->GetAssociatedWindow(getter_AddRefs(domWindow)); 1.6237 + if (!domWindow) { 1.6238 + return nullptr; 1.6239 + } 1.6240 + nsCOMPtr<nsPIDOMWindow> pDomWindow = do_QueryInterface(domWindow); 1.6241 + if (!pDomWindow) { 1.6242 + return nullptr; 1.6243 + } 1.6244 + if (!pDomWindow->IsInnerWindow()) { 1.6245 + pDomWindow = pDomWindow->GetCurrentInnerWindow(); 1.6246 + if (!pDomWindow) { 1.6247 + return nullptr; 1.6248 + } 1.6249 + } 1.6250 + 1.6251 + nsPerformance* docPerformance = pDomWindow->GetPerformance(); 1.6252 + if (!docPerformance) { 1.6253 + return nullptr; 1.6254 + } 1.6255 + // iframes should be added to the parent's entries list. 1.6256 + if (mLoadFlags & LOAD_DOCUMENT_URI) { 1.6257 + return docPerformance->GetParentPerformance(); 1.6258 + } 1.6259 + return docPerformance; 1.6260 +} 1.6261 + 1.6262 +void 1.6263 +nsHttpChannel::ForcePending(bool aForcePending) 1.6264 +{ 1.6265 + // Set true here so IsPending will return true. 1.6266 + // Required for callback diversion from child back to parent. In such cases 1.6267 + // OnStopRequest can be called in the parent before callbacks are diverted 1.6268 + // back from the child to the listener in the parent. 1.6269 + mForcePending = aForcePending; 1.6270 +} 1.6271 + 1.6272 +NS_IMETHODIMP 1.6273 +nsHttpChannel::IsPending(bool *aIsPending) 1.6274 +{ 1.6275 + NS_ENSURE_ARG_POINTER(aIsPending); 1.6276 + *aIsPending = mIsPending || mForcePending; 1.6277 + return NS_OK; 1.6278 +} 1.6279 + 1.6280 +} } // namespace mozilla::net