Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/fetch/FetchDriver.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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
#include "mozilla/DebugOnly.h"
8
#include "mozilla/dom/FetchDriver.h"
9
10
#include "nsIAsyncVerifyRedirectCallback.h"
11
#include "nsIDocument.h"
12
#include "nsIInputStream.h"
13
#include "nsIOutputStream.h"
14
#include "nsIFileChannel.h"
15
#include "nsIHttpChannel.h"
16
#include "nsIHttpChannelInternal.h"
17
#include "nsIScriptSecurityManager.h"
18
#include "nsISupportsPriority.h"
19
#include "nsIThreadRetargetableRequest.h"
20
#include "nsIUploadChannel2.h"
21
#include "nsIInterfaceRequestorUtils.h"
22
#include "nsIPipe.h"
23
24
#include "nsContentPolicyUtils.h"
25
#include "nsDataHandler.h"
26
#include "nsNetUtil.h"
27
#include "nsPrintfCString.h"
28
#include "nsProxyRelease.h"
29
#include "nsStreamUtils.h"
30
#include "nsStringStream.h"
31
#include "nsHttpChannel.h"
32
33
#include "mozilla/dom/BlobURLProtocolHandler.h"
34
#include "mozilla/dom/File.h"
35
#include "mozilla/dom/PerformanceStorage.h"
36
#include "mozilla/dom/WorkerCommon.h"
37
#include "mozilla/EventStateManager.h"
38
#include "mozilla/ipc/PBackgroundSharedTypes.h"
39
#include "mozilla/Unused.h"
40
41
#include "Fetch.h"
42
#include "FetchUtil.h"
43
#include "InternalRequest.h"
44
#include "InternalResponse.h"
45
46
namespace mozilla {
47
namespace dom {
48
49
namespace {
50
51
bool
52
ShouldCheckSRI(const InternalRequest* const aRequest,
53
               const InternalResponse* const aResponse)
54
0
{
55
0
  MOZ_DIAGNOSTIC_ASSERT(aRequest);
56
0
  MOZ_DIAGNOSTIC_ASSERT(aResponse);
57
0
58
0
  return !aRequest->GetIntegrity().IsEmpty() &&
59
0
         aResponse->Type() != ResponseType::Error;
60
0
}
61
62
} // anonymous namespace
63
64
//-----------------------------------------------------------------------------
65
// AlternativeDataStreamListener
66
//-----------------------------------------------------------------------------
67
class AlternativeDataStreamListener final : public nsIStreamListener,
68
                                            public nsIThreadRetargetableStreamListener
69
{
70
public:
71
  NS_DECL_THREADSAFE_ISUPPORTS
72
  NS_DECL_NSIREQUESTOBSERVER
73
  NS_DECL_NSISTREAMLISTENER
74
  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
75
76
  // The status of AlternativeDataStreamListener
77
  // LOADING: is the initial status, loading the alternative data
78
  // COMPLETED: Alternative data loading is completed
79
  // CANCELED: Alternative data loading is canceled, this would make
80
  //           AlternativeDataStreamListener ignore all channel callbacks
81
  // FALLBACK: fallback the channel callbacks to FetchDriver
82
  // Depends on different situaions, the status transition could be followings
83
  // 1. LOADING->COMPLETED
84
  //    This is the normal status transition for alternative data loading
85
  //
86
  // 2. LOADING->CANCELED
87
  //    LOADING->COMPLETED->CANCELED
88
  //    Alternative data loading could be canceled when cacheId from alternative
89
  //    data channel does not match with from main data channel(The cacheID
90
  //    checking is in FetchDriver::OnStartRequest).
91
  //    Notice the alternative data loading could finish before the cacheID
92
  //    checking, so the statust transition could be LOADING->COMPLETED->CANCELED
93
  //
94
  // 3. LOADING->FALLBACK
95
  //    For the case that alternative data loading could not be initialized, i.e.
96
  //    alternative data does not exist or no preferred alternative data type is
97
  //    requested. Once the status becomes FALLBACK, AlternativeDataStreamListener
98
  //    transits the channel callback request to FetchDriver, and the status
99
  //    should not go back to LOADING, COMPLETED, or CANCELED anymore.
100
  enum eStatus {
101
    LOADING = 0,
102
    COMPLETED,
103
    CANCELED,
104
    FALLBACK
105
  };
106
107
  AlternativeDataStreamListener(FetchDriver* aFetchDriver,
108
                                nsIChannel* aChannel,
109
                                const nsACString& aAlternativeDataType);
110
  eStatus Status();
111
  void Cancel();
112
  uint64_t GetAlternativeDataCacheEntryId();
113
  const nsACString& GetAlternativeDataType() const;
114
  already_AddRefed<nsICacheInfoChannel> GetCacheInfoChannel();
115
  already_AddRefed<nsIInputStream> GetAlternativeInputStream();
116
117
private:
118
0
  ~AlternativeDataStreamListener() = default;
119
120
  // This creates a strong reference cycle with FetchDriver and its
121
  // mAltDataListener. We need to clear at least one reference of them once the
122
  // data loading finishes.
123
  RefPtr<FetchDriver> mFetchDriver;
124
  nsCString mAlternativeDataType;
125
  nsCOMPtr<nsIInputStream> mPipeAlternativeInputStream;
126
  nsCOMPtr<nsIOutputStream> mPipeAlternativeOutputStream;
127
  uint64_t mAlternativeDataCacheEntryId;
128
  nsCOMPtr<nsICacheInfoChannel> mCacheInfoChannel;
129
  nsCOMPtr<nsIChannel> mChannel;
130
  Atomic<eStatus> mStatus;
131
};
132
133
NS_IMPL_ISUPPORTS(AlternativeDataStreamListener,
134
                  nsIStreamListener,
135
                  nsIThreadRetargetableStreamListener)
136
137
AlternativeDataStreamListener::AlternativeDataStreamListener(FetchDriver* aFetchDriver,
138
                                                             nsIChannel* aChannel,
139
                                                             const nsACString& aAlternativeDataType)
140
  : mFetchDriver(aFetchDriver)
141
  , mAlternativeDataType(aAlternativeDataType)
142
  , mAlternativeDataCacheEntryId(0)
143
  , mChannel(aChannel)
144
  , mStatus(AlternativeDataStreamListener::LOADING)
145
0
{
146
0
  MOZ_DIAGNOSTIC_ASSERT(mFetchDriver);
147
0
  MOZ_DIAGNOSTIC_ASSERT(mChannel);
148
0
}
149
150
AlternativeDataStreamListener::eStatus
151
AlternativeDataStreamListener::Status()
152
0
{
153
0
  return mStatus;
154
0
}
155
156
void
157
AlternativeDataStreamListener::Cancel()
158
0
{
159
0
  mAlternativeDataCacheEntryId = 0;
160
0
  mCacheInfoChannel = nullptr;
161
0
  mPipeAlternativeOutputStream = nullptr;
162
0
  mPipeAlternativeInputStream = nullptr;
163
0
  if (mChannel && mStatus != AlternativeDataStreamListener::FALLBACK) {
164
0
    // if mStatus is fallback, we need to keep channel to forward request back to
165
0
    // FetchDriver
166
0
    mChannel->Cancel(NS_BINDING_ABORTED);
167
0
    mChannel = nullptr;
168
0
  }
169
0
  mStatus = AlternativeDataStreamListener::CANCELED;
170
0
}
171
172
uint64_t
173
AlternativeDataStreamListener::GetAlternativeDataCacheEntryId()
174
0
{
175
0
  return mAlternativeDataCacheEntryId;
176
0
}
177
178
const nsACString&
179
AlternativeDataStreamListener::GetAlternativeDataType() const
180
0
{
181
0
  return mAlternativeDataType;
182
0
}
183
184
already_AddRefed<nsIInputStream>
185
AlternativeDataStreamListener::GetAlternativeInputStream()
186
0
{
187
0
  nsCOMPtr<nsIInputStream> inputStream = mPipeAlternativeInputStream;
188
0
  return inputStream.forget();
189
0
}
190
191
already_AddRefed<nsICacheInfoChannel>
192
AlternativeDataStreamListener::GetCacheInfoChannel()
193
0
{
194
0
  nsCOMPtr<nsICacheInfoChannel> channel = mCacheInfoChannel;
195
0
  return channel.forget();
196
0
}
197
198
NS_IMETHODIMP
199
AlternativeDataStreamListener::OnStartRequest(nsIRequest* aRequest,
200
                                              nsISupports* aContext)
201
0
{
202
0
  AssertIsOnMainThread();
203
0
  MOZ_ASSERT(!mAlternativeDataType.IsEmpty());
204
0
  // Checking the alternative data type is the same between we asked and the
205
0
  // saved in the channel.
206
0
  nsAutoCString alternativeDataType;
207
0
  nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest);
208
0
  mStatus = AlternativeDataStreamListener::LOADING;
209
0
  if (cic &&
210
0
      NS_SUCCEEDED(cic->GetAlternativeDataType(alternativeDataType)) &&
211
0
      mAlternativeDataType.Equals(alternativeDataType) &&
212
0
      NS_SUCCEEDED(cic->GetCacheEntryId(&mAlternativeDataCacheEntryId))) {
213
0
214
0
    MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeInputStream);
215
0
    MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeOutputStream);
216
0
    nsresult rv = NS_NewPipe(
217
0
      getter_AddRefs(mPipeAlternativeInputStream),
218
0
      getter_AddRefs(mPipeAlternativeOutputStream),
219
0
      0 /* default segment size */,
220
0
      UINT32_MAX /* infinite pipe */,
221
0
      true /* non-blocking input, otherwise you deadlock */,
222
0
      false /* blocking output, since the pipe is 'in'finite */);
223
0
224
0
    if (NS_FAILED(rv)) {
225
0
      mFetchDriver->FailWithNetworkError(rv);
226
0
      return rv;
227
0
    }
228
0
229
0
    MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel);
230
0
    mCacheInfoChannel = cic;
231
0
232
0
    // call FetchDriver::HttpFetch to load main body
233
0
    MOZ_ASSERT(mFetchDriver);
234
0
    return mFetchDriver->HttpFetch();
235
0
236
0
  } else {
237
0
    // Needn't load alternative data, since alternative data does not exist.
238
0
    // Set status to FALLBACK to reuse the opened channel to load main body, then
239
0
    // call FetchDriver::OnStartRequest to continue the work.
240
0
    // Unfortunately can't change the stream listener to mFetchDriver, need to
241
0
    // keep AlternativeDataStreamListener alive to redirect OnDataAvailable and
242
0
    // OnStopRequest to mFetchDriver.
243
0
    MOZ_ASSERT(alternativeDataType.IsEmpty());
244
0
    mStatus = AlternativeDataStreamListener::FALLBACK;
245
0
    mAlternativeDataCacheEntryId = 0;
246
0
    MOZ_ASSERT(mFetchDriver);
247
0
    return mFetchDriver->OnStartRequest(aRequest, aContext);
248
0
  }
249
0
  return NS_OK;
250
0
}
251
252
NS_IMETHODIMP
253
AlternativeDataStreamListener::OnDataAvailable(nsIRequest* aRequest,
254
                                               nsISupports* aContext,
255
                                               nsIInputStream* aInputStream,
256
                                               uint64_t aOffset,
257
                                               uint32_t aCount)
258
0
{
259
0
  if (mStatus == AlternativeDataStreamListener::LOADING) {
260
0
    MOZ_ASSERT(mPipeAlternativeOutputStream);
261
0
    uint32_t read;
262
0
    return aInputStream->ReadSegments(NS_CopySegmentToStream,
263
0
                                      mPipeAlternativeOutputStream,
264
0
                                      aCount, &read);
265
0
  }
266
0
  if (mStatus == AlternativeDataStreamListener::FALLBACK) {
267
0
    MOZ_ASSERT(mFetchDriver);
268
0
    return mFetchDriver->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
269
0
  }
270
0
  return NS_OK;
271
0
}
272
273
NS_IMETHODIMP
274
AlternativeDataStreamListener::OnStopRequest(nsIRequest* aRequest,
275
                                             nsISupports* aContext,
276
                                             nsresult aStatusCode)
277
0
{
278
0
  AssertIsOnMainThread();
279
0
280
0
  // Alternative data loading is going to finish, breaking the reference cycle
281
0
  // here by taking the ownership to a loacl variable.
282
0
  RefPtr<FetchDriver> fetchDriver = mFetchDriver.forget();
283
0
284
0
  if (mStatus == AlternativeDataStreamListener::CANCELED) {
285
0
    // do nothing
286
0
    return NS_OK;
287
0
  }
288
0
289
0
  if (mStatus == AlternativeDataStreamListener::FALLBACK) {
290
0
    MOZ_ASSERT(fetchDriver);
291
0
    return fetchDriver->OnStopRequest(aRequest, aContext, aStatusCode);
292
0
  }
293
0
294
0
  MOZ_DIAGNOSTIC_ASSERT(mStatus == AlternativeDataStreamListener::LOADING);
295
0
296
0
  MOZ_ASSERT(!mAlternativeDataType.IsEmpty() &&
297
0
             mPipeAlternativeOutputStream &&
298
0
             mPipeAlternativeInputStream);
299
0
300
0
  mPipeAlternativeOutputStream->Close();
301
0
  mPipeAlternativeOutputStream = nullptr;
302
0
303
0
  // Cleanup the states for alternative data if needed.
304
0
  if (NS_FAILED(aStatusCode)) {
305
0
    mAlternativeDataCacheEntryId = 0;
306
0
    mCacheInfoChannel = nullptr;
307
0
    mPipeAlternativeInputStream = nullptr;
308
0
  }
309
0
  mStatus = AlternativeDataStreamListener::COMPLETED;
310
0
  // alternative data loading finish, call FetchDriver::FinishOnStopRequest to
311
0
  // continue the final step for the case FetchDriver::OnStopRequest is called
312
0
  // earlier than AlternativeDataStreamListener::OnStopRequest
313
0
  MOZ_ASSERT(fetchDriver);
314
0
  return fetchDriver->FinishOnStopRequest(this);
315
0
}
316
317
NS_IMETHODIMP
318
AlternativeDataStreamListener::CheckListenerChain()
319
0
{
320
0
  return NS_OK;
321
0
}
322
//-----------------------------------------------------------------------------
323
// FetchDriver
324
//-----------------------------------------------------------------------------
325
326
NS_IMPL_ISUPPORTS(FetchDriver,
327
                  nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
328
                  nsIThreadRetargetableStreamListener)
329
330
FetchDriver::FetchDriver(InternalRequest* aRequest,
331
                         nsIPrincipal* aPrincipal,
332
                         nsILoadGroup* aLoadGroup,
333
                         nsIEventTarget* aMainThreadEventTarget,
334
                         PerformanceStorage* aPerformanceStorage,
335
                         bool aIsTrackingFetch)
336
  : mPrincipal(aPrincipal)
337
  , mLoadGroup(aLoadGroup)
338
  , mRequest(aRequest)
339
  , mMainThreadEventTarget(aMainThreadEventTarget)
340
  , mPerformanceStorage(aPerformanceStorage)
341
  , mNeedToObserveOnDataAvailable(false)
342
  , mIsTrackingFetch(aIsTrackingFetch)
343
  , mOnStopRequestCalled(false)
344
#ifdef DEBUG
345
  , mResponseAvailableCalled(false)
346
  , mFetchCalled(false)
347
#endif
348
0
{
349
0
  AssertIsOnMainThread();
350
0
351
0
  MOZ_ASSERT(aRequest);
352
0
  MOZ_ASSERT(aPrincipal);
353
0
  MOZ_ASSERT(aMainThreadEventTarget);
354
0
}
355
356
FetchDriver::~FetchDriver()
357
0
{
358
0
  AssertIsOnMainThread();
359
0
360
0
  // We assert this since even on failures, we should call
361
0
  // FailWithNetworkError().
362
0
  MOZ_ASSERT(mResponseAvailableCalled);
363
0
}
364
365
nsresult
366
FetchDriver::Fetch(AbortSignalImpl* aSignalImpl, FetchDriverObserver* aObserver)
367
0
{
368
0
  AssertIsOnMainThread();
369
#ifdef DEBUG
370
  MOZ_ASSERT(!mFetchCalled);
371
  mFetchCalled = true;
372
#endif
373
374
0
  mObserver = aObserver;
375
0
376
0
  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
377
0
                        mRequest->WasCreatedByFetchEvent());
378
0
379
0
  // FIXME(nsm): Deal with HSTS.
380
0
381
0
  MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
382
0
                     "Synchronous fetch not supported");
383
0
384
0
385
0
  UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
386
0
  nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
387
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
388
0
    return rv;
389
0
  }
390
0
391
0
  mRequest->SetPrincipalInfo(std::move(principalInfo));
392
0
393
0
  // If the signal is aborted, it's time to inform the observer and terminate
394
0
  // the operation.
395
0
  if (aSignalImpl) {
396
0
    if (aSignalImpl->Aborted()) {
397
0
      Abort();
398
0
      return NS_OK;
399
0
    }
400
0
401
0
    Follow(aSignalImpl);
402
0
  }
403
0
404
0
  rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
405
0
  if (NS_FAILED(rv)) {
406
0
    FailWithNetworkError(rv);
407
0
  }
408
0
409
0
  // Any failure is handled by FailWithNetworkError notifying the aObserver.
410
0
  return NS_OK;
411
0
}
412
413
// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
414
// Functionality is often split between here, the CORS listener proxy and the
415
// Necko HTTP implementation.
416
nsresult
417
FetchDriver::HttpFetch(const nsACString& aPreferredAlternativeDataType)
418
0
{
419
0
  MOZ_ASSERT(NS_IsMainThread());
420
0
421
0
  // Step 1. "Let response be null."
422
0
  mResponse = nullptr;
423
0
  mOnStopRequestCalled = false;
424
0
  nsresult rv;
425
0
426
0
  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
427
0
  NS_ENSURE_SUCCESS(rv, rv);
428
0
429
0
  nsAutoCString url;
430
0
  mRequest->GetURL(url);
431
0
  nsCOMPtr<nsIURI> uri;
432
0
  rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr, ios);
433
0
  NS_ENSURE_SUCCESS(rv, rv);
434
0
435
0
  // Unsafe requests aren't allowed with when using no-core mode.
436
0
  if (mRequest->Mode() == RequestMode::No_cors &&
437
0
      mRequest->UnsafeRequest() &&
438
0
      (!mRequest->HasSimpleMethod() ||
439
0
       !mRequest->Headers()->HasOnlySimpleHeaders())) {
440
0
    MOZ_ASSERT(false, "The API should have caught this");
441
0
    return NS_ERROR_DOM_BAD_URI;
442
0
  }
443
0
444
0
  // non-GET requests aren't allowed for blob.
445
0
  if (IsBlobURI(uri)) {
446
0
    nsAutoCString method;
447
0
    mRequest->GetMethod(method);
448
0
    if (!method.EqualsLiteral("GET")) {
449
0
      return NS_ERROR_DOM_NETWORK_ERR;
450
0
    }
451
0
  }
452
0
453
0
  // Step 2 deals with letting ServiceWorkers intercept requests. This is
454
0
  // handled by Necko after the channel is opened.
455
0
  // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
456
0
  // set based on the Request's flag.
457
0
458
0
  // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
459
0
  // true..." is handled by the CORS proxy.
460
0
  //
461
0
  // Step 3.2 "Set request's skip service worker flag." This isn't required
462
0
  // since Necko will fall back to the network if the ServiceWorker does not
463
0
  // respond with a valid Response.
464
0
  //
465
0
  // NS_StartCORSPreflight() will automatically kick off the original request
466
0
  // if it succeeds, so we need to have everything setup for the original
467
0
  // request too.
468
0
469
0
  // Step 3.3 "Let credentials flag be set if one of
470
0
  //  - request's credentials mode is "include"
471
0
  //  - request's credentials mode is "same-origin" and either the CORS flag
472
0
  //    is unset or response tainting is "opaque"
473
0
  // is true, and unset otherwise."
474
0
475
0
  // Set skip serviceworker flag.
476
0
  // While the spec also gates on the client being a ServiceWorker, we can't
477
0
  // infer that here. Instead we rely on callers to set the flag correctly.
478
0
  const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
479
0
                                 nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
480
0
481
0
  nsSecurityFlags secFlags = 0;
482
0
  if (mRequest->Mode() == RequestMode::Cors) {
483
0
    secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
484
0
  } else if (mRequest->Mode() == RequestMode::Same_origin ||
485
0
             mRequest->Mode() == RequestMode::Navigate) {
486
0
    secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
487
0
  } else if (mRequest->Mode() == RequestMode::No_cors) {
488
0
    secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
489
0
  } else {
490
0
    MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
491
0
    return NS_ERROR_UNEXPECTED;
492
0
  }
493
0
494
0
  if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
495
0
    secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
496
0
  }
497
0
498
0
  // This is handles the use credentials flag in "HTTP
499
0
  // network or cache fetch" in the spec and decides whether to transmit
500
0
  // cookies and other identifying information.
501
0
  if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
502
0
    secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
503
0
  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
504
0
    secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
505
0
  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
506
0
    secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
507
0
  } else {
508
0
    MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
509
0
    return NS_ERROR_UNEXPECTED;
510
0
  }
511
0
512
0
  // From here on we create a channel and set its properties with the
513
0
  // information from the InternalRequest. This is an implementation detail.
514
0
  MOZ_ASSERT(mLoadGroup);
515
0
  nsCOMPtr<nsIChannel> chan;
516
0
517
0
  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
518
0
    bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
519
0
  if (mDocument) {
520
0
    MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
521
0
    rv = NS_NewChannel(getter_AddRefs(chan),
522
0
                       uri,
523
0
                       mDocument,
524
0
                       secFlags,
525
0
                       mRequest->ContentPolicyType(),
526
0
                       nullptr, /* aPerformanceStorage */
527
0
                       mLoadGroup,
528
0
                       nullptr, /* aCallbacks */
529
0
                       loadFlags,
530
0
                       ios);
531
0
  } else if (mClientInfo.isSome()) {
532
0
    rv = NS_NewChannel(getter_AddRefs(chan),
533
0
                       uri,
534
0
                       mPrincipal,
535
0
                       mClientInfo.ref(),
536
0
                       mController,
537
0
                       secFlags,
538
0
                       mRequest->ContentPolicyType(),
539
0
                       mPerformanceStorage,
540
0
                       mLoadGroup,
541
0
                       nullptr, /* aCallbacks */
542
0
                       loadFlags,
543
0
                       ios);
544
0
  } else {
545
0
    rv = NS_NewChannel(getter_AddRefs(chan),
546
0
                       uri,
547
0
                       mPrincipal,
548
0
                       secFlags,
549
0
                       mRequest->ContentPolicyType(),
550
0
                       mPerformanceStorage,
551
0
                       mLoadGroup,
552
0
                       nullptr, /* aCallbacks */
553
0
                       loadFlags,
554
0
                       ios);
555
0
  }
556
0
  NS_ENSURE_SUCCESS(rv, rv);
557
0
558
0
  // Insert ourselves into the notification callbacks chain so we can set
559
0
  // headers on redirects.
560
#ifdef DEBUG
561
  {
562
    nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
563
    chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
564
    MOZ_ASSERT(!notificationCallbacks);
565
  }
566
#endif
567
0
  chan->SetNotificationCallbacks(this);
568
0
569
0
  nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan));
570
0
  // Mark channel as urgent-start if the Fetch is triggered by user input
571
0
  // events.
572
0
  if (cos && EventStateManager::IsHandlingUserInput()) {
573
0
    cos->AddClassFlags(nsIClassOfService::UrgentStart);
574
0
  }
575
0
576
0
  // Step 3.5 begins "HTTP network or cache fetch".
577
0
  // HTTP network or cache fetch
578
0
  // ---------------------------
579
0
  // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
580
0
  nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
581
0
  if (httpChan) {
582
0
    // Copy the method.
583
0
    nsAutoCString method;
584
0
    mRequest->GetMethod(method);
585
0
    rv = httpChan->SetRequestMethod(method);
586
0
    NS_ENSURE_SUCCESS(rv, rv);
587
0
588
0
    // Set the same headers.
589
0
    SetRequestHeaders(httpChan);
590
0
591
0
    // Step 5 of https://fetch.spec.whatwg.org/#main-fetch
592
0
    // If request's referrer policy is the empty string and request's client is
593
0
    // non-null, then set request's referrer policy to request's client's
594
0
    // associated referrer policy.
595
0
    // Basically, "client" is not in our implementation, we use
596
0
    // EnvironmentReferrerPolicy of the worker or document context
597
0
    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
598
0
    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
599
0
      mRequest->SetReferrerPolicy(net_referrerPolicy);
600
0
    }
601
0
    // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
602
0
    // If request’s referrer policy is the empty string,
603
0
    // then set request’s referrer policy to the user-set default policy.
604
0
    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
605
0
      nsCOMPtr<nsILoadInfo> loadInfo = httpChan->GetLoadInfo();
606
0
      bool isPrivate = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
607
0
      net::ReferrerPolicy referrerPolicy = static_cast<net::ReferrerPolicy>(NS_GetDefaultReferrerPolicy(isPrivate));
608
0
      mRequest->SetReferrerPolicy(referrerPolicy);
609
0
    }
610
0
611
0
    rv = FetchUtil::SetRequestReferrer(mPrincipal,
612
0
                                       mDocument,
613
0
                                       httpChan,
614
0
                                       mRequest);
615
0
    NS_ENSURE_SUCCESS(rv, rv);
616
0
617
0
    // Bug 1120722 - Authorization will be handled later.
618
0
    // Auth may require prompting, we don't support it yet.
619
0
    // The next patch in this same bug prevents this from aborting the request.
620
0
    // Credentials checks for CORS are handled by nsCORSListenerProxy,
621
0
622
0
    nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
623
0
624
0
    // Conversion between enumerations is safe due to static asserts in
625
0
    // dom/workers/ServiceWorkerManager.cpp
626
0
    rv = internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
627
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
628
0
    rv = internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
629
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
630
0
    mRequest->MaybeSkipCacheIfPerformingRevalidation();
631
0
    rv = internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode()));
632
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
633
0
    rv = internalChan->SetIntegrityMetadata(mRequest->GetIntegrity());
634
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
635
0
636
0
    // Set the initiator type
637
0
    nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
638
0
    if (timedChannel) {
639
0
      timedChannel->SetInitiatorType(NS_LITERAL_STRING("fetch"));
640
0
    }
641
0
  }
642
0
643
0
  // Step 5. Proxy authentication will be handled by Necko.
644
0
645
0
  // Continue setting up 'HTTPRequest'. Content-Type and body data.
646
0
  nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
647
0
  if (uploadChan) {
648
0
    nsAutoCString contentType;
649
0
    ErrorResult result;
650
0
    mRequest->Headers()->GetFirst(NS_LITERAL_CSTRING("content-type"), contentType, result);
651
0
    // We don't actually expect "result" to have failed here: that only happens
652
0
    // for invalid header names.  But if for some reason it did, just propagate
653
0
    // it out.
654
0
    if (result.Failed()) {
655
0
      return result.StealNSResult();
656
0
    }
657
0
658
0
    // Now contentType is the header that was set in mRequest->Headers(), or a
659
0
    // void string if no header was set.
660
#ifdef DEBUG
661
    bool hasContentTypeHeader =
662
      mRequest->Headers()->Has(NS_LITERAL_CSTRING("content-type"), result);
663
    MOZ_ASSERT(!result.Failed());
664
    MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
665
#endif // DEBUG
666
667
0
    int64_t bodyLength;
668
0
    nsCOMPtr<nsIInputStream> bodyStream;
669
0
    mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
670
0
    if (bodyStream) {
671
0
      nsAutoCString method;
672
0
      mRequest->GetMethod(method);
673
0
      rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
674
0
                                               bodyLength, method,
675
0
                                               false /* aStreamHasHeaders */);
676
0
      NS_ENSURE_SUCCESS(rv, rv);
677
0
    }
678
0
  }
679
0
680
0
  // If preflight is required, start a "CORS preflight fetch"
681
0
  // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
682
0
  // implementation is handled by the http channel calling into
683
0
  // nsCORSListenerProxy. We just inform it which unsafe headers are included
684
0
  // in the request.
685
0
  if (mRequest->Mode() == RequestMode::Cors) {
686
0
    AutoTArray<nsCString, 5> unsafeHeaders;
687
0
    mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
688
0
    nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
689
0
    if (loadInfo) {
690
0
      loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
691
0
    }
692
0
  }
693
0
694
0
  if (mIsTrackingFetch && nsContentUtils::IsTailingEnabled()) {
695
0
    cos->AddClassFlags(nsIClassOfService::Throttleable |
696
0
                       nsIClassOfService::Tail);
697
0
  }
698
0
699
0
  if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) {
700
0
    nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan);
701
0
    if (p) {
702
0
      p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
703
0
    }
704
0
  }
705
0
706
0
  // if the preferred alternative data type in InternalRequest is not empty, set
707
0
  // the data type on the created channel and also create a AlternativeDataStreamListener
708
0
  // to be the stream listener of the channel.
709
0
  if (!aPreferredAlternativeDataType.IsEmpty()) {
710
0
    nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
711
0
    if (cic) {
712
0
      cic->PreferAlternativeDataType(aPreferredAlternativeDataType);
713
0
      MOZ_ASSERT(!mAltDataListener);
714
0
      mAltDataListener =
715
0
        new AlternativeDataStreamListener(this, chan, aPreferredAlternativeDataType);
716
0
      rv = chan->AsyncOpen2(mAltDataListener);
717
0
    } else {
718
0
      rv = chan->AsyncOpen2(this);
719
0
    }
720
0
  } else {
721
0
    rv = chan->AsyncOpen2(this);
722
0
  }
723
0
  if (NS_FAILED(rv)) {
724
0
    return rv;
725
0
  }
726
0
727
0
  // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
728
0
729
0
  mChannel = chan;
730
0
  return NS_OK;
731
0
}
732
already_AddRefed<InternalResponse>
733
FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
734
                                         bool aFoundOpaqueRedirect)
735
0
{
736
0
  MOZ_ASSERT(aResponse);
737
0
  AutoTArray<nsCString, 4> reqURLList;
738
0
  mRequest->GetURLListWithoutFragment(reqURLList);
739
0
  MOZ_ASSERT(!reqURLList.IsEmpty());
740
0
  aResponse->SetURLList(reqURLList);
741
0
  RefPtr<InternalResponse> filteredResponse;
742
0
  if (aFoundOpaqueRedirect) {
743
0
    filteredResponse = aResponse->OpaqueRedirectResponse();
744
0
  } else {
745
0
    switch (mRequest->GetResponseTainting()) {
746
0
      case LoadTainting::Basic:
747
0
        filteredResponse = aResponse->BasicResponse();
748
0
        break;
749
0
      case LoadTainting::CORS:
750
0
        filteredResponse = aResponse->CORSResponse();
751
0
        break;
752
0
      case LoadTainting::Opaque: {
753
0
        filteredResponse = aResponse->OpaqueResponse();
754
0
        nsresult rv = filteredResponse->GeneratePaddingInfo();
755
0
        if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
756
0
        break;
757
0
      }
758
0
      default:
759
0
        MOZ_CRASH("Unexpected case");
760
0
    }
761
0
  }
762
0
763
0
  MOZ_ASSERT(filteredResponse);
764
0
  MOZ_ASSERT(mObserver);
765
0
  if (!ShouldCheckSRI(mRequest, filteredResponse)) {
766
0
    mObserver->OnResponseAvailable(filteredResponse);
767
  #ifdef DEBUG
768
    mResponseAvailableCalled = true;
769
  #endif
770
  }
771
0
772
0
  return filteredResponse.forget();
773
0
}
774
775
void
776
FetchDriver::FailWithNetworkError(nsresult rv)
777
0
{
778
0
  AssertIsOnMainThread();
779
0
  RefPtr<InternalResponse> error = InternalResponse::NetworkError(rv);
780
0
  if (mObserver) {
781
0
    mObserver->OnResponseAvailable(error);
782
#ifdef DEBUG
783
    mResponseAvailableCalled = true;
784
#endif
785
    mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
786
0
    mObserver = nullptr;
787
0
  }
788
0
789
0
  mChannel = nullptr;
790
0
}
791
792
NS_IMETHODIMP
793
FetchDriver::OnStartRequest(nsIRequest* aRequest,
794
                            nsISupports* aContext)
795
0
{
796
0
  AssertIsOnMainThread();
797
0
798
0
  // Note, this can be called multiple times if we are doing an opaqueredirect.
799
0
  // In that case we will get a simulated OnStartRequest() and then the real
800
0
  // channel will call in with an errored OnStartRequest().
801
0
802
0
  if (!mChannel) {
803
0
    MOZ_ASSERT(!mObserver);
804
0
    return NS_BINDING_ABORTED;
805
0
  }
806
0
807
0
  nsresult rv;
808
0
  aRequest->GetStatus(&rv);
809
0
  if (NS_FAILED(rv)) {
810
0
    FailWithNetworkError(rv);
811
0
    return rv;
812
0
  }
813
0
814
0
  // We should only get to the following code once.
815
0
  MOZ_ASSERT(!mPipeOutputStream);
816
0
  MOZ_ASSERT(mObserver);
817
0
818
0
  mNeedToObserveOnDataAvailable = mObserver->NeedOnDataAvailable();
819
0
820
0
  RefPtr<InternalResponse> response;
821
0
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
822
0
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
823
0
824
0
  // On a successful redirect we perform the following substeps of HTTP Fetch,
825
0
  // step 5, "redirect status", step 11.
826
0
827
0
  bool foundOpaqueRedirect = false;
828
0
829
0
  int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
830
0
  rv = channel->GetContentLength(&contentLength);
831
0
  MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
832
0
833
0
  if (httpChannel) {
834
0
    uint32_t responseStatus;
835
0
    rv = httpChannel->GetResponseStatus(&responseStatus);
836
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
837
0
838
0
    if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
839
0
      if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
840
0
        FailWithNetworkError(NS_BINDING_ABORTED);
841
0
        return NS_BINDING_FAILED;
842
0
      }
843
0
      if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
844
0
        foundOpaqueRedirect = true;
845
0
      }
846
0
    }
847
0
848
0
    nsAutoCString statusText;
849
0
    rv = httpChannel->GetResponseStatusText(statusText);
850
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
851
0
852
0
    response = new InternalResponse(responseStatus, statusText);
853
0
854
0
    response->Headers()->FillResponseHeaders(httpChannel);
855
0
856
0
    // If Content-Encoding or Transfer-Encoding headers are set, then the actual
857
0
    // Content-Length (which refer to the decoded data) is obscured behind the encodings.
858
0
    ErrorResult result;
859
0
    if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
860
0
        response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
861
0
      // We cannot trust the content-length when content-encoding or
862
0
      // transfer-encoding are set.  There are many servers which just
863
0
      // get this wrong.
864
0
      contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
865
0
    }
866
0
    MOZ_ASSERT(!result.Failed());
867
0
  } else {
868
0
    response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
869
0
870
0
    ErrorResult result;
871
0
    nsAutoCString contentType;
872
0
    rv = channel->GetContentType(contentType);
873
0
    if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
874
0
      nsAutoCString contentCharset;
875
0
      channel->GetContentCharset(contentCharset);
876
0
      if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
877
0
        contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
878
0
      }
879
0
880
0
      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
881
0
                                  contentType,
882
0
                                  result);
883
0
      MOZ_ASSERT(!result.Failed());
884
0
    }
885
0
886
0
    if (contentLength > 0) {
887
0
      nsAutoCString contentLenStr;
888
0
      contentLenStr.AppendInt(contentLength);
889
0
      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
890
0
                                  contentLenStr,
891
0
                                  result);
892
0
      MOZ_ASSERT(!result.Failed());
893
0
    }
894
0
  }
895
0
896
0
  nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest);
897
0
  if (cic && mAltDataListener) {
898
0
    // Skip the case that mAltDataListener->Status() equals to FALLBACK, that means
899
0
    // the opened channel for alternative data loading is reused for loading the
900
0
    // main data.
901
0
    if (mAltDataListener->Status() != AlternativeDataStreamListener::FALLBACK) {
902
0
      // Verify the cache ID is the same with from alternative data cache.
903
0
      // If the cache ID is different, droping the alternative data loading,
904
0
      // otherwise setup the response's alternative body and cacheInfoChannel.
905
0
      uint64_t cacheEntryId = 0;
906
0
      if (NS_SUCCEEDED(cic->GetCacheEntryId(&cacheEntryId)) &&
907
0
          cacheEntryId != mAltDataListener->GetAlternativeDataCacheEntryId()) {
908
0
        mAltDataListener->Cancel();
909
0
      } else {
910
0
        // AlternativeDataStreamListener::OnStartRequest had already been called,
911
0
        // the alternative data input stream and cacheInfo channel must be created.
912
0
        nsCOMPtr<nsICacheInfoChannel> cacheInfo = mAltDataListener->GetCacheInfoChannel();
913
0
        nsCOMPtr<nsIInputStream> altInputStream = mAltDataListener->GetAlternativeInputStream();
914
0
        MOZ_ASSERT(altInputStream && cacheInfo);
915
0
        response->SetAlternativeBody(altInputStream);
916
0
        nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
917
0
          new nsMainThreadPtrHolder<nsICacheInfoChannel>("nsICacheInfoChannel",
918
0
                                                         cacheInfo,
919
0
                                                         false));
920
0
        response->SetCacheInfoChannel(handle);
921
0
      }
922
0
    } else if (!mAltDataListener->GetAlternativeDataType().IsEmpty()) {
923
0
      // If the status is FALLBACK and the mAltDataListener::mAlternativeDataType
924
0
      // is not empty, that means the data need to be saved into cache, setup the
925
0
      // response's nsICacheInfoChannel for caching the data after loading.
926
0
      nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
927
0
        new nsMainThreadPtrHolder<nsICacheInfoChannel>("nsICacheInfoChannel",
928
0
                                                       cic,
929
0
                                                       false));
930
0
      response->SetCacheInfoChannel(handle);
931
0
    }
932
0
  }
933
0
934
0
  // We open a pipe so that we can immediately set the pipe's read end as the
935
0
  // response's body. Setting the segment size to UINT32_MAX means that the
936
0
  // pipe has infinite space. The nsIChannel will continue to buffer data in
937
0
  // xpcom events even if we block on a fixed size pipe.  It might be possible
938
0
  // to suspend the channel and then resume when there is space available, but
939
0
  // for now use an infinite pipe to avoid blocking.
940
0
  nsCOMPtr<nsIInputStream> pipeInputStream;
941
0
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
942
0
                  getter_AddRefs(mPipeOutputStream),
943
0
                  0, /* default segment size */
944
0
                  UINT32_MAX /* infinite pipe */,
945
0
                  true /* non-blocking input, otherwise you deadlock */,
946
0
                  false /* blocking output, since the pipe is 'in'finite */ );
947
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
948
0
    FailWithNetworkError(rv);
949
0
    // Cancel request.
950
0
    return rv;
951
0
  }
952
0
  response->SetBody(pipeInputStream, contentLength);
953
0
954
0
  // If the request is a file channel, then remember the local path to
955
0
  // that file so we can later create File blobs rather than plain ones.
956
0
  nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
957
0
  if (fc) {
958
0
    nsCOMPtr<nsIFile> file;
959
0
    rv = fc->GetFile(getter_AddRefs(file));
960
0
    if (!NS_WARN_IF(NS_FAILED(rv))) {
961
0
      nsAutoString path;
962
0
      file->GetPath(path);
963
0
      response->SetBodyLocalPath(path);
964
0
    }
965
0
  }
966
0
967
0
  response->InitChannelInfo(channel);
968
0
969
0
  nsCOMPtr<nsIURI> channelURI;
970
0
  rv = channel->GetURI(getter_AddRefs(channelURI));
971
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
972
0
    FailWithNetworkError(rv);
973
0
    // Cancel request.
974
0
    return rv;
975
0
  }
976
0
977
0
  nsCOMPtr<nsILoadInfo> loadInfo;
978
0
  rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
979
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
980
0
    FailWithNetworkError(rv);
981
0
    return rv;
982
0
  }
983
0
984
0
  // Propagate any tainting from the channel back to our response here.  This
985
0
  // step is not reflected in the spec because the spec is written such that
986
0
  // FetchEvent.respondWith() just passes the already-tainted Response back to
987
0
  // the outer fetch().  In gecko, however, we serialize the Response through
988
0
  // the channel and must regenerate the tainting from the channel in the
989
0
  // interception case.
990
0
  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
991
0
992
0
  // Resolves fetch() promise which may trigger code running in a worker.  Make
993
0
  // sure the Response is fully initialized before calling this.
994
0
  mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
995
0
  if (NS_WARN_IF(!mResponse)) {
996
0
    // Fail to generate a paddingInfo for opaque response.
997
0
    MOZ_DIAGNOSTIC_ASSERT(mResponse->Type() == ResponseType::Opaque);
998
0
    FailWithNetworkError(NS_ERROR_UNEXPECTED);
999
0
    return rv;
1000
0
  }
1001
0
1002
0
  // From "Main Fetch" step 19: SRI-part1.
1003
0
  if (ShouldCheckSRI(mRequest, mResponse) && mSRIMetadata.IsEmpty()) {
1004
0
    nsIConsoleReportCollector* reporter = nullptr;
1005
0
    if (mObserver) {
1006
0
      reporter = mObserver->GetReporter();
1007
0
    }
1008
0
1009
0
    nsAutoCString sourceUri;
1010
0
    if (mDocument && mDocument->GetDocumentURI()) {
1011
0
      mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1012
0
    } else if (!mWorkerScript.IsEmpty()) {
1013
0
      sourceUri.Assign(mWorkerScript);
1014
0
    }
1015
0
    SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri,
1016
0
                                reporter, &mSRIMetadata);
1017
0
    mSRIDataVerifier = new SRICheckDataVerifier(mSRIMetadata, sourceUri,
1018
0
                                                reporter);
1019
0
1020
0
    // Do not retarget off main thread when using SRI API.
1021
0
    return NS_OK;
1022
0
  }
1023
0
1024
0
  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
1025
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1026
0
    FailWithNetworkError(rv);
1027
0
    // Cancel request.
1028
0
    return rv;
1029
0
  }
1030
0
1031
0
  // Try to retarget off main thread.
1032
0
  if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
1033
0
    Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
1034
0
  }
1035
0
  return NS_OK;
1036
0
}
1037
1038
namespace {
1039
1040
// Runnable to call the observer OnDataAvailable on the main-thread.
1041
class DataAvailableRunnable final : public Runnable
1042
{
1043
  RefPtr<FetchDriverObserver> mObserver;
1044
1045
public:
1046
  explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
1047
    : Runnable("dom::DataAvailableRunnable")
1048
    , mObserver(aObserver)
1049
0
  {
1050
0
     MOZ_ASSERT(aObserver);
1051
0
  }
1052
1053
  NS_IMETHOD
1054
  Run() override
1055
0
  {
1056
0
    mObserver->OnDataAvailable();
1057
0
    mObserver = nullptr;
1058
0
    return NS_OK;
1059
0
  }
1060
};
1061
1062
struct SRIVerifierAndOutputHolder {
1063
  SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
1064
                             nsIOutputStream* aOutputStream)
1065
    : mVerifier(aVerifier)
1066
    , mOutputStream(aOutputStream)
1067
0
  {}
1068
1069
  SRICheckDataVerifier* mVerifier;
1070
  nsIOutputStream* mOutputStream;
1071
1072
private:
1073
  SRIVerifierAndOutputHolder() = delete;
1074
};
1075
1076
// Just like NS_CopySegmentToStream, but also sends the data into an
1077
// SRICheckDataVerifier.
1078
nsresult
1079
CopySegmentToStreamAndSRI(nsIInputStream* aInStr,
1080
                          void* aClosure,
1081
                          const char* aBuffer,
1082
                          uint32_t aOffset,
1083
                          uint32_t aCount,
1084
                          uint32_t* aCountWritten)
1085
0
{
1086
0
  auto holder = static_cast<SRIVerifierAndOutputHolder*>(aClosure);
1087
0
  MOZ_DIAGNOSTIC_ASSERT(holder && holder->mVerifier && holder->mOutputStream,
1088
0
                        "Bogus holder");
1089
0
  nsresult rv =
1090
0
    holder->mVerifier->Update(aCount,
1091
0
                              reinterpret_cast<const uint8_t*>(aBuffer));
1092
0
  NS_ENSURE_SUCCESS(rv, rv);
1093
0
1094
0
  // The rest is just like NS_CopySegmentToStream.
1095
0
  *aCountWritten = 0;
1096
0
  while (aCount) {
1097
0
    uint32_t n = 0;
1098
0
    rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
1099
0
    if (NS_FAILED(rv)) {
1100
0
      return rv;
1101
0
    }
1102
0
    aBuffer += n;
1103
0
    aCount -= n;
1104
0
    *aCountWritten += n;
1105
0
  }
1106
0
  return NS_OK;
1107
0
}
1108
1109
} // anonymous namespace
1110
1111
NS_IMETHODIMP
1112
FetchDriver::OnDataAvailable(nsIRequest* aRequest,
1113
                             nsISupports* aContext,
1114
                             nsIInputStream* aInputStream,
1115
                             uint64_t aOffset,
1116
                             uint32_t aCount)
1117
0
{
1118
0
  // NB: This can be called on any thread!  But we're guaranteed that it is
1119
0
  // called between OnStartRequest and OnStopRequest, so we don't need to worry
1120
0
  // about races.
1121
0
1122
0
  if (mNeedToObserveOnDataAvailable) {
1123
0
    mNeedToObserveOnDataAvailable = false;
1124
0
    if (mObserver) {
1125
0
      if (NS_IsMainThread()) {
1126
0
        mObserver->OnDataAvailable();
1127
0
      } else {
1128
0
        RefPtr<Runnable> runnable = new DataAvailableRunnable(mObserver);
1129
0
        nsresult rv =
1130
0
          mMainThreadEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1131
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
1132
0
          return rv;
1133
0
        }
1134
0
      }
1135
0
    }
1136
0
  }
1137
0
1138
0
  // Needs to be initialized to 0 because in some cases nsStringInputStream may
1139
0
  // not write to aRead.
1140
0
  uint32_t aRead = 0;
1141
0
  MOZ_ASSERT(mResponse);
1142
0
  MOZ_ASSERT(mPipeOutputStream);
1143
0
1144
0
  // From "Main Fetch" step 19: SRI-part2.
1145
0
  // Note: Avoid checking the hidden opaque body.
1146
0
  nsresult rv;
1147
0
  if (mResponse->Type() != ResponseType::Opaque &&
1148
0
      ShouldCheckSRI(mRequest, mResponse)) {
1149
0
    MOZ_ASSERT(mSRIDataVerifier);
1150
0
1151
0
    SRIVerifierAndOutputHolder holder(mSRIDataVerifier, mPipeOutputStream);
1152
0
    rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
1153
0
                                    &holder, aCount, &aRead);
1154
0
  } else {
1155
0
    rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
1156
0
                                    mPipeOutputStream,
1157
0
                                    aCount, &aRead);
1158
0
  }
1159
0
1160
0
  // If no data was read, it's possible the output stream is closed but the
1161
0
  // ReadSegments call followed its contract of returning NS_OK despite write
1162
0
  // errors.  Unfortunately, nsIOutputStream has an ill-conceived contract when
1163
0
  // taken together with ReadSegments' contract, because the pipe will just
1164
0
  // NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
1165
0
  // So we must just assume the pipe is broken.
1166
0
  if (aRead == 0 && aCount != 0) {
1167
0
    return NS_BASE_STREAM_CLOSED;
1168
0
  }
1169
0
  return rv;
1170
0
}
1171
1172
NS_IMETHODIMP
1173
FetchDriver::OnStopRequest(nsIRequest* aRequest,
1174
                           nsISupports* aContext,
1175
                           nsresult aStatusCode)
1176
0
{
1177
0
  AssertIsOnMainThread();
1178
0
1179
0
  MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled);
1180
0
  mOnStopRequestCalled = true;
1181
0
1182
0
  // main data loading is going to finish, breaking the reference cycle.
1183
0
  RefPtr<AlternativeDataStreamListener> altDataListener = mAltDataListener.forget();
1184
0
1185
0
  // We need to check mObserver, which is nulled by FailWithNetworkError(),
1186
0
  // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
1187
0
  // mResponse will definitely be null so we must not take the else branch.
1188
0
  if (NS_FAILED(aStatusCode) || !mObserver) {
1189
0
    nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
1190
0
    if (outputStream) {
1191
0
      outputStream->CloseWithStatus(NS_BINDING_FAILED);
1192
0
    }
1193
0
    if (altDataListener) {
1194
0
      altDataListener->Cancel();
1195
0
    }
1196
0
1197
0
    // We proceed as usual here, since we've already created a successful response
1198
0
    // from OnStartRequest.
1199
0
  } else {
1200
0
    MOZ_ASSERT(mResponse);
1201
0
    MOZ_ASSERT(!mResponse->IsError());
1202
0
1203
0
    // From "Main Fetch" step 19: SRI-part3.
1204
0
    if (ShouldCheckSRI(mRequest, mResponse)) {
1205
0
      MOZ_ASSERT(mSRIDataVerifier);
1206
0
1207
0
      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
1208
0
1209
0
      nsIConsoleReportCollector* reporter = nullptr;
1210
0
      if (mObserver) {
1211
0
        reporter = mObserver->GetReporter();
1212
0
      }
1213
0
1214
0
      nsAutoCString sourceUri;
1215
0
      if (mDocument && mDocument->GetDocumentURI()) {
1216
0
        mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1217
0
      } else if (!mWorkerScript.IsEmpty()) {
1218
0
        sourceUri.Assign(mWorkerScript);
1219
0
      }
1220
0
      nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri,
1221
0
                                             reporter);
1222
0
      if (NS_FAILED(rv)) {
1223
0
        if (altDataListener) {
1224
0
          altDataListener->Cancel();
1225
0
        }
1226
0
        FailWithNetworkError(rv);
1227
0
        // Cancel request.
1228
0
        return rv;
1229
0
      }
1230
0
    }
1231
0
1232
0
    if (mPipeOutputStream) {
1233
0
      mPipeOutputStream->Close();
1234
0
    }
1235
0
  }
1236
0
1237
0
  return FinishOnStopRequest(altDataListener);
1238
0
}
1239
1240
nsresult
1241
FetchDriver::FinishOnStopRequest(AlternativeDataStreamListener* aAltDataListener)
1242
0
{
1243
0
  AssertIsOnMainThread();
1244
0
  // OnStopRequest is not called from channel, that means the main data loading
1245
0
  // does not finish yet. Reaching here since alternative data loading finishes.
1246
0
  if (!mOnStopRequestCalled) {
1247
0
    return NS_OK;
1248
0
  }
1249
0
1250
0
  MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener);
1251
0
  // Wait for alternative data loading finish if we needed it.
1252
0
  if (aAltDataListener &&
1253
0
      aAltDataListener->Status() == AlternativeDataStreamListener::LOADING) {
1254
0
    // For LOADING case, channel holds the reference of altDataListener, no need
1255
0
    // to restore it to mAltDataListener.
1256
0
    return NS_OK;
1257
0
  }
1258
0
1259
0
  if (mObserver) {
1260
0
    // From "Main Fetch" step 19.1, 19.2: Process response.
1261
0
    if (ShouldCheckSRI(mRequest, mResponse)) {
1262
0
      MOZ_ASSERT(mResponse);
1263
0
      mObserver->OnResponseAvailable(mResponse);
1264
      #ifdef DEBUG
1265
        mResponseAvailableCalled = true;
1266
      #endif
1267
    }
1268
0
1269
0
    mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
1270
0
    mObserver = nullptr;
1271
0
  }
1272
0
1273
0
  mChannel = nullptr;
1274
0
  return NS_OK;
1275
0
}
1276
1277
NS_IMETHODIMP
1278
FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
1279
                                    nsIChannel* aNewChannel,
1280
                                    uint32_t aFlags,
1281
                                    nsIAsyncVerifyRedirectCallback *aCallback)
1282
0
{
1283
0
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
1284
0
  if (httpChannel) {
1285
0
    SetRequestHeaders(httpChannel);
1286
0
  }
1287
0
1288
0
  nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
1289
0
  nsAutoCString tRPHeaderCValue;
1290
0
  if (oldHttpChannel) {
1291
0
    Unused << oldHttpChannel->GetResponseHeader(NS_LITERAL_CSTRING("referrer-policy"),
1292
0
                                                tRPHeaderCValue);
1293
0
  }
1294
0
1295
0
  // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
1296
0
  // However, ignore internal redirects here.  We don't want to flip
1297
0
  // Response.redirected to true if an internal redirect occurs.  These
1298
0
  // should be transparent to script.
1299
0
  nsCOMPtr<nsIURI> uri;
1300
0
  MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
1301
0
1302
0
  nsCOMPtr<nsIURI> uriClone;
1303
0
  nsresult rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone));
1304
0
  if(NS_WARN_IF(NS_FAILED(rv))){
1305
0
    return rv;
1306
0
  }
1307
0
  nsCString spec;
1308
0
  rv = uriClone->GetSpec(spec);
1309
0
  if(NS_WARN_IF(NS_FAILED(rv))){
1310
0
    return rv;
1311
0
  }
1312
0
  nsCString fragment;
1313
0
  rv = uri->GetRef(fragment);
1314
0
  if(NS_WARN_IF(NS_FAILED(rv))){
1315
0
    return rv;
1316
0
  }
1317
0
1318
0
  if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
1319
0
    mRequest->AddURL(spec, fragment);
1320
0
  } else {
1321
0
    // Overwrite the URL only when the request is redirected by a service
1322
0
    // worker.
1323
0
    mRequest->SetURLForInternalRedirect(aFlags, spec, fragment);
1324
0
  }
1325
0
1326
0
  NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
1327
0
  // updates request’s associated referrer policy according to the
1328
0
  // Referrer-Policy header (if any).
1329
0
  if (!tRPHeaderValue.IsEmpty()) {
1330
0
    net::ReferrerPolicy net_referrerPolicy =
1331
0
      nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
1332
0
    if (net_referrerPolicy != net::RP_Unset) {
1333
0
      mRequest->SetReferrerPolicy(net_referrerPolicy);
1334
0
      // Should update channel's referrer policy
1335
0
      if (httpChannel) {
1336
0
        nsresult rv = FetchUtil::SetRequestReferrer(mPrincipal,
1337
0
                                                    mDocument,
1338
0
                                                    httpChannel,
1339
0
                                                    mRequest);
1340
0
        NS_ENSURE_SUCCESS(rv, rv);
1341
0
      }
1342
0
    }
1343
0
  }
1344
0
1345
0
  aCallback->OnRedirectVerifyCallback(NS_OK);
1346
0
  return NS_OK;
1347
0
}
1348
1349
NS_IMETHODIMP
1350
FetchDriver::CheckListenerChain()
1351
0
{
1352
0
  return NS_OK;
1353
0
}
1354
1355
NS_IMETHODIMP
1356
FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
1357
0
{
1358
0
  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1359
0
    *aResult = static_cast<nsIChannelEventSink*>(this);
1360
0
    NS_ADDREF_THIS();
1361
0
    return NS_OK;
1362
0
  }
1363
0
  if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
1364
0
    *aResult = static_cast<nsIStreamListener*>(this);
1365
0
    NS_ADDREF_THIS();
1366
0
    return NS_OK;
1367
0
  }
1368
0
  if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
1369
0
    *aResult = static_cast<nsIRequestObserver*>(this);
1370
0
    NS_ADDREF_THIS();
1371
0
    return NS_OK;
1372
0
  }
1373
0
1374
0
  return QueryInterface(aIID, aResult);
1375
0
}
1376
1377
void
1378
FetchDriver::SetDocument(nsIDocument* aDocument)
1379
0
{
1380
0
  // Cannot set document after Fetch() has been called.
1381
0
  MOZ_ASSERT(!mFetchCalled);
1382
0
  mDocument = aDocument;
1383
0
}
1384
1385
void
1386
FetchDriver::SetClientInfo(const ClientInfo& aClientInfo)
1387
0
{
1388
0
  MOZ_ASSERT(!mFetchCalled);
1389
0
  mClientInfo.emplace(aClientInfo);
1390
0
}
1391
1392
void
1393
FetchDriver::SetController(const Maybe<ServiceWorkerDescriptor>& aController)
1394
0
{
1395
0
  MOZ_ASSERT(!mFetchCalled);
1396
0
  mController = aController;
1397
0
}
1398
1399
void
1400
FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const
1401
0
{
1402
0
  MOZ_ASSERT(aChannel);
1403
0
1404
0
  AutoTArray<InternalHeaders::Entry, 5> headers;
1405
0
  mRequest->Headers()->GetEntries(headers);
1406
0
  bool hasAccept = false;
1407
0
  for (uint32_t i = 0; i < headers.Length(); ++i) {
1408
0
    if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) {
1409
0
      hasAccept = true;
1410
0
    }
1411
0
    if (headers[i].mValue.IsEmpty()) {
1412
0
      DebugOnly<nsresult> rv = aChannel->SetEmptyRequestHeader(headers[i].mName);
1413
0
      MOZ_ASSERT(NS_SUCCEEDED(rv));
1414
0
    } else {
1415
0
      DebugOnly<nsresult> rv =
1416
0
        aChannel->SetRequestHeader(headers[i].mName, headers[i].mValue,
1417
0
                                   false /* merge */);
1418
0
      MOZ_ASSERT(NS_SUCCEEDED(rv));
1419
0
    }
1420
0
  }
1421
0
1422
0
  if (!hasAccept) {
1423
0
    DebugOnly<nsresult> rv =
1424
0
      aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
1425
0
                                 NS_LITERAL_CSTRING("*/*"),
1426
0
                                 false /* merge */);
1427
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
1428
0
  }
1429
0
1430
0
  if (mRequest->ForceOriginHeader()) {
1431
0
    nsAutoString origin;
1432
0
    if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
1433
0
      DebugOnly<nsresult> rv =
1434
0
        aChannel->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
1435
0
                                   NS_ConvertUTF16toUTF8(origin),
1436
0
                                   false /* merge */);
1437
0
      MOZ_ASSERT(NS_SUCCEEDED(rv));
1438
0
    }
1439
0
  }
1440
0
}
1441
1442
void
1443
FetchDriver::Abort()
1444
0
{
1445
0
  if (mObserver) {
1446
  #ifdef DEBUG
1447
    mResponseAvailableCalled = true;
1448
  #endif
1449
    mObserver->OnResponseEnd(FetchDriverObserver::eAborted);
1450
0
    mObserver = nullptr;
1451
0
  }
1452
0
1453
0
  if (mChannel) {
1454
0
    mChannel->Cancel(NS_BINDING_ABORTED);
1455
0
    mChannel = nullptr;
1456
0
  }
1457
0
}
1458
1459
} // namespace dom
1460
} // namespace mozilla