xref: /aosp_15_r20/external/cronet/net/docs/life-of-a-feature.md (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Life of a Feature
2
3In the years since the Chromium browser was first open-sourced, the `//net`
4directory has expanded from being the basis of loading web content in the
5Chromium browser to accommodating a wide variety of networking needs,
6both in the Chromium browser and in other Google and third-party products
7and projects.
8
9This brings with it many new opportunities, such as the ability to
10introduce new protocols rapidly or push Web security forward, as well as
11new challenges, such as how to balance the needs of various `//net`
12consumers effectively.
13
14To make it easier to contribute new features or to change existing
15behaviors in `//net`, this document tries to capture the life of a
16feature in `//net`, from initial design to the eventual possibility of
17deprecation and removal.
18
19## Supported Projects
20
21When considering the introduction of a new `//net` feature or changing
22a `//net` behavior, it's first necessary to understand where `//net`
23is used, how it is used, and what the various constraints and limits are.
24
25To understand a more comprehensive matrix of the supported platforms and
26constraints, see [Supported Projects](supported-projects.md). When
27examining a new feature request, or a change in behavior, it's necessary
28to consider dimensions such as:
29
30  * Does this feature apply to all supported projects, or is this something
31    like a Browser-only feature?
32  * Does this feature apply to all supported platforms, or is this something
33    specific to a particular subset?
34  * Is the feature a basic networking library feature, or is it specific to
35    something in the Web Platform?
36  * Will some projects wish to strip the feature in order to meet targets
37    such as memory usage (RAM) or binary size?
38  * Does it depend on Google services or Google-specific behaviors or
39    features?
40  * How will this feature be tested / experimented with? For example,
41    __Field Trials (Finch)__ and __User Metrics (UMA)__ may not be available
42    on a number of platforms.
43  * How risky is the feature towards compatibility/stability? How will it
44    be undone if there is a bug?
45  * Are the power/memory/CPU/bandwidth requirements appropriate for the
46    targeted projects and/or platforms?
47
48## Design and Layering
49
50Once the supported platforms and constraints are identified, it's necessary
51to determine how to actually design the feature to meet those constraints,
52in hopefully the easiest way possible both for implementation and consumption.
53
54### Designing for multiple platforms
55
56In general, `//net` features try to support all platforms with a common
57interface, and generally eschew OS-specific interfaces from being exposed as
58part of `//net`.
59
60Cross-platform code is generally done via declaring an interface named
61`foo.h`, which is common for all platforms, and then using the build-system to
62do compile-time switching between implementations in `foo_win.cc`, `foo_mac.cc`,
63`foo_android.cc`, etc.
64
65The goal is to ensure that consumers generally don't have to think about
66OS-specific considerations, and can instead code to the interface.
67
68### Designing for multiple products
69
70While premature abstraction can significantly harm readability, if it is
71anticipated that different products will have different implementation needs,
72or may wish to selectively disable the feature, it's often necessary to
73abstract that interface sufficiently in `//net` to allow for dependency
74injection.
75
76This is true whether discussing concrete classes and interfaces or something
77as simple a boolean configuration flag that different consumers wish to set
78differently.
79
80The two most common approaches in `//net` are injection and delegation.
81
82#### Injection
83
84Injection refers to the pattern of defining the interface or concrete
85configuration parameter (such as a boolean), along with the concrete
86implementation, but requiring the `//net` embedder to supply an instance
87of the interface or the configuration parameters (perhaps optionally).
88
89Examples of this pattern include things such as the `ProxyConfigService`,
90which has concrete implementations in `//net` for a variety of platforms'
91configuration of proxies, but which requires it be supplied as part of the
92`URLRequestContextGetter` building, if proxies are going to be supported.
93
94An example of injecting configuration flags can be seen in the
95`HttpNetworkSessionParams` structure, which is used to provide much of
96the initialization parameters for the HTTP layer.
97
98The ideal form of injection is to pass ownership of the injected object,
99such as via a `std::unique_ptr<Foo>`. While this is not consistently
100applied in `//net`, as there are a number of places in which ownership is
101either shared or left to the embedder, with the injected object passed
102around as a naked/raw pointer, this is generally seen as an anti-pattern
103and not to be mirrored for new features.
104
105#### Delegation
106
107Delegation refers to forcing the `//net` embedder to respond to specific
108delegated calls via a Delegate interface that it implements. In general,
109when using the delegate pattern, ownership of the delegate should be
110transferred, so that the lifetime and threading semantics are clear and
111unambiguous.
112
113That is, for a given class `Foo`, which has a `Foo::Delegate` interface
114defined to allow embedders to alter behavior, prefer a constructor that
115is
116```
117explicit Foo(std::unique_ptr<Delegate> delegate);
118```
119so that it is clear that the lifetime of `delegate` is determined by
120`Foo`.
121
122While this may appear similar to Injection, the general difference
123between the two approaches is determining where the bulk of the
124implementation lies. With Injection, the interface describes a
125behavior contract that concrete implementations must adhere to; this
126allows for much more flexibility with behavior, but with the downside
127of significantly more work to implement or extend. Delegation attempts
128to keep the bulk of the implementation in `//net`, and the decision as
129to which implementation to use in `//net`, but allows `//net` to
130provide specific ways in which embedders can alter behaviors.
131
132The most notable example of the delegate pattern is `URLRequest::Delegate`,
133which keeps the vast majority of the loading logic within `URLRequest`,
134but allows the `URLRequest::Delegate` to participate during specific times
135in the request lifetime and alter specific behaviors as necessary. (Note:
136`URLRequest::Delegate`, like much of the original `//net` code, doesn't
137adhere to the recommended lifetime patterns of passing ownership of the
138Delegate. It is from the experience debugging and supporting these APIs
139that the `//net` team strongly encourages all new code pass explicit
140ownership, to reduce the complexity and risk of lifetime issues).
141
142While the use of a `base::RepeatingCallback` can also be considered a form of
143delegation, the `//net` layer tries to eschew any callbacks that can be
144called more than once, and instead favors defining class interfaces
145with concrete behavioral requirements in order to ensure the correct
146lifetimes of objects and to adjust over time. When `//net` takes a
147callback (e.g. `net::CompletionOnceCallback`), the intended pattern is to
148signal the asynchronous completion of a single method, invoking that
149callback at most once before deallocating it. For more discussion
150of these patterns, see [Code Patterns](code-patterns.md).
151
152### Understanding the Layering
153
154A significant challenge many feature proposals face is understanding the
155layering in `//net` and what different portions of `//net` are allowed to
156know.
157
158#### Socket Pools
159
160The most common challenge feature proposals encounter is the awareness
161that the act of associating an actual request to make with a socket is
162done lazily, referred to as "late-binding".
163
164With late-bound sockets, a given `URLRequest` will not be assigned an actual
165transport connection until the request is ready to be sent. This allows for
166reprioritizing requests as they come in, to ensure that higher priority requests
167get preferential treatment, but it also means that features or data associated
168with a `URLRequest` generally don't participate in socket establishment or
169maintenance.
170
171For example, a feature that wants to associate the low-level network socket
172with a `URLRequest` during connection establishment is not something that the
173`//net` design supports, since the `URLRequest` is kept unaware of how sockets
174are established by virtue of the socket pools and late binding. This allows for
175more flexibility when working to improve performance, such as the ability to
176coalesce multiple logical 'sockets' over a single HTTP/2 or QUIC stream, which
177may only have a single physical network socket involved.
178
179#### Making Additional Requests
180
181From time to time, `//net` feature proposals will involve needing to load
182a secondary resource as part of processing. For example, feature proposals have
183involved fetching a `/.well-known/` URI or reporting errors to a particular URL.
184
185This is particularly challenging, because often, these features are implemented
186deeper in the network stack, such as [`//net/cert`](../cert), [`//net/http`](../http),
187or [`//net/filter`](../filter), which [`//net/url_request`](../url_request) depends
188on. Because `//net/url_request` depends on these low-level directories, it would
189be a circular dependency to have these directories depend on `//net/url_request`,
190and circular dependencies are forbidden.
191
192The recommended solution to address this is to adopt the delegation or injection
193patterns. The lower-level directory will define some interface that represents the
194"I need this URL" request, and then elsewhere, in a directory allowed to depend
195on `//net/url_request`, an implementation of that interface/delegate that uses
196`//net/url_request` is implemented.
197
198### Understanding the Lifetimes
199
200Understanding the object lifetime and dependency graph can be one of the largest
201challenges to contributing and maintaining `//net`. As a consequence, features
202which require introducing more complexity to the lifetimes of objects generally
203have a greater challenge to acceptance.
204
205The `//net` stack is designed heavily around a sync-or-async pattern, as
206documented in [Code Patterns](code-patterns.md), while also having a strong
207requirement that it be possible to cleanly shutdown the network stack. As a
208consequence, features should have precise, well-defined lifetime semantics
209and support graceful cleanup. Further, because much of the network stack can
210have web-observable side-effects, it is often required for tasks to have
211defined sequencing that cannot be reordered. To be ensure these requirements
212are met, features should attempt to model object lifetimes as a hierarchical
213DAG, using explicit ownership and avoiding the use of reference counting or
214weak pointers as part of any of the exposed API contracts (even for features
215only consumed in `//net`). Features that pay close attention to the lifetime
216semantics are more likely to be reviewed and accepted than those that leave
217it ambiguous.
218
219In addition to preferring explicit lifetimes, such as through judicious use of
220`std::unique_ptr<>` to indicate ownership transfer of dependencies, many
221features in `//net` also expect that if a `base::{Once, Repeating}Callback` is
222involved (which includes `net::CompletionOnceCallback`), then it's possible that
223invoking the callback may result in the deletion of the current (calling)
224object. As further expanded upon in [Code Patterns](code-patterns.md), features
225and changes should be designed such that any callback invocation is the last bit
226of code executed, and that the callback is accessed via the stack (such as
227through the use of `std::move(callback_).Run()`.
228
229### Specs: What Are They Good For
230
231As `//net` is used as the basis for a number of browsers, it's an important part
232of the design philosophy to ensure behaviors are well-specified, and that the
233implementation conforms to those specifications. This may be seen as burdensome
234when it's unclear whether or not a feature will 'take off,' but it's equally
235critical to ensure that the Chromium projects do not fork the Web Platform.
236
237#### Incubation Is Required
238
239`//net` respects Chromium's overall position of [incubation first](https://groups.google.com/a/chromium.org/d/msg/blink-dev/PJ_E04kcFb8/baiLN3DTBgAJ) standards development.
240
241With an incubation first approach, before introducing any new features that
242might be exposed over the wire to servers, whether they are explicit behaviors,
243such as adding new headers, or implicit behaviors such as
244[Happy Eyeballs](https://tools.ietf.org/html/rfc6555), should have some form
245of specification written. That specification should at least be on an
246incubation track, and the expectation is that the specification should have a
247direct path to an appropriate standards track. Features which don't adhere to
248this pattern, or which are not making progress towards a standards track, will
249require high-level approvals, to ensure that the Platform doesn't fragment.
250
251#### Introducing New Headers
252
253A common form of feature request is the introduction of new headers, either via
254the `//net` implementation directly, or through consuming `//net` interfaces
255and modifying headers on the fly.
256
257The introduction of any additional headers SHOULD have an incubated spec attached,
258ideally with cross-vendor interest. Particularly, headers which only apply to
259Google or Google services are very likely to be rejected outright.
260
261#### Making Additional Requests
262
263While it's necessary to provide abstraction around `//net/url_request` for
264any lower-level components that may need to make additional requests, for most
265features, that's not all that is necessary. Because `//net/url_request` only
266provides a basic HTTP fetching mechanism, it's insufficient for any Web Platform
267feature, because it doesn't consider the broader platform concerns such as
268interactions with CORS, Service Workers, cookie and authentication policies, or
269even basic interactions with optional features like Extensions or SafeBrowsing.
270
271To account for all of these things, any resource fetching that is to support
272a feature of the Web Platform, whether because the resource will be directly
273exposed to web content (for example, an image load or prefetch) or because it
274is in response to invoking a Web Platform API (for example, invoking the
275credential management API), the feature's resource fetching should be
276explainable in terms of the  [Fetch Living Standard](https://fetch.spec.whatwg.org/).
277The Fetch standard defines a JavaScript API for fetching resources, but more
278importantly, defines a common set of infrastructure and terminology that
279tries to define how all resource loads in the Web Platform happen - whether
280it be through the JavaScript API, through `XMLHttpRequest`, or the `src`
281attribute in HTML tags, for example.
282
283This also includes any resource fetching that wishes to use the same socket
284pools or caches as the Web Platform, to ensure that every resource that is
285web exposed (directly or indirectly) is fetched in a consistent and
286well-documented way, thus minimizing platform fragmentation and security
287issues.
288
289There are exceptions to this, however, but they're generally few and far
290between. In general, any feature that needs to define an abstraction to
291allow it to "fetch resources," likely needs to also be "explained in terms
292of Fetch".
293
294## Implementation
295
296In general, prior to implementing, try to get a review on [email protected]
297for the general feedback and design review.
298
299In addition to the [email protected] early review, `//net` requires that any
300browser-exposed behavior should also adhere to the
301[Blink Process](https://www.chromium.org/blink#new-features), which includes an
302"Intent to Implement" message to [email protected]
303
304For features that are unclear about their future, such as experiments or trials,
305it's also expected that the design planning will also account for how features
306will be removed cleanly. For features that radically affect the architecture of
307`//net`, expect a high bar of justification, since reversing those changes if
308they fail to pan out can cause significant disruption to productivity and
309stability.
310
311## Deprecation
312
313Plan for obsolence, hope for success. Similar to implementation, features that
314are to be removed should also go through the
315[Blink Process](https://www.chromium.org/blink#TOC-Web-Platform-Changes:-Process)
316for removing features.
317
318Note that due to the diversity of [Supported Projects](supported-projects.md),
319removing a feature while minimizing disruption can be just as complex as adding
320a feature. For example, relying solely on __User Metrics (UMA)__ to signal the
321safety of removing a feature may not consider all projects, and relying on
322__Field Trials (Finch)__ to assess risk or restore the 'legacy' behavior may not
323work on all projects either.
324
325It's precisely because of these challenges that there's such a high bar for
326adding features, because they may represent multi-year commitments to support,
327even when the feature itself is deprecated or targeted for removal.
328