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