Source code

Revision control

Other Tools

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