tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

InterceptedHttpChannel.h (11431B)


      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 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_net_InterceptedHttpChannel_h
      8 #define mozilla_net_InterceptedHttpChannel_h
      9 
     10 #include "HttpBaseChannel.h"
     11 #include "nsIAsyncVerifyRedirectCallback.h"
     12 #include "nsINetworkInterceptController.h"
     13 #include "nsIInputStream.h"
     14 #include "nsICacheInfoChannel.h"
     15 #include "nsIThreadRetargetableRequest.h"
     16 #include "nsIThreadRetargetableStreamListener.h"
     17 
     18 namespace mozilla::net {
     19 
     20 // This class represents an http channel that is being intercepted by a
     21 // ServiceWorker.  This means that when the channel is opened a FetchEvent
     22 // will be fired on the ServiceWorker thread.  The channel will complete
     23 // depending on what the worker does.  The options are:
     24 //
     25 // 1. If the ServiceWorker does not handle the FetchEvent or does not call
     26 //    FetchEvent.respondWith(), then the channel needs to fall back to a
     27 //    normal request.  When this happens ResetInterception() is called and
     28 //    the channel will perform an internal redirect back to an nsHttpChannel.
     29 //
     30 // 2. If the ServiceWorker provides a Response to FetchEvent.respondWith()
     31 //    then the status, headers, and body must be synthesized.  When
     32 //    FinishSynthesizedResponse() is called the synthesized data must be
     33 //    reported back to the channel listener.  This is handled in a few
     34 //    different ways:
     35 //      a. If a redirect was synthesized, then we perform the redirect to
     36 //         a new nsHttpChannel.  This new channel might trigger yet another
     37 //         interception.
     38 //      b. If a same-origin or CORS Response was synthesized, then we simply
     39 //         crate an nsInputStreamPump to process it and call back to the
     40 //         listener.
     41 //      c. If an opaque Response was synthesized, then we perform an internal
     42 //         redirect to a new InterceptedHttpChannel using the cross-origin URL.
     43 //         When this new channel is opened, it then creates a pump as in case
     44 //         (b).  The extra redirect here is to make sure the various listeners
     45 //         treat the result as unsafe cross-origin data.
     46 //
     47 // 3. If an error occurs, such as the ServiceWorker passing garbage to
     48 //    FetchEvent.respondWith(), then CancelInterception() is called.  This is
     49 //    handled the same as a normal nsIChannel::Cancel() call.  We abort the
     50 //    channel and end up calling OnStopRequest() with an error code.
     51 class InterceptedHttpChannel final
     52    : public HttpBaseChannel,
     53      public HttpAsyncAborter<InterceptedHttpChannel>,
     54      public nsIInterceptedChannel,
     55      public nsICacheInfoChannel,
     56      public nsIAsyncVerifyRedirectCallback,
     57      public nsIThreadRetargetableRequest,
     58      public nsIThreadRetargetableStreamListener {
     59  NS_DECL_ISUPPORTS_INHERITED
     60  NS_DECL_NSIINTERCEPTEDCHANNEL
     61  NS_DECL_NSICACHEINFOCHANNEL
     62  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
     63  NS_DECL_NSIREQUESTOBSERVER
     64  NS_DECL_NSISTREAMLISTENER
     65  NS_DECL_NSITHREADRETARGETABLEREQUEST
     66  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     67 
     68 private:
     69  friend class HttpAsyncAborter<InterceptedHttpChannel>;
     70 
     71  UniquePtr<nsHttpResponseHead> mSynthesizedResponseHead;
     72  nsCOMPtr<nsIChannel> mRedirectChannel;
     73  nsCOMPtr<nsIInputStream> mBodyReader;
     74  nsCOMPtr<nsISupports> mReleaseHandle;
     75  nsCOMPtr<nsIProgressEventSink> mProgressSink;
     76  nsCOMPtr<nsIInterceptedBodyCallback> mBodyCallback;
     77  nsCOMPtr<nsICacheInfoChannel> mSynthesizedCacheInfo;
     78  RefPtr<nsInputStreamPump> mPump;
     79  TimeStamp mInterceptedChannelCreationTimestamp;
     80 
     81  // For the profiler markers
     82  TimeStamp mLastStatusReported;
     83 
     84  Atomic<int64_t> mProgress;
     85  int64_t mProgressReported;
     86  int64_t mSynthesizedStreamLength;
     87  uint64_t mResumeStartPos;
     88  nsCString mResumeEntityId;
     89  nsString mStatusHost;
     90  Atomic<bool> mCallingStatusAndProgress;
     91  bool mInterceptionReset{false};
     92 
     93  /**
     94   *  InterceptionTimeStamps is used to record the time stamps of the
     95   *  interception.
     96   *  The general usage:
     97   *  Step 1. Initialize the InterceptionTimeStamps;
     98   *    InterceptionTimeStamps::Init(channel);
     99   *  Step 2. Record time for each stage
    100   *    InterceptionTimeStamps::RecordTime(); or
    101   *    InterceptionTimeStamps::RecordTime(timeStamp);
    102   *  Step 3. Record time for the last stage with the final status
    103   *    InterceptionTimeStamps::RecordTime(InterceptionTimeStamps::Synthesized);
    104   */
    105  class InterceptionTimeStamps final {
    106   public:
    107    // The possible status of the interception.
    108    enum Status {
    109      Created,
    110      Initialized,
    111      Synthesized,
    112      Reset,
    113      Redirected,
    114      Canceled,
    115      CanceledAfterSynthesized,
    116      CanceledAfterReset,
    117      CanceledAfterRedirected
    118    };
    119 
    120    InterceptionTimeStamps();
    121    ~InterceptionTimeStamps() = default;
    122 
    123    /**
    124     * Initialize with the given channel.
    125     * This method should be called before any RecordTime().
    126     */
    127    void Init(nsIChannel* aChannel);
    128 
    129    /**
    130     * Record the given time stamp for current stage. If there is no given time
    131     * stamp, TimeStamp::Now() will be recorded.
    132     * The current stage is auto moved to the next one.
    133     */
    134    void RecordTime(TimeStamp&& aTimeStamp = TimeStamp::Now());
    135 
    136    /**
    137     * Record the given time stamp for the last stage(InterceptionFinish) and
    138     * set the final status to the given status.
    139     * If these is no given time stamp, TimeStamp::Now() will be recorded.
    140     * Notice that this method is for the last stage, it calls SaveTimeStamps()
    141     * to write data into telemetries.
    142     */
    143    void RecordTime(Status&& aStatus,
    144                    TimeStamp&& aTimeStamp = TimeStamp::Now());
    145 
    146    // The time stamp which the intercepted channel is created and async opend.
    147    TimeStamp mInterceptionStart;
    148 
    149    // The time stamp which the interception finishes.
    150    TimeStamp mInterceptionFinish;
    151 
    152    // The time stamp which the fetch event starts to be handled by fetch event
    153    // handler.
    154    TimeStamp mFetchHandlerStart;
    155 
    156    // The time stamp which the fetch event handling finishes. It would the time
    157    // which remote worker sends result back.
    158    TimeStamp mFetchHandlerFinish;
    159 
    160   private:
    161    // The stage of interception.
    162    enum Stage {
    163      InterceptionStart,
    164      FetchHandlerStart,
    165      FetchHandlerFinish,
    166      InterceptionFinish
    167    } mStage;
    168 
    169    // The final status of the interception.
    170    Status mStatus;
    171 
    172    bool mIsNonSubresourceRequest;
    173    // The keys used for telemetries.
    174    nsCString mKey;
    175    nsCString mSubresourceKey;
    176 
    177    void RecordTimeInternal(TimeStamp&& aTimeStamp);
    178 
    179    // Generate the record keys with final status.
    180    void GenKeysWithStatus(nsCString& aKey, nsCString& aSubresourceKey);
    181 
    182    // Save the time stamps into telemetries.
    183    void SaveTimeStamps();
    184  };
    185 
    186  InterceptionTimeStamps mTimeStamps;
    187 
    188  InterceptedHttpChannel(PRTime aCreationTime,
    189                         const TimeStamp& aCreationTimestamp,
    190                         const TimeStamp& aAsyncOpenTimestamp);
    191  ~InterceptedHttpChannel() = default;
    192 
    193  virtual void ReleaseListeners() override;
    194 
    195  [[nodiscard]] virtual nsresult SetupReplacementChannel(
    196      nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
    197      uint32_t aRedirectFlags) override;
    198 
    199  void AsyncOpenInternal();
    200 
    201  bool ShouldRedirect() const;
    202 
    203  nsresult FollowSyntheticRedirect();
    204 
    205  // If the response's URL is different from the request's then do a service
    206  // worker redirect. If Response.redirected is false we do an internal
    207  // redirect. Otherwise, if Response.redirect is true do a non-internal
    208  // redirect so end consumers detect the redirected state.
    209  nsresult RedirectForResponseURL(nsIURI* aResponseURI,
    210                                  bool aResponseRedirected);
    211 
    212  nsresult StartPump();
    213 
    214  nsresult OpenRedirectChannel();
    215 
    216  void MaybeCallStatusAndProgress();
    217 
    218  void MaybeCallBodyCallback();
    219 
    220  TimeStamp mServiceWorkerLaunchStart;
    221  TimeStamp mServiceWorkerLaunchEnd;
    222 
    223 public:
    224  static already_AddRefed<InterceptedHttpChannel> CreateForInterception(
    225      PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
    226      const TimeStamp& aAsyncOpenTimestamp);
    227 
    228  static already_AddRefed<InterceptedHttpChannel> CreateForSynthesis(
    229      const nsHttpResponseHead* aHead, nsIInputStream* aBody,
    230      nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
    231      const TimeStamp& aCreationTimestamp,
    232      const TimeStamp& aAsyncOpenTimestamp);
    233 
    234  NS_IMETHOD SetCanceledReason(const nsACString& aReason) override;
    235  NS_IMETHOD GetCanceledReason(nsACString& aReason) override;
    236  NS_IMETHOD CancelWithReason(nsresult status,
    237                              const nsACString& reason) override;
    238 
    239  NS_IMETHOD
    240  Cancel(nsresult aStatus) override;
    241 
    242  NS_IMETHOD
    243  Suspend(void) override;
    244 
    245  NS_IMETHOD
    246  Resume(void) override;
    247 
    248  NS_IMETHOD
    249  GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) override;
    250 
    251  NS_IMETHOD
    252  AsyncOpen(nsIStreamListener* aListener) override;
    253 
    254  NS_IMETHOD
    255  LogBlockedCORSRequest(const nsAString& aMessage, const nsACString& aCategory,
    256                        bool aIsWarning) override;
    257 
    258  NS_IMETHOD
    259  LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
    260                      const nsAString& aURL,
    261                      const nsAString& aContentType) override;
    262 
    263  NS_IMETHOD
    264  GetIsAuthChannel(bool* aIsAuthChannel) override;
    265 
    266  NS_IMETHOD
    267  SetPriority(int32_t aPriority) override;
    268 
    269  NS_IMETHOD
    270  SetClassFlags(uint32_t aClassFlags) override;
    271 
    272  NS_IMETHOD
    273  ClearClassFlags(uint32_t flags) override;
    274 
    275  NS_IMETHOD
    276  AddClassFlags(uint32_t flags) override;
    277 
    278  NS_IMETHOD
    279  SetClassOfService(ClassOfService cos) override;
    280 
    281  NS_IMETHOD
    282  SetIncremental(bool incremental) override;
    283 
    284  NS_IMETHOD
    285  ResumeAt(uint64_t startPos, const nsACString& entityID) override;
    286 
    287  NS_IMETHOD
    288  SetEarlyHintObserver(nsIEarlyHintObserver* aObserver) override {
    289    return NS_OK;
    290  }
    291 
    292  NS_IMETHOD SetWebTransportSessionEventListener(
    293      WebTransportSessionEventListener* aListener) override {
    294    return NS_OK;
    295  }
    296 
    297  NS_IMETHOD SetResponseOverride(
    298      nsIReplacedHttpResponse* aReplacedHttpResponse) override {
    299    return NS_OK;
    300  }
    301 
    302  NS_IMETHOD SetResponseStatus(uint32_t aStatus,
    303                               const nsACString& aStatusText) override {
    304    return NS_OK;
    305  }
    306 
    307  NS_IMETHOD SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) override;
    308  NS_IMETHOD GetLaunchServiceWorkerStart(TimeStamp* aRetVal) override;
    309 
    310  NS_IMETHOD SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) override;
    311  NS_IMETHOD GetLaunchServiceWorkerEnd(TimeStamp* aRetVal) override;
    312 
    313  NS_IMETHOD GetDispatchFetchEventStart(TimeStamp* aRetVal) override;
    314  NS_IMETHOD GetDispatchFetchEventEnd(TimeStamp* aRetVal) override;
    315 
    316  NS_IMETHOD GetHandleFetchEventStart(TimeStamp* aRetVal) override;
    317  NS_IMETHOD GetHandleFetchEventEnd(TimeStamp* aRetVal) override;
    318 
    319  void DoNotifyListenerCleanup() override;
    320 
    321  void DoAsyncAbort(nsresult aStatus) override;
    322 
    323  NS_IMETHOD GetDecompressDictionary(
    324      DictionaryCacheEntry** aDictionary) override {
    325    *aDictionary = nullptr;
    326    return NS_OK;
    327  }
    328  NS_IMETHOD SetDecompressDictionary(
    329      DictionaryCacheEntry* aDictionary) override {
    330    return NS_OK;
    331  }
    332 };
    333 
    334 }  // namespace mozilla::net
    335 
    336 #endif  // mozilla_net_InterceptedHttpChannel_h