netwerk/protocol/http/nsHttpChannel.cpp

changeset 0
6474c204b198
     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, &currentAge);
  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

mercurial