• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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