Source code

Revision control

Other Tools

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
4
/* This Source Code Form is subject to the terms of the Mozilla Public
5
* License, v. 2.0. If a copy of the MPL was not distributed with this
6
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
// HttpLog.h should generally be included first
9
#include "HttpLog.h"
10
11
#include "nsHttp.h"
12
#include "nsICacheEntry.h"
13
#include "mozilla/AntiTrackingCommon.h"
14
#include "mozilla/BasePrincipal.h"
15
#include "mozilla/Unused.h"
16
#include "mozilla/dom/ContentChild.h"
17
#include "mozilla/dom/DocGroup.h"
18
#include "mozilla/dom/ServiceWorkerUtils.h"
19
#include "mozilla/dom/BrowserChild.h"
20
#include "mozilla/dom/TabGroup.h"
21
#include "mozilla/extensions/StreamFilterParent.h"
22
#include "mozilla/ipc/FileDescriptorSetChild.h"
23
#include "mozilla/ipc/IPCStreamUtils.h"
24
#include "mozilla/net/NeckoChild.h"
25
#include "mozilla/net/HttpChannelChild.h"
26
#include "mozilla/net/UrlClassifierCommon.h"
27
#include "mozilla/net/UrlClassifierFeatureFactory.h"
28
29
#include "AltDataOutputStreamChild.h"
30
#include "CookieServiceChild.h"
31
#include "HttpBackgroundChannelChild.h"
32
#include "nsCOMPtr.h"
33
#include "nsContentPolicyUtils.h"
34
#include "nsDOMNavigationTiming.h"
35
#include "nsGlobalWindow.h"
36
#include "nsStringStream.h"
37
#include "nsHttpChannel.h"
38
#include "nsHttpHandler.h"
39
#include "nsNetUtil.h"
40
#include "nsSerializationHelper.h"
41
#include "mozilla/Attributes.h"
42
#include "mozilla/dom/PerformanceStorage.h"
43
#include "mozilla/ipc/InputStreamUtils.h"
44
#include "mozilla/ipc/URIUtils.h"
45
#include "mozilla/ipc/BackgroundUtils.h"
46
#include "mozilla/net/ChannelDiverterChild.h"
47
#include "mozilla/net/DNS.h"
48
#include "SerializedLoadContext.h"
49
#include "nsInputStreamPump.h"
50
#include "InterceptedChannel.h"
51
#include "nsContentSecurityManager.h"
52
#include "nsICompressConvStats.h"
53
#include "nsIDeprecationWarner.h"
54
#include "mozilla/dom/Document.h"
55
#include "nsIEventTarget.h"
56
#include "nsIScriptError.h"
57
#include "nsRedirectHistoryEntry.h"
58
#include "nsSocketTransportService2.h"
59
#include "nsStreamUtils.h"
60
#include "nsThreadUtils.h"
61
#include "nsCORSListenerProxy.h"
62
#include "nsApplicationCache.h"
63
#include "ClassifierDummyChannel.h"
64
65
#ifdef MOZ_TASK_TRACER
66
# include "GeckoTaskTracer.h"
67
#endif
68
69
#ifdef MOZ_GECKO_PROFILER
70
# include "ProfilerMarkerPayload.h"
71
#endif
72
73
#include <functional>
74
75
using namespace mozilla::dom;
76
using namespace mozilla::ipc;
77
78
namespace mozilla {
79
namespace net {
80
81
NS_IMPL_ISUPPORTS(InterceptStreamListener, nsIStreamListener,
82
nsIRequestObserver, nsIProgressEventSink)
83
84
NS_IMETHODIMP
85
InterceptStreamListener::OnStartRequest(nsIRequest* aRequest) {
86
if (mOwner) {
87
mOwner->DoOnStartRequest(mOwner, nullptr);
88
}
89
return NS_OK;
90
}
91
92
NS_IMETHODIMP
93
InterceptStreamListener::OnStatus(nsIRequest* aRequest, nsISupports* aContext,
94
nsresult status, const char16_t* aStatusArg) {
95
if (mOwner) {
96
mOwner->DoOnStatus(mOwner, status);
97
}
98
return NS_OK;
99
}
100
101
NS_IMETHODIMP
102
InterceptStreamListener::OnProgress(nsIRequest* aRequest, nsISupports* aContext,
103
int64_t aProgress, int64_t aProgressMax) {
104
if (mOwner) {
105
mOwner->DoOnProgress(mOwner, aProgress, aProgressMax);
106
}
107
return NS_OK;
108
}
109
110
NS_IMETHODIMP
111
InterceptStreamListener::OnDataAvailable(nsIRequest* aRequest,
112
nsIInputStream* aInputStream,
113
uint64_t aOffset, uint32_t aCount) {
114
if (!mOwner) {
115
return NS_OK;
116
}
117
118
uint32_t loadFlags;
119
mOwner->GetLoadFlags(&loadFlags);
120
121
if (!(loadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
122
nsCOMPtr<nsIURI> uri;
123
mOwner->GetURI(getter_AddRefs(uri));
124
125
nsAutoCString host;
126
uri->GetHost(host);
127
128
OnStatus(mOwner, nullptr, NS_NET_STATUS_READING,
129
NS_ConvertUTF8toUTF16(host).get());
130
131
int64_t progress = aOffset + aCount;
132
OnProgress(mOwner, nullptr, progress, mOwner->mSynthesizedStreamLength);
133
}
134
135
mOwner->DoOnDataAvailable(mOwner, nullptr, aInputStream, aOffset, aCount);
136
return NS_OK;
137
}
138
139
NS_IMETHODIMP
140
InterceptStreamListener::OnStopRequest(nsIRequest* aRequest,
141
nsresult aStatusCode) {
142
if (mOwner) {
143
mOwner->DoPreOnStopRequest(aStatusCode);
144
mOwner->DoOnStopRequest(mOwner, aStatusCode, mContext);
145
}
146
Cleanup();
147
return NS_OK;
148
}
149
150
void InterceptStreamListener::Cleanup() {
151
mOwner = nullptr;
152
mContext = nullptr;
153
}
154
155
//-----------------------------------------------------------------------------
156
// HttpChannelChild
157
//-----------------------------------------------------------------------------
158
159
HttpChannelChild::HttpChannelChild()
160
: HttpAsyncAborter<HttpChannelChild>(this),
161
NeckoTargetHolder(nullptr),
162
mBgChildMutex("HttpChannelChild::BgChildMutex"),
163
mEventTargetMutex("HttpChannelChild::EventTargetMutex"),
164
mSynthesizedStreamLength(0),
165
mCacheEntryId(0),
166
mCacheKey(0),
167
mCacheFetchCount(0),
168
mCacheExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME),
169
mDeletingChannelSent(false),
170
mUnknownDecoderInvolved(false),
171
mDivertingToParent(false),
172
mFlushedForDiversion(false),
173
mIsFromCache(false),
174
mIsRacing(false),
175
mCacheNeedToReportBytesReadInitialized(false),
176
mNeedToReportBytesRead(true),
177
mCacheEntryAvailable(false),
178
mAltDataCacheEntryAvailable(false),
179
mSendResumeAt(false),
180
mKeptAlive(false),
181
mIPCActorDeleted(false),
182
mSuspendSent(false),
183
mSynthesizedResponse(false),
184
mShouldInterceptSubsequentRedirect(false),
185
mRedirectingForSubsequentSynthesizedResponse(false),
186
mPostRedirectChannelShouldIntercept(false),
187
mPostRedirectChannelShouldUpgrade(false),
188
mShouldParentIntercept(false),
189
mSuspendParentAfterSynthesizeResponse(false) {
190
LOG(("Creating HttpChannelChild @%p\n", this));
191
192
mChannelCreationTime = PR_Now();
193
mChannelCreationTimestamp = TimeStamp::Now();
194
mLastStatusReported =
195
mChannelCreationTimestamp; // in case we enable the profiler after Init()
196
mAsyncOpenTime = TimeStamp::Now();
197
mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
198
199
// Ensure that the cookie service is initialized before the first
200
// IPC HTTP channel is created.
201
// We require that the parent cookie service actor exists while
202
// processing HTTP responses.
203
RefPtr<CookieServiceChild> cookieService = CookieServiceChild::GetSingleton();
204
}
205
206
HttpChannelChild::~HttpChannelChild() {
207
LOG(("Destroying HttpChannelChild @%p\n", this));
208
209
ReleaseMainThreadOnlyReferences();
210
}
211
212
void HttpChannelChild::ReleaseMainThreadOnlyReferences() {
213
if (NS_IsMainThread()) {
214
// Already on main thread, let dtor to
215
// take care of releasing references
216
return;
217
}
218
219
nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
220
arrayToRelease.AppendElement(mRedirectChannelChild.forget());
221
222
// To solve multiple inheritence of nsISupports in InterceptStreamListener
223
nsCOMPtr<nsIStreamListener> listener = mInterceptListener.forget();
224
arrayToRelease.AppendElement(listener.forget());
225
226
arrayToRelease.AppendElement(mInterceptedRedirectListener.forget());
227
arrayToRelease.AppendElement(mInterceptedRedirectContext.forget());
228
229
NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
230
}
231
//-----------------------------------------------------------------------------
232
// HttpChannelChild::nsISupports
233
//-----------------------------------------------------------------------------
234
235
NS_IMPL_ADDREF(HttpChannelChild)
236
237
NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
238
if (!NS_IsMainThread()) {
239
nsrefcnt count = mRefCnt;
240
nsresult rv = NS_DispatchToMainThread(NewNonOwningRunnableMethod(
241
"HttpChannelChild::Release", this, &HttpChannelChild::Release));
242
243
// Continue Release procedure if failed to dispatch to main thread.
244
if (!NS_WARN_IF(NS_FAILED(rv))) {
245
return count - 1;
246
}
247
}
248
249
nsrefcnt count = --mRefCnt;
250
MOZ_ASSERT(int32_t(count) >= 0, "dup release");
251
NS_LOG_RELEASE(this, count, "HttpChannelChild");
252
253
// Normally we Send_delete in OnStopRequest, but when we need to retain the
254
// remote channel for security info IPDL itself holds 1 reference, so we
255
// Send_delete when refCnt==1. But if !CanSend(), then there's nobody to send
256
// to, so we fall through.
257
if (mKeptAlive && count == 1 && CanSend()) {
258
mKeptAlive = false;
259
// We send a message to the parent, which calls SendDelete, and then the
260
// child calling Send__delete__() to finally drop the refcount to 0.
261
TrySendDeletingChannel();
262
return 1;
263
}
264
265
if (count == 0) {
266
mRefCnt = 1; /* stabilize */
267
delete this;
268
return 0;
269
}
270
return count;
271
}
272
273
NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
274
NS_INTERFACE_MAP_ENTRY(nsIRequest)
275
NS_INTERFACE_MAP_ENTRY(nsIChannel)
276
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
277
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
278
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICacheInfoChannel,
279
!mMultiPartID.isSome())
280
NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
281
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
282
NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
283
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
284
NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
285
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIApplicationCacheContainer,
286
!mMultiPartID.isSome())
287
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
288
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
289
NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
290
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild)
291
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDivertableChannel,
292
!mMultiPartID.isSome())
293
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiPartChannel, mMultiPartID.isSome())
294
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIThreadRetargetableRequest,
295
!mMultiPartID.isSome())
296
NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelChild)
297
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
298
299
//-----------------------------------------------------------------------------
300
// HttpChannelChild::PHttpChannelChild
301
//-----------------------------------------------------------------------------
302
303
void HttpChannelChild::OnBackgroundChildReady(
304
HttpBackgroundChannelChild* aBgChild) {
305
LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n", this,
306
aBgChild));
307
MOZ_ASSERT(OnSocketThread());
308
309
{
310
MutexAutoLock lock(mBgChildMutex);
311
312
// mBgChild might be removed or replaced while the original background
313
// channel is inited on STS thread.
314
if (mBgChild != aBgChild) {
315
return;
316
}
317
318
MOZ_ASSERT(mBgInitFailCallback);
319
mBgInitFailCallback = nullptr;
320
}
321
}
322
323
void HttpChannelChild::OnBackgroundChildDestroyed(
324
HttpBackgroundChannelChild* aBgChild) {
325
LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
326
// This function might be called during shutdown phase, so OnSocketThread()
327
// might return false even on STS thread. Use IsOnCurrentThreadInfallible()
328
// to get correct information.
329
MOZ_ASSERT(gSocketTransportService);
330
MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
331
332
nsCOMPtr<nsIRunnable> callback;
333
{
334
MutexAutoLock lock(mBgChildMutex);
335
336
// mBgChild might be removed or replaced while the original background
337
// channel is destroyed on STS thread.
338
if (aBgChild != mBgChild) {
339
return;
340
}
341
342
mBgChild = nullptr;
343
callback = mBgInitFailCallback.forget();
344
}
345
346
if (callback) {
347
nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
348
neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
349
}
350
}
351
352
mozilla::ipc::IPCResult HttpChannelChild::RecvAssociateApplicationCache(
353
const nsCString& aGroupID, const nsCString& aClientID) {
354
LOG(("HttpChannelChild::RecvAssociateApplicationCache [this=%p]\n", this));
355
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
356
this, [self = UnsafePtr<HttpChannelChild>(this), aGroupID, aClientID]() {
357
self->AssociateApplicationCache(aGroupID, aClientID);
358
}));
359
return IPC_OK();
360
}
361
362
void HttpChannelChild::AssociateApplicationCache(const nsCString& aGroupID,
363
const nsCString& aClientID) {
364
LOG(("HttpChannelChild::AssociateApplicationCache [this=%p]\n", this));
365
mApplicationCache = new nsApplicationCache();
366
367
mLoadedFromApplicationCache = true;
368
mApplicationCache->InitAsHandle(aGroupID, aClientID);
369
}
370
371
mozilla::ipc::IPCResult HttpChannelChild::RecvOnStartRequest(
372
const nsresult& aChannelStatus, const nsHttpResponseHead& aResponseHead,
373
const bool& aUseResponseHead, const nsHttpHeaderArray& aRequestHeaders,
374
const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
375
const bool& aIsFromCache, const bool& aIsRacing,
376
const bool& aCacheEntryAvailable, const uint64_t& aCacheEntryId,
377
const int32_t& aCacheFetchCount, const uint32_t& aCacheExpirationTime,
378
const nsCString& aCachedCharset,
379
const nsCString& aSecurityInfoSerialization, const NetAddr& aSelfAddr,
380
const NetAddr& aPeerAddr, const int16_t& aRedirectCount,
381
const uint32_t& aCacheKey, const nsCString& aAltDataType,
382
const int64_t& aAltDataLen, const bool& aDeliveringAltData,
383
const bool& aApplyConversion, const bool& aIsResolvedByTRR,
384
const ResourceTimingStructArgs& aTiming,
385
const bool& aAllRedirectsSameOrigin, const Maybe<uint32_t>& aMultiPartID,
386
const bool& aIsLastPartOfMultiPart,
387
const nsILoadInfo::CrossOriginOpenerPolicy& aOpenerPolicy) {
388
AUTO_PROFILER_LABEL("HttpChannelChild::RecvOnStartRequest", NETWORK);
389
LOG(("HttpChannelChild::RecvOnStartRequest [this=%p]\n", this));
390
// mFlushedForDiversion and mDivertingToParent should NEVER be set at this
391
// stage, as they are set in the listener's OnStartRequest.
392
MOZ_RELEASE_ASSERT(
393
!mFlushedForDiversion,
394
"mFlushedForDiversion should be unset before OnStartRequest!");
395
MOZ_RELEASE_ASSERT(
396
!mDivertingToParent,
397
"mDivertingToParent should be unset before OnStartRequest!");
398
399
mRedirectCount = aRedirectCount;
400
401
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
402
this,
403
[self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aResponseHead,
404
aUseResponseHead, aRequestHeaders, aLoadInfoForwarder, aIsFromCache,
405
aIsRacing, aCacheEntryAvailable, aCacheEntryId, aCacheFetchCount,
406
aCacheExpirationTime, aCachedCharset, aSecurityInfoSerialization,
407
aSelfAddr, aPeerAddr, aCacheKey, aAltDataType, aAltDataLen,
408
aDeliveringAltData, aApplyConversion, aIsResolvedByTRR, aTiming,
409
aAllRedirectsSameOrigin, aMultiPartID, aIsLastPartOfMultiPart,
410
aOpenerPolicy]() {
411
self->OnStartRequest(
412
aChannelStatus, aResponseHead, aUseResponseHead, aRequestHeaders,
413
aLoadInfoForwarder, aIsFromCache, aIsRacing, aCacheEntryAvailable,
414
aCacheEntryId, aCacheFetchCount, aCacheExpirationTime,
415
aCachedCharset, aSecurityInfoSerialization, aSelfAddr, aPeerAddr,
416
aCacheKey, aAltDataType, aAltDataLen, aDeliveringAltData,
417
aApplyConversion, aIsResolvedByTRR, aTiming,
418
aAllRedirectsSameOrigin, aMultiPartID, aIsLastPartOfMultiPart,
419
aOpenerPolicy);
420
}));
421
422
{
423
// Child's mEventQ is to control the execution order of the IPC messages
424
// from both main thread IPDL and PBackground IPDL.
425
// To guarantee the ordering, PBackground IPC messages that are sent after
426
// OnStartRequest will be throttled until OnStartRequest hits the Child's
427
// mEventQ.
428
MutexAutoLock lock(mBgChildMutex);
429
430
// We don't need to notify the background channel if this is a multipart
431
// stream, since all messages will be sent over the main-thread IPDL in
432
// that case.
433
if (mBgChild && !aMultiPartID) {
434
MOZ_RELEASE_ASSERT(gSocketTransportService);
435
DebugOnly<nsresult> rv = gSocketTransportService->Dispatch(
436
NewRunnableMethod(
437
"HttpBackgroundChannelChild::OnStartRequestReceived", mBgChild,
438
&HttpBackgroundChannelChild::OnStartRequestReceived),
439
NS_DISPATCH_NORMAL);
440
}
441
}
442
443
return IPC_OK();
444
}
445
446
static void ResourceTimingStructArgsToTimingsStruct(
447
const ResourceTimingStructArgs& aArgs, TimingStruct& aTimings) {
448
aTimings.domainLookupStart = aArgs.domainLookupStart();
449
aTimings.domainLookupEnd = aArgs.domainLookupEnd();
450
aTimings.connectStart = aArgs.connectStart();
451
aTimings.tcpConnectEnd = aArgs.tcpConnectEnd();
452
aTimings.secureConnectionStart = aArgs.secureConnectionStart();
453
aTimings.connectEnd = aArgs.connectEnd();
454
aTimings.requestStart = aArgs.requestStart();
455
aTimings.responseStart = aArgs.responseStart();
456
aTimings.responseEnd = aArgs.responseEnd();
457
}
458
459
void HttpChannelChild::OnStartRequest(
460
const nsresult& aChannelStatus, const nsHttpResponseHead& aResponseHead,
461
const bool& aUseResponseHead, const nsHttpHeaderArray& aRequestHeaders,
462
const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
463
const bool& aIsFromCache, const bool& aIsRacing,
464
const bool& aCacheEntryAvailable, const uint64_t& aCacheEntryId,
465
const int32_t& aCacheFetchCount, const uint32_t& aCacheExpirationTime,
466
const nsCString& aCachedCharset,
467
const nsCString& aSecurityInfoSerialization, const NetAddr& aSelfAddr,
468
const NetAddr& aPeerAddr, const uint32_t& aCacheKey,
469
const nsCString& aAltDataType, const int64_t& aAltDataLen,
470
const bool& aDeliveringAltData, const bool& aApplyConversion,
471
const bool& aIsResolvedByTRR, const ResourceTimingStructArgs& aTiming,
472
const bool& aAllRedirectsSameOrigin, const Maybe<uint32_t>& aMultiPartID,
473
const bool& aIsLastPartOfMultiPart,
474
const nsILoadInfo::CrossOriginOpenerPolicy& aOpenerPolicy) {
475
LOG(("HttpChannelChild::OnStartRequest [this=%p]\n", this));
476
477
// mFlushedForDiversion and mDivertingToParent should NEVER be set at this
478
// stage, as they are set in the listener's OnStartRequest.
479
MOZ_RELEASE_ASSERT(
480
!mFlushedForDiversion,
481
"mFlushedForDiversion should be unset before OnStartRequest!");
482
MOZ_RELEASE_ASSERT(
483
!mDivertingToParent,
484
"mDivertingToParent should be unset before OnStartRequest!");
485
486
// If this channel was aborted by ActorDestroy, then there may be other
487
// OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
488
// be handled. In that case we just ignore them to avoid calling the listener
489
// twice.
490
if (mOnStartRequestCalled && mIPCActorDeleted) {
491
return;
492
}
493
494
mComputedCrossOriginOpenerPolicy = aOpenerPolicy;
495
496
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
497
mStatus = aChannelStatus;
498
}
499
500
// Cookies headers should not be visible to the child process
501
MOZ_ASSERT(!aRequestHeaders.HasHeader(nsHttp::Cookie));
502
MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
503
504
if (aUseResponseHead && !mCanceled)
505
mResponseHead = new nsHttpResponseHead(aResponseHead);
506
507
if (!aSecurityInfoSerialization.IsEmpty()) {
508
nsresult rv = NS_DeserializeObject(aSecurityInfoSerialization,
509
getter_AddRefs(mSecurityInfo));
510
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
511
"Deserializing security info should not fail");
512
Unused << rv; // So we don't get an unused error in release builds.
513
}
514
515
ipc::MergeParentLoadInfoForwarder(aLoadInfoForwarder, mLoadInfo);
516
517
mIsFromCache = aIsFromCache;
518
mIsRacing = aIsRacing;
519
mCacheEntryAvailable = aCacheEntryAvailable;
520
mCacheEntryId = aCacheEntryId;
521
mCacheFetchCount = aCacheFetchCount;
522
mCacheExpirationTime = aCacheExpirationTime;
523
mCachedCharset = aCachedCharset;
524
mSelfAddr = aSelfAddr;
525
mPeerAddr = aPeerAddr;
526
527
mAvailableCachedAltDataType = aAltDataType;
528
mDeliveringAltData = aDeliveringAltData;
529
mAltDataLength = aAltDataLen;
530
mResolvedByTRR = aIsResolvedByTRR;
531
532
SetApplyConversion(aApplyConversion);
533
534
mAfterOnStartRequestBegun = true;
535
536
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
537
538
mCacheKey = aCacheKey;
539
540
// replace our request headers with what actually got sent in the parent
541
mRequestHead.SetHeaders(aRequestHeaders);
542
543
// Note: this is where we would notify "http-on-examine-response" observers.
544
// We have deliberately disabled this for child processes (see bug 806753)
545
//
546
// gHttpHandler->OnExamineResponse(this);
547
548
mTracingEnabled = false;
549
550
ResourceTimingStructArgsToTimingsStruct(aTiming, mTransactionTimings);
551
552
mAllRedirectsSameOrigin = aAllRedirectsSameOrigin;
553
554
mMultiPartID = aMultiPartID;
555
mIsLastPartOfMultiPart = aIsLastPartOfMultiPart;
556
557
DoOnStartRequest(this, nullptr);
558
}
559
560
mozilla::ipc::IPCResult HttpChannelChild::RecvOnTransportAndData(
561
const nsresult& aChannelStatus, const nsresult& aTransportStatus,
562
const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
563
AUTO_PROFILER_LABEL("HttpChannelChild::RecvOnTransportAndData", NETWORK);
564
LOG(("HttpChannelChild::RecvOnTransportAndData [this=%p]\n", this));
565
566
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
567
this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
568
aTransportStatus, aOffset, aCount, aData]() {
569
self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
570
aCount, aData);
571
}));
572
return IPC_OK();
573
}
574
575
mozilla::ipc::IPCResult HttpChannelChild::RecvOnStopRequest(
576
const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
577
const TimeStamp& aLastActiveTabOptHit,
578
const nsHttpHeaderArray& aResponseTrailers) {
579
AUTO_PROFILER_LABEL("HttpChannelChild::RecvOnStopRequest", NETWORK);
580
LOG(("HttpChannelChild::RecvOnStopRequest [this=%p]\n", this));
581
582
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
583
this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
584
aResponseTrailers]() {
585
self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers);
586
}));
587
return IPC_OK();
588
}
589
590
mozilla::ipc::IPCResult HttpChannelChild::RecvOnAfterLastPart(
591
const nsresult& aStatus) {
592
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
593
this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
594
self->OnAfterLastPart(aStatus);
595
}));
596
return IPC_OK();
597
}
598
599
void HttpChannelChild::OnAfterLastPart(const nsresult& aStatus) {
600
if (mOnStopRequestCalled) {
601
return;
602
}
603
mOnStopRequestCalled = true;
604
605
// notify "http-on-stop-connect" observers
606
gHttpHandler->OnStopRequest(this);
607
608
ReleaseListeners();
609
610
// If a preferred alt-data type was set, the parent would hold a reference to
611
// the cache entry in case the child calls openAlternativeOutputStream().
612
// (see nsHttpChannel::OnStopRequest)
613
if (!mPreferredCachedAltDataTypes.IsEmpty()) {
614
mAltDataCacheEntryAvailable = mCacheEntryAvailable;
615
}
616
mCacheEntryAvailable = false;
617
618
if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
619
CleanupBackgroundChannel();
620
621
if (mLoadFlags & LOAD_DOCUMENT_URI) {
622
// Keep IPDL channel open, but only for updating security info.
623
// If IPDL is already closed, then do nothing.
624
if (CanSend()) {
625
mKeptAlive = true;
626
SendDocumentChannelCleanup(true);
627
}
628
} else {
629
// The parent process will respond by sending a DeleteSelf message and
630
// making sure not to send any more messages after that.
631
TrySendDeletingChannel();
632
}
633
}
634
635
class SyntheticDiversionListener final : public nsIStreamListener {
636
RefPtr<HttpChannelChild> mChannel;
637
638
~SyntheticDiversionListener() = default;
639
640
public:
641
explicit SyntheticDiversionListener(HttpChannelChild* aChannel)
642
: mChannel(aChannel) {
643
MOZ_ASSERT(mChannel);
644
}
645
646
NS_IMETHOD
647
OnStartRequest(nsIRequest* aRequest) override {
648
MOZ_ASSERT_UNREACHABLE(
649
"SyntheticDiversionListener should never see OnStartRequest");
650
return NS_OK;
651
}
652
653
NS_IMETHOD
654
OnStopRequest(nsIRequest* aRequest, nsresult aStatus) override {
655
if (mChannel->CanSend()) {
656
mChannel->SendDivertOnStopRequest(aStatus);
657
mChannel->SendDivertComplete();
658
}
659
return NS_OK;
660
}
661
662
NS_IMETHOD
663
OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
664
uint64_t aOffset, uint32_t aCount) override {
665
if (!mChannel->CanSend()) {
666
aRequest->Cancel(NS_ERROR_ABORT);
667
return NS_ERROR_ABORT;
668
}
669
670
nsAutoCString data;
671
nsresult rv = NS_ConsumeStream(aInputStream, aCount, data);
672
if (NS_WARN_IF(NS_FAILED(rv))) {
673
aRequest->Cancel(rv);
674
return rv;
675
}
676
677
mChannel->SendDivertOnDataAvailable(data, aOffset, aCount);
678
return NS_OK;
679
}
680
681
NS_DECL_ISUPPORTS
682
};
683
684
NS_IMPL_ISUPPORTS(SyntheticDiversionListener, nsIStreamListener);
685
686
void HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest,
687
nsISupports* aContext) {
688
nsresult rv;
689
690
LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this));
691
692
// mListener could be null if the redirect setup is not completed.
693
MOZ_ASSERT(mListener || mOnStartRequestCalled);
694
if (!mListener) {
695
Cancel(NS_ERROR_FAILURE);
696
return;
697
}
698
699
if (mSynthesizedResponsePump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
700
mSynthesizedResponsePump->PeekStream(CallTypeSniffers,
701
static_cast<nsIChannel*>(this));
702
}
703
704
if (mListener) {
705
nsCOMPtr<nsIStreamListener> listener(mListener);
706
mOnStartRequestCalled = true;
707
rv = listener->OnStartRequest(aRequest);
708
} else {
709
rv = NS_ERROR_UNEXPECTED;
710
}
711
mOnStartRequestCalled = true;
712
713
if (NS_FAILED(rv)) {
714
Cancel(rv);
715
return;
716
}
717
718
if (mDivertingToParent) {
719
mListener = nullptr;
720
mCompressListener = nullptr;
721
if (mLoadGroup) {
722
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
723
}
724
725
// If the response has been synthesized in the child, then we are going
726
// be getting OnDataAvailable and OnStopRequest from the synthetic
727
// stream pump. We need to forward these back to the parent diversion
728
// listener.
729
if (mSynthesizedResponse) {
730
mListener = new SyntheticDiversionListener(this);
731
}
732
733
return;
734
}
735
736
nsCOMPtr<nsIStreamListener> listener;
737
rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
738
if (NS_FAILED(rv)) {
739
Cancel(rv);
740
} else if (listener) {
741
mListener = listener;
742
mCompressListener = listener;
743
}
744
}
745
746
void HttpChannelChild::ProcessOnTransportAndData(
747
const nsresult& aChannelStatus, const nsresult& aTransportStatus,
748
const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
749
LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
750
MOZ_ASSERT(OnSocketThread());
751
MOZ_ASSERT(
752
!mMultiPartID,
753
"Should only send ODA on the main-thread channel when using multi-part!");
754
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
755
"Should not be receiving any more callbacks from parent!");
756
mEventQ->RunOrEnqueue(
757
new ChannelFunctionEvent(
758
[self = UnsafePtr<HttpChannelChild>(this)]() {
759
return self->GetODATarget();
760
},
761
[self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
762
aTransportStatus, aOffset, aCount, aData]() {
763
self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
764
aCount, aData);
765
}),
766
mDivertingToParent);
767
}
768
769
void HttpChannelChild::MaybeDivertOnData(const nsCString& aData,
770
const uint64_t& aOffset,
771
const uint32_t& aCount) {
772
LOG(("HttpChannelChild::MaybeDivertOnData [this=%p]", this));
773
774
if (mDivertingToParent) {
775
SendDivertOnDataAvailable(aData, aOffset, aCount);
776
}
777
}
778
779
void HttpChannelChild::OnTransportAndData(const nsresult& aChannelStatus,
780
const nsresult& aTransportStatus,
781
const uint64_t& aOffset,
782
const uint32_t& aCount,
783
const nsCString& aData) {
784
LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
785
786
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
787
mStatus = aChannelStatus;
788
}
789
790
// For diversion to parent, just SendDivertOnDataAvailable.
791
if (mDivertingToParent) {
792
MOZ_ASSERT(NS_IsMainThread());
793
MOZ_RELEASE_ASSERT(
794
!mFlushedForDiversion,
795
"Should not be processing any more callbacks from parent!");
796
797
SendDivertOnDataAvailable(aData, aOffset, aCount);
798
return;
799
}
800
801
if (mCanceled) {
802
return;
803
}
804
805
if (mUnknownDecoderInvolved) {
806
LOG(("UnknownDecoder is involved queue OnDataAvailable call. [this=%p]",
807
this));
808
MOZ_ASSERT(NS_IsMainThread());
809
mUnknownDecoderEventQ.AppendElement(
810
MakeUnique<NeckoTargetChannelFunctionEvent>(
811
this,
812
[self = UnsafePtr<HttpChannelChild>(this), aData, aOffset,
813
aCount]() { self->MaybeDivertOnData(aData, aOffset, aCount); }));
814
}
815
816
// Hold queue lock throughout all three calls, else we might process a later
817
// necko msg in between them.
818
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
819
820
int64_t progressMax;
821
if (NS_FAILED(GetContentLength(&progressMax))) {
822
progressMax = -1;
823
}
824
825
const int64_t progress = aOffset + aCount;
826
827
// OnTransportAndData will be run on retargeted thread if applicable, however
828
// OnStatus/OnProgress event can only be fired on main thread. We need to
829
// dispatch the status/progress event handling back to main thread with the
830
// appropriate event target for networking.
831
if (NS_IsMainThread()) {
832
DoOnStatus(this, aTransportStatus);
833
DoOnProgress(this, progress, progressMax);
834
} else {
835
RefPtr<HttpChannelChild> self = this;
836
nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
837
MOZ_ASSERT(neckoTarget);
838
839
DebugOnly<nsresult> rv = neckoTarget->Dispatch(
840
NS_NewRunnableFunction(
841
"net::HttpChannelChild::OnTransportAndData",
842
[self, aTransportStatus, progress, progressMax]() {
843
self->DoOnStatus(self, aTransportStatus);
844
self->DoOnProgress(self, progress, progressMax);
845
}),
846
NS_DISPATCH_NORMAL);
847
MOZ_ASSERT(NS_SUCCEEDED(rv));
848
}
849
850
// OnDataAvailable
851
//
852
// NOTE: the OnDataAvailable contract requires the client to read all the data
853
// in the inputstream. This code relies on that ('data' will go away after
854
// this function). Apparently the previous, non-e10s behavior was to actually
855
// support only reading part of the data, allowing later calls to read the
856
// rest.
857
nsCOMPtr<nsIInputStream> stringStream;
858
nsresult rv =
859
NS_NewByteInputStream(getter_AddRefs(stringStream),
860
MakeSpan(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
861
if (NS_FAILED(rv)) {
862
Cancel(rv);
863
return;
864
}
865
866
DoOnDataAvailable(this, nullptr, stringStream, aOffset, aCount);
867
stringStream->Close();
868
869
if (NeedToReportBytesRead()) {
870
mUnreportBytesRead += aCount;
871
if (mUnreportBytesRead >= gHttpHandler->SendWindowSize() >> 2) {
872
if (NS_IsMainThread()) {
873
Unused << SendBytesRead(mUnreportBytesRead);
874
} else {
875
// PHttpChannel connects to the main thread
876
RefPtr<HttpChannelChild> self = this;
877
int32_t bytesRead = mUnreportBytesRead;
878
nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
879
MOZ_ASSERT(neckoTarget);
880
881
DebugOnly<nsresult> rv = neckoTarget->Dispatch(
882
NS_NewRunnableFunction("net::HttpChannelChild::SendBytesRead",
883
[self, bytesRead]() {
884
Unused << self->SendBytesRead(bytesRead);
885
}),
886
NS_DISPATCH_NORMAL);
887
MOZ_ASSERT(NS_SUCCEEDED(rv));
888
}
889
mUnreportBytesRead = 0;
890
}
891
}
892
}
893
894
bool HttpChannelChild::NeedToReportBytesRead() {
895
if (mCacheNeedToReportBytesReadInitialized) {
896
// No need to send SendRecvBytes when diversion starts since the parent
897
// process will suspend for diversion triggered in during OnStrartRequest at
898
// child side, which is earlier. Parent will take over the flow control
899
// after the diverting starts. Sending |SendBytesRead| is redundant.
900
return mNeedToReportBytesRead && !mDivertingToParent;
901
}
902
903
// Might notify parent for partial cache, and the IPC message is ignored by
904
// parent.
905
int64_t contentLength = -1;
906
if (gHttpHandler->SendWindowSize() == 0 || mIsFromCache ||
907
NS_FAILED(GetContentLength(&contentLength)) ||
908
contentLength < gHttpHandler->SendWindowSize()) {
909
mNeedToReportBytesRead = false;
910
}
911
912
mCacheNeedToReportBytesReadInitialized = true;
913
return mNeedToReportBytesRead;
914
}
915
916
void HttpChannelChild::DoOnStatus(nsIRequest* aRequest, nsresult status) {
917
LOG(("HttpChannelChild::DoOnStatus [this=%p]\n", this));
918
MOZ_ASSERT(NS_IsMainThread());
919
920
if (mCanceled) return;
921
922
// cache the progress sink so we don't have to query for it each time.
923
if (!mProgressSink) GetCallback(mProgressSink);
924
925
// block status/progress after Cancel or OnStopRequest has been called,
926
// or if channel has LOAD_BACKGROUND set.
927
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
928
!(mLoadFlags & LOAD_BACKGROUND)) {
929
nsAutoCString host;
930
mURI->GetHost(host);
931
mProgressSink->OnStatus(aRequest, nullptr, status,
932
NS_ConvertUTF8toUTF16(host).get());
933
}
934
}
935
936
void HttpChannelChild::DoOnProgress(nsIRequest* aRequest, int64_t progress,
937
int64_t progressMax) {
938
LOG(("HttpChannelChild::DoOnProgress [this=%p]\n", this));
939
MOZ_ASSERT(NS_IsMainThread());
940
941
if (mCanceled) return;
942
943
// cache the progress sink so we don't have to query for it each time.
944
if (!mProgressSink) GetCallback(mProgressSink);
945
946
// block status/progress after Cancel or OnStopRequest has been called,
947
// or if channel has LOAD_BACKGROUND set.
948
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
949
// OnProgress
950
//
951
if (progress > 0) {
952
mProgressSink->OnProgress(aRequest, nullptr, progress, progressMax);
953
}
954
}
955
}
956
957
void HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest,
958
nsISupports* aContext,
959
nsIInputStream* aStream,
960
uint64_t aOffset, uint32_t aCount) {
961
AUTO_PROFILER_LABEL("HttpChannelChild::DoOnDataAvailable", NETWORK);
962
LOG(("HttpChannelChild::DoOnDataAvailable [this=%p]\n", this));
963
if (mCanceled) return;
964
965
if (mListener) {
966
nsCOMPtr<nsIStreamListener> listener(mListener);
967
nsresult rv = listener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
968
if (NS_FAILED(rv)) {
969
CancelOnMainThread(rv);
970
}
971
}
972
}
973
974
void HttpChannelChild::ProcessOnStopRequest(
975
const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
976
const nsHttpHeaderArray& aResponseTrailers) {
977
LOG(("HttpChannelChild::ProcessOnStopRequest [this=%p]\n", this));
978
MOZ_ASSERT(OnSocketThread());
979
MOZ_ASSERT(
980
!mMultiPartID,
981
"Should only send ODA on the main-thread channel when using multi-part!");
982
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
983
"Should not be receiving any more callbacks from parent!");
984
985
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
986
this,
987
[self = UnsafePtr<HttpChannelChild>(this),
988
aChannelStatus, aTiming, aResponseTrailers]() {
989
self->OnStopRequest(aChannelStatus, aTiming,
990
aResponseTrailers);
991
}),
992
mDivertingToParent);
993
}
994
995
void HttpChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus) {
996
LOG(
997
("HttpChannelChild::MaybeDivertOnStop [this=%p, "
998
"mDivertingToParent=%d status=%" PRIx32 "]",
999
this, static_cast<bool>(mDivertingToParent),
1000
static_cast<uint32_t>(aChannelStatus)));
1001
if (mDivertingToParent) {
1002
SendDivertOnStopRequest(aChannelStatus);
1003
}
1004
}
1005
1006
void HttpChannelChild::OnStopRequest(
1007
const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
1008
const nsHttpHeaderArray& aResponseTrailers) {
1009
LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
1010
static_cast<uint32_t>(aChannelStatus)));
1011
MOZ_ASSERT(NS_IsMainThread());
1012
1013
// If this channel was aborted by ActorDestroy, then there may be other
1014
// OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
1015
// be handled. In that case we just ignore them to avoid calling the listener
1016
// twice.
1017
if (mOnStopRequestCalled && mIPCActorDeleted) {
1018
return;
1019
}
1020
1021
if (mDivertingToParent) {
1022
MOZ_RELEASE_ASSERT(
1023
!mFlushedForDiversion,
1024
"Should not be processing any more callbacks from parent!");
1025
1026
SendDivertOnStopRequest(aChannelStatus);
1027
return;
1028
}
1029
1030
if (mUnknownDecoderInvolved) {
1031
LOG(("UnknownDecoder is involved queue OnStopRequest call. [this=%p]",
1032
this));
1033
MOZ_ASSERT(NS_IsMainThread());
1034
mUnknownDecoderEventQ.AppendElement(
1035
MakeUnique<NeckoTargetChannelFunctionEvent>(
1036
this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus]() {
1037
self->MaybeDivertOnStop(aChannelStatus);
1038
}));
1039
}
1040
1041
nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
1042
if (conv) {
1043
conv->GetDecodedDataLength(&mDecodedBodySize);
1044
}
1045
1046
ResourceTimingStructArgsToTimingsStruct(aTiming, mTransactionTimings);
1047
1048
// Do not overwrite or adjust the original mAsyncOpenTime by timing.fetchStart
1049
// We must use the original child process time in order to account for child
1050
// side work and IPC transit overhead.
1051
// XXX: This depends on TimeStamp being equivalent across processes.
1052
// This is true for modern hardware but for older platforms it is not always
1053
// true.
1054
1055
mRedirectStartTimeStamp = aTiming.redirectStart();
1056
mRedirectEndTimeStamp = aTiming.redirectEnd();
1057
mTransferSize = aTiming.transferSize();
1058
mEncodedBodySize = aTiming.encodedBodySize();
1059
mProtocolVersion = aTiming.protocolVersion();
1060
1061
mCacheReadStart = aTiming.cacheReadStart();
1062
mCacheReadEnd = aTiming.cacheReadEnd();
1063
1064
#ifdef MOZ_GECKO_PROFILER
1065
if (profiler_can_accept_markers()) {
1066
int32_t priority = PRIORITY_NORMAL;
1067
GetPriority(&priority);
1068
profiler_add_network_marker(
1069
mURI, priority, mChannelId, NetworkLoadType::LOAD_STOP,
1070
mLastStatusReported, TimeStamp::Now(), mTransferSize, kCacheUnknown,
1071
&mTransactionTimings, nullptr, std::move(mSource));
1072
}
1073
#endif
1074
1075
mResponseTrailers = new nsHttpHeaderArray(aResponseTrailers);
1076
1077
DoPreOnStopRequest(aChannelStatus);
1078
1079
{ // We must flush the queue before we Send__delete__
1080
// (although we really shouldn't receive any msgs after OnStop),
1081
// so make sure this goes out of scope before then.
1082
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
1083
1084
DoOnStopRequest(this, aChannelStatus, nullptr);
1085
// DoOnStopRequest() calls ReleaseListeners()
1086
}
1087
1088
// If unknownDecoder is involved and the received content is short we will
1089
// know whether we need to divert to parent only after OnStopRequest of the
1090
// listeners chain is called in DoOnStopRequest. At that moment
1091
// unknownDecoder will call OnStartRequest of the real listeners of the
1092
// channel including the OnStopRequest of UrlLoader which decides whether we
1093
// need to divert to parent.
1094
// If we are diverting to parent we should not do a cleanup.
1095
if (mDivertingToParent) {
1096
LOG(
1097
("HttpChannelChild::OnStopRequest - We are diverting to parent, "
1098
"postpone cleaning up."));
1099
return;
1100
}
1101
1102
// If we're a multi-part stream, and this wasn't the last part, then don't
1103
// cleanup yet, as we're expecting more parts.
1104
if (mMultiPartID && !mIsLastPartOfMultiPart) {
1105
LOG(
1106
("HttpChannelChild::OnStopRequest - Expecting future parts on a "
1107
"multipart channel"
1108
"postpone cleaning up."));
1109
return;
1110
}
1111
1112
CleanupBackgroundChannel();
1113
1114
// If there is a possibility we might want to write alt data to the cache
1115
// entry, we keep the channel alive. We still send the DocumentChannelCleanup
1116
// message but request the cache entry to be kept by the parent.
1117
// If the channel has failed, the cache entry is in a non-writtable state and
1118
// we want to release it to not block following consumers.
1119
if (NS_SUCCEEDED(aChannelStatus) && !mPreferredCachedAltDataTypes.IsEmpty()) {
1120
mKeptAlive = true;
1121
SendDocumentChannelCleanup(false); // don't clear cache entry
1122
return;
1123
}
1124
1125
if (mLoadFlags & LOAD_DOCUMENT_URI) {
1126
// Keep IPDL channel open, but only for updating security info.
1127
// If IPDL is already closed, then do nothing.
1128
if (CanSend()) {
1129
mKeptAlive = true;
1130
SendDocumentChannelCleanup(true);
1131
}
1132
} else {
1133
// The parent process will respond by sending a DeleteSelf message and
1134
// making sure not to send any more messages after that.
1135
TrySendDeletingChannel();
1136
}
1137
}
1138
1139
void HttpChannelChild::DoPreOnStopRequest(nsresult aStatus) {
1140
AUTO_PROFILER_LABEL("HttpChannelChild::DoPreOnStopRequest", NETWORK);
1141
LOG(("HttpChannelChild::DoPreOnStopRequest [this=%p status=%" PRIx32 "]\n",
1142
this, static_cast<uint32_t>(aStatus)));
1143
mIsPending = false;
1144
1145
MaybeCallSynthesizedCallback();
1146
1147
MaybeReportTimingData();
1148
1149
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
1150
mStatus = aStatus;
1151
}
1152
1153
CollectOMTTelemetry();
1154
}
1155
1156
void HttpChannelChild::CollectOMTTelemetry() {
1157
MOZ_ASSERT(NS_IsMainThread());
1158
1159
// Only collect telemetry for HTTP channel that is loaded successfully and
1160
// completely.
1161
if (mCanceled || NS_FAILED(mStatus)) {
1162
return;
1163
}
1164
1165
// Use content policy type to accumulate data by usage.
1166
nsContentPolicyType type = mLoadInfo ? mLoadInfo->InternalContentPolicyType()
1167
: nsIContentPolicy::TYPE_OTHER;
1168
1169
nsAutoCString key(NS_CP_ContentTypeName(type));
1170
1171
Telemetry::AccumulateCategoricalKeyed(key, mOMTResult);
1172
}
1173
1174
void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
1175
nsresult aChannelStatus,
1176
nsISupports* aContext) {
1177
AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
1178
LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
1179
MOZ_ASSERT(NS_IsMainThread());
1180
MOZ_ASSERT(!mIsPending);
1181
1182
auto checkForBlockedContent = [&]() {
1183
// NB: We use aChannelStatus here instead of mStatus because if there was an
1184
// nsCORSListenerProxy on this request, it will override the tracking
1185
// protection's return value.
1186
if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
1187
aChannelStatus) ||
1188
aChannelStatus == NS_ERROR_MALWARE_URI ||
1189
aChannelStatus == NS_ERROR_UNWANTED_URI ||
1190
aChannelStatus == NS_ERROR_BLOCKED_URI ||
1191
aChannelStatus == NS_ERROR_HARMFUL_URI ||
1192
aChannelStatus == NS_ERROR_PHISHING_URI) {
1193
nsCString list, provider, fullhash;
1194
1195
nsresult rv = GetMatchedList(list);
1196
NS_ENSURE_SUCCESS_VOID(rv);
1197
1198
rv = GetMatchedProvider(provider);
1199
NS_ENSURE_SUCCESS_VOID(rv);
1200
1201
rv = GetMatchedFullHash(fullhash);
1202
NS_ENSURE_SUCCESS_VOID(rv);
1203
1204
UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
1205
provider, fullhash);
1206
}
1207
};
1208
checkForBlockedContent();
1209
1210
// See bug 1587686. If the redirect setup is not completed, the post-redirect
1211
// channel will be not opened and mListener will be null.
1212
MOZ_ASSERT(mListener || !mWasOpened);
1213
if (!mListener) {
1214
return;
1215
}
1216
1217
MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
1218
1219
if (mListener) {
1220
nsCOMPtr<nsIStreamListener> listener(mListener);
1221
mOnStopRequestCalled = true;
1222
listener->OnStopRequest(aRequest, mStatus);
1223
}
1224
mOnStopRequestCalled = true;
1225
1226
// If we're a multi-part stream, and this wasn't the last part, then don't
1227
// cleanup yet, as we're expecting more parts.
1228
if (mMultiPartID && !mIsLastPartOfMultiPart) {
1229
LOG(
1230
("HttpChannelChild::DoOnStopRequest - Expecting future parts on a "
1231
"multipart channel"
1232
"not releasing listeners."));
1233
mOnStopRequestCalled = false;
1234
mOnStartRequestCalled = false;
1235
return;
1236
}
1237
1238
// notify "http-on-stop-connect" observers
1239
gHttpHandler->OnStopRequest(this);
1240
1241
ReleaseListeners();
1242
1243
// If a preferred alt-data type was set, the parent would hold a reference to
1244
// the cache entry in case the child calls openAlternativeOutputStream().
1245
// (see nsHttpChannel::OnStopRequest)
1246
if (!mPreferredCachedAltDataTypes.IsEmpty()) {
1247
mAltDataCacheEntryAvailable = mCacheEntryAvailable;
1248
}
1249
mCacheEntryAvailable = false;
1250
1251
if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
1252
}
1253
1254
mozilla::ipc::IPCResult HttpChannelChild::RecvOnProgress(
1255
const int64_t& aProgress, const int64_t& aProgressMax) {
1256
LOG(("HttpChannelChild::RecvOnProgress [this=%p]\n", this));
1257
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1258
this,
1259
[self = UnsafePtr<HttpChannelChild>(this), aProgress, aProgressMax]() {
1260
AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1261
self->DoOnProgress(self, aProgress, aProgressMax);
1262
}));
1263
return IPC_OK();
1264
}
1265
1266
mozilla::ipc::IPCResult HttpChannelChild::RecvOnStatus(
1267
const nsresult& aStatus) {
1268
LOG(("HttpChannelChild::RecvOnStatus [this=%p]\n", this));
1269
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1270
this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1271
AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1272
self->DoOnStatus(self, aStatus);
1273
}));
1274
return IPC_OK();
1275
}
1276
1277
mozilla::ipc::IPCResult HttpChannelChild::RecvFailedAsyncOpen(
1278
const nsresult& aStatus) {
1279
LOG(("HttpChannelChild::RecvFailedAsyncOpen [this=%p]\n", this));
1280
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1281
this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1282
self->FailedAsyncOpen(aStatus);
1283
}));
1284
return IPC_OK();
1285
}
1286
1287
// We need to have an implementation of this function just so that we can keep
1288
// all references to mCallOnResume of type HttpChannelChild: it's not OK in C++
1289
// to set a member function ptr to a base class function.
1290
void HttpChannelChild::HandleAsyncAbort() {
1291
HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
1292
1293
// Ignore all the messages from background channel after channel aborted.
1294
CleanupBackgroundChannel();
1295
}
1296
1297
void HttpChannelChild::FailedAsyncOpen(const nsresult& status) {
1298
LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n", this,
1299
static_cast<uint32_t>(status)));
1300
MOZ_ASSERT(NS_IsMainThread());
1301
1302
// Might be called twice in race condition in theory.
1303
// (one by RecvFailedAsyncOpen, another by
1304
// HttpBackgroundChannelChild::ActorFailed)
1305
if (mOnStartRequestCalled) {
1306
return;
1307
}
1308
1309
if (NS_SUCCEEDED(mStatus)) {
1310
mStatus = status;
1311
}
1312
1313
// We're already being called from IPDL, therefore already "async"
1314
HandleAsyncAbort();
1315
1316
if (CanSend()) {
1317
TrySendDeletingChannel();
1318
}
1319
}
1320
1321
void HttpChannelChild::CleanupBackgroundChannel() {
1322
MutexAutoLock lock(mBgChildMutex);
1323
1324
AUTO_PROFILER_LABEL("HttpChannelChild::CleanupBackgroundChannel", NETWORK);
1325
LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
1326
this, mBgChild.get()));
1327
1328
mBgInitFailCallback = nullptr;
1329
1330
if (!mBgChild) {
1331
return;
1332
}
1333
1334
RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild.forget();
1335
1336
MOZ_RELEASE_ASSERT(gSocketTransportService);
1337
if (!OnSocketThread()) {
1338
gSocketTransportService->Dispatch(
1339
NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
1340
bgChild,
1341
&HttpBackgroundChannelChild::OnChannelClosed),
1342
NS_DISPATCH_NORMAL);
1343
} else {
1344
bgChild->OnChannelClosed();
1345
}
1346
}
1347
1348
void HttpChannelChild::DoNotifyListenerCleanup() {
1349
LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
1350
1351
if (mInterceptListener) {
1352
mInterceptListener->Cleanup();
1353
mInterceptListener = nullptr;
1354
}
1355
1356
MaybeCallSynthesizedCallback();
1357
}
1358
1359
void HttpChannelChild::DoAsyncAbort(nsresult aStatus) {
1360
Unused << AsyncAbort(aStatus);
1361
}
1362
1363
mozilla::ipc::IPCResult HttpChannelChild::RecvDeleteSelf() {
1364
LOG(("HttpChannelChild::RecvDeleteSelf [this=%p]\n", this));
1365
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1366
this,
1367
[self = UnsafePtr<HttpChannelChild>(this)]() { self->DeleteSelf(); }));
1368
return IPC_OK();
1369
}
1370
1371
HttpChannelChild::OverrideRunnable::OverrideRunnable(
1372
HttpChannelChild* aChannel, HttpChannelChild* aNewChannel,
1373
InterceptStreamListener* aListener, nsIInputStream* aInput,
1374
nsIInterceptedBodyCallback* aCallback, nsAutoPtr<nsHttpResponseHead>& aHead,
1375
nsICacheInfoChannel* aCacheInfo)
1376
: Runnable("net::HttpChannelChild::OverrideRunnable") {
1377
mChannel = aChannel;
1378
mNewChannel = aNewChannel;
1379
mListener = aListener;
1380
mInput = aInput;
1381
mCallback = aCallback;
1382
mHead = aHead;
1383
mSynthesizedCacheInfo = aCacheInfo;
1384
}
1385
1386
void HttpChannelChild::OverrideRunnable::OverrideWithSynthesizedResponse() {
1387
if (mNewChannel) {
1388
mNewChannel->OverrideWithSynthesizedResponse(
1389
mHead, mInput, mCallback, mListener, mSynthesizedCacheInfo);
1390
}
1391
}
1392
1393
NS_IMETHODIMP
1394
HttpChannelChild::OverrideRunnable::Run() {
1395
// Check to see if the channel was canceled in the middle of the redirect.
1396
nsresult rv = NS_OK;
1397
Unused << mChannel->GetStatus(&rv);
1398
if (NS_FAILED(rv)) {
1399
if (mCallback) {
1400
mCallback->BodyComplete(rv);
1401
mCallback = nullptr;
1402
}
1403
mChannel->CleanupRedirectingChannel(rv);
1404
if (mNewChannel) {
1405
mNewChannel->Cancel(rv);
1406
}
1407
return NS_OK;
1408
}
1409
1410
bool ret = mChannel->Redirect3Complete(this);
1411
1412
// If the method returns false, it means the IPDL connection is being
1413
// asyncly torn down and reopened, and OverrideWithSynthesizedResponse
1414
// will be called later from FinishInterceptedRedirect. This object will
1415
// be assigned to HttpChannelChild::mOverrideRunnable in order to do so.
1416
// If it is true, we can call the method right now.
1417
if (ret) {
1418
OverrideWithSynthesizedResponse();
1419
}
1420
1421
return NS_OK;
1422
}
1423
1424
mozilla::ipc::IPCResult HttpChannelChild::RecvFinishInterceptedRedirect() {
1425
// Hold a ref to this to keep it from being deleted by Send__delete__()
1426
RefPtr<HttpChannelChild> self(this);
1427
Send__delete__(this);
1428
1429
{
1430
// Reset the event target since the IPC actor is about to be destroyed.
1431
// Following channel event should be handled on main thread.
1432
MutexAutoLock lock(mEventTargetMutex);
1433
mNeckoTarget = nullptr;
1434
}
1435
1436
// The IPDL connection was torn down by a interception logic in
1437
// CompleteRedirectSetup, and we need to call FinishInterceptedRedirect.
1438
nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
1439
MOZ_ASSERT(neckoTarget);
1440
1441
Unused << neckoTarget->Dispatch(
1442
NewRunnableMethod("net::HttpChannelChild::FinishInterceptedRedirect",
1443
this, &HttpChannelChild::FinishInterceptedRedirect),
1444
NS_DISPATCH_NORMAL);
1445
1446
return IPC_OK();
1447
}
1448
1449
void HttpChannelChild::DeleteSelf() { Send__delete__(this); }
1450
1451
void HttpChannelChild::DoNotifyListener() {
1452
LOG(("HttpChannelChild::DoNotifyListener this=%p", this));
1453
MOZ_ASSERT(NS_IsMainThread());
1454
1455
// In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
1456
// LOAD_ONLY_IF_MODIFIED) we want to set mAfterOnStartRequestBegun to true
1457
// before notifying listener.
1458
if (!mAfterOnStartRequestBegun) {
1459
mAfterOnStartRequestBegun = true;
1460
}
1461
1462
if (mListener && !mOnStartRequestCalled) {
1463
nsCOMPtr<nsIStreamListener> listener = mListener;
1464
mOnStartRequestCalled = true; // avoid reentrancy bugs by setting this now
1465
listener->OnStartRequest(this);
1466
}
1467
mOnStartRequestCalled = true;
1468
1469
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1470
this, [self = UnsafePtr<HttpChannelChild>(this)] {
1471
self->ContinueDoNotifyListener();
1472
}));
1473
}
1474
1475
void HttpChannelChild::ContinueDoNotifyListener() {
1476
LOG(("HttpChannelChild::ContinueDoNotifyListener this=%p", this));
1477
MOZ_ASSERT(NS_IsMainThread());
1478
1479
// Make sure mIsPending is set to false. At this moment we are done from
1480
// the point of view of our consumer and we have to report our self
1481
// as not-pending.
1482
mIsPending = false;
1483
1484
if (mListener && !mOnStopRequestCalled) {
1485
nsCOMPtr<nsIStreamListener> listener = mListener;
1486
mOnStopRequestCalled = true;
1487
listener->OnStopRequest(this, mStatus);
1488
}
1489
mOnStopRequestCalled = true;
1490
1491
// notify "http-on-stop-request" observers
1492
gHttpHandler->OnStopRequest(this);
1493
1494
// This channel has finished its job, potentially release any tail-blocked
1495
// requests with this.
1496
RemoveAsNonTailRequest();
1497
1498
// We have to make sure to drop the references to listeners and callbacks
1499
// no longer needed.
1500
ReleaseListeners();
1501
1502
DoNotifyListenerCleanup();
1503
1504
// If this is a navigation, then we must let the docshell flush the reports
1505
// to the console later. The LoadDocument() is pointing at the detached
1506
// document that started the navigation. We want to show the reports on the
1507
// new document. Otherwise the console is wiped and the user never sees
1508
// the information.
1509
if (!IsNavigation()) {
1510
if (mLoadGroup) {
1511
FlushConsoleReports(mLoadGroup);
1512
} else if (mLoadInfo) {
1513
RefPtr<dom::Document> doc;
1514
mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
1515
FlushConsoleReports(doc);
1516
}
1517
}
1518
}
1519
1520
void HttpChannelChild::FinishInterceptedRedirect() {
1521
nsresult rv;
1522
MOZ_ASSERT(!mInterceptedRedirectContext, "the context should be null!");
1523
rv = AsyncOpen(mInterceptedRedirectListener);
1524
1525
mInterceptedRedirectListener = nullptr;
1526
mInterceptedRedirectContext = nullptr;
1527
1528
if (mInterceptingChannel) {
1529
mInterceptingChannel->CleanupRedirectingChannel(rv);
1530
mInterceptingChannel = nullptr;
1531
}
1532
1533
if (mOverrideRunnable) {
1534
mOverrideRunnable->OverrideWithSynthesizedResponse();
1535
mOverrideRunnable = nullptr;
1536
}
1537
}
1538
1539
mozilla::ipc::IPCResult HttpChannelChild::RecvReportSecurityMessage(
1540
const nsString& messageTag, const nsString& messageCategory) {
1541
DebugOnly<nsresult> rv = AddSecurityMessage(messageTag, messageCategory);
1542
MOZ_ASSERT(NS_SUCCEEDED(rv));
1543
return IPC_OK();
1544
}
1545
1546
mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect1Begin(
1547
const uint32_t& aRegistrarId, const URIParams& aNewUri,
1548
const uint32_t& aNewLoadFlags, const uint32_t& aRedirectFlags,
1549
const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
1550
const nsHttpResponseHead& aResponseHead,
1551
const nsCString& aSecurityInfoSerialization, const uint64_t& aChannelId,
1552
const NetAddr& aOldPeerAddr, const ResourceTimingStructArgs& aTiming) {
1553
// TODO: handle security info
1554
LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this));
1555
// We set peer address of child to the old peer,
1556
// Then it will be updated to new peer in OnStartRequest
1557
mPeerAddr = aOldPeerAddr;
1558
1559
// Cookies headers should not be visible to the child process
1560
MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
1561
1562
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1563
this, [self = UnsafePtr<HttpChannelChild>(this), aRegistrarId, aNewUri,
1564
aNewLoadFlags, aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1565
aSecurityInfoSerialization, aChannelId, aTiming]() {
1566
self->Redirect1Begin(aRegistrarId, aNewUri, aNewLoadFlags,
1567
aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1568
aSecurityInfoSerialization, aChannelId, aTiming);
1569
}));
1570
return IPC_OK();
1571
}
1572
1573
nsresult HttpChannelChild::SetupRedirect(nsIURI* uri,
1574
const nsHttpResponseHead* responseHead,
1575
const uint32_t& redirectFlags,
1576
nsIChannel** outChannel) {
1577
LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
1578
1579
nsresult rv;
1580
nsCOMPtr<nsIIOService> ioService;
1581
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
1582
NS_ENSURE_SUCCESS(rv, rv);
1583
1584
nsCOMPtr<nsIChannel> newChannel;
1585
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
1586
CloneLoadInfoForRedirect(uri, redirectFlags);
1587
rv = NS_NewChannelInternal(getter_AddRefs(newChannel), uri, redirectLoadInfo,
1588
nullptr, // PerformanceStorage
1589
nullptr, // aLoadGroup
1590
nullptr, // aCallbacks
1591
nsIRequest::LOAD_NORMAL, ioService);
1592
NS_ENSURE_SUCCESS(rv, rv);
1593
1594
// We won't get OnStartRequest, set cookies here.
1595
mResponseHead = new nsHttpResponseHead(*responseHead);
1596
1597
bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(
1598
mResponseHead->Status(), mRequestHead.ParsedMethod());
1599
1600
rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET, redirectFlags);
1601
NS_ENSURE_SUCCESS(rv, rv);
1602
1603
nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
1604
do_QueryInterface(newChannel);
1605
if (httpChannelChild) {
1606
bool shouldUpgrade = false;
1607
auto channelChild = static_cast<HttpChannelChild*>(httpChannelChild.get());
1608
if (mShouldInterceptSubsequentRedirect) {
1609
// In the case where there was a synthesized response that caused a
1610
// redirection, we must force the new channel to intercept the request in
1611
// the parent before a network transaction is initiated.
1612
rv = httpChannelChild->ForceIntercepted(false, false);
1613
} else if (mRedirectMode == nsIHttpChannelInternal::REDIRECT_MODE_MANUAL &&
1614
((redirectFlags & (nsIChannelEventSink::REDIRECT_TEMPORARY |
1615
nsIChannelEventSink::REDIRECT_PERMANENT)) !=
1616
0) &&
1617
channelChild->ShouldInterceptURI(uri, shouldUpgrade)) {
1618
// In the case where the redirect mode is manual, we need to check whether
1619
// the post-redirect channel needs to be intercepted. If that is the
1620
// case, force the new channel to intercept the request in the parent
1621
// similar to the case above, but also remember that ShouldInterceptURI()
1622
// returned true to avoid calling it a second time.
1623
rv = httpChannelChild->ForceIntercepted(true, shouldUpgrade);
1624
}
1625
MOZ_ASSERT(NS_SUCCEEDED(rv));
1626
}
1627
1628
mRedirectChannelChild = do_QueryInterface(newChannel);
1629
newChannel.forget(outChannel);
1630
1631
return NS_OK;
1632
}
1633
1634
void HttpChannelChild::Redirect1Begin(
1635
const uint32_t& registrarId, const URIParams& newOriginalURI,
1636
const uint32_t& newLoadFlags, const uint32_t& redirectFlags,
1637
const ParentLoadInfoForwarderArgs& loadInfoForwarder,
1638
const nsHttpResponseHead& responseHead,
1639
const nsACString& securityInfoSerialization, const uint64_t& channelId,
1640
const ResourceTimingStructArgs& timing) {
1641
nsresult rv;
1642
1643
LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
1644
1645
ipc::MergeParentLoadInfoForwarder(loadInfoForwarder, mLoadInfo);
1646
1647
nsCOMPtr<nsIURI> uri = DeserializeURI(newOriginalURI);
1648
1649
ResourceTimingStructArgsToTimingsStruct(timing, mTransactionTimings);
1650
PROFILER_ADD_NETWORK_MARKER(
1651
mURI, mPriority, mChannelId, NetworkLoadType::LOAD_REDIRECT,
1652
mLastStatusReported, TimeStamp::Now(), 0, kCacheUnknown,
1653
&mTransactionTimings, uri, std::move(mSource));
1654
1655
if (!securityInfoSerialization.IsEmpty()) {
1656
rv = NS_DeserializeObject(securityInfoSerialization,
1657
getter_AddRefs(mSecurityInfo));
1658
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
1659
"Deserializing security info should not fail");
1660
}
1661
1662
nsCOMPtr<nsIChannel> newChannel;
1663
rv = SetupRedirect(uri, &responseHead, redirectFlags,
1664
getter_AddRefs(newChannel));
1665
1666
if (NS_SUCCEEDED(rv)) {
1667
MOZ_ALWAYS_SUCCEEDS(newChannel->SetLoadFlags(newLoadFlags));
1668
1669
if (mRedirectChannelChild) {
1670
// Set the channelId allocated in parent to the child instance
1671
nsCOMPtr<nsIHttpChannel> httpChannel =
1672
do_QueryInterface(mRedirectChannelChild);
1673
if (httpChannel) {
1674
rv = httpChannel->SetChannelId(channelId);
1675
MOZ_ASSERT(NS_SUCCEEDED(rv));
1676
}
1677
mRedirectChannelChild->ConnectParent(registrarId);
1678
}
1679
1680
nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
1681
MOZ_ASSERT(target);
1682
1683
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags,
1684
target);
1685
}
1686
1687
if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
1688
}
1689
1690
void HttpChannelChild::BeginNonIPCRedirect(
1691
nsIURI* responseURI, const nsHttpResponseHead* responseHead,
1692
bool aResponseRedirected) {
1693
LOG(("HttpChannelChild::BeginNonIPCRedirect [this=%p]\n", this));
1694
1695
// This method is only used by child-side service workers. It should not be
1696
// used by new code. We want to remove it in the future.
1697
MOZ_DIAGNOSTIC_ASSERT(mSynthesizedResponse);
1698
1699
// If the response has been redirected, propagate all the URLs to content.
1700
// Thus, the exact value of the redirect flag does not matter as long as it's
1701
// not REDIRECT_INTERNAL.
1702
const uint32_t redirectFlag = aResponseRedirected
1703
? nsIChannelEventSink::REDIRECT_TEMPORARY
1704
: nsIChannelEventSink::REDIRECT_INTERNAL;
1705
1706
nsCOMPtr<nsIChannel> newChannel;
1707
nsresult rv = SetupRedirect(responseURI, responseHead, redirectFlag,
1708
getter_AddRefs(newChannel));
1709
1710
if (NS_SUCCEEDED(rv)) {
1711
// Ensure that the new channel shares the original channel's security
1712
// information, since it won't be provided via IPC. In particular, if the
1713
// target of this redirect is a synthesized response that has its own
1714
// security info, the pre-redirect channel has already received it and it
1715
// must be propagated to the post-redirect channel.
1716
nsCOMPtr<nsIHttpChannelChild> channelChild = do_QueryInterface(newChannel);
1717
if (mSecurityInfo && channelChild) {
1718
HttpChannelChild* httpChannelChild =
1719
static_cast<HttpChannelChild*>(channelChild.get());
1720
httpChannelChild->OverrideSecurityInfoForNonIPCRedirect(mSecurityInfo);
1721
}
1722
1723
// Normally we don't propagate the LoadInfo's service worker tainting
1724
// synthesis flag on redirect. A real redirect normally will want to allow
1725
// normal tainting to proceed from its starting taint. For this particular
1726
// redirect, though, we are performing a redirect to communicate the URL of
1727
// the service worker synthetic response itself. This redirect still
1728
// represents the synthetic response, so we must preserve the flag.
1729
if (mLoadInfo->GetServiceWorkerTaintingSynthesized()) {
1730
nsCOMPtr<nsILoadInfo> newChannelLoadInfo = newChannel->LoadInfo();
1731
if (newChannelLoadInfo) {
1732
newChannelLoadInfo->SynthesizeServiceWorkerTainting(
1733
mLoadInfo->GetTainting());
1734
}
1735
}
1736
1737
nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
1738
MOZ_ASSERT(target);
1739
1740
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlag,
1741
target);
1742
}
1743
1744
if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
1745
}
1746
1747
void HttpChannelChild::OverrideSecurityInfoForNonIPCRedirect(
1748
nsISupports* securityInfo) {
1749
mResponseCouldBeSynthesized = true;
1750
DebugOnly<nsresult> rv = OverrideSecurityInfo(securityInfo);
1751
MOZ_ASSERT(NS_SUCCEEDED(rv));
1752
}
1753
1754
mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect3Complete() {
1755
LOG(("HttpChannelChild::RecvRedirect3Complete [this=%p]\n", this));
1756
nsCOMPtr<nsIChannel> redirectChannel =
1757
do_QueryInterface(mRedirectChannelChild);
1758
MOZ_ASSERT(redirectChannel);
1759
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1760
this, [self = UnsafePtr<HttpChannelChild>(this), redirectChannel]() {
1761
nsresult rv = NS_OK;
1762
Unused << self->GetStatus(&rv);
1763
if (NS_FAILED(rv)) {
1764
// Pre-redirect channel was canceled. Call |HandleAsyncAbort|, so
1765
// mListener's OnStart/StopRequest can be called. Nothing else will
1766
// trigger these notification after this point.
1767
// We do this before |CompleteRedirectSetup|, so post-redirect channel
1768
// stays unopened and we also make sure that OnStart/StopRequest won't
1769
// be called twice.
1770
self->HandleAsyncAbort();
1771
1772
nsCOMPtr<nsIHttpChannelChild> chan =
1773
do_QueryInterface(redirectChannel);
1774
RefPtr<HttpChannelChild> httpChannelChild =
1775
static_cast<HttpChannelChild*>(chan.get());
1776
if (httpChannelChild) {
1777
// For sending an IPC message to parent channel so that the loading
1778
// can be cancelled.
1779
Unused << httpChannelChild->Cancel(rv);
1780
1781
// The post-redirect channel could still get OnStart/StopRequest IPC
1782
// messages from parent, but the mListener is still null. So, we
1783
// call |DoNotifyListener| to pretend that OnStart/StopRequest are
1784
// already called.
1785
httpChannelChild->DoNotifyListener();
1786
}
1787
return;
1788
}
1789
1790
self->Redirect3Complete(nullptr);
1791
}));
1792
return IPC_OK();
1793
}
1794
1795
void HttpChannelChild::ProcessFlushedForDiversion() {
1796
LOG(("HttpChannelChild::ProcessFlushedForDiversion [this=%p]\n", this));
1797
MOZ_ASSERT(OnSocketThread());
1798
MOZ_RELEASE_ASSERT(mDivertingToParent);
1799
1800
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1801
this,
1802
[self = UnsafePtr<HttpChannelChild>(this)]() {
1803
self->FlushedForDiversion();
1804
}),
1805
true);
1806
}
1807
1808
mozilla::ipc::IPCResult
1809
HttpChannelChild::RecvNotifyChannelClassifierProtectionDisabled(
1810
const uint32_t& aAcceptedReason) {
1811
LOG(
1812
("HttpChannelChild::RecvNotifyChannelClassifierProtectionDisabled "
1813
"[this=%p aAcceptedReason=%" PRIu32 "]\n",
1814
this, aAcceptedReason));
1815
MOZ_ASSERT(NS_IsMainThread());
1816
1817
mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1818
this, [self = UnsafePtr<HttpChannelChild>(this), aAcceptedReason] {
1819
UrlClassifierCommon::NotifyChannelClassifierProtectionDisabled(
1820
self, aAcceptedReason);
1821
}));
1822
1823
return IPC_OK();
1824
}
1825
1826
mozilla::ipc::IPCResult HttpChannelChild::RecvNotifyCookieAllowed() {
1827
LOG(("HttpChannelChild::RecvNotifyCookieAllowed [this=%p]\n", this));