/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 |