1 // Copyright 2018 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/browser/devtools/devtools_url_loader_interceptor.h"
6 #include "base/barrier_closure.h"
7 #include "base/base64.h"
8 #include "base/bind.h"
9 #include "base/no_destructor.h"
10 #include "base/strings/pattern.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/unguessable_token.h"
15 #include "content/browser/devtools/protocol/network.h"
16 #include "content/browser/devtools/protocol/network_handler.h"
17 #include "content/browser/loader/download_utils_impl.h"
18 #include "content/browser/renderer_host/frame_tree_node.h"
19 #include "content/public/browser/browser_task_traits.h"
20 #include "content/public/browser/storage_partition.h"
21 #include "content/public/common/content_client.h"
22 #include "mojo/public/cpp/bindings/receiver.h"
23 #include "mojo/public/cpp/bindings/receiver_set.h"
24 #include "mojo/public/cpp/system/data_pipe_drainer.h"
25 #include "net/base/load_flags.h"
26 #include "net/base/mime_sniffer.h"
27 #include "net/cookies/cookie_access_result.h"
28 #include "net/cookies/cookie_util.h"
29 #include "net/http/http_util.h"
30 #include "net/url_request/redirect_info.h"
31 #include "net/url_request/redirect_util.h"
32 #include "net/url_request/referrer_policy.h"
33 #include "services/network/public/cpp/cors/cors.h"
34 #include "services/network/public/cpp/resource_request_body.h"
35 #include "services/network/public/mojom/network_context.mojom.h"
36 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
37
38 namespace content {
39
40 InterceptedRequestInfo::InterceptedRequestInfo() = default;
41
42 InterceptedRequestInfo::~InterceptedRequestInfo() = default;
43
AuthChallengeResponse(ResponseType response_type)44 DevToolsURLLoaderInterceptor::AuthChallengeResponse::AuthChallengeResponse(
45 ResponseType response_type)
46 : response_type(response_type) {
47 DCHECK_NE(kProvideCredentials, response_type);
48 }
49
AuthChallengeResponse(const base::string16 & username,const base::string16 & password)50 DevToolsURLLoaderInterceptor::AuthChallengeResponse::AuthChallengeResponse(
51 const base::string16& username,
52 const base::string16& password)
53 : response_type(kProvideCredentials), credentials(username, password) {}
54
FilterEntry(const base::UnguessableToken & target_id,std::vector<Pattern> patterns,RequestInterceptedCallback callback)55 DevToolsURLLoaderInterceptor::FilterEntry::FilterEntry(
56 const base::UnguessableToken& target_id,
57 std::vector<Pattern> patterns,
58 RequestInterceptedCallback callback)
59 : target_id(target_id),
60 patterns(std::move(patterns)),
61 callback(std::move(callback)) {}
62
63 DevToolsURLLoaderInterceptor::FilterEntry::FilterEntry(FilterEntry&&) = default;
64 DevToolsURLLoaderInterceptor::FilterEntry::~FilterEntry() = default;
65
66 DevToolsURLLoaderInterceptor::Modifications::Modifications() = default;
67
Modifications(net::Error error_reason)68 DevToolsURLLoaderInterceptor::Modifications::Modifications(
69 net::Error error_reason)
70 : error_reason(error_reason) {}
71
Modifications(scoped_refptr<net::HttpResponseHeaders> response_headers,scoped_refptr<base::RefCountedMemory> response_body)72 DevToolsURLLoaderInterceptor::Modifications::Modifications(
73 scoped_refptr<net::HttpResponseHeaders> response_headers,
74 scoped_refptr<base::RefCountedMemory> response_body)
75 : response_headers(std::move(response_headers)),
76 response_body(std::move(response_body)) {}
77
Modifications(std::unique_ptr<AuthChallengeResponse> auth_challenge_response)78 DevToolsURLLoaderInterceptor::Modifications::Modifications(
79 std::unique_ptr<AuthChallengeResponse> auth_challenge_response)
80 : auth_challenge_response(std::move(auth_challenge_response)) {}
81
Modifications(protocol::Maybe<std::string> modified_url,protocol::Maybe<std::string> modified_method,protocol::Maybe<protocol::Binary> modified_post_data,std::unique_ptr<HeadersVector> modified_headers)82 DevToolsURLLoaderInterceptor::Modifications::Modifications(
83 protocol::Maybe<std::string> modified_url,
84 protocol::Maybe<std::string> modified_method,
85 protocol::Maybe<protocol::Binary> modified_post_data,
86 std::unique_ptr<HeadersVector> modified_headers)
87 : modified_url(std::move(modified_url)),
88 modified_method(std::move(modified_method)),
89 modified_post_data(std::move(modified_post_data)),
90 modified_headers(std::move(modified_headers)) {}
91
Modifications(base::Optional<net::Error> error_reason,scoped_refptr<net::HttpResponseHeaders> response_headers,scoped_refptr<base::RefCountedMemory> response_body,size_t body_offset,protocol::Maybe<std::string> modified_url,protocol::Maybe<std::string> modified_method,protocol::Maybe<protocol::Binary> modified_post_data,std::unique_ptr<HeadersVector> modified_headers,std::unique_ptr<AuthChallengeResponse> auth_challenge_response)92 DevToolsURLLoaderInterceptor::Modifications::Modifications(
93 base::Optional<net::Error> error_reason,
94 scoped_refptr<net::HttpResponseHeaders> response_headers,
95 scoped_refptr<base::RefCountedMemory> response_body,
96 size_t body_offset,
97 protocol::Maybe<std::string> modified_url,
98 protocol::Maybe<std::string> modified_method,
99 protocol::Maybe<protocol::Binary> modified_post_data,
100 std::unique_ptr<HeadersVector> modified_headers,
101 std::unique_ptr<AuthChallengeResponse> auth_challenge_response)
102 : error_reason(std::move(error_reason)),
103 response_headers(std::move(response_headers)),
104 response_body(std::move(response_body)),
105 body_offset(body_offset),
106 modified_url(std::move(modified_url)),
107 modified_method(std::move(modified_method)),
108 modified_post_data(std::move(modified_post_data)),
109 modified_headers(std::move(modified_headers)),
110 auth_challenge_response(std::move(auth_challenge_response)) {}
111
112 DevToolsURLLoaderInterceptor::Modifications::~Modifications() = default;
113
114 DevToolsURLLoaderInterceptor::Pattern::~Pattern() = default;
115
116 DevToolsURLLoaderInterceptor::Pattern::Pattern(const Pattern& other) = default;
117
Pattern(const std::string & url_pattern,base::flat_set<blink::mojom::ResourceType> resource_types,InterceptionStage interception_stage)118 DevToolsURLLoaderInterceptor::Pattern::Pattern(
119 const std::string& url_pattern,
120 base::flat_set<blink::mojom::ResourceType> resource_types,
121 InterceptionStage interception_stage)
122 : url_pattern(url_pattern),
123 resource_types(std::move(resource_types)),
124 interception_stage(interception_stage) {}
125
Matches(const std::string & url,blink::mojom::ResourceType resource_type) const126 bool DevToolsURLLoaderInterceptor::Pattern::Matches(
127 const std::string& url,
128 blink::mojom::ResourceType resource_type) const {
129 if (!resource_types.empty() &&
130 resource_types.find(resource_type) == resource_types.end()) {
131 return false;
132 }
133 return base::MatchPattern(url, url_pattern);
134 }
135
136 struct CreateLoaderParameters {
CreateLoaderParameterscontent::CreateLoaderParameters137 CreateLoaderParameters(
138 int32_t routing_id,
139 int32_t request_id,
140 uint32_t options,
141 network::ResourceRequest request,
142 net::MutableNetworkTrafficAnnotationTag traffic_annotation)
143 : routing_id(routing_id),
144 request_id(request_id),
145 options(options),
146 request(request),
147 traffic_annotation(traffic_annotation) {}
148
149 const int32_t routing_id;
150 const int32_t request_id;
151 const uint32_t options;
152 network::ResourceRequest request;
153 const net::MutableNetworkTrafficAnnotationTag traffic_annotation;
154 };
155
156 namespace {
157
158 using RequestInterceptedCallback =
159 DevToolsURLLoaderInterceptor::RequestInterceptedCallback;
160 using ContinueInterceptedRequestCallback =
161 DevToolsURLLoaderInterceptor::ContinueInterceptedRequestCallback;
162 using GetResponseBodyCallback =
163 DevToolsURLLoaderInterceptor::GetResponseBodyForInterceptionCallback;
164 using TakeResponseBodyPipeCallback =
165 DevToolsURLLoaderInterceptor::TakeResponseBodyPipeCallback;
166 using Modifications = DevToolsURLLoaderInterceptor::Modifications;
167 using InterceptionStage = DevToolsURLLoaderInterceptor::InterceptionStage;
168 using protocol::Response;
169 using GlobalRequestId = std::tuple<int32_t, int32_t, int32_t>;
170 using network::mojom::CredentialsMode;
171 using network::mojom::FetchResponseType;
172
173 class BodyReader : public mojo::DataPipeDrainer::Client {
174 public:
BodyReader(base::OnceClosure download_complete_callback)175 explicit BodyReader(base::OnceClosure download_complete_callback)
176 : download_complete_callback_(std::move(download_complete_callback)),
177 body_(base::MakeRefCounted<base::RefCountedString>()) {}
178
179 void StartReading(mojo::ScopedDataPipeConsumerHandle body);
180
AddCallback(std::unique_ptr<GetResponseBodyCallback> callback)181 void AddCallback(std::unique_ptr<GetResponseBodyCallback> callback) {
182 if (data_complete_) {
183 DCHECK(callbacks_.empty());
184 callback->sendSuccess(encoded_body_, true);
185 return;
186 }
187 callbacks_.push_back(std::move(callback));
188 }
189
data_complete() const190 bool data_complete() const { return data_complete_; }
191
body() const192 scoped_refptr<base::RefCountedMemory> body() const {
193 DCHECK(data_complete_);
194 return body_;
195 }
196
CancelWithError(std::string error)197 void CancelWithError(std::string error) {
198 for (auto& cb : callbacks_)
199 cb->sendFailure(Response::ServerError(error));
200 callbacks_.clear();
201 }
202
203 private:
OnDataAvailable(const void * data,size_t num_bytes)204 void OnDataAvailable(const void* data, size_t num_bytes) override {
205 DCHECK(!data_complete_);
206 body_->data().append(
207 std::string(static_cast<const char*>(data), num_bytes));
208 }
209
210 void OnDataComplete() override;
211
212 std::unique_ptr<mojo::DataPipeDrainer> body_pipe_drainer_;
213 std::vector<std::unique_ptr<GetResponseBodyCallback>> callbacks_;
214 base::OnceClosure download_complete_callback_;
215 scoped_refptr<base::RefCountedString> body_;
216 std::string encoded_body_;
217 bool data_complete_ = false;
218 };
219
StartReading(mojo::ScopedDataPipeConsumerHandle body)220 void BodyReader::StartReading(mojo::ScopedDataPipeConsumerHandle body) {
221 DCHECK(!callbacks_.empty());
222 DCHECK(!body_pipe_drainer_);
223 DCHECK(!data_complete_);
224
225 body_pipe_drainer_.reset(new mojo::DataPipeDrainer(this, std::move(body)));
226 }
227
OnDataComplete()228 void BodyReader::OnDataComplete() {
229 DCHECK(!data_complete_);
230 data_complete_ = true;
231 body_pipe_drainer_.reset();
232 // TODO(caseq): only encode if necessary.
233 base::Base64Encode(body_->data(), &encoded_body_);
234 for (auto& cb : callbacks_)
235 cb->sendSuccess(encoded_body_, true);
236 callbacks_.clear();
237 std::move(download_complete_callback_).Run();
238 }
239
240 struct ResponseMetadata {
241 ResponseMetadata() = default;
ResponseMetadatacontent::__anon044a4f3b0111::ResponseMetadata242 explicit ResponseMetadata(network::mojom::URLResponseHeadPtr head)
243 : head(std::move(head)) {}
244
245 network::mojom::URLResponseHeadPtr head =
246 network::mojom::URLResponseHead::New();
247 std::unique_ptr<net::RedirectInfo> redirect_info;
248 mojo_base::BigBuffer cached_metadata;
249 size_t encoded_length = 0;
250 size_t transfer_size = 0;
251 network::URLLoaderCompletionStatus status;
252 };
253
254 } // namespace
255
256 class InterceptionJob : public network::mojom::URLLoaderClient,
257 public network::mojom::URLLoader {
258 public:
FindByRequestId(const GlobalRequestId & global_req_id)259 static InterceptionJob* FindByRequestId(
260 const GlobalRequestId& global_req_id) {
261 const auto& map = GetInterceptionJobMap();
262 auto it = map.find(global_req_id);
263 return it == map.end() ? nullptr : it->second;
264 }
265
266 InterceptionJob(
267 DevToolsURLLoaderInterceptor* interceptor,
268 const std::string& id,
269 const base::UnguessableToken& frame_token,
270 int32_t process_id,
271 const base::Optional<std::string>& renderer_request_id,
272 std::unique_ptr<CreateLoaderParameters> create_loader_params,
273 bool is_download,
274 mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
275 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
276 mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
277 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager);
278
279 void GetResponseBody(std::unique_ptr<GetResponseBodyCallback> callback);
280 void TakeResponseBodyPipe(TakeResponseBodyPipeCallback callback);
281 void ContinueInterceptedRequest(
282 std::unique_ptr<Modifications> modifications,
283 std::unique_ptr<ContinueInterceptedRequestCallback> callback);
284 void Detach();
285
286 void OnAuthRequest(
287 const net::AuthChallengeInfo& auth_info,
288 DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback);
289
290 private:
GetInterceptionJobMap()291 static std::map<GlobalRequestId, InterceptionJob*>& GetInterceptionJobMap() {
292 static base::NoDestructor<std::map<GlobalRequestId, InterceptionJob*>> inst;
293 return *inst;
294 }
295
~InterceptionJob()296 ~InterceptionJob() override {
297 if (registered_in_global_request_map_) {
298 size_t erased = GetInterceptionJobMap().erase(global_req_id_);
299 DCHECK_EQ(1lu, erased);
300 }
301 }
302
303 Response InnerContinueRequest(std::unique_ptr<Modifications> modifications);
304 void ProcessAuthResponse(
305 const DevToolsURLLoaderInterceptor::AuthChallengeResponse&
306 auth_challenge_response);
307 Response ProcessResponseOverride(
308 scoped_refptr<net::HttpResponseHeaders> headers,
309 scoped_refptr<base::RefCountedMemory> body,
310 size_t response_body_offset);
311 void ProcessRedirectByClient(const GURL& redirect_url);
312 void ProcessSetCookies(const net::HttpResponseHeaders& response_headers,
313 base::OnceClosure callback);
314 void SendResponse(scoped_refptr<base::RefCountedMemory> body, size_t offset);
315 void ApplyModificationsToRequest(
316 std::unique_ptr<Modifications> modifications);
317
318 void StartRequest();
319 void CancelRequest();
320 void CompleteRequest(const network::URLLoaderCompletionStatus& status);
321 void Shutdown();
322
323 std::unique_ptr<InterceptedRequestInfo> BuildRequestInfo(
324 const network::mojom::URLResponseHeadPtr& head);
325 void NotifyClient(std::unique_ptr<InterceptedRequestInfo> request_info);
326 void FetchCookies(
327 network::mojom::CookieManager::GetCookieListCallback callback);
328 void NotifyClientWithCookies(
329 std::unique_ptr<InterceptedRequestInfo> request_info,
330 const net::CookieAccessResultList& cookies_with_access_result,
331 const net::CookieAccessResultList& excluded_cookies);
332
333 void ResponseBodyComplete();
334
ShouldBypassForResponse() const335 bool ShouldBypassForResponse() const {
336 if (state_ == State::kResponseTaken)
337 return false;
338 DCHECK_EQ(!!response_metadata_, !!body_reader_);
339 DCHECK_EQ(state_, State::kResponseReceived);
340 return !response_metadata_;
341 }
342
343 // network::mojom::URLLoader methods
344 void FollowRedirect(
345 const std::vector<std::string>& removed_headers,
346 const net::HttpRequestHeaders& modified_headers,
347 const net::HttpRequestHeaders& modified_cors_exempt_headers,
348 const base::Optional<GURL>& new_url) override;
349 void SetPriority(net::RequestPriority priority,
350 int32_t intra_priority_value) override;
351 void PauseReadingBodyFromNet() override;
352 void ResumeReadingBodyFromNet() override;
353
354 // network::mojom::URLLoaderClient methods
355 void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override;
356 void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
357 network::mojom::URLResponseHeadPtr head) override;
358 void OnUploadProgress(int64_t current_position,
359 int64_t total_size,
360 OnUploadProgressCallback callback) override;
361 void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override;
362 void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
363 void OnStartLoadingResponseBody(
364 mojo::ScopedDataPipeConsumerHandle body) override;
365 void OnComplete(const network::URLLoaderCompletionStatus& status) override;
366
367 bool CanGetResponseBody(std::string* error_reason);
368 bool StartJobAndMaybeNotify();
369
370 void UpdateCORSFlag();
371 network::mojom::FetchResponseType CalculateResponseTainting();
372 network::ResourceRequest GetResourceRequestForCookies();
373
374 const std::string id_prefix_;
375 const GlobalRequestId global_req_id_;
376 const base::UnguessableToken frame_token_;
377 const bool report_upload_;
378
379 DevToolsURLLoaderInterceptor* interceptor_;
380 InterceptionStage stage_;
381
382 std::unique_ptr<CreateLoaderParameters> create_loader_params_;
383 const bool is_download_;
384
385 mojo::Receiver<network::mojom::URLLoaderClient> client_receiver_{this};
386 mojo::Receiver<network::mojom::URLLoader> loader_receiver_{this};
387
388 mojo::Remote<network::mojom::URLLoaderClient> client_;
389 mojo::Remote<network::mojom::URLLoader> loader_;
390 mojo::Remote<network::mojom::URLLoaderFactory> target_factory_;
391 mojo::Remote<network::mojom::CookieManager> cookie_manager_;
392
393 enum State {
394 kNotStarted,
395 kRequestSent,
396 kRedirectReceived,
397 kFollowRedirect,
398 kAuthRequired,
399 kResponseReceived,
400 kResponseTaken,
401 };
402
403 State state_;
404 base::TimeTicks start_ticks_;
405 base::Time start_time_;
406
407 bool waiting_for_resolution_;
408 int redirect_count_;
409 bool tainted_origin_ = false;
410 bool fetch_cors_flag_ = false;
411 std::string current_id_;
412
413 std::unique_ptr<BodyReader> body_reader_;
414 std::unique_ptr<ResponseMetadata> response_metadata_;
415 bool registered_in_global_request_map_;
416
417 base::Optional<std::pair<net::RequestPriority, int32_t>> priority_;
418 DevToolsURLLoaderInterceptor::HandleAuthRequestCallback
419 pending_auth_callback_;
420 TakeResponseBodyPipeCallback pending_response_body_pipe_callback_;
421
422 const base::Optional<std::string> renderer_request_id_;
423
424 DISALLOW_COPY_AND_ASSIGN(InterceptionJob);
425 };
426
CreateJob(const base::UnguessableToken & frame_token,int32_t process_id,bool is_download,const base::Optional<std::string> & renderer_request_id,std::unique_ptr<CreateLoaderParameters> create_params,mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,mojo::PendingRemote<network::mojom::URLLoaderClient> client,mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,mojo::PendingRemote<network::mojom::CookieManager> cookie_manager)427 void DevToolsURLLoaderInterceptor::CreateJob(
428 const base::UnguessableToken& frame_token,
429 int32_t process_id,
430 bool is_download,
431 const base::Optional<std::string>& renderer_request_id,
432 std::unique_ptr<CreateLoaderParameters> create_params,
433 mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
434 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
435 mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
436 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager) {
437 DCHECK(!frame_token.is_empty());
438
439 static int last_id = 0;
440
441 std::string id = base::StringPrintf("interception-job-%d", ++last_id);
442 // This class will manage its own life time to match the loader client.
443 new InterceptionJob(
444 this, std::move(id), frame_token, process_id, renderer_request_id,
445 std::move(create_params), is_download, std::move(loader_receiver),
446 std::move(client), std::move(target_factory), std::move(cookie_manager));
447 }
448
GetInterceptionStage(const GURL & url,blink::mojom::ResourceType resource_type) const449 InterceptionStage DevToolsURLLoaderInterceptor::GetInterceptionStage(
450 const GURL& url,
451 blink::mojom::ResourceType resource_type) const {
452 InterceptionStage stage = InterceptionStage::DONT_INTERCEPT;
453 std::string unused;
454 std::string url_str = protocol::NetworkHandler::ExtractFragment(url, &unused);
455 for (const auto& pattern : patterns_) {
456 if (pattern.Matches(url_str, resource_type))
457 stage |= pattern.interception_stage;
458 }
459 return stage;
460 }
461
462 class DevToolsURLLoaderFactoryProxy : public network::mojom::URLLoaderFactory {
463 public:
464 DevToolsURLLoaderFactoryProxy(
465 const base::UnguessableToken& frame_token,
466 int32_t process_id,
467 bool is_download,
468 mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
469 mojo::PendingRemote<network::mojom::URLLoaderFactory>
470 target_factory_remote,
471 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager,
472 base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor);
473 ~DevToolsURLLoaderFactoryProxy() override;
474
475 private:
476 // network::mojom::URLLoaderFactory implementation
477 void CreateLoaderAndStart(
478 mojo::PendingReceiver<network::mojom::URLLoader> loader,
479 int32_t routing_id,
480 int32_t request_id,
481 uint32_t options,
482 const network::ResourceRequest& request,
483 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
484 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
485 override;
486 void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
487 override;
488
489 void OnProxyBindingError();
490 void OnTargetFactoryError();
491
492 const base::UnguessableToken frame_token_;
493 const int32_t process_id_;
494 const bool is_download_;
495
496 mojo::Remote<network::mojom::URLLoaderFactory> target_factory_;
497 mojo::Remote<network::mojom::CookieManager> cookie_manager_;
498 base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor_;
499 mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
500
501 SEQUENCE_CHECKER(sequence_checker_);
502 };
503
504 // This class owns itself and will delete self when any mojo
505 // connection is broken.
DevToolsURLLoaderFactoryProxy(const base::UnguessableToken & frame_token,int32_t process_id,bool is_download,mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_remote,mojo::PendingRemote<network::mojom::CookieManager> cookie_manager,base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor)506 DevToolsURLLoaderFactoryProxy::DevToolsURLLoaderFactoryProxy(
507 const base::UnguessableToken& frame_token,
508 int32_t process_id,
509 bool is_download,
510 mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
511 mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_remote,
512 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager,
513 base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor)
514 : frame_token_(frame_token),
515 process_id_(process_id),
516 is_download_(is_download),
517 target_factory_(std::move(target_factory_remote)),
518 interceptor_(std::move(interceptor)) {
519 target_factory_.set_disconnect_handler(
520 base::BindOnce(&DevToolsURLLoaderFactoryProxy::OnTargetFactoryError,
521 base::Unretained(this)));
522
523 receivers_.Add(this, std::move(loader_receiver));
524 receivers_.set_disconnect_handler(
525 base::BindRepeating(&DevToolsURLLoaderFactoryProxy::OnProxyBindingError,
526 base::Unretained(this)));
527
528 cookie_manager_.Bind(std::move(cookie_manager));
529 cookie_manager_.set_disconnect_handler(
530 base::BindOnce(&DevToolsURLLoaderFactoryProxy::OnTargetFactoryError,
531 base::Unretained(this)));
532 }
533
534 DevToolsURLLoaderFactoryProxy::~DevToolsURLLoaderFactoryProxy() = default;
535
CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation)536 void DevToolsURLLoaderFactoryProxy::CreateLoaderAndStart(
537 mojo::PendingReceiver<network::mojom::URLLoader> loader,
538 int32_t routing_id,
539 int32_t request_id,
540 uint32_t options,
541 const network::ResourceRequest& request,
542 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
543 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
544 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
545
546 DevToolsURLLoaderInterceptor* interceptor = interceptor_.get();
547 if (!interceptor_ || request.url.SchemeIs(url::kDataScheme)) {
548 target_factory_->CreateLoaderAndStart(
549 std::move(loader), routing_id, request_id, options, request,
550 std::move(client), traffic_annotation);
551 return;
552 }
553 auto creation_params = std::make_unique<CreateLoaderParameters>(
554 routing_id, request_id, options, request, traffic_annotation);
555 mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_clone;
556 target_factory_->Clone(factory_clone.InitWithNewPipeAndPassReceiver());
557 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_clone;
558 cookie_manager_->CloneInterface(
559 cookie_manager_clone.InitWithNewPipeAndPassReceiver());
560 interceptor->CreateJob(
561 frame_token_, process_id_, is_download_, request.devtools_request_id,
562 std::move(creation_params), std::move(loader), std::move(client),
563 std::move(factory_clone), std::move(cookie_manager_clone));
564 }
565
Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)566 void DevToolsURLLoaderFactoryProxy::Clone(
567 mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
568 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
569 receivers_.Add(this, std::move(receiver));
570 }
571
OnTargetFactoryError()572 void DevToolsURLLoaderFactoryProxy::OnTargetFactoryError() {
573 delete this;
574 }
575
OnProxyBindingError()576 void DevToolsURLLoaderFactoryProxy::OnProxyBindingError() {
577 if (receivers_.empty())
578 delete this;
579 }
580
581 // static
HandleAuthRequest(int32_t process_id,int32_t routing_id,int32_t request_id,const net::AuthChallengeInfo & auth_info,HandleAuthRequestCallback callback)582 void DevToolsURLLoaderInterceptor::HandleAuthRequest(
583 int32_t process_id,
584 int32_t routing_id,
585 int32_t request_id,
586 const net::AuthChallengeInfo& auth_info,
587 HandleAuthRequestCallback callback) {
588 GlobalRequestId req_id = std::make_tuple(process_id, routing_id, request_id);
589 if (auto* job = InterceptionJob::FindByRequestId(req_id))
590 job->OnAuthRequest(auth_info, std::move(callback));
591 else
592 std::move(callback).Run(true, base::nullopt);
593 }
594
DevToolsURLLoaderInterceptor(RequestInterceptedCallback callback)595 DevToolsURLLoaderInterceptor::DevToolsURLLoaderInterceptor(
596 RequestInterceptedCallback callback)
597 : request_intercepted_callback_(std::move(callback)), weak_factory_(this) {}
598
~DevToolsURLLoaderInterceptor()599 DevToolsURLLoaderInterceptor::~DevToolsURLLoaderInterceptor() {
600 for (auto const& entry : jobs_)
601 entry.second->Detach();
602 }
603
SetPatterns(std::vector<DevToolsURLLoaderInterceptor::Pattern> patterns,bool handle_auth)604 void DevToolsURLLoaderInterceptor::SetPatterns(
605 std::vector<DevToolsURLLoaderInterceptor::Pattern> patterns,
606 bool handle_auth) {
607 patterns_ = std::move(patterns);
608 handle_auth_ = handle_auth;
609 DCHECK(patterns_.size() || !handle_auth);
610 }
611
GetResponseBody(const std::string & interception_id,std::unique_ptr<GetResponseBodyCallback> callback)612 void DevToolsURLLoaderInterceptor::GetResponseBody(
613 const std::string& interception_id,
614 std::unique_ptr<GetResponseBodyCallback> callback) {
615 if (InterceptionJob* job = FindJob(interception_id, &callback))
616 job->GetResponseBody(std::move(callback));
617 }
618
TakeResponseBodyPipe(const std::string & interception_id,DevToolsURLLoaderInterceptor::TakeResponseBodyPipeCallback callback)619 void DevToolsURLLoaderInterceptor::TakeResponseBodyPipe(
620 const std::string& interception_id,
621 DevToolsURLLoaderInterceptor::TakeResponseBodyPipeCallback callback) {
622 auto it = jobs_.find(interception_id);
623 if (it == jobs_.end()) {
624 std::move(callback).Run(
625 protocol::Response::InvalidParams("Invalid InterceptionId."),
626 mojo::ScopedDataPipeConsumerHandle(), base::EmptyString());
627 return;
628 }
629 it->second->TakeResponseBodyPipe(std::move(callback));
630 }
631
ContinueInterceptedRequest(const std::string & interception_id,std::unique_ptr<Modifications> modifications,std::unique_ptr<ContinueInterceptedRequestCallback> callback)632 void DevToolsURLLoaderInterceptor::ContinueInterceptedRequest(
633 const std::string& interception_id,
634 std::unique_ptr<Modifications> modifications,
635 std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
636 if (InterceptionJob* job = FindJob(interception_id, &callback)) {
637 job->ContinueInterceptedRequest(std::move(modifications),
638 std::move(callback));
639 }
640 }
641
CreateProxyForInterception(RenderProcessHost * rph,const base::UnguessableToken & frame_token,bool is_navigation,bool is_download,network::mojom::URLLoaderFactoryOverride * intercepting_factory)642 bool DevToolsURLLoaderInterceptor::CreateProxyForInterception(
643 RenderProcessHost* rph,
644 const base::UnguessableToken& frame_token,
645 bool is_navigation,
646 bool is_download,
647 network::mojom::URLLoaderFactoryOverride* intercepting_factory) {
648 if (patterns_.empty())
649 return false;
650
651 // If we're the first interceptor to install an override, make a
652 // remote/receiver pair, then handle this similarly to appending
653 // a proxy to existing override.
654 if (!intercepting_factory->overriding_factory) {
655 DCHECK(!intercepting_factory->overridden_factory_receiver);
656 intercepting_factory->overridden_factory_receiver =
657 intercepting_factory->overriding_factory
658 .InitWithNewPipeAndPassReceiver();
659 }
660 mojo::PendingRemote<network::mojom::URLLoaderFactory> target_remote;
661 auto overridden_factory_receiver =
662 target_remote.InitWithNewPipeAndPassReceiver();
663 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager;
664 int process_id = is_navigation ? 0 : rph->GetID();
665 rph->GetStoragePartition()->GetNetworkContext()->GetCookieManager(
666 cookie_manager.InitWithNewPipeAndPassReceiver());
667 new DevToolsURLLoaderFactoryProxy(
668 frame_token, process_id, is_download,
669 std::move(intercepting_factory->overridden_factory_receiver),
670 std::move(target_remote), std::move(cookie_manager),
671 weak_factory_.GetWeakPtr());
672 intercepting_factory->overridden_factory_receiver =
673 std::move(overridden_factory_receiver);
674 return true;
675 }
676
InterceptionJob(DevToolsURLLoaderInterceptor * interceptor,const std::string & id,const base::UnguessableToken & frame_token,int process_id,const base::Optional<std::string> & renderer_request_id,std::unique_ptr<CreateLoaderParameters> create_loader_params,bool is_download,mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,mojo::PendingRemote<network::mojom::URLLoaderClient> client,mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,mojo::PendingRemote<network::mojom::CookieManager> cookie_manager)677 InterceptionJob::InterceptionJob(
678 DevToolsURLLoaderInterceptor* interceptor,
679 const std::string& id,
680 const base::UnguessableToken& frame_token,
681 int process_id,
682 const base::Optional<std::string>& renderer_request_id,
683 std::unique_ptr<CreateLoaderParameters> create_loader_params,
684 bool is_download,
685 mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
686 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
687 mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
688 mojo::PendingRemote<network::mojom::CookieManager> cookie_manager)
689 : id_prefix_(id),
690 global_req_id_(
691 std::make_tuple(process_id,
692 create_loader_params->request.render_frame_id,
693 create_loader_params->request_id)),
694 frame_token_(frame_token),
695 report_upload_(!!create_loader_params->request.request_body),
696 interceptor_(interceptor),
697 create_loader_params_(std::move(create_loader_params)),
698 is_download_(is_download),
699 client_(std::move(client)),
700 target_factory_(std::move(target_factory)),
701 cookie_manager_(std::move(cookie_manager)),
702 state_(kNotStarted),
703 waiting_for_resolution_(false),
704 redirect_count_(0),
705 renderer_request_id_(renderer_request_id) {
706 loader_receiver_.Bind(std::move(loader_receiver));
707 loader_receiver_.set_disconnect_handler(
708 base::BindOnce(&InterceptionJob::Shutdown, base::Unretained(this)));
709
710 auto& job_map = GetInterceptionJobMap();
711 // TODO(caseq): for now, all auth requests will go to the top-level job.
712 // Figure out if we need anything smarter here.
713 registered_in_global_request_map_ =
714 job_map.emplace(global_req_id_, this).second;
715
716 if (StartJobAndMaybeNotify())
717 return;
718
719 StartRequest();
720 }
721
StartJobAndMaybeNotify()722 bool InterceptionJob::StartJobAndMaybeNotify() {
723 UpdateCORSFlag();
724 start_ticks_ = base::TimeTicks::Now();
725 start_time_ = base::Time::Now();
726
727 current_id_ = id_prefix_ + base::StringPrintf(".%d", redirect_count_);
728 interceptor_->AddJob(current_id_, this);
729
730 const network::ResourceRequest& request = create_loader_params_->request;
731 stage_ = interceptor_->GetInterceptionStage(
732 request.url,
733 static_cast<blink::mojom::ResourceType>(request.resource_type));
734
735 if (!(stage_ & InterceptionStage::REQUEST))
736 return false;
737
738 if (state_ == State::kRedirectReceived)
739 state_ = State::kFollowRedirect;
740 else
741 DCHECK_EQ(State::kNotStarted, state_);
742 NotifyClient(BuildRequestInfo(nullptr));
743 return true;
744 }
745
746 // FIXME(caseq): The logic in the three methods below is borrowed from
747 // CorsURLLoader as a matter of a quick and mergeable fix for crbug.com/1022173.
748 // This logic should be unified with CorsURLLoader.
CalculateResponseTainting()749 network::mojom::FetchResponseType InterceptionJob::CalculateResponseTainting() {
750 if (fetch_cors_flag_)
751 return FetchResponseType::kCors;
752 if (create_loader_params_->request.mode ==
753 network::mojom::RequestMode::kNoCors &&
754 tainted_origin_) {
755 return FetchResponseType::kOpaque;
756 }
757 return FetchResponseType::kBasic;
758 }
759
GetResourceRequestForCookies()760 network::ResourceRequest InterceptionJob::GetResourceRequestForCookies() {
761 FetchResponseType response_tainting =
762 fetch_cors_flag_ ? FetchResponseType::kCors : FetchResponseType::kBasic;
763
764 network::ResourceRequest result = create_loader_params_->request;
765 result.credentials_mode =
766 network::cors::CalculateCredentialsFlag(
767 create_loader_params_->request.credentials_mode, response_tainting)
768 ? CredentialsMode::kInclude
769 : CredentialsMode::kOmit;
770 return result;
771 }
772
UpdateCORSFlag()773 void InterceptionJob::UpdateCORSFlag() {
774 if (fetch_cors_flag_)
775 return;
776
777 const network::ResourceRequest& request = create_loader_params_->request;
778 fetch_cors_flag_ = network::cors::ShouldCheckCors(
779 request.url, request.request_initiator, request.mode);
780 }
781
CanGetResponseBody(std::string * error_reason)782 bool InterceptionJob::CanGetResponseBody(std::string* error_reason) {
783 if (!(stage_ & InterceptionStage::RESPONSE)) {
784 *error_reason =
785 "Can only get response body on HeadersReceived pattern matched "
786 "requests.";
787 return false;
788 }
789 if (state_ != State::kResponseReceived || !waiting_for_resolution_) {
790 *error_reason =
791 "Can only get response body on requests captured after headers "
792 "received.";
793 return false;
794 }
795 return true;
796 }
797
GetResponseBody(std::unique_ptr<GetResponseBodyCallback> callback)798 void InterceptionJob::GetResponseBody(
799 std::unique_ptr<GetResponseBodyCallback> callback) {
800 std::string error_reason;
801 if (!CanGetResponseBody(&error_reason)) {
802 callback->sendFailure(Response::ServerError(std::move(error_reason)));
803 return;
804 }
805 if (!body_reader_) {
806 body_reader_ = std::make_unique<BodyReader>(base::BindOnce(
807 &InterceptionJob::ResponseBodyComplete, base::Unretained(this)));
808 client_receiver_.Resume();
809 loader_->ResumeReadingBodyFromNet();
810 }
811 body_reader_->AddCallback(std::move(callback));
812 }
813
TakeResponseBodyPipe(TakeResponseBodyPipeCallback callback)814 void InterceptionJob::TakeResponseBodyPipe(
815 TakeResponseBodyPipeCallback callback) {
816 std::string error_reason;
817 if (!CanGetResponseBody(&error_reason)) {
818 std::move(callback).Run(Response::ServerError(std::move(error_reason)),
819 mojo::ScopedDataPipeConsumerHandle(),
820 base::EmptyString());
821 return;
822 }
823 DCHECK_EQ(state_, State::kResponseReceived);
824 DCHECK(!!response_metadata_);
825 state_ = State::kResponseTaken;
826 pending_response_body_pipe_callback_ = std::move(callback);
827 client_receiver_.Resume();
828 loader_->ResumeReadingBodyFromNet();
829 }
830
ContinueInterceptedRequest(std::unique_ptr<Modifications> modifications,std::unique_ptr<ContinueInterceptedRequestCallback> callback)831 void InterceptionJob::ContinueInterceptedRequest(
832 std::unique_ptr<Modifications> modifications,
833 std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
834 Response response = InnerContinueRequest(std::move(modifications));
835 // |this| may be destroyed at this point.
836 if (response.IsSuccess())
837 callback->sendSuccess();
838 else
839 callback->sendFailure(std::move(response));
840 }
841
Detach()842 void InterceptionJob::Detach() {
843 stage_ = InterceptionStage::DONT_INTERCEPT;
844 interceptor_ = nullptr;
845 if (!waiting_for_resolution_)
846 return;
847 if (state_ == State::kAuthRequired) {
848 state_ = State::kRequestSent;
849 waiting_for_resolution_ = false;
850 std::move(pending_auth_callback_).Run(true, base::nullopt);
851 return;
852 }
853 InnerContinueRequest(std::make_unique<Modifications>());
854 }
855
InnerContinueRequest(std::unique_ptr<Modifications> modifications)856 Response InterceptionJob::InnerContinueRequest(
857 std::unique_ptr<Modifications> modifications) {
858 if (!waiting_for_resolution_) {
859 return Response::ServerError(
860 "Invalid state for continueInterceptedRequest");
861 }
862 waiting_for_resolution_ = false;
863
864 if (state_ == State::kAuthRequired) {
865 if (!modifications->auth_challenge_response)
866 return Response::InvalidParams("authChallengeResponse required.");
867 ProcessAuthResponse(*modifications->auth_challenge_response);
868 return Response::Success();
869 }
870 if (modifications->auth_challenge_response)
871 return Response::InvalidParams("authChallengeResponse not expected.");
872
873 if (modifications->error_reason) {
874 network::URLLoaderCompletionStatus status(
875 modifications->error_reason.value());
876 status.completion_time = base::TimeTicks::Now();
877 if (modifications->error_reason == net::ERR_BLOCKED_BY_CLIENT) {
878 // So we know that these modifications originated from devtools
879 // (also known as inspector), and can therefore annotate the
880 // request. We only do this for one specific error code thus
881 // far, to minimize risk of breaking other usages.
882 status.extended_error_code =
883 static_cast<int>(blink::ResourceRequestBlockedReason::kInspector);
884 }
885 CompleteRequest(status);
886 return Response::Success();
887 }
888
889 if (modifications->response_headers || modifications->response_body) {
890 return ProcessResponseOverride(std::move(modifications->response_headers),
891 std::move(modifications->response_body),
892 modifications->body_offset);
893 }
894
895 if (state_ == State::kFollowRedirect) {
896 if (modifications->modified_url.isJust()) {
897 CancelRequest();
898 // Fall through to the generic logic of re-starting the request
899 // at the bottom of the method.
900 } else {
901 // TODO(caseq): report error if other modifications are present.
902 state_ = State::kRequestSent;
903 loader_->FollowRedirect({}, {}, {}, base::nullopt);
904 return Response::Success();
905 }
906 }
907 if (state_ == State::kRedirectReceived) {
908 // TODO(caseq): report error if other modifications are present.
909 if (modifications->modified_url.isJust()) {
910 std::string location = modifications->modified_url.fromJust();
911 CancelRequest();
912 response_metadata_->head->headers->SetHeader("location", location);
913 GURL redirect_url = create_loader_params_->request.url.Resolve(location);
914 if (!redirect_url.is_valid())
915 return Response::ServerError("Invalid modified URL");
916 ProcessRedirectByClient(redirect_url);
917 return Response::Success();
918 }
919 client_->OnReceiveRedirect(*response_metadata_->redirect_info,
920 std::move(response_metadata_->head));
921 return Response::Success();
922 }
923
924 if (body_reader_) {
925 if (body_reader_->data_complete())
926 SendResponse(body_reader_->body(), 0);
927
928 // There are read callbacks pending, so let the reader do its job and come
929 // back when it's done.
930 return Response::Success();
931 }
932
933 if (response_metadata_) {
934 if (state_ == State::kResponseTaken) {
935 return Response::InvalidParams(
936 "Unable to continue request as is after body is taken");
937 }
938 // TODO(caseq): report error if other modifications are present.
939 if (response_metadata_->status.error_code) {
940 CompleteRequest(response_metadata_->status);
941 return Response::Success();
942 }
943 DCHECK_EQ(State::kResponseReceived, state_);
944 DCHECK(!body_reader_);
945 client_->OnReceiveResponse(std::move(response_metadata_->head));
946 response_metadata_.reset();
947 loader_->ResumeReadingBodyFromNet();
948 client_receiver_.Resume();
949 return Response::Success();
950 }
951
952 DCHECK_EQ(State::kNotStarted, state_);
953 ApplyModificationsToRequest(std::move(modifications));
954 StartRequest();
955 return Response::Success();
956 }
957
ApplyModificationsToRequest(std::unique_ptr<Modifications> modifications)958 void InterceptionJob::ApplyModificationsToRequest(
959 std::unique_ptr<Modifications> modifications) {
960 network::ResourceRequest* request = &create_loader_params_->request;
961
962 // Note this redirect is not visible to the page by design. If they want a
963 // visible redirect they can mock a response with a 302.
964 if (modifications->modified_url.isJust())
965 request->url = GURL(modifications->modified_url.fromJust());
966
967 if (modifications->modified_method.isJust())
968 request->method = modifications->modified_method.fromJust();
969
970 if (modifications->modified_post_data.isJust()) {
971 const auto& post_data = modifications->modified_post_data.fromJust();
972 request->request_body = network::ResourceRequestBody::CreateFromBytes(
973 reinterpret_cast<const char*>(post_data.data()), post_data.size());
974 }
975
976 if (modifications->modified_headers) {
977 request->headers.Clear();
978 for (const auto& entry : *modifications->modified_headers) {
979 if (base::EqualsCaseInsensitiveASCII(entry.first,
980 net::HttpRequestHeaders::kReferer)) {
981 request->referrer = GURL(entry.second);
982 request->referrer_policy = net::ReferrerPolicy::NEVER_CLEAR;
983 } else {
984 request->headers.SetHeader(entry.first, entry.second);
985 }
986 }
987 }
988 }
989
ProcessAuthResponse(const DevToolsURLLoaderInterceptor::AuthChallengeResponse & response)990 void InterceptionJob::ProcessAuthResponse(
991 const DevToolsURLLoaderInterceptor::AuthChallengeResponse& response) {
992 DCHECK_EQ(kAuthRequired, state_);
993 switch (response.response_type) {
994 case DevToolsURLLoaderInterceptor::AuthChallengeResponse::kDefault:
995 std::move(pending_auth_callback_).Run(true, base::nullopt);
996 break;
997 case DevToolsURLLoaderInterceptor::AuthChallengeResponse::kCancelAuth:
998 std::move(pending_auth_callback_).Run(false, base::nullopt);
999 break;
1000 case DevToolsURLLoaderInterceptor::AuthChallengeResponse::
1001 kProvideCredentials:
1002 std::move(pending_auth_callback_).Run(false, response.credentials);
1003 break;
1004 }
1005 state_ = kRequestSent;
1006 }
1007
ProcessResponseOverride(scoped_refptr<net::HttpResponseHeaders> headers,scoped_refptr<base::RefCountedMemory> body,size_t response_body_offset)1008 Response InterceptionJob::ProcessResponseOverride(
1009 scoped_refptr<net::HttpResponseHeaders> headers,
1010 scoped_refptr<base::RefCountedMemory> body,
1011 size_t response_body_offset) {
1012 CancelRequest();
1013
1014 DCHECK_LE(response_body_offset, body ? body->size() : 0);
1015 size_t body_size = body ? body->size() - response_body_offset : 0;
1016 response_metadata_ = std::make_unique<ResponseMetadata>();
1017 network::mojom::URLResponseHeadPtr& head = response_metadata_->head;
1018
1019 head->request_time = start_time_;
1020 head->response_time = base::Time::Now();
1021
1022 // TODO(caseq): we're only doing this because some clients expect load timing
1023 // to be present with mocked responses. Consider removing this.
1024 const base::TimeTicks now_ticks = base::TimeTicks::Now();
1025 head->load_timing.request_start_time = start_time_;
1026 head->load_timing.request_start = start_ticks_;
1027 head->load_timing.receive_headers_end = now_ticks;
1028
1029 static const char kDummyHeaders[] = "HTTP/1.1 200 OK\0\0";
1030 head->headers = std::move(headers);
1031 if (!head->headers) {
1032 head->headers =
1033 base::MakeRefCounted<net::HttpResponseHeaders>(kDummyHeaders);
1034 }
1035 head->headers->GetMimeTypeAndCharset(&head->mime_type, &head->charset);
1036 if (head->mime_type.empty() && body_size) {
1037 size_t bytes_to_sniff =
1038 std::min(body_size, static_cast<size_t>(net::kMaxBytesToSniff));
1039 net::SniffMimeType(
1040 base::StringPiece(body->front_as<const char>() + response_body_offset,
1041 bytes_to_sniff),
1042 create_loader_params_->request.url, "",
1043 net::ForceSniffFileUrlsForHtml::kDisabled, &head->mime_type);
1044 head->did_mime_sniff = true;
1045 }
1046 // TODO(caseq): we're cheating here a bit, raw_headers() have \0's
1047 // where real headers would have \r\n, but the sizes here
1048 // probably don't have to be exact.
1049 size_t headers_size = head->headers->raw_headers().size();
1050 head->content_length = body_size;
1051 head->encoded_data_length = headers_size;
1052 head->encoded_body_length = 0;
1053 head->request_start = start_ticks_;
1054 head->response_start = now_ticks;
1055
1056 response_metadata_->transfer_size = body_size;
1057
1058 response_metadata_->status.completion_time = base::TimeTicks::Now();
1059 response_metadata_->status.encoded_data_length = headers_size + body_size;
1060 response_metadata_->status.encoded_body_length = body_size;
1061 response_metadata_->status.decoded_body_length = body_size;
1062
1063 base::OnceClosure continue_after_cookies_set;
1064 std::string location;
1065 if (head->headers->IsRedirect(&location)) {
1066 GURL redirect_url = create_loader_params_->request.url.Resolve(location);
1067 if (redirect_url.is_valid()) {
1068 continue_after_cookies_set =
1069 base::BindOnce(&InterceptionJob::ProcessRedirectByClient,
1070 base::Unretained(this), std::move(redirect_url));
1071 }
1072 }
1073 if (!continue_after_cookies_set) {
1074 continue_after_cookies_set =
1075 base::BindOnce(&InterceptionJob::SendResponse, base::Unretained(this),
1076 std::move(body), response_body_offset);
1077 }
1078 ProcessSetCookies(*head->headers, std::move(continue_after_cookies_set));
1079
1080 return Response::Success();
1081 }
1082
ProcessSetCookies(const net::HttpResponseHeaders & headers,base::OnceClosure callback)1083 void InterceptionJob::ProcessSetCookies(const net::HttpResponseHeaders& headers,
1084 base::OnceClosure callback) {
1085 if (!GetResourceRequestForCookies().SavesCookies()) {
1086 std::move(callback).Run();
1087 return;
1088 }
1089
1090 std::vector<std::unique_ptr<net::CanonicalCookie>> cookies;
1091 base::Time response_date;
1092 base::Optional<base::Time> server_time = base::nullopt;
1093 if (headers.GetDateValue(&response_date))
1094 server_time = base::make_optional(response_date);
1095 base::Time now = base::Time::Now();
1096
1097 const base::StringPiece name("Set-Cookie");
1098 std::string cookie_line;
1099 size_t iter = 0;
1100 while (headers.EnumerateHeader(&iter, name, &cookie_line)) {
1101 std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
1102 create_loader_params_->request.url, cookie_line, now, server_time);
1103 if (cookie)
1104 cookies.emplace_back(std::move(cookie));
1105 }
1106
1107 net::CookieOptions options;
1108 options.set_include_httponly();
1109 bool should_treat_as_first_party =
1110 GetContentClient()
1111 ->browser()
1112 ->ShouldIgnoreSameSiteCookieRestrictionsWhenTopLevel(
1113 create_loader_params_->request.site_for_cookies.scheme(),
1114 create_loader_params_->request.url.SchemeIsCryptographic());
1115 options.set_same_site_cookie_context(
1116 net::cookie_util::ComputeSameSiteContextForResponse(
1117 create_loader_params_->request.url,
1118 create_loader_params_->request.site_for_cookies,
1119 create_loader_params_->request.request_initiator,
1120 (create_loader_params_->request.force_ignore_site_for_cookies ||
1121 should_treat_as_first_party)));
1122
1123 // |this| might be deleted here if |cookies| is empty!
1124 auto on_cookie_set = base::BindRepeating(
1125 [](base::RepeatingClosure closure, net::CookieAccessResult) {
1126 closure.Run();
1127 },
1128 base::BarrierClosure(cookies.size(), std::move(callback)));
1129 for (auto& cookie : cookies) {
1130 cookie_manager_->SetCanonicalCookie(
1131 *cookie, create_loader_params_->request.url, options, on_cookie_set);
1132 }
1133 }
1134
ProcessRedirectByClient(const GURL & redirect_url)1135 void InterceptionJob::ProcessRedirectByClient(const GURL& redirect_url) {
1136 DCHECK(redirect_url.is_valid());
1137
1138 const net::HttpResponseHeaders& headers = *response_metadata_->head->headers;
1139 const network::ResourceRequest& request = create_loader_params_->request;
1140
1141 auto first_party_url_policy =
1142 request.update_first_party_url_on_redirect
1143 ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
1144 : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL;
1145
1146 response_metadata_->redirect_info = std::make_unique<net::RedirectInfo>(
1147 net::RedirectInfo::ComputeRedirectInfo(
1148 request.method, request.url, request.site_for_cookies,
1149 first_party_url_policy, request.referrer_policy,
1150 request.referrer.spec(), headers.response_code(), redirect_url,
1151 net::RedirectUtil::GetReferrerPolicyHeader(&headers),
1152 false /* insecure_scheme_was_upgraded */, true /* copy_fragment */));
1153
1154 client_->OnReceiveRedirect(*response_metadata_->redirect_info,
1155 std::move(response_metadata_->head));
1156 }
1157
SendResponse(scoped_refptr<base::RefCountedMemory> body,size_t offset)1158 void InterceptionJob::SendResponse(scoped_refptr<base::RefCountedMemory> body,
1159 size_t offset) {
1160 client_->OnReceiveResponse(std::move(response_metadata_->head));
1161 if (response_metadata_->cached_metadata.size() != 0)
1162 client_->OnReceiveCachedMetadata(
1163 std::move(response_metadata_->cached_metadata));
1164
1165 if (body) {
1166 DCHECK_LE(offset, body->size());
1167 size_t body_size = body->size() - offset;
1168 // We shouldn't be able to transfer a string that big over the protocol,
1169 // but just in case...
1170 DCHECK_LE(body_size, UINT32_MAX)
1171 << "Response bodies larger than " << UINT32_MAX << " are not supported";
1172 mojo::DataPipe pipe(body_size);
1173 uint32_t num_bytes = body_size;
1174 MojoResult res = pipe.producer_handle->WriteData(
1175 body->front() + offset, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
1176 DCHECK_EQ(0u, res);
1177 DCHECK_EQ(num_bytes, body_size);
1178 client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
1179 }
1180 if (response_metadata_->transfer_size)
1181 client_->OnTransferSizeUpdated(response_metadata_->transfer_size);
1182 CompleteRequest(response_metadata_->status);
1183 }
1184
ResponseBodyComplete()1185 void InterceptionJob::ResponseBodyComplete() {
1186 if (waiting_for_resolution_)
1187 return;
1188 // We're here only if client has already told us to proceed with unmodified
1189 // response.
1190 SendResponse(body_reader_->body(), 0);
1191 }
1192
StartRequest()1193 void InterceptionJob::StartRequest() {
1194 DCHECK_EQ(State::kNotStarted, state_);
1195 DCHECK(!response_metadata_);
1196
1197 state_ = State::kRequestSent;
1198
1199 target_factory_->CreateLoaderAndStart(
1200 loader_.BindNewPipeAndPassReceiver(), create_loader_params_->routing_id,
1201 create_loader_params_->request_id, create_loader_params_->options,
1202 create_loader_params_->request,
1203 client_receiver_.BindNewPipeAndPassRemote(),
1204 create_loader_params_->traffic_annotation);
1205 client_receiver_.set_disconnect_handler(
1206 base::BindOnce(&InterceptionJob::Shutdown, base::Unretained(this)));
1207
1208 if (priority_)
1209 loader_->SetPriority(priority_->first, priority_->second);
1210 }
1211
CancelRequest()1212 void InterceptionJob::CancelRequest() {
1213 if (state_ == State::kNotStarted)
1214 return;
1215 client_receiver_.reset();
1216 loader_.reset();
1217 if (body_reader_) {
1218 body_reader_->CancelWithError(
1219 "Another command has cancelled the fetch request");
1220 body_reader_.reset();
1221 }
1222 state_ = State::kNotStarted;
1223 }
1224
BuildRequestInfo(const network::mojom::URLResponseHeadPtr & head)1225 std::unique_ptr<InterceptedRequestInfo> InterceptionJob::BuildRequestInfo(
1226 const network::mojom::URLResponseHeadPtr& head) {
1227 auto result = std::make_unique<InterceptedRequestInfo>();
1228 result->interception_id = current_id_;
1229 if (renderer_request_id_.has_value())
1230 result->renderer_request_id = renderer_request_id_.value();
1231 result->frame_id = frame_token_;
1232 blink::mojom::ResourceType resource_type =
1233 static_cast<blink::mojom::ResourceType>(
1234 create_loader_params_->request.resource_type);
1235 result->resource_type = resource_type;
1236 result->is_navigation =
1237 resource_type == blink::mojom::ResourceType::kMainFrame ||
1238 resource_type == blink::mojom::ResourceType::kSubFrame;
1239
1240 if (head && head->headers)
1241 result->response_headers = head->headers;
1242 return result;
1243 }
1244
FetchCookies(network::mojom::CookieManager::GetCookieListCallback callback)1245 void InterceptionJob::FetchCookies(
1246 network::mojom::CookieManager::GetCookieListCallback callback) {
1247 if (!GetResourceRequestForCookies().SendsCookies()) {
1248 std::move(callback).Run({}, {});
1249 return;
1250 }
1251 net::CookieOptions options;
1252 options.set_include_httponly();
1253 options.set_do_not_update_access_time();
1254
1255 const network::ResourceRequest& request = create_loader_params_->request;
1256
1257 bool should_treat_as_first_party =
1258 GetContentClient()
1259 ->browser()
1260 ->ShouldIgnoreSameSiteCookieRestrictionsWhenTopLevel(
1261 request.site_for_cookies.scheme(),
1262 request.url.SchemeIsCryptographic());
1263 options.set_same_site_cookie_context(
1264 net::cookie_util::ComputeSameSiteContextForRequest(
1265 request.method, request.url, request.site_for_cookies,
1266 request.request_initiator,
1267 (request.force_ignore_site_for_cookies ||
1268 should_treat_as_first_party)));
1269
1270 cookie_manager_->GetCookieList(request.url, options, std::move(callback));
1271 }
1272
NotifyClient(std::unique_ptr<InterceptedRequestInfo> request_info)1273 void InterceptionJob::NotifyClient(
1274 std::unique_ptr<InterceptedRequestInfo> request_info) {
1275 DCHECK(!waiting_for_resolution_);
1276 FetchCookies(base::BindOnce(&InterceptionJob::NotifyClientWithCookies,
1277 base::Unretained(this), std::move(request_info)));
1278 }
1279
NotifyClientWithCookies(std::unique_ptr<InterceptedRequestInfo> request_info,const net::CookieAccessResultList & cookies_with_access_result,const net::CookieAccessResultList & excluded_cookies)1280 void InterceptionJob::NotifyClientWithCookies(
1281 std::unique_ptr<InterceptedRequestInfo> request_info,
1282 const net::CookieAccessResultList& cookies_with_access_result,
1283 const net::CookieAccessResultList& excluded_cookies) {
1284 if (!interceptor_)
1285 return;
1286 std::string cookie_line;
1287 if (!cookies_with_access_result.empty()) {
1288 cookie_line =
1289 net::CanonicalCookie::BuildCookieLine(cookies_with_access_result);
1290 }
1291 request_info->network_request =
1292 protocol::NetworkHandler::CreateRequestFromResourceRequest(
1293 create_loader_params_->request, cookie_line);
1294
1295 waiting_for_resolution_ = true;
1296 interceptor_->request_intercepted_callback_.Run(std::move(request_info));
1297 }
1298
CompleteRequest(const network::URLLoaderCompletionStatus & status)1299 void InterceptionJob::CompleteRequest(
1300 const network::URLLoaderCompletionStatus& status) {
1301 client_->OnComplete(status);
1302 Shutdown();
1303 }
1304
Shutdown()1305 void InterceptionJob::Shutdown() {
1306 if (interceptor_)
1307 interceptor_->RemoveJob(current_id_);
1308 delete this;
1309 }
1310
1311 // URLLoader methods
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const net::HttpRequestHeaders & modified_cors_exempt_headers,const base::Optional<GURL> & new_url)1312 void InterceptionJob::FollowRedirect(
1313 const std::vector<std::string>& removed_headers,
1314 const net::HttpRequestHeaders& modified_headers,
1315 const net::HttpRequestHeaders& modified_cors_exempt_headers,
1316 const base::Optional<GURL>& new_url) {
1317 DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
1318 "supported yet. crbug.com/845683";
1319 DCHECK(!waiting_for_resolution_);
1320
1321 network::ResourceRequest* request = &create_loader_params_->request;
1322 const net::RedirectInfo& info = *response_metadata_->redirect_info;
1323 const auto current_origin = url::Origin::Create(request->url);
1324 if (request->request_initiator &&
1325 (!url::Origin::Create(info.new_url).IsSameOriginWith(current_origin) &&
1326 !request->request_initiator->IsSameOriginWith(current_origin))) {
1327 tainted_origin_ = true;
1328 }
1329
1330 bool clear_body = false;
1331 net::RedirectUtil::UpdateHttpRequest(request->url, request->method, info,
1332 removed_headers, modified_headers,
1333 &request->headers, &clear_body);
1334 request->cors_exempt_headers.MergeFrom(modified_cors_exempt_headers);
1335 for (const std::string& name : removed_headers)
1336 request->cors_exempt_headers.RemoveHeader(name);
1337
1338 if (clear_body)
1339 request->request_body = nullptr;
1340 request->method = info.new_method;
1341 request->url = info.new_url;
1342 request->site_for_cookies = info.new_site_for_cookies;
1343 request->referrer_policy = info.new_referrer_policy;
1344 request->referrer = GURL(info.new_referrer);
1345 response_metadata_.reset();
1346
1347 UpdateCORSFlag();
1348
1349 if (interceptor_) {
1350 // Pretend that each redirect hop is a new request -- this is for
1351 // compatibilty with URLRequestJob-based interception implementation.
1352 interceptor_->RemoveJob(current_id_);
1353 redirect_count_++;
1354 if (StartJobAndMaybeNotify())
1355 return;
1356 }
1357 if (state_ == State::kRedirectReceived) {
1358 state_ = State::kRequestSent;
1359 loader_->FollowRedirect(removed_headers, modified_headers,
1360 modified_cors_exempt_headers,
1361 base::nullopt /* new_url */);
1362 return;
1363 }
1364
1365 DCHECK_EQ(State::kNotStarted, state_);
1366 StartRequest();
1367 }
1368
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)1369 void InterceptionJob::SetPriority(net::RequestPriority priority,
1370 int32_t intra_priority_value) {
1371 priority_ = std::make_pair(priority, intra_priority_value);
1372
1373 if (loader_)
1374 loader_->SetPriority(priority, intra_priority_value);
1375 }
1376
PauseReadingBodyFromNet()1377 void InterceptionJob::PauseReadingBodyFromNet() {
1378 if (!body_reader_ && loader_ && state_ != State::kResponseTaken)
1379 loader_->PauseReadingBodyFromNet();
1380 }
1381
ResumeReadingBodyFromNet()1382 void InterceptionJob::ResumeReadingBodyFromNet() {
1383 if (!body_reader_ && loader_ && state_ != State::kResponseTaken)
1384 loader_->ResumeReadingBodyFromNet();
1385 }
1386
1387 // URLLoaderClient methods
OnReceiveResponse(network::mojom::URLResponseHeadPtr head)1388 void InterceptionJob::OnReceiveResponse(
1389 network::mojom::URLResponseHeadPtr head) {
1390 state_ = State::kResponseReceived;
1391 DCHECK(!response_metadata_);
1392 if (!(stage_ & InterceptionStage::RESPONSE)) {
1393 client_->OnReceiveResponse(std::move(head));
1394 return;
1395 }
1396 loader_->PauseReadingBodyFromNet();
1397 client_receiver_.Pause();
1398
1399 auto request_info = BuildRequestInfo(head);
1400 const network::ResourceRequest& request = create_loader_params_->request;
1401 request_info->is_download =
1402 request_info->is_navigation &&
1403 (is_download_ || download_utils::IsDownload(
1404 request.url, head->headers.get(), head->mime_type));
1405
1406 response_metadata_ = std::make_unique<ResponseMetadata>(std::move(head));
1407
1408 NotifyClient(std::move(request_info));
1409 }
1410
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr head)1411 void InterceptionJob::OnReceiveRedirect(
1412 const net::RedirectInfo& redirect_info,
1413 network::mojom::URLResponseHeadPtr head) {
1414 DCHECK_EQ(State::kRequestSent, state_);
1415 state_ = State::kRedirectReceived;
1416 response_metadata_ = std::make_unique<ResponseMetadata>(head.Clone());
1417 response_metadata_->redirect_info =
1418 std::make_unique<net::RedirectInfo>(redirect_info);
1419
1420 if (!(stage_ & InterceptionStage::RESPONSE)) {
1421 client_->OnReceiveRedirect(redirect_info, std::move(head));
1422 return;
1423 }
1424
1425 std::unique_ptr<InterceptedRequestInfo> request_info = BuildRequestInfo(head);
1426 request_info->redirect_url = redirect_info.new_url.spec();
1427 NotifyClient(std::move(request_info));
1428 }
1429
OnUploadProgress(int64_t current_position,int64_t total_size,OnUploadProgressCallback callback)1430 void InterceptionJob::OnUploadProgress(int64_t current_position,
1431 int64_t total_size,
1432 OnUploadProgressCallback callback) {
1433 if (!report_upload_)
1434 return;
1435 client_->OnUploadProgress(current_position, total_size, std::move(callback));
1436 }
1437
OnReceiveCachedMetadata(mojo_base::BigBuffer data)1438 void InterceptionJob::OnReceiveCachedMetadata(mojo_base::BigBuffer data) {
1439 if (ShouldBypassForResponse())
1440 client_->OnReceiveCachedMetadata(std::move(data));
1441 else
1442 response_metadata_->cached_metadata = std::move(data);
1443 }
1444
OnTransferSizeUpdated(int32_t transfer_size_diff)1445 void InterceptionJob::OnTransferSizeUpdated(int32_t transfer_size_diff) {
1446 if (ShouldBypassForResponse())
1447 client_->OnTransferSizeUpdated(transfer_size_diff);
1448 else
1449 response_metadata_->transfer_size += transfer_size_diff;
1450 }
1451
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body)1452 void InterceptionJob::OnStartLoadingResponseBody(
1453 mojo::ScopedDataPipeConsumerHandle body) {
1454 if (pending_response_body_pipe_callback_) {
1455 DCHECK_EQ(State::kResponseTaken, state_);
1456 DCHECK(!body_reader_);
1457 std::move(pending_response_body_pipe_callback_)
1458 .Run(Response::Success(), std::move(body),
1459 response_metadata_->head->mime_type);
1460 return;
1461 }
1462 DCHECK_EQ(State::kResponseReceived, state_);
1463 if (ShouldBypassForResponse())
1464 client_->OnStartLoadingResponseBody(std::move(body));
1465 else
1466 body_reader_->StartReading(std::move(body));
1467 }
1468
OnComplete(const network::URLLoaderCompletionStatus & status)1469 void InterceptionJob::OnComplete(
1470 const network::URLLoaderCompletionStatus& status) {
1471 // No need to listen to the channel any more, so just reset it, so if the pipe
1472 // is closed by the other end, |shutdown| isn't run.
1473 client_receiver_.reset();
1474 loader_.reset();
1475
1476 if (!response_metadata_) {
1477 // If we haven't seen response and get an error completion,
1478 // treat it as a response and intercept (provided response are
1479 // being intercepted).
1480 if (!(stage_ & InterceptionStage::RESPONSE) || !status.error_code) {
1481 CompleteRequest(status);
1482 return;
1483 }
1484 response_metadata_ = std::make_unique<ResponseMetadata>();
1485 response_metadata_->status = status;
1486 auto request_info = BuildRequestInfo(nullptr);
1487 request_info->response_error_code = status.error_code;
1488 NotifyClient(std::move(request_info));
1489 return;
1490 }
1491 // Since we're not forwarding OnComplete right now, make sure
1492 // we're in the proper state. The completion is due upon client response.
1493 DCHECK(state_ == State::kResponseReceived || state_ == State::kResponseTaken);
1494 DCHECK(waiting_for_resolution_);
1495
1496 response_metadata_->status = status;
1497 }
1498
OnAuthRequest(const net::AuthChallengeInfo & auth_info,DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback)1499 void InterceptionJob::OnAuthRequest(
1500 const net::AuthChallengeInfo& auth_info,
1501 DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback) {
1502 DCHECK_EQ(kRequestSent, state_);
1503 DCHECK(pending_auth_callback_.is_null());
1504 DCHECK(!waiting_for_resolution_);
1505
1506 if (!(stage_ & InterceptionStage::REQUEST) || !interceptor_ ||
1507 !interceptor_->handle_auth_) {
1508 std::move(callback).Run(true, base::nullopt);
1509 return;
1510 }
1511 state_ = State::kAuthRequired;
1512 auto request_info = BuildRequestInfo(nullptr);
1513 request_info->auth_challenge =
1514 std::make_unique<net::AuthChallengeInfo>(auth_info);
1515 pending_auth_callback_ = std::move(callback);
1516 NotifyClient(std::move(request_info));
1517 }
1518
DevToolsURLLoaderFactoryAdapter(mojo::PendingRemote<network::mojom::URLLoaderFactory> factory)1519 DevToolsURLLoaderFactoryAdapter::DevToolsURLLoaderFactoryAdapter(
1520 mojo::PendingRemote<network::mojom::URLLoaderFactory> factory)
1521 : factory_(std::move(factory)) {}
1522
1523 DevToolsURLLoaderFactoryAdapter::~DevToolsURLLoaderFactoryAdapter() = default;
1524
CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation)1525 void DevToolsURLLoaderFactoryAdapter::CreateLoaderAndStart(
1526 mojo::PendingReceiver<network::mojom::URLLoader> loader,
1527 int32_t routing_id,
1528 int32_t request_id,
1529 uint32_t options,
1530 const network::ResourceRequest& request,
1531 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
1532 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
1533 factory_->CreateLoaderAndStart(std::move(loader), routing_id, request_id,
1534 options, request, std::move(client),
1535 traffic_annotation);
1536 }
1537
Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)1538 void DevToolsURLLoaderFactoryAdapter::Clone(
1539 mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
1540 factory_->Clone(std::move(receiver));
1541 }
1542
1543 } // namespace content
1544