1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/url_request/url_request_job.h"
6
7 #include <utility>
8
9 #include "base/compiler_specific.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/location.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "base/values.h"
18 #include "net/base/auth.h"
19 #include "net/base/features.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/load_flags.h"
22 #include "net/base/load_states.h"
23 #include "net/base/net_errors.h"
24 #include "net/base/network_delegate.h"
25 #include "net/base/proxy_chain.h"
26 #include "net/base/schemeful_site.h"
27 #include "net/cert/x509_certificate.h"
28 #include "net/cookies/cookie_setting_override.h"
29 #include "net/cookies/cookie_util.h"
30 #include "net/log/net_log.h"
31 #include "net/log/net_log_capture_mode.h"
32 #include "net/log/net_log_event_type.h"
33 #include "net/log/net_log_with_source.h"
34 #include "net/nqe/network_quality_estimator.h"
35 #include "net/ssl/ssl_private_key.h"
36 #include "net/url_request/redirect_info.h"
37 #include "net/url_request/redirect_util.h"
38 #include "net/url_request/url_request_context.h"
39
40 namespace net {
41
42 namespace {
43
44 // Callback for TYPE_URL_REQUEST_FILTERS_SET net-internals event.
SourceStreamSetParams(SourceStream * source_stream)45 base::Value::Dict SourceStreamSetParams(SourceStream* source_stream) {
46 base::Value::Dict event_params;
47 event_params.Set("filters", source_stream->Description());
48 return event_params;
49 }
50
51 } // namespace
52
53 // Each SourceStreams own the previous SourceStream in the chain, but the
54 // ultimate source is URLRequestJob, which has other ownership semantics, so
55 // this class is a proxy for URLRequestJob that is owned by the first stream
56 // (in dataflow order).
57 class URLRequestJob::URLRequestJobSourceStream : public SourceStream {
58 public:
URLRequestJobSourceStream(URLRequestJob * job)59 explicit URLRequestJobSourceStream(URLRequestJob* job)
60 : SourceStream(SourceStream::TYPE_NONE), job_(job) {
61 DCHECK(job_);
62 }
63
64 URLRequestJobSourceStream(const URLRequestJobSourceStream&) = delete;
65 URLRequestJobSourceStream& operator=(const URLRequestJobSourceStream&) =
66 delete;
67
68 ~URLRequestJobSourceStream() override = default;
69
70 // SourceStream implementation:
Read(IOBuffer * dest_buffer,int buffer_size,CompletionOnceCallback callback)71 int Read(IOBuffer* dest_buffer,
72 int buffer_size,
73 CompletionOnceCallback callback) override {
74 DCHECK(job_);
75 return job_->ReadRawDataHelper(dest_buffer, buffer_size,
76 std::move(callback));
77 }
78
Description() const79 std::string Description() const override { return std::string(); }
80
MayHaveMoreBytes() const81 bool MayHaveMoreBytes() const override { return true; }
82
83 private:
84 // It is safe to keep a raw pointer because |job_| owns the last stream which
85 // indirectly owns |this|. Therefore, |job_| will not be destroyed when |this|
86 // is alive.
87 const raw_ptr<URLRequestJob> job_;
88 };
89
URLRequestJob(URLRequest * request)90 URLRequestJob::URLRequestJob(URLRequest* request) : request_(request) {}
91
92 URLRequestJob::~URLRequestJob() = default;
93
SetUpload(UploadDataStream * upload)94 void URLRequestJob::SetUpload(UploadDataStream* upload) {
95 }
96
SetExtraRequestHeaders(const HttpRequestHeaders & headers)97 void URLRequestJob::SetExtraRequestHeaders(const HttpRequestHeaders& headers) {
98 }
99
SetPriority(RequestPriority priority)100 void URLRequestJob::SetPriority(RequestPriority priority) {
101 }
102
Kill()103 void URLRequestJob::Kill() {
104 weak_factory_.InvalidateWeakPtrs();
105 // Make sure the URLRequest is notified that the job is done. This assumes
106 // that the URLRequest took care of setting its error status before calling
107 // Kill().
108 // TODO(mmenke): The URLRequest is currently deleted before this method
109 // invokes its async callback whenever this is called by the URLRequest.
110 // Try to simplify how cancellation works.
111 NotifyCanceled();
112 }
113
114 // This method passes reads down the filter chain, where they eventually end up
115 // at URLRequestJobSourceStream::Read, which calls back into
116 // URLRequestJob::ReadRawData.
Read(IOBuffer * buf,int buf_size)117 int URLRequestJob::Read(IOBuffer* buf, int buf_size) {
118 DCHECK(buf);
119
120 pending_read_buffer_ = buf;
121 int result = source_stream_->Read(
122 buf, buf_size,
123 base::BindOnce(&URLRequestJob::SourceStreamReadComplete,
124 weak_factory_.GetWeakPtr(), false));
125 if (result == ERR_IO_PENDING)
126 return ERR_IO_PENDING;
127
128 SourceStreamReadComplete(true, result);
129 return result;
130 }
131
GetTotalReceivedBytes() const132 int64_t URLRequestJob::GetTotalReceivedBytes() const {
133 return 0;
134 }
135
GetTotalSentBytes() const136 int64_t URLRequestJob::GetTotalSentBytes() const {
137 return 0;
138 }
139
GetReceivedBodyBytes() const140 int64_t URLRequestJob::GetReceivedBodyBytes() const {
141 return 0;
142 }
143
GetLoadState() const144 LoadState URLRequestJob::GetLoadState() const {
145 return LOAD_STATE_IDLE;
146 }
147
GetCharset(std::string * charset)148 bool URLRequestJob::GetCharset(std::string* charset) {
149 return false;
150 }
151
GetResponseInfo(HttpResponseInfo * info)152 void URLRequestJob::GetResponseInfo(HttpResponseInfo* info) {
153 }
154
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const155 void URLRequestJob::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
156 // Only certain request types return more than just request start times.
157 }
158
GetTransactionRemoteEndpoint(IPEndPoint * endpoint) const159 bool URLRequestJob::GetTransactionRemoteEndpoint(IPEndPoint* endpoint) const {
160 return false;
161 }
162
PopulateNetErrorDetails(NetErrorDetails * details) const163 void URLRequestJob::PopulateNetErrorDetails(NetErrorDetails* details) const {
164 return;
165 }
166
IsRedirectResponse(GURL * location,int * http_status_code,bool * insecure_scheme_was_upgraded)167 bool URLRequestJob::IsRedirectResponse(GURL* location,
168 int* http_status_code,
169 bool* insecure_scheme_was_upgraded) {
170 // For non-HTTP jobs, headers will be null.
171 HttpResponseHeaders* headers = request_->response_headers();
172 if (!headers)
173 return false;
174
175 std::string value;
176 if (!headers->IsRedirect(&value))
177 return false;
178 *insecure_scheme_was_upgraded = false;
179 *location = request_->url().Resolve(value);
180 // If this a redirect to HTTP of a request that had the
181 // 'upgrade-insecure-requests' policy set, upgrade it to HTTPS.
182 if (request_->upgrade_if_insecure()) {
183 if (location->SchemeIs("http")) {
184 *insecure_scheme_was_upgraded = true;
185 GURL::Replacements replacements;
186 replacements.SetSchemeStr("https");
187 *location = location->ReplaceComponents(replacements);
188 }
189 }
190 *http_status_code = headers->response_code();
191 return true;
192 }
193
CopyFragmentOnRedirect(const GURL & location) const194 bool URLRequestJob::CopyFragmentOnRedirect(const GURL& location) const {
195 return true;
196 }
197
IsSafeRedirect(const GURL & location)198 bool URLRequestJob::IsSafeRedirect(const GURL& location) {
199 return true;
200 }
201
NeedsAuth()202 bool URLRequestJob::NeedsAuth() {
203 return false;
204 }
205
GetAuthChallengeInfo()206 std::unique_ptr<AuthChallengeInfo> URLRequestJob::GetAuthChallengeInfo() {
207 // This will only be called if NeedsAuth() returns true, in which
208 // case the derived class should implement this!
209 NOTREACHED();
210 }
211
SetAuth(const AuthCredentials & credentials)212 void URLRequestJob::SetAuth(const AuthCredentials& credentials) {
213 // This will only be called if NeedsAuth() returns true, in which
214 // case the derived class should implement this!
215 NOTREACHED();
216 }
217
CancelAuth()218 void URLRequestJob::CancelAuth() {
219 // This will only be called if NeedsAuth() returns true, in which
220 // case the derived class should implement this!
221 NOTREACHED();
222 }
223
ContinueWithCertificate(scoped_refptr<X509Certificate> client_cert,scoped_refptr<SSLPrivateKey> client_private_key)224 void URLRequestJob::ContinueWithCertificate(
225 scoped_refptr<X509Certificate> client_cert,
226 scoped_refptr<SSLPrivateKey> client_private_key) {
227 // The derived class should implement this!
228 NOTREACHED();
229 }
230
ContinueDespiteLastError()231 void URLRequestJob::ContinueDespiteLastError() {
232 // Implementations should know how to recover from errors they generate.
233 // If this code was reached, we are trying to recover from an error that
234 // we don't know how to recover from.
235 NOTREACHED();
236 }
237
prefilter_bytes_read() const238 int64_t URLRequestJob::prefilter_bytes_read() const {
239 return prefilter_bytes_read_;
240 }
241
GetMimeType(std::string * mime_type) const242 bool URLRequestJob::GetMimeType(std::string* mime_type) const {
243 return false;
244 }
245
GetResponseCode() const246 int URLRequestJob::GetResponseCode() const {
247 HttpResponseHeaders* headers = request_->response_headers();
248 if (!headers)
249 return -1;
250 return headers->response_code();
251 }
252
GetResponseRemoteEndpoint() const253 IPEndPoint URLRequestJob::GetResponseRemoteEndpoint() const {
254 return IPEndPoint();
255 }
256
NotifyURLRequestDestroyed()257 void URLRequestJob::NotifyURLRequestDestroyed() {
258 }
259
GetConnectionAttempts() const260 ConnectionAttempts URLRequestJob::GetConnectionAttempts() const {
261 return {};
262 }
263
CloseConnectionOnDestruction()264 void URLRequestJob::CloseConnectionOnDestruction() {}
265
NeedsRetryWithStorageAccess()266 bool URLRequestJob::NeedsRetryWithStorageAccess() {
267 return false;
268 }
269
270 namespace {
271
272 // Assuming |url| has already been stripped for use as a referrer, if
273 // |should_strip_to_origin| is true, this method returns the output of the
274 // "Strip `url` for use as a referrer" algorithm from the Referrer Policy spec
275 // with its "origin-only" flag set to true:
276 // https://w3c.github.io/webappsec-referrer-policy/#strip-url
MaybeStripToOrigin(GURL url,bool should_strip_to_origin)277 GURL MaybeStripToOrigin(GURL url, bool should_strip_to_origin) {
278 if (!should_strip_to_origin)
279 return url;
280
281 return url.DeprecatedGetOriginAsURL();
282 }
283
284 } // namespace
285
286 // static
ComputeReferrerForPolicy(ReferrerPolicy policy,const GURL & original_referrer,const GURL & destination,bool * same_origin_out_for_metrics)287 GURL URLRequestJob::ComputeReferrerForPolicy(
288 ReferrerPolicy policy,
289 const GURL& original_referrer,
290 const GURL& destination,
291 bool* same_origin_out_for_metrics) {
292 // Here and below, numbered lines are from the Referrer Policy spec's
293 // "Determine request's referrer" algorithm:
294 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
295 //
296 // 4. Let referrerURL be the result of stripping referrerSource for use as a
297 // referrer.
298 GURL stripped_referrer = original_referrer.GetAsReferrer();
299
300 // 5. Let referrerOrigin be the result of stripping referrerSource for use as
301 // a referrer, with the origin-only flag set to true.
302 //
303 // (We use a boolean instead of computing the URL right away in order to avoid
304 // constructing a new GURL when it's not necessary.)
305 bool should_strip_to_origin = false;
306
307 // 6. If the result of serializing referrerURL is a string whose length is
308 // greater than 4096, set referrerURL to referrerOrigin.
309 if (stripped_referrer.spec().size() > 4096)
310 should_strip_to_origin = true;
311
312 bool same_origin = url::IsSameOriginWith(original_referrer, destination);
313
314 if (same_origin_out_for_metrics)
315 *same_origin_out_for_metrics = same_origin;
316
317 // 7. The user agent MAY alter referrerURL or referrerOrigin at this point to
318 // enforce arbitrary policy considerations in the interests of minimizing data
319 // leakage. For example, the user agent could strip the URL down to an origin,
320 // modify its host, replace it with an empty string, etc.
321 if (base::FeatureList::IsEnabled(
322 features::kCapReferrerToOriginOnCrossOrigin) &&
323 !same_origin) {
324 should_strip_to_origin = true;
325 }
326
327 bool secure_referrer_but_insecure_destination =
328 original_referrer.SchemeIsCryptographic() &&
329 !destination.SchemeIsCryptographic();
330
331 switch (policy) {
332 case ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
333 if (secure_referrer_but_insecure_destination)
334 return GURL();
335 return MaybeStripToOrigin(std::move(stripped_referrer),
336 should_strip_to_origin);
337
338 case ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
339 if (secure_referrer_but_insecure_destination)
340 return GURL();
341 if (!same_origin)
342 should_strip_to_origin = true;
343 return MaybeStripToOrigin(std::move(stripped_referrer),
344 should_strip_to_origin);
345
346 case ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
347 if (!same_origin)
348 should_strip_to_origin = true;
349 return MaybeStripToOrigin(std::move(stripped_referrer),
350 should_strip_to_origin);
351
352 case ReferrerPolicy::NEVER_CLEAR:
353 return MaybeStripToOrigin(std::move(stripped_referrer),
354 should_strip_to_origin);
355
356 case ReferrerPolicy::ORIGIN:
357 should_strip_to_origin = true;
358 return MaybeStripToOrigin(std::move(stripped_referrer),
359 should_strip_to_origin);
360
361 case ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN:
362 if (!same_origin)
363 return GURL();
364 return MaybeStripToOrigin(std::move(stripped_referrer),
365 should_strip_to_origin);
366
367 case ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
368 if (secure_referrer_but_insecure_destination)
369 return GURL();
370 should_strip_to_origin = true;
371 return MaybeStripToOrigin(std::move(stripped_referrer),
372 should_strip_to_origin);
373
374 case ReferrerPolicy::NO_REFERRER:
375 return GURL();
376 }
377
378 NOTREACHED();
379 }
380
NotifyConnected(const TransportInfo & info,CompletionOnceCallback callback)381 int URLRequestJob::NotifyConnected(const TransportInfo& info,
382 CompletionOnceCallback callback) {
383 return request_->NotifyConnected(info, std::move(callback));
384 }
385
NotifyCertificateRequested(SSLCertRequestInfo * cert_request_info)386 void URLRequestJob::NotifyCertificateRequested(
387 SSLCertRequestInfo* cert_request_info) {
388 request_->NotifyCertificateRequested(cert_request_info);
389 }
390
NotifySSLCertificateError(int net_error,const SSLInfo & ssl_info,bool fatal)391 void URLRequestJob::NotifySSLCertificateError(int net_error,
392 const SSLInfo& ssl_info,
393 bool fatal) {
394 request_->NotifySSLCertificateError(net_error, ssl_info, fatal);
395 }
396
CanSetCookie(const net::CanonicalCookie & cookie,CookieOptions * options,const net::FirstPartySetMetadata & first_party_set_metadata,CookieInclusionStatus * inclusion_status) const397 bool URLRequestJob::CanSetCookie(
398 const net::CanonicalCookie& cookie,
399 CookieOptions* options,
400 const net::FirstPartySetMetadata& first_party_set_metadata,
401 CookieInclusionStatus* inclusion_status) const {
402 return request_->CanSetCookie(cookie, options, first_party_set_metadata,
403 inclusion_status);
404 }
405
NotifyHeadersComplete()406 void URLRequestJob::NotifyHeadersComplete() {
407 if (has_handled_response_)
408 return;
409
410 // Initialize to the current time, and let the subclass optionally override
411 // the time stamps if it has that information. The default request_time is
412 // set by URLRequest before it calls our Start method.
413 request_->response_info_.response_time =
414 request_->response_info_.original_response_time = base::Time::Now();
415 GetResponseInfo(&request_->response_info_);
416
417 request_->OnHeadersComplete();
418
419 GURL new_location;
420 int http_status_code;
421 bool insecure_scheme_was_upgraded;
422
423 if (NeedsAuth()) {
424 CHECK(!IsRedirectResponse(&new_location, &http_status_code,
425 &insecure_scheme_was_upgraded));
426 std::unique_ptr<AuthChallengeInfo> auth_info = GetAuthChallengeInfo();
427 // Need to check for a NULL auth_info because the server may have failed
428 // to send a challenge with the 401 response.
429 if (auth_info) {
430 request_->NotifyAuthRequired(std::move(auth_info));
431 // Wait for SetAuth or CancelAuth to be called.
432 return;
433 }
434 }
435
436 if (NeedsRetryWithStorageAccess()) {
437 DoneReadingRetryResponse();
438 request_->RetryWithStorageAccess();
439 return;
440 }
441
442 if (IsRedirectResponse(&new_location, &http_status_code,
443 &insecure_scheme_was_upgraded)) {
444 CHECK(!NeedsAuth());
445 // Redirect response bodies are not read. Notify the transaction
446 // so it does not treat being stopped as an error.
447 DoneReadingRedirectResponse();
448
449 // Invalid redirect targets are failed early. This means the delegate can
450 // assume that, if it accepts the redirect, future calls to
451 // OnResponseStarted correspond to |redirect_info.new_url|.
452 int redirect_check_result = CanFollowRedirect(new_location);
453 if (redirect_check_result != OK) {
454 OnDone(redirect_check_result, true /* notify_done */);
455 return;
456 }
457
458 RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo(
459 request_->method(), request_->url(), request_->site_for_cookies(),
460 request_->first_party_url_policy(), request_->referrer_policy(),
461 request_->referrer(), http_status_code, new_location,
462 net::RedirectUtil::GetReferrerPolicyHeader(
463 request_->response_headers()),
464 insecure_scheme_was_upgraded, CopyFragmentOnRedirect(new_location));
465 request_->ReceivedRedirect(redirect_info);
466 // |this| may be destroyed at this point.
467 return;
468 }
469
470 NotifyFinalHeadersReceived();
471 // |this| may be destroyed at this point.
472 }
473
NotifyFinalHeadersReceived()474 void URLRequestJob::NotifyFinalHeadersReceived() {
475 DCHECK(!NeedsAuth() || !GetAuthChallengeInfo());
476
477 if (has_handled_response_)
478 return;
479
480 // While the request's status is normally updated in NotifyHeadersComplete(),
481 // URLRequestHttpJob::CancelAuth() posts a task to invoke this method
482 // directly, which bypasses that logic.
483 if (request_->status() == ERR_IO_PENDING)
484 request_->set_status(OK);
485
486 has_handled_response_ = true;
487 if (request_->status() == OK) {
488 DCHECK(!source_stream_);
489 source_stream_ = SetUpSourceStream();
490
491 if (!source_stream_) {
492 OnDone(ERR_CONTENT_DECODING_INIT_FAILED, true /* notify_done */);
493 return;
494 }
495 if (source_stream_->type() == SourceStream::TYPE_NONE) {
496 // If the subclass didn't set |expected_content_size|, and there are
497 // headers, and the response body is not compressed, try to get the
498 // expected content size from the headers.
499 if (expected_content_size_ == -1 && request_->response_headers()) {
500 // This sets |expected_content_size_| to its previous value of -1 if
501 // there's no Content-Length header.
502 expected_content_size_ =
503 request_->response_headers()->GetContentLength();
504 }
505 } else {
506 request_->net_log().AddEvent(
507 NetLogEventType::URL_REQUEST_FILTERS_SET,
508 [&] { return SourceStreamSetParams(source_stream_.get()); });
509 }
510 }
511
512 request_->NotifyResponseStarted(OK);
513 // |this| may be destroyed at this point.
514 }
515
ConvertResultToError(int result,Error * error,int * count)516 void URLRequestJob::ConvertResultToError(int result, Error* error, int* count) {
517 if (result >= 0) {
518 *error = OK;
519 *count = result;
520 } else {
521 *error = static_cast<Error>(result);
522 *count = 0;
523 }
524 }
525
ReadRawDataComplete(int result)526 void URLRequestJob::ReadRawDataComplete(int result) {
527 DCHECK_EQ(ERR_IO_PENDING, request_->status());
528 DCHECK_NE(ERR_IO_PENDING, result);
529
530 // The headers should be complete before reads complete
531 DCHECK(has_handled_response_);
532
533 GatherRawReadStats(result);
534
535 // Notify SourceStream.
536 DCHECK(!read_raw_callback_.is_null());
537
538 std::move(read_raw_callback_).Run(result);
539 // |this| may be destroyed at this point.
540 }
541
NotifyStartError(int net_error)542 void URLRequestJob::NotifyStartError(int net_error) {
543 DCHECK(!has_handled_response_);
544 DCHECK_EQ(ERR_IO_PENDING, request_->status());
545
546 has_handled_response_ = true;
547 // There may be relevant information in the response info even in the
548 // error case.
549 GetResponseInfo(&request_->response_info_);
550
551 request_->NotifyResponseStarted(net_error);
552 // |this| may have been deleted here.
553 }
554
OnDone(int net_error,bool notify_done)555 void URLRequestJob::OnDone(int net_error, bool notify_done) {
556 DCHECK_NE(ERR_IO_PENDING, net_error);
557 DCHECK(!done_) << "Job sending done notification twice";
558 if (done_)
559 return;
560 done_ = true;
561
562 // Unless there was an error, we should have at least tried to handle
563 // the response before getting here.
564 DCHECK(has_handled_response_ || net_error != OK);
565
566 request_->set_is_pending(false);
567 // With async IO, it's quite possible to have a few outstanding
568 // requests. We could receive a request to Cancel, followed shortly
569 // by a successful IO. For tracking the status(), once there is
570 // an error, we do not change the status back to success. To
571 // enforce this, only set the status if the job is so far
572 // successful.
573 if (!request_->failed()) {
574 if (net_error != net::OK && net_error != ERR_ABORTED) {
575 request_->net_log().AddEventWithNetErrorCode(NetLogEventType::FAILED,
576 net_error);
577 }
578 request_->set_status(net_error);
579 }
580
581 if (notify_done) {
582 // Complete this notification later. This prevents us from re-entering the
583 // delegate if we're done because of a synchronous call.
584 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
585 FROM_HERE,
586 base::BindOnce(&URLRequestJob::NotifyDone, weak_factory_.GetWeakPtr()));
587 }
588 }
589
NotifyDone()590 void URLRequestJob::NotifyDone() {
591 // Check if we should notify the URLRequest that we're done because of an
592 // error.
593 if (request_->failed()) {
594 // We report the error differently depending on whether we've called
595 // OnResponseStarted yet.
596 if (has_handled_response_) {
597 // We signal the error by calling OnReadComplete with a bytes_read of -1.
598 request_->NotifyReadCompleted(-1);
599 } else {
600 has_handled_response_ = true;
601 // Error code doesn't actually matter here, since the status has already
602 // been updated.
603 request_->NotifyResponseStarted(request_->status());
604 }
605 }
606 }
607
NotifyCanceled()608 void URLRequestJob::NotifyCanceled() {
609 if (!done_)
610 OnDone(ERR_ABORTED, true /* notify_done */);
611 }
612
OnCallToDelegate(NetLogEventType type)613 void URLRequestJob::OnCallToDelegate(NetLogEventType type) {
614 request_->OnCallToDelegate(type);
615 }
616
OnCallToDelegateComplete()617 void URLRequestJob::OnCallToDelegateComplete() {
618 request_->OnCallToDelegateComplete();
619 }
620
ReadRawData(IOBuffer * buf,int buf_size)621 int URLRequestJob::ReadRawData(IOBuffer* buf, int buf_size) {
622 return 0;
623 }
624
DoneReading()625 void URLRequestJob::DoneReading() {
626 // Do nothing.
627 }
628
DoneReadingRedirectResponse()629 void URLRequestJob::DoneReadingRedirectResponse() {
630 }
631
DoneReadingRetryResponse()632 void URLRequestJob::DoneReadingRetryResponse() {}
633
SetUpSourceStream()634 std::unique_ptr<SourceStream> URLRequestJob::SetUpSourceStream() {
635 return std::make_unique<URLRequestJobSourceStream>(this);
636 }
637
SetProxyChain(const ProxyChain & proxy_chain)638 void URLRequestJob::SetProxyChain(const ProxyChain& proxy_chain) {
639 request_->proxy_chain_ = proxy_chain;
640 }
641
SourceStreamReadComplete(bool synchronous,int result)642 void URLRequestJob::SourceStreamReadComplete(bool synchronous, int result) {
643 DCHECK_NE(ERR_IO_PENDING, result);
644
645 if (result > 0 && request()->net_log().IsCapturing()) {
646 request()->net_log().AddByteTransferEvent(
647 NetLogEventType::URL_REQUEST_JOB_FILTERED_BYTES_READ, result,
648 pending_read_buffer_->data());
649 }
650 pending_read_buffer_ = nullptr;
651
652 if (result < 0) {
653 OnDone(result, !synchronous /* notify_done */);
654 return;
655 }
656
657 if (result > 0) {
658 postfilter_bytes_read_ += result;
659 } else {
660 DCHECK_EQ(0, result);
661 DoneReading();
662 // In the synchronous case, the caller will notify the URLRequest of
663 // completion. In the async case, the NotifyReadCompleted call will.
664 // TODO(mmenke): Can this be combined with the error case?
665 OnDone(OK, false /* notify_done */);
666 }
667
668 if (!synchronous)
669 request_->NotifyReadCompleted(result);
670 }
671
ReadRawDataHelper(IOBuffer * buf,int buf_size,CompletionOnceCallback callback)672 int URLRequestJob::ReadRawDataHelper(IOBuffer* buf,
673 int buf_size,
674 CompletionOnceCallback callback) {
675 DCHECK(!raw_read_buffer_);
676
677 // Keep a pointer to the read buffer, so URLRequestJob::GatherRawReadStats()
678 // has access to it to log stats.
679 raw_read_buffer_ = buf;
680
681 // TODO(xunjieli): Make ReadRawData take in a callback rather than requiring
682 // subclass to call ReadRawDataComplete upon asynchronous completion.
683 int result = ReadRawData(buf, buf_size);
684
685 if (result != ERR_IO_PENDING) {
686 // If the read completes synchronously, either success or failure, invoke
687 // GatherRawReadStats so we can account for the completed read.
688 GatherRawReadStats(result);
689 } else {
690 read_raw_callback_ = std::move(callback);
691 }
692 return result;
693 }
694
CanFollowRedirect(const GURL & new_url)695 int URLRequestJob::CanFollowRedirect(const GURL& new_url) {
696 if (request_->redirect_limit_ <= 0) {
697 DVLOG(1) << "disallowing redirect: exceeds limit";
698 return ERR_TOO_MANY_REDIRECTS;
699 }
700
701 if (!new_url.is_valid())
702 return ERR_INVALID_REDIRECT;
703
704 if (!IsSafeRedirect(new_url)) {
705 DVLOG(1) << "disallowing redirect: unsafe protocol";
706 return ERR_UNSAFE_REDIRECT;
707 }
708
709 return OK;
710 }
711
GatherRawReadStats(int bytes_read)712 void URLRequestJob::GatherRawReadStats(int bytes_read) {
713 DCHECK(raw_read_buffer_ || bytes_read == 0);
714 DCHECK_NE(ERR_IO_PENDING, bytes_read);
715
716 if (bytes_read > 0) {
717 // If there is a filter, bytes will be logged after the filter is applied.
718 if (source_stream_->type() != SourceStream::TYPE_NONE &&
719 request()->net_log().IsCapturing()) {
720 request()->net_log().AddByteTransferEvent(
721 NetLogEventType::URL_REQUEST_JOB_BYTES_READ, bytes_read,
722 raw_read_buffer_->data());
723 }
724 RecordBytesRead(bytes_read);
725 }
726 raw_read_buffer_ = nullptr;
727 }
728
RecordBytesRead(int bytes_read)729 void URLRequestJob::RecordBytesRead(int bytes_read) {
730 DCHECK_GT(bytes_read, 0);
731 prefilter_bytes_read_ += base::checked_cast<size_t>(bytes_read);
732
733 // On first read, notify NetworkQualityEstimator that response headers have
734 // been received.
735 // TODO(tbansal): Move this to url_request_http_job.cc. This may catch
736 // Service Worker jobs twice.
737 // If prefilter_bytes_read_ is equal to bytes_read, it indicates this is the
738 // first raw read of the response body. This is used as the signal that
739 // response headers have been received.
740 if (request_->context()->network_quality_estimator()) {
741 if (prefilter_bytes_read() == bytes_read) {
742 request_->context()->network_quality_estimator()->NotifyHeadersReceived(
743 *request_, prefilter_bytes_read());
744 } else {
745 request_->context()->network_quality_estimator()->NotifyBytesRead(
746 *request_, prefilter_bytes_read());
747 }
748 }
749
750 DVLOG(2) << __FUNCTION__ << "() "
751 << "\"" << request_->url().spec() << "\""
752 << " pre bytes read = " << bytes_read
753 << " pre total = " << prefilter_bytes_read()
754 << " post total = " << postfilter_bytes_read();
755 }
756
757 } // namespace net
758