| # 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: |
| * The original API implemented in Chrome (Reporting V0) can be found at |
| [https://www.w3.org/TR/2018/WD-reporting-1-20180925/]. |
| * The newer API is split into two parts. Document and worker-level reporting |
| (Reporting V1) is specified in the [draft reporting spec] |
| (https://w3c.github.io/reporting/), while Network-level reporting is |
| specified in the [draft network reporting spec] |
| (https://w3c.github.io/reporting/network-reporting.html). |
| |
| 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](https://w3c.github.io/reporting/#observers) |
| interface lives [in `//third_party/blink/renderer/core/frame/`][1]. |
| |
| * It queues reports via the `NetworkContext` using a |
| `blink::mojom::ReportingServiceProxy` (implemented [in |
| `//content/browser/network/`][2]), which can queue Intervention, Deprecation, |
| CSP Violation, and Permissions Policy Violation reports. |
| |
| * The `ChromeNetworkDelegate` [in `//chrome/browser/net/`][3] 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. |
| |
| * This functionality is tested on Android by way of sending Network Error |
| Logging reports [in the Cronet Java tests][4]. |
| |
| ## 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. |
| |
| [1]: https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/renderer/core/frame/reporting_observer.h |
| [2]: https://chromium.googlesource.com/chromium/src/+/HEAD/content/browser/network/reporting_service_proxy.cc |
| [3]: https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/browser/net/chrome_network_delegate.h |
| [4]: https://chromium.googlesource.com/chromium/src/+/HEAD/components/cronet/android/test/javatests/src/org/chromium/net/NetworkErrorLoggingTest.java |