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