Source code
Revision control
Copy as Markdown
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
#include "InterceptedHttpChannel.h"
#include "NetworkMarker.h"
#include "nsContentSecurityManager.h"
#include "nsEscape.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/ChannelInfo.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "nsHttpChannel.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIRedirectResultListener.h"
#include "nsStringStream.h"
#include "nsStreamUtils.h"
#include "nsQueryObject.h"
#include "mozilla/Logging.h"
namespace mozilla::net {
mozilla::LazyLogModule gInterceptedLog("Intercepted");
#define INTERCEPTED_LOG(args) MOZ_LOG(gInterceptedLog, LogLevel::Debug, args)
NS_IMPL_ISUPPORTS_INHERITED(InterceptedHttpChannel, HttpBaseChannel,
nsIInterceptedChannel, nsICacheInfoChannel,
nsIAsyncVerifyRedirectCallback, nsIRequestObserver,
nsIStreamListener, nsIThreadRetargetableRequest,
nsIThreadRetargetableStreamListener,
nsIClassOfService)
InterceptedHttpChannel::InterceptedHttpChannel(
PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
const TimeStamp& aAsyncOpenTimestamp)
: HttpAsyncAborter<InterceptedHttpChannel>(this),
mProgress(0),
mProgressReported(0),
mSynthesizedStreamLength(-1),
mResumeStartPos(0),
mCallingStatusAndProgress(false) {
// Pre-set the creation and AsyncOpen times based on the original channel
// we are intercepting. We don't want our extra internal redirect to mask
// any time spent processing the channel.
INTERCEPTED_LOG(("Creating InterceptedHttpChannel [%p]", this));
mChannelCreationTime = aCreationTime;
mChannelCreationTimestamp = aCreationTimestamp;
mInterceptedChannelCreationTimestamp = TimeStamp::Now();
mAsyncOpenTime = aAsyncOpenTimestamp;
}
void InterceptedHttpChannel::ReleaseListeners() {
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
}
HttpBaseChannel::ReleaseListeners();
mSynthesizedResponseHead.reset();
mRedirectChannel = nullptr;
mBodyReader = nullptr;
mReleaseHandle = nullptr;
mProgressSink = nullptr;
mBodyCallback = nullptr;
mPump = nullptr;
MOZ_DIAGNOSTIC_ASSERT(!LoadIsPending());
}
nsresult InterceptedHttpChannel::SetupReplacementChannel(
nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
uint32_t aRedirectFlags) {
INTERCEPTED_LOG(
("InterceptedHttpChannel::SetupReplacementChannel [%p] flag: %u", this,
aRedirectFlags));
nsresult rv = HttpBaseChannel::SetupReplacementChannel(
aURI, aChannel, aPreserveMethod, aRedirectFlags);
if (NS_FAILED(rv)) {
return rv;
}
rv = CheckRedirectLimit(aURI, aRedirectFlags);
NS_ENSURE_SUCCESS(rv, rv);
// While we can't resume an synthetic response, we can still propagate
// the resume params across redirects for other channels to handle.
if (mResumeStartPos > 0) {
nsCOMPtr<nsIResumableChannel> resumable = do_QueryInterface(aChannel);
if (!resumable) {
return NS_ERROR_NOT_RESUMABLE;
}
resumable->ResumeAt(mResumeStartPos, mResumeEntityId);
}
return NS_OK;
}
void InterceptedHttpChannel::AsyncOpenInternal() {
// We save this timestamp from outside of the if block in case we enable the
// profiler after AsyncOpen().
INTERCEPTED_LOG(("InterceptedHttpChannel::AsyncOpenInternal [%p]", this));
mLastStatusReported = TimeStamp::Now();
if (profiler_thread_is_being_profiled_for_markers()) {
nsAutoCString requestMethod;
GetRequestMethod(requestMethod);
profiler_add_network_marker(
mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
mLoadInfo->GetInnerWindowID(),
mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(),
mRequestHead.Version(), mClassOfService.Flags());
}
// If an error occurs in this file we must ensure mListener callbacks are
// invoked in some way. We either Cancel() or ResetInterception below
// depending on which path we take.
nsresult rv = NS_OK;
// Start the interception, record the start time.
mTimeStamps.Init(this);
mTimeStamps.RecordTime();
// We should have pre-set the AsyncOpen time based on the original channel if
// timings are enabled.
MOZ_DIAGNOSTIC_ASSERT(!mAsyncOpenTime.IsNull());
StoreIsPending(true);
StoreResponseCouldBeSynthesized(true);
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
// If we already have a synthesized body then we are pre-synthesized.
// This can happen for two reasons:
// 1. We have a pre-synthesized redirect in e10s mode. In this case
// we should follow the redirect.
// 2. We are handling a "fake" redirect for an opaque response. Here
// we should just process the synthetic body.
if (mBodyReader) {
// If we fail in this path, then cancel the channel. We don't want
// to ResetInterception() after a synthetic result has already been
// produced by the ServiceWorker.
auto autoCancel = MakeScopeExit([&] {
if (NS_FAILED(rv)) {
Cancel(rv);
}
});
// The fetch event will not be dispatched, record current time for
// FetchHandlerStart and FetchHandlerFinish.
SetFetchHandlerStart(TimeStamp::Now());
SetFetchHandlerFinish(TimeStamp::Now());
if (ShouldRedirect()) {
rv = FollowSyntheticRedirect();
return;
}
rv = StartPump();
return;
}
// If we fail the initial interception, then attempt to ResetInterception
// to fall back to network. We only cancel if the reset fails.
auto autoReset = MakeScopeExit([&] {
if (NS_FAILED(rv)) {
rv = ResetInterception(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
Cancel(rv);
}
}
});
// Otherwise we need to trigger a FetchEvent in a ServiceWorker.
nsCOMPtr<nsINetworkInterceptController> controller;
GetCallback(controller);
if (NS_WARN_IF(!controller)) {
rv = NS_ERROR_DOM_INVALID_STATE_ERR;
return;
}
rv = controller->ChannelIntercepted(this);
NS_ENSURE_SUCCESS_VOID(rv);
}
bool InterceptedHttpChannel::ShouldRedirect() const {
// Determine if the synthetic response requires us to perform a real redirect.
return nsHttpChannel::WillRedirect(*mResponseHead) &&
!mLoadInfo->GetDontFollowRedirects();
}
nsresult InterceptedHttpChannel::FollowSyntheticRedirect() {
// Perform a real redirect based on the synthetic response.
nsCOMPtr<nsIIOService> ioService;
nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString location;
rv = mResponseHead->GetHeader(nsHttp::Location, location);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// make sure non-ASCII characters in the location header are escaped.
nsAutoCString locationBuf;
if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
locationBuf)) {
location = locationBuf;
}
nsCOMPtr<nsIURI> redirectURI;
rv = ioService->NewURI(nsDependentCString(location.get()), nullptr, mURI,
getter_AddRefs(redirectURI));
NS_ENSURE_SUCCESS(rv, NS_ERROR_CORRUPTED_CONTENT);
uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
if (nsHttp::IsPermanentRedirect(mResponseHead->Status())) {
redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
}
PropagateReferenceIfNeeded(mURI, redirectURI);
bool rewriteToGET = ShouldRewriteRedirectToGET(mResponseHead->Status(),
mRequestHead.ParsedMethod());
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
CloneLoadInfoForRedirect(redirectURI, redirectFlags);
rv = NS_NewChannelInternal(getter_AddRefs(newChannel), redirectURI,
redirectLoadInfo,
nullptr, // PerformanceStorage
nullptr, // aLoadGroup
nullptr, // aCallbacks
mLoadFlags, ioService);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET,
redirectFlags);
NS_ENSURE_SUCCESS(rv, rv);
mRedirectChannel = std::move(newChannel);
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel,
redirectFlags);
if (NS_WARN_IF(NS_FAILED(rv))) {
OnRedirectVerifyCallback(rv);
} else {
// Redirect success, record the finish time and the final status.
mTimeStamps.RecordTime(InterceptionTimeStamps::Redirected);
}
return rv;
}
nsresult InterceptedHttpChannel::RedirectForResponseURL(
nsIURI* aResponseURI, bool aResponseRedirected) {
// Perform a service worker redirect to another InterceptedHttpChannel using
// the given response URL. It allows content to see the final URL where
// appropriate and also helps us enforce cross-origin restrictions. The
// resulting channel will then process the synthetic response as normal. This
// extra redirect is performed so that listeners treat the result as unsafe
// cross-origin data.
nsresult rv = NS_OK;
// We want to pass ownership of the body callback to the new synthesized
// channel. We need to hold a reference to the callbacks on the stack
// as well, though, so we can call them if a failure occurs.
nsCOMPtr<nsIInterceptedBodyCallback> bodyCallback = std::move(mBodyCallback);
RefPtr<InterceptedHttpChannel> newChannel = CreateForSynthesis(
mResponseHead.get(), mBodyReader, bodyCallback, mChannelCreationTime,
mChannelCreationTimestamp, mAsyncOpenTime);
// If the response has been redirected, propagate all the URLs to content.
// Thus, the exact value of the redirect flag does not matter as long as it's
// not REDIRECT_INTERNAL.
uint32_t flags = aResponseRedirected ? nsIChannelEventSink::REDIRECT_TEMPORARY
: nsIChannelEventSink::REDIRECT_INTERNAL;
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
CloneLoadInfoForRedirect(aResponseURI, flags);
ExtContentPolicyType contentPolicyType =
redirectLoadInfo->GetExternalContentPolicyType();
rv = newChannel->Init(aResponseURI, mCaps,
static_cast<nsProxyInfo*>(mProxyInfo.get()),
mProxyResolveFlags, mProxyURI, mChannelId,
contentPolicyType, redirectLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// Normally we don't propagate the LoadInfo's service worker tainting
// synthesis flag on redirect. A real redirect normally will want to allow
// normal tainting to proceed from its starting taint. For this particular
// redirect, though, we are performing a redirect to communicate the URL of
// the service worker synthetic response itself. This redirect still
// represents the synthetic response, so we must preserve the flag.
if (redirectLoadInfo && mLoadInfo &&
mLoadInfo->GetServiceWorkerTaintingSynthesized()) {
redirectLoadInfo->SynthesizeServiceWorkerTainting(mLoadInfo->GetTainting());
}
rv = SetupReplacementChannel(aResponseURI, newChannel, true, flags);
NS_ENSURE_SUCCESS(rv, rv);
mRedirectChannel = newChannel;
MOZ_ASSERT(mBodyReader);
MOZ_ASSERT(!LoadApplyConversion());
newChannel->SetApplyConversion(false);
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
if (NS_FAILED(rv)) {
// Make sure to call the body callback since we took ownership
// above. Neither the new channel or our standard
// OnRedirectVerifyCallback() code will invoke the callback. Do it here.
bodyCallback->BodyComplete(rv);
OnRedirectVerifyCallback(rv);
}
return rv;
}
nsresult InterceptedHttpChannel::StartPump() {
MOZ_DIAGNOSTIC_ASSERT(!mPump);
MOZ_DIAGNOSTIC_ASSERT(mBodyReader);
// We don't support resuming an intercepted channel. We can't guarantee the
// ServiceWorker will always return the same data and we can't rely on the
// http cache code to detect changes. For now, just force the channel to
// NS_ERROR_NOT_RESUMABLE which should cause the front-end to recreate the
// channel without calling ResumeAt().
//
// It would also be possible to convert this information to a range request,
// but its unclear if we should do that for ServiceWorker FetchEvents. See:
//
if (mResumeStartPos > 0) {
return NS_ERROR_NOT_RESUMABLE;
}
// For progress we trust the content-length for the "maximum" size.
// We can't determine the full size from the stream itself since
// we may only receive the data incrementally. We can't trust
// Available() here.
// TODO: We could implement an nsIFixedLengthInputStream interface and
// QI to it here. This would let us determine the total length
Unused << GetContentLength(&mSynthesizedStreamLength);
nsresult rv =
nsInputStreamPump::Create(getter_AddRefs(mPump), mBodyReader, 0, 0, true);
NS_ENSURE_SUCCESS(rv, rv);
rv = mPump->AsyncRead(this);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t suspendCount = mSuspendCount;
while (suspendCount--) {
mPump->Suspend();
}
MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
return rv;
}
nsresult InterceptedHttpChannel::OpenRedirectChannel() {
INTERCEPTED_LOG(
("InterceptedHttpChannel::OpenRedirectChannel [%p], mRedirectChannel: %p",
this, mRedirectChannel.get()));
nsresult rv = NS_OK;
if (NS_FAILED(mStatus)) {
return mStatus;
}
if (!mRedirectChannel) {
return NS_ERROR_DOM_ABORT_ERR;
}
// Make sure to do this after we received redirect veto answer,
// i.e. after all sinks had been notified
mRedirectChannel->SetOriginalURI(mOriginalURI);
// open new channel
rv = mRedirectChannel->AsyncOpen(mListener);
NS_ENSURE_SUCCESS(rv, rv);
mStatus = NS_BINDING_REDIRECTED;
return rv;
}
void InterceptedHttpChannel::MaybeCallStatusAndProgress() {
// OnStatus() and OnProgress() must only be called on the main thread. If
// we are on a separate thread, then we maybe need to schedule a runnable
// to call them asynchronousnly.
if (!NS_IsMainThread()) {
// Check to see if we are already trying to call OnStatus/OnProgress
// asynchronously. If we are, then don't queue up another runnable.
// We don't want to flood the main thread.
if (mCallingStatusAndProgress) {
return;
}
mCallingStatusAndProgress = true;
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
"InterceptedHttpChannel::MaybeCallStatusAndProgress", this,
&InterceptedHttpChannel::MaybeCallStatusAndProgress);
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
return;
}
MOZ_ASSERT(NS_IsMainThread());
// We are about to capture out progress position. Clear the flag we use
// to de-duplicate progress report runnables. We want any further progress
// updates to trigger another runnable. We do this before capture the
// progress value since we're using atomics and not a mutex lock.
mCallingStatusAndProgress = false;
// Capture the current status from our atomic count.
int64_t progress = mProgress;
MOZ_DIAGNOSTIC_ASSERT(progress >= mProgressReported);
// Do nothing if we've already made the calls for this amount of progress
// or if the channel is not configured for these calls. Note, the check
// for mProgressSink here means we will not fire any spurious late calls
// after ReleaseListeners() is executed.
if (progress <= mProgressReported || mCanceled || !mProgressSink ||
(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
return;
}
// Capture the host name on the first set of calls to avoid doing this
// string processing repeatedly.
if (mProgressReported == 0) {
nsAutoCString host;
MOZ_ALWAYS_SUCCEEDS(mURI->GetHost(host));
CopyUTF8toUTF16(host, mStatusHost);
}
mProgressSink->OnStatus(this, NS_NET_STATUS_READING, mStatusHost.get());
mProgressSink->OnProgress(this, progress, mSynthesizedStreamLength);
mProgressReported = progress;
}
void InterceptedHttpChannel::MaybeCallBodyCallback() {
nsCOMPtr<nsIInterceptedBodyCallback> callback = std::move(mBodyCallback);
if (callback) {
callback->BodyComplete(mStatus);
}
}
// static
already_AddRefed<InterceptedHttpChannel>
InterceptedHttpChannel::CreateForInterception(
PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
const TimeStamp& aAsyncOpenTimestamp) {
// Create an InterceptedHttpChannel that will trigger a FetchEvent
// in a ServiceWorker when opened.
RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
return ref.forget();
}
// static
already_AddRefed<InterceptedHttpChannel>
InterceptedHttpChannel::CreateForSynthesis(
const nsHttpResponseHead* aHead, nsIInputStream* aBody,
nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
const TimeStamp& aCreationTimestamp, const TimeStamp& aAsyncOpenTimestamp) {
MOZ_DIAGNOSTIC_ASSERT(aHead);
MOZ_DIAGNOSTIC_ASSERT(aBody);
// Create an InterceptedHttpChannel that already has a synthesized response.
// The synthetic response will be processed when opened. A FetchEvent
// will not be triggered.
RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
ref->mResponseHead = MakeUnique<nsHttpResponseHead>(*aHead);
ref->mBodyReader = aBody;
ref->mBodyCallback = aBodyCallback;
return ref.forget();
}
NS_IMETHODIMP InterceptedHttpChannel::SetCanceledReason(
const nsACString& aReason) {
return SetCanceledReasonImpl(aReason);
}
NS_IMETHODIMP InterceptedHttpChannel::GetCanceledReason(nsACString& aReason) {
return GetCanceledReasonImpl(aReason);
}
NS_IMETHODIMP
InterceptedHttpChannel::CancelWithReason(nsresult aStatus,
const nsACString& aReason) {
return CancelWithReasonImpl(aStatus, aReason);
}
NS_IMETHODIMP
InterceptedHttpChannel::Cancel(nsresult aStatus) {
INTERCEPTED_LOG(("InterceptedHttpChannel::Cancel [%p]", this));
// Note: This class has been designed to send all error results through
// Cancel(). Don't add calls directly to AsyncAbort() or
// DoNotifyListener(). Instead call Cancel().
if (mCanceled) {
return NS_OK;
}
// The interception is canceled, record the finish time stamp and the final
// status
mTimeStamps.RecordTime(InterceptionTimeStamps::Canceled);
mCanceled = true;
if (mLastStatusReported && profiler_thread_is_being_profiled_for_markers()) {
// These do allocations/frees/etc; avoid if not active
// mLastStatusReported can be null if Cancel is called before we added the
// start marker.
nsAutoCString requestMethod;
GetRequestMethod(requestMethod);
int32_t priority = PRIORITY_NORMAL;
GetPriority(&priority);
uint64_t size = 0;
GetEncodedBodySize(&size);
profiler_add_network_marker(
mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_CANCEL,
mLastStatusReported, TimeStamp::Now(), size, kCacheUnknown,
mLoadInfo->GetInnerWindowID(),
mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(),
mRequestHead.Version(), mClassOfService.Flags(), &mTransactionTimings,
std::move(mSource));
}
MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus));
if (NS_SUCCEEDED(mStatus)) {
mStatus = aStatus;
}
if (mPump) {
return mPump->Cancel(mStatus);
}
return AsyncAbort(mStatus);
}
NS_IMETHODIMP
InterceptedHttpChannel::Suspend(void) {
++mSuspendCount;
if (mPump) {
return mPump->Suspend();
}
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::Resume(void) {
--mSuspendCount;
if (mPump) {
return mPump->Resume();
}
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::GetSecurityInfo(
nsITransportSecurityInfo** aSecurityInfo) {
nsCOMPtr<nsITransportSecurityInfo> ref(mSecurityInfo);
ref.forget(aSecurityInfo);
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::AsyncOpen(nsIStreamListener* aListener) {
INTERCEPTED_LOG(("InterceptedHttpChannel::AsyncOpen [%p], listener: %p", this,
aListener));
nsCOMPtr<nsIStreamListener> listener(aListener);
nsresult rv =
nsContentSecurityManager::doContentSecurityCheck(this, listener);
if (NS_WARN_IF(NS_FAILED(rv))) {
Cancel(rv);
return rv;
}
if (mCanceled) {
return mStatus;
}
// After this point we should try to return NS_OK and notify the listener
// of the result.
mListener = aListener;
AsyncOpenInternal();
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage,
const nsACString& aCategory,
bool aIsWarning) {
// Synthetic responses should not trigger CORS blocking.
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
InterceptedHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
bool aWarning,
const nsAString& aURL,
const nsAString& aContentType) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
InterceptedHttpChannel::GetIsAuthChannel(bool* aIsAuthChannel) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
InterceptedHttpChannel::SetPriority(int32_t aPriority) {
mPriority = std::clamp<int32_t>(aPriority, INT16_MIN, INT16_MAX);
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::SetClassFlags(uint32_t aClassFlags) {
mClassOfService.SetFlags(aClassFlags);
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::ClearClassFlags(uint32_t aClassFlags) {
mClassOfService.SetFlags(~aClassFlags & mClassOfService.Flags());
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::AddClassFlags(uint32_t aClassFlags) {
mClassOfService.SetFlags(aClassFlags | mClassOfService.Flags());
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::SetClassOfService(ClassOfService cos) {
mClassOfService = cos;
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::SetIncremental(bool incremental) {
mClassOfService.SetIncremental(incremental);
return NS_OK;
}
NS_IMETHODIMP
InterceptedHttpChannel::ResumeAt(uint64_t aStartPos,
const nsACString& aEntityId) {
// We don't support resuming synthesized responses, but we do track this
// information so it can be passed on to the resulting nsHttpChannel if
// ResetInterception is called.
mResumeStartPos = aStartPos;
mResumeEntityId = aEntityId;
return NS_OK;
}
void InterceptedHttpChannel::DoNotifyListenerCleanup() {
// Prefer to cleanup in ReleaseListeners() as it seems to be called
// more consistently in necko.
}
void InterceptedHttpChannel::DoAsyncAbort(nsresult aStatus) {
Unused << AsyncAbort(aStatus);
}
namespace {
class ResetInterceptionHeaderVisitor final : public nsIHttpHeaderVisitor {
nsCOMPtr<nsIHttpChannel> mTarget;
~ResetInterceptionHeaderVisitor() = default;
NS_IMETHOD
VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
// We skip Cookie header here, since it will be added during
// nsHttpChannel::AsyncOpen.
if (aHeader.Equals(nsHttp::Cookie.val())) {
return NS_OK;
}
if (aValue.IsEmpty()) {
return mTarget->SetEmptyRequestHeader(aHeader);
}
return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
}
public:
explicit ResetInterceptionHeaderVisitor(nsIHttpChannel* aTarget)
: mTarget(aTarget) {
MOZ_DIAGNOSTIC_ASSERT(mTarget);
}
NS_DECL_ISUPPORTS
};