1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et 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 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
9
10 #include "mozilla/ConsoleReportCollector.h"
11 #include "mozilla/ipc/FileDescriptorSetParent.h"
12 #include "mozilla/ipc/IPCStreamUtils.h"
13 #include "mozilla/net/HttpChannelParent.h"
14 #include "mozilla/dom/ContentParent.h"
15 #include "mozilla/dom/ContentProcessManager.h"
16 #include "mozilla/dom/Element.h"
17 #include "mozilla/dom/ServiceWorkerUtils.h"
18 #include "mozilla/dom/BrowserParent.h"
19 #include "mozilla/net/NeckoParent.h"
20 #include "mozilla/InputStreamLengthHelper.h"
21 #include "mozilla/IntegerPrintfMacros.h"
22 #include "mozilla/ProfilerLabels.h"
23 #include "mozilla/StoragePrincipalHelper.h"
24 #include "mozilla/UniquePtr.h"
25 #include "mozilla/Unused.h"
26 #include "HttpBackgroundChannelParent.h"
27 #include "ParentChannelListener.h"
28 #include "nsICacheInfoChannel.h"
29 #include "nsHttpHandler.h"
30 #include "nsNetCID.h"
31 #include "nsNetUtil.h"
32 #include "nsISupportsPriority.h"
33 #include "mozilla/net/BackgroundChannelRegistrar.h"
34 #include "nsSerializationHelper.h"
35 #include "nsISerializable.h"
36 #include "mozilla/ipc/InputStreamUtils.h"
37 #include "mozilla/ipc/URIUtils.h"
38 #include "SerializedLoadContext.h"
39 #include "nsIAuthPrompt.h"
40 #include "nsIAuthPrompt2.h"
41 #include "mozilla/ipc/BackgroundUtils.h"
42 #include "mozilla/LoadInfo.h"
43 #include "nsQueryObject.h"
44 #include "mozilla/BasePrincipal.h"
45 #include "nsCORSListenerProxy.h"
46 #include "nsIIPCSerializableInputStream.h"
47 #include "nsIPrompt.h"
48 #include "nsIPromptFactory.h"
49 #include "mozilla/net/ChannelEventQueue.h"
50 #include "mozilla/net/RedirectChannelRegistrar.h"
51 #include "nsIWindowWatcher.h"
52 #include "mozilla/dom/Document.h"
53 #include "nsISecureBrowserUI.h"
54 #include "nsStreamUtils.h"
55 #include "nsStringStream.h"
56 #include "nsThreadUtils.h"
57 #include "nsQueryObject.h"
58 #include "nsIMultiPartChannel.h"
59 #include "nsIViewSourceChannel.h"
60
61 using mozilla::BasePrincipal;
62 using namespace mozilla::dom;
63 using namespace mozilla::ipc;
64
65 namespace mozilla {
66 namespace net {
67
HttpChannelParent(dom::BrowserParent * iframeEmbedding,nsILoadContext * aLoadContext,PBOverrideStatus aOverrideStatus)68 HttpChannelParent::HttpChannelParent(dom::BrowserParent* iframeEmbedding,
69 nsILoadContext* aLoadContext,
70 PBOverrideStatus aOverrideStatus)
71 : mLoadContext(aLoadContext),
72 mIPCClosed(false),
73 mPBOverride(aOverrideStatus),
74 mStatus(NS_OK),
75 mIgnoreProgress(false),
76 mSentRedirect1BeginFailed(false),
77 mReceivedRedirect2Verify(false),
78 mHasSuspendedByBackPressure(false),
79 mCacheNeedFlowControlInitialized(false),
80 mNeedFlowControl(true),
81 mSuspendedForFlowControl(false),
82 mAfterOnStartRequestBegun(false),
83 mDataSentToChildProcess(false) {
84 LOG(("Creating HttpChannelParent [this=%p]\n", this));
85
86 // Ensure gHttpHandler is initialized: we need the atom table up and running.
87 nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
88 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
89
90 MOZ_ASSERT(gHttpHandler);
91 mHttpHandler = gHttpHandler;
92
93 mBrowserParent = iframeEmbedding;
94
95 mSendWindowSize = gHttpHandler->SendWindowSize();
96
97 mEventQ =
98 new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
99 }
100
~HttpChannelParent()101 HttpChannelParent::~HttpChannelParent() {
102 LOG(("Destroying HttpChannelParent [this=%p]\n", this));
103 CleanupBackgroundChannel();
104
105 MOZ_ASSERT(!mRedirectCallback);
106 if (NS_WARN_IF(mRedirectCallback)) {
107 mRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_UNEXPECTED);
108 mRedirectCallback = nullptr;
109 }
110 mEventQ->NotifyReleasingOwner();
111 }
112
ActorDestroy(ActorDestroyReason why)113 void HttpChannelParent::ActorDestroy(ActorDestroyReason why) {
114 // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
115 // yet, but child process has crashed. We must not try to send any more msgs
116 // to child, or IPDL will kill chrome process, too.
117 mIPCClosed = true;
118 CleanupBackgroundChannel();
119 }
120
Init(const HttpChannelCreationArgs & aArgs)121 bool HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) {
122 LOG(("HttpChannelParent::Init [this=%p]\n", this));
123 AUTO_PROFILER_LABEL("HttpChannelParent::Init", NETWORK);
124 switch (aArgs.type()) {
125 case HttpChannelCreationArgs::THttpChannelOpenArgs: {
126 const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
127 return DoAsyncOpen(
128 a.uri(), a.original(), a.doc(), a.referrerInfo(), a.apiRedirectTo(),
129 a.topWindowURI(), a.loadFlags(), a.requestHeaders(),
130 a.requestMethod(), a.uploadStream(), a.uploadStreamHasHeaders(),
131 a.priority(), a.classOfService(), a.redirectionLimit(), a.allowSTS(),
132 a.thirdPartyFlags(), a.resumeAt(), a.startPos(), a.entityID(),
133 a.allowSpdy(), a.allowHttp3(), a.allowAltSvc(), a.beConservative(),
134 a.bypassProxy(), a.tlsFlags(), a.loadInfo(), a.cacheKey(),
135 a.requestContextID(), a.preflightArgs(), a.initialRwin(),
136 a.blockAuthPrompt(), a.allowStaleCacheContent(),
137 a.preferCacheLoadOverBypass(), a.contentTypeHint(), a.corsMode(),
138 a.redirectMode(), a.channelId(), a.integrityMetadata(),
139 a.contentWindowId(), a.preferredAlternativeTypes(),
140 a.topBrowsingContextId(), a.launchServiceWorkerStart(),
141 a.launchServiceWorkerEnd(), a.dispatchFetchEventStart(),
142 a.dispatchFetchEventEnd(), a.handleFetchEventStart(),
143 a.handleFetchEventEnd(), a.forceMainDocumentChannel(),
144 a.navigationStartTimeStamp());
145 }
146 case HttpChannelCreationArgs::THttpChannelConnectArgs: {
147 const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
148 return ConnectChannel(cArgs.registrarId());
149 }
150 default:
151 MOZ_ASSERT_UNREACHABLE("unknown open type");
152 return false;
153 }
154 }
155
TryInvokeAsyncOpen(nsresult aRv)156 void HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv) {
157 LOG(("HttpChannelParent::TryInvokeAsyncOpen [this=%p barrier=%u rv=%" PRIx32
158 "]\n",
159 this, mAsyncOpenBarrier, static_cast<uint32_t>(aRv)));
160 MOZ_ASSERT(NS_IsMainThread());
161 AUTO_PROFILER_LABEL("HttpChannelParent::TryInvokeAsyncOpen", NETWORK);
162
163 // TryInvokeAsyncOpen is called more than we expected.
164 // Assert in nightly build but ignore it in release channel.
165 MOZ_DIAGNOSTIC_ASSERT(mAsyncOpenBarrier > 0);
166 if (NS_WARN_IF(!mAsyncOpenBarrier)) {
167 return;
168 }
169
170 if (--mAsyncOpenBarrier > 0 && NS_SUCCEEDED(aRv)) {
171 // Need to wait for more events.
172 return;
173 }
174
175 InvokeAsyncOpen(aRv);
176 }
177
OnBackgroundParentReady(HttpBackgroundChannelParent * aBgParent)178 void HttpChannelParent::OnBackgroundParentReady(
179 HttpBackgroundChannelParent* aBgParent) {
180 LOG(("HttpChannelParent::OnBackgroundParentReady [this=%p bgParent=%p]\n",
181 this, aBgParent));
182 MOZ_ASSERT(NS_IsMainThread());
183 MOZ_ASSERT(!mBgParent);
184
185 mBgParent = aBgParent;
186
187 mPromise.ResolveIfExists(true, __func__);
188 }
189
OnBackgroundParentDestroyed()190 void HttpChannelParent::OnBackgroundParentDestroyed() {
191 LOG(("HttpChannelParent::OnBackgroundParentDestroyed [this=%p]\n", this));
192 MOZ_ASSERT(NS_IsMainThread());
193
194 if (!mPromise.IsEmpty()) {
195 MOZ_ASSERT(!mBgParent);
196 mPromise.Reject(NS_ERROR_FAILURE, __func__);
197 return;
198 }
199
200 if (!mBgParent) {
201 return;
202 }
203
204 // Background channel is closed unexpectly, abort PHttpChannel operation.
205 mBgParent = nullptr;
206 Delete();
207 }
208
CleanupBackgroundChannel()209 void HttpChannelParent::CleanupBackgroundChannel() {
210 LOG(("HttpChannelParent::CleanupBackgroundChannel [this=%p bgParent=%p]\n",
211 this, mBgParent.get()));
212 MOZ_ASSERT(NS_IsMainThread());
213
214 if (mBgParent) {
215 RefPtr<HttpBackgroundChannelParent> bgParent = std::move(mBgParent);
216 bgParent->OnChannelClosed();
217 return;
218 }
219
220 // The nsHttpChannel may have a reference to this parent, release it
221 // to avoid circular references.
222 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
223 if (httpChannelImpl) {
224 httpChannelImpl->SetWarningReporter(nullptr);
225 }
226
227 if (!mPromise.IsEmpty()) {
228 mRequest.DisconnectIfExists();
229 mPromise.Reject(NS_ERROR_FAILURE, __func__);
230
231 if (!mChannel) {
232 return;
233 }
234
235 // This HttpChannelParent might still have a reference from
236 // BackgroundChannelRegistrar.
237 nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
238 BackgroundChannelRegistrar::GetOrCreate();
239 MOZ_ASSERT(registrar);
240
241 registrar->DeleteChannel(mChannel->ChannelId());
242
243 // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure
244 // is still on going. we need to abort AsyncOpen with failure to destroy
245 // PHttpChannel actor.
246 if (mAsyncOpenBarrier) {
247 TryInvokeAsyncOpen(NS_ERROR_FAILURE);
248 }
249 }
250 }
251
OtherPid() const252 base::ProcessId HttpChannelParent::OtherPid() const {
253 if (mIPCClosed) {
254 return 0;
255 }
256 return PHttpChannelParent::OtherPid();
257 }
258
259 //-----------------------------------------------------------------------------
260 // HttpChannelParent::nsISupports
261 //-----------------------------------------------------------------------------
262
263 NS_IMPL_ADDREF(HttpChannelParent)
NS_IMPL_RELEASE(HttpChannelParent)264 NS_IMPL_RELEASE(HttpChannelParent)
265 NS_INTERFACE_MAP_BEGIN(HttpChannelParent)
266 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
267 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
268 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
269 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
270 NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
271 NS_INTERFACE_MAP_ENTRY(nsIParentRedirectingChannel)
272 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
273 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
274 NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener)
275 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
276 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel)
277 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelParent)
278 NS_INTERFACE_MAP_END
279
280 //-----------------------------------------------------------------------------
281 // HttpChannelParent::nsIInterfaceRequestor
282 //-----------------------------------------------------------------------------
283
284 NS_IMETHODIMP
285 HttpChannelParent::GetInterface(const nsIID& aIID, void** result) {
286 // A system XHR can be created without reference to a window, hence mTabParent
287 // may be null. In that case we want to let the window watcher pick a prompt
288 // directly.
289 if (!mBrowserParent && (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
290 aIID.Equals(NS_GET_IID(nsIAuthPrompt2)))) {
291 nsresult rv;
292 nsCOMPtr<nsIWindowWatcher> wwatch =
293 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
294 NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
295
296 bool hasWindowCreator = false;
297 Unused << wwatch->HasWindowCreator(&hasWindowCreator);
298 if (!hasWindowCreator) {
299 return NS_ERROR_NO_INTERFACE;
300 }
301
302 nsCOMPtr<nsIPromptFactory> factory = do_QueryInterface(wwatch);
303 if (!factory) {
304 return NS_ERROR_NO_INTERFACE;
305 }
306 rv = factory->GetPrompt(nullptr, aIID, reinterpret_cast<void**>(result));
307 if (NS_FAILED(rv)) {
308 return NS_ERROR_NO_INTERFACE;
309 }
310 return NS_OK;
311 }
312
313 // Only support nsILoadContext if child channel's callbacks did too
314 if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
315 nsCOMPtr<nsILoadContext> copy = mLoadContext;
316 copy.forget(result);
317 return NS_OK;
318 }
319
320 return QueryInterface(aIID, result);
321 }
322
323 //-----------------------------------------------------------------------------
324 // HttpChannelParent::PHttpChannelParent
325 //-----------------------------------------------------------------------------
326
AsyncOpenFailed(nsresult aRv)327 void HttpChannelParent::AsyncOpenFailed(nsresult aRv) {
328 MOZ_ASSERT(NS_IsMainThread());
329 MOZ_ASSERT(NS_FAILED(aRv));
330
331 // Break the reference cycle among HttpChannelParent,
332 // ParentChannelListener, and nsHttpChannel to avoid memory leakage.
333 mChannel = nullptr;
334 mParentListener = nullptr;
335
336 if (!mIPCClosed) {
337 Unused << SendFailedAsyncOpen(aRv);
338 }
339 }
340
InvokeAsyncOpen(nsresult rv)341 void HttpChannelParent::InvokeAsyncOpen(nsresult rv) {
342 LOG(("HttpChannelParent::InvokeAsyncOpen [this=%p rv=%" PRIx32 "]\n", this,
343 static_cast<uint32_t>(rv)));
344 MOZ_ASSERT(NS_IsMainThread());
345
346 if (NS_FAILED(rv)) {
347 AsyncOpenFailed(rv);
348 return;
349 }
350
351 rv = mChannel->AsyncOpen(mParentListener);
352 if (NS_FAILED(rv)) {
353 AsyncOpenFailed(rv);
354 }
355 }
356
DoAsyncOpen(const URIParams & aURI,const Maybe<URIParams> & aOriginalURI,const Maybe<URIParams> & aDocURI,nsIReferrerInfo * aReferrerInfo,const Maybe<URIParams> & aAPIRedirectToURI,const Maybe<URIParams> & aTopWindowURI,const uint32_t & aLoadFlags,const RequestHeaderTuples & requestHeaders,const nsCString & requestMethod,const Maybe<IPCStream> & uploadStream,const bool & uploadStreamHasHeaders,const int16_t & priority,const uint32_t & classOfService,const uint8_t & redirectionLimit,const bool & allowSTS,const uint32_t & thirdPartyFlags,const bool & doResumeAt,const uint64_t & startPos,const nsCString & entityID,const bool & allowSpdy,const bool & allowHttp3,const bool & allowAltSvc,const bool & beConservative,const bool & bypassProxy,const uint32_t & tlsFlags,const Maybe<LoadInfoArgs> & aLoadInfoArgs,const uint32_t & aCacheKey,const uint64_t & aRequestContextID,const Maybe<CorsPreflightArgs> & aCorsPreflightArgs,const uint32_t & aInitialRwin,const bool & aBlockAuthPrompt,const bool & aAllowStaleCacheContent,const bool & aPreferCacheLoadOverBypass,const nsCString & aContentTypeHint,const uint32_t & aCorsMode,const uint32_t & aRedirectMode,const uint64_t & aChannelId,const nsString & aIntegrityMetadata,const uint64_t & aContentWindowId,const nsTArray<PreferredAlternativeDataTypeParams> & aPreferredAlternativeTypes,const uint64_t & aTopBrowsingContextId,const TimeStamp & aLaunchServiceWorkerStart,const TimeStamp & aLaunchServiceWorkerEnd,const TimeStamp & aDispatchFetchEventStart,const TimeStamp & aDispatchFetchEventEnd,const TimeStamp & aHandleFetchEventStart,const TimeStamp & aHandleFetchEventEnd,const bool & aForceMainDocumentChannel,const TimeStamp & aNavigationStartTimeStamp)357 bool HttpChannelParent::DoAsyncOpen(
358 const URIParams& aURI, const Maybe<URIParams>& aOriginalURI,
359 const Maybe<URIParams>& aDocURI, nsIReferrerInfo* aReferrerInfo,
360 const Maybe<URIParams>& aAPIRedirectToURI,
361 const Maybe<URIParams>& aTopWindowURI, const uint32_t& aLoadFlags,
362 const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
363 const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
364 const int16_t& priority, const uint32_t& classOfService,
365 const uint8_t& redirectionLimit, const bool& allowSTS,
366 const uint32_t& thirdPartyFlags, const bool& doResumeAt,
367 const uint64_t& startPos, const nsCString& entityID, const bool& allowSpdy,
368 const bool& allowHttp3, const bool& allowAltSvc, const bool& beConservative,
369 const bool& bypassProxy, const uint32_t& tlsFlags,
370 const Maybe<LoadInfoArgs>& aLoadInfoArgs, const uint32_t& aCacheKey,
371 const uint64_t& aRequestContextID,
372 const Maybe<CorsPreflightArgs>& aCorsPreflightArgs,
373 const uint32_t& aInitialRwin, const bool& aBlockAuthPrompt,
374 const bool& aAllowStaleCacheContent, const bool& aPreferCacheLoadOverBypass,
375 const nsCString& aContentTypeHint, const uint32_t& aCorsMode,
376 const uint32_t& aRedirectMode, const uint64_t& aChannelId,
377 const nsString& aIntegrityMetadata, const uint64_t& aContentWindowId,
378 const nsTArray<PreferredAlternativeDataTypeParams>&
379 aPreferredAlternativeTypes,
380 const uint64_t& aTopBrowsingContextId,
381 const TimeStamp& aLaunchServiceWorkerStart,
382 const TimeStamp& aLaunchServiceWorkerEnd,
383 const TimeStamp& aDispatchFetchEventStart,
384 const TimeStamp& aDispatchFetchEventEnd,
385 const TimeStamp& aHandleFetchEventStart,
386 const TimeStamp& aHandleFetchEventEnd,
387 const bool& aForceMainDocumentChannel,
388 const TimeStamp& aNavigationStartTimeStamp) {
389 nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
390 if (!uri) {
391 // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
392 // null deref here.
393 return false;
394 }
395 nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
396 nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
397 nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
398 nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
399
400 LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64
401 " top bid=%" PRIx64 "]\n",
402 this, uri->GetSpecOrDefault().get(), aChannelId, aTopBrowsingContextId));
403
404 nsresult rv;
405
406 nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
407 if (NS_FAILED(rv)) return SendFailedAsyncOpen(rv);
408
409 nsCOMPtr<nsILoadInfo> loadInfo;
410 rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
411 getter_AddRefs(loadInfo));
412 if (NS_FAILED(rv)) {
413 return SendFailedAsyncOpen(rv);
414 }
415
416 nsCOMPtr<nsIChannel> channel;
417 rv = NS_NewChannelInternal(getter_AddRefs(channel), uri, loadInfo, nullptr,
418 nullptr, nullptr, aLoadFlags, ios);
419 if (NS_FAILED(rv)) {
420 return SendFailedAsyncOpen(rv);
421 }
422
423 RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(channel, &rv);
424 if (NS_FAILED(rv)) {
425 return SendFailedAsyncOpen(rv);
426 }
427
428 // Set attributes needed to create a FetchEvent from this channel.
429 httpChannel->SetCorsMode(aCorsMode);
430 httpChannel->SetRedirectMode(aRedirectMode);
431
432 // Set the channelId allocated in child to the parent instance
433 httpChannel->SetChannelId(aChannelId);
434 httpChannel->SetTopLevelContentWindowId(aContentWindowId);
435 httpChannel->SetTopBrowsingContextId(aTopBrowsingContextId);
436
437 httpChannel->SetIntegrityMetadata(aIntegrityMetadata);
438
439 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(httpChannel);
440 if (httpChannelImpl) {
441 httpChannelImpl->SetWarningReporter(this);
442 }
443 httpChannel->SetTimingEnabled(true);
444 if (mPBOverride != kPBOverride_Unset) {
445 httpChannel->SetPrivate(mPBOverride == kPBOverride_Private);
446 }
447
448 if (doResumeAt) httpChannel->ResumeAt(startPos, entityID);
449
450 if (originalUri) httpChannel->SetOriginalURI(originalUri);
451 if (docUri) httpChannel->SetDocumentURI(docUri);
452 if (aReferrerInfo) {
453 // Referrer header is computed in child no need to recompute here
454 rv =
455 httpChannel->SetReferrerInfoInternal(aReferrerInfo, false, false, true);
456 MOZ_ASSERT(NS_SUCCEEDED(rv));
457 }
458
459 if (apiRedirectToUri) httpChannel->RedirectTo(apiRedirectToUri);
460 if (topWindowUri) {
461 httpChannel->SetTopWindowURI(topWindowUri);
462 }
463
464 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
465 httpChannel->SetLoadFlags(aLoadFlags);
466 }
467
468 if (aForceMainDocumentChannel) {
469 httpChannel->SetIsMainDocumentChannel(true);
470 }
471
472 for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
473 if (requestHeaders[i].mEmpty) {
474 httpChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
475 } else {
476 httpChannel->SetRequestHeader(requestHeaders[i].mHeader,
477 requestHeaders[i].mValue,
478 requestHeaders[i].mMerge);
479 }
480 }
481
482 RefPtr<ParentChannelListener> parentListener = new ParentChannelListener(
483 this, mBrowserParent ? mBrowserParent->GetBrowsingContext() : nullptr,
484 mLoadContext && mLoadContext->UsePrivateBrowsing());
485
486 httpChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
487
488 if (aCorsPreflightArgs.isSome()) {
489 const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
490 httpChannel->SetCorsPreflightParameters(args.unsafeHeaders(), false);
491 }
492
493 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
494 if (stream) {
495 int64_t length;
496 if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
497 httpChannel->InternalSetUploadStreamLength(length >= 0 ? length : 0);
498 } else {
499 // Wait for the nputStreamLengthHelper::GetAsyncLength callback.
500 ++mAsyncOpenBarrier;
501
502 // Let's resolve the size of the stream. The following operation is always
503 // async.
504 RefPtr<HttpChannelParent> self = this;
505 InputStreamLengthHelper::GetAsyncLength(stream, [self, httpChannel](
506 int64_t aLength) {
507 httpChannel->InternalSetUploadStreamLength(aLength >= 0 ? aLength : 0);
508 self->TryInvokeAsyncOpen(NS_OK);
509 });
510 }
511
512 httpChannel->InternalSetUploadStream(stream);
513 httpChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
514 }
515
516 nsCOMPtr<nsICacheInfoChannel> cacheChannel =
517 do_QueryInterface(static_cast<nsIChannel*>(httpChannel.get()));
518 if (cacheChannel) {
519 cacheChannel->SetCacheKey(aCacheKey);
520 for (const auto& data : aPreferredAlternativeTypes) {
521 cacheChannel->PreferAlternativeDataType(data.type(), data.contentType(),
522 data.deliverAltData());
523 }
524
525 cacheChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
526 cacheChannel->SetPreferCacheLoadOverBypass(aPreferCacheLoadOverBypass);
527
528 // This is to mark that the results are going to the content process.
529 if (httpChannelImpl) {
530 httpChannelImpl->SetAltDataForChild(true);
531 }
532 }
533
534 httpChannel->SetContentType(aContentTypeHint);
535
536 if (priority != nsISupportsPriority::PRIORITY_NORMAL) {
537 httpChannel->SetPriority(priority);
538 }
539 if (classOfService) {
540 httpChannel->SetClassFlags(classOfService);
541 }
542 httpChannel->SetRedirectionLimit(redirectionLimit);
543 httpChannel->SetAllowSTS(allowSTS);
544 httpChannel->SetThirdPartyFlags(thirdPartyFlags);
545 httpChannel->SetAllowSpdy(allowSpdy);
546 httpChannel->SetAllowHttp3(allowHttp3);
547 httpChannel->SetAllowAltSvc(allowAltSvc);
548 httpChannel->SetBeConservative(beConservative);
549 httpChannel->SetTlsFlags(tlsFlags);
550 httpChannel->SetInitialRwin(aInitialRwin);
551 httpChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
552
553 httpChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
554 httpChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
555 httpChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
556 httpChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
557 httpChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
558 httpChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
559
560 httpChannel->SetNavigationStartTimeStamp(aNavigationStartTimeStamp);
561 httpChannel->SetRequestContextID(aRequestContextID);
562
563 // Store the strong reference of channel and parent listener object until
564 // all the initialization procedure is complete without failure, to remove
565 // cycle reference in fail case and to avoid memory leakage.
566 mChannel = std::move(httpChannel);
567 mParentListener = std::move(parentListener);
568 mChannel->SetNotificationCallbacks(mParentListener);
569
570 MOZ_ASSERT(!mBgParent);
571 MOZ_ASSERT(mPromise.IsEmpty());
572 // Wait for HttpBackgrounChannel to continue the async open procedure.
573 ++mAsyncOpenBarrier;
574 RefPtr<HttpChannelParent> self = this;
575 WaitForBgParent()
576 ->Then(
577 GetMainThreadSerialEventTarget(), __func__,
578 [self]() {
579 self->mRequest.Complete();
580 self->TryInvokeAsyncOpen(NS_OK);
581 },
582 [self](nsresult aStatus) {
583 self->mRequest.Complete();
584 self->TryInvokeAsyncOpen(aStatus);
585 })
586 ->Track(mRequest);
587
588 // The stream, received from the child process, must be cloneable and seekable
589 // in order to allow devtools to inspect its content.
590 nsCOMPtr<nsIRunnable> r =
591 NS_NewRunnableFunction("HttpChannelParent::EnsureUploadStreamIsCloneable",
592 [self]() { self->TryInvokeAsyncOpen(NS_OK); });
593 ++mAsyncOpenBarrier;
594 mChannel->EnsureUploadStreamIsCloneable(r);
595 return true;
596 }
597
WaitForBgParent()598 RefPtr<GenericNonExclusivePromise> HttpChannelParent::WaitForBgParent() {
599 LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this));
600 MOZ_ASSERT(!mBgParent);
601
602 if (!mChannel) {
603 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
604 __func__);
605 }
606
607 nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
608 BackgroundChannelRegistrar::GetOrCreate();
609 MOZ_ASSERT(registrar);
610 registrar->LinkHttpChannel(mChannel->ChannelId(), this);
611
612 if (mBgParent) {
613 return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
614 }
615
616 return mPromise.Ensure(__func__);
617 }
618
ConnectChannel(const uint32_t & registrarId)619 bool HttpChannelParent::ConnectChannel(const uint32_t& registrarId) {
620 nsresult rv;
621
622 LOG(
623 ("HttpChannelParent::ConnectChannel: Looking for a registered channel "
624 "[this=%p, id=%" PRIu32 "]\n",
625 this, registrarId));
626 nsCOMPtr<nsIChannel> channel;
627 rv = NS_LinkRedirectChannels(registrarId, this, getter_AddRefs(channel));
628 if (NS_FAILED(rv)) {
629 NS_ERROR("Could not find the http channel to connect its IPC parent");
630 // This makes the channel delete itself safely. It's the only thing
631 // we can do now, since this parent channel cannot be used and there is
632 // no other way to tell the child side there were something wrong.
633 Delete();
634 return true;
635 }
636
637 LOG((" found channel %p, rv=%08" PRIx32, channel.get(),
638 static_cast<uint32_t>(rv)));
639 mChannel = do_QueryObject(channel);
640 if (!mChannel) {
641 LOG((" but it's not HttpBaseChannel"));
642 Delete();
643 return true;
644 }
645
646 LOG((" and it is HttpBaseChannel %p", mChannel.get()));
647
648 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
649 if (httpChannelImpl) {
650 httpChannelImpl->SetWarningReporter(this);
651 }
652
653 if (mPBOverride != kPBOverride_Unset) {
654 // redirected-to channel may not support PB
655 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
656 if (pbChannel) {
657 pbChannel->SetPrivate(mPBOverride == kPBOverride_Private);
658 }
659 }
660
661 MOZ_ASSERT(!mBgParent);
662 MOZ_ASSERT(mPromise.IsEmpty());
663 // Waiting for background channel
664 RefPtr<HttpChannelParent> self = this;
665 WaitForBgParent()
666 ->Then(
667 GetMainThreadSerialEventTarget(), __func__,
668 [self]() { self->mRequest.Complete(); },
669 [self](const nsresult& aResult) {
670 NS_ERROR("failed to establish the background channel");
671 self->mRequest.Complete();
672 })
673 ->Track(mRequest);
674 return true;
675 }
676
RecvSetPriority(const int16_t & priority)677 mozilla::ipc::IPCResult HttpChannelParent::RecvSetPriority(
678 const int16_t& priority) {
679 LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%d]\n", this,
680 priority));
681 AUTO_PROFILER_LABEL("HttpChannelParent::RecvSetPriority", NETWORK);
682
683 if (mChannel) {
684 mChannel->SetPriority(priority);
685 }
686
687 nsCOMPtr<nsISupportsPriority> priorityRedirectChannel =
688 do_QueryInterface(mRedirectChannel);
689 if (priorityRedirectChannel) priorityRedirectChannel->SetPriority(priority);
690
691 return IPC_OK();
692 }
693
RecvSetClassOfService(const uint32_t & cos)694 mozilla::ipc::IPCResult HttpChannelParent::RecvSetClassOfService(
695 const uint32_t& cos) {
696 if (mChannel) {
697 mChannel->SetClassFlags(cos);
698 }
699 return IPC_OK();
700 }
701
RecvSuspend()702 mozilla::ipc::IPCResult HttpChannelParent::RecvSuspend() {
703 LOG(("HttpChannelParent::RecvSuspend [this=%p]\n", this));
704
705 if (mChannel) {
706 mChannel->Suspend();
707 }
708 return IPC_OK();
709 }
710
RecvResume()711 mozilla::ipc::IPCResult HttpChannelParent::RecvResume() {
712 LOG(("HttpChannelParent::RecvResume [this=%p]\n", this));
713
714 if (mChannel) {
715 mChannel->Resume();
716 }
717 return IPC_OK();
718 }
719
RecvCancel(const nsresult & status,const uint32_t & requestBlockingReason)720 mozilla::ipc::IPCResult HttpChannelParent::RecvCancel(
721 const nsresult& status, const uint32_t& requestBlockingReason) {
722 LOG(("HttpChannelParent::RecvCancel [this=%p]\n", this));
723
724 // May receive cancel before channel has been constructed!
725 if (mChannel) {
726 mChannel->Cancel(status);
727
728 if (MOZ_UNLIKELY(requestBlockingReason !=
729 nsILoadInfo::BLOCKING_REASON_NONE)) {
730 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
731 loadInfo->SetRequestBlockingReason(requestBlockingReason);
732 }
733
734 // Once we receive |Cancel|, child will stop sending RecvBytesRead. Force
735 // the channel resumed if needed.
736 if (mSuspendedForFlowControl) {
737 LOG((" resume the channel due to e10s backpressure relief by cancel"));
738 Unused << mChannel->Resume();
739 mSuspendedForFlowControl = false;
740 }
741 } else if (!mIPCClosed) {
742 // Make sure that the child correctly delivers all stream listener
743 // notifications.
744 Unused << SendFailedAsyncOpen(status);
745 }
746
747 // We won't need flow control anymore. Toggle the flag to avoid |Suspend|
748 // since OnDataAvailable could be off-main-thread.
749 mCacheNeedFlowControlInitialized = true;
750 mNeedFlowControl = false;
751 return IPC_OK();
752 }
753
RecvRedirect2Verify(const nsresult & aResult,const RequestHeaderTuples & changedHeaders,const uint32_t & aSourceRequestBlockingReason,const Maybe<ChildLoadInfoForwarderArgs> & aTargetLoadInfoForwarder,const uint32_t & loadFlags,nsIReferrerInfo * aReferrerInfo,const Maybe<URIParams> & aAPIRedirectURI,const Maybe<CorsPreflightArgs> & aCorsPreflightArgs)754 mozilla::ipc::IPCResult HttpChannelParent::RecvRedirect2Verify(
755 const nsresult& aResult, const RequestHeaderTuples& changedHeaders,
756 const uint32_t& aSourceRequestBlockingReason,
757 const Maybe<ChildLoadInfoForwarderArgs>& aTargetLoadInfoForwarder,
758 const uint32_t& loadFlags, nsIReferrerInfo* aReferrerInfo,
759 const Maybe<URIParams>& aAPIRedirectURI,
760 const Maybe<CorsPreflightArgs>& aCorsPreflightArgs) {
761 LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
762 this, static_cast<uint32_t>(aResult)));
763
764 // Result from the child. If something fails here, we might overwrite a
765 // success with a further failure.
766 nsresult result = aResult;
767
768 // Local results.
769 nsresult rv;
770
771 if (NS_SUCCEEDED(result)) {
772 nsCOMPtr<nsIHttpChannel> newHttpChannel =
773 do_QueryInterface(mRedirectChannel);
774
775 if (newHttpChannel) {
776 nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI);
777
778 if (apiRedirectUri) {
779 rv = newHttpChannel->RedirectTo(apiRedirectUri);
780 MOZ_ASSERT(NS_SUCCEEDED(rv));
781 }
782
783 for (uint32_t i = 0; i < changedHeaders.Length(); i++) {
784 if (changedHeaders[i].mEmpty) {
785 rv = newHttpChannel->SetEmptyRequestHeader(changedHeaders[i].mHeader);
786 } else {
787 rv = newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
788 changedHeaders[i].mValue,
789 changedHeaders[i].mMerge);
790 }
791 MOZ_ASSERT(NS_SUCCEEDED(rv));
792 }
793
794 // A successfully redirected channel must have the LOAD_REPLACE flag.
795 MOZ_ASSERT(loadFlags & nsIChannel::LOAD_REPLACE);
796 if (loadFlags & nsIChannel::LOAD_REPLACE) {
797 newHttpChannel->SetLoadFlags(loadFlags);
798 }
799
800 if (aCorsPreflightArgs.isSome()) {
801 nsCOMPtr<nsIHttpChannelInternal> newInternalChannel =
802 do_QueryInterface(newHttpChannel);
803 MOZ_RELEASE_ASSERT(newInternalChannel);
804 const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
805 newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
806 false);
807 }
808
809 if (aReferrerInfo) {
810 RefPtr<HttpBaseChannel> baseChannel = do_QueryObject(newHttpChannel);
811 MOZ_ASSERT(baseChannel);
812 if (baseChannel) {
813 // Referrer header is computed in child no need to recompute here
814 rv = baseChannel->SetReferrerInfoInternal(aReferrerInfo, false, false,
815 true);
816 MOZ_ASSERT(NS_SUCCEEDED(rv));
817 }
818 }
819
820 if (aTargetLoadInfoForwarder.isSome()) {
821 nsCOMPtr<nsILoadInfo> newLoadInfo = newHttpChannel->LoadInfo();
822 rv = MergeChildLoadInfoForwarder(aTargetLoadInfoForwarder.ref(),
823 newLoadInfo);
824 if (NS_FAILED(rv) && NS_SUCCEEDED(result)) {
825 result = rv;
826 }
827 }
828 }
829 }
830
831 // If the redirect is vetoed, reason is set on the source (current) channel's
832 // load info, so we must carry iver the change.
833 // The channel may have already been cleaned up, so there is nothing we can
834 // do.
835 if (MOZ_UNLIKELY(aSourceRequestBlockingReason !=
836 nsILoadInfo::BLOCKING_REASON_NONE) &&
837 mChannel) {
838 nsCOMPtr<nsILoadInfo> sourceLoadInfo = mChannel->LoadInfo();
839 sourceLoadInfo->SetRequestBlockingReason(aSourceRequestBlockingReason);
840 }
841
842 // Continue the verification procedure if child has veto the redirection.
843 if (NS_FAILED(result)) {
844 ContinueRedirect2Verify(result);
845 return IPC_OK();
846 }
847
848 // Wait for background channel ready on target channel
849 nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
850 RedirectChannelRegistrar::GetOrCreate();
851 MOZ_ASSERT(redirectReg);
852
853 nsCOMPtr<nsIParentChannel> redirectParentChannel;
854 rv = redirectReg->GetParentChannel(mRedirectChannelId,
855 getter_AddRefs(redirectParentChannel));
856 MOZ_ASSERT(redirectParentChannel);
857 if (!redirectParentChannel) {
858 ContinueRedirect2Verify(rv);
859 return IPC_OK();
860 }
861
862 nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
863 do_QueryInterface(redirectParentChannel);
864 if (!redirectedParent) {
865 // Continue verification procedure if redirecting to non-Http protocol
866 ContinueRedirect2Verify(result);
867 return IPC_OK();
868 }
869
870 // Ask redirected channel if verification can proceed.
871 // ContinueRedirect2Verify will be invoked when redirected channel is ready.
872 redirectedParent->ContinueVerification(this);
873
874 return IPC_OK();
875 }
876
877 // from nsIParentRedirectingChannel
878 NS_IMETHODIMP
ContinueVerification(nsIAsyncVerifyRedirectReadyCallback * aCallback)879 HttpChannelParent::ContinueVerification(
880 nsIAsyncVerifyRedirectReadyCallback* aCallback) {
881 LOG(("HttpChannelParent::ContinueVerification [this=%p callback=%p]\n", this,
882 aCallback));
883
884 MOZ_ASSERT(NS_IsMainThread());
885 MOZ_ASSERT(aCallback);
886
887 // Continue the verification procedure if background channel is ready.
888 if (mBgParent) {
889 aCallback->ReadyToVerify(NS_OK);
890 return NS_OK;
891 }
892
893 // ConnectChannel must be received before Redirect2Verify.
894 MOZ_ASSERT(!mPromise.IsEmpty());
895
896 // Otherwise, wait for the background channel.
897 nsCOMPtr<nsIAsyncVerifyRedirectReadyCallback> callback = aCallback;
898 WaitForBgParent()->Then(
899 GetMainThreadSerialEventTarget(), __func__,
900 [callback]() { callback->ReadyToVerify(NS_OK); },
901 [callback](const nsresult& aResult) {
902 NS_ERROR("failed to establish the background channel");
903 callback->ReadyToVerify(aResult);
904 });
905 return NS_OK;
906 }
907
ContinueRedirect2Verify(const nsresult & aResult)908 void HttpChannelParent::ContinueRedirect2Verify(const nsresult& aResult) {
909 LOG(("HttpChannelParent::ContinueRedirect2Verify [this=%p result=%" PRIx32
910 "]\n",
911 this, static_cast<uint32_t>(aResult)));
912
913 if (!mRedirectCallback) {
914 // This should according the logic never happen, log the situation.
915 if (mReceivedRedirect2Verify) {
916 LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
917 }
918 if (mSentRedirect1BeginFailed) {
919 LOG(("RecvRedirect2Verify[%p]: Send to child failed", this));
920 }
921 if ((mRedirectChannelId > 0) && NS_FAILED(aResult)) {
922 LOG(("RecvRedirect2Verify[%p]: Redirect failed", this));
923 }
924 if ((mRedirectChannelId > 0) && NS_SUCCEEDED(aResult)) {
925 LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this));
926 }
927 if (!mRedirectChannel) {
928 LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this));
929 }
930
931 NS_ERROR(
932 "Unexpcted call to HttpChannelParent::RecvRedirect2Verify, "
933 "mRedirectCallback null");
934 }
935
936 mReceivedRedirect2Verify = true;
937
938 if (mRedirectCallback) {
939 LOG(
940 ("HttpChannelParent::ContinueRedirect2Verify call "
941 "OnRedirectVerifyCallback"
942 " [this=%p result=%" PRIx32 ", mRedirectCallback=%p]\n",
943 this, static_cast<uint32_t>(aResult), mRedirectCallback.get()));
944 mRedirectCallback->OnRedirectVerifyCallback(aResult);
945 mRedirectCallback = nullptr;
946 }
947 }
948
RecvDocumentChannelCleanup(const bool & clearCacheEntry)949 mozilla::ipc::IPCResult HttpChannelParent::RecvDocumentChannelCleanup(
950 const bool& clearCacheEntry) {
951 CleanupBackgroundChannel(); // Background channel can be closed.
952 mChannel = nullptr; // Reclaim some memory sooner.
953 if (clearCacheEntry) {
954 mCacheEntry = nullptr; // Else we'll block other channels reading same URI
955 }
956 return IPC_OK();
957 }
958
RecvRemoveCorsPreflightCacheEntry(const URIParams & uri,const mozilla::ipc::PrincipalInfo & requestingPrincipal,const OriginAttributes & originAttributes)959 mozilla::ipc::IPCResult HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(
960 const URIParams& uri,
961 const mozilla::ipc::PrincipalInfo& requestingPrincipal,
962 const OriginAttributes& originAttributes) {
963 nsCOMPtr<nsIURI> deserializedURI = DeserializeURI(uri);
964 if (!deserializedURI) {
965 return IPC_FAIL_NO_REASON(this);
966 }
967 auto principalOrErr = PrincipalInfoToPrincipal(requestingPrincipal);
968 if (NS_WARN_IF(principalOrErr.isErr())) {
969 return IPC_FAIL_NO_REASON(this);
970 }
971 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
972 nsCORSListenerProxy::RemoveFromCorsPreflightCache(deserializedURI, principal,
973 originAttributes);
974 return IPC_OK();
975 }
976
977 //-----------------------------------------------------------------------------
978 // HttpChannelParent::nsIRequestObserver
979 //-----------------------------------------------------------------------------
980
GetTimingAttributes(HttpBaseChannel * aChannel)981 static ResourceTimingStructArgs GetTimingAttributes(HttpBaseChannel* aChannel) {
982 ResourceTimingStructArgs args;
983 TimeStamp timeStamp;
984 aChannel->GetDomainLookupStart(&timeStamp);
985 args.domainLookupStart() = timeStamp;
986 aChannel->GetDomainLookupEnd(&timeStamp);
987 args.domainLookupEnd() = timeStamp;
988 aChannel->GetConnectStart(&timeStamp);
989 args.connectStart() = timeStamp;
990 aChannel->GetTcpConnectEnd(&timeStamp);
991 args.tcpConnectEnd() = timeStamp;
992 aChannel->GetSecureConnectionStart(&timeStamp);
993 args.secureConnectionStart() = timeStamp;
994 aChannel->GetConnectEnd(&timeStamp);
995 args.connectEnd() = timeStamp;
996 aChannel->GetRequestStart(&timeStamp);
997 args.requestStart() = timeStamp;
998 aChannel->GetResponseStart(&timeStamp);
999 args.responseStart() = timeStamp;
1000 aChannel->GetResponseEnd(&timeStamp);
1001 args.responseEnd() = timeStamp;
1002 aChannel->GetAsyncOpen(&timeStamp);
1003 args.fetchStart() = timeStamp;
1004 aChannel->GetRedirectStart(&timeStamp);
1005 args.redirectStart() = timeStamp;
1006 aChannel->GetRedirectEnd(&timeStamp);
1007 args.redirectEnd() = timeStamp;
1008
1009 uint64_t size = 0;
1010 aChannel->GetTransferSize(&size);
1011 args.transferSize() = size;
1012
1013 aChannel->GetEncodedBodySize(&size);
1014 args.encodedBodySize() = size;
1015 // decodedBodySize can be computed in the child process so it doesn't need
1016 // to be passed down.
1017
1018 nsCString protocolVersion;
1019 aChannel->GetProtocolVersion(protocolVersion);
1020 args.protocolVersion() = protocolVersion;
1021
1022 aChannel->GetCacheReadStart(&timeStamp);
1023 args.cacheReadStart() = timeStamp;
1024
1025 aChannel->GetCacheReadEnd(&timeStamp);
1026 args.cacheReadEnd() = timeStamp;
1027 return args;
1028 }
1029
1030 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)1031 HttpChannelParent::OnStartRequest(nsIRequest* aRequest) {
1032 nsresult rv;
1033
1034 LOG(("HttpChannelParent::OnStartRequest [this=%p, aRequest=%p]\n", this,
1035 aRequest));
1036 MOZ_ASSERT(NS_IsMainThread());
1037
1038 Maybe<uint32_t> multiPartID;
1039 bool isFirstPartOfMultiPart = false;
1040 bool isLastPartOfMultiPart = false;
1041 DebugOnly<bool> isMultiPart = false;
1042
1043 RefPtr<HttpBaseChannel> chan = do_QueryObject(aRequest);
1044 if (!chan) {
1045 if (nsCOMPtr<nsIMultiPartChannel> multiPartChannel =
1046 do_QueryInterface(aRequest)) {
1047 isMultiPart = true;
1048 nsCOMPtr<nsIChannel> baseChannel;
1049 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
1050 chan = do_QueryObject(baseChannel);
1051
1052 uint32_t partID = 0;
1053 multiPartChannel->GetPartID(&partID);
1054 multiPartID = Some(partID);
1055 multiPartChannel->GetIsFirstPart(&isFirstPartOfMultiPart);
1056 multiPartChannel->GetIsLastPart(&isLastPartOfMultiPart);
1057 } else if (nsCOMPtr<nsIViewSourceChannel> viewSourceChannel =
1058 do_QueryInterface(aRequest)) {
1059 chan = do_QueryObject(viewSourceChannel->GetInnerChannel());
1060 }
1061 }
1062 MOZ_ASSERT(multiPartID || !isMultiPart, "Changed multi-part state?");
1063
1064 if (!chan) {
1065 LOG((" aRequest is not HttpBaseChannel"));
1066 NS_ERROR(
1067 "Expecting only HttpBaseChannel as aRequest in "
1068 "HttpChannelParent::OnStartRequest");
1069 return NS_ERROR_UNEXPECTED;
1070 }
1071
1072 mAfterOnStartRequestBegun = true;
1073
1074 // Todo: re-enable when bug 1589749 is fixed.
1075 /*MOZ_ASSERT(mChannel == chan,
1076 "HttpChannelParent getting OnStartRequest from a different "
1077 "HttpBaseChannel instance");*/
1078
1079 HttpChannelOnStartRequestArgs args;
1080
1081 // Send down any permissions/cookies which are relevant to this URL if we are
1082 // performing a document load. We can't do that if mIPCClosed is set.
1083 if (!mIPCClosed) {
1084 PContentParent* pcp = Manager()->Manager();
1085 MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
1086 DebugOnly<nsresult> rv =
1087 static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpDocumentForChild(
1088 chan, &args.shouldWaitForOnStartRequestSent());
1089 MOZ_ASSERT(NS_SUCCEEDED(rv));
1090 }
1091
1092 args.multiPartID() = multiPartID;
1093 args.isFirstPartOfMultiPart() = isFirstPartOfMultiPart;
1094 args.isLastPartOfMultiPart() = isLastPartOfMultiPart;
1095
1096 args.cacheExpirationTime() = nsICacheEntry::NO_EXPIRATION_TIME;
1097
1098 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(chan);
1099
1100 if (httpChannelImpl) {
1101 httpChannelImpl->IsFromCache(&args.isFromCache());
1102 httpChannelImpl->IsRacing(&args.isRacing());
1103 httpChannelImpl->GetCacheEntryId(&args.cacheEntryId());
1104 httpChannelImpl->GetCacheTokenFetchCount(&args.cacheFetchCount());
1105 httpChannelImpl->GetCacheTokenExpirationTime(&args.cacheExpirationTime());
1106
1107 mDataSentToChildProcess = httpChannelImpl->DataSentToChildProcess();
1108
1109 // If RCWN is enabled and cache wins, we can't use the ODA from socket
1110 // process.
1111 if (args.isRacing()) {
1112 mDataSentToChildProcess =
1113 httpChannelImpl->DataSentToChildProcess() && !args.isFromCache();
1114 }
1115 args.dataFromSocketProcess() = mDataSentToChildProcess;
1116 }
1117
1118 // Propagate whether or not conversion should occur from the parent-side
1119 // channel to the child-side channel. Then disable the parent-side
1120 // conversion so that it only occurs in the child.
1121 Unused << chan->GetApplyConversion(&args.applyConversion());
1122 chan->SetApplyConversion(false);
1123
1124 // If we've already applied the conversion (as can happen if we installed
1125 // a multipart converted), then don't apply it again on the child.
1126 if (chan->HasAppliedConversion()) {
1127 args.applyConversion() = false;
1128 }
1129
1130 chan->GetStatus(&args.channelStatus());
1131
1132 // Keep the cache entry for future use when opening alternative streams.
1133 // It could be already released by nsHttpChannel at that time.
1134 nsCOMPtr<nsISupports> cacheEntry;
1135
1136 if (httpChannelImpl) {
1137 httpChannelImpl->GetCacheToken(getter_AddRefs(cacheEntry));
1138 mCacheEntry = do_QueryInterface(cacheEntry);
1139 args.cacheEntryAvailable() = static_cast<bool>(mCacheEntry);
1140
1141 httpChannelImpl->GetCacheKey(&args.cacheKey());
1142 httpChannelImpl->GetAlternativeDataType(args.altDataType());
1143 }
1144
1145 args.altDataLength() = chan->GetAltDataLength();
1146 args.deliveringAltData() = chan->IsDeliveringAltData();
1147
1148 UpdateAndSerializeSecurityInfo(args.securityInfoSerialization());
1149
1150 chan->GetRedirectCount(&args.redirectCount());
1151 chan->GetHasHTTPSRR(&args.hasHTTPSRR());
1152
1153 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
1154 mozilla::ipc::LoadInfoToParentLoadInfoForwarder(loadInfo,
1155 &args.loadInfoForwarder());
1156
1157 nsHttpResponseHead* responseHead = chan->GetResponseHead();
1158 bool useResponseHead = !!responseHead;
1159 nsHttpResponseHead cleanedUpResponseHead;
1160
1161 if (responseHead &&
1162 (responseHead->HasHeader(nsHttp::Set_Cookie) || multiPartID)) {
1163 cleanedUpResponseHead = *responseHead;
1164 cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie);
1165 if (multiPartID) {
1166 nsCOMPtr<nsIChannel> multiPartChannel = do_QueryInterface(aRequest);
1167 // For the multipart channel, use the parsed subtype instead. Note that
1168 // `chan` is the underlying base channel of the multipart channel in this
1169 // case, which is different from `multiPartChannel`.
1170 MOZ_ASSERT(multiPartChannel);
1171 nsAutoCString contentType;
1172 multiPartChannel->GetContentType(contentType);
1173 cleanedUpResponseHead.SetContentType(contentType);
1174 }
1175 responseHead = &cleanedUpResponseHead;
1176 }
1177
1178 if (!responseHead) {
1179 responseHead = &cleanedUpResponseHead;
1180 }
1181
1182 chan->GetIsResolvedByTRR(&args.isResolvedByTRR());
1183 chan->GetAllRedirectsSameOrigin(&args.allRedirectsSameOrigin());
1184 chan->GetCrossOriginOpenerPolicy(&args.openerPolicy());
1185 args.selfAddr() = chan->GetSelfAddr();
1186 args.peerAddr() = chan->GetPeerAddr();
1187 args.timing() = GetTimingAttributes(mChannel);
1188 if (mOverrideReferrerInfo) {
1189 args.overrideReferrerInfo() = ToRefPtr(std::move(mOverrideReferrerInfo));
1190 }
1191 if (!mCookie.IsEmpty()) {
1192 args.cookie() = std::move(mCookie);
1193 }
1194
1195 nsHttpRequestHead* requestHead = chan->GetRequestHead();
1196 // !!! We need to lock headers and please don't forget to unlock them !!!
1197 requestHead->Enter();
1198
1199 nsHttpHeaderArray cleanedUpRequestHeaders;
1200 bool cleanedUpRequest = false;
1201 if (requestHead->HasHeader(nsHttp::Cookie)) {
1202 cleanedUpRequestHeaders = requestHead->Headers();
1203 cleanedUpRequestHeaders.ClearHeader(nsHttp::Cookie);
1204 cleanedUpRequest = true;
1205 }
1206
1207 rv = NS_OK;
1208
1209 nsCOMPtr<nsICacheEntry> altDataSource;
1210 nsCOMPtr<nsICacheInfoChannel> cacheChannel =
1211 do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
1212 if (cacheChannel) {
1213 for (const auto& pref : cacheChannel->PreferredAlternativeDataTypes()) {
1214 if (pref.type() == args.altDataType() &&
1215 pref.deliverAltData() ==
1216 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::
1217 SERIALIZE) {
1218 altDataSource = mCacheEntry;
1219 break;
1220 }
1221 }
1222 }
1223
1224 if (mIPCClosed ||
1225 !mBgParent->OnStartRequest(
1226 *responseHead, useResponseHead,
1227 cleanedUpRequest ? cleanedUpRequestHeaders : requestHead->Headers(),
1228 args, altDataSource)) {
1229 rv = NS_ERROR_UNEXPECTED;
1230 }
1231 requestHead->Exit();
1232
1233 // Need to wait for the cookies/permissions to content process, which is sent
1234 // via PContent in AboutToLoadHttpFtpDocumentForChild. For multipart channel,
1235 // send only one time since the cookies/permissions are the same.
1236 if (NS_SUCCEEDED(rv) && args.shouldWaitForOnStartRequestSent() &&
1237 multiPartID.valueOr(0) == 0) {
1238 LOG(("HttpChannelParent::SendOnStartRequestSent\n"));
1239 Unused << SendOnStartRequestSent();
1240 }
1241
1242 return rv;
1243 }
1244
1245 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)1246 HttpChannelParent::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1247 LOG(("HttpChannelParent::OnStopRequest: [this=%p aRequest=%p status=%" PRIx32
1248 "]\n",
1249 this, aRequest, static_cast<uint32_t>(aStatusCode)));
1250 MOZ_ASSERT(NS_IsMainThread());
1251
1252 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1253 if (httpChannelImpl) {
1254 httpChannelImpl->SetWarningReporter(nullptr);
1255 }
1256
1257 nsHttpHeaderArray* responseTrailer = mChannel->GetResponseTrailers();
1258
1259 nsTArray<ConsoleReportCollected> consoleReports;
1260
1261 RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(mChannel);
1262 if (httpChannel) {
1263 httpChannel->StealConsoleReports(consoleReports);
1264 }
1265
1266 // Either IPC channel is closed or background channel
1267 // is ready to send OnStopRequest.
1268 MOZ_ASSERT(mIPCClosed || mBgParent);
1269
1270 if (mDataSentToChildProcess) {
1271 if (mIPCClosed || !mBgParent ||
1272 !mBgParent->OnConsoleReport(consoleReports)) {
1273 return NS_ERROR_UNEXPECTED;
1274 }
1275 return NS_OK;
1276 }
1277
1278 // If we're handling a multi-part stream, then send this directly
1279 // over PHttpChannel to make synchronization easier.
1280 if (mIPCClosed || !mBgParent ||
1281 !mBgParent->OnStopRequest(
1282 aStatusCode, GetTimingAttributes(mChannel),
1283 responseTrailer ? *responseTrailer : nsHttpHeaderArray(),
1284 consoleReports)) {
1285 return NS_ERROR_UNEXPECTED;
1286 }
1287
1288 if (NeedFlowControl()) {
1289 bool isLocal = false;
1290 NetAddr peerAddr = mChannel->GetPeerAddr();
1291
1292 #if defined(XP_UNIX)
1293 // Unix-domain sockets are always local.
1294 isLocal = (peerAddr.raw.family == PR_AF_LOCAL);
1295 #endif
1296
1297 isLocal = isLocal || peerAddr.IsLoopbackAddr();
1298
1299 if (!isLocal) {
1300 if (!mHasSuspendedByBackPressure) {
1301 AccumulateCategorical(
1302 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1303 NotSuspended);
1304 } else {
1305 AccumulateCategorical(
1306 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1307 Suspended);
1308
1309 // Only analyze non-local suspended cases, which we are interested in.
1310 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
1311 Telemetry::Accumulate(
1312 Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_CP_TYPE,
1313 loadInfo->InternalContentPolicyType());
1314 }
1315 } else {
1316 if (!mHasSuspendedByBackPressure) {
1317 AccumulateCategorical(
1318 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1319 NotSuspendedLocal);
1320 } else {
1321 AccumulateCategorical(
1322 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1323 SuspendedLocal);
1324 }
1325 }
1326 }
1327 return NS_OK;
1328 }
1329
1330 //-----------------------------------------------------------------------------
1331 // HttpChannelParent::nsIMultiPartChannelListener
1332 //-----------------------------------------------------------------------------
1333
1334 NS_IMETHODIMP
OnAfterLastPart(nsresult aStatus)1335 HttpChannelParent::OnAfterLastPart(nsresult aStatus) {
1336 LOG(("HttpChannelParent::OnAfterLastPart [this=%p]\n", this));
1337 MOZ_ASSERT(NS_IsMainThread());
1338
1339 // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1340 if (mIPCClosed) {
1341 return NS_OK;
1342 }
1343
1344 // If IPC channel is open, background channel should be ready to send
1345 // OnAfterLastPart.
1346 MOZ_ASSERT(mBgParent);
1347
1348 if (!mBgParent || !mBgParent->OnAfterLastPart(aStatus)) {
1349 return NS_ERROR_UNEXPECTED;
1350 }
1351
1352 return NS_OK;
1353 }
1354
1355 //-----------------------------------------------------------------------------
1356 // HttpChannelParent::nsIStreamListener
1357 //-----------------------------------------------------------------------------
1358
1359 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)1360 HttpChannelParent::OnDataAvailable(nsIRequest* aRequest,
1361 nsIInputStream* aInputStream,
1362 uint64_t aOffset, uint32_t aCount) {
1363 LOG(("HttpChannelParent::OnDataAvailable [this=%p aRequest=%p offset=%" PRIu64
1364 " count=%" PRIu32 "]\n",
1365 this, aRequest, aOffset, aCount));
1366 MOZ_ASSERT(NS_IsMainThread());
1367
1368 if (mDataSentToChildProcess) {
1369 uint32_t n;
1370 return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &n);
1371 }
1372
1373 nsresult channelStatus = NS_OK;
1374 mChannel->GetStatus(&channelStatus);
1375
1376 nsresult transportStatus = NS_NET_STATUS_RECEIVING_FROM;
1377 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1378 if (httpChannelImpl) {
1379 if (httpChannelImpl->IsReadingFromCache()) {
1380 transportStatus = NS_NET_STATUS_READING;
1381 }
1382 }
1383
1384 nsCString data;
1385 nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
1386 if (NS_FAILED(rv)) {
1387 return rv;
1388 }
1389
1390 // Either IPC channel is closed or background channel
1391 // is ready to send OnTransportAndData.
1392 MOZ_ASSERT(mIPCClosed || mBgParent);
1393
1394 if (mIPCClosed || !mBgParent ||
1395 !mBgParent->OnTransportAndData(channelStatus, transportStatus, aOffset,
1396 aCount, data)) {
1397 return NS_ERROR_UNEXPECTED;
1398 }
1399
1400 int32_t count = static_cast<int32_t>(aCount);
1401
1402 if (NeedFlowControl()) {
1403 // We're going to run out of sending window size
1404 if (mSendWindowSize > 0 && mSendWindowSize <= count) {
1405 MOZ_ASSERT(!mSuspendedForFlowControl);
1406 LOG((" suspend the channel due to e10s backpressure"));
1407 Unused << mChannel->Suspend();
1408 mSuspendedForFlowControl = true;
1409 mHasSuspendedByBackPressure = true;
1410 } else if (!mResumedTimestamp.IsNull()) {
1411 // Calculate the delay when the first packet arrived after resume
1412 Telemetry::AccumulateTimeDelta(
1413 Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_DELAY_TIME_MS,
1414 mResumedTimestamp);
1415 mResumedTimestamp = TimeStamp();
1416 }
1417 mSendWindowSize -= count;
1418 }
1419
1420 return NS_OK;
1421 }
1422
NeedFlowControl()1423 bool HttpChannelParent::NeedFlowControl() {
1424 if (mCacheNeedFlowControlInitialized) {
1425 return mNeedFlowControl;
1426 }
1427
1428 int64_t contentLength = -1;
1429
1430 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1431
1432 // By design, we won't trigger the flow control if
1433 // a. pref-out
1434 // b. the resource is from cache or partial cache
1435 // c. the resource is small
1436 // d. data will be sent from socket process to child process directly
1437 // Note that we served the cached resource first for partical cache, which is
1438 // ignored here since we only take the first ODA into consideration.
1439 if (gHttpHandler->SendWindowSize() == 0 || !httpChannelImpl ||
1440 httpChannelImpl->IsReadingFromCache() ||
1441 NS_FAILED(httpChannelImpl->GetContentLength(&contentLength)) ||
1442 contentLength < gHttpHandler->SendWindowSize() ||
1443 mDataSentToChildProcess) {
1444 mNeedFlowControl = false;
1445 }
1446 mCacheNeedFlowControlInitialized = true;
1447 return mNeedFlowControl;
1448 }
1449
RecvBytesRead(const int32_t & aCount)1450 mozilla::ipc::IPCResult HttpChannelParent::RecvBytesRead(
1451 const int32_t& aCount) {
1452 if (!NeedFlowControl()) {
1453 return IPC_OK();
1454 }
1455
1456 LOG(("HttpChannelParent::RecvBytesRead [this=%p count=%" PRId32 "]\n", this,
1457 aCount));
1458
1459 if (mSendWindowSize <= 0 && mSendWindowSize + aCount > 0) {
1460 MOZ_ASSERT(mSuspendedForFlowControl);
1461 LOG((" resume the channel due to e10s backpressure relief"));
1462 Unused << mChannel->Resume();
1463 mSuspendedForFlowControl = false;
1464
1465 mResumedTimestamp = TimeStamp::Now();
1466 }
1467 mSendWindowSize += aCount;
1468 return IPC_OK();
1469 }
1470
RecvOpenOriginalCacheInputStream()1471 mozilla::ipc::IPCResult HttpChannelParent::RecvOpenOriginalCacheInputStream() {
1472 if (mIPCClosed) {
1473 return IPC_OK();
1474 }
1475 AutoIPCStream autoStream;
1476 if (mCacheEntry) {
1477 nsCOMPtr<nsIInputStream> inputStream;
1478 nsresult rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
1479 if (NS_SUCCEEDED(rv)) {
1480 PContentParent* pcp = Manager()->Manager();
1481 Unused << autoStream.Serialize(inputStream,
1482 static_cast<ContentParent*>(pcp));
1483 }
1484 }
1485
1486 Unused << SendOriginalCacheInputStreamAvailable(
1487 autoStream.TakeOptionalValue());
1488 return IPC_OK();
1489 }
1490
1491 //-----------------------------------------------------------------------------
1492 // HttpChannelParent::nsIProgressEventSink
1493 //-----------------------------------------------------------------------------
1494
1495 NS_IMETHODIMP
OnProgress(nsIRequest * aRequest,int64_t aProgress,int64_t aProgressMax)1496 HttpChannelParent::OnProgress(nsIRequest* aRequest, int64_t aProgress,
1497 int64_t aProgressMax) {
1498 LOG(("HttpChannelParent::OnProgress [this=%p progress=%" PRId64 "max=%" PRId64
1499 "]\n",
1500 this, aProgress, aProgressMax));
1501 MOZ_ASSERT(NS_IsMainThread());
1502
1503 // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1504 if (mIPCClosed) {
1505 return NS_OK;
1506 }
1507
1508 // If it indicates this precedes OnDataAvailable, child can derive the value
1509 // in ODA.
1510 if (mIgnoreProgress) {
1511 mIgnoreProgress = false;
1512 return NS_OK;
1513 }
1514
1515 // If IPC channel is open, background channel should be ready to send
1516 // OnProgress.
1517 MOZ_ASSERT(mBgParent);
1518
1519 // Send OnProgress events to the child for data upload progress notifications
1520 // (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has
1521 // LOAD_BACKGROUND set.
1522 if (!mBgParent || !mBgParent->OnProgress(aProgress, aProgressMax)) {
1523 return NS_ERROR_UNEXPECTED;
1524 }
1525
1526 return NS_OK;
1527 }
1528
1529 NS_IMETHODIMP
OnStatus(nsIRequest * aRequest,nsresult aStatus,const char16_t * aStatusArg)1530 HttpChannelParent::OnStatus(nsIRequest* aRequest, nsresult aStatus,
1531 const char16_t* aStatusArg) {
1532 LOG(("HttpChannelParent::OnStatus [this=%p status=%" PRIx32 "]\n", this,
1533 static_cast<uint32_t>(aStatus)));
1534 MOZ_ASSERT(NS_IsMainThread());
1535
1536 // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1537 if (mIPCClosed) {
1538 return NS_OK;
1539 }
1540
1541 // If this precedes OnDataAvailable, transportStatus will be derived in ODA.
1542 if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
1543 aStatus == NS_NET_STATUS_READING) {
1544 // The transport status and progress generated by ODA will be coalesced
1545 // into one IPC message. Therefore, we can ignore the next OnProgress event
1546 // since it is generated by ODA as well.
1547 mIgnoreProgress = true;
1548 return NS_OK;
1549 }
1550
1551 // If IPC channel is open, background channel should be ready to send
1552 // OnStatus.
1553 MOZ_ASSERT(mIPCClosed || mBgParent);
1554
1555 // Otherwise, send to child now
1556 if (!mBgParent || !mBgParent->OnStatus(aStatus)) {
1557 return NS_ERROR_UNEXPECTED;
1558 }
1559
1560 return NS_OK;
1561 }
1562
1563 //-----------------------------------------------------------------------------
1564 // HttpChannelParent::nsIParentChannel
1565 //-----------------------------------------------------------------------------
1566
1567 NS_IMETHODIMP
SetParentListener(ParentChannelListener * aListener)1568 HttpChannelParent::SetParentListener(ParentChannelListener* aListener) {
1569 LOG(("HttpChannelParent::SetParentListener [this=%p aListener=%p]\n", this,
1570 aListener));
1571 MOZ_ASSERT(aListener);
1572 MOZ_ASSERT(!mParentListener,
1573 "SetParentListener should only be called for "
1574 "new HttpChannelParents after a redirect, when "
1575 "mParentListener is null.");
1576 mParentListener = aListener;
1577 return NS_OK;
1578 }
1579
1580 NS_IMETHODIMP
SetClassifierMatchedInfo(const nsACString & aList,const nsACString & aProvider,const nsACString & aFullHash)1581 HttpChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
1582 const nsACString& aProvider,
1583 const nsACString& aFullHash) {
1584 LOG(("HttpChannelParent::SetClassifierMatchedInfo [this=%p]\n", this));
1585 if (!mIPCClosed) {
1586 MOZ_ASSERT(mBgParent);
1587 Unused << mBgParent->OnSetClassifierMatchedInfo(aList, aProvider,
1588 aFullHash);
1589 }
1590 return NS_OK;
1591 }
1592
1593 NS_IMETHODIMP
SetClassifierMatchedTrackingInfo(const nsACString & aLists,const nsACString & aFullHashes)1594 HttpChannelParent::SetClassifierMatchedTrackingInfo(
1595 const nsACString& aLists, const nsACString& aFullHashes) {
1596 LOG(("HttpChannelParent::SetClassifierMatchedTrackingInfo [this=%p]\n",
1597 this));
1598 if (!mIPCClosed) {
1599 MOZ_ASSERT(mBgParent);
1600 Unused << mBgParent->OnSetClassifierMatchedTrackingInfo(aLists,
1601 aFullHashes);
1602 }
1603 return NS_OK;
1604 }
1605
1606 NS_IMETHODIMP
NotifyClassificationFlags(uint32_t aClassificationFlags,bool aIsThirdParty)1607 HttpChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
1608 bool aIsThirdParty) {
1609 LOG(
1610 ("HttpChannelParent::NotifyClassificationFlags "
1611 "classificationFlags=%" PRIu32 ", thirdparty=%d [this=%p]\n",
1612 aClassificationFlags, static_cast<int>(aIsThirdParty), this));
1613 if (!mIPCClosed) {
1614 MOZ_ASSERT(mBgParent);
1615 Unused << mBgParent->OnNotifyClassificationFlags(aClassificationFlags,
1616 aIsThirdParty);
1617 }
1618 return NS_OK;
1619 }
1620
1621 NS_IMETHODIMP
NotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState)1622 HttpChannelParent::NotifyFlashPluginStateChanged(
1623 nsIHttpChannel::FlashPluginState aState) {
1624 LOG(("HttpChannelParent::NotifyFlashPluginStateChanged [this=%p]\n", this));
1625 if (!mIPCClosed) {
1626 MOZ_ASSERT(mBgParent);
1627 Unused << mBgParent->OnNotifyFlashPluginStateChanged(aState);
1628 }
1629 return NS_OK;
1630 }
1631
1632 NS_IMETHODIMP
Delete()1633 HttpChannelParent::Delete() {
1634 if (!mIPCClosed) Unused << DoSendDeleteSelf();
1635
1636 return NS_OK;
1637 }
1638
1639 NS_IMETHODIMP
GetRemoteType(nsACString & aRemoteType)1640 HttpChannelParent::GetRemoteType(nsACString& aRemoteType) {
1641 if (!CanSend()) {
1642 return NS_ERROR_UNEXPECTED;
1643 }
1644
1645 dom::PContentParent* pcp = Manager()->Manager();
1646 aRemoteType = static_cast<dom::ContentParent*>(pcp)->GetRemoteType();
1647 return NS_OK;
1648 }
1649
1650 //-----------------------------------------------------------------------------
1651 // HttpChannelParent::nsIParentRedirectingChannel
1652 //-----------------------------------------------------------------------------
1653
1654 NS_IMETHODIMP
StartRedirect(nsIChannel * newChannel,uint32_t redirectFlags,nsIAsyncVerifyRedirectCallback * callback)1655 HttpChannelParent::StartRedirect(nsIChannel* newChannel, uint32_t redirectFlags,
1656 nsIAsyncVerifyRedirectCallback* callback) {
1657 nsresult rv;
1658
1659 LOG(("HttpChannelParent::StartRedirect [this=%p, newChannel=%p callback=%p]",
1660 this, newChannel, callback));
1661
1662 // Register the new channel and obtain id for it
1663 nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
1664 RedirectChannelRegistrar::GetOrCreate();
1665 MOZ_ASSERT(registrar);
1666
1667 mRedirectChannelId = nsContentUtils::GenerateLoadIdentifier();
1668 rv = registrar->RegisterChannel(newChannel, mRedirectChannelId);
1669 NS_ENSURE_SUCCESS(rv, rv);
1670
1671 LOG(("Registered %p channel under id=%" PRIx64, newChannel,
1672 mRedirectChannelId));
1673
1674 if (mIPCClosed) {
1675 return NS_BINDING_ABORTED;
1676 }
1677
1678 // If this is an internal redirect for service worker interception, then
1679 // hide it from the child process. The original e10s interception code
1680 // was not designed with this in mind and its not necessary to replace
1681 // the HttpChannelChild/Parent objects in this case.
1682 if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
1683 nsCOMPtr<nsIInterceptedChannel> oldIntercepted =
1684 do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
1685 nsCOMPtr<nsIInterceptedChannel> newIntercepted =
1686 do_QueryInterface(newChannel);
1687
1688 // We only want to hide the special internal redirect from nsHttpChannel
1689 // to InterceptedHttpChannel. We want to allow through internal redirects
1690 // initiated from the InterceptedHttpChannel even if they are to another
1691 // InterceptedHttpChannel.
1692 if (!oldIntercepted && newIntercepted) {
1693 // We need to move across the reserved and initial client information
1694 // to the new channel. Normally this would be handled by the child
1695 // ClientChannelHelper, but that is not notified of this redirect since
1696 // we're not propagating it back to the child process.
1697 nsCOMPtr<nsILoadInfo> oldLoadInfo = mChannel->LoadInfo();
1698
1699 nsCOMPtr<nsILoadInfo> newLoadInfo = newChannel->LoadInfo();
1700
1701 Maybe<ClientInfo> reservedClientInfo(
1702 oldLoadInfo->GetReservedClientInfo());
1703 if (reservedClientInfo.isSome()) {
1704 newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
1705 }
1706
1707 Maybe<ClientInfo> initialClientInfo(oldLoadInfo->GetInitialClientInfo());
1708 if (initialClientInfo.isSome()) {
1709 newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
1710 }
1711
1712 // Re-link the HttpChannelParent to the new InterceptedHttpChannel.
1713 nsCOMPtr<nsIChannel> linkedChannel;
1714 rv = NS_LinkRedirectChannels(mRedirectChannelId, this,
1715 getter_AddRefs(linkedChannel));
1716 NS_ENSURE_SUCCESS(rv, rv);
1717 MOZ_ASSERT(linkedChannel == newChannel);
1718
1719 // We immediately store the InterceptedHttpChannel as our nested
1720 // mChannel. None of the redirect IPC messaging takes place.
1721 mChannel = do_QueryObject(newChannel);
1722
1723 callback->OnRedirectVerifyCallback(NS_OK);
1724 return NS_OK;
1725 }
1726 }
1727
1728 // Sending down the original URI, because that is the URI we have
1729 // to construct the channel from - this is the URI we've been actually
1730 // redirected to. URI of the channel may be an inner channel URI.
1731 // URI of the channel will be reconstructed by the protocol handler
1732 // on the child process, no need to send it then.
1733 nsCOMPtr<nsIURI> newOriginalURI;
1734 newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
1735
1736 URIParams uriParams;
1737 SerializeURI(newOriginalURI, uriParams);
1738
1739 uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
1740 MOZ_ALWAYS_SUCCEEDS(newChannel->GetLoadFlags(&newLoadFlags));
1741
1742 nsCString secInfoSerialization;
1743 UpdateAndSerializeSecurityInfo(secInfoSerialization);
1744
1745 // If the channel is a HTTP channel, we also want to inform the child
1746 // about the parent's channelId attribute, so that both parent and child
1747 // share the same ID. Useful for monitoring channel activity in devtools.
1748 uint64_t channelId = 0;
1749 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
1750 if (httpChannel) {
1751 rv = httpChannel->GetChannelId(&channelId);
1752 NS_ENSURE_SUCCESS(rv, NS_BINDING_ABORTED);
1753 }
1754
1755 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
1756
1757 ParentLoadInfoForwarderArgs loadInfoForwarderArg;
1758 mozilla::ipc::LoadInfoToParentLoadInfoForwarder(loadInfo,
1759 &loadInfoForwarderArg);
1760
1761 nsHttpResponseHead* responseHead = mChannel->GetResponseHead();
1762
1763 nsHttpResponseHead cleanedUpResponseHead;
1764 if (responseHead && responseHead->HasHeader(nsHttp::Set_Cookie)) {
1765 cleanedUpResponseHead = *responseHead;
1766 cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie);
1767 responseHead = &cleanedUpResponseHead;
1768 }
1769
1770 if (!responseHead) {
1771 responseHead = &cleanedUpResponseHead;
1772 }
1773
1774 bool result = false;
1775 if (!mIPCClosed) {
1776 result = SendRedirect1Begin(
1777 mRedirectChannelId, uriParams, newLoadFlags, redirectFlags,
1778 loadInfoForwarderArg, *responseHead, secInfoSerialization, channelId,
1779 mChannel->GetPeerAddr(), GetTimingAttributes(mChannel));
1780 }
1781 if (!result) {
1782 // Bug 621446 investigation
1783 mSentRedirect1BeginFailed = true;
1784 return NS_BINDING_ABORTED;
1785 }
1786
1787 // Result is handled in RecvRedirect2Verify above
1788
1789 mRedirectChannel = newChannel;
1790 mRedirectCallback = callback;
1791 return NS_OK;
1792 }
1793
1794 NS_IMETHODIMP
CompleteRedirect(bool succeeded)1795 HttpChannelParent::CompleteRedirect(bool succeeded) {
1796 LOG(("HttpChannelParent::CompleteRedirect [this=%p succeeded=%d]\n", this,
1797 succeeded));
1798
1799 // If this was an internal redirect for a service worker interception then
1800 // we will not have a redirecting channel here. Hide this redirect from
1801 // the child.
1802 if (!mRedirectChannel) {
1803 return NS_OK;
1804 }
1805
1806 if (succeeded && !mIPCClosed) {
1807 // TODO: check return value: assume child dead if failed
1808 Unused << SendRedirect3Complete();
1809 }
1810
1811 mRedirectChannel = nullptr;
1812 return NS_OK;
1813 }
1814
OpenAlternativeOutputStream(const nsACString & type,int64_t predictedSize,nsIAsyncOutputStream ** _retval)1815 nsresult HttpChannelParent::OpenAlternativeOutputStream(
1816 const nsACString& type, int64_t predictedSize,
1817 nsIAsyncOutputStream** _retval) {
1818 // We need to make sure the child does not call SendDocumentChannelCleanup()
1819 // before opening the altOutputStream, because that clears mCacheEntry.
1820 if (!mCacheEntry) {
1821 return NS_ERROR_NOT_AVAILABLE;
1822 }
1823 nsresult rv =
1824 mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
1825 if (NS_SUCCEEDED(rv)) {
1826 mCacheEntry->SetMetaDataElement("alt-data-from-child", "1");
1827 }
1828 return rv;
1829 }
1830
UpdateAndSerializeSecurityInfo(nsACString & aSerializedSecurityInfoOut)1831 void HttpChannelParent::UpdateAndSerializeSecurityInfo(
1832 nsACString& aSerializedSecurityInfoOut) {
1833 nsCOMPtr<nsISupports> secInfoSupp;
1834 mChannel->GetSecurityInfo(getter_AddRefs(secInfoSupp));
1835 if (secInfoSupp) {
1836 nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfoSupp);
1837 if (secInfoSer) {
1838 NS_SerializeToString(secInfoSer, aSerializedSecurityInfoOut);
1839 }
1840 }
1841 }
1842
DoSendDeleteSelf()1843 bool HttpChannelParent::DoSendDeleteSelf() {
1844 mIPCClosed = true;
1845 bool rv = SendDeleteSelf();
1846
1847 CleanupBackgroundChannel();
1848
1849 return rv;
1850 }
1851
RecvDeletingChannel()1852 mozilla::ipc::IPCResult HttpChannelParent::RecvDeletingChannel() {
1853 // We need to ensure that the parent channel will not be sending any more IPC
1854 // messages after this, as the child is going away. DoSendDeleteSelf will
1855 // set mIPCClosed = true;
1856 if (!DoSendDeleteSelf()) {
1857 return IPC_FAIL_NO_REASON(this);
1858 }
1859 return IPC_OK();
1860 }
1861
1862 //-----------------------------------------------------------------------------
1863 // HttpChannelSecurityWarningReporter
1864 //-----------------------------------------------------------------------------
1865
ReportSecurityMessage(const nsAString & aMessageTag,const nsAString & aMessageCategory)1866 nsresult HttpChannelParent::ReportSecurityMessage(
1867 const nsAString& aMessageTag, const nsAString& aMessageCategory) {
1868 if (mIPCClosed || NS_WARN_IF(!SendReportSecurityMessage(
1869 nsString(aMessageTag), nsString(aMessageCategory)))) {
1870 return NS_ERROR_UNEXPECTED;
1871 }
1872 return NS_OK;
1873 }
1874
1875 //-----------------------------------------------------------------------------
1876 // nsIAsyncVerifyRedirectReadyCallback
1877 //-----------------------------------------------------------------------------
1878
1879 NS_IMETHODIMP
ReadyToVerify(nsresult aResult)1880 HttpChannelParent::ReadyToVerify(nsresult aResult) {
1881 LOG(("HttpChannelParent::ReadyToVerify [this=%p result=%" PRIx32 "]\n", this,
1882 static_cast<uint32_t>(aResult)));
1883 MOZ_ASSERT(NS_IsMainThread());
1884
1885 ContinueRedirect2Verify(aResult);
1886
1887 return NS_OK;
1888 }
1889
DoSendSetPriority(int16_t aValue)1890 void HttpChannelParent::DoSendSetPriority(int16_t aValue) {
1891 if (!mIPCClosed) {
1892 Unused << SendSetPriority(aValue);
1893 }
1894 }
1895
LogBlockedCORSRequest(const nsAString & aMessage,const nsACString & aCategory)1896 nsresult HttpChannelParent::LogBlockedCORSRequest(const nsAString& aMessage,
1897 const nsACString& aCategory) {
1898 if (mIPCClosed || NS_WARN_IF(!SendLogBlockedCORSRequest(
1899 nsString(aMessage), nsCString(aCategory)))) {
1900 return NS_ERROR_UNEXPECTED;
1901 }
1902 return NS_OK;
1903 }
1904
LogMimeTypeMismatch(const nsACString & aMessageName,bool aWarning,const nsAString & aURL,const nsAString & aContentType)1905 nsresult HttpChannelParent::LogMimeTypeMismatch(const nsACString& aMessageName,
1906 bool aWarning,
1907 const nsAString& aURL,
1908 const nsAString& aContentType) {
1909 if (mIPCClosed || NS_WARN_IF(!SendLogMimeTypeMismatch(
1910 nsCString(aMessageName), aWarning, nsString(aURL),
1911 nsString(aContentType)))) {
1912 return NS_ERROR_UNEXPECTED;
1913 }
1914 return NS_OK;
1915 }
1916
1917 //-----------------------------------------------------------------------------
1918 // nsIChannelEventSink
1919 //-----------------------------------------------------------------------------
1920
1921 NS_IMETHODIMP
AsyncOnChannelRedirect(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aRedirectFlags,nsIAsyncVerifyRedirectCallback * aCallback)1922 HttpChannelParent::AsyncOnChannelRedirect(
1923 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirectFlags,
1924 nsIAsyncVerifyRedirectCallback* aCallback) {
1925 LOG(
1926 ("HttpChannelParent::AsyncOnChannelRedirect [this=%p, old=%p, "
1927 "new=%p, flags=%u]",
1928 this, aOldChannel, aNewChannel, aRedirectFlags));
1929
1930 return StartRedirect(aNewChannel, aRedirectFlags, aCallback);
1931 }
1932
1933 //-----------------------------------------------------------------------------
1934 // nsIRedirectResultListener
1935 //-----------------------------------------------------------------------------
1936
1937 NS_IMETHODIMP
OnRedirectResult(bool succeeded)1938 HttpChannelParent::OnRedirectResult(bool succeeded) {
1939 LOG(("HttpChannelParent::OnRedirectResult [this=%p, suc=%d]", this,
1940 succeeded));
1941
1942 nsresult rv;
1943
1944 nsCOMPtr<nsIParentChannel> redirectChannel;
1945 if (mRedirectChannelId) {
1946 nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
1947 RedirectChannelRegistrar::GetOrCreate();
1948 MOZ_ASSERT(registrar);
1949
1950 rv = registrar->GetParentChannel(mRedirectChannelId,
1951 getter_AddRefs(redirectChannel));
1952 if (NS_FAILED(rv) || !redirectChannel) {
1953 // Redirect might get canceled before we got AsyncOnChannelRedirect
1954 LOG(("Registered parent channel not found under id=%" PRIx64,
1955 mRedirectChannelId));
1956
1957 nsCOMPtr<nsIChannel> newChannel;
1958 rv = registrar->GetRegisteredChannel(mRedirectChannelId,
1959 getter_AddRefs(newChannel));
1960 MOZ_ASSERT(newChannel, "Already registered channel not found");
1961
1962 if (NS_SUCCEEDED(rv)) {
1963 newChannel->Cancel(NS_BINDING_ABORTED);
1964 }
1965 }
1966
1967 // Release all previously registered channels, they are no longer need to be
1968 // kept in the registrar from this moment.
1969 registrar->DeregisterChannels(mRedirectChannelId);
1970
1971 mRedirectChannelId = 0;
1972 }
1973
1974 if (!redirectChannel) {
1975 succeeded = false;
1976 }
1977
1978 CompleteRedirect(succeeded);
1979
1980 if (succeeded) {
1981 if (!SameCOMIdentity(redirectChannel,
1982 static_cast<nsIParentRedirectingChannel*>(this))) {
1983 Delete();
1984 mParentListener->SetListenerAfterRedirect(redirectChannel);
1985 redirectChannel->SetParentListener(mParentListener);
1986 }
1987 } else if (redirectChannel) {
1988 // Delete the redirect target channel: continue using old channel
1989 redirectChannel->Delete();
1990 }
1991
1992 return NS_OK;
1993 }
1994
OverrideReferrerInfoDuringBeginConnect(nsIReferrerInfo * aReferrerInfo)1995 void HttpChannelParent::OverrideReferrerInfoDuringBeginConnect(
1996 nsIReferrerInfo* aReferrerInfo) {
1997 MOZ_ASSERT(aReferrerInfo);
1998 MOZ_ASSERT(!mAfterOnStartRequestBegun);
1999
2000 mOverrideReferrerInfo = aReferrerInfo;
2001 }
2002
AttachStreamFilter(Endpoint<extensions::PStreamFilterParent> && aParentEndpoint,Endpoint<extensions::PStreamFilterChild> && aChildEndpoint)2003 auto HttpChannelParent::AttachStreamFilter(
2004 Endpoint<extensions::PStreamFilterParent>&& aParentEndpoint,
2005 Endpoint<extensions::PStreamFilterChild>&& aChildEndpoint)
2006 -> RefPtr<ChildEndpointPromise> {
2007 LOG(("HttpChannelParent::AttachStreamFilter [this=%p]", this));
2008 MOZ_ASSERT(!mAfterOnStartRequestBegun);
2009
2010 if (mIPCClosed) {
2011 return ChildEndpointPromise::CreateAndReject(false, __func__);
2012 }
2013
2014 // If IPC channel is open, background channel should be ready to send
2015 // SendAttachStreamFilter.
2016 MOZ_ASSERT(mBgParent);
2017 return InvokeAsync(mBgParent->GetBackgroundTarget(), mBgParent.get(),
2018 __func__, &HttpBackgroundChannelParent::AttachStreamFilter,
2019 std::move(aParentEndpoint), std::move(aChildEndpoint));
2020 }
2021
SetCookie(nsCString && aCookie)2022 void HttpChannelParent::SetCookie(nsCString&& aCookie) {
2023 LOG(("HttpChannelParent::SetCookie [this=%p]", this));
2024 MOZ_ASSERT(!mAfterOnStartRequestBegun);
2025 MOZ_ASSERT(mCookie.IsEmpty());
2026 mCookie = std::move(aCookie);
2027 }
2028
2029 } // namespace net
2030 } // namespace mozilla
2031