1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_HttpChannelChild_h
#define mozilla_net_HttpChannelChild_h
#include "mozilla/Mutex.h"
#include "mozilla/Telemetry.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/net/HttpBaseChannel.h"
#include "mozilla/net/NeckoTargetHolder.h"
#include "mozilla/net/PHttpChannelChild.h"
#include "mozilla/net/ChannelEventQueue.h"
#include "nsIStreamListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIProgressEventSink.h"
#include "nsICacheEntry.h"
#include "nsICacheInfoChannel.h"
#include "nsIResumableChannel.h"
#include "nsIProxiedChannel.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIChildChannel.h"
#include "nsIHttpChannelChild.h"
#include "nsIMultiPartChannel.h"
#include "nsIThreadRetargetableRequest.h"
#include "mozilla/net/DNS.h"
using mozilla::Telemetry::LABELS_HTTP_CHILD_OMT_STATS;
class nsIEventTarget;
class nsIInterceptedBodyCallback;
class nsISerialEventTarget;
class nsITransportSecurityInfo;
class nsInputStreamPump;
#define HTTP_CHANNEL_CHILD_IID \
{ \
0x321bd99e, 0x2242, 0x4dc6, { \
0xbb, 0xec, 0xd5, 0x06, 0x29, 0x7c, 0x39, 0x83 \
} \
}
namespace mozilla::net {
class HttpBackgroundChannelChild;
class HttpChannelChild final : public PHttpChannelChild,
public HttpBaseChannel,
public HttpAsyncAborter<HttpChannelChild>,
public nsICacheInfoChannel,
public nsIProxiedChannel,
public nsIAsyncVerifyRedirectCallback,
public nsIChildChannel,
public nsIHttpChannelChild,
public nsIMultiPartChannel,
public nsIThreadRetargetableRequest,
public NeckoTargetHolder {
virtual ~HttpChannelChild();
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICACHEINFOCHANNEL
NS_DECL_NSIPROXIEDCHANNEL
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
NS_DECL_NSICHILDCHANNEL
NS_DECL_NSIHTTPCHANNELCHILD
NS_DECL_NSIMULTIPARTCHANNEL
NS_DECL_NSITHREADRETARGETABLEREQUEST
NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_CHILD_IID)
HttpChannelChild();
// Methods HttpBaseChannel didn't implement for us or that we override.
//
// nsIRequest
NS_IMETHOD SetCanceledReason(const nsACString& aReason) override;
NS_IMETHOD GetCanceledReason(nsACString& aReason) override;
NS_IMETHOD CancelWithReason(nsresult status,
const nsACString& reason) override;
NS_IMETHOD Cancel(nsresult status) override;
NS_IMETHOD Suspend() override;
NS_IMETHOD Resume() override;
// nsIChannel
NS_IMETHOD GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) override;
NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
// HttpBaseChannel::nsIHttpChannel
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
const nsACString& aValue, bool aMerge) override;
NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
NS_IMETHOD RedirectTo(nsIURI* newURI) override;
NS_IMETHOD UpgradeToSecure() override;
NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
void DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() override;
// nsIHttpChannelInternal
NS_IMETHOD GetIsAuthChannel(bool* aIsAuthChannel) override;
NS_IMETHOD SetEarlyHintObserver(nsIEarlyHintObserver* aObserver) override;
NS_IMETHOD SetWebTransportSessionEventListener(
WebTransportSessionEventListener* aListener) override;
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
// nsIClassOfService
NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
NS_IMETHOD SetClassOfService(ClassOfService inCos) override;
NS_IMETHOD SetIncremental(bool inIncremental) override;
// nsIResumableChannel
NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID) override;
nsresult SetReferrerHeader(const nsACString& aReferrer,
bool aRespectBeforeConnect) override;
[[nodiscard]] bool IsSuspended();
// Callback while background channel is ready.
void OnBackgroundChildReady(HttpBackgroundChannelChild* aBgChild);
// Callback while background channel is destroyed.
void OnBackgroundChildDestroyed(HttpBackgroundChannelChild* aBgChild);
nsresult CrossProcessRedirectFinished(nsresult aStatus);
void RegisterStreamFilter(
RefPtr<extensions::StreamFilterParent>& aStreamFilter);
protected:
mozilla::ipc::IPCResult RecvOnStartRequestSent() override;
mozilla::ipc::IPCResult RecvFailedAsyncOpen(const nsresult& status) override;
mozilla::ipc::IPCResult RecvRedirect1Begin(
const uint32_t& registrarId, nsIURI* newOriginalURI,
const uint32_t& newLoadFlags, const uint32_t& redirectFlags,
const ParentLoadInfoForwarderArgs& loadInfoForwarder,
const nsHttpResponseHead& responseHead,
nsITransportSecurityInfo* securityInfo, const uint64_t& channelId,
const NetAddr& oldPeerAddr,
const ResourceTimingStructArgs& aTiming) override;
mozilla::ipc::IPCResult RecvRedirect3Complete() override;
mozilla::ipc::IPCResult RecvRedirectFailed(const nsresult& status) override;
mozilla::ipc::IPCResult RecvDeleteSelf() override;
mozilla::ipc::IPCResult RecvReportSecurityMessage(
const nsAString& messageTag, const nsAString& messageCategory) override;
mozilla::ipc::IPCResult RecvSetPriority(const int16_t& aPriority) override;
mozilla::ipc::IPCResult RecvOriginalCacheInputStreamAvailable(
const Maybe<IPCStream>& aStream) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual void DoNotifyListenerCleanup() override;
virtual void DoAsyncAbort(nsresult aStatus) override;
nsresult AsyncCall(
void (HttpChannelChild::*funcPtr)(),
nsRunnableMethod<HttpChannelChild>** retval = nullptr) override {
// Normally, this method would just be implemented directly, but clang
// miscompiles the corresponding non-virtual thunk on linux x86.
// It however doesn't when going though a non-virtual method.
// https://bugs.llvm.org/show_bug.cgi?id=38466
return AsyncCallImpl(funcPtr, retval);
};
// Get event target for processing network events.
already_AddRefed<nsISerialEventTarget> GetNeckoTarget() override;
virtual mozilla::ipc::IPCResult RecvLogBlockedCORSRequest(
const nsAString& aMessage, const nsACString& aCategory,
const bool& aIsWarning) override;
NS_IMETHOD LogBlockedCORSRequest(const nsAString& aMessage,
const nsACString& aCategory,
bool aIsWarning = false) override;
virtual mozilla::ipc::IPCResult RecvLogMimeTypeMismatch(
const nsACString& aMessageName, const bool& aWarning,
const nsAString& aURL, const nsAString& aContentType) override;
NS_IMETHOD LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
const nsAString& aURL,
const nsAString& aContentType) override;
private:
// We want to handle failure result of AsyncOpen, hence AsyncOpen calls the
// Internal method
nsresult AsyncOpenInternal(nsIStreamListener* aListener);
nsresult AsyncCallImpl(void (HttpChannelChild::*funcPtr)(),
nsRunnableMethod<HttpChannelChild>** retval);
// Sets the event target for future IPC messages. Messages will either be
// directed to the TabGroup or DocGroup, depending on the LoadInfo associated
// with the channel. Should be called when a new channel is being set up,
// before the constructor message is sent to the parent.
void SetEventTarget();
// Get event target for ODA.
already_AddRefed<nsIEventTarget> GetODATarget();
[[nodiscard]] nsresult ContinueAsyncOpen();
void ProcessOnStartRequest(const nsHttpResponseHead& aResponseHead,
const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const HttpChannelOnStartRequestArgs& aArgs,
const HttpChannelAltDataStream& aAltData);
// Callbacks while receiving OnTransportAndData/OnStopRequest/OnProgress/
// OnStatus/FlushedForDiversion/DivertMessages on background IPC channel.
void ProcessOnTransportAndData(const nsresult& aChannelStatus,
const nsresult& aTransportStatus,
const uint64_t& aOffset,
const uint32_t& aCount,
const nsACString& aData);
void ProcessOnStopRequest(const nsresult& aChannelStatus,
const ResourceTimingStructArgs& aTiming,
const nsHttpHeaderArray& aResponseTrailers,
nsTArray<ConsoleReportCollected>&& aConsoleReports,
bool aFromSocketProcess);
void ProcessOnConsoleReport(
nsTArray<ConsoleReportCollected>&& aConsoleReports);
void ProcessNotifyClassificationFlags(uint32_t aClassificationFlags,
bool aIsThirdParty);
void ProcessSetClassifierMatchedInfo(const nsACString& aList,
const nsACString& aProvider,
const nsACString& aFullHash);
void ProcessSetClassifierMatchedTrackingInfo(const nsACString& aLists,
const nsACString& aFullHashes);
void ProcessOnAfterLastPart(const nsresult& aStatus);
void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
void ProcessOnStatus(const nsresult& aStatus);
void ProcessAttachStreamFilter(
Endpoint<extensions::PStreamFilterParent>&& aEndpoint);
void ProcessDetachStreamFilters();
// Return true if we need to tell the parent the size of unreported received
// data
bool NeedToReportBytesRead();
int32_t mUnreportBytesRead = 0;
void DoOnConsoleReport(nsTArray<ConsoleReportCollected>&& aConsoleReports);
void DoOnStartRequest(nsIRequest* aRequest);
void DoOnStatus(nsIRequest* aRequest, nsresult status);
void DoOnProgress(nsIRequest* aRequest, int64_t progress,
int64_t progressMax);
void DoOnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
uint64_t offset, uint32_t count);
void DoPreOnStopRequest(nsresult aStatus);
void DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus);
void ContinueOnStopRequest();
// Try send DeletingChannel message to parent side. Dispatch an async task to
// main thread if invoking on non-main thread.
void TrySendDeletingChannel();
// Try invoke Cancel if on main thread, or prepend a CancelEvent in mEventQ to
// ensure Cacnel is processed before any other channel events.
void CancelOnMainThread(nsresult aRv, const nsACString& aReason);
nsresult MaybeLogCOEPError(nsresult aStatus);
private:
// this section is for main-thread-only object
// all the references need to be proxy released on main thread.
nsCOMPtr<nsIChildChannel> mRedirectChannelChild;
// Proxy release all members above on main thread.
void ReleaseMainThreadOnlyReferences();
private:
nsCString mProtocolVersion;
RequestHeaderTuples mClientSetRequestHeaders;
RefPtr<ChannelEventQueue> mEventQ;
nsCOMPtr<nsIInputStreamReceiver> mOriginalInputStreamReceiver;
nsCOMPtr<nsIInputStream> mAltDataInputStream;
// Used to ensure atomicity of mBgChild and mBgInitFailCallback
Mutex mBgChildMutex{"HttpChannelChild::BgChildMutex"};
// Associated HTTP background channel
RefPtr<HttpBackgroundChannelChild> mBgChild MOZ_GUARDED_BY(mBgChildMutex);
// Error handling procedure if failed to establish PBackground IPC
nsCOMPtr<nsIRunnable> mBgInitFailCallback MOZ_GUARDED_BY(mBgChildMutex);
// Remove the association with background channel after OnStopRequest
// or AsyncAbort.
void CleanupBackgroundChannel();
// Target thread for delivering ODA.
nsCOMPtr<nsISerialEventTarget> mODATarget MOZ_GUARDED_BY(mEventTargetMutex);
// Used to ensure atomicity of mNeckoTarget / mODATarget;
Mutex mEventTargetMutex{"HttpChannelChild::EventTargetMutex"};
TimeStamp mLastStatusReported;
uint64_t mCacheEntryId{0};
// The result of RetargetDeliveryTo for this channel.
// |notRequested| represents OMT is not requested by the channel owner.
LABELS_HTTP_CHILD_OMT_STATS mOMTResult =
LABELS_HTTP_CHILD_OMT_STATS::notRequested;
uint32_t mCacheKey{0};
int32_t mCacheFetchCount{0};
uint32_t mCacheExpirationTime{
static_cast<uint32_t>(nsICacheEntry::NO_EXPIRATION_TIME)};
// If we're handling a multi-part response, then this is set to the current
// part ID during OnStartRequest.
Maybe<uint32_t> mMultiPartID;
// To ensure only one SendDeletingChannel is triggered.
Atomic<bool> mDeletingChannelSent{false};
Atomic<bool, SequentiallyConsistent> mIsFromCache{false};
Atomic<bool, SequentiallyConsistent> mIsRacing{false};
// Set if we get the result and cache |mNeedToReportBytesRead|
Atomic<bool, SequentiallyConsistent> mCacheNeedToReportBytesReadInitialized{
false};
// True if we need to tell the parent the size of unreported received data
Atomic<bool, SequentiallyConsistent> mNeedToReportBytesRead{true};
// Attached StreamFilterParents
// Using raw pointer here since StreamFilterParent owns the channel.
// Should be only accessed on the main thread.
using StreamFilters = nsTArray<extensions::StreamFilterParent*>;
StreamFilters mStreamFilters;
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
bool mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy = false;
bool mAsyncOpenSucceeded = false;
bool mSuccesfullyRedirected = false;
bool mRemoteChannelExistedAtCancel = false;
bool mEverHadBgChildAtAsyncOpen = false;
bool mEverHadBgChildAtConnectParent = false;
bool mCreateBackgroundChannelFailed = false;
bool mBgInitFailCallbackTriggered = false;
bool mCanSendAtCancel = false;
// State of the HttpBackgroundChannelChild's event queue during destruction.
enum BckChildQueueStatus {
// BckChild never told us
BCKCHILD_UNKNOWN,
// BckChild was empty at the time of destruction
BCKCHILD_EMPTY,
// BckChild was keeping events in the queue at the destruction time!
BCKCHILD_NON_EMPTY
};
Atomic<BckChildQueueStatus> mBackgroundChildQueueFinalState{BCKCHILD_UNKNOWN};
Maybe<ActorDestroyReason> mActorDestroyReason;
#endif
uint8_t mCacheEntryAvailable : 1;
uint8_t mAltDataCacheEntryAvailable : 1;
// If ResumeAt is called before AsyncOpen, we need to send extra data upstream
uint8_t mSendResumeAt : 1;
uint8_t mKeptAlive : 1; // IPC kept open, but only for security info
// Set when ActorDestroy(ActorDestroyReason::Deletion) is called
// The channel must ignore any following OnStart/Stop/DataAvailable messages
uint8_t mIPCActorDeleted : 1;
// Set if SendSuspend is called. Determines if SendResume is needed when
// diverting callbacks to parent.
uint8_t mSuspendSent : 1;
// True if this channel is a multi-part channel, and the first part
// is currently being processed.
uint8_t mIsFirstPartOfMultiPart : 1;
// True if this channel is a multi-part channel, and the last part
// is currently being processed.
uint8_t mIsLastPartOfMultiPart : 1;
// True if this channel is suspended by ConnectParent and not resumed by
// CompleteRedirectSetup/RecvDeleteSelf.
uint8_t mSuspendForWaitCompleteRedirectSetup : 1;
// True if RecvOnStartRequestSent was received.
uint8_t mRecvOnStartRequestSentCalled : 1;
// True if this channel is for a document and suspended by waiting for
// permission or cookie. That is, RecvOnStartRequestSent is received.
uint8_t mSuspendedByWaitingForPermissionCookie : 1;
// HttpChannelChild::Release has some special logic that makes sure
// OnStart/OnStop are always called when releasing the channel.
// But we have to make sure we only do this once - otherwise we could
// get stuck in a loop.
uint8_t mAlreadyReleased : 1;
void CleanupRedirectingChannel(nsresult rv);
// Calls OnStartRequest and/or OnStopRequest on our listener in case we didn't
// do that so far. If we already did, it will just release references to
// cleanup.
void NotifyOrReleaseListeners(nsresult rv);
// true after successful AsyncOpen until OnStopRequest completes.
bool RemoteChannelExists() { return CanSend() && !mKeptAlive; }
void OnStartRequest(const nsHttpResponseHead& aResponseHead,
const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const HttpChannelOnStartRequestArgs& aArgs);
void OnTransportAndData(const nsresult& channelStatus, const nsresult& status,
const uint64_t& offset, const uint32_t& count,
const nsACString& data);
void OnStopRequest(const nsresult& channelStatus,
const ResourceTimingStructArgs& timing,
const nsHttpHeaderArray& aResponseTrailers);
void FailedAsyncOpen(const nsresult& status);
void HandleAsyncAbort();
void Redirect1Begin(const uint32_t& registrarId, nsIURI* newOriginalURI,
const uint32_t& newLoadFlags,
const uint32_t& redirectFlags,
const ParentLoadInfoForwarderArgs& loadInfoForwarder,
const nsHttpResponseHead& responseHead,
nsITransportSecurityInfo* securityInfo,
const uint64_t& channelId,
const ResourceTimingStructArgs& timing);
void Redirect3Complete();
void DeleteSelf();
// aUseEventQueue should only be false when called from
// HttpChannelChild::Release to make sure OnStopRequest is called syncly.
void DoNotifyListener(bool aUseEventQueue = true);
void ContinueDoNotifyListener();
void OnAfterLastPart(const nsresult& aStatus);
void MaybeConnectToSocketProcess();
void OnDetachStreamFilters();
// Create a a new channel to be used in a redirection, based on the provided
// response headers.
[[nodiscard]] nsresult SetupRedirect(nsIURI* uri,
const nsHttpResponseHead* responseHead,
const uint32_t& redirectFlags,
nsIChannel** outChannel);
// Collect telemetry for the successful rate of OMT.
void CollectOMTTelemetry();
friend class HttpAsyncAborter<HttpChannelChild>;
friend class InterceptStreamListener;
friend class InterceptedChannelContent;
friend class HttpBackgroundChannelChild;
friend class NeckoTargetChannelFunctionEvent;
};
NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelChild, HTTP_CHANNEL_CHILD_IID)
//-----------------------------------------------------------------------------
// inline functions
//-----------------------------------------------------------------------------
inline bool HttpChannelChild::IsSuspended() { return mSuspendCount != 0; }
} // namespace mozilla::net
#endif // mozilla_net_HttpChannelChild_h
|