Coverage Report

Created: 2018-09-25 14:53

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