Line data Source code
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 "nsIHttpChannel.h"
15 : #include "nsIHttpChannelInternal.h"
16 : #include "nsIScriptSecurityManager.h"
17 : #include "nsISupportsPriority.h"
18 : #include "nsIThreadRetargetableRequest.h"
19 : #include "nsIUploadChannel2.h"
20 : #include "nsIInterfaceRequestorUtils.h"
21 : #include "nsIPipe.h"
22 :
23 : #include "nsContentPolicyUtils.h"
24 : #include "nsDataHandler.h"
25 : #include "nsHostObjectProtocolHandler.h"
26 : #include "nsNetUtil.h"
27 : #include "nsPrintfCString.h"
28 : #include "nsStreamUtils.h"
29 : #include "nsStringStream.h"
30 : #include "nsHttpChannel.h"
31 :
32 : #include "mozilla/dom/File.h"
33 : #include "mozilla/dom/workers/Workers.h"
34 : #include "mozilla/EventStateManager.h"
35 : #include "mozilla/ipc/PBackgroundSharedTypes.h"
36 : #include "mozilla/Unused.h"
37 :
38 : #include "Fetch.h"
39 : #include "FetchUtil.h"
40 : #include "InternalRequest.h"
41 : #include "InternalResponse.h"
42 :
43 : namespace mozilla {
44 : namespace dom {
45 :
46 31 : NS_IMPL_ISUPPORTS(FetchDriver,
47 : nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
48 : nsIThreadRetargetableStreamListener)
49 :
50 1 : FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
51 : nsILoadGroup* aLoadGroup, nsIEventTarget* aMainThreadEventTarget,
52 1 : bool aIsTrackingFetch)
53 : : mPrincipal(aPrincipal)
54 : , mLoadGroup(aLoadGroup)
55 : , mRequest(aRequest)
56 : , mMainThreadEventTarget(aMainThreadEventTarget)
57 : , mIsTrackingFetch(aIsTrackingFetch)
58 : #ifdef DEBUG
59 : , mResponseAvailableCalled(false)
60 1 : , mFetchCalled(false)
61 : #endif
62 : {
63 1 : MOZ_ASSERT(aRequest);
64 1 : MOZ_ASSERT(aPrincipal);
65 1 : MOZ_ASSERT(aMainThreadEventTarget);
66 1 : }
67 :
68 3 : FetchDriver::~FetchDriver()
69 : {
70 : // We assert this since even on failures, we should call
71 : // FailWithNetworkError().
72 1 : MOZ_ASSERT(mResponseAvailableCalled);
73 3 : }
74 :
75 : nsresult
76 1 : FetchDriver::Fetch(FetchSignal* aSignal, FetchDriverObserver* aObserver)
77 : {
78 1 : workers::AssertIsOnMainThread();
79 : #ifdef DEBUG
80 1 : MOZ_ASSERT(!mFetchCalled);
81 1 : mFetchCalled = true;
82 : #endif
83 :
84 1 : mObserver = aObserver;
85 :
86 1 : Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
87 2 : mRequest->WasCreatedByFetchEvent());
88 :
89 : // FIXME(nsm): Deal with HSTS.
90 :
91 1 : MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
92 : "Synchronous fetch not supported");
93 :
94 :
95 2 : UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
96 1 : nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
97 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
98 0 : return rv;
99 : }
100 :
101 1 : mRequest->SetPrincipalInfo(Move(principalInfo));
102 :
103 : // If the signal is aborted, it's time to inform the observer and terminate
104 : // the operation.
105 1 : if (aSignal) {
106 0 : if (aSignal->Aborted()) {
107 0 : Aborted();
108 0 : return NS_OK;
109 : }
110 :
111 0 : Follow(aSignal);
112 : }
113 :
114 1 : if (NS_FAILED(HttpFetch())) {
115 0 : FailWithNetworkError();
116 : }
117 :
118 : // Any failure is handled by FailWithNetworkError notifying the aObserver.
119 1 : return NS_OK;
120 : }
121 :
122 : // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
123 : // Functionality is often split between here, the CORS listener proxy and the
124 : // Necko HTTP implementation.
125 : nsresult
126 1 : FetchDriver::HttpFetch()
127 : {
128 1 : MOZ_ASSERT(NS_IsMainThread());
129 :
130 : // Step 1. "Let response be null."
131 1 : mResponse = nullptr;
132 : nsresult rv;
133 :
134 2 : nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
135 1 : NS_ENSURE_SUCCESS(rv, rv);
136 :
137 2 : nsAutoCString url;
138 1 : mRequest->GetURL(url);
139 2 : nsCOMPtr<nsIURI> uri;
140 1 : rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr, ios);
141 1 : NS_ENSURE_SUCCESS(rv, rv);
142 :
143 : // Unsafe requests aren't allowed with when using no-core mode.
144 2 : if (mRequest->Mode() == RequestMode::No_cors &&
145 1 : mRequest->UnsafeRequest() &&
146 0 : (!mRequest->HasSimpleMethod() ||
147 0 : !mRequest->Headers()->HasOnlySimpleHeaders())) {
148 0 : MOZ_ASSERT(false, "The API should have caught this");
149 : return NS_ERROR_DOM_BAD_URI;
150 : }
151 :
152 : // non-GET requests aren't allowed for blob.
153 1 : if (IsBlobURI(uri)) {
154 0 : nsAutoCString method;
155 0 : mRequest->GetMethod(method);
156 0 : if (!method.EqualsLiteral("GET")) {
157 0 : return NS_ERROR_DOM_NETWORK_ERR;
158 : }
159 : }
160 :
161 : // Step 2 deals with letting ServiceWorkers intercept requests. This is
162 : // handled by Necko after the channel is opened.
163 : // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
164 : // set based on the Request's flag.
165 :
166 : // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
167 : // true..." is handled by the CORS proxy.
168 : //
169 : // Step 3.2 "Set request's skip service worker flag." This isn't required
170 : // since Necko will fall back to the network if the ServiceWorker does not
171 : // respond with a valid Response.
172 : //
173 : // NS_StartCORSPreflight() will automatically kick off the original request
174 : // if it succeeds, so we need to have everything setup for the original
175 : // request too.
176 :
177 : // Step 3.3 "Let credentials flag be set if one of
178 : // - request's credentials mode is "include"
179 : // - request's credentials mode is "same-origin" and either the CORS flag
180 : // is unset or response tainting is "opaque"
181 : // is true, and unset otherwise."
182 :
183 : // Set skip serviceworker flag.
184 : // While the spec also gates on the client being a ServiceWorker, we can't
185 : // infer that here. Instead we rely on callers to set the flag correctly.
186 1 : const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
187 1 : nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
188 :
189 1 : nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS;
190 1 : if (mRequest->Mode() == RequestMode::Cors) {
191 1 : secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
192 0 : } else if (mRequest->Mode() == RequestMode::Same_origin ||
193 0 : mRequest->Mode() == RequestMode::Navigate) {
194 0 : secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
195 0 : } else if (mRequest->Mode() == RequestMode::No_cors) {
196 0 : secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
197 : } else {
198 0 : MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
199 : return NS_ERROR_UNEXPECTED;
200 : }
201 :
202 1 : if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
203 0 : secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
204 : }
205 :
206 : // This is handles the use credentials flag in "HTTP
207 : // network or cache fetch" in the spec and decides whether to transmit
208 : // cookies and other identifying information.
209 1 : if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
210 0 : secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
211 1 : } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
212 1 : secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
213 0 : } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
214 0 : secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
215 : } else {
216 0 : MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
217 : return NS_ERROR_UNEXPECTED;
218 : }
219 :
220 : // From here on we create a channel and set its properties with the
221 : // information from the InternalRequest. This is an implementation detail.
222 1 : MOZ_ASSERT(mLoadGroup);
223 2 : nsCOMPtr<nsIChannel> chan;
224 :
225 : nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
226 1 : bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
227 1 : if (mDocument) {
228 0 : MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
229 0 : rv = NS_NewChannel(getter_AddRefs(chan),
230 : uri,
231 : mDocument,
232 : secFlags,
233 : mRequest->ContentPolicyType(),
234 : mLoadGroup,
235 : nullptr, /* aCallbacks */
236 : loadFlags,
237 0 : ios);
238 : } else {
239 1 : rv = NS_NewChannel(getter_AddRefs(chan),
240 : uri,
241 : mPrincipal,
242 : secFlags,
243 : mRequest->ContentPolicyType(),
244 : mLoadGroup,
245 : nullptr, /* aCallbacks */
246 : loadFlags,
247 : ios);
248 : }
249 1 : NS_ENSURE_SUCCESS(rv, rv);
250 :
251 1 : mLoadGroup = nullptr;
252 :
253 : // Insert ourselves into the notification callbacks chain so we can set
254 : // headers on redirects.
255 : #ifdef DEBUG
256 : {
257 2 : nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
258 1 : chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
259 1 : MOZ_ASSERT(!notificationCallbacks);
260 : }
261 : #endif
262 1 : chan->SetNotificationCallbacks(this);
263 :
264 2 : nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan));
265 : // Mark channel as urgent-start if the Fetch is triggered by user input
266 : // events.
267 1 : if (cos && EventStateManager::IsHandlingUserInput()) {
268 0 : cos->AddClassFlags(nsIClassOfService::UrgentStart);
269 : }
270 :
271 : // Step 3.5 begins "HTTP network or cache fetch".
272 : // HTTP network or cache fetch
273 : // ---------------------------
274 : // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
275 2 : nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
276 1 : if (httpChan) {
277 : // Copy the method.
278 0 : nsAutoCString method;
279 0 : mRequest->GetMethod(method);
280 0 : rv = httpChan->SetRequestMethod(method);
281 0 : NS_ENSURE_SUCCESS(rv, rv);
282 :
283 : // Set the same headers.
284 0 : SetRequestHeaders(httpChan);
285 :
286 0 : net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
287 : // Step 6 of
288 : // https://fetch.spec.whatwg.org/#main-fetch
289 : // If request's referrer policy is the empty string and request's client is
290 : // non-null, then set request's referrer policy to request's client's
291 : // associated referrer policy.
292 : // Basically, "client" is not in our implementation, we use
293 : // EnvironmentReferrerPolicy of the worker or document context
294 0 : if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
295 0 : mRequest->SetReferrerPolicy(net_referrerPolicy);
296 : }
297 : // Step 7 of
298 : // https://fetch.spec.whatwg.org/#main-fetch
299 : // If request’s referrer policy is the empty string,
300 : // then set request’s referrer policy to "no-referrer-when-downgrade".
301 0 : if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
302 : net::ReferrerPolicy referrerPolicy =
303 0 : static_cast<net::ReferrerPolicy>(NS_GetDefaultReferrerPolicy());
304 0 : mRequest->SetReferrerPolicy(referrerPolicy);
305 : }
306 :
307 0 : rv = FetchUtil::SetRequestReferrer(mPrincipal,
308 : mDocument,
309 : httpChan,
310 : mRequest);
311 0 : NS_ENSURE_SUCCESS(rv, rv);
312 :
313 : // Bug 1120722 - Authorization will be handled later.
314 : // Auth may require prompting, we don't support it yet.
315 : // The next patch in this same bug prevents this from aborting the request.
316 : // Credentials checks for CORS are handled by nsCORSListenerProxy,
317 :
318 0 : nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
319 :
320 : // Conversion between enumerations is safe due to static asserts in
321 : // dom/workers/ServiceWorkerManager.cpp
322 0 : rv = internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
323 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
324 0 : rv = internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
325 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
326 0 : mRequest->MaybeSkipCacheIfPerformingRevalidation();
327 0 : rv = internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode()));
328 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
329 0 : rv = internalChan->SetIntegrityMetadata(mRequest->GetIntegrity());
330 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
331 : }
332 :
333 : // Step 5. Proxy authentication will be handled by Necko.
334 :
335 : // Continue setting up 'HTTPRequest'. Content-Type and body data.
336 2 : nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
337 1 : if (uploadChan) {
338 0 : nsAutoCString contentType;
339 0 : ErrorResult result;
340 0 : mRequest->Headers()->GetFirst(NS_LITERAL_CSTRING("content-type"), contentType, result);
341 : // We don't actually expect "result" to have failed here: that only happens
342 : // for invalid header names. But if for some reason it did, just propagate
343 : // it out.
344 0 : if (result.Failed()) {
345 0 : return result.StealNSResult();
346 : }
347 :
348 : // Now contentType is the header that was set in mRequest->Headers(), or a
349 : // void string if no header was set.
350 : #ifdef DEBUG
351 : bool hasContentTypeHeader =
352 0 : mRequest->Headers()->Has(NS_LITERAL_CSTRING("content-type"), result);
353 0 : MOZ_ASSERT(!result.Failed());
354 0 : MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
355 : #endif // DEBUG
356 :
357 0 : nsCOMPtr<nsIInputStream> bodyStream;
358 0 : mRequest->GetBody(getter_AddRefs(bodyStream));
359 0 : if (bodyStream) {
360 0 : nsAutoCString method;
361 0 : mRequest->GetMethod(method);
362 0 : rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
363 0 : NS_ENSURE_SUCCESS(rv, rv);
364 : }
365 : }
366 :
367 : // If preflight is required, start a "CORS preflight fetch"
368 : // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
369 : // implementation is handled by the http channel calling into
370 : // nsCORSListenerProxy. We just inform it which unsafe headers are included
371 : // in the request.
372 1 : if (mRequest->Mode() == RequestMode::Cors) {
373 2 : AutoTArray<nsCString, 5> unsafeHeaders;
374 1 : mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
375 2 : nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
376 1 : if (loadInfo) {
377 1 : loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
378 : }
379 : }
380 :
381 1 : if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) {
382 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan);
383 0 : if (p) {
384 0 : p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
385 : }
386 : }
387 :
388 1 : rv = chan->AsyncOpen2(this);
389 1 : NS_ENSURE_SUCCESS(rv, rv);
390 :
391 : // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
392 :
393 1 : mChannel = chan;
394 1 : return NS_OK;
395 : }
396 : already_AddRefed<InternalResponse>
397 1 : FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
398 : bool aFoundOpaqueRedirect)
399 : {
400 1 : MOZ_ASSERT(aResponse);
401 2 : AutoTArray<nsCString, 4> reqURLList;
402 1 : mRequest->GetURLListWithoutFragment(reqURLList);
403 1 : MOZ_ASSERT(!reqURLList.IsEmpty());
404 1 : aResponse->SetURLList(reqURLList);
405 2 : RefPtr<InternalResponse> filteredResponse;
406 1 : if (aFoundOpaqueRedirect) {
407 0 : filteredResponse = aResponse->OpaqueRedirectResponse();
408 : } else {
409 1 : switch (mRequest->GetResponseTainting()) {
410 : case LoadTainting::Basic:
411 1 : filteredResponse = aResponse->BasicResponse();
412 1 : break;
413 : case LoadTainting::CORS:
414 0 : filteredResponse = aResponse->CORSResponse();
415 0 : break;
416 : case LoadTainting::Opaque:
417 0 : filteredResponse = aResponse->OpaqueResponse();
418 0 : break;
419 : default:
420 0 : MOZ_CRASH("Unexpected case");
421 : }
422 : }
423 :
424 1 : MOZ_ASSERT(filteredResponse);
425 1 : MOZ_ASSERT(mObserver);
426 2 : if (filteredResponse->Type() == ResponseType::Error ||
427 1 : mRequest->GetIntegrity().IsEmpty()) {
428 1 : mObserver->OnResponseAvailable(filteredResponse);
429 : #ifdef DEBUG
430 1 : mResponseAvailableCalled = true;
431 : #endif
432 : }
433 :
434 2 : return filteredResponse.forget();
435 : }
436 :
437 : void
438 0 : FetchDriver::FailWithNetworkError()
439 : {
440 0 : workers::AssertIsOnMainThread();
441 0 : RefPtr<InternalResponse> error = InternalResponse::NetworkError();
442 0 : if (mObserver) {
443 0 : mObserver->OnResponseAvailable(error);
444 : #ifdef DEBUG
445 0 : mResponseAvailableCalled = true;
446 : #endif
447 0 : mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
448 0 : mObserver = nullptr;
449 : }
450 :
451 0 : mChannel = nullptr;
452 0 : }
453 :
454 : NS_IMETHODIMP
455 1 : FetchDriver::OnStartRequest(nsIRequest* aRequest,
456 : nsISupports* aContext)
457 : {
458 1 : workers::AssertIsOnMainThread();
459 :
460 : // Note, this can be called multiple times if we are doing an opaqueredirect.
461 : // In that case we will get a simulated OnStartRequest() and then the real
462 : // channel will call in with an errored OnStartRequest().
463 :
464 : nsresult rv;
465 1 : aRequest->GetStatus(&rv);
466 1 : if (NS_FAILED(rv)) {
467 0 : FailWithNetworkError();
468 0 : return rv;
469 : }
470 :
471 : // We should only get to the following code once.
472 1 : MOZ_ASSERT(!mPipeOutputStream);
473 1 : MOZ_ASSERT(mObserver);
474 :
475 2 : RefPtr<InternalResponse> response;
476 2 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
477 2 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
478 :
479 : // On a successful redirect we perform the following substeps of HTTP Fetch,
480 : // step 5, "redirect status", step 11.
481 :
482 1 : bool foundOpaqueRedirect = false;
483 :
484 1 : int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
485 1 : rv = channel->GetContentLength(&contentLength);
486 1 : MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
487 :
488 1 : if (httpChannel) {
489 : uint32_t responseStatus;
490 0 : rv = httpChannel->GetResponseStatus(&responseStatus);
491 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
492 :
493 0 : if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
494 0 : if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
495 0 : FailWithNetworkError();
496 0 : return NS_BINDING_FAILED;
497 : }
498 0 : if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
499 0 : foundOpaqueRedirect = true;
500 : }
501 : }
502 :
503 0 : nsAutoCString statusText;
504 0 : rv = httpChannel->GetResponseStatusText(statusText);
505 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
506 :
507 0 : response = new InternalResponse(responseStatus, statusText);
508 :
509 0 : response->Headers()->FillResponseHeaders(httpChannel);
510 :
511 : // If Content-Encoding or Transfer-Encoding headers are set, then the actual
512 : // Content-Length (which refer to the decoded data) is obscured behind the encodings.
513 0 : ErrorResult result;
514 0 : if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
515 0 : response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
516 : NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
517 0 : "or Transfer-Encoding headers.");
518 0 : contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
519 : }
520 0 : MOZ_ASSERT(!result.Failed());
521 : } else {
522 2 : response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
523 :
524 2 : ErrorResult result;
525 2 : nsAutoCString contentType;
526 1 : rv = channel->GetContentType(contentType);
527 1 : if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
528 2 : nsAutoCString contentCharset;
529 1 : channel->GetContentCharset(contentCharset);
530 1 : if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
531 1 : contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
532 : }
533 :
534 3 : response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
535 : contentType,
536 2 : result);
537 1 : MOZ_ASSERT(!result.Failed());
538 : }
539 :
540 1 : if (contentLength > 0) {
541 2 : nsAutoCString contentLenStr;
542 1 : contentLenStr.AppendInt(contentLength);
543 3 : response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
544 : contentLenStr,
545 2 : result);
546 1 : MOZ_ASSERT(!result.Failed());
547 : }
548 : }
549 :
550 : // We open a pipe so that we can immediately set the pipe's read end as the
551 : // response's body. Setting the segment size to UINT32_MAX means that the
552 : // pipe has infinite space. The nsIChannel will continue to buffer data in
553 : // xpcom events even if we block on a fixed size pipe. It might be possible
554 : // to suspend the channel and then resume when there is space available, but
555 : // for now use an infinite pipe to avoid blocking.
556 2 : nsCOMPtr<nsIInputStream> pipeInputStream;
557 1 : rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
558 2 : getter_AddRefs(mPipeOutputStream),
559 : 0, /* default segment size */
560 : UINT32_MAX /* infinite pipe */,
561 : true /* non-blocking input, otherwise you deadlock */,
562 : false /* blocking output, since the pipe is 'in'finite */ );
563 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
564 0 : FailWithNetworkError();
565 : // Cancel request.
566 0 : return rv;
567 : }
568 1 : response->SetBody(pipeInputStream, contentLength);
569 :
570 1 : response->InitChannelInfo(channel);
571 :
572 2 : nsCOMPtr<nsIURI> channelURI;
573 1 : rv = channel->GetURI(getter_AddRefs(channelURI));
574 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
575 0 : FailWithNetworkError();
576 : // Cancel request.
577 0 : return rv;
578 : }
579 :
580 2 : nsCOMPtr<nsILoadInfo> loadInfo;
581 1 : rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
582 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
583 0 : FailWithNetworkError();
584 0 : return rv;
585 : }
586 :
587 : // Propagate any tainting from the channel back to our response here. This
588 : // step is not reflected in the spec because the spec is written such that
589 : // FetchEvent.respondWith() just passes the already-tainted Response back to
590 : // the outer fetch(). In gecko, however, we serialize the Response through
591 : // the channel and must regenerate the tainting from the channel in the
592 : // interception case.
593 1 : mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
594 :
595 : // Resolves fetch() promise which may trigger code running in a worker. Make
596 : // sure the Response is fully initialized before calling this.
597 1 : mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
598 :
599 : // From "Main Fetch" step 17: SRI-part1.
600 3 : if (mResponse->Type() != ResponseType::Error &&
601 1 : !mRequest->GetIntegrity().IsEmpty() &&
602 0 : mSRIMetadata.IsEmpty()) {
603 0 : nsIConsoleReportCollector* aReporter = nullptr;
604 0 : if (mObserver) {
605 0 : aReporter = mObserver->GetReporter();
606 : }
607 :
608 0 : nsAutoCString sourceUri;
609 0 : if (mDocument && mDocument->GetDocumentURI()) {
610 0 : mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
611 0 : } else if (!mWorkerScript.IsEmpty()) {
612 0 : sourceUri.Assign(mWorkerScript);
613 : }
614 0 : SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri,
615 0 : aReporter, &mSRIMetadata);
616 : mSRIDataVerifier = new SRICheckDataVerifier(mSRIMetadata, sourceUri,
617 0 : aReporter);
618 :
619 : // Do not retarget off main thread when using SRI API.
620 0 : return NS_OK;
621 : }
622 :
623 2 : nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
624 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
625 0 : FailWithNetworkError();
626 : // Cancel request.
627 0 : return rv;
628 : }
629 :
630 : // Try to retarget off main thread.
631 2 : if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
632 1 : Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
633 : }
634 1 : return NS_OK;
635 : }
636 :
637 : namespace {
638 :
639 : // Runnable to call the observer OnDataAvailable on the main-thread.
640 3 : class DataAvailableRunnable final : public Runnable
641 : {
642 : RefPtr<FetchDriverObserver> mObserver;
643 :
644 : public:
645 1 : explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
646 1 : : Runnable("dom::DataAvailableRunnable")
647 1 : , mObserver(aObserver)
648 : {
649 1 : MOZ_ASSERT(aObserver);
650 1 : }
651 :
652 : NS_IMETHOD
653 1 : Run() override
654 : {
655 1 : mObserver->OnDataAvailable();
656 1 : mObserver = nullptr;
657 1 : return NS_OK;
658 : }
659 : };
660 :
661 : struct SRIVerifierAndOutputHolder {
662 0 : SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
663 : nsIOutputStream* aOutputStream)
664 0 : : mVerifier(aVerifier)
665 0 : , mOutputStream(aOutputStream)
666 0 : {}
667 :
668 : SRICheckDataVerifier* mVerifier;
669 : nsIOutputStream* mOutputStream;
670 :
671 : private:
672 : SRIVerifierAndOutputHolder() = delete;
673 : };
674 :
675 : // Just like NS_CopySegmentToStream, but also sends the data into an
676 : // SRICheckDataVerifier.
677 : nsresult
678 0 : CopySegmentToStreamAndSRI(nsIInputStream* aInStr,
679 : void* aClosure,
680 : const char* aBuffer,
681 : uint32_t aOffset,
682 : uint32_t aCount,
683 : uint32_t* aCountWritten)
684 : {
685 0 : auto holder = static_cast<SRIVerifierAndOutputHolder*>(aClosure);
686 0 : MOZ_DIAGNOSTIC_ASSERT(holder && holder->mVerifier && holder->mOutputStream,
687 : "Bogus holder");
688 : nsresult rv =
689 0 : holder->mVerifier->Update(aCount,
690 0 : reinterpret_cast<const uint8_t*>(aBuffer));
691 0 : NS_ENSURE_SUCCESS(rv, rv);
692 :
693 : // The rest is just like NS_CopySegmentToStream.
694 0 : *aCountWritten = 0;
695 0 : while (aCount) {
696 0 : uint32_t n = 0;
697 0 : rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
698 0 : if (NS_FAILED(rv)) {
699 0 : return rv;
700 : }
701 0 : aBuffer += n;
702 0 : aCount -= n;
703 0 : *aCountWritten += n;
704 : }
705 0 : return NS_OK;
706 : }
707 :
708 : } // anonymous namespace
709 :
710 : NS_IMETHODIMP
711 1 : FetchDriver::OnDataAvailable(nsIRequest* aRequest,
712 : nsISupports* aContext,
713 : nsIInputStream* aInputStream,
714 : uint64_t aOffset,
715 : uint32_t aCount)
716 : {
717 : // NB: This can be called on any thread! But we're guaranteed that it is
718 : // called between OnStartRequest and OnStopRequest, so we don't need to worry
719 : // about races.
720 :
721 1 : if (mObserver) {
722 1 : if (NS_IsMainThread()) {
723 0 : mObserver->OnDataAvailable();
724 : } else {
725 3 : RefPtr<Runnable> runnable = new DataAvailableRunnable(mObserver);
726 : nsresult rv =
727 1 : mMainThreadEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
728 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
729 0 : return rv;
730 : }
731 : }
732 : }
733 :
734 : uint32_t aRead;
735 1 : MOZ_ASSERT(mResponse);
736 1 : MOZ_ASSERT(mPipeOutputStream);
737 :
738 : // From "Main Fetch" step 17: SRI-part2.
739 2 : if (mResponse->Type() != ResponseType::Error &&
740 1 : !mRequest->GetIntegrity().IsEmpty()) {
741 0 : MOZ_ASSERT(mSRIDataVerifier);
742 :
743 0 : SRIVerifierAndOutputHolder holder(mSRIDataVerifier, mPipeOutputStream);
744 : nsresult rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
745 0 : &holder, aCount, &aRead);
746 0 : return rv;
747 : }
748 :
749 : nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
750 : mPipeOutputStream,
751 1 : aCount, &aRead);
752 1 : return rv;
753 : }
754 :
755 : NS_IMETHODIMP
756 1 : FetchDriver::OnStopRequest(nsIRequest* aRequest,
757 : nsISupports* aContext,
758 : nsresult aStatusCode)
759 : {
760 1 : workers::AssertIsOnMainThread();
761 1 : if (NS_FAILED(aStatusCode)) {
762 0 : nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
763 0 : if (outputStream) {
764 0 : outputStream->CloseWithStatus(NS_BINDING_FAILED);
765 : }
766 :
767 : // We proceed as usual here, since we've already created a successful response
768 : // from OnStartRequest.
769 : } else {
770 1 : MOZ_ASSERT(mResponse);
771 1 : MOZ_ASSERT(!mResponse->IsError());
772 :
773 : // From "Main Fetch" step 17: SRI-part3.
774 2 : if (mResponse->Type() != ResponseType::Error &&
775 1 : !mRequest->GetIntegrity().IsEmpty()) {
776 0 : MOZ_ASSERT(mSRIDataVerifier);
777 :
778 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
779 :
780 0 : nsIConsoleReportCollector* aReporter = nullptr;
781 0 : if (mObserver) {
782 0 : aReporter = mObserver->GetReporter();
783 : }
784 :
785 0 : nsAutoCString sourceUri;
786 0 : if (mDocument && mDocument->GetDocumentURI()) {
787 0 : mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
788 0 : } else if (!mWorkerScript.IsEmpty()) {
789 0 : sourceUri.Assign(mWorkerScript);
790 : }
791 0 : nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri,
792 0 : aReporter);
793 0 : if (NS_FAILED(rv)) {
794 0 : FailWithNetworkError();
795 : // Cancel request.
796 0 : return rv;
797 : }
798 : }
799 :
800 1 : if (mPipeOutputStream) {
801 1 : mPipeOutputStream->Close();
802 : }
803 : }
804 :
805 1 : if (mObserver) {
806 2 : if (mResponse->Type() != ResponseType::Error &&
807 1 : !mRequest->GetIntegrity().IsEmpty()) {
808 : //From "Main Fetch" step 23: Process response.
809 0 : MOZ_ASSERT(mResponse);
810 0 : mObserver->OnResponseAvailable(mResponse);
811 : #ifdef DEBUG
812 0 : mResponseAvailableCalled = true;
813 : #endif
814 : }
815 :
816 1 : mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
817 1 : mObserver = nullptr;
818 : }
819 :
820 1 : mChannel = nullptr;
821 1 : return NS_OK;
822 : }
823 :
824 : NS_IMETHODIMP
825 0 : FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
826 : nsIChannel* aNewChannel,
827 : uint32_t aFlags,
828 : nsIAsyncVerifyRedirectCallback *aCallback)
829 : {
830 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
831 0 : if (httpChannel) {
832 0 : SetRequestHeaders(httpChannel);
833 : }
834 :
835 0 : nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
836 0 : nsAutoCString tRPHeaderCValue;
837 0 : if (oldHttpChannel) {
838 0 : Unused << oldHttpChannel->GetResponseHeader(NS_LITERAL_CSTRING("referrer-policy"),
839 0 : tRPHeaderCValue);
840 : }
841 :
842 : // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
843 0 : nsCOMPtr<nsIURI> uri;
844 0 : MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
845 :
846 0 : nsCOMPtr<nsIURI> uriClone;
847 0 : nsresult rv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
848 0 : if(NS_WARN_IF(NS_FAILED(rv))){
849 0 : return rv;
850 : }
851 0 : nsCString spec;
852 0 : rv = uriClone->GetSpec(spec);
853 0 : if(NS_WARN_IF(NS_FAILED(rv))){
854 0 : return rv;
855 : }
856 0 : nsCString fragment;
857 0 : rv = uri->GetRef(fragment);
858 0 : if(NS_WARN_IF(NS_FAILED(rv))){
859 0 : return rv;
860 : }
861 :
862 0 : mRequest->AddURL(spec, fragment);
863 0 : NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
864 : // updates request’s associated referrer policy according to the
865 : // Referrer-Policy header (if any).
866 0 : if (!tRPHeaderValue.IsEmpty()) {
867 : net::ReferrerPolicy net_referrerPolicy =
868 0 : nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
869 0 : if (net_referrerPolicy != net::RP_Unset) {
870 0 : mRequest->SetReferrerPolicy(net_referrerPolicy);
871 : // Should update channel's referrer policy
872 0 : if (httpChannel) {
873 0 : rv = FetchUtil::SetRequestReferrer(mPrincipal,
874 : mDocument,
875 : httpChannel,
876 0 : mRequest);
877 0 : NS_ENSURE_SUCCESS(rv, rv);
878 : }
879 : }
880 : }
881 :
882 0 : aCallback->OnRedirectVerifyCallback(NS_OK);
883 0 : return NS_OK;
884 : }
885 :
886 : NS_IMETHODIMP
887 1 : FetchDriver::CheckListenerChain()
888 : {
889 1 : return NS_OK;
890 : }
891 :
892 : NS_IMETHODIMP
893 3 : FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
894 : {
895 3 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
896 0 : *aResult = static_cast<nsIChannelEventSink*>(this);
897 0 : NS_ADDREF_THIS();
898 0 : return NS_OK;
899 : }
900 3 : if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
901 0 : *aResult = static_cast<nsIStreamListener*>(this);
902 0 : NS_ADDREF_THIS();
903 0 : return NS_OK;
904 : }
905 3 : if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
906 0 : *aResult = static_cast<nsIRequestObserver*>(this);
907 0 : NS_ADDREF_THIS();
908 0 : return NS_OK;
909 : }
910 :
911 3 : return QueryInterface(aIID, aResult);
912 : }
913 :
914 : void
915 1 : FetchDriver::SetDocument(nsIDocument* aDocument)
916 : {
917 : // Cannot set document after Fetch() has been called.
918 1 : MOZ_ASSERT(!mFetchCalled);
919 1 : mDocument = aDocument;
920 1 : }
921 :
922 : void
923 0 : FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const
924 : {
925 0 : MOZ_ASSERT(aChannel);
926 :
927 0 : AutoTArray<InternalHeaders::Entry, 5> headers;
928 0 : mRequest->Headers()->GetEntries(headers);
929 0 : bool hasAccept = false;
930 0 : for (uint32_t i = 0; i < headers.Length(); ++i) {
931 0 : if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) {
932 0 : hasAccept = true;
933 : }
934 0 : if (headers[i].mValue.IsEmpty()) {
935 0 : DebugOnly<nsresult> rv = aChannel->SetEmptyRequestHeader(headers[i].mName);
936 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
937 : } else {
938 : DebugOnly<nsresult> rv =
939 0 : aChannel->SetRequestHeader(headers[i].mName, headers[i].mValue,
940 0 : false /* merge */);
941 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
942 : }
943 : }
944 :
945 0 : if (!hasAccept) {
946 : DebugOnly<nsresult> rv =
947 0 : aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
948 0 : NS_LITERAL_CSTRING("*/*"),
949 0 : false /* merge */);
950 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
951 : }
952 :
953 0 : if (mRequest->ForceOriginHeader()) {
954 0 : nsAutoString origin;
955 0 : if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
956 : DebugOnly<nsresult> rv =
957 0 : aChannel->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
958 0 : NS_ConvertUTF16toUTF8(origin),
959 0 : false /* merge */);
960 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
961 : }
962 : }
963 0 : }
964 :
965 : void
966 0 : FetchDriver::Aborted()
967 : {
968 0 : if (mObserver) {
969 : #ifdef DEBUG
970 0 : mResponseAvailableCalled = true;
971 : #endif
972 0 : mObserver->OnResponseEnd(FetchDriverObserver::eAborted);
973 0 : mObserver = nullptr;
974 : }
975 :
976 0 : if (mChannel) {
977 0 : mChannel->Cancel(NS_BINDING_ABORTED);
978 0 : mChannel = nullptr;
979 : }
980 0 : }
981 :
982 : } // namespace dom
983 : } // namespace mozilla
|