Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/StaticPrefsBase.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_2;
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.
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,
const TimeStamp& aOnStartRequestStartTime);
// 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,
const TimeStamp& aOnDataAvailableStartTime);
void ProcessOnStopRequest(const nsresult& aChannelStatus,
const ResourceTimingStructArgs& aTiming,
const nsHttpHeaderArray& aResponseTrailers,
nsTArray<ConsoleReportCollected>&& aConsoleReports,
bool aFromSocketProcess,
const TimeStamp& aOnStopRequestStartTime);
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);
void RetargetDeliveryToImpl(nsISerialEventTarget* aNewTarget,
MutexAutoLock& aLockRef);
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.
Atomic<LABELS_HTTP_CHILD_OMT_STATS_2, mozilla::Relaxed> mOMTResult{
LABELS_HTTP_CHILD_OMT_STATS_2::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};
Atomic<uint32_t, mozilla::Relaxed> mOnProgressEventSent{false};
// 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();
void SendOnDataFinished(const nsresult& aChannelStatus);
// 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();
// Collect telemetry for mixed content.
void CollectMixedContentTelemetry();
void RecordChannelCompletionDurationForEarlyHint();
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