1 // Copyright 2017 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 "content/renderer/loader/sync_load_context.h"
6 
7 #include <string>
8 
9 #include "base/bind.h"
10 #include "base/check_op.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/optional.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/time/time.h"
15 #include "mojo/public/cpp/bindings/associated_remote.h"
16 #include "net/url_request/redirect_info.h"
17 #include "services/network/public/cpp/resource_request.h"
18 #include "services/network/public/mojom/url_response_head.mojom.h"
19 #include "third_party/blink/public/common/client_hints/client_hints.h"
20 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
21 #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
22 #include "third_party/blink/public/platform/sync_load_response.h"
23 
24 namespace content {
25 
26 // An inner helper class to manage the SyncLoadContext's events and timeouts,
27 // so that we can stop or resumse all of them at once.
28 class SyncLoadContext::SignalHelper final {
29  public:
SignalHelper(SyncLoadContext * context,base::WaitableEvent * redirect_or_response_event,base::WaitableEvent * abort_event,base::TimeDelta timeout)30   SignalHelper(SyncLoadContext* context,
31                base::WaitableEvent* redirect_or_response_event,
32                base::WaitableEvent* abort_event,
33                base::TimeDelta timeout)
34       : context_(context),
35         redirect_or_response_event_(redirect_or_response_event),
36         abort_event_(abort_event) {
37     // base::TimeDelta::Max() means no timeout.
38     if (timeout != base::TimeDelta::Max()) {
39       // Instantiate a base::OneShotTimer instance.
40       timeout_timer_.emplace();
41     }
42     Start(timeout);
43   }
44 
SignalRedirectOrResponseComplete()45   void SignalRedirectOrResponseComplete() {
46     abort_watcher_.StopWatching();
47     if (timeout_timer_)
48       timeout_timer_->AbandonAndStop();
49     redirect_or_response_event_->Signal();
50   }
51 
RestartAfterRedirect()52   bool RestartAfterRedirect() {
53     if (abort_event_ && abort_event_->IsSignaled())
54       return false;
55 
56     base::TimeDelta timeout_remainder = base::TimeDelta::Max();
57     if (timeout_timer_) {
58       timeout_remainder =
59           timeout_timer_->desired_run_time() - base::TimeTicks::Now();
60       if (timeout_remainder <= base::TimeDelta())
61         return false;
62     }
63     Start(timeout_remainder);
64     return true;
65   }
66 
67  private:
Start(base::TimeDelta timeout)68   void Start(base::TimeDelta timeout) {
69     DCHECK(!redirect_or_response_event_->IsSignaled());
70     if (abort_event_) {
71       abort_watcher_.StartWatching(
72           abort_event_,
73           base::BindOnce(&SyncLoadContext::OnAbort, base::Unretained(context_)),
74           context_->task_runner_);
75     }
76     if (timeout_timer_) {
77       DCHECK_NE(base::TimeDelta::Max(), timeout);
78       timeout_timer_->Start(FROM_HERE, timeout, context_,
79                             &SyncLoadContext::OnTimeout);
80     }
81   }
82 
83   SyncLoadContext* context_;
84   base::WaitableEvent* redirect_or_response_event_;
85   base::WaitableEvent* abort_event_;
86   base::WaitableEventWatcher abort_watcher_;
87   base::Optional<base::OneShotTimer> timeout_timer_;
88 };
89 
90 // static
StartAsyncWithWaitableEvent(std::unique_ptr<network::ResourceRequest> request,int routing_id,scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner,const net::NetworkTrafficAnnotationTag & traffic_annotation,uint32_t loader_options,std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_url_loader_factory,std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles,blink::SyncLoadResponse * response,SyncLoadContext ** context_for_redirect,base::WaitableEvent * redirect_or_response_event,base::WaitableEvent * abort_event,base::TimeDelta timeout,mojo::PendingRemote<blink::mojom::BlobRegistry> download_to_blob_registry,const std::vector<std::string> & cors_exempt_header_list,std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper> resource_load_info_notifier_wrapper)91 void SyncLoadContext::StartAsyncWithWaitableEvent(
92     std::unique_ptr<network::ResourceRequest> request,
93     int routing_id,
94     scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner,
95     const net::NetworkTrafficAnnotationTag& traffic_annotation,
96     uint32_t loader_options,
97     std::unique_ptr<network::PendingSharedURLLoaderFactory>
98         pending_url_loader_factory,
99     std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles,
100     blink::SyncLoadResponse* response,
101     SyncLoadContext** context_for_redirect,
102     base::WaitableEvent* redirect_or_response_event,
103     base::WaitableEvent* abort_event,
104     base::TimeDelta timeout,
105     mojo::PendingRemote<blink::mojom::BlobRegistry> download_to_blob_registry,
106     const std::vector<std::string>& cors_exempt_header_list,
107     std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
108         resource_load_info_notifier_wrapper) {
109   auto* context = new SyncLoadContext(
110       request.get(), std::move(pending_url_loader_factory), response,
111       context_for_redirect, redirect_or_response_event, abort_event, timeout,
112       std::move(download_to_blob_registry), loading_task_runner,
113       cors_exempt_header_list);
114   context->request_id_ = context->resource_dispatcher_->StartAsync(
115       std::move(request), routing_id, std::move(loading_task_runner),
116       traffic_annotation, loader_options, base::WrapUnique(context),
117       context->url_loader_factory_, std::move(throttles),
118       std::move(resource_load_info_notifier_wrapper));
119 }
120 
SyncLoadContext(network::ResourceRequest * request,std::unique_ptr<network::PendingSharedURLLoaderFactory> url_loader_factory,blink::SyncLoadResponse * response,SyncLoadContext ** context_for_redirect,base::WaitableEvent * redirect_or_response_event,base::WaitableEvent * abort_event,base::TimeDelta timeout,mojo::PendingRemote<blink::mojom::BlobRegistry> download_to_blob_registry,scoped_refptr<base::SingleThreadTaskRunner> task_runner,const std::vector<std::string> & cors_exempt_header_list)121 SyncLoadContext::SyncLoadContext(
122     network::ResourceRequest* request,
123     std::unique_ptr<network::PendingSharedURLLoaderFactory> url_loader_factory,
124     blink::SyncLoadResponse* response,
125     SyncLoadContext** context_for_redirect,
126     base::WaitableEvent* redirect_or_response_event,
127     base::WaitableEvent* abort_event,
128     base::TimeDelta timeout,
129     mojo::PendingRemote<blink::mojom::BlobRegistry> download_to_blob_registry,
130     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
131     const std::vector<std::string>& cors_exempt_header_list)
132     : response_(response),
133       context_for_redirect_(context_for_redirect),
134       body_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
135       download_to_blob_registry_(std::move(download_to_blob_registry)),
136       task_runner_(std::move(task_runner)),
137       signals_(std::make_unique<SignalHelper>(this,
138                                               redirect_or_response_event,
139                                               abort_event,
140                                               timeout)) {
141   if (download_to_blob_registry_)
142     mode_ = Mode::kBlob;
143 
144   url_loader_factory_ =
145       network::SharedURLLoaderFactory::Create(std::move(url_loader_factory));
146 
147   // Constructs a new ResourceDispatcher specifically for this request.
148   resource_dispatcher_ = std::make_unique<ResourceDispatcher>();
149   resource_dispatcher_->SetCorsExemptHeaderList(cors_exempt_header_list);
150 
151   // Initialize the final URL with the original request URL. It will be
152   // overwritten on redirects.
153   response_->url = request->url;
154 }
155 
~SyncLoadContext()156 SyncLoadContext::~SyncLoadContext() {}
157 
OnUploadProgress(uint64_t position,uint64_t size)158 void SyncLoadContext::OnUploadProgress(uint64_t position, uint64_t size) {}
159 
OnReceivedRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr head,std::vector<std::string> * removed_headers)160 bool SyncLoadContext::OnReceivedRedirect(
161     const net::RedirectInfo& redirect_info,
162     network::mojom::URLResponseHeadPtr head,
163     std::vector<std::string>* removed_headers) {
164   DCHECK(!Completed());
165   if (removed_headers) {
166     // TODO(yoav): Get the actual FeaturePolicy here to support selective
167     // removal for sync XHR.
168     blink::FindClientHintsToRemove(nullptr /* feature_policy */,
169                                    redirect_info.new_url, removed_headers);
170   }
171 
172   response_->url = redirect_info.new_url;
173   response_->head = std::move(head);
174   response_->redirect_info = redirect_info;
175   *context_for_redirect_ = this;
176   resource_dispatcher_->SetDefersLoading(
177       request_id_, blink::WebURLLoader::DeferType::kDeferred);
178   signals_->SignalRedirectOrResponseComplete();
179   return true;
180 }
181 
EvictFromBackForwardCache(blink::mojom::RendererEvictionReason reason)182 void SyncLoadContext::EvictFromBackForwardCache(
183     blink::mojom::RendererEvictionReason reason) {
184   return;
185 }
186 
FollowRedirect()187 void SyncLoadContext::FollowRedirect() {
188   if (!signals_->RestartAfterRedirect()) {
189     CancelRedirect();
190     return;
191   }
192 
193   response_->redirect_info = net::RedirectInfo();
194   *context_for_redirect_ = nullptr;
195 
196   resource_dispatcher_->SetDefersLoading(
197       request_id_, blink::WebURLLoader::DeferType::kNotDeferred);
198 }
199 
CancelRedirect()200 void SyncLoadContext::CancelRedirect() {
201   response_->redirect_info = net::RedirectInfo();
202   *context_for_redirect_ = nullptr;
203 
204   response_->error_code = net::ERR_ABORTED;
205   CompleteRequest();
206 }
207 
OnReceivedResponse(network::mojom::URLResponseHeadPtr head)208 void SyncLoadContext::OnReceivedResponse(
209     network::mojom::URLResponseHeadPtr head) {
210   DCHECK(!Completed());
211   response_->head = std::move(head);
212 }
213 
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body)214 void SyncLoadContext::OnStartLoadingResponseBody(
215     mojo::ScopedDataPipeConsumerHandle body) {
216   if (mode_ == Mode::kBlob) {
217     DCHECK(download_to_blob_registry_);
218     DCHECK(!blob_response_started_);
219 
220     blob_response_started_ = true;
221 
222     download_to_blob_registry_->RegisterFromStream(
223         response_->head->mime_type, "",
224         std::max<int64_t>(0, response_->head->content_length), std::move(body),
225         mojo::NullAssociatedRemote(),
226         base::BindOnce(&SyncLoadContext::OnFinishCreatingBlob,
227                        base::Unretained(this)));
228     return;
229   }
230   DCHECK_EQ(Mode::kInitial, mode_);
231   mode_ = Mode::kDataPipe;
232   // setup datapipe to read.
233   body_handle_ = std::move(body);
234   body_watcher_.Watch(
235       body_handle_.get(),
236       MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
237       MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
238       base::BindRepeating(&SyncLoadContext::OnBodyReadable,
239                           base::Unretained(this)));
240   body_watcher_.ArmOrNotify();
241 }
242 
OnTransferSizeUpdated(int transfer_size_diff)243 void SyncLoadContext::OnTransferSizeUpdated(int transfer_size_diff) {}
244 
OnCompletedRequest(const network::URLLoaderCompletionStatus & status)245 void SyncLoadContext::OnCompletedRequest(
246     const network::URLLoaderCompletionStatus& status) {
247   if (Completed()) {
248     // It means the response has been aborted due to an error before finishing
249     // the response.
250     return;
251   }
252   request_completed_ = true;
253   response_->error_code = status.error_code;
254   response_->extended_error_code = status.extended_error_code;
255   response_->resolve_error_info = status.resolve_error_info;
256   response_->cors_error = status.cors_error_status;
257   response_->head->encoded_data_length = status.encoded_data_length;
258   response_->head->encoded_body_length = status.encoded_body_length;
259   if ((blob_response_started_ && !blob_finished_) || body_handle_.is_valid()) {
260     // The body is still begin downloaded as a Blob, or being read through the
261     // handle. Wait until it's completed.
262     return;
263   }
264   CompleteRequest();
265 }
266 
OnFinishCreatingBlob(blink::mojom::SerializedBlobPtr blob)267 void SyncLoadContext::OnFinishCreatingBlob(
268     blink::mojom::SerializedBlobPtr blob) {
269   DCHECK(!Completed());
270   blob_finished_ = true;
271   response_->downloaded_blob = std::move(blob);
272   if (request_completed_)
273     CompleteRequest();
274 }
275 
OnBodyReadable(MojoResult,const mojo::HandleSignalsState &)276 void SyncLoadContext::OnBodyReadable(MojoResult,
277                                      const mojo::HandleSignalsState&) {
278   DCHECK_EQ(Mode::kDataPipe, mode_);
279   DCHECK(body_handle_.is_valid());
280   const void* buffer = nullptr;
281   uint32_t read_bytes = 0;
282   MojoResult result = body_handle_->BeginReadData(&buffer, &read_bytes,
283                                                   MOJO_READ_DATA_FLAG_NONE);
284   if (result == MOJO_RESULT_SHOULD_WAIT) {
285     body_watcher_.ArmOrNotify();
286     return;
287   }
288   if (result == MOJO_RESULT_FAILED_PRECONDITION) {
289     // Whole body has been read.
290     body_handle_.reset();
291     body_watcher_.Cancel();
292     if (request_completed_)
293       CompleteRequest();
294     return;
295   }
296   if (result != MOJO_RESULT_OK) {
297     // Something went wrong.
298     body_handle_.reset();
299     body_watcher_.Cancel();
300     response_->error_code = net::ERR_FAILED;
301     CompleteRequest();
302     return;
303   }
304 
305   response_->data.Append(static_cast<const char*>(buffer), read_bytes);
306   body_handle_->EndReadData(read_bytes);
307   body_watcher_.ArmOrNotify();
308 }
309 
OnAbort(base::WaitableEvent * event)310 void SyncLoadContext::OnAbort(base::WaitableEvent* event) {
311   DCHECK(!Completed());
312   response_->error_code = net::ERR_ABORTED;
313   CompleteRequest();
314 }
315 
OnTimeout()316 void SyncLoadContext::OnTimeout() {
317   // OnTimeout() must not be called after CompleteRequest() was called, because
318   // the OneShotTimer must have been stopped.
319   DCHECK(!Completed());
320   response_->error_code = net::ERR_TIMED_OUT;
321   CompleteRequest();
322 }
323 
CompleteRequest()324 void SyncLoadContext::CompleteRequest() {
325   DCHECK(blob_finished_ || (mode_ != Mode::kBlob));
326   DCHECK(!body_handle_.is_valid());
327   body_watcher_.Cancel();
328   signals_->SignalRedirectOrResponseComplete();
329   signals_ = nullptr;
330   response_ = nullptr;
331 
332   // This will indirectly cause this object to be deleted.
333   resource_dispatcher_->RemovePendingRequest(request_id_, task_runner_);
334 }
335 
Completed() const336 bool SyncLoadContext::Completed() const {
337   DCHECK_EQ(!signals_, !response_);
338   return !response_;
339 }
340 
341 }  // namespace content
342