/* 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_TRRServiceChannel_h
#define mozilla_net_TRRServiceChannel_h

#include "HttpBaseChannel.h"
#include "mozilla/DataMutex.h"
#include "nsIDNSListener.h"
#include "nsIProtocolProxyCallback.h"
#include "nsIProxiedChannel.h"
#include "nsIStreamListener.h"
#include "nsITransport.h"
#include "nsWeakReference.h"

class nsDNSPrefetch;

namespace mozilla::net {

class HttpTransactionShell;
class nsHttpHandler;

// Use to support QI nsIChannel to TRRServiceChannel
#define NS_TRRSERVICECHANNEL_IID \
  {0x361c4bb1, 0xd6b2, 0x493b, {0x86, 0xbc, 0x88, 0xd3, 0x5d, 0x16, 0x38, 0xfa}}

// TRRServiceChannel is designed to fetch DNS data from DoH server. This channel
// MUST only be used by TRR.
class TRRServiceChannel : public HttpBaseChannel,
                          public HttpAsyncAborter<TRRServiceChannel>,
                          public nsIDNSListener,
                          public nsIStreamListener,
                          public nsITransportEventSink,
                          public nsIProxiedChannel,
                          public nsIProtocolProxyCallback,
                          public nsSupportsWeakReference {
 public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSIDNSLISTENER
  NS_DECL_NSIREQUESTOBSERVER
  NS_DECL_NSISTREAMLISTENER
  NS_DECL_NSITRANSPORTEVENTSINK
  NS_DECL_NSIPROXIEDCHANNEL
  NS_DECL_NSIPROTOCOLPROXYCALLBACK
  NS_INLINE_DECL_STATIC_IID(NS_TRRSERVICECHANNEL_IID)

  // 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;
  NS_IMETHOD GetLoadFlags(nsLoadFlags* aLoadFlags) override;
  NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) override;
  NS_IMETHOD GetURI(nsIURI** aURI) override;
  NS_IMETHOD GetNotificationCallbacks(
      nsIInterfaceRequestor** aCallbacks) override;
  NS_IMETHOD GetLoadGroup(nsILoadGroup** aLoadGroup) override;
  NS_IMETHOD GetRequestMethod(nsACString& aMethod) override;
  // nsIChannel
  NS_IMETHOD GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) override;
  NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;

  NS_IMETHOD LogBlockedCORSRequest(const nsAString& aMessage,
                                   const nsACString& aCategory,
                                   bool aIsWarning) override;
  NS_IMETHOD LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
                                 const nsAString& aURL,
                                 const nsAString& aContentType) override;
  NS_IMETHOD GetIsAuthChannel(bool* aIsAuthChannel) override;

  NS_IMETHOD SetNotificationCallbacks(
      nsIInterfaceRequestor* aCallbacks) override;
  NS_IMETHOD GetDecompressDictionary(
      DictionaryCacheEntry** aDictionary) override {
    *aDictionary = nullptr;
    return NS_OK;
  }
  NS_IMETHOD SetDecompressDictionary(
      DictionaryCacheEntry* aDictionary) override {
    return NS_OK;
  }
  // 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 SetIncremental(bool inFlag) override;
  NS_IMETHOD SetClassOfService(ClassOfService cos) override;
  // nsIResumableChannel
  NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID) override;
  NS_IMETHOD SetEarlyHintObserver(nsIEarlyHintObserver* aObserver) override {
    return NS_OK;
  }
  NS_IMETHOD SetWebTransportSessionEventListener(
      WebTransportSessionEventListener* aListener) override {
    return NS_OK;
  }

  NS_IMETHOD SetResponseOverride(
      nsIReplacedHttpResponse* aReplacedHttpResponse) override {
    return NS_OK;
  }

  NS_IMETHOD SetResponseStatus(uint32_t aStatus,
                               const nsACString& aStatusText) override {
    return NS_OK;
  }

  // nsITimedChannel
  NS_IMETHOD GetDomainLookupStart(
      mozilla::TimeStamp* aDomainLookupStart) override;
  NS_IMETHOD GetDomainLookupEnd(mozilla::TimeStamp* aDomainLookupEnd) override;
  NS_IMETHOD GetConnectStart(mozilla::TimeStamp* aConnectStart) override;
  NS_IMETHOD GetTcpConnectEnd(mozilla::TimeStamp* aTcpConnectEnd) override;
  NS_IMETHOD GetSecureConnectionStart(
      mozilla::TimeStamp* aSecureConnectionStart) override;
  NS_IMETHOD GetConnectEnd(mozilla::TimeStamp* aConnectEnd) override;
  NS_IMETHOD GetRequestStart(mozilla::TimeStamp* aRequestStart) override;
  NS_IMETHOD GetResponseStart(mozilla::TimeStamp* aResponseStart) override;
  NS_IMETHOD GetResponseEnd(mozilla::TimeStamp* aResponseEnd) override;
  NS_IMETHOD SetLoadGroup(nsILoadGroup* aLoadGroup) override;
  NS_IMETHOD TimingAllowCheck(nsIPrincipal* aOrigin, bool* aResult) override;

 protected:
  TRRServiceChannel();
  virtual ~TRRServiceChannel();

  void CancelNetworkRequest(nsresult aStatus);
  nsresult BeginConnect();
  nsresult ContinueOnBeforeConnect();
  nsresult Connect();
  nsresult SetupTransaction();
  void OnClassOfServiceUpdated();
  virtual void DoNotifyListenerCleanup() override;
  virtual void DoAsyncAbort(nsresult aStatus) override;
  bool IsIsolated() { return false; };
  void ProcessAltService(nsHttpConnectionInfo* aTransConnInfo);
  nsresult CallOnStartRequest();

  void MaybeStartDNSPrefetch();
  void DoNotifyListener();
  nsresult MaybeResolveProxyAndBeginConnect();
  nsresult ResolveProxy();
  void AfterApplyContentConversions(nsresult aResult,
                                    nsIStreamListener* aListener);
  nsresult SyncProcessRedirection(uint32_t aHttpStatus);
  [[nodiscard]] virtual nsresult SetupReplacementChannel(
      nsIURI* aNewURI, nsIChannel* aNewChannel, bool aPreserveMethod,
      uint32_t aRedirectFlags) override;
  // Skip this check for TRRServiceChannel.
  virtual bool ShouldTaintReplacementChannelOrigin(
      nsIChannel* aNewChannel, uint32_t aRedirectFlags) override {
    return false;
  }
  virtual bool SameOriginWithOriginalUri(nsIURI* aURI) override;
  bool DispatchRelease();

  nsCString mUsername;

  // Needed for accurate DNS timing
  RefPtr<nsDNSPrefetch> mDNSPrefetch;

  nsCOMPtr<nsIRequest> mTransactionPump;
  RefPtr<HttpTransactionShell> mTransaction;
  DataMutex<nsCOMPtr<nsICancelable>> mProxyRequest;
  nsCOMPtr<nsIEventTarget> mCurrentEventTarget;

  friend class HttpAsyncAborter<TRRServiceChannel>;
  friend class nsHttpHandler;
};

}  // namespace mozilla::net

#endif  // mozilla_net_TRRServiceChannel_h
