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