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 {
19 namespace net {
20 
21 // This class represents an http channel that is being intercepted by a
22 // ServiceWorker.  This means that when the channel is opened a FetchEvent
23 // will be fired on the ServiceWorker thread.  The channel will complete
24 // depending on what the worker does.  The options are:
25 //
26 // 1. If the ServiceWorker does not handle the FetchEvent or does not call
27 //    FetchEvent.respondWith(), then the channel needs to fall back to a
28 //    normal request.  When this happens ResetInterception() is called and
29 //    the channel will perform an internal redirect back to an nsHttpChannel.
30 //
31 // 2. If the ServiceWorker provides a Response to FetchEvent.respondWith()
32 //    then the status, headers, and body must be synthesized.  When
33 //    FinishSynthesizedResponse() is called the synthesized data must be
34 //    reported back to the channel listener.  This is handled in a few
35 //    different ways:
36 //      a. If a redirect was synthesized, then we perform the redirect to
37 //         a new nsHttpChannel.  This new channel might trigger yet another
38 //         interception.
39 //      b. If a same-origin or CORS Response was synthesized, then we simply
40 //         crate an nsInputStreamPump to process it and call back to the
41 //         listener.
42 //      c. If an opaque Response was synthesized, then we perform an internal
43 //         redirect to a new InterceptedHttpChannel using the cross-origin URL.
44 //         When this new channel is opened, it then creates a pump as in case
45 //         (b).  The extra redirect here is to make sure the various listeners
46 //         treat the result as unsafe cross-origin data.
47 //
48 // 3. If an error occurs, such as the ServiceWorker passing garbage to
49 //    FetchEvent.respondWith(), then CancelInterception() is called.  This is
50 //    handled the same as a normal nsIChannel::Cancel() call.  We abort the
51 //    channel and end up calling OnStopRequest() with an error code.
52 class InterceptedHttpChannel final
53     : public HttpBaseChannel,
54       public HttpAsyncAborter<InterceptedHttpChannel>,
55       public nsIInterceptedChannel,
56       public nsICacheInfoChannel,
57       public nsIAsyncVerifyRedirectCallback,
58       public nsIStreamListener,
59       public nsIThreadRetargetableRequest,
60       public nsIThreadRetargetableStreamListener {
61   NS_DECL_ISUPPORTS_INHERITED
62   NS_DECL_NSIINTERCEPTEDCHANNEL
63   NS_DECL_NSICACHEINFOCHANNEL
64   NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
65   NS_DECL_NSIREQUESTOBSERVER
66   NS_DECL_NSISTREAMLISTENER
67   NS_DECL_NSITHREADRETARGETABLEREQUEST
68   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
69 
70  private:
71   friend class HttpAsyncAborter<InterceptedHttpChannel>;
72 
73   UniquePtr<nsHttpResponseHead> mSynthesizedResponseHead;
74   nsCOMPtr<nsIChannel> mRedirectChannel;
75   nsCOMPtr<nsIInputStream> mBodyReader;
76   nsCOMPtr<nsISupports> mReleaseHandle;
77   nsCOMPtr<nsIProgressEventSink> mProgressSink;
78   nsCOMPtr<nsIInterceptedBodyCallback> mBodyCallback;
79   nsCOMPtr<nsICacheInfoChannel> mSynthesizedCacheInfo;
80   RefPtr<nsInputStreamPump> mPump;
81   TimeStamp mFinishResponseStart;
82   TimeStamp mFinishResponseEnd;
83   Atomic<int64_t> mProgress;
84   int64_t mProgressReported;
85   int64_t mSynthesizedStreamLength;
86   uint64_t mResumeStartPos;
87   nsCString mResumeEntityId;
88   nsString mStatusHost;
89   enum { Invalid = 0, Synthesized, Reset } mSynthesizedOrReset;
90   Atomic<bool> mCallingStatusAndProgress;
91 
92   InterceptedHttpChannel(PRTime aCreationTime,
93                          const TimeStamp& aCreationTimestamp,
94                          const TimeStamp& aAsyncOpenTimestamp);
95   ~InterceptedHttpChannel() = default;
96 
97   virtual void ReleaseListeners() override;
98 
99   [[nodiscard]] virtual nsresult SetupReplacementChannel(
100       nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
101       uint32_t aRedirectFlags) override;
102 
103   void AsyncOpenInternal();
104 
105   bool ShouldRedirect() const;
106 
107   nsresult FollowSyntheticRedirect();
108 
109   // If the response's URL is different from the request's then do a service
110   // worker redirect. If Response.redirected is false we do an internal
111   // redirect. Otherwise, if Response.redirect is true do a non-internal
112   // redirect so end consumers detect the redirected state.
113   nsresult RedirectForResponseURL(nsIURI* aResponseURI,
114                                   bool aResponseRedirected);
115 
116   nsresult StartPump();
117 
118   nsresult OpenRedirectChannel();
119 
120   void MaybeCallStatusAndProgress();
121 
122   void MaybeCallBodyCallback();
123 
124  public:
125   static already_AddRefed<InterceptedHttpChannel> CreateForInterception(
126       PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
127       const TimeStamp& aAsyncOpenTimestamp);
128 
129   static already_AddRefed<InterceptedHttpChannel> CreateForSynthesis(
130       const nsHttpResponseHead* aHead, nsIInputStream* aBody,
131       nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
132       const TimeStamp& aCreationTimestamp,
133       const TimeStamp& aAsyncOpenTimestamp);
134 
135   NS_IMETHOD
136   Cancel(nsresult aStatus) override;
137 
138   NS_IMETHOD
139   Suspend(void) override;
140 
141   NS_IMETHOD
142   Resume(void) override;
143 
144   NS_IMETHOD
145   GetSecurityInfo(nsISupports** aSecurityInfo) override;
146 
147   NS_IMETHOD
148   AsyncOpen(nsIStreamListener* aListener) override;
149 
150   NS_IMETHOD
151   LogBlockedCORSRequest(const nsAString& aMessage,
152                         const nsACString& aCategory) override;
153 
154   NS_IMETHOD
155   LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
156                       const nsAString& aURL,
157                       const nsAString& aContentType) override;
158 
159   NS_IMETHOD
160   GetIsAuthChannel(bool* aIsAuthChannel) override;
161 
162   NS_IMETHOD
163   SetPriority(int32_t aPriority) override;
164 
165   NS_IMETHOD
166   SetClassFlags(uint32_t aClassFlags) override;
167 
168   NS_IMETHOD
169   ClearClassFlags(uint32_t flags) override;
170 
171   NS_IMETHOD
172   AddClassFlags(uint32_t flags) override;
173 
174   NS_IMETHOD
175   ResumeAt(uint64_t startPos, const nsACString& entityID) override;
176 
177   void DoNotifyListenerCleanup() override;
178 
179   void DoAsyncAbort(nsresult aStatus) override;
180 };
181 
182 }  // namespace net
183 }  // namespace mozilla
184 
185 #endif  // mozilla_net_InterceptedHttpChannel_h
186