fune/netwerk/protocol/http/InterceptedChannel.cpp
Eden Chuang 3fd3115e94 Bug 1423623 - Add telemetry for alternate data stream on service worker synthesized channels. r=bkelly, data-r=francois
1. Create a new telemetry scalar SW_ALTERNATIVE_BODY_USED_COUNT to count the
       number of the alternative body used in service worker synthesized channels.

    2. To report values of fetching related time of InterceptChannel according to
       the detail subresource type. Now subresource/script, subresource/other,
       subresource/image and subresource/stylesheet are provided, and keep using
       subresource for other types.
2017-12-15 14:35:44 +08:00

414 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)
{
}
InterceptedChannelBase::~InterceptedChannelBase()
{
}
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());
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