blob: bcf67e66cadf44b0a810d85f7b01f1c8028cd811 [file] [log] [blame]
Hiroshige Hayashizakibf37abc32023-09-13 10:02:361// Copyright 2023 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_RESPONSE_READER_H_
6#define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_RESPONSE_READER_H_
7
8#include "base/time/time.h"
Hiroshige Hayashizakibf37abc32023-09-13 10:02:369#include "content/browser/preloading/prefetch/prefetch_streaming_url_loader_common_types.h"
10#include "content/common/content_export.h"
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3611#include "mojo/public/cpp/bindings/receiver_set.h"
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3612#include "mojo/public/cpp/bindings/remote_set.h"
Jeremy Roman43c200c2024-06-04 15:51:2413#include "net/http/http_cookie_indices.h"
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3614#include "services/network/public/mojom/url_loader.mojom.h"
15#include "services/network/public/mojom/url_response_head.mojom-forward.h"
16
Hiroshige Hayashizaki57872f32025-07-09 11:52:0217namespace ukm::builders {
18class PrefetchProxy_PrefetchedResource;
19} // namespace ukm::builders
20
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3621namespace content {
22
Hiroshige Hayashizaki57872f32025-07-09 11:52:0223class PrefetchContainer;
Hiroshige Hayashizaki583b4962025-08-19 18:58:2624class PrefetchDataPipeTee;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3625class PrefetchStreamingURLLoader;
Hiroshige Hayashizaki055871f22025-03-14 03:27:3826class ServiceWorkerClient;
Hiroshige Hayashizaki0830e182025-03-14 02:03:0627class ServiceWorkerMainResourceHandle;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3628
29// `PrefetchResponseReader` stores the prefetched data needed for serving, and
30// serves URLLoaderClients (`serving_url_loader_clients_`). One
31// `PrefetchResponseReader` corresponds to one
32// `PrefetchContainer::SinglePrefetch`, i.e. one redirect hop.
33//
34// A sequences of events are received from `PrefetchStreamingURLLoader` and
35// served to each of `serving_url_loader_clients_`.
36//
37// `PrefetchResponseReader` is kept alive by:
38// - `PrefetchContainer::SinglePrefetch::response_reader_`
39// as long as `PrefetchContainer` is alive,
40// - `PrefetchResponseReader::self_pointer_`
41// while it is serving to its `mojom::URLLoaderClient`, or
42// - The `PrefetchRequestHandler` returned by `CreateRequestHandler()`
43// until it is called.
44//
Alison Gale770f3fc2024-04-27 00:39:5845// TODO(crbug.com/40064891): Currently at most one client per
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3646// `PrefetchResponseReader` is allowed due to other servablility conditions.
47// Upcoming CLs will enable multiple clients/navigation requests per
48// `PrefetchResponseReader` behind a flag.
49class CONTENT_EXPORT PrefetchResponseReader final
50 : public network::mojom::URLLoader,
51 public base::RefCounted<PrefetchResponseReader> {
52 public:
Hiroshige Hayashizaki40270b72025-07-23 01:52:0153 PrefetchResponseReader(base::OnceClosure on_determined_head_callback,
54 OnPrefetchResponseCompletedCallback
55 on_prefetch_response_completed_callback);
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3656
57 void SetStreamingURLLoader(
58 base::WeakPtr<PrefetchStreamingURLLoader> streaming_url_loader);
59 base::WeakPtr<PrefetchStreamingURLLoader> GetStreamingLoader() const;
60
61 // Asynchronously release `self_pointer_` if eligible. Note that `this` might
62 // be still be kept alive by others even after that.
63 void MaybeReleaseSoonSelfPointer();
64
65 // Adds events from the methods with the same names in
66 // `PrefetchStreamingURLLoader` to `event_queue_` and existing
67 // `serving_url_loader_clients_`.
68 void OnReceiveEarlyHints(network::mojom::EarlyHintsPtr early_hints);
Hiroshige Hayashizaki0830e182025-03-14 02:03:0669 void OnReceiveResponse(
70 std::optional<PrefetchErrorOnResponseReceived> status,
71 network::mojom::URLResponseHeadPtr head,
72 mojo::ScopedDataPipeConsumerHandle body,
73 std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle);
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3674 void HandleRedirect(PrefetchRedirectStatus redirect_status,
75 const net::RedirectInfo& redirect_info,
76 network::mojom::URLResponseHeadPtr redirect_head);
77 void OnTransferSizeUpdated(int32_t transfer_size_diff);
78 void OnComplete(network::URLLoaderCompletionStatus completion_status);
79
80 // Creates a request handler to serve the response of the prefetch.
Hiroshige Hayashizaki8f74d22b2023-09-28 04:48:0881 //
82 // `CreateRequestHandler()` is responsible for the final check for servability
83 // and can return a null PrefetchRequestHandler if the final check fails (even
84 // if `GetServableState()` previously returned `kServable`).
85 //
86 // The caller is responsible for:
87 // - Cookie-related checks and processing.
88 // For example, checking `HaveDefaultContextCookiesChanged()` is false and
89 // copying isolated cookies if needed.
90 // `PrefetchResponseReader::CreateRequestHandler()`,
91 // `PrefetchResponseReader::Servable()` nor
92 // `PrefetchContainer::GetServableState()` don't perform cookie-related
93 // checks.
94 // - Checking `Servable()`/`GetServableState()`.
95 // `cacheable_duration` is checked only there.
Hiroshige Hayashizaki055871f22025-03-14 03:27:3896 std::pair<PrefetchRequestHandler, base::WeakPtr<ServiceWorkerClient>>
97 CreateRequestHandler();
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3698
99 bool Servable(base::TimeDelta cacheable_duration) const;
100 bool IsWaitingForResponse() const;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36101 const network::mojom::URLResponseHead* GetHead() const { return head_.get(); }
102
Jeremy Roman43c200c2024-06-04 15:51:24103 // True if this response had Vary: Cookie (or Vary: *), and a Cookie-Indices
104 // header also applies.
105 bool VariesOnCookieIndices() const;
106
107 // True if the request cookies `cookies` match those originally used when the
108 // prefetch request was made, to the extent required by Cookie-Indices.
109 // Do not call this if |VariesOnCookieIndices()| returns false.
110 bool MatchesCookieIndices(
111 base::span<const std::pair<std::string, std::string>> cookies) const;
112
Hiroshige Hayashizaki57872f32025-07-09 11:52:02113 void RecordOnPrefetchContainerDestroyed(
114 base::PassKey<PrefetchContainer>,
115 ukm::builders::PrefetchProxy_PrefetchedResource& builder) const;
116
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:32117 // Valid state transitions (which imply valid event sequences) are:
118 // - Redirect: `kStarted` -> `kRedirectHandled`
119 // - Non-redirect: `kStarted` -> `kResponseReceived` -> `kCompleted`
120 // - Failure: `kStarted` -> `kFailed`
121 // `kStarted` -> `kFailedRedirect`
122 // `kStarted` -> `kFailedResponseReceived` -> `kFailed`
123 // `kStarted` -> `kResponseReceived` -> `kFailed`
124 // Optional `OnReceiveEarlyHints()` and `OnTransferSizeUpdated()` events can
125 // be received in any non-final states.
126 enum class LoadState {
127 // Initial state, not yet receiving a redirect nor non-redirect response.
128 kStarted,
129
130 // [Final] A redirect response is received (`HandleRedirect()` is called).
131 // This is a final state because we always switch to a new
132 // `PrefetchResponseReader` on redirects.
133 kRedirectHandled,
134
135 // [servable] A non-redirect successful response is received
136 // (`OnReceiveResponse()` is called with `servable` = true).
137 kResponseReceived,
138
139 // A non-redirect failed response is received (`OnReceiveResponse()` is
140 // called with `servable` = false).
141 kFailedResponseReceived,
142
143 // [Final, servable] Successful completion (`OnComplete(net::OK)` is called
144 // after `kResponseReceived`.
145 kCompleted,
146
147 // [Final] Failed completion (`OnComplete()` is called, either with
148 // non-`net::OK`, or after `kFailedResponseReceived`).
149 kFailed,
150
151 // [Final] Failed redirects.
152 kFailedRedirect
153 };
154
155 LoadState load_state() const { return load_state_; }
156
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36157 base::WeakPtr<PrefetchResponseReader> GetWeakPtr() {
158 return weak_ptr_factory_.GetWeakPtr();
159 }
160
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36161 private:
162 // Identifies a client in `serving_url_loader_clients_`.
163 using ServingUrlLoaderClientId = mojo::RemoteSetElementId;
164
165 friend class base::RefCounted<PrefetchResponseReader>;
kenossccc35892025-06-09 13:09:47166 // This is necessary because `PrefetchContainerObserver` emulates a callback
167 // that we will provide in the future.
168 //
169 // TODO(crbug.com/400761083): Remove it.
170 friend class PrefetchContainerObserver;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36171
172 ~PrefetchResponseReader() override;
173
174 void BindAndStart(
175 mojo::ScopedDataPipeConsumerHandle body,
176 const network::ResourceRequest& resource_request,
177 mojo::PendingReceiver<network::mojom::URLLoader> receiver,
178 mojo::PendingRemote<network::mojom::URLLoaderClient> client);
179
180 // Adds an event to the queue.
181 // The callbacks are called in-order for each of
182 // `serving_url_loader_clients_`, regardless of whether events are added
183 // before or after clients are added.
Hiroshige Hayashizaki33d92a0a12025-03-13 19:07:58184 using EventCallback = base::RepeatingCallback<void(ServingUrlLoaderClientId)>;
185 void AddEventToQueue(EventCallback callback);
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36186 // Sends all stored events in `event_queue_` to the client.
187 // Called when a new client (identified by `client_id_`) is added.
188 void RunEventQueue(ServingUrlLoaderClientId client_id);
189
190 // Helper functions to send the appropriate events to a client.
191 void ForwardCompletionStatus(ServingUrlLoaderClientId client_id);
192 void ForwardEarlyHints(const network::mojom::EarlyHintsPtr& early_hints,
193 ServingUrlLoaderClientId client_id);
194 void ForwardTransferSizeUpdate(int32_t transfer_size_diff,
195 ServingUrlLoaderClientId client_id);
196 void ForwardRedirect(const net::RedirectInfo& redirect_info,
197 const network::mojom::URLResponseHeadPtr&,
198 ServingUrlLoaderClientId client_id);
199 void ForwardResponse(ServingUrlLoaderClientId client_id);
200
201 // network::mojom::URLLoader
202 void FollowRedirect(
203 const std::vector<std::string>& removed_headers,
204 const net::HttpRequestHeaders& modified_headers,
205 const net::HttpRequestHeaders& modified_cors_exempt_headers,
Arthur Sonzognic686e8f2024-01-11 08:36:37206 const std::optional<GURL>& new_url) override;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36207 void SetPriority(net::RequestPriority priority,
208 int32_t intra_priority_value) override;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36209
210 void OnServingURLLoaderMojoDisconnect();
211
212 PrefetchStreamingURLLoaderStatus GetStatusForRecording() const;
213
Jeremy Roman43c200c2024-06-04 15:51:24214 // Stores info from the response head that will be needed later, before it is
215 // stored into `head_` (for non-redirect responses) or `event_queue_` (or
216 // redirect responses).
217 void StoreInfoFromResponseHead(const network::mojom::URLResponseHead& head);
218
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36219 // All URLLoader events are queued up here.
Hiroshige Hayashizaki33d92a0a12025-03-13 19:07:58220 std::vector<EventCallback> event_queue_;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36221
222 // The status of the event queue.
223 enum class EventQueueStatus {
224 kNotRunning,
225 kRunning,
226 };
227 EventQueueStatus event_queue_status_{EventQueueStatus::kNotRunning};
228
Hiroshige Hayashizaki3ba258862025-03-14 16:06:39229 // Always access/update through `load_state()` and
230 // `SetLoadStateAndAddEventToQueue()` below, to avoid unintentional state
231 // changes and missing related callbacks on state changes.
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36232 LoadState load_state_{LoadState::kStarted};
Hiroshige Hayashizaki3ba258862025-03-14 16:06:39233
Hiroshige Hayashizaki3ba258862025-03-14 16:06:39234 void SetLoadStateAndAddEventToQueue(LoadState new_load_state,
235 EventCallback callback);
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36236
Hiroshige Hayashizaki40270b72025-07-23 01:52:01237 // Called when transitioned for the first time to a state other than
238 // `kStarted` nor `kRedirectHandled`.
239 //
240 // This should be always called once for the entire `PrefetchResponseReader`s
241 // for a given `PrefetchContainer`.
242 // TODO(https://crbug.com/400761083): This isn't called for:
243 // - unexpected mojo disconnection cases (See
244 // `PrefetchStreamingURLLoaderTest.UnexpectedUrlLoaderDisconnect`).
245 base::OnceClosure on_determined_head_callback_;
246
247 // Called when transitioned to `kCompleted` or `kFailed`.
248 // This is called after `on_determined_head_callback_` at most once for the
249 // entire `PrefetchResponseReader`s for a given `PrefetchContainer`.
250 // TODO(https://crbug.com/400761083): This isn't called for:
251 // - `kFailedRedirect` (See
252 // `PrefetchStreamingURLLoaderTest.IneligibleRedirect`) or
253 // - unexpected mojo disconnection cases (See
254 // `PrefetchStreamingURLLoaderTest.UnexpectedUrlLoaderDisconnect`).
255 OnPrefetchResponseCompletedCallback on_prefetch_response_completed_callback_;
256
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36257 // Used for UMA recording.
Alison Gale770f3fc2024-04-27 00:39:58258 // TODO(crbug.com/40064891): we might want to adapt these flags and UMA
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36259 // semantics for multiple client settings, but so far we don't have any
260 // specific plans.
Arthur Sonzognic686e8f2024-01-11 08:36:37261 std::optional<PrefetchErrorOnResponseReceived> failure_reason_;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36262 bool served_before_completion_{false};
263 bool served_after_completion_{false};
264 bool should_record_metrics_{true};
265
Jeremy Roman43c200c2024-06-04 15:51:24266 // If present, this includes the sorted and unique names of the cookies which
267 // were specified in the Cookie-Indices header, and a hash of their values as
268 // obtained from `net::HashCookieIndices`. This is not set unless the Vary
269 // header also specified Cookie (or *).
270 //
271 // As one quirk, we presently still don't vary on cookies if Vary is specified
272 // and Cookie-Indices isn't, both because that was the prior behavior and
273 // because doing so requires having the precise string value of the header
274 // (including whitespace).
275 struct CookieIndicesInfo {
276 CookieIndicesInfo();
277 ~CookieIndicesInfo();
278
279 std::vector<std::string> cookie_names;
280 net::CookieIndicesHash expected_hash;
281 };
282 std::optional<CookieIndicesInfo> cookie_indices_;
283
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36284 // The prefetched data and metadata. Not set for a redirect response.
285 network::mojom::URLResponseHeadPtr head_;
Hiroshige Hayashizakic0b6aea2023-10-17 21:56:43286 scoped_refptr<PrefetchDataPipeTee> body_tee_;
Arthur Sonzognic686e8f2024-01-11 08:36:37287 std::optional<network::URLLoaderCompletionStatus> completion_status_;
Kouhei Ueno8c5784d2024-07-03 03:34:17288 // Recorded on `OnComplete` and used to check if the prefetch data is still
289 // fresh for use.
Arthur Sonzognic686e8f2024-01-11 08:36:37290 std::optional<base::TimeTicks> response_complete_time_;
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36291
292 // Only used temporarily to plumb the body `BindAndStart()` to
293 // `ForwardResponse()`.
294 mojo::ScopedDataPipeConsumerHandle forward_body_;
295
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36296 // The URL loader clients that will serve the prefetched data.
297 mojo::ReceiverSet<network::mojom::URLLoader> serving_url_loader_receivers_;
298 mojo::RemoteSet<network::mojom::URLLoaderClient> serving_url_loader_clients_;
299
300 // Set when this manages its own lifetime.
301 scoped_refptr<PrefetchResponseReader> self_pointer_;
302
303 base::WeakPtr<PrefetchStreamingURLLoader> streaming_url_loader_;
304
Hiroshige Hayashizaki0830e182025-03-14 02:03:06305 // TODO(https://crbug.com/40947546): Currently redirects are not supported for
306 // ServiceWorker-controlled prefetches and thus we don't care about alignment
307 // between `PrefetchResponseReader`, `PrefetchStreamingURLLoader` and
308 // `PrefetchContainer` in terms of `ServiceWorkerMainResourceHandle`.
309 std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle_;
310
Hiroshige Hayashizakibf37abc32023-09-13 10:02:36311 base::WeakPtrFactory<PrefetchResponseReader> weak_ptr_factory_{this};
312};
313
314} // namespace content
315
316#endif // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_RESPONSE_READER_H_