1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h"
6
7 #include "base/callback.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/optional.h"
10 #include "mojo/public/cpp/bindings/remote.h"
11 #include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
12 #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
13 #include "third_party/blink/public/platform/platform.h"
14 #include "third_party/blink/public/platform/task_type.h"
15 #include "third_party/blink/public/platform/web_url_loader.h"
16 #include "third_party/blink/public/platform/web_url_loader_client.h"
17 #include "third_party/blink/public/platform/web_url_loader_factory.h"
18 #include "third_party/blink/public/platform/web_url_response.h"
19 #include "third_party/blink/renderer/core/dom/document.h"
20 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
21 #include "third_party/blink/renderer/core/frame/local_frame.h"
22 #include "third_party/blink/renderer/core/frame/navigator.h"
23 #include "third_party/blink/renderer/core/inspector/console_message.h"
24 #include "third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h"
25 #include "third_party/blink/renderer/platform/heap/heap.h"
26 #include "third_party/blink/renderer/platform/loader/link_header.h"
27 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
28 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
29 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
30 #include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
31 #include "third_party/blink/renderer/platform/wtf/functional.h"
32 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
33 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
34 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
35 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
36 #include "third_party/blink/renderer/platform/wtf/vector.h"
37
38 namespace blink {
39
40 class PrefetchedSignedExchangeManager::PrefetchedSignedExchangeLoader
41 : public WebURLLoader {
42 public:
PrefetchedSignedExchangeLoader(const WebURLRequest & request,scoped_refptr<base::SingleThreadTaskRunner> task_runner)43 PrefetchedSignedExchangeLoader(
44 const WebURLRequest& request,
45 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
46 : task_runner_(std::move(task_runner)) {
47 request_.CopyFrom(request);
48 }
49
~PrefetchedSignedExchangeLoader()50 ~PrefetchedSignedExchangeLoader() override {}
51
GetWeakPtr()52 base::WeakPtr<PrefetchedSignedExchangeLoader> GetWeakPtr() {
53 return weak_ptr_factory_.GetWeakPtr();
54 }
55
SetURLLoader(std::unique_ptr<WebURLLoader> url_loader)56 void SetURLLoader(std::unique_ptr<WebURLLoader> url_loader) {
57 DCHECK(!url_loader_);
58 url_loader_ = std::move(url_loader);
59 ExecutePendingMethodCalls();
60 }
61
request() const62 const WebURLRequest& request() const { return request_; }
63
64 // WebURLLoader methods:
LoadSynchronously(std::unique_ptr<network::ResourceRequest> request,scoped_refptr<WebURLRequest::ExtraData> request_extra_data,int requestor_id,bool download_to_network_cache_only,bool pass_response_pipe_to_client,bool no_mime_sniffing,base::TimeDelta timeout_interval,WebURLLoaderClient * client,WebURLResponse & response,base::Optional<WebURLError> & error,WebData & data,int64_t & encoded_data_length,int64_t & encoded_body_length,WebBlobInfo & downloaded_blob)65 void LoadSynchronously(
66 std::unique_ptr<network::ResourceRequest> request,
67 scoped_refptr<WebURLRequest::ExtraData> request_extra_data,
68 int requestor_id,
69 bool download_to_network_cache_only,
70 bool pass_response_pipe_to_client,
71 bool no_mime_sniffing,
72 base::TimeDelta timeout_interval,
73 WebURLLoaderClient* client,
74 WebURLResponse& response,
75 base::Optional<WebURLError>& error,
76 WebData& data,
77 int64_t& encoded_data_length,
78 int64_t& encoded_body_length,
79 WebBlobInfo& downloaded_blob) override {
80 NOTREACHED();
81 }
LoadAsynchronously(std::unique_ptr<network::ResourceRequest> request,scoped_refptr<WebURLRequest::ExtraData> request_extra_data,int requestor_id,bool download_to_network_cache_only,bool no_mime_sniffing,WebURLLoaderClient * client)82 void LoadAsynchronously(
83 std::unique_ptr<network::ResourceRequest> request,
84 scoped_refptr<WebURLRequest::ExtraData> request_extra_data,
85 int requestor_id,
86 bool download_to_network_cache_only,
87 bool no_mime_sniffing,
88 WebURLLoaderClient* client) override {
89 if (url_loader_) {
90 url_loader_->LoadAsynchronously(
91 std::move(request), std::move(request_extra_data), requestor_id,
92 download_to_network_cache_only, no_mime_sniffing, client);
93 return;
94 }
95 // It is safe to use Unretained(client), because |client| is a
96 // ResourceLoader which owns |this|, and we are binding with weak ptr of
97 // |this| here.
98 pending_method_calls_.push(WTF::Bind(
99 &PrefetchedSignedExchangeLoader::LoadAsynchronously, GetWeakPtr(),
100 std::move(request), std::move(request_extra_data), requestor_id,
101 download_to_network_cache_only, no_mime_sniffing,
102 WTF::Unretained(client)));
103 }
SetDefersLoading(bool value)104 void SetDefersLoading(bool value) override {
105 if (url_loader_) {
106 url_loader_->SetDefersLoading(value);
107 return;
108 }
109 pending_method_calls_.push(
110 WTF::Bind(&PrefetchedSignedExchangeLoader::SetDefersLoading,
111 GetWeakPtr(), value));
112 }
DidChangePriority(WebURLRequest::Priority new_priority,int intra_priority_value)113 void DidChangePriority(WebURLRequest::Priority new_priority,
114 int intra_priority_value) override {
115 if (url_loader_) {
116 url_loader_->DidChangePriority(new_priority, intra_priority_value);
117 return;
118 }
119 pending_method_calls_.push(
120 WTF::Bind(&PrefetchedSignedExchangeLoader::DidChangePriority,
121 GetWeakPtr(), new_priority, intra_priority_value));
122 }
GetTaskRunner()123 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override {
124 return task_runner_;
125 }
126
127 private:
ExecutePendingMethodCalls()128 void ExecutePendingMethodCalls() {
129 std::queue<base::OnceClosure> pending_calls =
130 std::move(pending_method_calls_);
131 while (!pending_calls.empty()) {
132 std::move(pending_calls.front()).Run();
133 pending_calls.pop();
134 }
135 }
136
137 WebURLRequest request_;
138 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
139 std::unique_ptr<WebURLLoader> url_loader_;
140 std::queue<base::OnceClosure> pending_method_calls_;
141
142 base::WeakPtrFactory<PrefetchedSignedExchangeLoader> weak_ptr_factory_{this};
143
144 DISALLOW_COPY_AND_ASSIGN(PrefetchedSignedExchangeLoader);
145 };
146
147 // static
MaybeCreate(LocalFrame * frame,const String & outer_link_header,const String & inner_link_header,WebVector<std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>> prefetched_signed_exchanges)148 PrefetchedSignedExchangeManager* PrefetchedSignedExchangeManager::MaybeCreate(
149 LocalFrame* frame,
150 const String& outer_link_header,
151 const String& inner_link_header,
152 WebVector<std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
153 prefetched_signed_exchanges) {
154 if (prefetched_signed_exchanges.empty())
155 return nullptr;
156 std::unique_ptr<AlternateSignedExchangeResourceInfo> alternative_resources =
157 AlternateSignedExchangeResourceInfo::CreateIfValid(outer_link_header,
158 inner_link_header);
159 if (!alternative_resources) {
160 // There is no "allowed-alt-sxg" link header for this resource.
161 return nullptr;
162 }
163
164 HashMap<KURL, std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
165 prefetched_exchanges_map;
166 for (auto& exchange : prefetched_signed_exchanges) {
167 const KURL outer_url = exchange->outer_url;
168 prefetched_exchanges_map.Set(outer_url, std::move(exchange));
169 }
170
171 return MakeGarbageCollected<PrefetchedSignedExchangeManager>(
172 frame, std::move(alternative_resources),
173 std::move(prefetched_exchanges_map));
174 }
175
PrefetchedSignedExchangeManager(LocalFrame * frame,std::unique_ptr<AlternateSignedExchangeResourceInfo> alternative_resources,HashMap<KURL,std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>> prefetched_exchanges_map)176 PrefetchedSignedExchangeManager::PrefetchedSignedExchangeManager(
177 LocalFrame* frame,
178 std::unique_ptr<AlternateSignedExchangeResourceInfo> alternative_resources,
179 HashMap<KURL,
180 std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
181 prefetched_exchanges_map)
182 : frame_(frame),
183 alternative_resources_(std::move(alternative_resources)),
184 prefetched_exchanges_map_(std::move(prefetched_exchanges_map)) {
185 }
186
~PrefetchedSignedExchangeManager()187 PrefetchedSignedExchangeManager::~PrefetchedSignedExchangeManager() {}
188
Trace(Visitor * visitor)189 void PrefetchedSignedExchangeManager::Trace(Visitor* visitor) {
190 visitor->Trace(frame_);
191 }
192
StartPrefetchedLinkHeaderPreloads()193 void PrefetchedSignedExchangeManager::StartPrefetchedLinkHeaderPreloads() {
194 DCHECK(!started_);
195 started_ = true;
196 TriggerLoad();
197 // Clears |prefetched_exchanges_map_| to release URLLoaderFactory in the
198 // browser process.
199 prefetched_exchanges_map_.clear();
200 // Clears |alternative_resources_| which will not be used forever.
201 alternative_resources_.reset();
202 }
203
204 std::unique_ptr<WebURLLoader>
MaybeCreateURLLoader(const WebURLRequest & request)205 PrefetchedSignedExchangeManager::MaybeCreateURLLoader(
206 const WebURLRequest& request) {
207 if (started_)
208 return nullptr;
209 const auto* matching_resource = alternative_resources_->FindMatchingEntry(
210 request.Url(), request.GetRequestContext(),
211 frame_->DomWindow()->navigator()->languages());
212 if (!matching_resource)
213 return nullptr;
214
215 std::unique_ptr<PrefetchedSignedExchangeLoader> loader =
216 std::make_unique<PrefetchedSignedExchangeLoader>(
217 request, frame_->GetFrameScheduler()->GetTaskRunner(
218 TaskType::kInternalLoading));
219 loaders_.emplace_back(loader->GetWeakPtr());
220 return loader;
221 }
222
223 std::unique_ptr<WebURLLoader>
CreateDefaultURLLoader(const WebURLRequest & request)224 PrefetchedSignedExchangeManager::CreateDefaultURLLoader(
225 const WebURLRequest& request) {
226 return frame_->GetURLLoaderFactory()->CreateURLLoader(
227 request,
228 frame_->GetFrameScheduler()->CreateResourceLoadingTaskRunnerHandle());
229 }
230
231 std::unique_ptr<WebURLLoader>
CreatePrefetchedSignedExchangeURLLoader(const WebURLRequest & request,mojo::PendingRemote<network::mojom::blink::URLLoaderFactory> loader_factory)232 PrefetchedSignedExchangeManager::CreatePrefetchedSignedExchangeURLLoader(
233 const WebURLRequest& request,
234 mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
235 loader_factory) {
236 return Platform::Current()
237 ->WrapURLLoaderFactory(loader_factory.PassPipe())
238 ->CreateURLLoader(
239 request,
240 frame_->GetFrameScheduler()->CreateResourceLoadingTaskRunnerHandle());
241 }
242
TriggerLoad()243 void PrefetchedSignedExchangeManager::TriggerLoad() {
244 Vector<WebNavigationParams::PrefetchedSignedExchange*>
245 maching_prefetched_exchanges;
246 for (auto loader : loaders_) {
247 if (!loader) {
248 // The loader has been canceled.
249 maching_prefetched_exchanges.emplace_back(nullptr);
250 // We can continue the matching, because the distributor can't send
251 // arbitrary information to the publisher using this resource.
252 continue;
253 }
254 const auto* matching_resource = alternative_resources_->FindMatchingEntry(
255 loader->request().Url(), loader->request().GetRequestContext(),
256 frame_->DomWindow()->navigator()->languages());
257 const auto alternative_url = matching_resource->alternative_url();
258 if (!alternative_url.IsValid()) {
259 // There is no matching "alternate" link header in outer response header.
260 break;
261 }
262 const auto exchange_it = prefetched_exchanges_map_.find(alternative_url);
263 if (exchange_it == prefetched_exchanges_map_.end()) {
264 // There is no matching prefetched exchange.
265 break;
266 }
267 if (String(exchange_it->value->header_integrity) !=
268 matching_resource->header_integrity()) {
269 // The header integrity doesn't match.
270 break;
271 }
272 if (KURL(exchange_it->value->inner_url) !=
273 matching_resource->anchor_url()) {
274 // The inner URL doesn't match.
275 break;
276 }
277 maching_prefetched_exchanges.emplace_back(exchange_it->value.get());
278 }
279 if (loaders_.size() != maching_prefetched_exchanges.size()) {
280 // Need to load the all original resources in this case to prevent the
281 // distributor from sending arbitrary information to the publisher.
282 String message =
283 "Failed to match prefetched alternative signed exchange subresources. "
284 "Requesting the all original resources ignoreing all alternative signed"
285 " exchange responses.";
286 frame_->GetDocument()->AddConsoleMessage(
287 MakeGarbageCollected<ConsoleMessage>(
288 mojom::ConsoleMessageSource::kNetwork,
289 mojom::ConsoleMessageLevel::kError, message));
290 for (auto loader : loaders_) {
291 if (!loader)
292 continue;
293 loader->SetURLLoader(CreateDefaultURLLoader(loader->request()));
294 }
295 return;
296 }
297 for (wtf_size_t i = 0; i < loaders_.size(); ++i) {
298 auto loader = loaders_.at(i);
299 if (!loader)
300 continue;
301 auto* prefetched_exchange = maching_prefetched_exchanges.at(i);
302 mojo::Remote<network::mojom::blink::URLLoaderFactory> loader_factory(
303 mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>(
304 std::move(prefetched_exchange->loader_factory_handle),
305 network::mojom::URLLoaderFactory::Version_));
306 mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
307 loader_factory_clone;
308 loader_factory->Clone(
309 loader_factory_clone.InitWithNewPipeAndPassReceiver());
310 // Reset loader_factory_handle to support loading the same resource again.
311 prefetched_exchange->loader_factory_handle =
312 loader_factory_clone.PassPipe();
313 loader->SetURLLoader(CreatePrefetchedSignedExchangeURLLoader(
314 loader->request(), loader_factory.Unbind()));
315 }
316 }
317
318 } // namespace blink
319