LCOV - app.info - netwerk/protocol/http/nsHttpChannel.cpp

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-07-07 | 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            3513 : 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            3513 :     , mDidReval(false)
         143                 : {
         144            3513 :     LOG(("Creating nsHttpChannel [this=%p]\n", this));
         145            3513 :     mChannelCreationTime = PR_Now();
         146            3513 :     mChannelCreationTimestamp = mozilla::TimeStamp::Now();
         147            3513 : }
         148                 :
         149            9717 : nsHttpChannel::~nsHttpChannel()
         150                 : {
         151            3239 :     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
         152                 :
         153            3239 :     if (mAuthProvider)
         154             480 :         mAuthProvider->Disconnect(NS_ERROR_ABORT);
         155           12956 : }
         156                 :
         157                 : nsresult
         158            3513 : nsHttpChannel::Init(nsIURI *uri,
         159                 :                     PRUint8 caps,
         160                 :                     nsProxyInfo *proxyInfo)
         161                 : {
         162            3513 :     nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo);
         163            3513 :     if (NS_FAILED(rv))
         164               0 :         return rv;
         165                 :
         166            3513 :     LOG(("nsHttpChannel::Init [this=%p]\n", this));
         167                 :
         168                 :     mAuthProvider =
         169                 :         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
         170            3513 :                           &rv);
         171            3513 :     if (NS_FAILED(rv))
         172               0 :         return rv;
         173            3513 :     rv = mAuthProvider->Init(this);
         174                 :
         175            3513 :     return rv;
         176                 : }
         177                 : //-----------------------------------------------------------------------------
         178                 : // nsHttpChannel <private>
         179                 : //-----------------------------------------------------------------------------
         180                 :
         181                 : nsresult
         182            5054 : nsHttpChannel::Connect(bool firstTime)
         183                 : {
         184                 :     nsresult rv;
         185                 :
         186            5054 :     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            5054 :     bool usingSSL = false;
         193            5054 :     rv = mURI->SchemeIs("https", &usingSSL);
         194            5054 :     NS_ENSURE_SUCCESS(rv,rv);
         195                 :
         196            5054 :     if (!usingSSL) {
         197                 :         // enforce Strict-Transport-Security
         198            5046 :         nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
         199            5046 :         NS_ENSURE_TRUE(stss, NS_ERROR_OUT_OF_MEMORY);
         200                 :
         201            5046 :         bool isStsHost = false;
         202            5046 :         rv = stss->IsStsURI(mURI, &isStsHost);
         203                 :
         204                 :         // if STS fails, there's no reason to cancel the load, but it's
         205                 :         // worrisome.
         206            5046 :         NS_ASSERTION(NS_SUCCEEDED(rv),
         207                 :                      "Something is wrong with STS: IsStsURI failed.");
         208                 :
         209            5046 :         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            5046 :         if (gHttpHandler->IsSpdyEnabled() && mAllowSpdy) {
         216           10092 :             nsCAutoString hostPort;
         217                 :
         218           10092 :             if (NS_SUCCEEDED(mURI->GetHostPort(hostPort)) &&
         219            5046 :                 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            5054 :     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
         229               0 :         return NS_ERROR_UNKNOWN_HOST;
         230                 :
         231                 :     // true when called from AsyncOpen
         232            5054 :     if (firstTime) {
         233                 :         // are we offline?
         234            3481 :         bool offline = gIOService->IsOffline();
         235            3481 :         if (offline)
         236               3 :             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
         237            3478 :         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            3471 :         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            3471 :         rv = OpenCacheEntry();
         248                 :
         249            3471 :         if (NS_FAILED(rv)) {
         250            1006 :             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            1006 :             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            3471 :         if (mCacheForOfflineUse) {
         267              24 :             rv = OpenOfflineCacheEntryForWriting();
         268              24 :             if (NS_FAILED(rv)) return rv;
         269                 :         }
         270                 :
         271            3471 :         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            3191 :     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            1575 :     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            2965 :     if (mLoadFlags & LOAD_NO_NETWORK_IO) {
         333               0 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
         334                 :     }
         335                 :
         336                 :     // hit the net...
         337            2965 :     rv = SetupTransaction();
         338            2965 :     if (NS_FAILED(rv)) return rv;
         339                 :
         340            2965 :     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
         341            2965 :     if (NS_FAILED(rv)) return rv;
         342                 :
         343            2953 :     rv = mTransactionPump->AsyncRead(this, nsnull);
         344            2953 :     if (NS_FAILED(rv)) return rv;
         345                 :
         346            2953 :     PRUint32 suspendCount = mSuspendCount;
         347            5906 :     while (suspendCount--)
         348               0 :         mTransactionPump->Suspend();
         349                 :
         350            2953 :     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            2988 : nsHttpChannel::SetupTransaction()
         493                 : {
         494            2988 :     LOG(("nsHttpChannel::SetupTransaction [this=%p]\n", this));
         495                 :
         496            2988 :     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
         497                 :
         498                 :     nsresult rv;
         499                 :
         500            2988 :     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            2988 :     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            5976 :     nsCAutoString buf, path;
         526                 :     nsCString* requestURI;
         527            8956 :     if (mConnectionInfo->UsingSSL() ||
         528            2984 :         mConnectionInfo->ShouldForceConnectMethod() ||
         529            2984 :         !mConnectionInfo->UsingHttpProxy()) {
         530            2961 :         rv = mURI->GetPath(path);
         531            2961 :         if (NS_FAILED(rv)) return rv;
         532                 :         // path may contain UTF-8 characters, so ensure that they're escaped.
         533            2961 :         if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf))
         534               0 :             requestURI = &buf;
         535                 :         else
         536            2961 :             requestURI = &path;
         537            2961 :         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            2988 :     PRInt32 ref = requestURI->FindChar('#');
         560            2988 :     if (ref != kNotFound)
         561               0 :         requestURI->SetLength(ref);
         562                 :
         563            2988 :     mRequestHead.SetRequestURI(*requestURI);
         564                 :
         565                 :     // set the request time for cache expiration calculations
         566            2988 :     mRequestTime = NowInSeconds();
         567            2988 :     mRequestTimeInitialized = true;
         568                 :
         569                 :     // if doing a reload, force end-to-end
         570            2988 :     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            1919 :         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            1919 :         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
         578            1919 :             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            2988 :     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            5976 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
         623                 :     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
         624            2988 :                                            getter_AddRefs(callbacks));
         625            2988 :     if (!callbacks)
         626               0 :         return NS_ERROR_OUT_OF_MEMORY;
         627                 :
         628                 :     // create the transaction object
         629            2988 :     mTransaction = new nsHttpTransaction();
         630            2988 :     if (!mTransaction)
         631               0 :         return NS_ERROR_OUT_OF_MEMORY;
         632                 :
         633                 :     // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
         634            2988 :     if (mLoadFlags & LOAD_ANONYMOUS)
         635               1 :         mCaps |= NS_HTTP_LOAD_ANONYMOUS;
         636                 :
         637            2988 :     if (mTimingEnabled)
         638               2 :         mCaps |= NS_HTTP_TIMING_ENABLED;
         639                 :
         640            2988 :     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
         641                 :
         642            2988 :     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            5976 :     nsCOMPtr<nsIAsyncInputStream> responseStream;
         654                 :     rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
         655                 :                             mUploadStream, mUploadStreamHasHeaders,
         656            2988 :                             NS_GetCurrentThread(), callbacks, this,
         657            5976 :                             getter_AddRefs(responseStream));
         658            2988 :     if (NS_FAILED(rv)) {
         659               0 :         mTransaction = nsnull;
         660               0 :         return rv;
         661                 :     }
         662                 :
         663            2988 :     rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
         664            5976 :                                    responseStream);
         665            2988 :     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            3036 : nsHttpChannel::CallOnStartRequest()
         691                 : {
         692            3036 :     mTracingEnabled = false;
         693                 :
         694            3036 :     if (mResponseHead && mResponseHead->ContentType().IsEmpty()) {
         695             947 :         NS_ASSERTION(mConnectionInfo, "Should have connection info here");
         696             947 :         if (!mContentTypeHint.IsEmpty())
         697              57 :             mResponseHead->SetContentType(mContentTypeHint);
         698             891 :         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            1778 :             nsCOMPtr<nsIStreamConverterService> serv;
         708                 :             nsresult rv = gHttpHandler->
         709             889 :                 GetStreamConverterService(getter_AddRefs(serv));
         710                 :             // If we failed, we just fall through to the "normal" case
         711             889 :             if (NS_SUCCEEDED(rv)) {
         712            1778 :                 nsCOMPtr<nsIStreamListener> converter;
         713             889 :                 rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
         714                 :                                             "*/*",
         715                 :                                             mListener,
         716                 :                                             mListenerContext,
         717             889 :                                             getter_AddRefs(converter));
         718             889 :                 if (NS_SUCCEEDED(rv)) {
         719             889 :                     mListener = converter;
         720                 :                 }
         721                 :             }
         722                 :         }
         723                 :     }
         724                 :
         725            3036 :     if (mResponseHead && mResponseHead->ContentCharset().IsEmpty())
         726            2876 :         mResponseHead->SetContentCharset(mContentCharsetHint);
         727                 :
         728            3036 :     if (mResponseHead) {
         729                 :         SetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH,
         730            2880 :                            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            2880 :         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            3037 :     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            3036 :     LOG(("  calling mListener->OnStartRequest\n"));
         764            3036 :     nsresult rv = mListener->OnStartRequest(this, mListenerContext);
         765            3036 :     if (NS_FAILED(rv)) return rv;
         766                 :
         767                 :     // install stream converter if required
         768            3020 :     rv = ApplyContentConversions();
         769            3020 :     if (NS_FAILED(rv)) return rv;
         770                 :
         771                 :     // if this channel is for a download, close off access to the cache.
         772            3020 :     if (mCacheEntry && mChannelIsForDownload) {
         773               6 :         mCacheEntry->Doom();
         774               6 :         CloseCacheEntry(false);
         775                 :     }
         776                 :
         777            3020 :     if (!mCanceled) {
         778                 :         // create offline cache entry if offline caching was requested
         779            3002 :         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            3020 :     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            2818 : nsHttpChannel::ProcessSTSHeader()
         893                 : {
         894                 :     nsresult rv;
         895            2818 :     bool isHttps = false;
         896            2818 :     rv = mURI->SchemeIs("https", &isHttps);
         897            2818 :     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            2818 :     if (!isHttps)
         902            2814 :         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            2818 : nsHttpChannel::ProcessResponse()
         974                 : {
         975                 :     nsresult rv;
         976            2818 :     PRUint32 httpStatus = mResponseHead->Status();
         977                 :
         978            2818 :     LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n",
         979                 :         this, httpStatus));
         980                 :
         981            2818 :     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            2818 :         rv = ProcessSTSHeader();
         989            2818 :         NS_ASSERTION(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
         990                 :     }
         991                 :
         992                 :     // notify "http-on-examine-response" observers
         993            2818 :     gHttpHandler->OnExamineResponse(this);
         994                 :
         995            2818 :     SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
         996                 :
         997                 :     // handle unused username and password in url (see bug 232567)
         998            2818 :     if (httpStatus != 401 && httpStatus != 407) {
         999            2759 :         if (!mAuthRetryPending)
        1000            2759 :             mAuthProvider->CheckForSuperfluousAuth();
        1001            2759 :         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            2759 :         mAuthProvider->Disconnect(NS_ERROR_ABORT);
        1007            2759 :         mAuthProvider = nsnull;
        1008            2759 :         LOG(("  continuation state has been reset"));
        1009                 :     }
        1010                 :
        1011            2818 :     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            2818 :     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              24 :         rv = ProcessNotModified();
        1060              24 :         if (NS_FAILED(rv)) {
        1061              18 :             LOG(("ProcessNotModified failed [rv=%x]\n", rv));
        1062              18 :             rv = ProcessNormal();
        1063                 :         }
        1064                 :         else {
        1065               6 :             successfulReval = true;
        1066                 :         }
        1067              24 :         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            2818 :     if (!mDidReval)
        1104            2467 :         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            2818 :             cacheDisposition);
        1112            2818 :     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            2818 :     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            2660 : nsHttpChannel::ProcessNormal()
        1177                 : {
        1178                 :     nsresult rv;
        1179                 :
        1180            2660 :     LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
        1181                 :
        1182                 :     bool succeeded;
        1183            2660 :     rv = GetRequestSucceeded(&succeeded);
        1184            2660 :     if (NS_SUCCEEDED(rv) && !succeeded) {
        1185             675 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
        1186                 :         bool waitingForRedirectCallback;
        1187             675 :         (void)ProcessFallback(&waitingForRedirectCallback);
        1188             675 :         if (waitingForRedirectCallback) {
        1189                 :             // The transaction has been suspended by ProcessFallback.
        1190               3 :             return NS_OK;
        1191                 :         }
        1192             672 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
        1193                 :     }
        1194                 :
        1195            2657 :     return ContinueProcessNormal(NS_OK);
        1196                 : }
        1197                 :
        1198                 : nsresult
        1199            2660 : nsHttpChannel::ContinueProcessNormal(nsresult rv)
        1200                 : {
        1201            2660 :     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            2658 :     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            2657 :     mCachedContentIsPartial = false;
        1219                 :
        1220            2657 :     ClearBogusContentEncodingIfNeeded();
        1221                 :
        1222            2657 :     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            2657 :     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            2657 :     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            2657 :     rv = CallOnStartRequest();
        1261            2657 :     if (NS_FAILED(rv)) return rv;
        1262                 :
        1263                 :     // install cache listener if we still have a cache entry open
        1264            2647 :     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE)) {
        1265            1132 :         rv = InstallCacheListener();
        1266            1132 :         if (NS_FAILED(rv)) return rv;
        1267                 :     }
        1268                 :
        1269            2647 :     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              24 : nsHttpChannel::ProcessNotModified()
        1845                 : {
        1846                 :     nsresult rv;
        1847                 :
        1848              24 :     LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this));
        1849                 :
        1850              24 :     if (mCustomConditionalRequest) {
        1851               0 :         LOG(("Bypassing ProcessNotModified due to custom conditional headers"));
        1852               0 :         return NS_ERROR_FAILURE;
        1853                 :     }
        1854                 :
        1855              24 :     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             836 : nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback)
        1893                 : {
        1894             836 :     LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
        1895                 :     nsresult rv;
        1896                 :
        1897             836 :     *waitingForRedirectCallback = false;
        1898             836 :     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             836 :     if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) {
        1904             827 :         LOG(("  choosing not to fallback [%p,%s,%d]",
        1905                 :              mApplicationCache.get(), mFallbackKey.get(), mFallbackChannel));
        1906             827 :         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            2949 : IsSubRangeRequest(nsHttpRequestHead &aRequestHead)
        2023                 : {
        2024            2949 :     if (!aRequestHead.PeekHeader(nsHttp::Range))
        2025            2926 :         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            3471 : nsHttpChannel::OpenCacheEntry()
        2033                 : {
        2034                 :     nsresult rv;
        2035                 :
        2036            3471 :     mAsyncCacheOpen = false;
        2037            3471 :     mLoadedFromApplicationCache = false;
        2038                 :
        2039            3471 :     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
        2040                 :
        2041                 :     // make sure we're not abusing this function
        2042            3471 :     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
        2043                 :
        2044            6942 :     nsCAutoString cacheKey;
        2045                 :
        2046            3471 :     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            3830 :     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            2952 :     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            2925 :     if (IsSubRangeRequest(mRequestHead))
        2068              23 :         return NS_OK;
        2069                 :
        2070            2902 :     GenerateCacheKey(mPostID, cacheKey);
        2071                 :
        2072                 :     // Set the desired cache access mode accordingly...
        2073                 :     nsCacheAccessMode accessRequested;
        2074            2902 :     rv = DetermineCacheAccess(&accessRequested);
        2075            2902 :     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            5528 : nsHttpChannel::GenerateCacheKey(PRUint32 postID, nsACString &cacheKey)
        2395                 : {
        2396            5528 :     AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
        2397           11056 :                      postID, cacheKey);
        2398            5528 :     return NS_OK;
        2399                 : }
        2400                 :
        2401                 : // Assembles a cache-key from the given pieces of information and |mLoadFlags|
        2402                 : void
        2403            5566 : nsHttpChannel::AssembleCacheKey(const char *spec, PRUint32 postID,
        2404                 :                                 nsACString &cacheKey)
        2405                 : {
        2406            5566 :     cacheKey.Truncate();
        2407                 :
        2408            5566 :     if (mLoadFlags & LOAD_ANONYMOUS) {
        2409               2 :         cacheKey.AssignLiteral("anon&");
        2410                 :     }
        2411                 :
        2412            5566 :     if (postID) {
        2413                 :         char buf[32];
        2414             190 :         PR_snprintf(buf, sizeof(buf), "id=%x&", postID);
        2415             190 :         cacheKey.Append(buf);
        2416                 :     }
        2417                 :
        2418            5566 :     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            5566 :     const char *p = strchr(spec, '#');
        2424            5566 :     if (p)
        2425               0 :         cacheKey.Append(spec, p - spec);
        2426                 :     else
        2427            5566 :         cacheKey.Append(spec);
        2428            5566 : }
        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             850 :         PRUint32 freshnessLifetime = 0;
        2449                 :
        2450             850 :         rv = mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
        2451             850 :         if (NS_FAILED(rv)) return rv;
        2452                 :
        2453             850 :         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             266 :         LOG(("Validating based on MustValidate() returning TRUE\n"));
        2637             266 :         doValidation = true;
        2638                 :     }
        2639                 :
        2640             249 :     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             243 :         PRUint32 time = 0; // a temporary variable for storing time values...
        2648                 :
        2649             243 :         rv = mCacheEntry->GetExpirationTime(&time);
        2650             243 :         NS_ENSURE_SUCCESS(rv, rv);
        2651                 :
        2652             243 :         if (NowInSeconds() <= time)
        2653             194 :             doValidation = false;
        2654              49 :         else if (mCachedResponseHead->MustValidateIfExpired())
        2655               0 :             doValidation = true;
        2656              49 :         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              49 :             doValidation = true;
        2672                 :
        2673             243 :         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             249 : 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             249 :     if (mRequestHead.Method() == nsHttp::Get)
        2779                 :     {
        2780             470 :         nsCAutoString query;
        2781             470 :         nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
        2782             235 :         nsresult rv = url->GetQuery(query);
        2783             235 :         if (NS_SUCCEEDED(rv) && !query.IsEmpty()) {
        2784                 :             PRUint32 tmp; // we don't need the value, just whether it's set
        2785              15 :             rv = mCachedResponseHead->GetExpiresValue(&tmp);
        2786              15 :             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             243 :     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            2893 : 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            2893 :     if (mResponseHead->NoStore())
        3035               6 :         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
        3036                 :
        3037                 :     // Only cache SSL content on disk if the pref is set
        3038            2893 :     if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
        3039               0 :         mConnectionInfo->UsingSSL())
        3040               0 :         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
        3041            2893 : }
        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            2659 : 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            2670 :     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            2657 :     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            2659 : }
        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           89749 : NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
        3628           89475 : NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
        3629                 :
        3630          140165 : NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
        3631          140165 :     NS_INTERFACE_MAP_ENTRY(nsIRequest)
        3632          133631 :     NS_INTERFACE_MAP_ENTRY(nsIChannel)
        3633          122171 :     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
        3634          122171 :     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
        3635          118978 :     NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
        3636          103441 :     NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
        3637          103441 :     NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
        3638          102632 :     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
        3639          101737 :     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
        3640           97531 :     NS_INTERFACE_MAP_ENTRY(nsICacheListener)
        3641           97531 :     NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
        3642           95472 :     NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
        3643           95427 :     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
        3644           95427 :     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
        3645           95425 :     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
        3646           95415 :     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
        3647           95415 :     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
        3648           95415 :     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
        3649           95415 :     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
        3650           95222 :     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
        3651           94912 :     NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
        3652           94753 : 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            3488 : nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
        3737                 : {
        3738            3488 :     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
        3739                 :
        3740            3488 :     NS_ENSURE_ARG_POINTER(listener);
        3741            3488 :     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
        3742            3485 :     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
        3743                 :
        3744                 :     nsresult rv;
        3745                 :
        3746            3482 :     if (mCanceled)
        3747               0 :         return mStatus;
        3748                 :
        3749            3482 :     rv = NS_CheckPortSafety(mURI);
        3750            3482 :     if (NS_FAILED(rv))
        3751               0 :         return rv;
        3752                 :
        3753            3482 :     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            6940 :         mDNSPrefetch = new nsDNSPrefetch(mURI, mTimingEnabled);
        3766            3470 :         mDNSPrefetch->PrefetchHigh();
        3767                 :     }
        3768                 :
        3769                 :     // Remember the cookie header that was set, if any
        3770            3482 :     const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
        3771            3482 :     if (cookieHeader) {
        3772               8 :         mUserSetCookieHeader = cookieHeader;
        3773                 :     }
        3774                 :
        3775            3482 :     AddCookiesToRequest();
        3776                 :
        3777                 :     // check to see if authorization headers should be included
        3778            3482 :     mAuthProvider->AddAuthorizationHeaders();
        3779                 :
        3780                 :     // notify "http-on-modify-request" observers
        3781            3482 :     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            3482 :     if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close"))
        3787               0 :         mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING);
        3788                 :
        3789            3482 :     if ((mLoadFlags & VALIDATE_ALWAYS) ||
        3790                 :         (BYPASS_LOCAL_CACHE(mLoadFlags)))
        3791            1930 :         mCaps |= NS_HTTP_REFRESH_DNS;
        3792                 :
        3793                 :     // Force-Reload should reset the persistent connection pool for this host
        3794            3482 :     if (mLoadFlags & LOAD_FRESH_CONNECTION)
        3795               0 :         mCaps |= NS_HTTP_CLEAR_KEEPALIVES;
        3796                 :
        3797            3482 :     mIsPending = true;
        3798            3482 :     mWasOpened = true;
        3799                 :
        3800            3482 :     mListener = listener;
        3801            3482 :     mListenerContext = context;
        3802                 :
        3803                 :     // add ourselves to the load group.  from this point forward, we'll report
        3804                 :     // all failures asynchronously.
        3805            3482 :     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            3482 :     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            3482 :     if (mCanceled)
        3818               1 :         rv = mStatus;
        3819                 :     else
        3820            3481 :         rv = Connect();
        3821            3482 :     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            3480 :     } 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            3482 :     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            3541 : nsHttpChannel::GetProxyInfo(nsIProxyInfo **result)
        3901                 : {
        3902            3541 :     if (!mConnectionInfo)
        3903               0 :         *result = nsnull;
        3904                 :     else {
        3905            3541 :         *result = mConnectionInfo->ProxyInfo();
        3906            3541 :         NS_IF_ADDREF(*result);
        3907                 :     }
        3908            3541 :     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            3513 : nsHttpChannel::GetIsSSL(bool *aIsSSL)
        4052                 : {
        4053            3513 :     *aIsSSL = mConnectionInfo->UsingSSL();
        4054            3513 :     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           12496 : nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
        4109                 : {
        4110           12496 :     return HttpBaseChannel::GetLoadFlags(aLoadFlags);
        4111                 : }
        4112                 :
        4113                 : NS_IMETHODIMP
        4114           11341 : nsHttpChannel::GetURI(nsIURI **aURI)
        4115                 : {
        4116           11341 :     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             780 : nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
        4127                 : {
        4128             780 :     return HttpBaseChannel::GetLoadGroup(aLoadGroup);
        4129                 : }
        4130                 :
        4131                 : NS_IMETHODIMP
        4132            5398 : nsHttpChannel::GetRequestMethod(nsACString& aMethod)
        4133                 : {
        4134            5398 :     return HttpBaseChannel::GetRequestMethod(aMethod);
        4135                 : }
        4136                 :
        4137                 :
        4138                 : //-----------------------------------------------------------------------------
        4139                 : // nsHttpChannel::nsIRequestObserver
        4140                 : //-----------------------------------------------------------------------------
        4141                 :
        4142                 : NS_IMETHODIMP
        4143            3193 : nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
        4144                 : {
        4145            6386 :     SAMPLE_LABEL("nsHttpChannel", "OnStartRequest");
        4146            3193 :     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            3188 :         request->GetStatus(&mStatus);
        4150                 :     }
        4151                 :
        4152            3193 :     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            3193 :     NS_ASSERTION(request == mCachePump || request == mTransactionPump,
        4157                 :                  "Unexpected request");
        4158            3193 :     NS_ASSERTION(!(mTransactionPump && mCachePump) || mCachedContentIsPartial,
        4159                 :                  "If we have both pumps, the cache content must be partial");
        4160                 :
        4161            3193 :     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            2976 :         mSecurityInfo = mTransaction->SecurityInfo();
        4165                 :     }
        4166                 :
        4167            3195 :     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            3193 :     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            2818 :         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            2818 :         if (mResponseHead)
        4186            2818 :             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            3193 : nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
        4250                 : {
        4251            6386 :     SAMPLE_LABEL("network", "nsHttpChannel::OnStopRequest");
        4252            3193 :     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n",
        4253                 :         this, request, status));
        4254                 :
        4255            3193 :     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            3193 :      bool contentComplete = NS_SUCCEEDED(status);
        4261                 :
        4262                 :     // honor the cancelation status even if the underlying transaction completed.
        4263            3193 :     if (mCanceled || NS_FAILED(mStatus))
        4264             318 :         status = mStatus;
        4265                 :
        4266            3193 :     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            3191 :     if (mTransaction) {
        4288                 :         // determine if we should call DoAuthRetry
        4289            2976 :         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            5952 :         nsRefPtr<nsAHttpConnection> conn;
        4301            2976 :         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            5952 :         nsRefPtr<nsAHttpConnection> stickyConn;
        4311            2976 :         if (mCaps & NS_HTTP_STICKY_CONNECTION)
        4312              23 :             stickyConn = mTransaction->Connection();
        4313                 :
        4314                 :         // at this point, we're done with the transaction
        4315            2976 :         mTransactionTimings = mTransaction->Timings();
        4316            2976 :         mTransaction = nsnull;
        4317            2976 :         mTransactionPump = 0;
        4318                 :
        4319                 :         // We no longer need the dns prefetch object
        4320            2976 :         if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) {
        4321                 :             mTransactionTimings.domainLookupStart =
        4322               0 :                 mDNSPrefetch->StartTimestamp();
        4323                 :             mTransactionTimings.domainLookupEnd =
        4324               0 :                 mDNSPrefetch->EndTimestamp();
        4325                 :         }
        4326            2976 :         mDNSPrefetch = nsnull;
        4327                 :
        4328                 :         // handle auth retry...
        4329            2976 :         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            2953 :         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            2953 :         if (mTransactionReplaced)
        4347               6 :             return NS_OK;
        4348                 :
        4349            2947 :         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            3162 :     mIsPending = false;
        4367            3162 :     mStatus = status;
        4368                 :
        4369                 :     // perform any final cache operations before we close the cache entry.
        4370            3162 :     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) &&
        4371                 :         mRequestTimeInitialized){
        4372            1272 :         FinalizeCacheEntry();
        4373                 :     }
        4374                 :
        4375            3162 :     if (mListener) {
        4376            3038 :         LOG(("  calling OnStopRequest\n"));
        4377            3038 :         mListener->OnStopRequest(this, mListenerContext, status);
        4378            3038 :         mListener = 0;
        4379            3038 :         mListenerContext = 0;
        4380                 :     }
        4381                 :
        4382            3162 :     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            3162 :     if (mOfflineCacheEntry)
        4404              24 :         CloseOfflineCacheEntry();
        4405                 :
        4406            3162 :     if (mLoadGroup)
        4407               2 :         mLoadGroup->RemoveRequest(this, nsnull, status);
        4408                 :
        4409                 :     // We don't need this info anymore
        4410            3162 :     CleanRedirectCacheChainIfNecessary();
        4411                 :
        4412            3162 :     mCallbacks = nsnull;
        4413            3162 :     mProgressSink = nsnull;
        4414                 :
        4415            3162 :     return NS_OK;
        4416                 : }
        4417                 :
        4418                 : //-----------------------------------------------------------------------------
        4419                 : // nsHttpChannel::nsIStreamListener
        4420                 : //-----------------------------------------------------------------------------
        4421                 :
        4422                 : NS_IMETHODIMP
        4423            3065 : nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
        4424                 :                                nsIInputStream *input,
        4425                 :                                PRUint32 offset, PRUint32 count)
        4426                 : {
        4427            6130 :     SAMPLE_LABEL("network", "nsHttpChannel::OnDataAvailable");
        4428            3065 :     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            3065 :     if (mCanceled)
        4433               0 :         return mStatus;
        4434                 :
        4435            3065 :     NS_ASSERTION(mResponseHead, "No response head in ODA!!");
        4436                 :
        4437            3065 :     NS_ASSERTION(!(mCachedContentIsPartial && (request == mTransactionPump)),
        4438                 :             "transaction pump not suspended");
        4439                 :
        4440            3065 :     if (mAuthRetryPending || (request == mTransactionPump && mTransactionReplaced)) {
        4441                 :         PRUint32 n;
        4442              23 :         return input->ReadSegments(NS_DiscardSegment, nsnull, count, &n);
        4443                 :     }
        4444                 :
        4445            3042 :     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            3042 :         if (request == mCachePump)
        4453             217 :             transportStatus = nsITransport::STATUS_READING;
        4454                 :         else
        4455            2825 :             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            3042 :         PRUint64 progressMax(PRUint64(mResponseHead->ContentLength()));
        4463            3042 :         PRUint64 progress = mLogicalOffset + PRUint64(count);
        4464            3042 :         NS_ASSERTION(progress <= progressMax, "unexpected progress values");
        4465                 :
        4466            3042 :         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            3042 :         nsresult rv =  mListener->OnDataAvailable(this,
        4475                 :                                                   mListenerContext,
        4476                 :                                                   input,
        4477                 :                                                   mLogicalOffset,
        4478            3042 :                                                   count);
        4479            3042 :         if (NS_SUCCEEDED(rv))
        4480            3038 :             mLogicalOffset = progress;
        4481            3042 :         return rv;
        4482                 :     }
        4483                 :
        4484               0 :     return NS_ERROR_ABORT;
        4485                 : }
        4486                 :
        4487                 : //-----------------------------------------------------------------------------
        4488                 : // nsHttpChannel::nsITransportEventSink
        4489                 : //-----------------------------------------------------------------------------
        4490                 :
        4491                 : NS_IMETHODIMP
        4492            6835 : 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            6835 :     if (!mProgressSink)
        4497            5853 :         GetCallback(mProgressSink);
        4498                 :
        4499            6835 :     if (status == nsISocketTransport::STATUS_CONNECTED_TO ||
        4500                 :         status == nsISocketTransport::STATUS_WAITING_FOR) {
        4501                 :         nsCOMPtr<nsISocketTransport> socketTransport =
        4502            5802 :             do_QueryInterface(trans);
        4503            2901 :         if (socketTransport) {
        4504            2901 :             socketTransport->GetSelfAddr(&mSelfAddr);
        4505            2901 :             socketTransport->GetPeerAddr(&mPeerAddr);
        4506                 :         }
        4507                 :     }
        4508                 :
        4509                 :     // block socket status event after Cancel or OnStopRequest has been called.
        4510            6835 :     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
        4511             214 :         LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",
        4512                 :             this, status, progress, progressMax));
        4513                 :
        4514             428 :         nsCAutoString host;
        4515             214 :         mURI->GetHost(host);
        4516             214 :         mProgressSink->OnStatus(this, nsnull, status,
        4517             214 :                                 NS_ConvertUTF8toUTF16(host).get());
        4518                 :
        4519             214 :         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            6621 :         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            6835 :     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            1159 : nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func)
        5092                 : {
        5093            1159 :     mRedirectFuncStack.AppendElement(func);
        5094            1159 : }
        5095                 :
        5096                 : void
        5097             848 : nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func)
        5098                 : {
        5099             848 :     NS_ASSERTION(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
        5100                 :         "Trying to pop wrong method from redirect async stack!");
        5101                 :
        5102             848 :     mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
        5103             848 : }
        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            4797 : nsHttpChannel::DetermineCacheAccess(nsCacheAccessMode *_retval)
        5215                 : {
        5216            4797 :     bool offline = gIOService->IsOffline();
        5217                 :
        5218            4797 :     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            1012 :         if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline)
        5223            1003 :             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