mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
265e672179
# ignore-this-changeset --HG-- extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
404 lines
12 KiB
C++
404 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: -*- */
|
|
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
|
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "HttpLog.h"
|
|
|
|
#include "InterceptedChannel.h"
|
|
#include "nsICancelable.h"
|
|
#include "nsInputStreamPump.h"
|
|
#include "nsIPipe.h"
|
|
#include "nsIStreamListener.h"
|
|
#include "nsITimedChannel.h"
|
|
#include "nsHttpChannel.h"
|
|
#include "HttpChannelChild.h"
|
|
#include "nsHttpResponseHead.h"
|
|
#include "nsNetUtil.h"
|
|
#include "mozilla/ConsoleReportCollector.h"
|
|
#include "mozilla/dom/ChannelInfo.h"
|
|
#include "nsIChannelEventSink.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
extern nsresult DoUpdateExpirationTime(nsHttpChannel* aSelf,
|
|
nsICacheEntry* aCacheEntry,
|
|
nsHttpResponseHead* aResponseHead,
|
|
uint32_t& aExpirationTime);
|
|
extern nsresult DoAddCacheEntryHeaders(nsHttpChannel* self,
|
|
nsICacheEntry* entry,
|
|
nsHttpRequestHead* requestHead,
|
|
nsHttpResponseHead* responseHead,
|
|
nsISupports* securityInfo);
|
|
|
|
NS_IMPL_ISUPPORTS(InterceptedChannelBase, nsIInterceptedChannel)
|
|
|
|
InterceptedChannelBase::InterceptedChannelBase(
|
|
nsINetworkInterceptController* aController)
|
|
: mController(aController),
|
|
mReportCollector(new ConsoleReportCollector()),
|
|
mClosed(false),
|
|
mSynthesizedOrReset(Invalid) {}
|
|
|
|
void InterceptedChannelBase::EnsureSynthesizedResponse() {
|
|
if (mSynthesizedResponseHead.isNothing()) {
|
|
mSynthesizedResponseHead.emplace(new nsHttpResponseHead());
|
|
}
|
|
}
|
|
|
|
void InterceptedChannelBase::DoNotifyController() {
|
|
nsresult rv = NS_OK;
|
|
|
|
if (NS_WARN_IF(!mController)) {
|
|
rv = ResetInterception();
|
|
if (NS_FAILED(rv)) {
|
|
NS_WARNING("Failed to resume intercepted network request");
|
|
CancelInterception(rv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
rv = mController->ChannelIntercepted(this);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
rv = ResetInterception();
|
|
if (NS_FAILED(rv)) {
|
|
NS_WARNING("Failed to resume intercepted network request");
|
|
CancelInterception(rv);
|
|
}
|
|
}
|
|
mController = nullptr;
|
|
}
|
|
|
|
nsresult InterceptedChannelBase::DoSynthesizeStatus(uint16_t aStatus,
|
|
const nsACString& aReason) {
|
|
EnsureSynthesizedResponse();
|
|
|
|
// Always assume HTTP 1.1 for synthesized responses.
|
|
nsAutoCString statusLine;
|
|
statusLine.AppendLiteral("HTTP/1.1 ");
|
|
statusLine.AppendInt(aStatus);
|
|
statusLine.AppendLiteral(" ");
|
|
statusLine.Append(aReason);
|
|
|
|
(*mSynthesizedResponseHead)->ParseStatusLine(statusLine);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult InterceptedChannelBase::DoSynthesizeHeader(const nsACString& aName,
|
|
const nsACString& aValue) {
|
|
EnsureSynthesizedResponse();
|
|
|
|
nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
|
|
// Overwrite any existing header.
|
|
nsresult rv = (*mSynthesizedResponseHead)->ParseHeaderLine(header);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelBase::GetConsoleReportCollector(
|
|
nsIConsoleReportCollector** aCollectorOut) {
|
|
MOZ_ASSERT(aCollectorOut);
|
|
nsCOMPtr<nsIConsoleReportCollector> ref = mReportCollector;
|
|
ref.forget(aCollectorOut);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!mReleaseHandle);
|
|
MOZ_ASSERT(aHandle);
|
|
|
|
// We need to keep it and mChannel alive until destructor clear it up.
|
|
mReleaseHandle = aHandle;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelBase::SaveTimeStamps() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// If we were not able to start the fetch event for some reason (like
|
|
// corrupted scripts), then just do nothing here.
|
|
if (mHandleFetchEventStart.IsNull()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIChannel> underlyingChannel;
|
|
nsresult rv = GetChannel(getter_AddRefs(underlyingChannel));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(underlyingChannel);
|
|
MOZ_ASSERT(timedChannel);
|
|
|
|
rv = timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
rv = timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
rv = timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
rv = timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
rv = timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
rv = timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
nsCOMPtr<nsIChannel> channel;
|
|
GetChannel(getter_AddRefs(channel));
|
|
if (NS_WARN_IF(!channel)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
bool isNonSubresourceRequest =
|
|
nsContentUtils::IsNonSubresourceRequest(channel);
|
|
nsCString navigationOrSubresource = isNonSubresourceRequest
|
|
? NS_LITERAL_CSTRING("navigation")
|
|
: NS_LITERAL_CSTRING("subresource");
|
|
|
|
nsAutoCString subresourceKey(EmptyCString());
|
|
GetSubresourceTimeStampKey(channel, subresourceKey);
|
|
|
|
// We may have null timestamps if the fetch dispatch runnable was cancelled
|
|
// and we defaulted to resuming the request.
|
|
if (!mFinishResponseStart.IsNull() && !mFinishResponseEnd.IsNull()) {
|
|
MOZ_ASSERT(mSynthesizedOrReset != Invalid);
|
|
|
|
Telemetry::HistogramID id =
|
|
(mSynthesizedOrReset == Synthesized)
|
|
? Telemetry::
|
|
SERVICE_WORKER_FETCH_EVENT_FINISH_SYNTHESIZED_RESPONSE_MS
|
|
: Telemetry::SERVICE_WORKER_FETCH_EVENT_CHANNEL_RESET_MS;
|
|
Telemetry::Accumulate(
|
|
id, navigationOrSubresource,
|
|
static_cast<uint32_t>(
|
|
(mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
|
|
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
|
|
Telemetry::Accumulate(
|
|
id, subresourceKey,
|
|
static_cast<uint32_t>(
|
|
(mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
|
|
}
|
|
}
|
|
|
|
Telemetry::Accumulate(
|
|
Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
|
|
navigationOrSubresource,
|
|
static_cast<uint32_t>((mHandleFetchEventStart - mDispatchFetchEventStart)
|
|
.ToMilliseconds()));
|
|
|
|
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
|
|
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
|
|
subresourceKey,
|
|
static_cast<uint32_t>((mHandleFetchEventStart -
|
|
mDispatchFetchEventStart)
|
|
.ToMilliseconds()));
|
|
}
|
|
|
|
if (!mFinishResponseEnd.IsNull()) {
|
|
Telemetry::Accumulate(
|
|
Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
|
|
navigationOrSubresource,
|
|
static_cast<uint32_t>(
|
|
(mFinishResponseEnd - mDispatchFetchEventStart).ToMilliseconds()));
|
|
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
|
|
Telemetry::Accumulate(
|
|
Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
|
|
subresourceKey,
|
|
static_cast<uint32_t>((mFinishResponseEnd - mDispatchFetchEventStart)
|
|
.ToMilliseconds()));
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<nsIURI> InterceptedChannelBase::SecureUpgradeChannelURI(
|
|
nsIChannel* aChannel) {
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
nsCOMPtr<nsIURI> upgradedURI;
|
|
rv = NS_GetSecureUpgradedURI(uri, getter_AddRefs(upgradedURI));
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
return upgradedURI.forget();
|
|
}
|
|
|
|
InterceptedChannelContent::InterceptedChannelContent(
|
|
HttpChannelChild* aChannel, nsINetworkInterceptController* aController,
|
|
InterceptStreamListener* aListener, bool aSecureUpgrade)
|
|
: InterceptedChannelBase(aController),
|
|
mChannel(aChannel),
|
|
mStreamListener(aListener),
|
|
mSecureUpgrade(aSecureUpgrade) {}
|
|
|
|
void InterceptedChannelContent::NotifyController() { DoNotifyController(); }
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::GetChannel(nsIChannel** aChannel) {
|
|
NS_IF_ADDREF(*aChannel = mChannel);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::ResetInterception() {
|
|
if (mClosed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
mReportCollector->FlushConsoleReports(mChannel);
|
|
|
|
mChannel->ResetInterception();
|
|
|
|
mClosed = true;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::SynthesizeStatus(uint16_t aStatus,
|
|
const nsACString& aReason) {
|
|
if (mClosed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
return DoSynthesizeStatus(aStatus, aReason);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::SynthesizeHeader(const nsACString& aName,
|
|
const nsACString& aValue) {
|
|
if (mClosed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
return DoSynthesizeHeader(aName, aValue);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::StartSynthesizedResponse(
|
|
nsIInputStream* aBody, nsIInterceptedBodyCallback* aBodyCallback,
|
|
nsICacheInfoChannel* aCacheInfoChannel, const nsACString& aFinalURLSpec,
|
|
bool aResponseRedirected) {
|
|
if (NS_WARN_IF(mClosed)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
EnsureSynthesizedResponse();
|
|
|
|
nsCOMPtr<nsIURI> originalURI;
|
|
mChannel->GetURI(getter_AddRefs(originalURI));
|
|
|
|
nsCOMPtr<nsIURI> responseURI;
|
|
if (!aFinalURLSpec.IsEmpty()) {
|
|
nsresult rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
} else if (mSecureUpgrade) {
|
|
nsresult rv =
|
|
NS_GetSecureUpgradedURI(originalURI, getter_AddRefs(responseURI));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
} else {
|
|
responseURI = originalURI;
|
|
}
|
|
|
|
bool equal = false;
|
|
originalURI->Equals(responseURI, &equal);
|
|
if (!equal) {
|
|
mChannel->ForceIntercepted(aBody, aBodyCallback, aCacheInfoChannel);
|
|
mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr(),
|
|
aResponseRedirected);
|
|
} else {
|
|
mChannel->OverrideWithSynthesizedResponse(
|
|
mSynthesizedResponseHead.ref(), aBody, aBodyCallback, mStreamListener,
|
|
aCacheInfoChannel);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::FinishSynthesizedResponse() {
|
|
if (NS_WARN_IF(mClosed)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
mReportCollector->FlushConsoleReports(mChannel);
|
|
|
|
mStreamListener = nullptr;
|
|
mClosed = true;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::CancelInterception(nsresult aStatus) {
|
|
MOZ_ASSERT(NS_FAILED(aStatus));
|
|
|
|
if (mClosed) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
mClosed = true;
|
|
|
|
mReportCollector->FlushConsoleReports(mChannel);
|
|
|
|
Unused << mChannel->Cancel(aStatus);
|
|
mStreamListener = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::SetChannelInfo(dom::ChannelInfo* aChannelInfo) {
|
|
if (mClosed) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return aChannelInfo->ResurrectInfoOnChannel(mChannel);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::GetInternalContentPolicyType(
|
|
nsContentPolicyType* aPolicyType) {
|
|
NS_ENSURE_ARG(aPolicyType);
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo;
|
|
nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (loadInfo) {
|
|
*aPolicyType = loadInfo->InternalContentPolicyType();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
InterceptedChannelContent::GetSecureUpgradedChannelURI(nsIURI** aURI) {
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (mSecureUpgrade) {
|
|
uri = SecureUpgradeChannelURI(mChannel);
|
|
} else {
|
|
nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
if (uri) {
|
|
uri.forget(aURI);
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|