diff options
Diffstat (limited to 'netwerk/protocol/http/HttpChannelChild.h')
-rw-r--r-- | netwerk/protocol/http/HttpChannelChild.h | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h new file mode 100644 index 0000000000..38895a0555 --- /dev/null +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -0,0 +1,481 @@ +/* -*- 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; + +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, + 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); + + 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, mozilla::Relaxed> 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}; + 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 |