gecko-dev/netwerk/protocol/http/InterceptedChannel.cpp
Sylvestre Ledru 265e672179 Bug 1511181 - Reformat everything to the Google coding style r=ehsan a=clang-format
# ignore-this-changeset

--HG--
extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
2018-11-30 11:46:48 +01:00

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