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 "nsINetworkInterceptController.h"
12 #include "nsIInputStream.h"
13 #include "nsICacheInfoChannel.h"
14 #include "nsIChannelWithDivertableParentListener.h"
15 #include "nsIThreadRetargetableRequest.h"
16 
17 namespace mozilla {
18 namespace 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 nsIStreamListener,
58       public nsIChannelWithDivertableParentListener,
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_NSICHANNELWITHDIVERTABLEPARENTLISTENER
68   NS_DECL_NSITHREADRETARGETABLEREQUEST
69   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
70 
71  private:
72   friend class HttpAsyncAborter<InterceptedHttpChannel>;
73 
74   UniquePtr<nsHttpResponseHead> mSynthesizedResponseHead;
75   nsCOMPtr<nsIChannel> mRedirectChannel;
76   nsCOMPtr<nsIInputStream> mBodyReader;
77   nsCOMPtr<nsISupports> mReleaseHandle;
78   nsCOMPtr<nsIProgressEventSink> mProgressSink;
79   nsCOMPtr<nsIInterceptedBodyCallback> mBodyCallback;
80   nsCOMPtr<nsICacheInfoChannel> mSynthesizedCacheInfo;
81   RefPtr<nsInputStreamPump> mPump;
82   RefPtr<ADivertableParentChannel> mParentChannel;
83   TimeStamp mFinishResponseStart;
84   TimeStamp mFinishResponseEnd;
85   Atomic<int64_t> mProgress;
86   int64_t mProgressReported;
87   int64_t mSynthesizedStreamLength;
88   uint64_t mResumeStartPos;
89   nsCString mResumeEntityId;
90   nsString mStatusHost;
91   enum { Invalid = 0, Synthesized, Reset } mSynthesizedOrReset;
92   Atomic<bool> mCallingStatusAndProgress;
93   bool mDiverting;
94 
95   InterceptedHttpChannel(PRTime aCreationTime,
96                          const TimeStamp& aCreationTimestamp,
97                          const TimeStamp& aAsyncOpenTimestamp);
98   ~InterceptedHttpChannel() = default;
99 
100   virtual void ReleaseListeners() override;
101 
102   virtual MOZ_MUST_USE nsresult SetupReplacementChannel(
103       nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
104       uint32_t aRedirectFlags) override;
105 
106   void AsyncOpenInternal();
107 
108   bool ShouldRedirect() const;
109 
110   nsresult FollowSyntheticRedirect();
111 
112   // If the response's URL is different from the request's then do a service
113   // worker redirect. If Response.redirected is false we do an internal
114   // redirect. Otherwise, if Response.redirect is true do a non-internal
115   // redirect so end consumers detect the redirected state.
116   nsresult RedirectForResponseURL(nsIURI* aResponseURI,
117                                   bool aResponseRedirected);
118 
119   nsresult StartPump();
120 
121   nsresult OpenRedirectChannel();
122 
123   void MaybeCallStatusAndProgress();
124 
125   void MaybeCallBodyCallback();
126 
127  public:
128   static already_AddRefed<InterceptedHttpChannel> CreateForInterception(
129       PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
130       const TimeStamp& aAsyncOpenTimestamp);
131 
132   static already_AddRefed<InterceptedHttpChannel> CreateForSynthesis(
133       const nsHttpResponseHead* aHead, nsIInputStream* aBody,
134       nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
135       const TimeStamp& aCreationTimestamp,
136       const TimeStamp& aAsyncOpenTimestamp);
137 
138   NS_IMETHOD
139   Cancel(nsresult aStatus) override;
140 
141   NS_IMETHOD
142   Suspend(void) override;
143 
144   NS_IMETHOD
145   Resume(void) override;
146 
147   NS_IMETHOD
148   GetSecurityInfo(nsISupports** aSecurityInfo) override;
149 
150   NS_IMETHOD
151   AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext) override;
152 
153   NS_IMETHOD
154   AsyncOpen2(nsIStreamListener* aListener) override;
155 
156   NS_IMETHOD
157   LogBlockedCORSRequest(const nsAString& aMessage) override;
158 
159   NS_IMETHOD
160   SetupFallbackChannel(const char* aFallbackKey) override;
161 
162   NS_IMETHOD
163   GetResponseSynthesized(bool* aResponseSynthesized) override;
164 
165   NS_IMETHOD
166   SetPriority(int32_t aPriority) override;
167 
168   NS_IMETHOD
169   SetClassFlags(uint32_t aClassFlags) override;
170 
171   NS_IMETHOD
172   ClearClassFlags(uint32_t flags) override;
173 
174   NS_IMETHOD
175   AddClassFlags(uint32_t flags) override;
176 
177   NS_IMETHOD
178   ResumeAt(uint64_t startPos, const nsACString& entityID) override;
179 
180   void DoNotifyListenerCleanup() override;
181 };
182 
183 }  // namespace net
184 }  // namespace mozilla
185 
186 #endif  // mozilla_net_InterceptedHttpChannel_h
187