LCOV - code coverage report
Current view: directory - netwerk/protocol/http - nsHttpChannel.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 2351 1814 77.2 %
Date: 2012-06-02 Functions: 181 135 74.6 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* vim:set expandtab ts=4 sw=4 sts=4 cin: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Darin Fisher <darin@meer.net> (original author)
      25                 :  *   Christian Biesinger <cbiesinger@web.de>
      26                 :  *   Google Inc.
      27                 :  *   Jan Wrobel <wrobel@blues.ath.cx>
      28                 :  *   Jan Odvarko <odvarko@gmail.com>
      29                 :  *   Dave Camp <dcamp@mozilla.com>
      30                 :  *   Honza Bambas <honzab@firemni.cz>
      31                 :  *   Daniel Witte <dwitte@mozilla.com>
      32                 :  *   Jason Duell <jduell.mcbugs@gmail.com>
      33                 :  *
      34                 :  * Alternatively, the contents of this file may be used under the terms of
      35                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      36                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      37                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      38                 :  * of those above. If you wish to allow use of your version of this file only
      39                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      40                 :  * use your version of this file under the terms of the MPL, indicate your
      41                 :  * decision by deleting the provisions above and replace them with the notice
      42                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      43                 :  * the provisions above, a recipient may use your version of this file under
      44                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      45                 :  *
      46                 :  * ***** END LICENSE BLOCK ***** */
      47                 : 
      48                 : #include "nsHttpChannel.h"
      49                 : #include "nsHttpHandler.h"
      50                 : #include "nsIApplicationCacheService.h"
      51                 : #include "nsIApplicationCacheContainer.h"
      52                 : #include "nsIAuthInformation.h"
      53                 : #include "nsIStringBundle.h"
      54                 : #include "nsIIDNService.h"
      55                 : #include "nsIStreamListenerTee.h"
      56                 : #include "nsISeekableStream.h"
      57                 : #include "nsMimeTypes.h"
      58                 : #include "nsNetUtil.h"
      59                 : #include "prprf.h"
      60                 : #include "prnetdb.h"
      61                 : #include "nsEscape.h"
      62                 : #include "nsStreamUtils.h"
      63                 : #include "nsIOService.h"
      64                 : #include "nsICacheService.h"
      65                 : #include "nsDNSPrefetch.h"
      66                 : #include "nsChannelClassifier.h"
      67                 : #include "nsIRedirectResultListener.h"
      68                 : #include "mozilla/TimeStamp.h"
      69                 : #include "mozilla/Telemetry.h"
      70                 : #include "nsDOMError.h"
      71                 : #include "nsAlgorithm.h"
      72                 : #include "sampler.h"
      73                 : 
      74                 : using namespace mozilla;
      75                 : 
      76                 : // Device IDs for various cache types
      77                 : const char kDiskDeviceID[] = "disk";
      78                 : const char kMemoryDeviceID[] = "memory";
      79                 : const char kOfflineDeviceID[] = "offline";
      80                 : 
      81                 : // True if the local cache should be bypassed when processing a request.
      82                 : #define BYPASS_LOCAL_CACHE(loadFlags) \
      83                 :         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
      84                 :                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
      85                 : 
      86                 : static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
      87                 : 
      88                 : class AutoRedirectVetoNotifier
      89                 : {
      90                 : public:
      91             155 :     AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel) {}
      92             155 :     ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);}
      93             140 :     void RedirectSucceeded() {ReportRedirectResult(true);}
      94                 : 
      95                 : private:
      96                 :     nsHttpChannel* mChannel;
      97                 :     void ReportRedirectResult(bool succeeded);
      98                 : };
      99                 : 
     100                 : void
     101             295 : AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
     102                 : {
     103             295 :     if (!mChannel)
     104             140 :         return;
     105                 : 
     106             155 :     mChannel->mRedirectChannel = nsnull;
     107                 : 
     108             310 :     nsCOMPtr<nsIRedirectResultListener> vetoHook;
     109                 :     NS_QueryNotificationCallbacks(mChannel, 
     110                 :                                   NS_GET_IID(nsIRedirectResultListener), 
     111             155 :                                   getter_AddRefs(vetoHook));
     112             155 :     mChannel = nsnull;
     113             155 :     if (vetoHook)
     114               0 :         vetoHook->OnRedirectResult(succeeded);
     115                 : }
     116                 : 
     117                 : //-----------------------------------------------------------------------------
     118                 : // nsHttpChannel <public>
     119                 : //-----------------------------------------------------------------------------
     120                 : 
     121            3514 : nsHttpChannel::nsHttpChannel()
     122                 :     : HttpAsyncAborter<nsHttpChannel>(this)
     123                 :     , mLogicalOffset(0)
     124                 :     , mCacheAccess(0)
     125                 :     , mPostID(0)
     126                 :     , mRequestTime(0)
     127                 :     , mOnCacheEntryAvailableCallback(nsnull)
     128                 :     , mAsyncCacheOpen(false)
     129                 :     , mCachedContentIsValid(false)
     130                 :     , mCachedContentIsPartial(false)
     131                 :     , mTransactionReplaced(false)
     132                 :     , mAuthRetryPending(false)
     133                 :     , mResuming(false)
     134                 :     , mInitedCacheEntry(false)
     135                 :     , mCacheForOfflineUse(false)
     136                 :     , mCachingOpportunistically(false)
     137                 :     , mFallbackChannel(false)
     138                 :     , mCustomConditionalRequest(false)
     139                 :     , mFallingBack(false)
     140                 :     , mWaitingForRedirectCallback(false)
     141                 :     , mRequestTimeInitialized(false)
     142            3514 :     , mDidReval(false)
     143                 : {
     144            3514 :     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     145            3514 :     mChannelCreationTime = PR_Now();
     146            3514 :     mChannelCreationTimestamp = mozilla::TimeStamp::Now();
     147            3514 : }
     148                 : 
     149            9720 : nsHttpChannel::~nsHttpChannel()
     150                 : {
     151            3240 :     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
     152                 : 
     153            3240 :     if (mAuthProvider)
     154             480 :         mAuthProvider->Disconnect(NS_ERROR_ABORT);
     155           12960 : }
     156                 : 
     157                 : nsresult
     158            3514 : nsHttpChannel::Init(nsIURI *uri,
     159                 :                     PRUint8 caps,
     160                 :                     nsProxyInfo *proxyInfo)
     161                 : {
     162            3514 :     nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo);
     163            3514 :     if (NS_FAILED(rv))
     164               0 :         return rv;
     165                 : 
     166            3514 :     LOG(("nsHttpChannel::Init [this=%p]\n", this));
     167                 : 
     168                 :     mAuthProvider =
     169                 :         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
     170            3514 :                           &rv);
     171            3514 :     if (NS_FAILED(rv))
     172               0 :         return rv;
     173            3514 :     rv = mAuthProvider->Init(this);
     174                 : 
     175            3514 :     return rv;
     176                 : }
     177                 : //-----------------------------------------------------------------------------
     178                 : // nsHttpChannel <private>
     179                 : //-----------------------------------------------------------------------------
     180                 : 
     181                 : nsresult
     182            5055 : nsHttpChannel::Connect(bool firstTime)
     183                 : {
     184                 :     nsresult rv;
     185                 : 
     186            5055 :     LOG(("nsHttpChannel::Connect [this=%p]\n", this));
     187                 : 
     188                 :     // Even if we're in private browsing mode, we still enforce existing STS
     189                 :     // data (it is read-only).
     190                 :     // if the connection is not using SSL and either the exact host matches or
     191                 :     // a superdomain wants to force HTTPS, do it.
     192            5055 :     bool usingSSL = false;
     193            5055 :     rv = mURI->SchemeIs("https", &usingSSL);
     194            5055 :     NS_ENSURE_SUCCESS(rv,rv);
     195                 : 
     196            5055 :     if (!usingSSL) {
     197                 :         // enforce Strict-Transport-Security
     198            5047 :         nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
     199            5047 :         NS_ENSURE_TRUE(stss, NS_ERROR_OUT_OF_MEMORY);
     200                 : 
     201            5047 :         bool isStsHost = false;
     202            5047 :         rv = stss->IsStsURI(mURI, &isStsHost);
     203                 : 
     204                 :         // if STS fails, there's no reason to cancel the load, but it's
     205                 :         // worrisome.
     206            5047 :         NS_ASSERTION(NS_SUCCEEDED(rv),
     207                 :                      "Something is wrong with STS: IsStsURI failed.");
     208                 : 
     209            5047 :         if (NS_SUCCEEDED(rv) && isStsHost) {
     210               0 :             LOG(("nsHttpChannel::Connect() STS permissions found\n"));
     211               0 :             return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
     212                 :         }
     213                 : 
     214                 :         // Check for a previous SPDY Alternate-Protocol directive
     215            5047 :         if (gHttpHandler->IsSpdyEnabled() && mAllowSpdy) {
     216           10094 :             nsCAutoString hostPort;
     217                 : 
     218           10094 :             if (NS_SUCCEEDED(mURI->GetHostPort(hostPort)) &&
     219            5047 :                 gHttpHandler->ConnMgr()->GetSpdyAlternateProtocol(hostPort)) {
     220               0 :                 LOG(("nsHttpChannel::Connect() Alternate-Protocol found\n"));
     221                 :                 return AsyncCall(
     222               0 :                     &nsHttpChannel::HandleAsyncRedirectChannelToHttps);
     223                 :             }
     224                 :         }
     225                 :     }
     226                 : 
     227                 :     // ensure that we are using a valid hostname
     228            5055 :     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
     229               0 :         return NS_ERROR_UNKNOWN_HOST;
     230                 : 
     231                 :     // true when called from AsyncOpen
     232            5055 :     if (firstTime) {
     233                 :         // are we offline?
     234            3482 :         bool offline = gIOService->IsOffline();
     235            3482 :         if (offline)
     236               3 :             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
     237            3479 :         else if (PL_strcmp(mConnectionInfo->ProxyType(), "unknown") == 0)
     238              10 :             return ResolveProxy();  // Lazily resolve proxy info
     239                 : 
     240                 :         // Don't allow resuming when cache must be used
     241            3472 :         if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
     242               0 :             LOG(("Resuming from cache is not supported yet"));
     243               0 :             return NS_ERROR_DOCUMENT_NOT_CACHED;
     244                 :         }
     245                 : 
     246                 :         // open a cache entry for this channel...
     247            3472 :         rv = OpenCacheEntry();
     248                 : 
     249            3472 :         if (NS_FAILED(rv)) {
     250            1007 :             LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
     251                 :             // if this channel is only allowed to pull from the cache, then
     252                 :             // we must fail if we were unable to open a cache entry.
     253            1007 :             if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
     254                 :                 // If we have a fallback URI (and we're not already
     255                 :                 // falling back), process the fallback asynchronously.
     256               0 :                 if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
     257               0 :                     return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
     258                 :                 }
     259               0 :                 return NS_ERROR_DOCUMENT_NOT_CACHED;
     260                 :             }
     261                 :             // otherwise, let's just proceed without using the cache.
     262                 :         }
     263                 : 
     264                 :         // if cacheForOfflineUse has been set, open up an offline cache
     265                 :         // entry to update
     266            3472 :         if (mCacheForOfflineUse) {
     267              24 :             rv = OpenOfflineCacheEntryForWriting();
     268              24 :             if (NS_FAILED(rv)) return rv;
     269                 :         }
     270                 : 
     271            3472 :         if (NS_SUCCEEDED(rv) && mAsyncCacheOpen)
     272            1853 :             return NS_OK;
     273                 :     }
     274                 : 
     275                 :     // we may or may not have a cache entry at this point
     276            3192 :     if (mCacheEntry) {
     277                 :         // inspect the cache entry to determine whether or not we need to go
     278                 :         // out to net to validate it.  this call sets mCachedContentIsValid
     279                 :         // and may set request headers as required for cache validation.
     280            1616 :         rv = CheckCache();
     281            1616 :         if (NS_FAILED(rv))
     282               0 :             NS_WARNING("cache check failed");
     283                 : 
     284                 :         // read straight from the cache if possible...
     285            1616 :         if (mCachedContentIsValid) {
     286             220 :             nsRunnableMethod<nsHttpChannel> *event = nsnull;
     287             220 :             if (!mCachedContentIsPartial) {
     288             220 :                 AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
     289                 :             }
     290             220 :             rv = ReadFromCache();
     291             220 :             if (NS_FAILED(rv) && event) {
     292               0 :                 event->Revoke();
     293                 :             }
     294                 :             mozilla::Telemetry::Accumulate(
     295             220 :                     mozilla::Telemetry::HTTP_CACHE_DISPOSITION, kCacheHit);
     296                 : 
     297             220 :             char* cacheDeviceID = nsnull;
     298             220 :             mCacheEntry->GetDeviceID(&cacheDeviceID);
     299             220 :             if (cacheDeviceID) {
     300             220 :                 if (!strcmp(cacheDeviceID, kDiskDeviceID))
     301                 :                     mozilla::Telemetry::Accumulate(
     302                 :                             mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION,
     303             167 :                             kCacheHit);
     304              53 :                 else if (!strcmp(cacheDeviceID, kMemoryDeviceID))
     305                 :                     mozilla::Telemetry::Accumulate(
     306                 :                             mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION,
     307              49 :                             kCacheHit);
     308               4 :                 else if (!strcmp(cacheDeviceID, kOfflineDeviceID))
     309                 :                     mozilla::Telemetry::Accumulate(
     310                 :                             mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION,
     311               4 :                             kCacheHit);
     312                 :             }
     313             220 :             return rv;
     314                 :         }
     315            1396 :         else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
     316                 :             // the cache contains the requested resource, but it must be 
     317                 :             // validated before we can reuse it.  since we are not allowed
     318                 :             // to hit the net, there's nothing more to do.  the document
     319                 :             // is effectively not in the cache.
     320               5 :             return NS_ERROR_DOCUMENT_NOT_CACHED;
     321                 :         }
     322                 :     }
     323            1576 :     else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
     324                 :         // If we have a fallback URI (and we're not already
     325                 :         // falling back), process the fallback asynchronously.
     326               1 :         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
     327               0 :             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
     328                 :         }
     329               1 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
     330                 :     }
     331                 : 
     332            2966 :     if (mLoadFlags & LOAD_NO_NETWORK_IO) {
     333               0 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
     334                 :     }
     335                 : 
     336                 :     // hit the net...
     337            2966 :     rv = SetupTransaction();
     338            2966 :     if (NS_FAILED(rv)) return rv;
     339                 : 
     340            2966 :     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
     341            2966 :     if (NS_FAILED(rv)) return rv;
     342                 : 
     343            2954 :     rv = mTransactionPump->AsyncRead(this, nsnull);
     344            2954 :     if (NS_FAILED(rv)) return rv;
     345                 : 
     346            2954 :     PRUint32 suspendCount = mSuspendCount;
     347            5908 :     while (suspendCount--)
     348               0 :         mTransactionPump->Suspend();
     349                 : 
     350            2954 :     return NS_OK;
     351                 : }
     352                 : 
     353                 : void
     354              42 : nsHttpChannel::DoNotifyListenerCleanup()
     355                 : {
     356                 :     // We don't need this info anymore
     357              42 :     CleanRedirectCacheChainIfNecessary();
     358              42 : }
     359                 : 
     360                 : void
     361              11 : nsHttpChannel::HandleAsyncRedirect()
     362                 : {
     363              11 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
     364                 :     
     365              11 :     if (mSuspendCount) {
     366               0 :         LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
     367               0 :         mCallOnResume = &nsHttpChannel::HandleAsyncRedirect;
     368               0 :         return;
     369                 :     }
     370                 : 
     371              11 :     nsresult rv = NS_OK;
     372                 : 
     373              11 :     LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this));
     374                 : 
     375                 :     // since this event is handled asynchronously, it is possible that this
     376                 :     // channel could have been canceled, in which case there would be no point
     377                 :     // in processing the redirect.
     378              11 :     if (NS_SUCCEEDED(mStatus)) {
     379              11 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
     380              11 :         rv = AsyncProcessRedirection(mResponseHead->Status());
     381              11 :         if (NS_FAILED(rv)) {
     382               1 :             PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
     383               1 :             ContinueHandleAsyncRedirect(rv);
     384                 :         }
     385                 :     }
     386                 :     else {
     387               0 :         ContinueHandleAsyncRedirect(NS_OK);
     388                 :     }
     389                 : }
     390                 : 
     391                 : nsresult
     392              11 : nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv)
     393                 : {
     394              11 :     if (NS_FAILED(rv)) {
     395                 :         // If AsyncProcessRedirection fails, then we have to send out the
     396                 :         // OnStart/OnStop notifications.
     397               3 :         LOG(("ContinueHandleAsyncRedirect got failure result [rv=%x]\n", rv));
     398               3 :         mStatus = rv;
     399               3 :         DoNotifyListener();
     400                 :     }
     401                 : 
     402                 :     // close the cache entry.  Blow it away if we couldn't process the redirect
     403                 :     // for some reason (the cache entry might be corrupt).
     404              11 :     if (mCacheEntry) {
     405              11 :         if (NS_FAILED(rv))
     406               3 :             mCacheEntry->Doom();
     407              11 :         CloseCacheEntry(false);
     408                 :     }
     409                 : 
     410              11 :     mIsPending = false;
     411                 : 
     412              11 :     if (mLoadGroup)
     413               0 :         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
     414                 : 
     415              11 :     return NS_OK;
     416                 : }
     417                 : 
     418                 : void
     419               0 : nsHttpChannel::HandleAsyncNotModified()
     420                 : {
     421               0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
     422                 :     
     423               0 :     if (mSuspendCount) {
     424               0 :         LOG(("Waiting until resume to do async not-modified [this=%p]\n",
     425                 :              this));
     426               0 :         mCallOnResume = &nsHttpChannel::HandleAsyncNotModified;
     427               0 :         return;
     428                 :     }
     429                 :     
     430               0 :     LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
     431                 : 
     432               0 :     DoNotifyListener();
     433                 : 
     434               0 :     CloseCacheEntry(true);
     435                 : 
     436               0 :     mIsPending = false;
     437                 : 
     438               0 :     if (mLoadGroup)
     439               0 :         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
     440                 : }
     441                 : 
     442                 : void
     443               2 : nsHttpChannel::HandleAsyncFallback()
     444                 : {
     445               2 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
     446                 : 
     447               2 :     if (mSuspendCount) {
     448               0 :         LOG(("Waiting until resume to do async fallback [this=%p]\n", this));
     449               0 :         mCallOnResume = &nsHttpChannel::HandleAsyncFallback;
     450               0 :         return;
     451                 :     }
     452                 : 
     453               2 :     nsresult rv = NS_OK;
     454                 : 
     455               2 :     LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n", this));
     456                 : 
     457                 :     // since this event is handled asynchronously, it is possible that this
     458                 :     // channel could have been canceled, in which case there would be no point
     459                 :     // in processing the fallback.
     460               2 :     if (!mCanceled) {
     461               2 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
     462                 :         bool waitingForRedirectCallback;
     463               2 :         rv = ProcessFallback(&waitingForRedirectCallback);
     464               2 :         if (waitingForRedirectCallback)
     465               2 :             return;
     466               0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
     467                 :     }
     468                 : 
     469               0 :     ContinueHandleAsyncFallback(rv);
     470                 : }
     471                 : 
     472                 : nsresult
     473               2 : nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv)
     474                 : {
     475               2 :     if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) {
     476                 :         // If ProcessFallback fails, then we have to send out the
     477                 :         // OnStart/OnStop notifications.
     478               1 :         LOG(("ProcessFallback failed [rv=%x, %d]\n", rv, mFallingBack));
     479               1 :         mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED;
     480               1 :         DoNotifyListener();
     481                 :     }
     482                 : 
     483               2 :     mIsPending = false;
     484                 : 
     485               2 :     if (mLoadGroup)
     486               0 :         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
     487                 : 
     488               2 :     return rv;
     489                 : }
     490                 : 
     491                 : nsresult
     492            2989 : nsHttpChannel::SetupTransaction()
     493                 : {
     494            2989 :     LOG(("nsHttpChannel::SetupTransaction [this=%p]\n", this));
     495                 : 
     496            2989 :     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
     497                 : 
     498                 :     nsresult rv;
     499                 : 
     500            2989 :     if (mCaps & NS_HTTP_ALLOW_PIPELINING) {
     501                 :         //
     502                 :         // disable pipelining if:
     503                 :         //   (1) pipelining has been explicitly disabled
     504                 :         //   (2) request corresponds to a top-level document load (link click)
     505                 :         //   (3) request method is non-idempotent
     506                 :         //
     507                 :         // XXX does the toplevel document check really belong here?  or, should
     508                 :         //     we push it out entirely to necko consumers?
     509                 :         //
     510               0 :         if (!mAllowPipelining || (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) ||
     511               0 :             !(mRequestHead.Method() == nsHttp::Get ||
     512               0 :               mRequestHead.Method() == nsHttp::Head ||
     513               0 :               mRequestHead.Method() == nsHttp::Propfind ||
     514               0 :               mRequestHead.Method() == nsHttp::Proppatch)) {
     515               0 :             LOG(("  pipelining disallowed\n"));
     516               0 :             mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
     517                 :         }
     518                 :     }
     519                 : 
     520            2989 :     if (!mAllowSpdy)
     521               0 :         mCaps |= NS_HTTP_DISALLOW_SPDY;
     522                 : 
     523                 :     // use the URI path if not proxying (transparent proxying such as SSL proxy
     524                 :     // does not count here). also, figure out what version we should be speaking.
     525            5978 :     nsCAutoString buf, path;
     526                 :     nsCString* requestURI;
     527            8959 :     if (mConnectionInfo->UsingSSL() ||
     528            2985 :         mConnectionInfo->ShouldForceConnectMethod() ||
     529            2985 :         !mConnectionInfo->UsingHttpProxy()) {
     530            2962 :         rv = mURI->GetPath(path);
     531            2962 :         if (NS_FAILED(rv)) return rv;
     532                 :         // path may contain UTF-8 characters, so ensure that they're escaped.
     533            2962 :         if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf))
     534               0 :             requestURI = &buf;
     535                 :         else
     536            2962 :             requestURI = &path;
     537            2962 :         mRequestHead.SetVersion(gHttpHandler->HttpVersion());
     538                 :     }
     539                 :     else {
     540              27 :         rv = mURI->GetUserPass(buf);
     541              27 :         if (NS_FAILED(rv)) return rv;
     542              27 :         if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
     543               0 :                                 strncmp(mSpec.get(), "https:", 6) == 0)) {
     544               0 :             nsCOMPtr<nsIURI> tempURI;
     545               0 :             rv = mURI->Clone(getter_AddRefs(tempURI));
     546               0 :             if (NS_FAILED(rv)) return rv;
     547               0 :             rv = tempURI->SetUserPass(EmptyCString());
     548               0 :             if (NS_FAILED(rv)) return rv;
     549               0 :             rv = tempURI->GetAsciiSpec(path);
     550               0 :             if (NS_FAILED(rv)) return rv;
     551               0 :             requestURI = &path;
     552                 :         }
     553                 :         else
     554              27 :             requestURI = &mSpec;
     555              27 :         mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
     556                 :     }
     557                 : 
     558                 :     // trim off the #ref portion if any...
     559            2989 :     PRInt32 ref = requestURI->FindChar('#');
     560            2989 :     if (ref != kNotFound)
     561               0 :         requestURI->SetLength(ref);
     562                 : 
     563            2989 :     mRequestHead.SetRequestURI(*requestURI);
     564                 : 
     565                 :     // set the request time for cache expiration calculations
     566            2989 :     mRequestTime = NowInSeconds();
     567            2989 :     mRequestTimeInitialized = true;
     568                 : 
     569                 :     // if doing a reload, force end-to-end
     570            2989 :     if (mLoadFlags & LOAD_BYPASS_CACHE) {
     571                 :         // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
     572                 :         // no proxy is configured since we might be talking with a transparent
     573                 :         // proxy, i.e. one that operates at the network level.  See bug #14772.
     574            1920 :         mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache"), true);
     575                 :         // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
     576                 :         // no-cache'
     577            1920 :         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
     578            1920 :             mRequestHead.SetHeader(nsHttp::Cache_Control, NS_LITERAL_CSTRING("no-cache"), true);
     579                 :     }
     580            1069 :     else if ((mLoadFlags & VALIDATE_ALWAYS) && (mCacheAccess & nsICache::ACCESS_READ)) {
     581                 :         // We need to send 'Cache-Control: max-age=0' to force each cache along
     582                 :         // the path to the origin server to revalidate its own entry, if any,
     583                 :         // with the next cache or server.  See bug #84847.
     584                 :         //
     585                 :         // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
     586               3 :         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
     587               3 :             mRequestHead.SetHeader(nsHttp::Cache_Control, NS_LITERAL_CSTRING("max-age=0"), true);
     588                 :         else
     589               0 :             mRequestHead.SetHeader(nsHttp::Pragma, NS_LITERAL_CSTRING("no-cache"), true);
     590                 :     }
     591                 : 
     592            2989 :     if (mResuming) {
     593                 :         char byteRange[32];
     594              28 :         PR_snprintf(byteRange, sizeof(byteRange), "bytes=%llu-", mStartPos);
     595              28 :         mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
     596                 : 
     597              28 :         if (!mEntityID.IsEmpty()) {
     598                 :             // Also, we want an error if this resource changed in the meantime
     599                 :             // Format of the entity id is: escaped_etag/size/lastmod
     600              28 :             nsCString::const_iterator start, end, slash;
     601              28 :             mEntityID.BeginReading(start);
     602              28 :             mEntityID.EndReading(end);
     603              28 :             mEntityID.BeginReading(slash);
     604                 : 
     605              28 :             if (FindCharInReadable('/', slash, end)) {
     606              56 :                 nsCAutoString ifMatch;
     607                 :                 mRequestHead.SetHeader(nsHttp::If_Match,
     608              28 :                         NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
     609                 : 
     610              28 :                 ++slash; // Incrementing, so that searching for '/' won't find
     611                 :                          // the same slash again
     612                 :             }
     613                 : 
     614              28 :             if (FindCharInReadable('/', slash, end)) {
     615                 :                 mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
     616              28 :                         Substring(++slash, end));
     617                 :             }
     618                 :         }
     619                 :     }
     620                 : 
     621                 :     // create wrapper for this channel's notification callbacks
     622            5978 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     623                 :     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
     624            2989 :                                            getter_AddRefs(callbacks));
     625            2989 :     if (!callbacks)
     626               0 :         return NS_ERROR_OUT_OF_MEMORY;
     627                 : 
     628                 :     // create the transaction object
     629            2989 :     mTransaction = new nsHttpTransaction();
     630            2989 :     if (!mTransaction)
     631               0 :         return NS_ERROR_OUT_OF_MEMORY;
     632                 : 
     633                 :     // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
     634            2989 :     if (mLoadFlags & LOAD_ANONYMOUS)
     635               1 :         mCaps |= NS_HTTP_LOAD_ANONYMOUS;
     636                 : 
     637            2989 :     if (mTimingEnabled)
     638               2 :         mCaps |= NS_HTTP_TIMING_ENABLED;
     639                 : 
     640            2989 :     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
     641                 : 
     642            2989 :     if (mUpgradeProtocolCallback) {
     643               0 :         mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
     644                 :         mRequestHead.SetHeader(nsHttp::Connection,
     645               0 :                                nsDependentCString(nsHttp::Upgrade.get()),
     646               0 :                                true);
     647               0 :         mCaps |=  NS_HTTP_STICKY_CONNECTION;
     648               0 :         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
     649               0 :         mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
     650               0 :         mCaps |=  NS_HTTP_DISALLOW_SPDY;
     651                 :     }
     652                 : 
     653            5978 :     nsCOMPtr<nsIAsyncInputStream> responseStream;
     654                 :     rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
     655                 :                             mUploadStream, mUploadStreamHasHeaders,
     656            2989 :                             NS_GetCurrentThread(), callbacks, this,
     657            5978 :                             getter_AddRefs(responseStream));
     658            2989 :     if (NS_FAILED(rv)) {
     659               0 :         mTransaction = nsnull;
     660               0 :         return rv;
     661                 :     }
     662                 : 
     663            2989 :     rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
     664            5978 :                                    responseStream);
     665            2989 :     return rv;
     666                 : }
     667                 : 
     668                 : // NOTE: This function duplicates code from nsBaseChannel. This will go away
     669                 : // once HTTP uses nsBaseChannel (part of bug 312760)
     670                 : static void
     671               1 : CallTypeSniffers(void *aClosure, const PRUint8 *aData, PRUint32 aCount)
     672                 : {
     673               1 :   nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
     674                 : 
     675                 :   const nsCOMArray<nsIContentSniffer>& sniffers =
     676               1 :     gIOService->GetContentSniffers();
     677               1 :   PRUint32 length = sniffers.Count();
     678               3 :   for (PRUint32 i = 0; i < length; ++i) {
     679               6 :     nsCAutoString newType;
     680                 :     nsresult rv =
     681               3 :       sniffers[i]->GetMIMETypeFromContent(chan, aData, aCount, newType);
     682               3 :     if (NS_SUCCEEDED(rv) && !newType.IsEmpty()) {
     683               1 :       chan->SetContentType(newType);
     684                 :       break;
     685                 :     }
     686                 :   }
     687               1 : }
     688                 : 
     689                 : nsresult
     690            3037 : nsHttpChannel::CallOnStartRequest()
     691                 : {
     692            3037 :     mTracingEnabled = false;
     693                 : 
     694            3037 :     if (mResponseHead && mResponseHead->ContentType().IsEmpty()) {
     695             948 :         NS_ASSERTION(mConnectionInfo, "Should have connection info here");
     696             948 :         if (!mContentTypeHint.IsEmpty())
     697              57 :             mResponseHead->SetContentType(mContentTypeHint);
     698             892 :         else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 &&
     699               1 :                  mConnectionInfo->Port() != mConnectionInfo->DefaultPort())
     700               1 :             mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
     701                 :         else {
     702                 :             // Uh-oh.  We had better find out what type we are!
     703                 : 
     704                 :             // XXX This does not work with content-encodings...  but
     705                 :             // neither does applying the conversion from the URILoader
     706                 : 
     707            1780 :             nsCOMPtr<nsIStreamConverterService> serv;
     708                 :             nsresult rv = gHttpHandler->
     709             890 :                 GetStreamConverterService(getter_AddRefs(serv));
     710                 :             // If we failed, we just fall through to the "normal" case
     711             890 :             if (NS_SUCCEEDED(rv)) {
     712            1780 :                 nsCOMPtr<nsIStreamListener> converter;
     713             890 :                 rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
     714                 :                                             "*/*",
     715                 :                                             mListener,
     716                 :                                             mListenerContext,
     717             890 :                                             getter_AddRefs(converter));
     718             890 :                 if (NS_SUCCEEDED(rv)) {
     719             890 :                     mListener = converter;
     720                 :                 }
     721                 :             }
     722                 :         }
     723                 :     }
     724                 : 
     725            3037 :     if (mResponseHead && mResponseHead->ContentCharset().IsEmpty())
     726            2877 :         mResponseHead->SetContentCharset(mContentCharsetHint);
     727                 : 
     728            3037 :     if (mResponseHead) {
     729                 :         SetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH,
     730            2881 :                            mResponseHead->ContentLength());
     731                 :         // If we have a cache entry, set its predicted size to ContentLength to
     732                 :         // avoid caching an entry that will exceed the max size limit.
     733            2881 :         if (mCacheEntry) {
     734                 :             nsresult rv;
     735            1363 :             PRInt64 predictedDataSize = -1; // -1 in case GetAsInt64 fails.
     736                 :             GetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH, 
     737            1363 :                                &predictedDataSize);
     738            1363 :             rv = mCacheEntry->SetPredictedDataSize(predictedDataSize);
     739            1363 :             if (NS_FAILED(rv)) return rv;
     740                 :         }
     741                 :     }
     742                 :     // Allow consumers to override our content type
     743            3038 :     if ((mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) &&
     744               1 :         gIOService->GetContentSniffers().Count() != 0) {
     745                 :         // NOTE: We can have both a txn pump and a cache pump when the cache
     746                 :         // content is partial. In that case, we need to read from the cache,
     747                 :         // because that's the one that has the initial contents. If that fails
     748                 :         // then give the transaction pump a shot.
     749                 : 
     750               1 :         nsIChannel* thisChannel = static_cast<nsIChannel*>(this);
     751                 : 
     752               1 :         bool typeSniffersCalled = false;
     753               1 :         if (mCachePump) {
     754                 :           typeSniffersCalled =
     755               0 :             NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel));
     756                 :         }
     757                 :         
     758               1 :         if (!typeSniffersCalled && mTransactionPump) {
     759               1 :           mTransactionPump->PeekStream(CallTypeSniffers, thisChannel);
     760                 :         }
     761                 :     }
     762                 : 
     763            3037 :     LOG(("  calling mListener->OnStartRequest\n"));
     764            3037 :     nsresult rv = mListener->OnStartRequest(this, mListenerContext);
     765            3037 :     if (NS_FAILED(rv)) return rv;
     766                 : 
     767                 :     // install stream converter if required
     768            3021 :     rv = ApplyContentConversions();
     769            3021 :     if (NS_FAILED(rv)) return rv;
     770                 : 
     771                 :     // if this channel is for a download, close off access to the cache.
     772            3021 :     if (mCacheEntry && mChannelIsForDownload) {
     773               6 :         mCacheEntry->Doom();
     774               6 :         CloseCacheEntry(false);
     775                 :     }
     776                 : 
     777            3021 :     if (!mCanceled) {
     778                 :         // create offline cache entry if offline caching was requested
     779            3003 :         if (mCacheForOfflineUse) {
     780                 :             bool shouldCacheForOfflineUse;
     781              24 :             rv = ShouldUpdateOfflineCacheEntry(&shouldCacheForOfflineUse);
     782              24 :             if (NS_FAILED(rv)) return rv;
     783                 :             
     784              24 :             if (shouldCacheForOfflineUse) {
     785              24 :                 LOG(("writing to the offline cache"));
     786              24 :                 rv = InitOfflineCacheEntry();
     787              24 :                 if (NS_FAILED(rv)) return rv;
     788                 :                 
     789              24 :                 if (mOfflineCacheEntry) {
     790              24 :                   rv = InstallOfflineCacheListener();
     791              24 :                   if (NS_FAILED(rv)) return rv;
     792                 :                 }
     793                 :             } else {
     794               0 :                 LOG(("offline cache is up to date, not updating"));
     795               0 :                 CloseOfflineCacheEntry();
     796                 :             }
     797                 :         }
     798                 :     }
     799                 : 
     800            3021 :     return NS_OK;
     801                 : }
     802                 : 
     803                 : nsresult
     804               0 : nsHttpChannel::ProcessFailedSSLConnect(PRUint32 httpStatus)
     805                 : {
     806                 :     // Failure to set up SSL proxy tunnel means one of the following:
     807                 :     // 1) Proxy wants authorization, or forbids.
     808                 :     // 2) DNS at proxy couldn't resolve target URL.
     809                 :     // 3) Proxy connection to target failed or timed out.
     810                 :     // 4) Eve noticed our proxy CONNECT, and is replying with malicious HTML.
     811                 :     // 
     812                 :     // Our current architecture will parse response content with the
     813                 :     // permission of the target URL!  Given #4, we must avoid rendering the
     814                 :     // body of the reply, and instead give the user a (hopefully helpful) 
     815                 :     // boilerplate error page, based on just the HTTP status of the reply.
     816                 : 
     817               0 :     NS_ABORT_IF_FALSE(mConnectionInfo->UsingSSL(),
     818                 :                       "SSL connect failed but not using SSL?");
     819                 :     nsresult rv;
     820               0 :     switch (httpStatus) 
     821                 :     {
     822                 :     case 300: case 301: case 302: case 303: case 307:
     823                 :         // Bad redirect: not top-level, or it's a POST, bad/missing Location,
     824                 :         // or ProcessRedirect() failed for some other reason.  Legal
     825                 :         // redirects that fail because site not available, etc., are handled
     826                 :         // elsewhere, in the regular codepath.
     827               0 :         rv = NS_ERROR_CONNECTION_REFUSED;
     828               0 :         break;
     829                 :     case 403: // HTTP/1.1: "Forbidden"
     830                 :     case 407: // ProcessAuthentication() failed
     831                 :     case 501: // HTTP/1.1: "Not Implemented"
     832                 :         // user sees boilerplate Mozilla "Proxy Refused Connection" page.
     833               0 :         rv = NS_ERROR_PROXY_CONNECTION_REFUSED; 
     834               0 :         break;
     835                 :     // Squid sends 404 if DNS fails (regular 404 from target is tunneled)
     836                 :     case 404: // HTTP/1.1: "Not Found"
     837                 :     // RFC 2616: "some deployed proxies are known to return 400 or 500 when
     838                 :     // DNS lookups time out."  (Squid uses 500 if it runs out of sockets: so
     839                 :     // we have a conflict here).
     840                 :     case 400: // HTTP/1.1 "Bad Request"
     841                 :     case 500: // HTTP/1.1: "Internal Server Error"
     842                 :         /* User sees: "Address Not Found: Firefox can't find the server at
     843                 :          * www.foo.com."
     844                 :          */
     845               0 :         rv = NS_ERROR_UNKNOWN_HOST; 
     846               0 :         break;
     847                 :     case 502: // HTTP/1.1: "Bad Gateway" (invalid resp from target server)
     848                 :     // Squid returns 503 if target request fails for anything but DNS.
     849                 :     case 503: // HTTP/1.1: "Service Unavailable"
     850                 :         /* User sees: "Failed to Connect:
     851                 :          *  Firefox can't establish a connection to the server at
     852                 :          *  www.foo.com.  Though the site seems valid, the browser
     853                 :          *  was unable to establish a connection."
     854                 :          */
     855               0 :         rv = NS_ERROR_CONNECTION_REFUSED;
     856               0 :         break;
     857                 :     // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to
     858                 :     // do here: picking target timeout, as DNS covered by 400/404/500
     859                 :     case 504: // HTTP/1.1: "Gateway Timeout" 
     860                 :         // user sees: "Network Timeout: The server at www.foo.com
     861                 :         //              is taking too long to respond."
     862               0 :         rv = NS_ERROR_NET_TIMEOUT;
     863               0 :         break;
     864                 :     // Confused proxy server or malicious response
     865                 :     default:
     866               0 :         rv = NS_ERROR_PROXY_CONNECTION_REFUSED; 
     867               0 :         break;
     868                 :     }
     869               0 :     LOG(("Cancelling failed SSL proxy connection [this=%p httpStatus=%u]\n",
     870                 :          this, httpStatus)); 
     871               0 :     Cancel(rv);
     872               0 :     CallOnStartRequest();
     873               0 :     return rv;
     874                 : }
     875                 : 
     876                 : bool
     877               0 : nsHttpChannel::ShouldSSLProxyResponseContinue(PRUint32 httpStatus)
     878                 : {
     879                 :     // When SSL connect has failed, allow proxy reply to continue only if it's
     880                 :     // a 407 (proxy authentication required) response
     881               0 :     return (httpStatus == 407);
     882                 : }
     883                 : 
     884                 : /**
     885                 :  * Decide whether or not to remember Strict-Transport-Security, and whether
     886                 :  * or not to enforce channel integrity.
     887                 :  *
     888                 :  * @return NS_ERROR_FAILURE if there's security information missing even though
     889                 :  *             it's an HTTPS connection.
     890                 :  */
     891                 : nsresult
     892            2819 : nsHttpChannel::ProcessSTSHeader()
     893                 : {
     894                 :     nsresult rv;
     895            2819 :     bool isHttps = false;
     896            2819 :     rv = mURI->SchemeIs("https", &isHttps);
     897            2819 :     NS_ENSURE_SUCCESS(rv, rv);
     898                 : 
     899                 :     // If this channel is not loading securely, STS doesn't do anything.
     900                 :     // The upgrade to HTTPS takes place earlier in the channel load process.
     901            2819 :     if (!isHttps)
     902            2815 :         return NS_OK;
     903                 : 
     904               8 :     nsCAutoString asciiHost;
     905               4 :     rv = mURI->GetAsciiHost(asciiHost);
     906               4 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     907                 : 
     908                 :     // If the channel is not a hostname, but rather an IP, STS doesn't do
     909                 :     // anything.
     910                 :     PRNetAddr hostAddr;
     911               4 :     if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr))
     912               0 :         return NS_OK;
     913                 : 
     914               4 :     nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
     915               4 :     NS_ENSURE_TRUE(stss, NS_ERROR_OUT_OF_MEMORY);
     916                 : 
     917                 :     // mSecurityInfo may not always be present, and if it's not then it is okay
     918                 :     // to just disregard any STS headers since we know nothing about the
     919                 :     // security of the connection.
     920               4 :     NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
     921                 : 
     922                 :     // Check the trustworthiness of the channel (are there any cert errors?)
     923                 :     // If there are certificate errors, we still load the data, we just ignore
     924                 :     // any STS headers that are present.
     925               4 :     bool tlsIsBroken = false;
     926               4 :     rv = stss->ShouldIgnoreStsHeader(mSecurityInfo, &tlsIsBroken);
     927               4 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     928                 : 
     929                 :     // If this was already an STS host, the connection should have been aborted
     930                 :     // by the bad cert handler in the case of cert errors.  If it didn't abort the connection,
     931                 :     // there's probably something funny going on.
     932                 :     // If this wasn't an STS host, errors are allowed, but no more STS processing
     933                 :     // will happen during the session.
     934                 :     bool wasAlreadySTSHost;
     935               4 :     rv = stss->IsStsURI(mURI, &wasAlreadySTSHost);
     936                 :     // Failure here means STS is broken.  Don't prevent the load, but this
     937                 :     // shouldn't fail.
     938               4 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     939               4 :     NS_ASSERTION(!(wasAlreadySTSHost && tlsIsBroken),
     940                 :                  "connection should have been aborted by nss-bad-cert-handler");
     941                 : 
     942                 :     // Any STS header is ignored if the channel is not trusted due to
     943                 :     // certificate errors (STS Spec 7.1) -- there is nothing else to do, and
     944                 :     // the load may progress.
     945               4 :     if (tlsIsBroken) {
     946               0 :         LOG(("STS: Transport layer is not trustworthy, ignoring "
     947                 :              "STS headers and continuing load\n"));
     948               0 :         return NS_OK;
     949                 :     }
     950                 : 
     951                 :     // If there's a STS header, process it (STS Spec 7.1).  At this point in
     952                 :     // processing, the channel is trusted, so the header should not be ignored.
     953               4 :     const nsHttpAtom atom = nsHttp::ResolveAtom("Strict-Transport-Security");
     954               8 :     nsCAutoString stsHeader;
     955               4 :     rv = mResponseHead->GetHeader(atom, stsHeader);
     956               4 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
     957               0 :         LOG(("STS: No STS header, continuing load.\n"));
     958               0 :         return NS_OK;
     959                 :     }
     960                 :     // All other failures are fatal.
     961               4 :     NS_ENSURE_SUCCESS(rv, rv);
     962                 : 
     963               4 :     rv = stss->ProcessStsHeader(mURI, stsHeader.get());
     964               4 :     if (NS_FAILED(rv)) {
     965               0 :         LOG(("STS: Failed to parse STS header, continuing load.\n"));
     966               0 :         return NS_OK;
     967                 :     }
     968                 : 
     969               4 :     return NS_OK;
     970                 : }
     971                 : 
     972                 : nsresult
     973            2819 : nsHttpChannel::ProcessResponse()
     974                 : {
     975                 :     nsresult rv;
     976            2819 :     PRUint32 httpStatus = mResponseHead->Status();
     977                 : 
     978            2819 :     LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n",
     979                 :         this, httpStatus));
     980                 : 
     981            2819 :     if (mTransaction->SSLConnectFailed()) {
     982               0 :         if (!ShouldSSLProxyResponseContinue(httpStatus))
     983               0 :             return ProcessFailedSSLConnect(httpStatus);
     984                 :         // If SSL proxy response needs to complete, wait to process connection
     985                 :         // for Strict-Transport-Security.
     986                 :     } else {
     987                 :         // Given a successful connection, process any STS data that's relevant.
     988            2819 :         rv = ProcessSTSHeader();
     989            2819 :         NS_ASSERTION(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
     990                 :     }
     991                 : 
     992                 :     // notify "http-on-examine-response" observers
     993            2819 :     gHttpHandler->OnExamineResponse(this);
     994                 : 
     995            2819 :     SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
     996                 : 
     997                 :     // handle unused username and password in url (see bug 232567)
     998            2819 :     if (httpStatus != 401 && httpStatus != 407) {
     999            2760 :         if (!mAuthRetryPending)
    1000            2760 :             mAuthProvider->CheckForSuperfluousAuth();
    1001            2760 :         if (mCanceled)
    1002               0 :             return CallOnStartRequest();
    1003                 : 
    1004                 :         // reset the authentication's current continuation state because our
    1005                 :         // last authentication attempt has been completed successfully
    1006            2760 :         mAuthProvider->Disconnect(NS_ERROR_ABORT);
    1007            2760 :         mAuthProvider = nsnull;
    1008            2760 :         LOG(("  continuation state has been reset"));
    1009                 :     }
    1010                 : 
    1011            2819 :     bool successfulReval = false;
    1012                 : 
    1013                 :     // handle different server response categories.  Note that we handle
    1014                 :     // caching or not caching of error pages in
    1015                 :     // nsHttpResponseHead::MustValidate; if you change this switch, update that
    1016                 :     // one
    1017            2819 :     switch (httpStatus) {
    1018                 :     case 200:
    1019                 :     case 203:
    1020                 :         // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
    1021                 :         // So if a server does that and sends 200 instead of 206 that we
    1022                 :         // expect, notify our caller.
    1023                 :         // However, if we wanted to start from the beginning, let it go through
    1024            1969 :         if (mResuming && mStartPos != 0) {
    1025               3 :             LOG(("Server ignored our Range header, cancelling [this=%p]\n", this));
    1026               3 :             Cancel(NS_ERROR_NOT_RESUMABLE);
    1027               3 :             rv = CallOnStartRequest();
    1028               3 :             break;
    1029                 :         }
    1030                 :         // these can normally be cached
    1031            1966 :         rv = ProcessNormal();
    1032            1966 :         MaybeInvalidateCacheEntryForSubsequentGet();
    1033            1966 :         break;
    1034                 :     case 206:
    1035              19 :         if (mCachedContentIsPartial) // an internal byte range request...
    1036               2 :             rv = ProcessPartialContent();
    1037                 :         else
    1038              17 :             rv = ProcessNormal();
    1039              19 :         break;
    1040                 :     case 300:
    1041                 :     case 301:
    1042                 :     case 302:
    1043                 :     case 307:
    1044                 :     case 303:
    1045                 : #if 0
    1046                 :     case 305: // disabled as a security measure (see bug 187996).
    1047                 : #endif
    1048                 :         // don't store the response body for redirects
    1049             145 :         MaybeInvalidateCacheEntryForSubsequentGet();
    1050             145 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse);
    1051             145 :         rv = AsyncProcessRedirection(httpStatus);
    1052             145 :         if (NS_FAILED(rv)) {
    1053              20 :             PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse);
    1054              20 :             LOG(("AsyncProcessRedirection failed [rv=%x]\n", rv));
    1055              20 :             rv = ContinueProcessResponse(rv);
    1056                 :         }
    1057             145 :         break;
    1058                 :     case 304:
    1059              25 :         rv = ProcessNotModified();
    1060              25 :         if (NS_FAILED(rv)) {
    1061              19 :             LOG(("ProcessNotModified failed [rv=%x]\n", rv));
    1062              19 :             rv = ProcessNormal();
    1063                 :         }
    1064                 :         else {
    1065               6 :             successfulReval = true;
    1066                 :         }
    1067              25 :         break;
    1068                 :     case 401:
    1069                 :     case 407:
    1070              59 :         rv = mAuthProvider->ProcessAuthentication(
    1071              59 :             httpStatus, mConnectionInfo->UsingSSL() &&
    1072             118 :                         mTransaction->SSLConnectFailed());
    1073              59 :         if (rv == NS_ERROR_IN_PROGRESS)  {
    1074                 :             // authentication prompt has been invoked and result
    1075                 :             // is expected asynchronously
    1076              18 :             mAuthRetryPending = true;
    1077                 :             // suspend the transaction pump to stop receiving the
    1078                 :             // unauthenticated content data. We will throw that data
    1079                 :             // away when user provides credentials or resume the pump
    1080                 :             // when user refuses to authenticate.
    1081              18 :             LOG(("Suspending the transaction, asynchronously prompting for credentials"));
    1082              18 :             mTransactionPump->Suspend();
    1083              18 :             rv = NS_OK;
    1084                 :         }
    1085              41 :         else if (NS_FAILED(rv)) {
    1086              33 :             LOG(("ProcessAuthentication failed [rv=%x]\n", rv));
    1087              33 :             if (mTransaction->SSLConnectFailed())
    1088               0 :                 return ProcessFailedSSLConnect(httpStatus);
    1089              33 :             if (!mAuthRetryPending)
    1090              33 :                 mAuthProvider->CheckForSuperfluousAuth();
    1091              33 :             rv = ProcessNormal();
    1092                 :         }
    1093                 :         else
    1094               8 :             mAuthRetryPending = true; // see DoAuthRetry
    1095              59 :         break;
    1096                 :     default:
    1097             602 :         rv = ProcessNormal();
    1098             602 :         MaybeInvalidateCacheEntryForSubsequentGet();
    1099             602 :         break;
    1100                 :     }
    1101                 : 
    1102                 :     int cacheDisposition;
    1103            2819 :     if (!mDidReval)
    1104            2468 :         cacheDisposition = kCacheMissed;
    1105             351 :     else if (successfulReval)
    1106               6 :         cacheDisposition = kCacheHitViaReval;
    1107                 :     else
    1108             345 :         cacheDisposition = kCacheMissedViaReval;
    1109                 : 
    1110                 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::HTTP_CACHE_DISPOSITION,
    1111            2819 :             cacheDisposition);
    1112            2819 :     if (mCacheEntry) {
    1113            1267 :         char* cacheDeviceID = nsnull;
    1114            1267 :         mCacheEntry->GetDeviceID(&cacheDeviceID);
    1115            1267 :         if (cacheDeviceID) {
    1116             367 :             if (!strcmp(cacheDeviceID, kDiskDeviceID))
    1117                 :                 mozilla::Telemetry::Accumulate(
    1118                 :                         mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION,
    1119               6 :                         cacheDisposition);
    1120             361 :             else if (!strcmp(cacheDeviceID, kMemoryDeviceID))
    1121                 :                 mozilla::Telemetry::Accumulate(
    1122                 :                         mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION,
    1123             361 :                         cacheDisposition);
    1124               0 :             else if (!strcmp(cacheDeviceID, kOfflineDeviceID))
    1125                 :                 mozilla::Telemetry::Accumulate(
    1126                 :                         mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION,
    1127               0 :                         cacheDisposition);
    1128                 :         }
    1129                 :     }
    1130                 : 
    1131            2819 :     return rv;
    1132                 : }
    1133                 : 
    1134                 : nsresult
    1135             145 : nsHttpChannel::ContinueProcessResponse(nsresult rv)
    1136                 : {
    1137             145 :     if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) {
    1138                 : 
    1139               3 :         bool isHTTP = false;
    1140               3 :         if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP)))
    1141               0 :             isHTTP = false;
    1142               3 :         if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP)))
    1143               0 :             isHTTP = false;
    1144                 :         
    1145               3 :         if (!isHTTP) {
    1146                 :             // This was a blocked attempt to redirect and subvert the system by
    1147                 :             // redirecting to another protocol (perhaps javascript:)
    1148                 :             // In that case we want to throw an error instead of displaying the
    1149                 :             // non-redirected response body.
    1150                 : 
    1151               2 :             LOG(("ContinueProcessResponse detected rejected Non-HTTP Redirection"));
    1152               2 :             return NS_ERROR_CORRUPTED_CONTENT;
    1153                 :         }
    1154                 :     }
    1155                 : 
    1156             143 :     if (NS_SUCCEEDED(rv)) {
    1157             119 :         InitCacheEntry();
    1158             119 :         CloseCacheEntry(false);
    1159                 : 
    1160             119 :         if (mCacheForOfflineUse) {
    1161                 :             // Store response in the offline cache
    1162               0 :             InitOfflineCacheEntry();
    1163               0 :             CloseOfflineCacheEntry();
    1164                 :         }
    1165             119 :         return NS_OK;
    1166                 :     }
    1167                 : 
    1168              24 :     LOG(("ContinueProcessResponse got failure result [rv=%x]\n", rv));
    1169              24 :     if (mTransaction->SSLConnectFailed()) {
    1170               0 :         return ProcessFailedSSLConnect(mRedirectType);
    1171                 :     }
    1172              24 :     return ProcessNormal();
    1173                 : }
    1174                 : 
    1175                 : nsresult
    1176            2661 : nsHttpChannel::ProcessNormal()
    1177                 : {
    1178                 :     nsresult rv;
    1179                 : 
    1180            2661 :     LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
    1181                 : 
    1182                 :     bool succeeded;
    1183            2661 :     rv = GetRequestSucceeded(&succeeded);
    1184            2661 :     if (NS_SUCCEEDED(rv) && !succeeded) {
    1185             676 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
    1186                 :         bool waitingForRedirectCallback;
    1187             676 :         (void)ProcessFallback(&waitingForRedirectCallback);
    1188             676 :         if (waitingForRedirectCallback) {
    1189                 :             // The transaction has been suspended by ProcessFallback.
    1190               3 :             return NS_OK;
    1191                 :         }
    1192             673 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
    1193                 :     }
    1194                 : 
    1195            2658 :     return ContinueProcessNormal(NS_OK);
    1196                 : }
    1197                 : 
    1198                 : nsresult
    1199            2661 : nsHttpChannel::ContinueProcessNormal(nsresult rv)
    1200                 : {
    1201            2661 :     if (NS_FAILED(rv)) {
    1202                 :         // Fill the failure status here, we have failed to fall back, thus we
    1203                 :         // have to report our status as failed.
    1204               2 :         mStatus = rv;
    1205               2 :         DoNotifyListener();
    1206               2 :         return rv;
    1207                 :     }
    1208                 : 
    1209            2659 :     if (mFallingBack) {
    1210                 :         // Do not continue with normal processing, fallback is in
    1211                 :         // progress now.
    1212               1 :         return NS_OK;
    1213                 :     }
    1214                 : 
    1215                 :     // if we're here, then any byte-range requests failed to result in a partial
    1216                 :     // response.  we must clear this flag to prevent BufferPartialContent from
    1217                 :     // being called inside our OnDataAvailable (see bug 136678).
    1218            2658 :     mCachedContentIsPartial = false;
    1219                 : 
    1220            2658 :     ClearBogusContentEncodingIfNeeded();
    1221                 : 
    1222            2658 :     UpdateInhibitPersistentCachingFlag();
    1223                 : 
    1224                 :     // this must be called before firing OnStartRequest, since http clients,
    1225                 :     // such as imagelib, expect our cache entry to already have the correct
    1226                 :     // expiration time (bug 87710).
    1227            2658 :     if (mCacheEntry) {
    1228            1143 :         rv = InitCacheEntry();
    1229            1143 :         if (NS_FAILED(rv))
    1230               0 :             CloseCacheEntry(true);
    1231                 :     }
    1232                 : 
    1233                 :     // Check that the server sent us what we were asking for
    1234            2658 :     if (mResuming) {
    1235                 :         // Create an entity id from the response
    1236              40 :         nsCAutoString id;
    1237              20 :         rv = GetEntityID(id);
    1238              20 :         if (NS_FAILED(rv)) {
    1239                 :             // If creating an entity id is not possible -> error
    1240               4 :             Cancel(NS_ERROR_NOT_RESUMABLE);
    1241                 :         }
    1242              22 :         else if (mResponseHead->Status() != 206 &&
    1243               6 :                  mResponseHead->Status() != 200) {
    1244                 :             // Probably 404 Not Found, 412 Precondition Failed or
    1245                 :             // 416 Invalid Range -> error
    1246               3 :             LOG(("Unexpected response status while resuming, aborting [this=%p]\n",
    1247                 :                  this));
    1248               3 :             Cancel(NS_ERROR_ENTITY_CHANGED);
    1249                 :         }
    1250                 :         // If we were passed an entity id, verify it's equal to the server's
    1251              13 :         else if (!mEntityID.IsEmpty()) {
    1252              13 :             if (!mEntityID.Equals(id)) {
    1253               0 :                 LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",
    1254                 :                      mEntityID.get(), id.get(), this));
    1255               0 :                 Cancel(NS_ERROR_ENTITY_CHANGED);
    1256                 :             }
    1257                 :         }
    1258                 :     }
    1259                 : 
    1260            2658 :     rv = CallOnStartRequest();
    1261            2658 :     if (NS_FAILED(rv)) return rv;
    1262                 : 
    1263                 :     // install cache listener if we still have a cache entry open
    1264            2648 :     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE)) {
    1265            1132 :         rv = InstallCacheListener();
    1266            1132 :         if (NS_FAILED(rv)) return rv;
    1267                 :     }
    1268                 : 
    1269            2648 :     return NS_OK;
    1270                 : }
    1271                 : 
    1272                 : nsresult
    1273              16 : nsHttpChannel::PromptTempRedirect()
    1274                 : {
    1275              16 :     if (!gHttpHandler->PromptTempRedirect()) {
    1276               2 :         return NS_OK;
    1277                 :     }
    1278                 :     nsresult rv;
    1279                 :     nsCOMPtr<nsIStringBundleService> bundleService =
    1280              28 :             do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    1281              14 :     if (NS_FAILED(rv)) return rv;
    1282                 : 
    1283              28 :     nsCOMPtr<nsIStringBundle> stringBundle;
    1284              14 :     rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle));
    1285              14 :     if (NS_FAILED(rv)) return rv;
    1286                 : 
    1287              28 :     nsXPIDLString messageString;
    1288              14 :     rv = stringBundle->GetStringFromName(NS_LITERAL_STRING("RepostFormData").get(), getter_Copies(messageString));
    1289                 :     // GetStringFromName can return NS_OK and NULL messageString.
    1290              14 :     if (NS_SUCCEEDED(rv) && messageString) {
    1291              14 :         bool repost = false;
    1292                 : 
    1293              28 :         nsCOMPtr<nsIPrompt> prompt;
    1294              14 :         GetCallback(prompt);
    1295              14 :         if (!prompt)
    1296              14 :             return NS_ERROR_NO_INTERFACE;
    1297                 : 
    1298               0 :         prompt->Confirm(nsnull, messageString, &repost);
    1299               0 :         if (!repost)
    1300               0 :             return NS_ERROR_FAILURE;
    1301                 :     }
    1302                 : 
    1303               0 :     return rv;
    1304                 : }
    1305                 : 
    1306                 : nsresult
    1307               2 : nsHttpChannel::ProxyFailover()
    1308                 : {
    1309               2 :     LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this));
    1310                 : 
    1311                 :     nsresult rv;
    1312                 : 
    1313                 :     nsCOMPtr<nsIProtocolProxyService> pps =
    1314               4 :             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
    1315               2 :     if (NS_FAILED(rv))
    1316               0 :         return rv;
    1317                 : 
    1318               4 :     nsCOMPtr<nsIProxyInfo> pi;
    1319               4 :     rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus,
    1320               4 :                                   getter_AddRefs(pi));
    1321               2 :     if (NS_FAILED(rv))
    1322               0 :         return rv;
    1323                 : 
    1324                 :     // XXXbz so where does this codepath remove us from the loadgroup,
    1325                 :     // exactly?
    1326               2 :     return AsyncDoReplaceWithProxy(pi);
    1327                 : }
    1328                 : 
    1329                 : void
    1330              10 : nsHttpChannel::HandleAsyncReplaceWithProxy()
    1331                 : {
    1332              10 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    1333                 : 
    1334              10 :     if (mSuspendCount) {
    1335               0 :         LOG(("Waiting until resume to do async proxy replacement [this=%p]\n",
    1336                 :              this));
    1337               0 :         mCallOnResume = &nsHttpChannel::HandleAsyncReplaceWithProxy;
    1338               0 :         return;
    1339                 :     }
    1340                 : 
    1341              10 :     nsresult status = mStatus;
    1342                 :     
    1343              20 :     nsCOMPtr<nsIProxyInfo> pi;
    1344              10 :     pi.swap(mTargetProxyInfo);
    1345              10 :     if (!mCanceled) {
    1346              10 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncReplaceWithProxy);
    1347              10 :         status = AsyncDoReplaceWithProxy(pi);
    1348              10 :         if (NS_SUCCEEDED(status))
    1349                 :             return;
    1350               0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncReplaceWithProxy);
    1351                 :     }
    1352                 : 
    1353               0 :     if (NS_FAILED(status)) {
    1354               0 :         ContinueHandleAsyncReplaceWithProxy(status);
    1355                 :     }
    1356                 : }
    1357                 : 
    1358                 : nsresult
    1359              10 : nsHttpChannel::ContinueHandleAsyncReplaceWithProxy(nsresult status)
    1360                 : {
    1361              10 :     if (mLoadGroup && NS_SUCCEEDED(status)) {
    1362               0 :         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
    1363                 :     }
    1364              10 :     else if (NS_FAILED(status)) {
    1365               1 :         AsyncAbort(status);
    1366                 :     }
    1367                 : 
    1368                 :     // Return NS_OK here, even it seems to be breaking the async function stack
    1369                 :     // contract (i.e. passing the result code to a function bellow).
    1370                 :     // ContinueHandleAsyncReplaceWithProxy will always be at the bottom of the
    1371                 :     // stack. If we would return the failure code, the async function stack
    1372                 :     // logic would cancel the channel synchronously, which is undesired after
    1373                 :     // invoking AsyncAbort above.
    1374              10 :     return NS_OK;
    1375                 : }
    1376                 : 
    1377                 : void
    1378               0 : nsHttpChannel::HandleAsyncRedirectChannelToHttps()
    1379                 : {
    1380               0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    1381                 : 
    1382               0 :     if (mSuspendCount) {
    1383               0 :         LOG(("Waiting until resume to do async redirect to https [this=%p]\n", this));
    1384               0 :         mCallOnResume = &nsHttpChannel::HandleAsyncRedirectChannelToHttps;
    1385               0 :         return;
    1386                 :     }
    1387                 : 
    1388               0 :     nsresult rv = AsyncRedirectChannelToHttps();
    1389               0 :     if (NS_FAILED(rv))
    1390               0 :         ContinueAsyncRedirectChannelToHttps(rv);
    1391                 : }
    1392                 : 
    1393                 : nsresult
    1394               0 : nsHttpChannel::AsyncRedirectChannelToHttps()
    1395                 : {
    1396               0 :     nsresult rv = NS_OK;
    1397               0 :     LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
    1398                 : 
    1399               0 :     nsCOMPtr<nsIChannel> newChannel;
    1400               0 :     nsCOMPtr<nsIURI> upgradedURI;
    1401                 : 
    1402               0 :     rv = mURI->Clone(getter_AddRefs(upgradedURI));
    1403               0 :     NS_ENSURE_SUCCESS(rv,rv);
    1404                 : 
    1405               0 :     upgradedURI->SetScheme(NS_LITERAL_CSTRING("https"));
    1406                 : 
    1407               0 :     PRInt32 oldPort = -1;
    1408               0 :     rv = mURI->GetPort(&oldPort);
    1409               0 :     if (NS_FAILED(rv)) return rv;
    1410                 : 
    1411                 :     // Keep any nonstandard ports so only the scheme is changed.
    1412                 :     // For example:
    1413                 :     //  http://foo.com:80 -> https://foo.com:443
    1414                 :     //  http://foo.com:81 -> https://foo.com:81
    1415                 : 
    1416               0 :     if (oldPort == 80 || oldPort == -1)
    1417               0 :         upgradedURI->SetPort(-1);
    1418                 :     else
    1419               0 :         upgradedURI->SetPort(oldPort);
    1420                 : 
    1421               0 :     nsCOMPtr<nsIIOService> ioService;
    1422               0 :     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    1423               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1424                 : 
    1425               0 :     rv = ioService->NewChannelFromURI(upgradedURI, getter_AddRefs(newChannel));
    1426               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1427                 : 
    1428               0 :     rv = SetupReplacementChannel(upgradedURI, newChannel, true, false);
    1429               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1430                 : 
    1431                 :     // Inform consumers about this fake redirect
    1432               0 :     mRedirectChannel = newChannel;
    1433               0 :     PRUint32 flags = nsIChannelEventSink::REDIRECT_PERMANENT;
    1434                 : 
    1435                 :     PushRedirectAsyncFunc(
    1436               0 :         &nsHttpChannel::ContinueAsyncRedirectChannelToHttps);
    1437               0 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
    1438                 : 
    1439               0 :     if (NS_SUCCEEDED(rv))
    1440               0 :         rv = WaitForRedirectCallback();
    1441                 : 
    1442               0 :     if (NS_FAILED(rv)) {
    1443               0 :         AutoRedirectVetoNotifier notifier(this);
    1444                 :         PopRedirectAsyncFunc(
    1445               0 :             &nsHttpChannel::ContinueAsyncRedirectChannelToHttps);
    1446                 :     }
    1447                 : 
    1448               0 :     return rv;
    1449                 : }
    1450                 : 
    1451                 : nsresult
    1452               0 : nsHttpChannel::ContinueAsyncRedirectChannelToHttps(nsresult rv)
    1453                 : {
    1454               0 :     AutoRedirectVetoNotifier notifier(this);
    1455                 : 
    1456               0 :     if (NS_FAILED(rv)) {
    1457                 :         // Fill the failure status here, the update to https had been vetoed
    1458                 :         // but from the security reasons we have to discard the whole channel
    1459                 :         // load.
    1460               0 :         mStatus = rv;
    1461                 :     }
    1462                 : 
    1463               0 :     if (mLoadGroup)
    1464               0 :         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
    1465                 : 
    1466               0 :     if (NS_FAILED(rv)) {
    1467                 :         // We have to manually notify the listener because there is not any pump
    1468                 :         // that would call our OnStart/StopRequest after resume from waiting for
    1469                 :         // the redirect callback.
    1470               0 :         DoNotifyListener();
    1471               0 :         return rv;
    1472                 :     }
    1473                 : 
    1474                 :     // Make sure to do this _after_ calling OnChannelRedirect
    1475               0 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    1476                 : 
    1477                 :     // And now, notify observers the deprecated way
    1478               0 :     nsCOMPtr<nsIHttpEventSink> httpEventSink;
    1479               0 :     GetCallback(httpEventSink);
    1480               0 :     if (httpEventSink) {
    1481                 :         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
    1482                 :         // versions.
    1483               0 :         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
    1484               0 :         if (NS_FAILED(rv)) {
    1485               0 :             mStatus = rv;
    1486               0 :             DoNotifyListener();
    1487               0 :             return rv;
    1488                 :         }
    1489                 :     }
    1490                 : 
    1491                 :     // open new channel
    1492               0 :     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    1493               0 :     if (NS_FAILED(rv)) {
    1494               0 :         mStatus = rv;
    1495               0 :         DoNotifyListener();
    1496               0 :         return rv;
    1497                 :     }
    1498                 : 
    1499               0 :     mStatus = NS_BINDING_REDIRECTED;
    1500                 : 
    1501               0 :     notifier.RedirectSucceeded();
    1502                 : 
    1503                 :     // disconnect from the old listeners...
    1504               0 :     mListener = nsnull;
    1505               0 :     mListenerContext = nsnull;
    1506                 : 
    1507                 :     // ...and the old callbacks
    1508               0 :     mCallbacks = nsnull;
    1509               0 :     mProgressSink = nsnull;
    1510                 : 
    1511               0 :     return rv;
    1512                 : }
    1513                 : 
    1514                 : nsresult
    1515              12 : nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
    1516                 : {
    1517              12 :     LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
    1518                 :     nsresult rv;
    1519                 : 
    1520              24 :     nsCOMPtr<nsIChannel> newChannel;
    1521              12 :     rv = gHttpHandler->NewProxiedChannel(mURI, pi, getter_AddRefs(newChannel));
    1522              12 :     if (NS_FAILED(rv))
    1523               0 :         return rv;
    1524                 : 
    1525              12 :     rv = SetupReplacementChannel(mURI, newChannel, true, true);
    1526              12 :     if (NS_FAILED(rv))
    1527               0 :         return rv;
    1528                 : 
    1529                 :     // Inform consumers about this fake redirect
    1530              12 :     mRedirectChannel = newChannel;
    1531              12 :     PRUint32 flags = nsIChannelEventSink::REDIRECT_INTERNAL;
    1532                 : 
    1533              12 :     PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
    1534              12 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
    1535                 : 
    1536              12 :     if (NS_SUCCEEDED(rv))
    1537              12 :         rv = WaitForRedirectCallback();
    1538                 : 
    1539              12 :     if (NS_FAILED(rv)) {
    1540               0 :         AutoRedirectVetoNotifier notifier(this);
    1541               0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
    1542                 :     }
    1543                 : 
    1544              12 :     return rv;
    1545                 : }
    1546                 : 
    1547                 : nsresult
    1548              12 : nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv)
    1549                 : {
    1550              24 :     AutoRedirectVetoNotifier notifier(this);
    1551                 : 
    1552              12 :     if (NS_FAILED(rv))
    1553               2 :         return rv;
    1554                 : 
    1555              10 :     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
    1556                 : 
    1557                 :     // Make sure to do this _after_ calling OnChannelRedirect
    1558              10 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    1559                 : 
    1560                 :     // open new channel
    1561              10 :     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    1562              10 :     if (NS_FAILED(rv))
    1563               0 :         return rv;
    1564                 : 
    1565              10 :     mStatus = NS_BINDING_REDIRECTED;
    1566                 : 
    1567              10 :     notifier.RedirectSucceeded();
    1568                 : 
    1569                 :     // disconnect from the old listeners...
    1570              10 :     mListener = nsnull;
    1571              10 :     mListenerContext = nsnull;
    1572                 : 
    1573                 :     // ...and the old callbacks
    1574              10 :     mCallbacks = nsnull;
    1575              10 :     mProgressSink = nsnull;
    1576                 : 
    1577              10 :     return rv;
    1578                 : }
    1579                 : 
    1580                 : nsresult
    1581              10 : nsHttpChannel::ResolveProxy()
    1582                 : {
    1583              10 :     LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this));
    1584                 : 
    1585                 :     nsresult rv;
    1586                 : 
    1587                 :     nsCOMPtr<nsIProtocolProxyService> pps =
    1588              20 :             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
    1589              10 :     if (NS_FAILED(rv))
    1590               0 :         return rv;
    1591                 : 
    1592              10 :     PRUint32 resolveFlags = 0;
    1593              10 :     if (mConnectionInfo->ProxyInfo())
    1594              10 :         mConnectionInfo->ProxyInfo()->GetResolveFlags(&resolveFlags);
    1595                 : 
    1596              10 :     return pps->AsyncResolve(mURI, resolveFlags, this, getter_AddRefs(mProxyRequest));
    1597                 : }
    1598                 : 
    1599                 : bool
    1600             545 : nsHttpChannel::ResponseWouldVary()
    1601                 : {
    1602                 :     nsresult rv;
    1603            1090 :     nsCAutoString buf, metaKey;
    1604             545 :     mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
    1605             545 :     if (!buf.IsEmpty()) {
    1606              44 :         NS_NAMED_LITERAL_CSTRING(prefix, "request-");
    1607                 : 
    1608                 :         // enumerate the elements of the Vary header...
    1609              22 :         char *val = buf.BeginWriting(); // going to munge buf
    1610              22 :         char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
    1611              22 :         while (token) {
    1612              22 :             LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
    1613                 :                  "processing %s\n",
    1614                 :                  this, token));
    1615                 :             //
    1616                 :             // if "*", then assume response would vary.  technically speaking,
    1617                 :             // "Vary: header, *" is not permitted, but we allow it anyways.
    1618                 :             //
    1619                 :             // We hash values of cookie-headers for the following reasons:
    1620                 :             //
    1621                 :             //   1- cookies can be very large in size
    1622                 :             //
    1623                 :             //   2- cookies may contain sensitive information.  (for parity with
    1624                 :             //      out policy of not storing Set-cookie headers in the cache
    1625                 :             //      meta data, we likewise do not want to store cookie headers
    1626                 :             //      here.)
    1627                 :             //
    1628              22 :             if (*token == '*')
    1629               0 :                 return true; // if we encounter this, just get out of here
    1630                 : 
    1631                 :             // build cache meta data key...
    1632              22 :             metaKey = prefix + nsDependentCString(token);
    1633                 : 
    1634                 :             // check the last value of the given request header to see if it has
    1635                 :             // since changed.  if so, then indeed the cached response is invalid.
    1636              44 :             nsXPIDLCString lastVal;
    1637              22 :             mCacheEntry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
    1638              22 :             LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
    1639                 :                     "stored value = %c%s%c\n", this, '"', lastVal.get(), '"'));
    1640                 : 
    1641                 :             // Look for value of "Cookie" in the request headers
    1642              22 :             nsHttpAtom atom = nsHttp::ResolveAtom(token);
    1643              22 :             const char *newVal = mRequestHead.PeekHeader(atom);
    1644              22 :             if (!lastVal.IsEmpty()) {
    1645                 :                 // value for this header in cache, but no value in request
    1646              20 :                 if (!newVal)
    1647               1 :                     return true; // yes - response would vary
    1648                 : 
    1649                 :                 // If this is a cookie-header, stored metadata is not
    1650                 :                 // the value itself but the hash. So we also hash the
    1651                 :                 // outgoing value here in order to compare the hashes
    1652              38 :                 nsCAutoString hash;
    1653              19 :                 if (atom == nsHttp::Cookie) {
    1654               4 :                     rv = Hash(newVal, hash);
    1655                 :                     // If hash failed, be conservative (the cached hash
    1656                 :                     // exists at this point) and claim response would vary
    1657               4 :                     if (NS_FAILED(rv))
    1658               0 :                         return true;
    1659               4 :                     newVal = hash.get();
    1660                 : 
    1661               4 :                     LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
    1662                 :                             "set-cookie value hashed to %s\n",
    1663                 :                          this, newVal));
    1664                 :                 }
    1665                 : 
    1666              19 :                 if (strcmp(newVal, lastVal))
    1667              10 :                     return true; // yes, response would vary
    1668                 : 
    1669               2 :             } else if (newVal) { // old value is empty, but newVal is set
    1670               1 :                 return true;
    1671                 :             }
    1672                 : 
    1673                 :             // next token...
    1674              32 :             token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
    1675                 :         }
    1676                 :     }
    1677             533 :     return false;
    1678                 : }
    1679                 : 
    1680                 : // We need to have an implementation of this function just so that we can keep
    1681                 : // all references to mCallOnResume of type nsHttpChannel:  it's not OK in C++
    1682                 : // to set a member function ptr to  a base class function.
    1683                 : void
    1684              36 : nsHttpChannel::HandleAsyncAbort()
    1685                 : {
    1686              36 :     HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
    1687              36 : }
    1688                 : 
    1689                 : 
    1690                 : nsresult
    1691               8 : nsHttpChannel::Hash(const char *buf, nsACString &hash)
    1692                 : {
    1693                 :     nsresult rv;
    1694               8 :     if (!mHasher) {
    1695               6 :         mHasher = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    1696               6 :         if (NS_FAILED(rv)) {
    1697               0 :             LOG(("nsHttpChannel: Failed to instantiate crypto-hasher"));
    1698               0 :             return rv;
    1699                 :         }
    1700                 :     }
    1701                 : 
    1702               8 :     rv = mHasher->Init(nsICryptoHash::SHA1);
    1703               8 :     NS_ENSURE_SUCCESS(rv, rv);
    1704                 : 
    1705               8 :    rv = mHasher->Update(reinterpret_cast<unsigned const char*>(buf),
    1706               8 :                          strlen(buf));
    1707               8 :     NS_ENSURE_SUCCESS(rv, rv);
    1708                 : 
    1709               8 :     rv = mHasher->Finish(true, hash);
    1710               8 :     NS_ENSURE_SUCCESS(rv, rv);
    1711                 : 
    1712               8 :     return NS_OK;
    1713                 : }
    1714                 : 
    1715                 : //-----------------------------------------------------------------------------
    1716                 : // nsHttpChannel <byte-range>
    1717                 : //-----------------------------------------------------------------------------
    1718                 : 
    1719                 : nsresult
    1720               2 : nsHttpChannel::SetupByteRangeRequest(PRUint32 partialLen)
    1721                 : {
    1722                 :     // cached content has been found to be partial, add necessary request
    1723                 :     // headers to complete cache entry.
    1724                 : 
    1725                 :     // use strongest validator available...
    1726               2 :     const char *val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
    1727               2 :     if (!val)
    1728               1 :         val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
    1729               2 :     if (!val) {
    1730                 :         // if we hit this code it means mCachedResponseHead->IsResumable() is
    1731                 :         // either broken or not being called.
    1732               0 :         NS_NOTREACHED("no cache validator");
    1733               0 :         return NS_ERROR_FAILURE;
    1734                 :     }
    1735                 : 
    1736                 :     char buf[32];
    1737               2 :     PR_snprintf(buf, sizeof(buf), "bytes=%u-", partialLen);
    1738                 : 
    1739               2 :     mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
    1740               2 :     mRequestHead.SetHeader(nsHttp::If_Range, nsDependentCString(val));
    1741                 : 
    1742               2 :     return NS_OK;
    1743                 : }
    1744                 : 
    1745                 : nsresult
    1746               2 : nsHttpChannel::ProcessPartialContent()
    1747                 : {
    1748                 :     // ok, we've just received a 206
    1749                 :     //
    1750                 :     // we need to stream whatever data is in the cache out first, and then
    1751                 :     // pick up whatever data is on the wire, writing it into the cache.
    1752                 : 
    1753               2 :     LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this)); 
    1754                 : 
    1755               2 :     NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
    1756               2 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
    1757                 : 
    1758                 :     // Make sure to clear bogus content-encodings before looking at the header
    1759               2 :     ClearBogusContentEncodingIfNeeded();
    1760                 :     
    1761                 :     // Check if the content-encoding we now got is different from the one we
    1762                 :     // got before
    1763               2 :     if (PL_strcasecmp(mResponseHead->PeekHeader(nsHttp::Content_Encoding),
    1764               2 :                       mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding))
    1765                 :                       != 0) {
    1766               0 :         Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);
    1767               0 :         return CallOnStartRequest();
    1768                 :     }
    1769                 : 
    1770                 : 
    1771                 :     // suspend the current transaction
    1772               2 :     nsresult rv = mTransactionPump->Suspend();
    1773               2 :     if (NS_FAILED(rv)) return rv;
    1774                 : 
    1775                 :     // merge any new headers with the cached response headers
    1776               2 :     rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
    1777               2 :     if (NS_FAILED(rv)) return rv;
    1778                 : 
    1779                 :     // update the cached response head
    1780               4 :     nsCAutoString head;
    1781               2 :     mCachedResponseHead->Flatten(head, true);
    1782               2 :     rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
    1783               2 :     if (NS_FAILED(rv)) return rv;
    1784                 : 
    1785                 :     // make the cached response be the current response
    1786               2 :     mResponseHead = mCachedResponseHead;
    1787                 : 
    1788               2 :     UpdateInhibitPersistentCachingFlag();
    1789                 : 
    1790               2 :     rv = UpdateExpirationTime();
    1791               2 :     if (NS_FAILED(rv)) return rv;
    1792                 : 
    1793                 :     // notify observers interested in looking at a response that has been
    1794                 :     // merged with any cached headers (http-on-examine-merged-response).
    1795               2 :     gHttpHandler->OnExamineMergedResponse(this);
    1796                 : 
    1797                 :     // the cached content is valid, although incomplete.
    1798               2 :     mCachedContentIsValid = true;
    1799               2 :     return ReadFromCache();
    1800                 : }
    1801                 : 
    1802                 : nsresult
    1803               2 : nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone)
    1804                 : {
    1805                 :     nsresult rv;
    1806                 : 
    1807               2 :     LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
    1808                 : 
    1809                 :     // by default, assume we would have streamed all data or failed...
    1810               2 :     *streamDone = true;
    1811                 : 
    1812                 :     // setup cache listener to append to cache entry
    1813                 :     PRUint32 size;
    1814               2 :     rv = mCacheEntry->GetDataSize(&size);
    1815               2 :     if (NS_FAILED(rv)) return rv;
    1816                 : 
    1817               2 :     rv = InstallCacheListener(size);
    1818               2 :     if (NS_FAILED(rv)) return rv;
    1819                 : 
    1820                 :     // need to track the logical offset of the data being sent to our listener
    1821               2 :     mLogicalOffset = size;
    1822                 : 
    1823                 :     // we're now completing the cached content, so we can clear this flag.
    1824                 :     // this puts us in the state of a regular download.
    1825               2 :     mCachedContentIsPartial = false;
    1826                 : 
    1827                 :     // resume the transaction if it exists, otherwise the pipe contained the
    1828                 :     // remaining part of the document and we've now streamed all of the data.
    1829               2 :     if (mTransactionPump) {
    1830               2 :         rv = mTransactionPump->Resume();
    1831               2 :         if (NS_SUCCEEDED(rv))
    1832               2 :             *streamDone = false;
    1833                 :     }
    1834                 :     else
    1835               0 :         NS_NOTREACHED("no transaction");
    1836               2 :     return rv;
    1837                 : }
    1838                 : 
    1839                 : //-----------------------------------------------------------------------------
    1840                 : // nsHttpChannel <cache>
    1841                 : //-----------------------------------------------------------------------------
    1842                 : 
    1843                 : nsresult
    1844              25 : nsHttpChannel::ProcessNotModified()
    1845                 : {
    1846                 :     nsresult rv;
    1847                 : 
    1848              25 :     LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this)); 
    1849                 : 
    1850              25 :     if (mCustomConditionalRequest) {
    1851               0 :         LOG(("Bypassing ProcessNotModified due to custom conditional headers")); 
    1852               0 :         return NS_ERROR_FAILURE;
    1853                 :     }
    1854                 : 
    1855              25 :     NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
    1856               6 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
    1857                 : 
    1858                 :     // merge any new headers with the cached response headers
    1859               6 :     rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
    1860               6 :     if (NS_FAILED(rv)) return rv;
    1861                 : 
    1862                 :     // update the cached response head
    1863              12 :     nsCAutoString head;
    1864               6 :     mCachedResponseHead->Flatten(head, true);
    1865               6 :     rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
    1866               6 :     if (NS_FAILED(rv)) return rv;
    1867                 : 
    1868                 :     // make the cached response be the current response
    1869               6 :     mResponseHead = mCachedResponseHead;
    1870                 : 
    1871               6 :     UpdateInhibitPersistentCachingFlag();
    1872                 : 
    1873               6 :     rv = UpdateExpirationTime();
    1874               6 :     if (NS_FAILED(rv)) return rv;
    1875                 : 
    1876               6 :     rv = AddCacheEntryHeaders(mCacheEntry);
    1877               6 :     if (NS_FAILED(rv)) return rv;
    1878                 : 
    1879                 :     // notify observers interested in looking at a reponse that has been
    1880                 :     // merged with any cached headers
    1881               6 :     gHttpHandler->OnExamineMergedResponse(this);
    1882                 : 
    1883               6 :     mCachedContentIsValid = true;
    1884               6 :     rv = ReadFromCache();
    1885               6 :     if (NS_FAILED(rv)) return rv;
    1886                 : 
    1887               6 :     mTransactionReplaced = true;
    1888               6 :     return NS_OK;
    1889                 : }
    1890                 : 
    1891                 : nsresult
    1892             837 : nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback)
    1893                 : {
    1894             837 :     LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
    1895                 :     nsresult rv;
    1896                 : 
    1897             837 :     *waitingForRedirectCallback = false;
    1898             837 :     mFallingBack = false;
    1899                 : 
    1900                 :     // At this point a load has failed (either due to network problems
    1901                 :     // or an error returned on the server).  Perform an application
    1902                 :     // cache fallback if we have a URI to fall back to.
    1903             837 :     if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) {
    1904             828 :         LOG(("  choosing not to fallback [%p,%s,%d]",
    1905                 :              mApplicationCache.get(), mFallbackKey.get(), mFallbackChannel));
    1906             828 :         return NS_OK;
    1907                 :     }
    1908                 : 
    1909                 :     // Make sure the fallback entry hasn't been marked as a foreign
    1910                 :     // entry.
    1911                 :     PRUint32 fallbackEntryType;
    1912               9 :     rv = mApplicationCache->GetTypes(mFallbackKey, &fallbackEntryType);
    1913               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1914                 : 
    1915               9 :     if (fallbackEntryType & nsIApplicationCache::ITEM_FOREIGN) {
    1916                 :         // This cache points to a fallback that refers to a different
    1917                 :         // manifest.  Refuse to fall back.
    1918               0 :         return NS_OK;
    1919                 :     }
    1920                 : 
    1921               9 :     NS_ASSERTION(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK,
    1922                 :                  "Fallback entry not marked correctly!");
    1923                 : 
    1924                 :     // Kill any opportunistic cache entry, and disable opportunistic
    1925                 :     // caching for the fallback.
    1926               9 :     if (mOfflineCacheEntry) {
    1927               0 :         mOfflineCacheEntry->Doom();
    1928               0 :         mOfflineCacheEntry = 0;
    1929               0 :         mOfflineCacheAccess = 0;
    1930                 :     }
    1931                 : 
    1932               9 :     mCacheForOfflineUse = false;
    1933               9 :     mCachingOpportunistically = false;
    1934               9 :     mOfflineCacheClientID.Truncate();
    1935               9 :     mOfflineCacheEntry = 0;
    1936               9 :     mOfflineCacheAccess = 0;
    1937                 : 
    1938                 :     // Close the current cache entry.
    1939               9 :     if (mCacheEntry)
    1940               6 :         CloseCacheEntry(true);
    1941                 : 
    1942                 :     // Create a new channel to load the fallback entry.
    1943              18 :     nsRefPtr<nsIChannel> newChannel;
    1944               9 :     rv = gHttpHandler->NewChannel(mURI, getter_AddRefs(newChannel));
    1945               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1946                 : 
    1947               9 :     rv = SetupReplacementChannel(mURI, newChannel, true, false);
    1948               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1949                 : 
    1950                 :     // Make sure the new channel loads from the fallback key.
    1951                 :     nsCOMPtr<nsIHttpChannelInternal> httpInternal =
    1952              18 :         do_QueryInterface(newChannel, &rv);
    1953               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1954                 : 
    1955               9 :     rv = httpInternal->SetupFallbackChannel(mFallbackKey.get());
    1956               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1957                 : 
    1958                 :     // ... and fallbacks should only load from the cache.
    1959               9 :     PRUint32 newLoadFlags = mLoadFlags | LOAD_REPLACE | LOAD_ONLY_FROM_CACHE;
    1960               9 :     rv = newChannel->SetLoadFlags(newLoadFlags);
    1961                 : 
    1962                 :     // Inform consumers about this fake redirect
    1963               9 :     mRedirectChannel = newChannel;
    1964               9 :     PRUint32 redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
    1965                 : 
    1966               9 :     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
    1967               9 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
    1968                 : 
    1969               9 :     if (NS_SUCCEEDED(rv))
    1970               9 :         rv = WaitForRedirectCallback();
    1971                 : 
    1972               9 :     if (NS_FAILED(rv)) {
    1973               0 :         AutoRedirectVetoNotifier notifier(this);
    1974               0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
    1975               0 :         return rv;
    1976                 :     }
    1977                 : 
    1978                 :     // Indicate we are now waiting for the asynchronous redirect callback
    1979                 :     // if all went OK.
    1980               9 :     *waitingForRedirectCallback = true;
    1981               9 :     return NS_OK;
    1982                 : }
    1983                 : 
    1984                 : nsresult
    1985               9 : nsHttpChannel::ContinueProcessFallback(nsresult rv)
    1986                 : {
    1987              18 :     AutoRedirectVetoNotifier notifier(this);
    1988                 : 
    1989               9 :     if (NS_FAILED(rv))
    1990               5 :         return rv;
    1991                 : 
    1992               4 :     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
    1993                 : 
    1994                 :     // Make sure to do this _after_ calling OnChannelRedirect
    1995               4 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    1996                 : 
    1997               4 :     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    1998               4 :     if (NS_FAILED(rv))
    1999               0 :         return rv;
    2000                 : 
    2001                 :     // close down this channel
    2002               4 :     Cancel(NS_BINDING_REDIRECTED);
    2003                 : 
    2004               4 :     notifier.RedirectSucceeded();
    2005                 : 
    2006                 :     // disconnect from our listener
    2007               4 :     mListener = 0;
    2008               4 :     mListenerContext = 0;
    2009                 : 
    2010                 :     // and from our callbacks
    2011               4 :     mCallbacks = nsnull;
    2012               4 :     mProgressSink = nsnull;
    2013                 : 
    2014               4 :     mFallingBack = true;
    2015                 : 
    2016               4 :     return NS_OK;
    2017                 : }
    2018                 : 
    2019                 : // Determines if a request is a byte range request for a subrange,
    2020                 : // i.e. is a byte range request, but not a 0- byte range request.
    2021                 : static bool
    2022            2950 : IsSubRangeRequest(nsHttpRequestHead &aRequestHead)
    2023                 : {
    2024            2950 :     if (!aRequestHead.PeekHeader(nsHttp::Range))
    2025            2927 :         return false;
    2026              46 :     nsCAutoString byteRange;
    2027              23 :     aRequestHead.GetHeader(nsHttp::Range, byteRange);
    2028              23 :     return !byteRange.EqualsLiteral("bytes=0-");
    2029                 : }
    2030                 : 
    2031                 : nsresult
    2032            3472 : nsHttpChannel::OpenCacheEntry()
    2033                 : {
    2034                 :     nsresult rv;
    2035                 : 
    2036            3472 :     mAsyncCacheOpen = false;
    2037            3472 :     mLoadedFromApplicationCache = false;
    2038                 : 
    2039            3472 :     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
    2040                 : 
    2041                 :     // make sure we're not abusing this function
    2042            3472 :     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
    2043                 : 
    2044            6944 :     nsCAutoString cacheKey;
    2045                 : 
    2046            3472 :     if (mRequestHead.Method() == nsHttp::Post) {
    2047                 :         // If the post id is already set then this is an attempt to replay
    2048                 :         // a post transaction via the cache.  Otherwise, we need a unique
    2049                 :         // post id for this transaction.
    2050             176 :         if (mPostID == 0)
    2051             176 :             mPostID = gHttpHandler->GenerateUniqueID();
    2052                 :     }
    2053            3831 :     else if ((mRequestHead.Method() != nsHttp::Get) &&
    2054             535 :              (mRequestHead.Method() != nsHttp::Head)) {
    2055                 :         // don't use the cache for other types of requests
    2056             519 :         return NS_OK;
    2057                 :     }
    2058                 : 
    2059            2953 :     if (mResuming) {
    2060                 :         // We don't support caching for requests initiated
    2061                 :         // via nsIResumableChannel.
    2062              27 :         return NS_OK;
    2063                 :     }
    2064                 : 
    2065                 :     // Don't cache byte range requests which are subranges, only cache 0-
    2066                 :     // byte range requests.
    2067            2926 :     if (IsSubRangeRequest(mRequestHead))
    2068              23 :         return NS_OK;
    2069                 : 
    2070            2903 :     GenerateCacheKey(mPostID, cacheKey);
    2071                 : 
    2072                 :     // Set the desired cache access mode accordingly...
    2073                 :     nsCacheAccessMode accessRequested;
    2074            2903 :     rv = DetermineCacheAccess(&accessRequested);
    2075            2903 :     if (NS_FAILED(rv)) return rv;
    2076                 : 
    2077            1899 :     if (!mApplicationCache && mInheritApplicationCache) {
    2078                 :         // Pick up an application cache from the notification
    2079                 :         // callbacks if available
    2080            3790 :         nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
    2081            1895 :         GetCallback(appCacheContainer);
    2082                 : 
    2083            1895 :         if (appCacheContainer) {
    2084               0 :             appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
    2085                 :         }
    2086                 :     }
    2087                 : 
    2088            1899 :     if (!mApplicationCache &&
    2089                 :         (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE))) {
    2090                 :         // We're supposed to load from an application cache, but
    2091                 :         // one was not supplied by the load group.  Ask the
    2092                 :         // application cache service to choose one for us.
    2093                 :         nsCOMPtr<nsIApplicationCacheService> appCacheService =
    2094              64 :             do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
    2095              32 :         if (appCacheService) {
    2096              32 :             nsresult rv = appCacheService->ChooseApplicationCache
    2097              32 :                 (cacheKey, getter_AddRefs(mApplicationCache));
    2098              32 :             NS_ENSURE_SUCCESS(rv, rv);
    2099                 :         }
    2100                 :     }
    2101                 : 
    2102            3798 :     nsCOMPtr<nsICacheSession> session;
    2103                 : 
    2104                 :     // If we have an application cache, we check it first.
    2105            1899 :     if (mApplicationCache) {
    2106              24 :         nsCAutoString appCacheClientID;
    2107              12 :         mApplicationCache->GetClientID(appCacheClientID);
    2108                 : 
    2109                 :         nsCOMPtr<nsICacheService> serv =
    2110              24 :             do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
    2111              12 :         NS_ENSURE_SUCCESS(rv, rv);
    2112                 : 
    2113              12 :         rv = serv->CreateSession(appCacheClientID.get(),
    2114                 :                                  nsICache::STORE_OFFLINE,
    2115                 :                                  nsICache::STREAM_BASED,
    2116              12 :                                  getter_AddRefs(session));
    2117              12 :         NS_ENSURE_SUCCESS(rv, rv);
    2118                 : 
    2119              12 :         if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
    2120                 :             // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
    2121               0 :             rv = session->OpenCacheEntry(cacheKey,
    2122                 :                                          nsICache::ACCESS_READ, false,
    2123               0 :                                          getter_AddRefs(mCacheEntry));
    2124               0 :             if (NS_SUCCEEDED(rv)) {
    2125               0 :                 mCacheEntry->GetAccessGranted(&mCacheAccess);
    2126               0 :                 LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]",
    2127                 :                     this, mCacheAccess));
    2128               0 :                 mLoadedFromApplicationCache = true;
    2129               0 :                 return NS_OK;
    2130               0 :             } else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
    2131               0 :                 LOG(("bypassing local cache since it is busy\n"));
    2132                 :                 // Don't try to load normal cache entry
    2133               0 :                 return NS_ERROR_NOT_AVAILABLE;
    2134                 :             }
    2135                 :         } else {
    2136                 :             mOnCacheEntryAvailableCallback =
    2137              12 :                 &nsHttpChannel::OnOfflineCacheEntryAvailable;
    2138                 :             // We open with ACCESS_READ only, because we don't want to
    2139                 :             // overwrite the offline cache entry non-atomically.
    2140                 :             // ACCESS_READ will prevent us from writing to the offline
    2141                 :             // cache as a normal cache entry.
    2142              12 :             rv = session->AsyncOpenCacheEntry(cacheKey,
    2143                 :                                               nsICache::ACCESS_READ,
    2144              12 :                                               this);
    2145                 : 
    2146              12 :             if (NS_SUCCEEDED(rv)) {
    2147              12 :                 mAsyncCacheOpen = true;
    2148              12 :                 return NS_OK;
    2149                 :             }
    2150                 :         }
    2151                 : 
    2152                 :         // sync or async opening failed
    2153                 :         return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE,
    2154               0 :                                             rv, true);
    2155                 :     }
    2156                 : 
    2157            1887 :     return OpenNormalCacheEntry(true);
    2158                 : }
    2159                 : 
    2160                 : nsresult
    2161              12 : nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
    2162                 :                                             nsCacheAccessMode aAccess,
    2163                 :                                             nsresult aEntryStatus,
    2164                 :                                             bool aIsSync)
    2165                 : {
    2166                 :     nsresult rv;
    2167                 : 
    2168              12 :     if (NS_SUCCEEDED(aEntryStatus)) {
    2169                 :         // We successfully opened an offline cache session and the entry,
    2170                 :         // so indicate we will load from the offline cache.
    2171               4 :         mLoadedFromApplicationCache = true;
    2172               4 :         mCacheEntry = aEntry;
    2173               4 :         mCacheAccess = aAccess;
    2174                 :     }
    2175                 : 
    2176              12 :     if (mCanceled && NS_FAILED(mStatus)) {
    2177               0 :         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
    2178               0 :         return mStatus;
    2179                 :     }
    2180                 : 
    2181              12 :     if (NS_SUCCEEDED(aEntryStatus))
    2182                 :         // Called from OnCacheEntryAvailable, advance to the next state
    2183               4 :         return Connect(false);
    2184                 : 
    2185               8 :     if (!mCacheForOfflineUse && !mFallbackChannel) {
    2186              16 :         nsCAutoString cacheKey;
    2187               8 :         GenerateCacheKey(mPostID, cacheKey);
    2188                 : 
    2189                 :         // Check for namespace match.
    2190              16 :         nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
    2191               8 :         rv = mApplicationCache->GetMatchingNamespace
    2192               8 :             (cacheKey, getter_AddRefs(namespaceEntry));
    2193               8 :         if (NS_FAILED(rv) && !aIsSync)
    2194               0 :             return Connect(false);
    2195               8 :         NS_ENSURE_SUCCESS(rv, rv);
    2196                 : 
    2197               8 :         PRUint32 namespaceType = 0;
    2198              16 :         if (!namespaceEntry ||
    2199               8 :             NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
    2200                 :             (namespaceType &
    2201                 :              (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
    2202                 :               nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC |
    2203                 :               nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) {
    2204                 :             // When loading from an application cache, only items
    2205                 :             // on the whitelist or matching a
    2206                 :             // fallback/opportunistic namespace should hit the
    2207                 :             // network...
    2208               0 :             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
    2209                 : 
    2210                 :             // ... and if there were an application cache entry,
    2211                 :             // we would have found it earlier.
    2212               0 :             return aIsSync ? NS_ERROR_CACHE_KEY_NOT_FOUND : Connect(false);
    2213                 :         }
    2214                 : 
    2215               8 :         if (namespaceType &
    2216                 :             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
    2217               8 :             rv = namespaceEntry->GetData(mFallbackKey);
    2218               8 :             if (NS_FAILED(rv) && !aIsSync)
    2219               0 :                 return Connect(false);
    2220               8 :             NS_ENSURE_SUCCESS(rv, rv);
    2221                 :         }
    2222                 : 
    2223               8 :         if ((namespaceType &
    2224                 :              nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) &&
    2225                 :             mLoadFlags & LOAD_DOCUMENT_URI) {
    2226                 :             // Document loads for items in an opportunistic namespace
    2227                 :             // should be placed in the offline cache.
    2228               0 :             nsCString clientID;
    2229               0 :             mApplicationCache->GetClientID(clientID);
    2230                 : 
    2231               0 :             mCacheForOfflineUse = !clientID.IsEmpty();
    2232               0 :             SetOfflineCacheClientID(clientID);
    2233               0 :             mCachingOpportunistically = true;
    2234                 :         }
    2235                 :     }
    2236                 : 
    2237               8 :     return OpenNormalCacheEntry(aIsSync);
    2238                 : }
    2239                 : 
    2240                 : 
    2241                 : nsresult
    2242            1895 : nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
    2243                 : {
    2244            1895 :     NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
    2245                 : 
    2246                 :     nsresult rv;
    2247                 : 
    2248            3790 :     nsCAutoString cacheKey;
    2249            1895 :     GenerateCacheKey(mPostID, cacheKey);
    2250                 : 
    2251            1895 :     nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
    2252                 : 
    2253            3790 :     nsCOMPtr<nsICacheSession> session;
    2254                 :     rv = gHttpHandler->GetCacheSession(storagePolicy,
    2255            1895 :                                        getter_AddRefs(session));
    2256            1895 :     if (NS_FAILED(rv)) return rv;
    2257                 : 
    2258                 :     nsCacheAccessMode accessRequested;
    2259            1895 :     rv = DetermineCacheAccess(&accessRequested);
    2260            1895 :     if (NS_FAILED(rv)) return rv;
    2261                 : 
    2262            1895 :     if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
    2263              46 :         if (!aIsSync) {
    2264                 :             // Unexpected state: we were called from OnCacheEntryAvailable(),
    2265                 :             // so LOAD_BYPASS_LOCAL_CACHE_IF_BUSY shouldn't be set. Unless
    2266                 :             // somebody altered mLoadFlags between OpenCacheEntry() and
    2267                 :             // OnCacheEntryAvailable()...
    2268                 :             NS_WARNING(
    2269                 :                 "OpenNormalCacheEntry() called from OnCacheEntryAvailable() "
    2270               0 :                 "when LOAD_BYPASS_LOCAL_CACHE_IF_BUSY was specified");
    2271                 :         }
    2272                 : 
    2273                 :         // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
    2274              46 :         rv = session->OpenCacheEntry(cacheKey, accessRequested, false,
    2275              46 :                                      getter_AddRefs(mCacheEntry));
    2276              46 :         if (NS_SUCCEEDED(rv)) {
    2277              43 :             mCacheEntry->GetAccessGranted(&mCacheAccess);
    2278              43 :             LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]",
    2279                 :                 this, mCacheAccess));
    2280                 :         }
    2281               3 :         else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
    2282               3 :             LOG(("bypassing local cache since it is busy\n"));
    2283               3 :             rv = NS_ERROR_NOT_AVAILABLE;
    2284                 :         }
    2285                 :     }
    2286                 :     else {
    2287                 :         mOnCacheEntryAvailableCallback =
    2288            1849 :             &nsHttpChannel::OnNormalCacheEntryAvailable;
    2289            1849 :         rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this);
    2290            1849 :         if (NS_SUCCEEDED(rv)) {
    2291            1849 :             mAsyncCacheOpen = true;
    2292            1849 :             return NS_OK;
    2293                 :         }
    2294                 :     }
    2295                 : 
    2296              46 :     if (!aIsSync)
    2297                 :         // Called from OnCacheEntryAvailable, advance to the next state
    2298               0 :         rv = Connect(false);
    2299                 : 
    2300              46 :     return rv;
    2301                 : }
    2302                 : 
    2303                 : nsresult
    2304            1587 : nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
    2305                 :                                            nsCacheAccessMode aAccess,
    2306                 :                                            nsresult aEntryStatus,
    2307                 :                                            bool aIsSync)
    2308                 : {
    2309            1587 :     NS_ASSERTION(!aIsSync, "aIsSync should be false");
    2310                 : 
    2311            1587 :     if (NS_SUCCEEDED(aEntryStatus)) {
    2312            1583 :         mCacheEntry = aEntry;
    2313            1583 :         mCacheAccess = aAccess;
    2314                 :     }
    2315                 : 
    2316            1587 :     if (mCanceled && NS_FAILED(mStatus)) {
    2317              14 :         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
    2318              14 :         return mStatus;
    2319                 :     }
    2320                 : 
    2321            1573 :     if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(aEntryStatus))
    2322                 :         // if this channel is only allowed to pull from the cache, then
    2323                 :         // we must fail if we were unable to open a cache entry.
    2324               4 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
    2325                 : 
    2326                 :     // advance to the next state...
    2327            1569 :     return Connect(false);
    2328                 : }
    2329                 : 
    2330                 : 
    2331                 : nsresult
    2332              24 : nsHttpChannel::OpenOfflineCacheEntryForWriting()
    2333                 : {
    2334                 :     nsresult rv;
    2335                 : 
    2336              24 :     LOG(("nsHttpChannel::OpenOfflineCacheEntryForWriting [this=%p]", this));
    2337                 : 
    2338                 :     // make sure we're not abusing this function
    2339              24 :     NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open");
    2340                 : 
    2341              24 :     bool offline = gIOService->IsOffline();
    2342              24 :     if (offline) {
    2343                 :         // only put things in the offline cache while online
    2344               0 :         return NS_OK;
    2345                 :     }
    2346                 : 
    2347              24 :     if (mRequestHead.Method() != nsHttp::Get) {
    2348                 :         // only cache complete documents offline
    2349               0 :         return NS_OK;
    2350                 :     }
    2351                 : 
    2352                 :     // Don't cache byte range requests which are subranges, only cache 0-
    2353                 :     // byte range requests.
    2354              24 :     if (IsSubRangeRequest(mRequestHead))
    2355               0 :         return NS_OK;
    2356                 : 
    2357              48 :     nsCAutoString cacheKey;
    2358              24 :     GenerateCacheKey(mPostID, cacheKey);
    2359                 : 
    2360              24 :     NS_ENSURE_TRUE(!mOfflineCacheClientID.IsEmpty(),
    2361                 :                    NS_ERROR_NOT_AVAILABLE);
    2362                 : 
    2363              48 :     nsCOMPtr<nsICacheSession> session;
    2364                 :     nsCOMPtr<nsICacheService> serv =
    2365              48 :         do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
    2366              24 :     if (NS_FAILED(rv)) return rv;
    2367                 : 
    2368              24 :     rv = serv->CreateSession(mOfflineCacheClientID.get(),
    2369                 :                              nsICache::STORE_OFFLINE,
    2370                 :                              nsICache::STREAM_BASED,
    2371              24 :                              getter_AddRefs(session));
    2372              24 :     if (NS_FAILED(rv)) return rv;
    2373                 : 
    2374              24 :     rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
    2375              24 :                                  false, getter_AddRefs(mOfflineCacheEntry));
    2376                 : 
    2377              24 :     if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
    2378                 :         // access to the cache entry has been denied (because the cache entry
    2379                 :         // is probably in use by another channel).  Either the cache is being
    2380                 :         // read from (we're offline) or it's being updated elsewhere.
    2381               0 :         return NS_OK;
    2382                 :     }
    2383                 : 
    2384              24 :     if (NS_SUCCEEDED(rv)) {
    2385              24 :         mOfflineCacheEntry->GetAccessGranted(&mOfflineCacheAccess);
    2386              24 :         LOG(("got offline cache entry [access=%x]\n", mOfflineCacheAccess));
    2387                 :     }
    2388                 : 
    2389              24 :     return rv;
    2390                 : }
    2391                 : 
    2392                 : // Generates the proper cache-key for this instance of nsHttpChannel
    2393                 : nsresult
    2394            5529 : nsHttpChannel::GenerateCacheKey(PRUint32 postID, nsACString &cacheKey)
    2395                 : {
    2396            5529 :     AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
    2397           11058 :                      postID, cacheKey);
    2398            5529 :     return NS_OK;
    2399                 : }
    2400                 : 
    2401                 : // Assembles a cache-key from the given pieces of information and |mLoadFlags|
    2402                 : void
    2403            5567 : nsHttpChannel::AssembleCacheKey(const char *spec, PRUint32 postID,
    2404                 :                                 nsACString &cacheKey)
    2405                 : {
    2406            5567 :     cacheKey.Truncate();
    2407                 : 
    2408            5567 :     if (mLoadFlags & LOAD_ANONYMOUS) {
    2409               2 :         cacheKey.AssignLiteral("anon&");
    2410                 :     }
    2411                 : 
    2412            5567 :     if (postID) {
    2413                 :         char buf[32];
    2414             190 :         PR_snprintf(buf, sizeof(buf), "id=%x&", postID);
    2415             190 :         cacheKey.Append(buf);
    2416                 :     }
    2417                 : 
    2418            5567 :     if (!cacheKey.IsEmpty()) {
    2419             192 :         cacheKey.AppendLiteral("uri=");
    2420                 :     }
    2421                 : 
    2422                 :     // Strip any trailing #ref from the URL before using it as the key
    2423            5567 :     const char *p = strchr(spec, '#');
    2424            5567 :     if (p)
    2425               0 :         cacheKey.Append(spec, p - spec);
    2426                 :     else
    2427            5567 :         cacheKey.Append(spec);
    2428            5567 : }
    2429                 : 
    2430                 : // UpdateExpirationTime is called when a new response comes in from the server.
    2431                 : // It updates the stored response-time and sets the expiration time on the
    2432                 : // cache entry.  
    2433                 : //
    2434                 : // From section 13.2.4 of RFC2616, we compute expiration time as follows:
    2435                 : //
    2436                 : //    timeRemaining = freshnessLifetime - currentAge
    2437                 : //    expirationTime = now + timeRemaining
    2438                 : // 
    2439                 : nsresult
    2440            1246 : nsHttpChannel::UpdateExpirationTime()
    2441                 : {
    2442            1246 :     NS_ENSURE_TRUE(mResponseHead, NS_ERROR_FAILURE);
    2443                 : 
    2444                 :     nsresult rv;
    2445                 : 
    2446            1246 :     PRUint32 expirationTime = 0;
    2447            1246 :     if (!mResponseHead->MustValidate()) {
    2448             851 :         PRUint32 freshnessLifetime = 0;
    2449                 : 
    2450             851 :         rv = mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
    2451             851 :         if (NS_FAILED(rv)) return rv;
    2452                 : 
    2453             851 :         if (freshnessLifetime > 0) {
    2454             613 :             PRUint32 now = NowInSeconds(), currentAge = 0;
    2455                 : 
    2456             613 :             rv = mResponseHead->ComputeCurrentAge(now, mRequestTime, &currentAge); 
    2457             613 :             if (NS_FAILED(rv)) return rv;
    2458                 : 
    2459             613 :             LOG(("freshnessLifetime = %u, currentAge = %u\n",
    2460                 :                 freshnessLifetime, currentAge));
    2461                 : 
    2462             613 :             if (freshnessLifetime > currentAge) {
    2463             612 :                 PRUint32 timeRemaining = freshnessLifetime - currentAge;
    2464                 :                 // be careful... now + timeRemaining may overflow
    2465             612 :                 if (now + timeRemaining < now)
    2466              76 :                     expirationTime = PRUint32(-1);
    2467                 :                 else
    2468             536 :                     expirationTime = now + timeRemaining;
    2469                 :             }
    2470                 :             else
    2471               1 :                 expirationTime = now;
    2472                 :         }
    2473                 :     }
    2474                 : 
    2475            1246 :     rv = mCacheEntry->SetExpirationTime(expirationTime);
    2476            1246 :     NS_ENSURE_SUCCESS(rv, rv);
    2477                 : 
    2478            1246 :     if (mOfflineCacheEntry) {
    2479              24 :         rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
    2480              24 :         NS_ENSURE_SUCCESS(rv, rv);
    2481                 :     }
    2482                 : 
    2483            1246 :     return NS_OK;
    2484                 : }
    2485                 : 
    2486                 : // CheckCache is called from Connect after a cache entry has been opened for
    2487                 : // this URL but before going out to net.  It's purpose is to set or clear the 
    2488                 : // mCachedContentIsValid flag, and to configure an If-Modified-Since request
    2489                 : // if validation is required.
    2490                 : nsresult
    2491            1616 : nsHttpChannel::CheckCache()
    2492                 : {
    2493            1616 :     nsresult rv = NS_OK;
    2494                 : 
    2495            1616 :     LOG(("nsHTTPChannel::CheckCache enter [this=%p entry=%p access=%d]",
    2496                 :         this, mCacheEntry.get(), mCacheAccess));
    2497                 :     
    2498                 :     // Be pessimistic: assume the cache entry has no useful data.
    2499            1616 :     mCachedContentIsValid = false;
    2500                 : 
    2501                 :     // Don't proceed unless we have opened a cache entry for reading.
    2502            1616 :     if (!mCacheEntry || !(mCacheAccess & nsICache::ACCESS_READ))
    2503            1043 :         return NS_OK;
    2504                 : 
    2505            1146 :     nsXPIDLCString buf;
    2506                 : 
    2507                 :     // Get the method that was used to generate the cached response
    2508             573 :     rv = mCacheEntry->GetMetaDataElement("request-method", getter_Copies(buf));
    2509             573 :     NS_ENSURE_SUCCESS(rv, rv);
    2510                 : 
    2511             573 :     nsHttpAtom method = nsHttp::ResolveAtom(buf);
    2512             573 :     if (method == nsHttp::Head) {
    2513                 :         // The cached response does not contain an entity.  We can only reuse
    2514                 :         // the response if the current request is also HEAD.
    2515               6 :         if (mRequestHead.Method() != nsHttp::Head)
    2516               6 :             return NS_OK;
    2517                 :     }
    2518             567 :     buf.Adopt(0);
    2519                 : 
    2520                 :     // We'll need this value in later computations...
    2521                 :     PRUint32 lastModifiedTime;
    2522             567 :     rv = mCacheEntry->GetLastModified(&lastModifiedTime);
    2523             567 :     NS_ENSURE_SUCCESS(rv, rv);
    2524                 : 
    2525                 :     // Determine if this is the first time that this cache entry
    2526                 :     // has been accessed during this session.
    2527                 :     bool fromPreviousSession =
    2528             567 :             (gHttpHandler->SessionStartTime() > lastModifiedTime);
    2529                 : 
    2530                 :     // Get the cached HTTP response headers
    2531             567 :     rv = mCacheEntry->GetMetaDataElement("response-head", getter_Copies(buf));
    2532             567 :     NS_ENSURE_SUCCESS(rv, rv);
    2533                 : 
    2534                 :     // Parse the cached HTTP response headers
    2535             567 :     mCachedResponseHead = new nsHttpResponseHead();
    2536             567 :     if (!mCachedResponseHead)
    2537               0 :         return NS_ERROR_OUT_OF_MEMORY;
    2538             567 :     rv = mCachedResponseHead->Parse((char *) buf.get());
    2539             567 :     NS_ENSURE_SUCCESS(rv, rv);
    2540             567 :     buf.Adopt(0);
    2541                 : 
    2542                 :     // Don't bother to validate items that are read-only,
    2543                 :     // unless they are read-only because of INHIBIT_CACHING or because
    2544                 :     // we're updating the offline cache.
    2545                 :     // Don't bother to validate if this is a fallback entry.
    2546             577 :     if (!mCacheForOfflineUse &&
    2547                 :         (mLoadedFromApplicationCache ||
    2548                 :          (mCacheAccess == nsICache::ACCESS_READ &&
    2549              10 :           !(mLoadFlags & INHIBIT_CACHING)) ||
    2550                 :          mFallbackChannel)) {
    2551              14 :         mCachedContentIsValid = true;
    2552              14 :         return NS_OK;
    2553                 :     }
    2554                 : 
    2555             553 :     PRUint16 isCachedRedirect = mCachedResponseHead->Status()/100 == 3;
    2556                 : 
    2557                 :     mCustomConditionalRequest =
    2558             553 :         mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
    2559             553 :         mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
    2560             553 :         mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
    2561             553 :         mRequestHead.PeekHeader(nsHttp::If_Match) ||
    2562            2212 :         mRequestHead.PeekHeader(nsHttp::If_Range);
    2563                 : 
    2564             553 :     if (method != nsHttp::Head && !isCachedRedirect) {
    2565                 :         // If the cached content-length is set and it does not match the data
    2566                 :         // size of the cached content, then the cached response is partial...
    2567                 :         // either we need to issue a byte range request or we need to refetch
    2568                 :         // the entire document.
    2569             532 :         PRInt64 contentLength = mCachedResponseHead->ContentLength();
    2570             532 :         if (contentLength != PRInt64(-1)) {
    2571                 :             PRUint32 size;
    2572             531 :             rv = mCacheEntry->GetDataSize(&size);
    2573             531 :             NS_ENSURE_SUCCESS(rv, rv);
    2574                 : 
    2575             531 :             if (PRInt64(size) != contentLength) {
    2576               8 :                 LOG(("Cached data size does not match the Content-Length header "
    2577                 :                      "[content-length=%lld size=%u]\n", PRInt64(contentLength), size));
    2578                 : 
    2579                 :                 bool hasContentEncoding =
    2580               8 :                     mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding)
    2581               8 :                     != nsnull;
    2582              26 :                 if ((PRInt64(size) < contentLength) &&
    2583                 :                      size > 0 &&
    2584               6 :                      !hasContentEncoding &&
    2585               5 :                      mCachedResponseHead->IsResumable() &&
    2586               4 :                      !mCustomConditionalRequest &&
    2587               3 :                      !mCachedResponseHead->NoStore()) {
    2588                 :                     // looks like a partial entry we can reuse
    2589               2 :                     rv = SetupByteRangeRequest(size);
    2590               2 :                     NS_ENSURE_SUCCESS(rv, rv);
    2591               2 :                     mCachedContentIsPartial = true;
    2592                 :                 }
    2593               8 :                 return NS_OK;
    2594                 :             }
    2595                 :         }
    2596                 :     }
    2597                 : 
    2598             545 :     bool doValidation = false;
    2599             545 :     bool canAddImsHeader = true;
    2600                 : 
    2601                 :     // Cached entry is not the entity we request (see bug #633743)
    2602             545 :     if (ResponseWouldVary()) {
    2603              12 :         LOG(("Validating based on Vary headers returning TRUE\n"));
    2604              12 :         canAddImsHeader = false;
    2605              12 :         doValidation = true;
    2606                 :     }
    2607                 :     // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used
    2608             533 :     else if (mLoadFlags & LOAD_FROM_CACHE) {
    2609              10 :         LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));
    2610              10 :         doValidation = false;
    2611                 :     }
    2612                 :     // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until
    2613                 :     // it's revalidated with the server.
    2614             523 :     else if (mLoadFlags & VALIDATE_ALWAYS) {
    2615               3 :         LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));
    2616               3 :         doValidation = true;
    2617                 :     }
    2618                 :     // Even if the VALIDATE_NEVER flag is set, there are still some cases in
    2619                 :     // which we must validate the cached response with the server.
    2620             520 :     else if (mLoadFlags & VALIDATE_NEVER) {
    2621               5 :         LOG(("VALIDATE_NEVER set\n"));
    2622                 :         // if no-store or if no-cache and ssl, validate cached response (see
    2623                 :         // bug 112564 for an explanation of this logic)
    2624              10 :         if (mCachedResponseHead->NoStore() ||
    2625               5 :            (mCachedResponseHead->NoCache() && mConnectionInfo->UsingSSL())) {
    2626               1 :             LOG(("Validating based on (no-store || (no-cache && ssl)) logic\n"));
    2627               1 :             doValidation = true;
    2628                 :         }
    2629                 :         else {
    2630               4 :             LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));
    2631               4 :             doValidation = false;
    2632                 :         }
    2633                 :     }
    2634                 :     // check if validation is strictly required...
    2635             515 :     else if (mCachedResponseHead->MustValidate()) {
    2636             265 :         LOG(("Validating based on MustValidate() returning TRUE\n"));
    2637             265 :         doValidation = true;
    2638                 :     }
    2639                 : 
    2640             250 :     else if (MustValidateBasedOnQueryUrl()) {
    2641               6 :         LOG(("Validating based on RFC 2616 section 13.9 "
    2642                 :              "(query-url w/o explicit expiration-time)\n"));
    2643               6 :         doValidation = true;
    2644                 :     }
    2645                 :     // Check if the cache entry has expired...
    2646                 :     else {
    2647             244 :         PRUint32 time = 0; // a temporary variable for storing time values...
    2648                 : 
    2649             244 :         rv = mCacheEntry->GetExpirationTime(&time);
    2650             244 :         NS_ENSURE_SUCCESS(rv, rv);
    2651                 : 
    2652             244 :         if (NowInSeconds() <= time)
    2653             194 :             doValidation = false;
    2654              50 :         else if (mCachedResponseHead->MustValidateIfExpired())
    2655               0 :             doValidation = true;
    2656              50 :         else if (mLoadFlags & VALIDATE_ONCE_PER_SESSION) {
    2657                 :             // If the cached response does not include expiration infor-
    2658                 :             // mation, then we must validate the response, despite whether
    2659                 :             // or not this is the first access this session.  This behavior
    2660                 :             // is consistent with existing browsers and is generally expected
    2661                 :             // by web authors.
    2662               0 :             rv = mCachedResponseHead->ComputeFreshnessLifetime(&time);
    2663               0 :             NS_ENSURE_SUCCESS(rv, rv);
    2664                 : 
    2665               0 :             if (time == 0)
    2666               0 :                 doValidation = true;
    2667                 :             else
    2668               0 :                 doValidation = fromPreviousSession;
    2669                 :         }
    2670                 :         else
    2671              50 :             doValidation = true;
    2672                 : 
    2673             244 :         LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v"));
    2674                 :     }
    2675                 : 
    2676             545 :     if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) &&
    2677               0 :         (method == nsHttp::Get || method == nsHttp::Head)) {
    2678                 :         const char *requestedETag, *cachedETag;
    2679               0 :         cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
    2680               0 :         requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match);
    2681               0 :         if (cachedETag && (!strncmp(cachedETag, "W/", 2) ||
    2682               0 :             strcmp(requestedETag, cachedETag))) {
    2683                 :             // User has defined If-Match header, if the cached entry is not 
    2684                 :             // matching the provided header value or the cached ETag is weak,
    2685                 :             // force validation.
    2686               0 :             doValidation = true;
    2687                 :         }
    2688                 :     }
    2689                 : 
    2690             545 :     if (!doValidation) {
    2691                 :         //
    2692                 :         // Check the authorization headers used to generate the cache entry.
    2693                 :         // We must validate the cache entry if:
    2694                 :         //
    2695                 :         // 1) the cache entry was generated prior to this session w/
    2696                 :         //    credentials (see bug 103402).
    2697                 :         // 2) the cache entry was generated w/o credentials, but would now
    2698                 :         //    require credentials (see bug 96705).
    2699                 :         //
    2700                 :         // NOTE: this does not apply to proxy authentication.
    2701                 :         //
    2702             208 :         mCacheEntry->GetMetaDataElement("auth", getter_Copies(buf));
    2703                 :         doValidation =
    2704               0 :             (fromPreviousSession && !buf.IsEmpty()) ||
    2705             208 :             (buf.IsEmpty() && mRequestHead.PeekHeader(nsHttp::Authorization));
    2706                 :     }
    2707                 : 
    2708                 :     // Bug #561276: We maintain a chain of cache-keys which returns cached
    2709                 :     // 3xx-responses (redirects) in order to detect cycles. If a cycle is
    2710                 :     // found, ignore the cached response and hit the net. Otherwise, use
    2711                 :     // the cached response and add the cache-key to the chain. Note that
    2712                 :     // a limited number of redirects (cached or not) is allowed and is
    2713                 :     // enforced independently of this mechanism
    2714             545 :     if (!doValidation && isCachedRedirect) {
    2715              26 :         nsCAutoString cacheKey;
    2716              13 :         GenerateCacheKey(mPostID, cacheKey);
    2717                 : 
    2718              13 :         if (!mRedirectedCachekeys)
    2719              10 :             mRedirectedCachekeys = new nsTArray<nsCString>();
    2720               3 :         else if (mRedirectedCachekeys->Contains(cacheKey))
    2721               2 :             doValidation = true;
    2722                 : 
    2723              13 :         LOG(("Redirection-chain %s key %s\n",
    2724                 :              doValidation ? "contains" : "does not contain", cacheKey.get()));
    2725                 : 
    2726                 :         // Append cacheKey if not in the chain already
    2727              13 :         if (!doValidation)
    2728              11 :             mRedirectedCachekeys->AppendElement(cacheKey);
    2729                 :     }
    2730                 : 
    2731             545 :     mCachedContentIsValid = !doValidation;
    2732                 : 
    2733             545 :     if (doValidation) {
    2734                 :         //
    2735                 :         // now, we are definitely going to issue a HTTP request to the server.
    2736                 :         // make it conditional if possible.
    2737                 :         //
    2738                 :         // do not attempt to validate no-store content, since servers will not
    2739                 :         // expect it to be cached.  (we only keep it in our cache for the
    2740                 :         // purposes of back/forward, etc.)
    2741                 :         //
    2742                 :         // the request method MUST be either GET or HEAD (see bug 175641).
    2743                 :         //
    2744                 :         // do not override conditional headers when consumer has defined its own
    2745            1023 :         if (!mCachedResponseHead->NoStore() &&
    2746             335 :             (mRequestHead.Method() == nsHttp::Get ||
    2747              14 :              mRequestHead.Method() == nsHttp::Head) &&
    2748             335 :              !mCustomConditionalRequest) {
    2749                 :             const char *val;
    2750                 :             // Add If-Modified-Since header if a Last-Modified was given
    2751                 :             // and we are allowed to do this (see bugs 510359 and 269303)
    2752             335 :             if (canAddImsHeader) {
    2753             324 :                 val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
    2754             324 :                 if (val)
    2755                 :                     mRequestHead.SetHeader(nsHttp::If_Modified_Since,
    2756               6 :                                            nsDependentCString(val));
    2757                 :             }
    2758                 :             // Add If-None-Match header if an ETag was given in the response
    2759             335 :             val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
    2760             335 :             if (val)
    2761                 :                 mRequestHead.SetHeader(nsHttp::If_None_Match,
    2762               9 :                                        nsDependentCString(val));
    2763             335 :             mDidReval = true;
    2764                 :         }
    2765                 :     }
    2766                 : 
    2767             545 :     LOG(("nsHTTPChannel::CheckCache exit [this=%p doValidation=%d]\n", this, doValidation));
    2768             545 :     return NS_OK;
    2769                 : }
    2770                 : 
    2771                 : bool
    2772             250 : nsHttpChannel::MustValidateBasedOnQueryUrl()
    2773                 : {
    2774                 :     // RFC 2616, section 13.9 states that GET-requests with a query-url
    2775                 :     // MUST NOT be treated as fresh unless the server explicitly provides
    2776                 :     // an expiration-time in the response. See bug #468594
    2777                 :     // Section 13.2.1 (6th paragraph) defines "explicit expiration time"
    2778             250 :     if (mRequestHead.Method() == nsHttp::Get)
    2779                 :     {
    2780             472 :         nsCAutoString query;
    2781             472 :         nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
    2782             236 :         nsresult rv = url->GetQuery(query);
    2783             236 :         if (NS_SUCCEEDED(rv) && !query.IsEmpty()) {
    2784                 :             PRUint32 tmp; // we don't need the value, just whether it's set
    2785              16 :             rv = mCachedResponseHead->GetExpiresValue(&tmp);
    2786              16 :             if (NS_FAILED(rv)) {
    2787              11 :                 rv = mCachedResponseHead->GetMaxAgeValue(&tmp);
    2788              11 :                 if (NS_FAILED(rv)) {
    2789               6 :                     return true;
    2790                 :                 }
    2791                 :             }
    2792                 :         }
    2793                 :     }
    2794             244 :     return false;
    2795                 : }
    2796                 : 
    2797                 : 
    2798                 : nsresult
    2799              24 : nsHttpChannel::ShouldUpdateOfflineCacheEntry(bool *shouldCacheForOfflineUse)
    2800                 : {
    2801              24 :     *shouldCacheForOfflineUse = false;
    2802                 : 
    2803              24 :     if (!mOfflineCacheEntry) {
    2804               0 :         return NS_OK;
    2805                 :     }
    2806                 : 
    2807                 :     // if we're updating the cache entry, update the offline cache entry too
    2808              24 :     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE)) {
    2809              24 :         *shouldCacheForOfflineUse = true;
    2810              24 :         return NS_OK;
    2811                 :     }
    2812                 : 
    2813                 :     // if there's nothing in the offline cache, add it
    2814               0 :     if (mOfflineCacheEntry && (mOfflineCacheAccess == nsICache::ACCESS_WRITE)) {
    2815               0 :         *shouldCacheForOfflineUse = true;
    2816               0 :         return NS_OK;
    2817                 :     }
    2818                 : 
    2819                 :     // if the document is newer than the offline entry, update it
    2820                 :     PRUint32 docLastModifiedTime;
    2821               0 :     nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
    2822               0 :     if (NS_FAILED(rv)) {
    2823               0 :         *shouldCacheForOfflineUse = true;
    2824               0 :         return NS_OK;
    2825                 :     }
    2826                 : 
    2827                 :     PRUint32 offlineLastModifiedTime;
    2828               0 :     rv = mOfflineCacheEntry->GetLastModified(&offlineLastModifiedTime);
    2829               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2830                 : 
    2831               0 :     if (docLastModifiedTime > offlineLastModifiedTime) {
    2832               0 :         *shouldCacheForOfflineUse = true;
    2833               0 :         return NS_OK;
    2834                 :     }
    2835                 : 
    2836               0 :     return NS_OK;
    2837                 : }
    2838                 : 
    2839                 : // If the data in the cache hasn't expired, then there's no need to
    2840                 : // talk with the server, not even to do an if-modified-since.  This
    2841                 : // method creates a stream from the cache, synthesizing all the various
    2842                 : // channel-related events.
    2843                 : nsresult
    2844             228 : nsHttpChannel::ReadFromCache()
    2845                 : {
    2846                 :     nsresult rv;
    2847                 : 
    2848             228 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
    2849             228 :     NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE);
    2850                 : 
    2851             228 :     LOG(("nsHttpChannel::ReadFromCache [this=%p] "
    2852                 :          "Using cached copy of: %s\n", this, mSpec.get()));
    2853                 : 
    2854             228 :     if (mCachedResponseHead)
    2855             220 :         mResponseHead = mCachedResponseHead;
    2856                 : 
    2857             228 :     UpdateInhibitPersistentCachingFlag();
    2858                 : 
    2859                 :     // if we don't already have security info, try to get it from the cache 
    2860                 :     // entry. there are two cases to consider here: 1) we are just reading
    2861                 :     // from the cache, or 2) this may be due to a 304 not modified response,
    2862                 :     // in which case we could have security info from a socket transport.
    2863             228 :     if (!mSecurityInfo)
    2864             228 :         mCacheEntry->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
    2865                 : 
    2866             228 :     if ((mCacheAccess & nsICache::ACCESS_WRITE) && !mCachedContentIsPartial) {
    2867                 :         // We have write access to the cache, but we don't need to go to the
    2868                 :         // server to validate at this time, so just mark the cache entry as
    2869                 :         // valid in order to allow others access to this cache entry.
    2870             212 :         mCacheEntry->MarkValid();
    2871                 :     }
    2872                 : 
    2873                 :     // if this is a cached redirect, we must process the redirect asynchronously
    2874                 :     // since AsyncOpen may not have returned yet.  Make sure there is a Location
    2875                 :     // header, otherwise we'll have to treat this like a normal 200 response.
    2876             239 :     if (mResponseHead && (mResponseHead->Status() / 100 == 3) 
    2877              11 :                       && (mResponseHead->PeekHeader(nsHttp::Location)))
    2878              11 :         return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
    2879                 : 
    2880                 :     // have we been configured to skip reading from the cache?
    2881             217 :     if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
    2882                 :         // if offline caching has been requested and the offline cache needs
    2883                 :         // updating, complete the call even if the main cache entry is
    2884                 :         // up-to-date
    2885                 :         bool shouldUpdateOffline;
    2886               0 :         if (!mCacheForOfflineUse ||
    2887               0 :             NS_FAILED(ShouldUpdateOfflineCacheEntry(&shouldUpdateOffline)) ||
    2888               0 :             !shouldUpdateOffline) {
    2889                 : 
    2890               0 :             LOG(("skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
    2891                 :                  "load flag\n"));
    2892               0 :             return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
    2893                 :         }
    2894                 :     }
    2895                 : 
    2896                 :     // open input stream for reading...
    2897             434 :     nsCOMPtr<nsIInputStream> stream;
    2898             217 :     rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(stream));
    2899             217 :     if (NS_FAILED(rv)) return rv;
    2900                 : 
    2901             217 :     rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump),
    2902                 :                                    stream, PRInt64(-1), PRInt64(-1), 0, 0,
    2903             434 :                                    true);
    2904             217 :     if (NS_FAILED(rv)) return rv;
    2905                 : 
    2906             217 :     rv = mCachePump->AsyncRead(this, mListenerContext);
    2907             217 :     if (NS_FAILED(rv)) return rv;
    2908                 : 
    2909             217 :     if (mTimingEnabled)
    2910               0 :         mCacheReadStart = mozilla::TimeStamp::Now();
    2911                 : 
    2912             217 :     PRUint32 suspendCount = mSuspendCount;
    2913             434 :     while (suspendCount--)
    2914               0 :         mCachePump->Suspend();
    2915                 : 
    2916             217 :     return NS_OK;
    2917                 : }
    2918                 : 
    2919                 : void
    2920            1658 : nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
    2921                 : {
    2922            1658 :     if (!mCacheEntry)
    2923              28 :         return;
    2924                 : 
    2925            1630 :     LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%x mCacheAccess=%x",
    2926                 :          this, mStatus, mCacheAccess));
    2927                 : 
    2928                 :     // If we have begun to create or replace a cache entry, and that cache
    2929                 :     // entry is not complete and not resumable, then it needs to be doomed.
    2930                 :     // Otherwise, CheckCache will make the mistake of thinking that the
    2931                 :     // partial cache entry is complete.
    2932                 : 
    2933            1630 :     bool doom = false;
    2934            1630 :     if (mInitedCacheEntry) {
    2935            1238 :         NS_ASSERTION(mResponseHead, "oops");
    2936            1265 :         if (NS_FAILED(mStatus) && doomOnFailure &&
    2937                 :             (mCacheAccess & nsICache::ACCESS_WRITE) &&
    2938              27 :             !mResponseHead->IsResumable())
    2939              25 :             doom = true;
    2940                 :     }
    2941             392 :     else if (mCacheAccess == nsICache::ACCESS_WRITE)
    2942             156 :         doom = true;
    2943                 : 
    2944            1630 :     if (doom) {
    2945             181 :         LOG(("  dooming cache entry!!"));
    2946             181 :         mCacheEntry->Doom();
    2947                 :     }
    2948                 : 
    2949            1630 :     mCachedResponseHead = nsnull;
    2950                 : 
    2951            1630 :     mCachePump = 0;
    2952            1630 :     mCacheEntry = 0;
    2953            1630 :     mCacheAccess = 0;
    2954            1630 :     mInitedCacheEntry = false;
    2955                 : }
    2956                 : 
    2957                 : 
    2958                 : void
    2959              24 : nsHttpChannel::CloseOfflineCacheEntry()
    2960                 : {
    2961              24 :     if (!mOfflineCacheEntry)
    2962               0 :         return;
    2963                 : 
    2964              24 :     LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]", this));
    2965                 : 
    2966              24 :     if (NS_FAILED(mStatus)) {
    2967               0 :         mOfflineCacheEntry->Doom();
    2968                 :     }
    2969                 :     else {
    2970                 :         bool succeeded;
    2971              24 :         if (NS_SUCCEEDED(GetRequestSucceeded(&succeeded)) && !succeeded)
    2972               0 :             mOfflineCacheEntry->Doom();
    2973                 :     }
    2974                 : 
    2975              24 :     mOfflineCacheEntry = 0;
    2976              24 :     mOfflineCacheAccess = 0;
    2977                 : 
    2978              24 :     if (mCachingOpportunistically) {
    2979                 :         nsCOMPtr<nsIApplicationCacheService> appCacheService =
    2980               0 :             do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
    2981               0 :         if (appCacheService) {
    2982               0 :             nsCAutoString cacheKey;
    2983               0 :             GenerateCacheKey(mPostID, cacheKey);
    2984               0 :             appCacheService->CacheOpportunistically(mApplicationCache,
    2985               0 :                                                     cacheKey);
    2986                 :         }
    2987                 :     }
    2988                 : }
    2989                 : 
    2990                 : 
    2991                 : // Initialize the cache entry for writing.
    2992                 : //  - finalize storage policy
    2993                 : //  - store security info
    2994                 : //  - update expiration time
    2995                 : //  - store headers and other meta data
    2996                 : nsresult
    2997            1262 : nsHttpChannel::InitCacheEntry()
    2998                 : {
    2999                 :     nsresult rv;
    3000                 : 
    3001            1262 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED);
    3002                 :     // if only reading, nothing to be done here.
    3003            1238 :     if (mCacheAccess == nsICache::ACCESS_READ)
    3004               0 :         return NS_OK;
    3005                 : 
    3006                 :     // Don't cache the response again if already cached...
    3007            1238 :     if (mCachedContentIsValid)
    3008               0 :         return NS_OK;
    3009                 : 
    3010            1238 :     LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n",
    3011                 :         this, mCacheEntry.get()));
    3012                 : 
    3013            1238 :     if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
    3014               5 :         rv = mCacheEntry->SetStoragePolicy(nsICache::STORE_IN_MEMORY);
    3015               5 :         if (NS_FAILED(rv)) return rv;
    3016                 :     }
    3017                 : 
    3018                 :     // Set the expiration time for this cache entry
    3019            1238 :     rv = UpdateExpirationTime();
    3020            1238 :     if (NS_FAILED(rv)) return rv;
    3021                 : 
    3022            1238 :     rv = AddCacheEntryHeaders(mCacheEntry);
    3023            1238 :     if (NS_FAILED(rv)) return rv;
    3024                 : 
    3025            1238 :     mInitedCacheEntry = true;
    3026            1238 :     return NS_OK;
    3027                 : }
    3028                 : 
    3029                 : void
    3030            2894 : nsHttpChannel::UpdateInhibitPersistentCachingFlag()
    3031                 : {
    3032                 :     // The no-store directive within the 'Cache-Control:' header indicates
    3033                 :     // that we must not store the response in a persistent cache.
    3034            2894 :     if (mResponseHead->NoStore())
    3035               6 :         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
    3036                 : 
    3037                 :     // Only cache SSL content on disk if the pref is set
    3038            2894 :     if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
    3039               0 :         mConnectionInfo->UsingSSL())
    3040               0 :         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
    3041            2894 : }
    3042                 : 
    3043                 : nsresult
    3044              24 : nsHttpChannel::InitOfflineCacheEntry()
    3045                 : {
    3046                 :     // This function can be called even when we fail to connect (bug 551990)
    3047                 : 
    3048              24 :     if (!mOfflineCacheEntry) {
    3049               0 :         return NS_OK;
    3050                 :     }
    3051                 : 
    3052              24 :     if (mResponseHead && mResponseHead->NoStore()) {
    3053               0 :         CloseOfflineCacheEntry();
    3054                 : 
    3055               0 :         return NS_OK;
    3056                 :     }
    3057                 : 
    3058                 :     // This entry's expiration time should match the main entry's expiration
    3059                 :     // time.  UpdateExpirationTime() will keep it in sync once the offline
    3060                 :     // cache entry has been created.
    3061              24 :     if (mCacheEntry) {
    3062                 :         PRUint32 expirationTime;
    3063              24 :         nsresult rv = mCacheEntry->GetExpirationTime(&expirationTime);
    3064              24 :         NS_ENSURE_SUCCESS(rv, rv);
    3065                 : 
    3066              24 :         mOfflineCacheEntry->SetExpirationTime(expirationTime);
    3067                 :     }
    3068                 : 
    3069              24 :     return AddCacheEntryHeaders(mOfflineCacheEntry);
    3070                 : }
    3071                 : 
    3072                 : 
    3073                 : nsresult
    3074            1268 : nsHttpChannel::AddCacheEntryHeaders(nsICacheEntryDescriptor *entry)
    3075                 : {
    3076                 :     nsresult rv;
    3077                 : 
    3078            1268 :     LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%x] begin", this));
    3079                 :     // Store secure data in memory only
    3080            1268 :     if (mSecurityInfo)
    3081               4 :         entry->SetSecurityInfo(mSecurityInfo);
    3082                 : 
    3083                 :     // Store the HTTP request method with the cache entry so we can distinguish
    3084                 :     // for example GET and HEAD responses.
    3085                 :     rv = entry->SetMetaDataElement("request-method",
    3086            1268 :                                    mRequestHead.Method().get());
    3087            1268 :     if (NS_FAILED(rv)) return rv;
    3088                 : 
    3089                 :     // Store the HTTP authorization scheme used if any...
    3090            1268 :     rv = StoreAuthorizationMetaData(entry);
    3091            1268 :     if (NS_FAILED(rv)) return rv;
    3092                 : 
    3093                 :     // Iterate over the headers listed in the Vary response header, and
    3094                 :     // store the value of the corresponding request header so we can verify
    3095                 :     // that it has not varied when we try to re-use the cached response at
    3096                 :     // a later time.  Take care to store "Cookie" headers only as hashes
    3097                 :     // due to security considerations and the fact that they can be pretty
    3098                 :     // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.
    3099                 :     //
    3100                 :     // NOTE: if "Vary: accept, cookie", then we will store the "accept" header
    3101                 :     // in the cache.  we could try to avoid needlessly storing the "accept"
    3102                 :     // header in this case, but it doesn't seem worth the extra code to perform
    3103                 :     // the check.
    3104                 :     {
    3105            2536 :         nsCAutoString buf, metaKey;
    3106            1268 :         mResponseHead->GetHeader(nsHttp::Vary, buf);
    3107            1268 :         if (!buf.IsEmpty()) {
    3108              40 :             NS_NAMED_LITERAL_CSTRING(prefix, "request-");
    3109                 :            
    3110              20 :             char *val = buf.BeginWriting(); // going to munge buf
    3111              20 :             char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
    3112              64 :             while (token) {
    3113              24 :                 LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%x] " \
    3114                 :                         "processing %s", this, token));
    3115              24 :                 if (*token != '*') {
    3116              24 :                     nsHttpAtom atom = nsHttp::ResolveAtom(token);
    3117              24 :                     const char *val = mRequestHead.PeekHeader(atom);
    3118              48 :                     nsCAutoString hash;
    3119              24 :                     if (val) {
    3120                 :                         // If cookie-header, store a hash of the value
    3121              18 :                         if (atom == nsHttp::Cookie) {
    3122               4 :                             LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%x] " \
    3123                 :                                     "cookie-value %s", this, val));
    3124               4 :                             rv = Hash(val, hash);
    3125                 :                             // If hash failed, store a string not very likely
    3126                 :                             // to be the result of subsequent hashes
    3127               4 :                             if (NS_FAILED(rv))
    3128               0 :                                 val = "<hash failed>";
    3129                 :                             else
    3130               4 :                                 val = hash.get();
    3131                 : 
    3132               4 :                             LOG(("   hashed to %s\n", val));
    3133                 :                         }
    3134                 : 
    3135                 :                         // build cache meta data key and set meta data element...
    3136              18 :                         metaKey = prefix + nsDependentCString(token);
    3137              18 :                         entry->SetMetaDataElement(metaKey.get(), val);
    3138                 :                     } else {
    3139               6 :                         LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%x] " \
    3140                 :                                 "clearing metadata for %s", this, token));
    3141               6 :                         metaKey = prefix + nsDependentCString(token);
    3142               6 :                         entry->SetMetaDataElement(metaKey.get(), nsnull);
    3143                 :                     }
    3144                 :                 }
    3145              24 :                 token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
    3146                 :             }
    3147                 :         }
    3148                 :     }
    3149                 : 
    3150                 : 
    3151                 :     // Store the received HTTP head with the cache entry as an element of
    3152                 :     // the meta data.
    3153            2536 :     nsCAutoString head;
    3154            1268 :     mResponseHead->Flatten(head, true);
    3155            1268 :     rv = entry->SetMetaDataElement("response-head", head.get());
    3156                 : 
    3157            1268 :     return rv;
    3158                 : }
    3159                 : 
    3160                 : inline void
    3161              65 : GetAuthType(const char *challenge, nsCString &authType)
    3162                 : {
    3163                 :     const char *p;
    3164                 : 
    3165                 :     // get the challenge type
    3166              65 :     if ((p = strchr(challenge, ' ')) != nsnull)
    3167              64 :         authType.Assign(challenge, p - challenge);
    3168                 :     else
    3169               1 :         authType.Assign(challenge);
    3170              65 : }
    3171                 : 
    3172                 : nsresult
    3173            1268 : nsHttpChannel::StoreAuthorizationMetaData(nsICacheEntryDescriptor *entry)
    3174                 : {
    3175                 :     // Not applicable to proxy authorization...
    3176            1268 :     const char *val = mRequestHead.PeekHeader(nsHttp::Authorization);
    3177            1268 :     if (!val)
    3178            1259 :         return NS_OK;
    3179                 : 
    3180                 :     // eg. [Basic realm="wally world"]
    3181              18 :     nsCAutoString buf;
    3182               9 :     GetAuthType(val, buf);
    3183               9 :     return entry->SetMetaDataElement("auth", buf.get());
    3184                 : }
    3185                 : 
    3186                 : // Finalize the cache entry
    3187                 : //  - may need to rewrite response headers if any headers changed
    3188                 : //  - may need to recalculate the expiration time if any headers changed
    3189                 : //  - called only for freshly written cache entries
    3190                 : nsresult
    3191            1272 : nsHttpChannel::FinalizeCacheEntry()
    3192                 : {
    3193            1272 :     LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this));
    3194                 : 
    3195            1272 :     if (mResponseHead && mResponseHeadersModified) {
    3196                 :         // Set the expiration time for this cache entry
    3197               0 :         nsresult rv = UpdateExpirationTime();
    3198               0 :         if (NS_FAILED(rv)) return rv;
    3199                 :     }
    3200            1272 :     return NS_OK;
    3201                 : }
    3202                 : 
    3203                 : // Open an output stream to the cache entry and insert a listener tee into
    3204                 : // the chain of response listeners.
    3205                 : nsresult
    3206            1134 : nsHttpChannel::InstallCacheListener(PRUint32 offset)
    3207                 : {
    3208                 :     nsresult rv;
    3209                 : 
    3210            1134 :     LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
    3211                 : 
    3212            1134 :     NS_ASSERTION(mCacheEntry, "no cache entry");
    3213            1134 :     NS_ASSERTION(mListener, "no listener");
    3214                 : 
    3215                 :     nsCacheStoragePolicy policy;
    3216            1134 :     rv = mCacheEntry->GetStoragePolicy(&policy);
    3217            1134 :     if (NS_FAILED(rv)) {
    3218               0 :         policy = nsICache::STORE_ON_DISK_AS_FILE;
    3219                 :     }
    3220                 : 
    3221                 :     // If the content is compressible and the server has not compressed it,
    3222                 :     // mark the cache entry for compression.
    3223            6675 :     if ((mResponseHead->PeekHeader(nsHttp::Content_Encoding) == nsnull) && (
    3224                 :          policy != nsICache::STORE_ON_DISK_AS_FILE) && (
    3225            1131 :          mResponseHead->ContentType().EqualsLiteral(TEXT_HTML) ||
    3226             813 :          mResponseHead->ContentType().EqualsLiteral(TEXT_PLAIN) ||
    3227             667 :          mResponseHead->ContentType().EqualsLiteral(TEXT_CSS) ||
    3228             667 :          mResponseHead->ContentType().EqualsLiteral(TEXT_JAVASCRIPT) ||
    3229             667 :          mResponseHead->ContentType().EqualsLiteral(TEXT_ECMASCRIPT) ||
    3230             667 :          mResponseHead->ContentType().EqualsLiteral(TEXT_XML) ||
    3231             242 :          mResponseHead->ContentType().EqualsLiteral(APPLICATION_JAVASCRIPT) ||
    3232             229 :          mResponseHead->ContentType().EqualsLiteral(APPLICATION_ECMASCRIPT) ||
    3233             229 :          mResponseHead->ContentType().EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
    3234             229 :          mResponseHead->ContentType().EqualsLiteral(APPLICATION_XHTML_XML))) {
    3235             902 :         rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0"); 
    3236             902 :         if (NS_FAILED(rv)) {
    3237               0 :             LOG(("unable to mark cache entry for compression"));
    3238                 :         }
    3239                 :     } 
    3240                 :       
    3241            2268 :     nsCOMPtr<nsIOutputStream> out;
    3242            1134 :     rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
    3243            1134 :     if (NS_FAILED(rv)) return rv;
    3244                 : 
    3245                 :     // XXX disk cache does not support overlapped i/o yet
    3246                 : #if 0
    3247                 :     // Mark entry valid inorder to allow simultaneous reading...
    3248                 :     rv = mCacheEntry->MarkValid();
    3249                 :     if (NS_FAILED(rv)) return rv;
    3250                 : #endif
    3251                 : 
    3252                 :     nsCOMPtr<nsIStreamListenerTee> tee =
    3253            2268 :         do_CreateInstance(kStreamListenerTeeCID, &rv);
    3254            1134 :     if (NS_FAILED(rv)) return rv;
    3255                 : 
    3256                 :     nsCOMPtr<nsICacheService> serv =
    3257            2268 :         do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
    3258            1134 :     NS_ENSURE_SUCCESS(rv, rv);
    3259                 : 
    3260            2268 :     nsCOMPtr<nsIEventTarget> cacheIOTarget;
    3261            1134 :     serv->GetCacheIOTarget(getter_AddRefs(cacheIOTarget));
    3262                 : 
    3263            2268 :     if (policy == nsICache::STORE_ON_DISK_AS_FILE ||
    3264            1134 :         !cacheIOTarget) {
    3265               0 :         LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%x policy=%d "
    3266                 :              "cacheIOTarget=%p", tee.get(), rv, policy, cacheIOTarget.get()));
    3267               0 :         rv = tee->Init(mListener, out, nsnull);
    3268                 :     } else {
    3269            1134 :         LOG(("nsHttpChannel::InstallCacheListener async tee %p", tee.get()));
    3270            1134 :         rv = tee->InitAsync(mListener, cacheIOTarget, out, nsnull);
    3271                 :     }
    3272                 : 
    3273            1134 :     if (NS_FAILED(rv)) return rv;
    3274            1134 :     mListener = tee;
    3275            1134 :     return NS_OK;
    3276                 : }
    3277                 : 
    3278                 : nsresult
    3279              24 : nsHttpChannel::InstallOfflineCacheListener()
    3280                 : {
    3281                 :     nsresult rv;
    3282                 : 
    3283              24 :     LOG(("Preparing to write data into the offline cache [uri=%s]\n",
    3284                 :          mSpec.get()));
    3285                 : 
    3286              24 :     NS_ASSERTION(mOfflineCacheEntry, "no offline cache entry");
    3287              24 :     NS_ASSERTION(mListener, "no listener");
    3288                 : 
    3289              48 :     nsCOMPtr<nsIOutputStream> out;
    3290              24 :     rv = mOfflineCacheEntry->OpenOutputStream(0, getter_AddRefs(out));
    3291              24 :     if (NS_FAILED(rv)) return rv;
    3292                 : 
    3293                 :     nsCOMPtr<nsIStreamListenerTee> tee =
    3294              48 :         do_CreateInstance(kStreamListenerTeeCID, &rv);
    3295              24 :     if (NS_FAILED(rv)) return rv;
    3296                 : 
    3297              24 :     rv = tee->Init(mListener, out, nsnull);
    3298              24 :     if (NS_FAILED(rv)) return rv;
    3299                 : 
    3300              24 :     mListener = tee;
    3301                 : 
    3302              24 :     return NS_OK;
    3303                 : }
    3304                 : 
    3305                 : void
    3306            2660 : nsHttpChannel::ClearBogusContentEncodingIfNeeded()
    3307                 : {
    3308                 :     // For .gz files, apache sends both a Content-Type: application/x-gzip
    3309                 :     // as well as Content-Encoding: gzip, which is completely wrong.  In
    3310                 :     // this case, we choose to ignore the rogue Content-Encoding header. We
    3311                 :     // must do this early on so as to prevent it from being seen up stream.
    3312                 :     // The same problem exists for Content-Encoding: compress in default
    3313                 :     // Apache installs.
    3314            2671 :     if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") && (
    3315               5 :         mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP) ||
    3316               3 :         mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP2) ||
    3317               3 :         mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP3))) {
    3318                 :         // clear the Content-Encoding header
    3319               2 :         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
    3320                 :     }
    3321            2658 :     else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "compress") && (
    3322               0 :              mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS) ||
    3323               0 :              mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS2))) {
    3324                 :         // clear the Content-Encoding header
    3325               0 :         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
    3326                 :     }
    3327            2660 : }
    3328                 : 
    3329                 : //-----------------------------------------------------------------------------
    3330                 : // nsHttpChannel <redirect>
    3331                 : //-----------------------------------------------------------------------------
    3332                 : 
    3333                 : nsresult
    3334             155 : nsHttpChannel::SetupReplacementChannel(nsIURI       *newURI, 
    3335                 :                                        nsIChannel   *newChannel,
    3336                 :                                        bool          preserveMethod,
    3337                 :                                        bool          forProxy)
    3338                 : {
    3339             155 :     LOG(("nsHttpChannel::SetupReplacementChannel "
    3340                 :          "[this=%p newChannel=%p preserveMethod=%d]",
    3341                 :          this, newChannel, preserveMethod));
    3342                 : 
    3343                 :     nsresult rv = HttpBaseChannel::SetupReplacementChannel(newURI, newChannel,
    3344             155 :                                                            preserveMethod, forProxy);
    3345             155 :     if (NS_FAILED(rv))
    3346               0 :         return rv;
    3347                 : 
    3348             310 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
    3349             155 :     if (!httpChannel)
    3350               2 :         return NS_OK; // no other options to set
    3351                 : 
    3352                 :     // convey the mApplyConversion flag (bug 91862)
    3353             306 :     nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
    3354             153 :     if (encodedChannel)
    3355             153 :         encodedChannel->SetApplyConversion(mApplyConversion);
    3356                 : 
    3357                 :     // transfer the resume information
    3358             153 :     if (mResuming) {
    3359               4 :         nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(newChannel));
    3360               2 :         if (!resumableChannel) {
    3361               0 :             NS_WARNING("Got asked to resume, but redirected to non-resumable channel!");
    3362               0 :             return NS_ERROR_NOT_RESUMABLE;
    3363                 :         }
    3364               4 :         resumableChannel->ResumeAt(mStartPos, mEntityID);
    3365                 :     }
    3366                 : 
    3367             153 :     if (forProxy) {
    3368                 :         // Transfer the cache info to the new channel, if needed.
    3369              24 :         nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(newChannel);
    3370              12 :         if (cachingChannel) {
    3371                 :             // cacheKey is just mPostID wrapped in an nsISupportsPRUint32,
    3372                 :             // we don't need to transfer it if it's 0.
    3373              12 :             if (mPostID) {
    3374               2 :                 nsCOMPtr<nsISupports> cacheKey;
    3375               1 :                 GetCacheKey(getter_AddRefs(cacheKey));
    3376               1 :                 if (cacheKey) {
    3377               1 :                     cachingChannel->SetCacheKey(cacheKey);
    3378                 :                 }
    3379                 :             }
    3380                 : 
    3381                 :             // cacheClientID, cacheForOfflineUse
    3382              12 :             cachingChannel->SetOfflineCacheClientID(mOfflineCacheClientID);
    3383              12 :             cachingChannel->SetCacheForOfflineUse(mCacheForOfflineUse);
    3384                 :         }
    3385                 :     }
    3386                 : 
    3387             153 :     return NS_OK;
    3388                 : }
    3389                 : 
    3390                 : nsresult
    3391             156 : nsHttpChannel::AsyncProcessRedirection(PRUint32 redirectType)
    3392                 : {
    3393             156 :     LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",
    3394                 :         this, redirectType));
    3395                 : 
    3396             156 :     const char *location = mResponseHead->PeekHeader(nsHttp::Location);
    3397                 : 
    3398                 :     // if a location header was not given, then we can't perform the redirect,
    3399                 :     // so just carry on as though this were a normal response.
    3400             156 :     if (!location)
    3401               1 :         return NS_ERROR_FAILURE;
    3402                 : 
    3403                 :     // make sure non-ASCII characters in the location header are escaped.
    3404             310 :     nsCAutoString locationBuf;
    3405             155 :     if (NS_EscapeURL(location, -1, esc_OnlyNonASCII, locationBuf))
    3406               0 :         location = locationBuf.get();
    3407                 : 
    3408             155 :     if (mRedirectionLimit == 0) {
    3409               3 :         LOG(("redirection limit reached!\n"));
    3410                 :         // this error code is fatal, and should be conveyed to our listener.
    3411               3 :         Cancel(NS_ERROR_REDIRECT_LOOP);
    3412               3 :         return NS_ERROR_REDIRECT_LOOP;
    3413                 :     }
    3414                 : 
    3415             152 :     mRedirectType = redirectType;
    3416                 : 
    3417             152 :     LOG(("redirecting to: %s [redirection-limit=%u]\n",
    3418                 :         location, PRUint32(mRedirectionLimit)));
    3419                 : 
    3420             152 :     nsresult rv = CreateNewURI(location, getter_AddRefs(mRedirectURI));
    3421                 : 
    3422             152 :     if (NS_FAILED(rv)) return rv;
    3423                 : 
    3424             152 :     if (mApplicationCache) {
    3425                 :         // if we are redirected to a different origin check if there is a fallback
    3426                 :         // cache entry to fall back to. we don't care about file strict 
    3427                 :         // checking, at least mURI is not a file URI.
    3428               2 :         if (!NS_SecurityCompareURIs(mURI, mRedirectURI, false)) {
    3429               2 :             PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
    3430                 :             bool waitingForRedirectCallback;
    3431               2 :             (void)ProcessFallback(&waitingForRedirectCallback);
    3432               2 :             if (waitingForRedirectCallback)
    3433               2 :                 return NS_OK;
    3434               0 :             PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
    3435                 :         }
    3436                 :     }
    3437                 : 
    3438             150 :     return ContinueProcessRedirectionAfterFallback(NS_OK);
    3439                 : }
    3440                 : 
    3441                 : // Creates an URI to the given location using current URI for base and charset
    3442                 : nsresult
    3443             190 : nsHttpChannel::CreateNewURI(const char *loc, nsIURI **newURI)
    3444                 : {
    3445             380 :     nsCOMPtr<nsIIOService> ioService;
    3446             190 :     nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    3447             190 :     if (NS_FAILED(rv)) return rv;
    3448                 : 
    3449                 :     // the new uri should inherit the origin charset of the current uri
    3450             380 :     nsCAutoString originCharset;
    3451             190 :     rv = mURI->GetOriginCharset(originCharset);
    3452             190 :     if (NS_FAILED(rv))
    3453               0 :         originCharset.Truncate();
    3454                 : 
    3455             380 :     return ioService->NewURI(nsDependentCString(loc),
    3456                 :                              originCharset.get(),
    3457                 :                              mURI,
    3458             190 :                              newURI);
    3459                 : }
    3460                 : 
    3461                 : nsresult
    3462             152 : nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
    3463                 : {
    3464             152 :     if (NS_SUCCEEDED(rv) && mFallingBack) {
    3465                 :         // do not continue with redirect processing, fallback is in
    3466                 :         // progress now.
    3467               1 :         return NS_OK;
    3468                 :     }
    3469                 : 
    3470                 :     // Kill the current cache entry if we are redirecting
    3471                 :     // back to ourself.
    3472             151 :     bool redirectingBackToSameURI = false;
    3473             264 :     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) &&
    3474             113 :         NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
    3475                 :         redirectingBackToSameURI)
    3476              60 :             mCacheEntry->Doom();
    3477                 : 
    3478                 :     // move the reference of the old location to the new one if the new
    3479                 :     // one has none.
    3480             302 :     nsCAutoString ref;
    3481             151 :     rv = mRedirectURI->GetRef(ref);
    3482             151 :     if (NS_SUCCEEDED(rv) && ref.IsEmpty()) {
    3483             151 :         mURI->GetRef(ref);
    3484             151 :         if (!ref.IsEmpty()) {
    3485                 :             // NOTE: SetRef will fail if mRedirectURI is immutable
    3486                 :             // (e.g. an about: URI)... Oh well.
    3487               0 :             mRedirectURI->SetRef(ref);
    3488                 :         }
    3489                 :     }
    3490                 : 
    3491                 :     bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(
    3492             151 :         mRedirectType, mRequestHead.Method());
    3493                 :       
    3494                 :     // prompt if the method is not safe (such as POST, PUT, DELETE, ...)
    3495             286 :     if (!rewriteToGET &&
    3496             135 :         !HttpBaseChannel::IsSafeMethod(mRequestHead.Method())) {
    3497              16 :         rv = PromptTempRedirect();
    3498              16 :         if (NS_FAILED(rv)) return rv;
    3499                 :     }
    3500                 : 
    3501             274 :     nsCOMPtr<nsIIOService> ioService;
    3502             137 :     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    3503             137 :     if (NS_FAILED(rv)) return rv;
    3504                 : 
    3505             274 :     nsCOMPtr<nsIChannel> newChannel;
    3506             137 :     rv = ioService->NewChannelFromURI(mRedirectURI, getter_AddRefs(newChannel));
    3507             137 :     if (NS_FAILED(rv)) return rv;
    3508                 : 
    3509             134 :     rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET, false);
    3510             134 :     if (NS_FAILED(rv)) return rv;
    3511                 : 
    3512                 :     PRUint32 redirectFlags;
    3513             134 :     if (mRedirectType == 301) // Moved Permanently
    3514              89 :         redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
    3515                 :     else
    3516              45 :         redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
    3517                 : 
    3518                 :     // verify that this is a legal redirect
    3519             134 :     mRedirectChannel = newChannel;
    3520                 : 
    3521             134 :     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
    3522             134 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
    3523                 : 
    3524             134 :     if (NS_SUCCEEDED(rv))
    3525             134 :         rv = WaitForRedirectCallback();
    3526                 : 
    3527             134 :     if (NS_FAILED(rv)) {
    3528               0 :         AutoRedirectVetoNotifier notifier(this);
    3529               0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
    3530                 :     }
    3531                 : 
    3532             134 :     return rv;
    3533                 : }
    3534                 : 
    3535                 : nsresult
    3536             134 : nsHttpChannel::ContinueProcessRedirection(nsresult rv)
    3537                 : {
    3538             268 :     AutoRedirectVetoNotifier notifier(this);
    3539                 : 
    3540             134 :     LOG(("ContinueProcessRedirection [rv=%x]\n", rv));
    3541             134 :     if (NS_FAILED(rv))
    3542               8 :         return rv;
    3543                 : 
    3544             126 :     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
    3545                 : 
    3546                 :     // Make sure to do this _after_ calling OnChannelRedirect
    3547             126 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    3548                 : 
    3549                 :     // And now, the deprecated way
    3550             252 :     nsCOMPtr<nsIHttpEventSink> httpEventSink;
    3551             126 :     GetCallback(httpEventSink);
    3552             126 :     if (httpEventSink) {
    3553                 :         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
    3554                 :         // versions.
    3555               0 :         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
    3556               0 :         if (NS_FAILED(rv))
    3557               0 :             return rv;
    3558                 :     }
    3559                 :     // XXX we used to talk directly with the script security manager, but that
    3560                 :     // should really be handled by the event sink implementation.
    3561                 : 
    3562                 :     // begin loading the new channel
    3563             126 :     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    3564                 : 
    3565             126 :     if (NS_FAILED(rv))
    3566               0 :         return rv;
    3567                 : 
    3568                 :     // close down this channel
    3569             126 :     Cancel(NS_BINDING_REDIRECTED);
    3570                 :     
    3571             126 :     notifier.RedirectSucceeded();
    3572                 : 
    3573                 :     // disconnect from our listener
    3574             126 :     mListener = 0;
    3575             126 :     mListenerContext = 0;
    3576                 : 
    3577                 :     // and from our callbacks
    3578             126 :     mCallbacks = nsnull;
    3579             126 :     mProgressSink = nsnull;
    3580             126 :     return NS_OK;
    3581                 : }
    3582                 : 
    3583                 : //-----------------------------------------------------------------------------
    3584                 : // nsHttpChannel <auth>
    3585                 : //-----------------------------------------------------------------------------
    3586                 : 
    3587              15 : NS_IMETHODIMP nsHttpChannel::OnAuthAvailable()
    3588                 : {
    3589              15 :     LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this));
    3590                 : 
    3591                 :     // setting mAuthRetryPending flag and resuming the transaction
    3592                 :     // triggers process of throwing away the unauthenticated data already
    3593                 :     // coming from the network
    3594              15 :     mAuthRetryPending = true;
    3595              15 :     LOG(("Resuming the transaction, we got credentials from user"));
    3596              15 :     mTransactionPump->Resume();
    3597                 :   
    3598              15 :     return NS_OK;
    3599                 : }
    3600                 : 
    3601               3 : NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel)
    3602                 : {
    3603               3 :     LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this));
    3604                 : 
    3605               3 :     if (mTransactionPump) {
    3606                 :         // ensure call of OnStartRequest of the current listener here,
    3607                 :         // it would not be called otherwise at all
    3608               3 :         nsresult rv = CallOnStartRequest();
    3609                 : 
    3610                 :         // drop mAuthRetryPending flag and resume the transaction
    3611                 :         // this resumes load of the unauthenticated content data
    3612               3 :         mAuthRetryPending = false;
    3613               3 :         LOG(("Resuming the transaction, user cancelled the auth dialog"));
    3614               3 :         mTransactionPump->Resume();
    3615                 : 
    3616               3 :         if (NS_FAILED(rv))
    3617               0 :             mTransactionPump->Cancel(rv);
    3618                 :     }
    3619                 :     
    3620               3 :     return NS_OK;
    3621                 : }
    3622                 : 
    3623                 : //-----------------------------------------------------------------------------
    3624                 : // nsHttpChannel::nsISupports
    3625                 : //-----------------------------------------------------------------------------
    3626                 : 
    3627           89779 : NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
    3628           89505 : NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
    3629                 : 
    3630          140226 : NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
    3631          140226 :     NS_INTERFACE_MAP_ENTRY(nsIRequest)
    3632          133688 :     NS_INTERFACE_MAP_ENTRY(nsIChannel)
    3633          122224 :     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    3634          122224 :     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    3635          119030 :     NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
    3636          103486 :     NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
    3637          103486 :     NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
    3638          102677 :     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
    3639          101782 :     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
    3640           97575 :     NS_INTERFACE_MAP_ENTRY(nsICacheListener)
    3641           97575 :     NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
    3642           95516 :     NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
    3643           95471 :     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
    3644           95471 :     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
    3645           95469 :     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
    3646           95459 :     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
    3647           95459 :     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
    3648           95459 :     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
    3649           95459 :     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
    3650           95266 :     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
    3651           94956 :     NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
    3652           94797 : NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
    3653                 : 
    3654                 : //-----------------------------------------------------------------------------
    3655                 : // nsHttpChannel::nsIRequest
    3656                 : //-----------------------------------------------------------------------------
    3657                 : 
    3658                 : NS_IMETHODIMP
    3659             192 : nsHttpChannel::Cancel(nsresult status)
    3660                 : {
    3661             192 :     LOG(("nsHttpChannel::Cancel [this=%p status=%x]\n", this, status));
    3662             192 :     if (mCanceled) {
    3663               0 :         LOG(("  ignoring; already canceled\n"));
    3664               0 :         return NS_OK;
    3665                 :     }
    3666             192 :     if (mWaitingForRedirectCallback) {
    3667               0 :         LOG(("channel canceled during wait for redirect callback"));
    3668                 :     }
    3669             192 :     mCanceled = true;
    3670             192 :     mStatus = status;
    3671             192 :     if (mProxyRequest)
    3672               0 :         mProxyRequest->Cancel(status);
    3673             192 :     if (mTransaction)
    3674             162 :         gHttpHandler->CancelTransaction(mTransaction, status);
    3675             192 :     if (mTransactionPump)
    3676             162 :         mTransactionPump->Cancel(status);
    3677             192 :     if (mCachePump)
    3678               4 :         mCachePump->Cancel(status);
    3679             192 :     if (mAuthProvider)
    3680              36 :         mAuthProvider->Cancel(status);
    3681             192 :     return NS_OK;
    3682                 : }
    3683                 : 
    3684                 : NS_IMETHODIMP
    3685               8 : nsHttpChannel::Suspend()
    3686                 : {
    3687               8 :     NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE);
    3688                 :     
    3689               8 :     LOG(("nsHttpChannel::Suspend [this=%p]\n", this));
    3690                 : 
    3691               8 :     ++mSuspendCount;
    3692                 : 
    3693               8 :     if (mTransactionPump)
    3694               8 :         return mTransactionPump->Suspend();
    3695               0 :     if (mCachePump)
    3696               0 :         return mCachePump->Suspend();
    3697                 : 
    3698               0 :     return NS_OK;
    3699                 : }
    3700                 : 
    3701                 : NS_IMETHODIMP
    3702               8 : nsHttpChannel::Resume()
    3703                 : {
    3704               8 :     NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
    3705                 :     
    3706               8 :     LOG(("nsHttpChannel::Resume [this=%p]\n", this));
    3707                 :         
    3708               8 :     if (--mSuspendCount == 0 && mCallOnResume) {
    3709               0 :         nsresult rv = AsyncCall(mCallOnResume);
    3710               0 :         mCallOnResume = nsnull;
    3711               0 :         NS_ENSURE_SUCCESS(rv, rv);
    3712                 :     }
    3713                 : 
    3714               8 :     if (mTransactionPump)
    3715               8 :         return mTransactionPump->Resume();
    3716               0 :     if (mCachePump)
    3717               0 :         return mCachePump->Resume();
    3718                 : 
    3719               0 :     return NS_OK;
    3720                 : }
    3721                 : 
    3722                 : //-----------------------------------------------------------------------------
    3723                 : // nsHttpChannel::nsIChannel
    3724                 : //-----------------------------------------------------------------------------
    3725                 : 
    3726                 : NS_IMETHODIMP
    3727               4 : nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo)
    3728                 : {
    3729               4 :     NS_ENSURE_ARG_POINTER(securityInfo);
    3730               4 :     *securityInfo = mSecurityInfo;
    3731               4 :     NS_IF_ADDREF(*securityInfo);
    3732               4 :     return NS_OK;
    3733                 : }
    3734                 : 
    3735                 : NS_IMETHODIMP
    3736            3489 : nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
    3737                 : {
    3738            3489 :     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
    3739                 : 
    3740            3489 :     NS_ENSURE_ARG_POINTER(listener);
    3741            3489 :     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
    3742            3486 :     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
    3743                 : 
    3744                 :     nsresult rv;
    3745                 : 
    3746            3483 :     if (mCanceled)
    3747               0 :         return mStatus;
    3748                 : 
    3749            3483 :     rv = NS_CheckPortSafety(mURI);
    3750            3483 :     if (NS_FAILED(rv))
    3751               0 :         return rv;
    3752                 : 
    3753            3483 :     if (!(mConnectionInfo && mConnectionInfo->UsingHttpProxy())) {
    3754                 :         // Start a DNS lookup very early in case the real open is queued the DNS can 
    3755                 :         // happen in parallel. Do not do so in the presence of an HTTP proxy as 
    3756                 :         // all lookups other than for the proxy itself are done by the proxy.
    3757                 :         //
    3758                 :         // We keep the DNS prefetch object around so that we can retrieve
    3759                 :         // timing information from it. There is no guarantee that we actually
    3760                 :         // use the DNS prefetch data for the real connection, but as we keep
    3761                 :         // this data around for 3 minutes by default, this should almost always
    3762                 :         // be correct, and even when it isn't, the timing still represents _a_
    3763                 :         // valid DNS lookup timing for the site, even if it is not _the_
    3764                 :         // timing we used.
    3765            6942 :         mDNSPrefetch = new nsDNSPrefetch(mURI, mTimingEnabled);
    3766            3471 :         mDNSPrefetch->PrefetchHigh();
    3767                 :     }
    3768                 :     
    3769                 :     // Remember the cookie header that was set, if any
    3770            3483 :     const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
    3771            3483 :     if (cookieHeader) {
    3772               8 :         mUserSetCookieHeader = cookieHeader;
    3773                 :     }
    3774                 : 
    3775            3483 :     AddCookiesToRequest();
    3776                 :  
    3777                 :     // check to see if authorization headers should be included
    3778            3483 :     mAuthProvider->AddAuthorizationHeaders();
    3779                 : 
    3780                 :     // notify "http-on-modify-request" observers
    3781            3483 :     gHttpHandler->OnModifyRequest(this);
    3782                 : 
    3783                 :     // Adjust mCaps according to our request headers:
    3784                 :     //  - If "Connection: close" is set as a request header, then do not bother
    3785                 :     //    trying to establish a keep-alive connection.
    3786            3483 :     if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close"))
    3787               0 :         mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING);
    3788                 :     
    3789            3483 :     if ((mLoadFlags & VALIDATE_ALWAYS) || 
    3790                 :         (BYPASS_LOCAL_CACHE(mLoadFlags)))
    3791            1931 :         mCaps |= NS_HTTP_REFRESH_DNS;
    3792                 : 
    3793                 :     // Force-Reload should reset the persistent connection pool for this host
    3794            3483 :     if (mLoadFlags & LOAD_FRESH_CONNECTION)
    3795               0 :         mCaps |= NS_HTTP_CLEAR_KEEPALIVES;
    3796                 :     
    3797            3483 :     mIsPending = true;
    3798            3483 :     mWasOpened = true;
    3799                 : 
    3800            3483 :     mListener = listener;
    3801            3483 :     mListenerContext = context;
    3802                 : 
    3803                 :     // add ourselves to the load group.  from this point forward, we'll report
    3804                 :     // all failures asynchronously.
    3805            3483 :     if (mLoadGroup)
    3806               8 :         mLoadGroup->AddRequest(this, nsnull);
    3807                 : 
    3808                 :     // Collect mAsyncOpenTime after we have called all obsrevers like
    3809                 :     // "http-on-modify-request" and load group observers that may set
    3810                 :     // mTimingEnabled flag.
    3811            3483 :     if (mTimingEnabled)
    3812               2 :         mAsyncOpenTime = mozilla::TimeStamp::Now();
    3813                 : 
    3814                 :     // We may have been cancelled already, either by on-modify-request
    3815                 :     // listeners or by load group observers; in that case, we should
    3816                 :     // not send the request to the server
    3817            3483 :     if (mCanceled)
    3818               1 :         rv = mStatus;
    3819                 :     else
    3820            3482 :         rv = Connect();
    3821            3483 :     if (NS_FAILED(rv)) {
    3822               2 :         LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
    3823               2 :         CloseCacheEntry(true);
    3824               2 :         AsyncAbort(rv);
    3825            3481 :     } else if (mLoadFlags & LOAD_CLASSIFY_URI) {
    3826               0 :         nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
    3827               0 :         if (!classifier) {
    3828               0 :             Cancel(NS_ERROR_OUT_OF_MEMORY);
    3829               0 :             return NS_OK;
    3830                 :         }
    3831                 : 
    3832               0 :         rv = classifier->Start(this);
    3833               0 :         if (NS_FAILED(rv)) {
    3834               0 :             Cancel(rv);
    3835                 :         }
    3836                 :     }
    3837                 : 
    3838            3483 :     return NS_OK;
    3839                 : }
    3840                 : 
    3841                 : //-----------------------------------------------------------------------------
    3842                 : // nsHttpChannel::nsIHttpChannelInternal
    3843                 : //-----------------------------------------------------------------------------
    3844                 : 
    3845                 : NS_IMETHODIMP
    3846               9 : nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
    3847                 : {
    3848               9 :     LOG(("nsHttpChannel::SetupFallbackChannel [this=%x, key=%s]",
    3849                 :          this, aFallbackKey));
    3850               9 :     mFallbackChannel = true;
    3851               9 :     mFallbackKey = aFallbackKey;
    3852                 : 
    3853               9 :     return NS_OK;
    3854                 : }
    3855                 : 
    3856                 : //-----------------------------------------------------------------------------
    3857                 : // nsHttpChannel::nsISupportsPriority
    3858                 : //-----------------------------------------------------------------------------
    3859                 : 
    3860                 : NS_IMETHODIMP
    3861               2 : nsHttpChannel::SetPriority(PRInt32 value)
    3862                 : {
    3863               2 :     PRInt16 newValue = clamped(value, PR_INT16_MIN, PR_INT16_MAX);
    3864               2 :     if (mPriority == newValue)
    3865               0 :         return NS_OK;
    3866               2 :     mPriority = newValue;
    3867               2 :     if (mTransaction)
    3868               0 :         gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
    3869               2 :     return NS_OK;
    3870                 : }
    3871                 : 
    3872                 : //-----------------------------------------------------------------------------
    3873                 : // nsHttpChannel::nsIProtocolProxyCallback
    3874                 : //-----------------------------------------------------------------------------
    3875                 : 
    3876                 : NS_IMETHODIMP
    3877              10 : nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIURI *uri,
    3878                 :                                 nsIProxyInfo *pi, nsresult status)
    3879                 : {
    3880              10 :     mProxyRequest = nsnull;
    3881                 : 
    3882                 :     // If status is a failure code, then it means that we failed to resolve
    3883                 :     // proxy info.  That is a non-fatal error assuming it wasn't because the
    3884                 :     // request was canceled.  We just failover to DIRECT when proxy resolution
    3885                 :     // fails (failure can mean that the PAC URL could not be loaded).
    3886                 :     
    3887                 :     // Need to replace this channel with a new one.  It would be complex to try
    3888                 :     // to change the value of mConnectionInfo since so much of our state may
    3889                 :     // depend on its state.
    3890              10 :     mTargetProxyInfo = pi;
    3891              10 :     HandleAsyncReplaceWithProxy();
    3892              10 :     return NS_OK;
    3893                 : }
    3894                 : 
    3895                 : //-----------------------------------------------------------------------------
    3896                 : // nsHttpChannel::nsIProxiedChannel
    3897                 : //-----------------------------------------------------------------------------
    3898                 : 
    3899                 : NS_IMETHODIMP
    3900            3542 : nsHttpChannel::GetProxyInfo(nsIProxyInfo **result)
    3901                 : {
    3902            3542 :     if (!mConnectionInfo)
    3903               0 :         *result = nsnull;
    3904                 :     else {
    3905            3542 :         *result = mConnectionInfo->ProxyInfo();
    3906            3542 :         NS_IF_ADDREF(*result);
    3907                 :     }
    3908            3542 :     return NS_OK;
    3909                 : }
    3910                 : 
    3911                 : //-----------------------------------------------------------------------------
    3912                 : // nsHttpChannel::nsITimedChannel
    3913                 : //-----------------------------------------------------------------------------
    3914                 : 
    3915                 : NS_IMETHODIMP
    3916             155 : nsHttpChannel::SetTimingEnabled(bool enabled) {
    3917             155 :     mTimingEnabled = enabled;
    3918             155 :     return NS_OK;
    3919                 : }
    3920                 : 
    3921                 : NS_IMETHODIMP
    3922               0 : nsHttpChannel::GetTimingEnabled(bool* _retval) {
    3923               0 :     *_retval = mTimingEnabled;
    3924               0 :     return NS_OK;
    3925                 : }
    3926                 : 
    3927                 : NS_IMETHODIMP
    3928               0 : nsHttpChannel::GetChannelCreation(mozilla::TimeStamp* _retval) {
    3929               0 :     *_retval = mChannelCreationTimestamp;
    3930               0 :     return NS_OK;
    3931                 : }
    3932                 : 
    3933                 : NS_IMETHODIMP
    3934               0 : nsHttpChannel::GetAsyncOpen(mozilla::TimeStamp* _retval) {
    3935               0 :     *_retval = mAsyncOpenTime;
    3936               0 :     return NS_OK;
    3937                 : }
    3938                 : 
    3939                 : NS_IMETHODIMP
    3940               0 : nsHttpChannel::GetDomainLookupStart(mozilla::TimeStamp* _retval) {
    3941               0 :     if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
    3942               0 :         *_retval = mDNSPrefetch->StartTimestamp();
    3943               0 :     else if (mTransaction)
    3944               0 :         *_retval = mTransaction->Timings().domainLookupStart;
    3945                 :     else
    3946               0 :         *_retval = mTransactionTimings.domainLookupStart;
    3947               0 :     return NS_OK;
    3948                 : }
    3949                 : 
    3950                 : NS_IMETHODIMP
    3951               0 : nsHttpChannel::GetDomainLookupEnd(mozilla::TimeStamp* _retval) {
    3952               0 :     if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
    3953               0 :         *_retval = mDNSPrefetch->EndTimestamp();
    3954               0 :     else if (mTransaction)
    3955               0 :         *_retval = mTransaction->Timings().domainLookupEnd;
    3956                 :     else
    3957               0 :         *_retval = mTransactionTimings.domainLookupEnd;
    3958               0 :     return NS_OK;
    3959                 : }
    3960                 : 
    3961                 : NS_IMETHODIMP
    3962               0 : nsHttpChannel::GetConnectStart(mozilla::TimeStamp* _retval) {
    3963               0 :     if (mTransaction)
    3964               0 :         *_retval = mTransaction->Timings().connectStart;
    3965                 :     else
    3966               0 :         *_retval = mTransactionTimings.connectStart;
    3967               0 :     return NS_OK;
    3968                 : }
    3969                 : 
    3970                 : NS_IMETHODIMP
    3971               0 : nsHttpChannel::GetConnectEnd(mozilla::TimeStamp* _retval) {
    3972               0 :     if (mTransaction)
    3973               0 :         *_retval = mTransaction->Timings().connectEnd;
    3974                 :     else
    3975               0 :         *_retval = mTransactionTimings.connectEnd;
    3976               0 :     return NS_OK;
    3977                 : }
    3978                 : 
    3979                 : NS_IMETHODIMP
    3980               0 : nsHttpChannel::GetRequestStart(mozilla::TimeStamp* _retval) {
    3981               0 :     if (mTransaction)
    3982               0 :         *_retval = mTransaction->Timings().requestStart;
    3983                 :     else
    3984               0 :         *_retval = mTransactionTimings.requestStart;
    3985               0 :     return NS_OK;
    3986                 : }
    3987                 : 
    3988                 : NS_IMETHODIMP
    3989               0 : nsHttpChannel::GetResponseStart(mozilla::TimeStamp* _retval) {
    3990               0 :     if (mTransaction)
    3991               0 :         *_retval = mTransaction->Timings().responseStart;
    3992                 :     else
    3993               0 :         *_retval = mTransactionTimings.responseStart;
    3994               0 :     return NS_OK;
    3995                 : }
    3996                 : 
    3997                 : NS_IMETHODIMP
    3998               0 : nsHttpChannel::GetResponseEnd(mozilla::TimeStamp* _retval) {
    3999               0 :     if (mTransaction)
    4000               0 :         *_retval = mTransaction->Timings().responseEnd;
    4001                 :     else
    4002               0 :         *_retval = mTransactionTimings.responseEnd;
    4003               0 :     return NS_OK;
    4004                 : }
    4005                 : 
    4006                 : NS_IMETHODIMP
    4007               0 : nsHttpChannel::GetCacheReadStart(mozilla::TimeStamp* _retval) {
    4008               0 :     *_retval = mCacheReadStart;
    4009               0 :     return NS_OK;
    4010                 : }
    4011                 : 
    4012                 : NS_IMETHODIMP
    4013               0 : nsHttpChannel::GetCacheReadEnd(mozilla::TimeStamp* _retval) {
    4014               0 :     *_retval = mCacheReadEnd;
    4015               0 :     return NS_OK;
    4016                 : }
    4017                 : 
    4018                 : #define IMPL_TIMING_ATTR(name)                                 \
    4019                 : NS_IMETHODIMP                                                  \
    4020                 : nsHttpChannel::Get##name##Time(PRTime* _retval) {              \
    4021                 :     mozilla::TimeStamp stamp;                                  \
    4022                 :     Get##name(&stamp);                                         \
    4023                 :     if (stamp.IsNull()) {                                      \
    4024                 :         *_retval = 0;                                          \
    4025                 :         return NS_OK;                                          \
    4026                 :     }                                                          \
    4027                 :     *_retval = mChannelCreationTime +                          \
    4028                 :         (stamp - mChannelCreationTimestamp).ToSeconds() * 1e6; \
    4029                 :     return NS_OK;                                              \
    4030                 : }
    4031                 : 
    4032               0 : IMPL_TIMING_ATTR(ChannelCreation)
    4033               0 : IMPL_TIMING_ATTR(AsyncOpen)
    4034               0 : IMPL_TIMING_ATTR(DomainLookupStart)
    4035               0 : IMPL_TIMING_ATTR(DomainLookupEnd)
    4036               0 : IMPL_TIMING_ATTR(ConnectStart)
    4037               0 : IMPL_TIMING_ATTR(ConnectEnd)
    4038               0 : IMPL_TIMING_ATTR(RequestStart)
    4039               0 : IMPL_TIMING_ATTR(ResponseStart)
    4040               0 : IMPL_TIMING_ATTR(ResponseEnd)
    4041               0 : IMPL_TIMING_ATTR(CacheReadStart)
    4042               0 : IMPL_TIMING_ATTR(CacheReadEnd)
    4043                 : 
    4044                 : #undef IMPL_TIMING_ATTR
    4045                 : 
    4046                 : //-----------------------------------------------------------------------------
    4047                 : // nsHttpChannel::nsIHttpAuthenticableChannel
    4048                 : //-----------------------------------------------------------------------------
    4049                 : 
    4050                 : NS_IMETHODIMP
    4051            3514 : nsHttpChannel::GetIsSSL(bool *aIsSSL)
    4052                 : {
    4053            3514 :     *aIsSSL = mConnectionInfo->UsingSSL();
    4054            3514 :     return NS_OK;
    4055                 : }
    4056                 : 
    4057                 : NS_IMETHODIMP
    4058               2 : nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect)
    4059                 : {
    4060                 :     *aProxyMethodIsConnect =
    4061               2 :         (mConnectionInfo->UsingHttpProxy() && mConnectionInfo->UsingSSL()) ||
    4062               2 :         mConnectionInfo->ShouldForceConnectMethod();
    4063               2 :     return NS_OK;
    4064                 : }
    4065                 : 
    4066                 : NS_IMETHODIMP
    4067               2 : nsHttpChannel::GetServerResponseHeader(nsACString &value)
    4068                 : {
    4069               2 :     if (!mResponseHead)
    4070               0 :         return NS_ERROR_NOT_AVAILABLE;
    4071               2 :     return mResponseHead->GetHeader(nsHttp::Server, value);
    4072                 : }
    4073                 : 
    4074                 : NS_IMETHODIMP
    4075              10 : nsHttpChannel::GetProxyChallenges(nsACString &value)
    4076                 : {
    4077              10 :     if (!mResponseHead)
    4078               0 :         return NS_ERROR_UNEXPECTED;
    4079              10 :     return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value);
    4080                 : }
    4081                 : 
    4082                 : NS_IMETHODIMP
    4083              49 : nsHttpChannel::GetWWWChallenges(nsACString &value)
    4084                 : {
    4085              49 :     if (!mResponseHead)
    4086               0 :         return NS_ERROR_UNEXPECTED;
    4087              49 :     return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value);
    4088                 : }
    4089                 : 
    4090                 : NS_IMETHODIMP
    4091               9 : nsHttpChannel::SetProxyCredentials(const nsACString &value)
    4092                 : {
    4093               9 :     return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value);
    4094                 : }
    4095                 : 
    4096                 : NS_IMETHODIMP
    4097              21 : nsHttpChannel::SetWWWCredentials(const nsACString &value)
    4098                 : {
    4099              21 :     return mRequestHead.SetHeader(nsHttp::Authorization, value);
    4100                 : }
    4101                 : 
    4102                 : //-----------------------------------------------------------------------------
    4103                 : // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we
    4104                 : // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.
    4105                 : //
    4106                 : 
    4107                 : NS_IMETHODIMP
    4108           12499 : nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
    4109                 : {
    4110           12499 :     return HttpBaseChannel::GetLoadFlags(aLoadFlags);
    4111                 : }
    4112                 : 
    4113                 : NS_IMETHODIMP
    4114           11346 : nsHttpChannel::GetURI(nsIURI **aURI)
    4115                 : {
    4116           11346 :     return HttpBaseChannel::GetURI(aURI);
    4117                 : }
    4118                 : 
    4119                 : NS_IMETHODIMP
    4120             955 : nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
    4121                 : {
    4122             955 :     return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
    4123                 : }
    4124                 : 
    4125                 : NS_IMETHODIMP
    4126             784 : nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
    4127                 : {
    4128             784 :     return HttpBaseChannel::GetLoadGroup(aLoadGroup);
    4129                 : }
    4130                 : 
    4131                 : NS_IMETHODIMP
    4132            5400 : nsHttpChannel::GetRequestMethod(nsACString& aMethod)
    4133                 : {
    4134            5400 :     return HttpBaseChannel::GetRequestMethod(aMethod);
    4135                 : }
    4136                 : 
    4137                 : 
    4138                 : //-----------------------------------------------------------------------------
    4139                 : // nsHttpChannel::nsIRequestObserver
    4140                 : //-----------------------------------------------------------------------------
    4141                 : 
    4142                 : NS_IMETHODIMP
    4143            3194 : nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
    4144                 : {
    4145            6388 :     SAMPLE_LABEL("nsHttpChannel", "OnStartRequest");
    4146            3194 :     if (!(mCanceled || NS_FAILED(mStatus))) {
    4147                 :         // capture the request's status, so our consumers will know ASAP of any
    4148                 :         // connection failures, etc - bug 93581
    4149            3189 :         request->GetStatus(&mStatus);
    4150                 :     }
    4151                 : 
    4152            3194 :     LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%x]\n",
    4153                 :         this, request, mStatus));
    4154                 : 
    4155                 :     // Make sure things are what we expect them to be...
    4156            3194 :     NS_ASSERTION(request == mCachePump || request == mTransactionPump,
    4157                 :                  "Unexpected request");
    4158            3194 :     NS_ASSERTION(!(mTransactionPump && mCachePump) || mCachedContentIsPartial,
    4159                 :                  "If we have both pumps, the cache content must be partial");
    4160                 : 
    4161            3194 :     if (!mSecurityInfo && !mCachePump && mTransaction) {
    4162                 :         // grab the security info from the connection object; the transaction
    4163                 :         // is guaranteed to own a reference to the connection.
    4164            2977 :         mSecurityInfo = mTransaction->SecurityInfo();
    4165                 :     }
    4166                 : 
    4167            3196 :     if (!mCachePump && NS_FAILED(mStatus) &&
    4168               2 :         (mLoadFlags & LOAD_REPLACE) && mOriginalURI && mAllowSpdy) {
    4169                 :         // For sanity's sake we may want to cancel an alternate protocol
    4170                 :         // redirection involving the original host name
    4171                 : 
    4172               4 :         nsCAutoString hostPort;
    4173               2 :         if (NS_SUCCEEDED(mOriginalURI->GetHostPort(hostPort)))
    4174               2 :             gHttpHandler->ConnMgr()->RemoveSpdyAlternateProtocol(hostPort);
    4175                 :     }
    4176                 : 
    4177                 :     // don't enter this block if we're reading from the cache...
    4178            3194 :     if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
    4179                 :         // mTransactionPump doesn't hit OnInputStreamReady and call this until
    4180                 :         // all of the response headers have been acquired, so we can take ownership
    4181                 :         // of them from the transaction.
    4182            2819 :         mResponseHead = mTransaction->TakeResponseHead();
    4183                 :         // the response head may be null if the transaction was cancelled.  in
    4184                 :         // which case we just need to call OnStartRequest/OnStopRequest.
    4185            2819 :         if (mResponseHead)
    4186            2819 :             return ProcessResponse();
    4187                 : 
    4188               0 :         NS_WARNING("No response head in OnStartRequest");
    4189                 :     }
    4190                 : 
    4191                 :     // avoid crashing if mListener happens to be null...
    4192             375 :     if (!mListener) {
    4193               0 :         NS_NOTREACHED("mListener is null");
    4194               0 :         return NS_OK;
    4195                 :     }
    4196                 : 
    4197                 :     // on proxy errors, try to failover
    4198             375 :     if (mConnectionInfo->ProxyInfo() &&
    4199                 :        (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
    4200                 :         mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
    4201                 :         mStatus == NS_ERROR_NET_TIMEOUT)) {
    4202                 : 
    4203               2 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
    4204               2 :         if (NS_SUCCEEDED(ProxyFailover()))
    4205               2 :             return NS_OK;
    4206               0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
    4207                 :     }
    4208                 : 
    4209             373 :     return ContinueOnStartRequest2(NS_OK);
    4210                 : }
    4211                 : 
    4212                 : nsresult
    4213               2 : nsHttpChannel::ContinueOnStartRequest1(nsresult result)
    4214                 : {
    4215                 :     // Success indicates we passed ProxyFailover, in that case we must not continue
    4216                 :     // with this code chain.
    4217               2 :     if (NS_SUCCEEDED(result))
    4218               1 :         return NS_OK;
    4219                 : 
    4220               1 :     return ContinueOnStartRequest2(result);
    4221                 : }
    4222                 : 
    4223                 : nsresult
    4224             374 : nsHttpChannel::ContinueOnStartRequest2(nsresult result)
    4225                 : {
    4226                 :     // on other request errors, try to fall back
    4227             374 :     if (NS_FAILED(mStatus)) {
    4228             157 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
    4229                 :         bool waitingForRedirectCallback;
    4230             157 :         ProcessFallback(&waitingForRedirectCallback);
    4231             157 :         if (waitingForRedirectCallback)
    4232               2 :             return NS_OK;
    4233             155 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
    4234                 :     }
    4235                 : 
    4236             372 :     return ContinueOnStartRequest3(NS_OK);
    4237                 : }
    4238                 : 
    4239                 : nsresult
    4240             374 : nsHttpChannel::ContinueOnStartRequest3(nsresult result)
    4241                 : {
    4242             374 :     if (mFallingBack)
    4243               1 :         return NS_OK;
    4244                 : 
    4245             373 :     return CallOnStartRequest();
    4246                 : }
    4247                 : 
    4248                 : NS_IMETHODIMP
    4249            3194 : nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
    4250                 : {
    4251            6388 :     SAMPLE_LABEL("network", "nsHttpChannel::OnStopRequest");
    4252            3194 :     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n",
    4253                 :         this, request, status));
    4254                 : 
    4255            3194 :     if (mTimingEnabled && request == mCachePump) {
    4256               0 :         mCacheReadEnd = mozilla::TimeStamp::Now();
    4257                 :     }
    4258                 : 
    4259                 :      // allow content to be cached if it was loaded successfully (bug #482935)
    4260            3194 :      bool contentComplete = NS_SUCCEEDED(status);
    4261                 : 
    4262                 :     // honor the cancelation status even if the underlying transaction completed.
    4263            3194 :     if (mCanceled || NS_FAILED(mStatus))
    4264             318 :         status = mStatus;
    4265                 : 
    4266            3194 :     if (mCachedContentIsPartial) {
    4267               2 :         if (NS_SUCCEEDED(status)) {
    4268                 :             // mTransactionPump should be suspended
    4269               2 :             NS_ASSERTION(request != mTransactionPump,
    4270                 :                 "byte-range transaction finished prematurely");
    4271                 : 
    4272               2 :             if (request == mCachePump) {
    4273                 :                 bool streamDone;
    4274               2 :                 status = OnDoneReadingPartialCacheEntry(&streamDone);
    4275               2 :                 if (NS_SUCCEEDED(status) && !streamDone)
    4276               2 :                     return status;
    4277                 :                 // otherwise, fall through and fire OnStopRequest...
    4278                 :             }
    4279                 :             else
    4280               0 :                 NS_NOTREACHED("unexpected request");
    4281                 :         }
    4282                 :         // Do not to leave the transaction in a suspended state in error cases.
    4283               0 :         if (NS_FAILED(status) && mTransaction)
    4284               0 :             gHttpHandler->CancelTransaction(mTransaction, status); 
    4285                 :     }
    4286                 : 
    4287            3192 :     if (mTransaction) {
    4288                 :         // determine if we should call DoAuthRetry
    4289            2977 :         bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
    4290                 : 
    4291                 :         //
    4292                 :         // grab references to connection in case we need to retry an
    4293                 :         // authentication request over it or use it for an upgrade
    4294                 :         // to another protocol.
    4295                 :         //
    4296                 :         // this code relies on the code in nsHttpTransaction::Close, which
    4297                 :         // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
    4298                 :         // keep the connection around after the transaction is finished.
    4299                 :         //
    4300            5954 :         nsRefPtr<nsAHttpConnection> conn;
    4301            2977 :         if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION)) {
    4302              11 :             conn = mTransaction->Connection();
    4303                 :             // This is so far a workaround to fix leak when reusing unpersistent
    4304                 :             // connection for authentication retry. See bug 459620 comment 4
    4305                 :             // for details.
    4306              11 :             if (conn && !conn->IsPersistent())
    4307              11 :                 conn = nsnull;
    4308                 :         }
    4309                 : 
    4310            5954 :         nsRefPtr<nsAHttpConnection> stickyConn;
    4311            2977 :         if (mCaps & NS_HTTP_STICKY_CONNECTION)
    4312              23 :             stickyConn = mTransaction->Connection();
    4313                 :         
    4314                 :         // at this point, we're done with the transaction
    4315            2977 :         mTransactionTimings = mTransaction->Timings();
    4316            2977 :         mTransaction = nsnull;
    4317            2977 :         mTransactionPump = 0;
    4318                 : 
    4319                 :         // We no longer need the dns prefetch object
    4320            2977 :         if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) {
    4321                 :             mTransactionTimings.domainLookupStart =
    4322               0 :                 mDNSPrefetch->StartTimestamp();
    4323                 :             mTransactionTimings.domainLookupEnd =
    4324               0 :                 mDNSPrefetch->EndTimestamp();
    4325                 :         }
    4326            2977 :         mDNSPrefetch = nsnull;
    4327                 : 
    4328                 :         // handle auth retry...
    4329            2977 :         if (authRetry) {
    4330              23 :             mAuthRetryPending = false;
    4331              23 :             status = DoAuthRetry(conn);
    4332              23 :             if (NS_SUCCEEDED(status))
    4333              23 :                 return NS_OK;
    4334                 :         }
    4335                 : 
    4336                 :         // If DoAuthRetry failed, or if we have been cancelled since showing
    4337                 :         // the auth. dialog, then we need to send OnStartRequest now
    4338            2954 :         if (authRetry || (mAuthRetryPending && NS_FAILED(status))) {
    4339               0 :             NS_ASSERTION(NS_FAILED(status), "should have a failure code here");
    4340                 :             // NOTE: since we have a failure status, we can ignore the return
    4341                 :             // value from onStartRequest.
    4342               0 :             mListener->OnStartRequest(this, mListenerContext);
    4343                 :         }
    4344                 : 
    4345                 :         // if this transaction has been replaced, then bail.
    4346            2954 :         if (mTransactionReplaced)
    4347               6 :             return NS_OK;
    4348                 :         
    4349            2948 :         if (mUpgradeProtocolCallback && stickyConn &&
    4350               0 :             mResponseHead && mResponseHead->Status() == 101) {
    4351               0 :             nsCOMPtr<nsISocketTransport>    socketTransport;
    4352               0 :             nsCOMPtr<nsIAsyncInputStream>   socketIn;
    4353               0 :             nsCOMPtr<nsIAsyncOutputStream>  socketOut;
    4354                 : 
    4355                 :             nsresult rv;
    4356               0 :             rv = stickyConn->TakeTransport(getter_AddRefs(socketTransport),
    4357               0 :                                            getter_AddRefs(socketIn),
    4358               0 :                                            getter_AddRefs(socketOut));
    4359               0 :             if (NS_SUCCEEDED(rv))
    4360               0 :                 mUpgradeProtocolCallback->OnTransportAvailable(socketTransport,
    4361                 :                                                                socketIn,
    4362               0 :                                                                socketOut);
    4363                 :         }
    4364                 :     }
    4365                 : 
    4366            3163 :     mIsPending = false;
    4367            3163 :     mStatus = status;
    4368                 : 
    4369                 :     // perform any final cache operations before we close the cache entry.
    4370            3163 :     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) &&
    4371                 :         mRequestTimeInitialized){
    4372            1272 :         FinalizeCacheEntry();
    4373                 :     }
    4374                 :     
    4375            3163 :     if (mListener) {
    4376            3039 :         LOG(("  calling OnStopRequest\n"));
    4377            3039 :         mListener->OnStopRequest(this, mListenerContext, status);
    4378            3039 :         mListener = 0;
    4379            3039 :         mListenerContext = 0;
    4380                 :     }
    4381                 : 
    4382            3163 :     if (mCacheEntry) {
    4383            1481 :         bool asFile = false;
    4384            3728 :         if (mInitedCacheEntry && !mCachedContentIsPartial &&
    4385            1137 :             (NS_SUCCEEDED(mStatus) || contentComplete) &&
    4386                 :             (mCacheAccess & nsICache::ACCESS_WRITE) &&
    4387            1110 :             NS_SUCCEEDED(GetCacheAsFile(&asFile)) && asFile) {
    4388                 :             // We can allow others access to the cache entry
    4389                 :             // because we don't write to the cache anymore.
    4390                 :             // CloseCacheEntry may not actually close the cache
    4391                 :             // entry immediately because someone (such as XHR2
    4392                 :             // blob response) may hold the token to the cache
    4393                 :             // entry. So we mark the cache valid here.
    4394                 :             // We also need to check the entry is stored as file
    4395                 :             // because we write to the cache asynchronously when
    4396                 :             // it isn't stored in the file and it isn't completely
    4397                 :             // written to the disk yet.
    4398               0 :             mCacheEntry->MarkValid();
    4399                 :         }
    4400            1481 :         CloseCacheEntry(!contentComplete);
    4401                 :     }
    4402                 : 
    4403            3163 :     if (mOfflineCacheEntry)
    4404              24 :         CloseOfflineCacheEntry();
    4405                 : 
    4406            3163 :     if (mLoadGroup)
    4407               2 :         mLoadGroup->RemoveRequest(this, nsnull, status);
    4408                 : 
    4409                 :     // We don't need this info anymore
    4410            3163 :     CleanRedirectCacheChainIfNecessary();
    4411                 : 
    4412            3163 :     mCallbacks = nsnull;
    4413            3163 :     mProgressSink = nsnull;
    4414                 :     
    4415            3163 :     return NS_OK;
    4416                 : }
    4417                 : 
    4418                 : //-----------------------------------------------------------------------------
    4419                 : // nsHttpChannel::nsIStreamListener
    4420                 : //-----------------------------------------------------------------------------
    4421                 : 
    4422                 : NS_IMETHODIMP
    4423            3064 : nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
    4424                 :                                nsIInputStream *input,
    4425                 :                                PRUint32 offset, PRUint32 count)
    4426                 : {
    4427            6128 :     SAMPLE_LABEL("network", "nsHttpChannel::OnDataAvailable");
    4428            3064 :     LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%u count=%u]\n",
    4429                 :         this, request, offset, count));
    4430                 : 
    4431                 :     // don't send out OnDataAvailable notifications if we've been canceled.
    4432            3064 :     if (mCanceled)
    4433               0 :         return mStatus;
    4434                 : 
    4435            3064 :     NS_ASSERTION(mResponseHead, "No response head in ODA!!");
    4436                 : 
    4437            3064 :     NS_ASSERTION(!(mCachedContentIsPartial && (request == mTransactionPump)),
    4438                 :             "transaction pump not suspended");
    4439                 : 
    4440            3064 :     if (mAuthRetryPending || (request == mTransactionPump && mTransactionReplaced)) {
    4441                 :         PRUint32 n;
    4442              23 :         return input->ReadSegments(NS_DiscardSegment, nsnull, count, &n);
    4443                 :     }
    4444                 : 
    4445            3041 :     if (mListener) {
    4446                 :         //
    4447                 :         // synthesize transport progress event.  we do this here since we want
    4448                 :         // to delay OnProgress events until we start streaming data.  this is
    4449                 :         // crucially important since it impacts the lock icon (see bug 240053).
    4450                 :         //
    4451                 :         nsresult transportStatus;
    4452            3041 :         if (request == mCachePump)
    4453             217 :             transportStatus = nsITransport::STATUS_READING;
    4454                 :         else
    4455            2824 :             transportStatus = nsISocketTransport::STATUS_RECEIVING_FROM;
    4456                 : 
    4457                 :         // mResponseHead may reference new or cached headers, but either way it
    4458                 :         // holds our best estimate of the total content length.  Even in the case
    4459                 :         // of a byte range request, the content length stored in the cached
    4460                 :         // response headers is what we want to use here.
    4461                 : 
    4462            3041 :         PRUint64 progressMax(PRUint64(mResponseHead->ContentLength()));
    4463            3041 :         PRUint64 progress = mLogicalOffset + PRUint64(count);
    4464            3041 :         NS_ASSERTION(progress <= progressMax, "unexpected progress values");
    4465                 : 
    4466            3041 :         OnTransportStatus(nsnull, transportStatus, progress, progressMax);
    4467                 : 
    4468                 :         //
    4469                 :         // we have to manually keep the logical offset of the stream up-to-date.
    4470                 :         // we cannot depend solely on the offset provided, since we may have 
    4471                 :         // already streamed some data from another source (see, for example,
    4472                 :         // OnDoneReadingPartialCacheEntry).
    4473                 :         //
    4474            3041 :         nsresult rv =  mListener->OnDataAvailable(this,
    4475                 :                                                   mListenerContext,
    4476                 :                                                   input,
    4477                 :                                                   mLogicalOffset,
    4478            3041 :                                                   count);
    4479            3041 :         if (NS_SUCCEEDED(rv))
    4480            3037 :             mLogicalOffset = progress;
    4481            3041 :         return rv;
    4482                 :     }
    4483                 : 
    4484               0 :     return NS_ERROR_ABORT;
    4485                 : }
    4486                 : 
    4487                 : //-----------------------------------------------------------------------------
    4488                 : // nsHttpChannel::nsITransportEventSink
    4489                 : //-----------------------------------------------------------------------------
    4490                 : 
    4491                 : NS_IMETHODIMP
    4492            6914 : nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
    4493                 :                                  PRUint64 progress, PRUint64 progressMax)
    4494                 : {
    4495                 :     // cache the progress sink so we don't have to query for it each time.
    4496            6914 :     if (!mProgressSink)
    4497            5920 :         GetCallback(mProgressSink);
    4498                 : 
    4499            6914 :     if (status == nsISocketTransport::STATUS_CONNECTED_TO ||
    4500                 :         status == nsISocketTransport::STATUS_WAITING_FOR) {
    4501                 :         nsCOMPtr<nsISocketTransport> socketTransport =
    4502            5862 :             do_QueryInterface(trans);
    4503            2931 :         if (socketTransport) {
    4504            2931 :             socketTransport->GetSelfAddr(&mSelfAddr);
    4505            2931 :             socketTransport->GetPeerAddr(&mPeerAddr);
    4506                 :         }
    4507                 :     }
    4508                 : 
    4509                 :     // block socket status event after Cancel or OnStopRequest has been called.
    4510            6914 :     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
    4511             219 :         LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",
    4512                 :             this, status, progress, progressMax));
    4513                 : 
    4514             438 :         nsCAutoString host;
    4515             219 :         mURI->GetHost(host);
    4516             219 :         mProgressSink->OnStatus(this, nsnull, status,
    4517             219 :                                 NS_ConvertUTF8toUTF16(host).get());
    4518                 : 
    4519             219 :         if (progress > 0) {
    4520             120 :             NS_ASSERTION(progress <= progressMax, "unexpected progress values");
    4521             120 :             mProgressSink->OnProgress(this, nsnull, progress, progressMax);
    4522                 :         }
    4523                 :     }
    4524                 : #ifdef DEBUG
    4525                 :     else
    4526            6695 :         LOG(("skipping status notification [this=%p sink=%p pending=%u background=%x]\n",
    4527                 :             this, mProgressSink.get(), mIsPending, (mLoadFlags & LOAD_BACKGROUND)));
    4528                 : #endif
    4529                 : 
    4530            6914 :     return NS_OK;
    4531                 : } 
    4532                 : 
    4533                 : //-----------------------------------------------------------------------------
    4534                 : // nsHttpChannel::nsICacheInfoChannel
    4535                 : //-----------------------------------------------------------------------------
    4536                 : 
    4537                 : NS_IMETHODIMP
    4538              19 : nsHttpChannel::IsFromCache(bool *value)
    4539                 : {
    4540              19 :     if (!mIsPending)
    4541               0 :         return NS_ERROR_NOT_AVAILABLE;
    4542                 : 
    4543                 :     // return false if reading a partial cache entry; the data isn't entirely
    4544                 :     // from the cache!
    4545                 : 
    4546              19 :     *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
    4547              19 :               mCachedContentIsValid && !mCachedContentIsPartial;
    4548                 : 
    4549              19 :     return NS_OK;
    4550                 : }
    4551                 : 
    4552                 : NS_IMETHODIMP
    4553               0 : nsHttpChannel::GetCacheTokenExpirationTime(PRUint32 *_retval)
    4554                 : {
    4555               0 :     NS_ENSURE_ARG_POINTER(_retval);
    4556               0 :     if (!mCacheEntry)
    4557               0 :         return NS_ERROR_NOT_AVAILABLE;
    4558                 : 
    4559               0 :     return mCacheEntry->GetExpirationTime(_retval);
    4560                 : }
    4561                 : 
    4562                 : NS_IMETHODIMP
    4563               0 : nsHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval)
    4564                 : {
    4565                 :     nsresult rv;
    4566                 : 
    4567               0 :     if (!mCacheEntry)
    4568               0 :         return NS_ERROR_NOT_AVAILABLE;
    4569                 : 
    4570               0 :     nsXPIDLCString cachedCharset;
    4571               0 :     rv = mCacheEntry->GetMetaDataElement("charset",
    4572               0 :                                          getter_Copies(cachedCharset));
    4573               0 :     if (NS_SUCCEEDED(rv))
    4574               0 :         _retval = cachedCharset;
    4575                 : 
    4576               0 :     return rv;
    4577                 : }
    4578                 : 
    4579                 : NS_IMETHODIMP
    4580               0 : nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset)
    4581                 : {
    4582               0 :     if (!mCacheEntry)
    4583               0 :         return NS_ERROR_NOT_AVAILABLE;
    4584                 : 
    4585               0 :     return mCacheEntry->SetMetaDataElement("charset",
    4586               0 :                                            PromiseFlatCString(aCharset).get());
    4587                 : }
    4588                 : 
    4589                 : //-----------------------------------------------------------------------------
    4590                 : // nsHttpChannel::nsICachingChannel
    4591                 : //-----------------------------------------------------------------------------
    4592                 : 
    4593                 : NS_IMETHODIMP
    4594             373 : nsHttpChannel::GetCacheToken(nsISupports **token)
    4595                 : {
    4596             373 :     NS_ENSURE_ARG_POINTER(token);
    4597             373 :     if (!mCacheEntry)
    4598               0 :         return NS_ERROR_NOT_AVAILABLE;
    4599             373 :     return CallQueryInterface(mCacheEntry, token);
    4600                 : }
    4601                 : 
    4602                 : NS_IMETHODIMP
    4603               0 : nsHttpChannel::SetCacheToken(nsISupports *token)
    4604                 : {
    4605               0 :     return NS_ERROR_NOT_IMPLEMENTED;
    4606                 : }
    4607                 : 
    4608                 : NS_IMETHODIMP
    4609               8 : nsHttpChannel::GetOfflineCacheToken(nsISupports **token)
    4610                 : {
    4611               8 :     NS_ENSURE_ARG_POINTER(token);
    4612               8 :     if (!mOfflineCacheEntry)
    4613               0 :         return NS_ERROR_NOT_AVAILABLE;
    4614               8 :     return CallQueryInterface(mOfflineCacheEntry, token);
    4615                 : }
    4616                 : 
    4617                 : NS_IMETHODIMP
    4618               0 : nsHttpChannel::SetOfflineCacheToken(nsISupports *token)
    4619                 : {
    4620               0 :     return NS_ERROR_NOT_IMPLEMENTED;
    4621                 : }
    4622                 : 
    4623                 : class nsHttpChannelCacheKey : public nsISupportsPRUint32,
    4624                 :                               public nsISupportsCString
    4625              10 : {
    4626                 :     NS_DECL_ISUPPORTS
    4627                 : 
    4628                 :     NS_DECL_NSISUPPORTSPRIMITIVE
    4629               9 :     NS_FORWARD_NSISUPPORTSPRUINT32(mSupportsPRUint32->)
    4630                 :     
    4631                 :     // Both interfaces declares toString method with the same signature.
    4632                 :     // Thus we have to delegate only to nsISupportsPRUint32 implementation.
    4633               0 :     NS_SCRIPTABLE NS_IMETHOD GetData(nsACString & aData) 
    4634                 :     { 
    4635               0 :         return mSupportsCString->GetData(aData);
    4636                 :     }
    4637               0 :     NS_SCRIPTABLE NS_IMETHOD SetData(const nsACString & aData)
    4638                 :     { 
    4639               0 :         return mSupportsCString->SetData(aData);
    4640                 :     }
    4641                 :     
    4642                 : public:
    4643                 :     nsresult SetData(PRUint32 aPostID, const nsACString& aKey);
    4644                 : 
    4645                 : protected:
    4646                 :     nsCOMPtr<nsISupportsPRUint32> mSupportsPRUint32;
    4647                 :     nsCOMPtr<nsISupportsCString> mSupportsCString;
    4648                 : };
    4649                 : 
    4650              36 : NS_IMPL_ADDREF(nsHttpChannelCacheKey)
    4651              41 : NS_IMPL_RELEASE(nsHttpChannelCacheKey)
    4652              64 : NS_INTERFACE_TABLE_HEAD(nsHttpChannelCacheKey)
    4653                 : NS_INTERFACE_TABLE_BEGIN
    4654                 : NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsHttpChannelCacheKey,
    4655                 :                                    nsISupports, nsISupportsPRUint32)
    4656                 : NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsHttpChannelCacheKey,
    4657                 :                                    nsISupportsPrimitive, nsISupportsPRUint32)
    4658                 : NS_INTERFACE_TABLE_ENTRY(nsHttpChannelCacheKey,
    4659                 :                          nsISupportsPRUint32)
    4660                 : NS_INTERFACE_TABLE_ENTRY(nsHttpChannelCacheKey,
    4661                 :                          nsISupportsCString)
    4662              64 : NS_INTERFACE_TABLE_END
    4663              64 : NS_INTERFACE_TABLE_TAIL
    4664                 : 
    4665               0 : NS_IMETHODIMP nsHttpChannelCacheKey::GetType(PRUint16 *aType)
    4666                 : {
    4667               0 :     NS_ENSURE_ARG_POINTER(aType);
    4668                 : 
    4669               0 :     *aType = TYPE_PRUINT32;
    4670               0 :     return NS_OK;
    4671                 : }
    4672                 : 
    4673               5 : nsresult nsHttpChannelCacheKey::SetData(PRUint32 aPostID,
    4674                 :                                         const nsACString& aKey)
    4675                 : {
    4676                 :     nsresult rv;
    4677                 : 
    4678                 :     mSupportsCString = 
    4679               5 :         do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
    4680               5 :     if (NS_FAILED(rv)) return rv;
    4681                 : 
    4682               5 :     mSupportsCString->SetData(aKey);
    4683               5 :     if (NS_FAILED(rv)) return rv;
    4684                 : 
    4685                 :     mSupportsPRUint32 = 
    4686               5 :         do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
    4687               5 :     if (NS_FAILED(rv)) return rv;
    4688                 : 
    4689               5 :     mSupportsPRUint32->SetData(aPostID);
    4690               5 :     if (NS_FAILED(rv)) return rv;
    4691                 : 
    4692               5 :     return NS_OK;
    4693                 : }
    4694                 : 
    4695                 : NS_IMETHODIMP
    4696               5 : nsHttpChannel::GetCacheKey(nsISupports **key)
    4697                 : {
    4698                 :     nsresult rv;
    4699               5 :     NS_ENSURE_ARG_POINTER(key);
    4700                 : 
    4701               5 :     LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this));
    4702                 : 
    4703               5 :     *key = nsnull;
    4704                 : 
    4705                 :     nsRefPtr<nsHttpChannelCacheKey> container =
    4706              10 :         new nsHttpChannelCacheKey();
    4707                 : 
    4708               5 :     if (!container)
    4709               0 :         return NS_ERROR_OUT_OF_MEMORY;
    4710                 : 
    4711              10 :     nsCAutoString cacheKey;
    4712               5 :     rv = GenerateCacheKey(mPostID, cacheKey);
    4713               5 :     if (NS_FAILED(rv)) return rv;
    4714                 : 
    4715               5 :     rv = container->SetData(mPostID, cacheKey);
    4716               5 :     if (NS_FAILED(rv)) return rv;
    4717                 : 
    4718               5 :     return CallQueryInterface(container.get(), key);
    4719                 : }
    4720                 : 
    4721                 : NS_IMETHODIMP
    4722               3 : nsHttpChannel::SetCacheKey(nsISupports *key)
    4723                 : {
    4724                 :     nsresult rv;
    4725                 : 
    4726               3 :     LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n", this, key));
    4727                 : 
    4728                 :     // can only set the cache key if a load is not in progress
    4729               3 :     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
    4730                 : 
    4731               3 :     if (!key)
    4732               0 :         mPostID = 0;
    4733                 :     else {
    4734                 :         // extract the post id
    4735               6 :         nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(key, &rv);
    4736               3 :         if (NS_FAILED(rv)) return rv;
    4737                 : 
    4738               3 :         rv = container->GetData(&mPostID);
    4739               3 :         if (NS_FAILED(rv)) return rv;
    4740                 :     }
    4741               3 :     return NS_OK;
    4742                 : }
    4743                 : 
    4744                 : NS_IMETHODIMP
    4745            1110 : nsHttpChannel::GetCacheAsFile(bool *value)
    4746                 : {
    4747            1110 :     NS_ENSURE_ARG_POINTER(value);
    4748            1110 :     if (!mCacheEntry)
    4749               0 :         return NS_ERROR_NOT_AVAILABLE;
    4750                 :     nsCacheStoragePolicy storagePolicy;
    4751            1110 :     mCacheEntry->GetStoragePolicy(&storagePolicy);
    4752            1110 :     *value = (storagePolicy == nsICache::STORE_ON_DISK_AS_FILE);
    4753            1110 :     return NS_OK;
    4754                 : }
    4755                 : 
    4756                 : NS_IMETHODIMP
    4757               1 : nsHttpChannel::SetCacheAsFile(bool value)
    4758                 : {
    4759               1 :     if (!mCacheEntry || mLoadFlags & INHIBIT_PERSISTENT_CACHING)
    4760               0 :         return NS_ERROR_NOT_AVAILABLE;
    4761                 :     nsCacheStoragePolicy policy;
    4762               1 :     if (value)
    4763               1 :         policy = nsICache::STORE_ON_DISK_AS_FILE;
    4764                 :     else
    4765               0 :         policy = nsICache::STORE_ANYWHERE;
    4766               1 :     return mCacheEntry->SetStoragePolicy(policy);
    4767                 : }
    4768                 : 
    4769                 : 
    4770                 : NS_IMETHODIMP
    4771               0 : nsHttpChannel::GetCacheForOfflineUse(bool *value)
    4772                 : {
    4773               0 :     *value = mCacheForOfflineUse;
    4774                 : 
    4775               0 :     return NS_OK;
    4776                 : }
    4777                 : 
    4778                 : NS_IMETHODIMP
    4779              36 : nsHttpChannel::SetCacheForOfflineUse(bool value)
    4780                 : {
    4781              36 :     mCacheForOfflineUse = value;
    4782                 : 
    4783              36 :     return NS_OK;
    4784                 : }
    4785                 : 
    4786                 : NS_IMETHODIMP
    4787               0 : nsHttpChannel::GetOfflineCacheClientID(nsACString &value)
    4788                 : {
    4789               0 :     value = mOfflineCacheClientID;
    4790                 : 
    4791               0 :     return NS_OK;
    4792                 : }
    4793                 : 
    4794                 : NS_IMETHODIMP
    4795              36 : nsHttpChannel::SetOfflineCacheClientID(const nsACString &value)
    4796                 : {
    4797              36 :     mOfflineCacheClientID = value;
    4798                 : 
    4799              36 :     return NS_OK;
    4800                 : }
    4801                 : 
    4802                 : NS_IMETHODIMP
    4803               0 : nsHttpChannel::GetCacheFile(nsIFile **cacheFile)
    4804                 : {
    4805               0 :     if (!mCacheEntry)
    4806               0 :         return NS_ERROR_NOT_AVAILABLE;
    4807               0 :     return mCacheEntry->GetFile(cacheFile);
    4808                 : }
    4809                 : 
    4810                 : //-----------------------------------------------------------------------------
    4811                 : // nsHttpChannel::nsIResumableChannel
    4812                 : //-----------------------------------------------------------------------------
    4813                 : 
    4814                 : NS_IMETHODIMP
    4815              27 : nsHttpChannel::ResumeAt(PRUint64 aStartPos,
    4816                 :                         const nsACString& aEntityID)
    4817                 : {
    4818              27 :     LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%llu id='%s']\n",
    4819                 :          this, aStartPos, PromiseFlatCString(aEntityID).get()));
    4820              27 :     mEntityID = aEntityID;
    4821              27 :     mStartPos = aStartPos;
    4822              27 :     mResuming = true;
    4823              27 :     return NS_OK;
    4824                 : }
    4825                 : 
    4826                 : //-----------------------------------------------------------------------------
    4827                 : // nsHttpChannel::nsICacheListener
    4828                 : //-----------------------------------------------------------------------------
    4829                 : 
    4830                 : NS_IMETHODIMP
    4831            1599 : nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
    4832                 :                                      nsCacheAccessMode access,
    4833                 :                                      nsresult status)
    4834                 : {
    4835                 :     nsresult rv;
    4836                 : 
    4837            1599 :     LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
    4838                 :          "access=%x status=%x]\n", this, entry, access, status));
    4839                 : 
    4840                 :     // if the channel's already fired onStopRequest, then we should ignore
    4841                 :     // this event.
    4842            1599 :     if (!mIsPending)
    4843               0 :         return NS_OK;
    4844                 : 
    4845            1599 :     nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback;
    4846            1599 :     mOnCacheEntryAvailableCallback = nsnull;
    4847                 : 
    4848            1599 :     NS_ASSERTION(callback,
    4849                 :         "nsHttpChannel::OnCacheEntryAvailable called without callback");
    4850            1599 :     rv = ((*this).*callback)(entry, access, status, false);
    4851                 : 
    4852            1599 :     if (NS_FAILED(rv)) {
    4853              35 :         LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
    4854              35 :         if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
    4855                 :             // If we have a fallback URI (and we're not already
    4856                 :             // falling back), process the fallback asynchronously.
    4857               9 :             if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
    4858               2 :                 rv = AsyncCall(&nsHttpChannel::HandleAsyncFallback);
    4859               2 :                 if (NS_SUCCEEDED(rv))
    4860               2 :                     return rv;
    4861                 :             }
    4862                 :         }
    4863              33 :         CloseCacheEntry(true);
    4864              33 :         AsyncAbort(rv);
    4865                 :     }
    4866                 : 
    4867            1597 :     return NS_OK;
    4868                 : }
    4869                 : 
    4870                 : nsresult
    4871              23 : nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
    4872                 : {
    4873              23 :     LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
    4874                 : 
    4875              23 :     NS_ASSERTION(!mTransaction, "should not have a transaction");
    4876                 :     nsresult rv;
    4877                 : 
    4878                 :     // toggle mIsPending to allow nsIObserver implementations to modify
    4879                 :     // the request headers (bug 95044).
    4880              23 :     mIsPending = false;
    4881                 : 
    4882                 :     // fetch cookies, and add them to the request header.
    4883                 :     // the server response could have included cookies that must be sent with
    4884                 :     // this authentication attempt (bug 84794).
    4885                 :     // TODO: save cookies from auth response and send them here (bug 572151).
    4886              23 :     AddCookiesToRequest();
    4887                 :     
    4888                 :     // notify "http-on-modify-request" observers
    4889              23 :     gHttpHandler->OnModifyRequest(this);
    4890                 : 
    4891              23 :     mIsPending = true;
    4892                 : 
    4893                 :     // get rid of the old response headers
    4894              23 :     mResponseHead = nsnull;
    4895                 : 
    4896                 :     // set sticky connection flag and disable pipelining.
    4897              23 :     mCaps |=  NS_HTTP_STICKY_CONNECTION;
    4898              23 :     mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
    4899                 :    
    4900                 :     // and create a new one...
    4901              23 :     rv = SetupTransaction();
    4902              23 :     if (NS_FAILED(rv)) return rv;
    4903                 : 
    4904                 :     // transfer ownership of connection to transaction
    4905              23 :     if (conn)
    4906               0 :         mTransaction->SetConnection(conn);
    4907                 : 
    4908                 :     // rewind the upload stream
    4909              23 :     if (mUploadStream) {
    4910               0 :         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
    4911               0 :         if (seekable)
    4912               0 :             seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
    4913                 :     }
    4914                 : 
    4915              23 :     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
    4916              23 :     if (NS_FAILED(rv)) return rv;
    4917                 : 
    4918              23 :     rv = mTransactionPump->AsyncRead(this, nsnull);
    4919              23 :     if (NS_FAILED(rv)) return rv;
    4920                 : 
    4921              23 :     PRUint32 suspendCount = mSuspendCount;
    4922              46 :     while (suspendCount--)
    4923               0 :         mTransactionPump->Suspend();
    4924                 : 
    4925              23 :     return NS_OK;
    4926                 : }
    4927                 : 
    4928                 : //-----------------------------------------------------------------------------
    4929                 : // nsHttpChannel::nsIApplicationCacheChannel
    4930                 : //-----------------------------------------------------------------------------
    4931                 : NS_IMETHODIMP
    4932               0 : nsHttpChannel::GetApplicationCache(nsIApplicationCache **out)
    4933                 : {
    4934               0 :     NS_IF_ADDREF(*out = mApplicationCache);
    4935               0 :     return NS_OK;
    4936                 : }
    4937                 : 
    4938                 : NS_IMETHODIMP
    4939             177 : nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache)
    4940                 : {
    4941             177 :     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
    4942                 : 
    4943             177 :     mApplicationCache = appCache;
    4944             177 :     return NS_OK;
    4945                 : }
    4946                 : 
    4947                 : NS_IMETHODIMP
    4948               0 : nsHttpChannel::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache)
    4949                 : {
    4950               0 :     *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
    4951               0 :     return NS_OK;
    4952                 : }
    4953                 : 
    4954                 : NS_IMETHODIMP
    4955               0 : nsHttpChannel::GetInheritApplicationCache(bool *aInherit)
    4956                 : {
    4957               0 :     *aInherit = mInheritApplicationCache;
    4958               0 :     return NS_OK;
    4959                 : }
    4960                 : 
    4961                 : NS_IMETHODIMP
    4962             153 : nsHttpChannel::SetInheritApplicationCache(bool aInherit)
    4963                 : {
    4964             153 :     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
    4965                 : 
    4966             153 :     mInheritApplicationCache = aInherit;
    4967             153 :     return NS_OK;
    4968                 : }
    4969                 : 
    4970                 : NS_IMETHODIMP
    4971               0 : nsHttpChannel::GetChooseApplicationCache(bool *aChoose)
    4972                 : {
    4973               0 :     *aChoose = mChooseApplicationCache;
    4974               0 :     return NS_OK;
    4975                 : }
    4976                 : 
    4977                 : NS_IMETHODIMP
    4978               8 : nsHttpChannel::SetChooseApplicationCache(bool aChoose)
    4979                 : {
    4980               8 :     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
    4981                 : 
    4982               8 :     mChooseApplicationCache = aChoose;
    4983               8 :     return NS_OK;
    4984                 : }
    4985                 : 
    4986                 : NS_IMETHODIMP
    4987               0 : nsHttpChannel::MarkOfflineCacheEntryAsForeign()
    4988                 : {
    4989               0 :     if (!mApplicationCache)
    4990               0 :         return NS_ERROR_NOT_AVAILABLE;
    4991                 : 
    4992                 :     nsresult rv;
    4993                 : 
    4994               0 :     nsCAutoString cacheKey;
    4995               0 :     rv = GenerateCacheKey(mPostID, cacheKey);
    4996               0 :     NS_ENSURE_SUCCESS(rv, rv);
    4997                 : 
    4998               0 :     rv = mApplicationCache->MarkEntry(cacheKey,
    4999               0 :                                       nsIApplicationCache::ITEM_FOREIGN);
    5000               0 :     NS_ENSURE_SUCCESS(rv, rv);
    5001                 : 
    5002               0 :     return NS_OK;
    5003                 : }
    5004                 : 
    5005                 : //-----------------------------------------------------------------------------
    5006                 : // nsHttpChannel::nsIAsyncVerifyRedirectCallback
    5007                 : //-----------------------------------------------------------------------------
    5008                 : 
    5009                 : nsresult
    5010             155 : nsHttpChannel::WaitForRedirectCallback()
    5011                 : {
    5012                 :     nsresult rv;
    5013             155 :     LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this));
    5014                 : 
    5015             155 :     if (mTransactionPump) {
    5016             133 :         rv = mTransactionPump->Suspend();
    5017             133 :         NS_ENSURE_SUCCESS(rv, rv);
    5018                 :     }
    5019             155 :     if (mCachePump) {
    5020               0 :         rv = mCachePump->Suspend();
    5021               0 :         if (NS_FAILED(rv) && mTransactionPump) {
    5022                 : #ifdef DEBUG
    5023                 :             nsresult resume = 
    5024                 : #endif
    5025               0 :             mTransactionPump->Resume();
    5026               0 :             NS_ASSERTION(NS_SUCCEEDED(resume),
    5027                 :                 "Failed to resume transaction pump");
    5028                 :         }
    5029               0 :         NS_ENSURE_SUCCESS(rv, rv);
    5030                 :     }
    5031                 : 
    5032             155 :     mWaitingForRedirectCallback = true;
    5033             155 :     return NS_OK;
    5034                 : }
    5035                 : 
    5036                 : NS_IMETHODIMP
    5037             155 : nsHttpChannel::OnRedirectVerifyCallback(nsresult result)
    5038                 : {
    5039             155 :     LOG(("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
    5040                 :          "result=%x stack=%d mWaitingForRedirectCallback=%u\n",
    5041                 :          this, result, mRedirectFuncStack.Length(), mWaitingForRedirectCallback));
    5042             155 :     NS_ASSERTION(mWaitingForRedirectCallback,
    5043                 :                  "Someone forgot to call WaitForRedirectCallback() ?!");
    5044             155 :     mWaitingForRedirectCallback = false;
    5045                 : 
    5046             155 :     if (mCanceled && NS_SUCCEEDED(result))
    5047               0 :         result = NS_BINDING_ABORTED;
    5048                 : 
    5049             619 :     for (PRUint32 i = mRedirectFuncStack.Length(); i > 0;) {
    5050             311 :         --i;
    5051                 :         // Pop the last function pushed to the stack
    5052             311 :         nsContinueRedirectionFunc func = mRedirectFuncStack[i];
    5053             311 :         mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length() - 1);
    5054                 : 
    5055                 :         // Call it with the result we got from the callback or the deeper
    5056                 :         // function call.
    5057             311 :         result = (this->*func)(result);
    5058                 : 
    5059                 :         // If a new function has been pushed to the stack and placed us in the
    5060                 :         // waiting state, we need to break the chain and wait for the callback
    5061                 :         // again.
    5062             311 :         if (mWaitingForRedirectCallback)
    5063               2 :             break;
    5064                 :     }
    5065                 : 
    5066             155 :     if (NS_FAILED(result) && !mCanceled) {
    5067                 :         // First, cancel this channel if we are in failure state to set mStatus
    5068                 :         // and let it be propagated to pumps.
    5069               5 :         Cancel(result);
    5070                 :     }
    5071                 : 
    5072             155 :     if (!mWaitingForRedirectCallback) {
    5073                 :         // We are not waiting for the callback. At this moment we must release
    5074                 :         // reference to the redirect target channel, otherwise we may leak.
    5075             153 :         mRedirectChannel = nsnull;
    5076                 :     }
    5077                 : 
    5078                 :     // We always resume the pumps here. If all functions on stack have been
    5079                 :     // called we need OnStopRequest to be triggered, and if we broke out of the
    5080                 :     // loop above (and are thus waiting for a new callback) the suspension
    5081                 :     // count must be balanced in the pumps.
    5082             155 :     if (mTransactionPump)
    5083             133 :         mTransactionPump->Resume();
    5084             155 :     if (mCachePump)
    5085               0 :         mCachePump->Resume();
    5086                 : 
    5087             155 :     return result;
    5088                 : }
    5089                 : 
    5090                 : void
    5091            1160 : nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func)
    5092                 : {
    5093            1160 :     mRedirectFuncStack.AppendElement(func);
    5094            1160 : }
    5095                 : 
    5096                 : void
    5097             849 : nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func)
    5098                 : {
    5099             849 :     NS_ASSERTION(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
    5100                 :         "Trying to pop wrong method from redirect async stack!");
    5101                 : 
    5102             849 :     mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
    5103             849 : }
    5104                 : 
    5105                 : 
    5106                 : //-----------------------------------------------------------------------------
    5107                 : // nsHttpChannel internal functions
    5108                 : //-----------------------------------------------------------------------------
    5109                 : 
    5110                 : void
    5111            2713 : nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet()
    5112                 : {
    5113                 :     // See RFC 2616 section 5.1.1. These are considered valid
    5114                 :     // methods which DO NOT invalidate cache-entries for the
    5115                 :     // referred resource. POST, PUT and DELETE as well as any
    5116                 :     // other method not listed here will potentially invalidate
    5117                 :     // any cached copy of the resource
    5118            7483 :     if (mRequestHead.Method() == nsHttp::Options ||
    5119            2713 :        mRequestHead.Method() == nsHttp::Get ||
    5120             695 :        mRequestHead.Method() == nsHttp::Head ||
    5121             681 :        mRequestHead.Method() == nsHttp::Trace ||
    5122             681 :        mRequestHead.Method() == nsHttp::Connect)
    5123            2032 :         return;
    5124                 : 
    5125                 : 
    5126                 :     // Invalidate the request-uri.
    5127                 :     // Pass 0 in first param to get the cache-key for a GET-request.
    5128            1362 :     nsCAutoString tmpCacheKey;
    5129             681 :     GenerateCacheKey(0, tmpCacheKey);
    5130             681 :     LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n", 
    5131                 :         this, tmpCacheKey.get()));
    5132             681 :     DoInvalidateCacheEntry(tmpCacheKey);
    5133                 : 
    5134                 :     // Invalidate Location-header if set
    5135             681 :     const char *location = mResponseHead->PeekHeader(nsHttp::Location);
    5136             681 :     if (location) {
    5137              36 :         LOG(("  Location-header=%s\n", location));
    5138              36 :         InvalidateCacheEntryForLocation(location);
    5139                 :     }
    5140                 : 
    5141                 :     // Invalidate Content-Location-header if set
    5142             681 :     location = mResponseHead->PeekHeader(nsHttp::Content_Location);
    5143             681 :     if (location) {
    5144               2 :         LOG(("  Content-Location-header=%s\n", location));
    5145               2 :         InvalidateCacheEntryForLocation(location);
    5146                 :     }
    5147                 : }
    5148                 : 
    5149                 : void
    5150              38 : nsHttpChannel::InvalidateCacheEntryForLocation(const char *location)
    5151                 : {
    5152              76 :     nsCAutoString tmpCacheKey, tmpSpec;
    5153              76 :     nsCOMPtr<nsIURI> resultingURI;
    5154              38 :     nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI));
    5155              38 :     if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) {
    5156              38 :         if (NS_SUCCEEDED(resultingURI->GetAsciiSpec(tmpSpec))) {
    5157              38 :             location = tmpSpec.get();  //reusing |location|
    5158                 : 
    5159                 :             // key for a GET-request to |location| with current load-flags
    5160              38 :             AssembleCacheKey(location, 0, tmpCacheKey);
    5161              38 :             DoInvalidateCacheEntry(tmpCacheKey);
    5162                 :         } else
    5163               0 :             NS_WARNING(("  failed getting ascii-spec\n"));
    5164                 :     } else {
    5165               0 :         LOG(("  hosts not matching\n"));
    5166                 :     }
    5167              38 : }
    5168                 : 
    5169                 : void
    5170             719 : nsHttpChannel::DoInvalidateCacheEntry(nsACString &key)
    5171                 : {
    5172                 :     // NOTE:
    5173                 :     // Following comments 24,32 and 33 in bug #327765, we only care about
    5174                 :     // the cache in the protocol-handler, not the application cache.
    5175                 :     // The logic below deviates from the original logic in OpenCacheEntry on
    5176                 :     // one point by using only READ_ONLY access-policy. I think this is safe.
    5177                 : 
    5178                 :     // First, find session holding the cache-entry - use current storage-policy
    5179            1438 :     nsCOMPtr<nsICacheSession> session;
    5180             719 :     nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
    5181                 : 
    5182                 :     nsresult rv = gHttpHandler->GetCacheSession(storagePolicy,
    5183             719 :                                                 getter_AddRefs(session));
    5184                 : 
    5185             719 :     if (NS_FAILED(rv))
    5186                 :         return;
    5187                 : 
    5188                 :     // Now, find the actual cache-entry
    5189            1438 :     nsCOMPtr<nsICacheEntryDescriptor> tmpCacheEntry;
    5190             719 :     rv = session->OpenCacheEntry(key, nsICache::ACCESS_READ,
    5191                 :                                  false,
    5192             719 :                                  getter_AddRefs(tmpCacheEntry));
    5193                 : 
    5194                 :     // If entry was found, set its expiration-time = 0
    5195             719 :     if(NS_SUCCEEDED(rv)) {
    5196              63 :         tmpCacheEntry->SetExpirationTime(0);
    5197              63 :         LOG(("  cache-entry invalidated [key=%s]\n", key.Data()));
    5198                 :     } else {
    5199             656 :         LOG(("  cache-entry not found [key=%s]\n", key.Data()));
    5200                 :     }
    5201                 : }
    5202                 : 
    5203                 : nsCacheStoragePolicy
    5204            2614 : nsHttpChannel::DetermineStoragePolicy()
    5205                 : {
    5206            2614 :     nsCacheStoragePolicy policy = nsICache::STORE_ANYWHERE;
    5207            2614 :     if (mLoadFlags & INHIBIT_PERSISTENT_CACHING)
    5208               0 :         policy = nsICache::STORE_IN_MEMORY;
    5209                 : 
    5210            2614 :     return policy;
    5211                 : }
    5212                 : 
    5213                 : nsresult
    5214            4798 : nsHttpChannel::DetermineCacheAccess(nsCacheAccessMode *_retval)
    5215                 : {
    5216            4798 :     bool offline = gIOService->IsOffline();
    5217                 : 
    5218            4798 :     if (offline || (mLoadFlags & INHIBIT_CACHING)) {
    5219                 :         // If we have been asked to bypass the cache and not write to the
    5220                 :         // cache, then don't use the cache at all.  Unless we're actually
    5221                 :         // offline, which takes precedence over BYPASS_LOCAL_CACHE.
    5222            1013 :         if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline)
    5223            1004 :             return NS_ERROR_NOT_AVAILABLE;
    5224               9 :         *_retval = nsICache::ACCESS_READ;
    5225                 :     }
    5226            3785 :     else if (BYPASS_LOCAL_CACHE(mLoadFlags))
    5227             866 :         *_retval = nsICache::ACCESS_WRITE; // replace cache entry
    5228                 :     else
    5229            2919 :         *_retval = nsICache::ACCESS_READ_WRITE; // normal browsing
    5230                 : 
    5231            3794 :     return NS_OK;
    5232                 : }
    5233                 : 
    5234                 : void
    5235             220 : nsHttpChannel::AsyncOnExamineCachedResponse()
    5236                 : {
    5237             220 :     gHttpHandler->OnExamineCachedResponse(this);
    5238             220 : }

Generated by: LCOV version 1.7