Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at */
#ifndef mozilla_net_InterceptedHttpChannel_h
#define mozilla_net_InterceptedHttpChannel_h
#include "HttpBaseChannel.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsINetworkInterceptController.h"
#include "nsIInputStream.h"
#include "nsICacheInfoChannel.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIThreadRetargetableStreamListener.h"
namespace mozilla {
namespace net {
// This class represents an http channel that is being intercepted by a
// ServiceWorker. This means that when the channel is opened a FetchEvent
// will be fired on the ServiceWorker thread. The channel will complete
// depending on what the worker does. The options are:
// 1. If the ServiceWorker does not handle the FetchEvent or does not call
// FetchEvent.respondWith(), then the channel needs to fall back to a
// normal request. When this happens ResetInterception() is called and
// the channel will perform an internal redirect back to an nsHttpChannel.
// 2. If the ServiceWorker provides a Response to FetchEvent.respondWith()
// then the status, headers, and body must be synthesized. When
// FinishSynthesizedResponse() is called the synthesized data must be
// reported back to the channel listener. This is handled in a few
// different ways:
// a. If a redirect was synthesized, then we perform the redirect to
// a new nsHttpChannel. This new channel might trigger yet another
// interception.
// b. If a same-origin or CORS Response was synthesized, then we simply
// crate an nsInputStreamPump to process it and call back to the
// listener.
// c. If an opaque Response was synthesized, then we perform an internal
// redirect to a new InterceptedHttpChannel using the cross-origin URL.
// When this new channel is opened, it then creates a pump as in case
// (b). The extra redirect here is to make sure the various listeners
// treat the result as unsafe cross-origin data.
// 3. If an error occurs, such as the ServiceWorker passing garbage to
// FetchEvent.respondWith(), then CancelInterception() is called. This is
// handled the same as a normal nsIChannel::Cancel() call. We abort the
// channel and end up calling OnStopRequest() with an error code.
class InterceptedHttpChannel final
: public HttpBaseChannel,
public HttpAsyncAborter<InterceptedHttpChannel>,
public nsIInterceptedChannel,
public nsICacheInfoChannel,
public nsIAsyncVerifyRedirectCallback,
public nsIStreamListener,
public nsIThreadRetargetableRequest,
public nsIThreadRetargetableStreamListener {
friend class HttpAsyncAborter<InterceptedHttpChannel>;
UniquePtr<nsHttpResponseHead> mSynthesizedResponseHead;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCOMPtr<nsIInputStream> mBodyReader;
nsCOMPtr<nsISupports> mReleaseHandle;
nsCOMPtr<nsIProgressEventSink> mProgressSink;
nsCOMPtr<nsIInterceptedBodyCallback> mBodyCallback;
nsCOMPtr<nsICacheInfoChannel> mSynthesizedCacheInfo;
RefPtr<nsInputStreamPump> mPump;
TimeStamp mFinishResponseStart;
TimeStamp mFinishResponseEnd;
Atomic<int64_t> mProgress;
int64_t mProgressReported;
int64_t mSynthesizedStreamLength;
uint64_t mResumeStartPos;
nsCString mResumeEntityId;
nsString mStatusHost;
enum { Invalid = 0, Synthesized, Reset } mSynthesizedOrReset;
Atomic<bool> mCallingStatusAndProgress;
InterceptedHttpChannel(PRTime aCreationTime,
const TimeStamp& aCreationTimestamp,
const TimeStamp& aAsyncOpenTimestamp);
~InterceptedHttpChannel() = default;
virtual void ReleaseListeners() override;
[[nodiscard]] virtual nsresult SetupReplacementChannel(
nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
uint32_t aRedirectFlags) override;
void AsyncOpenInternal();
bool ShouldRedirect() const;
nsresult FollowSyntheticRedirect();
// If the response's URL is different from the request's then do a service
// worker redirect. If Response.redirected is false we do an internal
// redirect. Otherwise, if Response.redirect is true do a non-internal
// redirect so end consumers detect the redirected state.
nsresult RedirectForResponseURL(nsIURI* aResponseURI,
bool aResponseRedirected);
nsresult StartPump();
nsresult OpenRedirectChannel();
void MaybeCallStatusAndProgress();
void MaybeCallBodyCallback();
static already_AddRefed<InterceptedHttpChannel> CreateForInterception(
PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
const TimeStamp& aAsyncOpenTimestamp);
static already_AddRefed<InterceptedHttpChannel> CreateForSynthesis(
const nsHttpResponseHead* aHead, nsIInputStream* aBody,
nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
const TimeStamp& aCreationTimestamp,
const TimeStamp& aAsyncOpenTimestamp);
Cancel(nsresult aStatus) override;
Suspend(void) override;
Resume(void) override;
GetSecurityInfo(nsISupports** aSecurityInfo) override;
AsyncOpen(nsIStreamListener* aListener) override;
LogBlockedCORSRequest(const nsAString& aMessage,
const nsACString& aCategory) override;
LogMimeTypeMismatch(const nsACString& aMessageName, bool aWarning,
const nsAString& aURL,
const nsAString& aContentType) override;
SetupFallbackChannel(const char* aFallbackKey) override;
SetPriority(int32_t aPriority) override;
SetClassFlags(uint32_t aClassFlags) override;
ClearClassFlags(uint32_t flags) override;
AddClassFlags(uint32_t flags) override;
ResumeAt(uint64_t startPos, const nsACString& entityID) override;
void DoNotifyListenerCleanup() override;
void DoAsyncAbort(nsresult aStatus) override;
} // namespace net
} // namespace mozilla
#endif // mozilla_net_InterceptedHttpChannel_h