Reporting

Reporting is a central mechanism for sending out-of-band error reports to origins from various other components (e.g. HTTP Public Key Pinning, Interventions, or Content Security Policy could potentially use it).

The parts of it that are exposed to the web platform are specified in three documents:

This document assumes that you've read those ones.

Reporting in Chromium

Reporting is implemented as part of the network stack in Chromium, such that it can be used by other parts of the network stack (e.g. HPKP) or by non-browser embedders as well as by Chromium.

Inside //net

  • The top-level class is the ReportingService. This lives in the URLRequestContext, and provides the high-level operations used by other parts of //net and other components: queueing reports, handling configuration headers, clearing browsing data, and so on.

    • A ReportingPolicy specifies a number of parameters for the Reporting API, such as the maximum number of reports and endpoints to queue, the time interval between delivery attempts, whether or not to persist reports and clients across network changes, etc. It is used to create a ReportingService obeying the specified parameters.

    • Within ReportingService lives ReportingContext, which in turn contains the inner workings of Reporting, spread across several classes:

      • The ReportingCache stores undelivered reports and endpoint configurations (aka “clients” in the V0 spec, and the named endpoint per reporting source in the V1 spec).

      • The ReportingHeaderParser parses Report-To: and `Reporting-Endpoints' headers and updates the cache accordingly.

      • The ReportingDeliveryAgent reads reports from the cache, decides which endpoints to deliver them to, and attempts to do so. It uses a couple of helper classes:

        • The ReportingUploader does the low-level work of delivering reports: accepts a URL and JSON from the DeliveryAgent, creates a URLRequest, and parses the result. It also handles sending CORS preflight requests for cross-origin report uploads.

        • The ReportingEndpointManager chooses an endpoint from the cache when one is requested by the ReportingDeliveryAgent, and manages exponential backoff (using BackoffEntry) for failing endpoints.

      • The ReportingGarbageCollector periodically examines the cache and removes reports that have remained undelivered for too long, or that have failed delivery too many times.

      • The ReportingBrowsingDataRemover examines the cache upon request and removes browsing data (reports and endpoints) of selected types and origins.

      • The ReportingDelegate calls upon the NetworkDelegate (see below) to check permissions for queueing/sending reports and setting/using clients.

  • The ReportingService is set up in a URLRequestContext by passing a ReportingPolicy to the URLRequestContextBuilder. This creates a ReportingService which is owned by the URLRequestContext.

  • Report-To: headers are processed by an HttpNetworkTransaction when they are received, and passed on to the ReportingService to be added to the cache.

  • Reporting-Endpoints: headers are initially parsed by PopulateParsedHeaders, where the raw header data is run through the Structured Headers parser. If valid, this structure is stored on the network response until a reporting source can be associated with it, and is then passed through the ReportingService to be further validated and added to the cache.

  • A reporting source, used only by V1 reports, is a base::UnguessableToken associated with the document (or worker eventually) which configures reporting using a Reporting-Endpoints: header. This same token must be passed into the ReportingService when a report is queued for the correct endpoint to be found. Since the ReportingService in //net does not know anything about documents or workers, it tracks configurations and reports using this source token. Any object creating such a token is responsible for informing the ReportingService when the token will no longer be used (when the document is destroyed, for instance.) This will cause any outstanding reports for that token to be sent, and the configuration removed from the cache.

Outside //net

  • In the network service, a network::NetworkContext queues reports by getting the ReportingService from the URLRequestContext.

  • The JavaScript ReportingObserver interface lives in //third_party/blink/renderer/core/frame/.

    • It queues reports via the NetworkContext using a blink::mojom::ReportingServiceProxy (implemented in //content/browser/network/), which can queue Intervention, Deprecation, CSP Violation, and Permissions Policy Violation reports.
  • The ChromeNetworkDelegate in //chrome/browser/net/ checks permissions for queueing reports and setting/using clients based on whether cookie access is allowed, and checks permissions for sending reports using a ReportingPermissionsChecker, which checks whether the user has allowed report uploading via the BACKGROUND_SYNC permission.

  • Cronet can configure “preloaded” Report-To: headers (as well as Network Error Logging headers) when initializing a CronetURLRequestContext, to allow embedders to collect and send reports before having received a header in an actual response.

Differences between V0 and V1 reporting

The original V0 reporting API included support for the Report-To header only, which configures endpoint groups which apply to an entire origin. This is still required for Network Error Logging, as those reports are not associated with any successful document load.

All V0 reports destined for the same endpoint group may be bundled together for delivery, regardless of their source (subject to NIK isolation).

V1 reporting drops the Report-To header in favor of Reporting-Endpoints, which configures named endpoints (single URLs) which are only valid for the network resource with which the header was sent. (In general, this means documents and workers, since other resources do not currently generate reports. Chrome ignores any Reporting-Endpoints headers on those responses.) The V1 API does not support multiple weighted URLs for an endpoint, or failover between them.

V1 reports from the same source may be bundled together in a single delivery, but must be delivered separtely from other reports, even those coming from a different Document object at the same URL.

Supporting both V0 and V1 reporting in the same codebase

Chrome cannot yet drop support for NEL, and therefore for the Report-To header. Until we can, it is possible for reports to be sent to endpoints configured with either header. NEL reports can only go to those endpoint groups configured with Report-To.

To support both mechanisms simultaneously, we do the following:

  • V1 endpoints are stored in the cache along with V0 endpoint groups. Separate maps are kept of (origin -> endpoint groups) and (source token -> endpoints).

  • All reports which can be associated with a specific source (currently all reports except for NEL, which requires origin-scoped V0 configuration) must be queued with that source's reporting source token.

  • When a report is to be delivered, the ReportingDeliveryAgent will first attempt to find a matching V1 endpoint for the source. Only if that is unsuccessful, because the source is null, or because the named endpoint is not configured, will it fall back to searching for a matching V0 named endpoint group.