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