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_server.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 ? absl::make_optional(net::SchemefulSite(
92 request->initiator().value()))
93 : absl::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 absl::optional<std::vector<std::string>> & removed_headers,const absl::optional<net::HttpRequestHeaders> & modified_headers)238 void URLRequestJob::FollowDeferredRedirect(
239 const absl::optional<std::vector<std::string>>& removed_headers,
240 const absl::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 absl::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) const407 bool URLRequestJob::CanSetCookie(const net::CanonicalCookie& cookie,
408 CookieOptions* options) const {
409 return request_->CanSetCookie(cookie, options);
410 }
411
NotifyHeadersComplete()412 void URLRequestJob::NotifyHeadersComplete() {
413 if (has_handled_response_)
414 return;
415
416 // Initialize to the current time, and let the subclass optionally override
417 // the time stamps if it has that information. The default request_time is
418 // set by URLRequest before it calls our Start method.
419 request_->response_info_.response_time = base::Time::Now();
420 GetResponseInfo(&request_->response_info_);
421
422 request_->OnHeadersComplete();
423
424 GURL new_location;
425 int http_status_code;
426 bool insecure_scheme_was_upgraded;
427
428 if (IsRedirectResponse(&new_location, &http_status_code,
429 &insecure_scheme_was_upgraded)) {
430 // Redirect response bodies are not read. Notify the transaction
431 // so it does not treat being stopped as an error.
432 DoneReadingRedirectResponse();
433
434 // Invalid redirect targets are failed early before
435 // NotifyReceivedRedirect. This means the delegate can assume that, if it
436 // accepts the redirect, future calls to OnResponseStarted correspond to
437 // |redirect_info.new_url|.
438 int redirect_check_result = CanFollowRedirect(new_location);
439 if (redirect_check_result != OK) {
440 OnDone(redirect_check_result, true /* notify_done */);
441 return;
442 }
443
444 // When notifying the URLRequest::Delegate, it can destroy the request,
445 // which will destroy |this|. After calling to the URLRequest::Delegate,
446 // pointer must be checked to see if |this| still exists, and if not, the
447 // code must return immediately.
448 base::WeakPtr<URLRequestJob> weak_this(weak_factory_.GetWeakPtr());
449
450 RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo(
451 request_->method(), request_->url(), request_->site_for_cookies(),
452 request_->first_party_url_policy(), request_->referrer_policy(),
453 request_->referrer(), http_status_code, new_location,
454 net::RedirectUtil::GetReferrerPolicyHeader(
455 request_->response_headers()),
456 insecure_scheme_was_upgraded, CopyFragmentOnRedirect(new_location));
457 bool defer_redirect = false;
458 request_->NotifyReceivedRedirect(redirect_info, &defer_redirect);
459
460 // Ensure that the request wasn't detached, destroyed, or canceled in
461 // NotifyReceivedRedirect.
462 if (!weak_this || request_->failed())
463 return;
464
465 if (defer_redirect) {
466 deferred_redirect_info_ = std::move(redirect_info);
467 } else {
468 FollowRedirect(redirect_info, absl::nullopt, /* removed_headers */
469 absl::nullopt /* modified_headers */);
470 }
471 return;
472 }
473
474 if (NeedsAuth()) {
475 std::unique_ptr<AuthChallengeInfo> auth_info = GetAuthChallengeInfo();
476 // Need to check for a NULL auth_info because the server may have failed
477 // to send a challenge with the 401 response.
478 if (auth_info) {
479 request_->NotifyAuthRequired(std::move(auth_info));
480 // Wait for SetAuth or CancelAuth to be called.
481 return;
482 }
483 }
484
485 NotifyFinalHeadersReceived();
486 // |this| may be destroyed at this point.
487 }
488
NotifyFinalHeadersReceived()489 void URLRequestJob::NotifyFinalHeadersReceived() {
490 DCHECK(!NeedsAuth() || !GetAuthChallengeInfo());
491
492 if (has_handled_response_)
493 return;
494
495 // While the request's status is normally updated in NotifyHeadersComplete(),
496 // URLRequestHttpJob::CancelAuth() posts a task to invoke this method
497 // directly, which bypasses that logic.
498 if (request_->status() == ERR_IO_PENDING)
499 request_->set_status(OK);
500
501 has_handled_response_ = true;
502 if (request_->status() == OK) {
503 DCHECK(!source_stream_);
504 source_stream_ = SetUpSourceStream();
505
506 if (!source_stream_) {
507 OnDone(ERR_CONTENT_DECODING_INIT_FAILED, true /* notify_done */);
508 return;
509 }
510 if (source_stream_->type() == SourceStream::TYPE_NONE) {
511 // If the subclass didn't set |expected_content_size|, and there are
512 // headers, and the response body is not compressed, try to get the
513 // expected content size from the headers.
514 if (expected_content_size_ == -1 && request_->response_headers()) {
515 // This sets |expected_content_size_| to its previous value of -1 if
516 // there's no Content-Length header.
517 expected_content_size_ =
518 request_->response_headers()->GetContentLength();
519 }
520 } else {
521 request_->net_log().AddEvent(
522 NetLogEventType::URL_REQUEST_FILTERS_SET,
523 [&] { return SourceStreamSetParams(source_stream_.get()); });
524 }
525 }
526
527 request_->NotifyResponseStarted(OK);
528 // |this| may be destroyed at this point.
529 }
530
ConvertResultToError(int result,Error * error,int * count)531 void URLRequestJob::ConvertResultToError(int result, Error* error, int* count) {
532 if (result >= 0) {
533 *error = OK;
534 *count = result;
535 } else {
536 *error = static_cast<Error>(result);
537 *count = 0;
538 }
539 }
540
ReadRawDataComplete(int result)541 void URLRequestJob::ReadRawDataComplete(int result) {
542 DCHECK_EQ(ERR_IO_PENDING, request_->status());
543 DCHECK_NE(ERR_IO_PENDING, result);
544
545 // The headers should be complete before reads complete
546 DCHECK(has_handled_response_);
547
548 GatherRawReadStats(result);
549
550 // Notify SourceStream.
551 DCHECK(!read_raw_callback_.is_null());
552
553 std::move(read_raw_callback_).Run(result);
554 // |this| may be destroyed at this point.
555 }
556
NotifyStartError(int net_error)557 void URLRequestJob::NotifyStartError(int net_error) {
558 DCHECK(!has_handled_response_);
559 DCHECK_EQ(ERR_IO_PENDING, request_->status());
560
561 has_handled_response_ = true;
562 // There may be relevant information in the response info even in the
563 // error case.
564 GetResponseInfo(&request_->response_info_);
565
566 request_->NotifyResponseStarted(net_error);
567 // |this| may have been deleted here.
568 }
569
OnDone(int net_error,bool notify_done)570 void URLRequestJob::OnDone(int net_error, bool notify_done) {
571 DCHECK_NE(ERR_IO_PENDING, net_error);
572 DCHECK(!done_) << "Job sending done notification twice";
573 if (done_)
574 return;
575 done_ = true;
576
577 // Unless there was an error, we should have at least tried to handle
578 // the response before getting here.
579 DCHECK(has_handled_response_ || net_error != OK);
580
581 request_->set_is_pending(false);
582 // With async IO, it's quite possible to have a few outstanding
583 // requests. We could receive a request to Cancel, followed shortly
584 // by a successful IO. For tracking the status(), once there is
585 // an error, we do not change the status back to success. To
586 // enforce this, only set the status if the job is so far
587 // successful.
588 if (!request_->failed()) {
589 if (net_error != net::OK && net_error != ERR_ABORTED) {
590 request_->net_log().AddEventWithNetErrorCode(NetLogEventType::FAILED,
591 net_error);
592 }
593 request_->set_status(net_error);
594 }
595
596 if (notify_done) {
597 // Complete this notification later. This prevents us from re-entering the
598 // delegate if we're done because of a synchronous call.
599 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
600 FROM_HERE,
601 base::BindOnce(&URLRequestJob::NotifyDone, weak_factory_.GetWeakPtr()));
602 }
603 }
604
NotifyDone()605 void URLRequestJob::NotifyDone() {
606 // Check if we should notify the URLRequest that we're done because of an
607 // error.
608 if (request_->failed()) {
609 // We report the error differently depending on whether we've called
610 // OnResponseStarted yet.
611 if (has_handled_response_) {
612 // We signal the error by calling OnReadComplete with a bytes_read of -1.
613 request_->NotifyReadCompleted(-1);
614 } else {
615 has_handled_response_ = true;
616 // Error code doesn't actually matter here, since the status has already
617 // been updated.
618 request_->NotifyResponseStarted(request_->status());
619 }
620 }
621 }
622
NotifyCanceled()623 void URLRequestJob::NotifyCanceled() {
624 if (!done_)
625 OnDone(ERR_ABORTED, true /* notify_done */);
626 }
627
OnCallToDelegate(NetLogEventType type)628 void URLRequestJob::OnCallToDelegate(NetLogEventType type) {
629 request_->OnCallToDelegate(type);
630 }
631
OnCallToDelegateComplete()632 void URLRequestJob::OnCallToDelegateComplete() {
633 request_->OnCallToDelegateComplete();
634 }
635
ReadRawData(IOBuffer * buf,int buf_size)636 int URLRequestJob::ReadRawData(IOBuffer* buf, int buf_size) {
637 return 0;
638 }
639
DoneReading()640 void URLRequestJob::DoneReading() {
641 // Do nothing.
642 }
643
DoneReadingRedirectResponse()644 void URLRequestJob::DoneReadingRedirectResponse() {
645 }
646
SetUpSourceStream()647 std::unique_ptr<SourceStream> URLRequestJob::SetUpSourceStream() {
648 return std::make_unique<URLRequestJobSourceStream>(this);
649 }
650
SetProxyServer(const ProxyServer & proxy_server)651 void URLRequestJob::SetProxyServer(const ProxyServer& proxy_server) {
652 request_->proxy_server_ = proxy_server;
653 }
654
SourceStreamReadComplete(bool synchronous,int result)655 void URLRequestJob::SourceStreamReadComplete(bool synchronous, int result) {
656 DCHECK_NE(ERR_IO_PENDING, result);
657
658 if (result > 0 && request()->net_log().IsCapturing()) {
659 request()->net_log().AddByteTransferEvent(
660 NetLogEventType::URL_REQUEST_JOB_FILTERED_BYTES_READ, result,
661 pending_read_buffer_->data());
662 }
663 pending_read_buffer_ = nullptr;
664
665 if (result < 0) {
666 OnDone(result, !synchronous /* notify_done */);
667 return;
668 }
669
670 if (result > 0) {
671 postfilter_bytes_read_ += result;
672 } else {
673 DCHECK_EQ(0, result);
674 DoneReading();
675 // In the synchronous case, the caller will notify the URLRequest of
676 // completion. In the async case, the NotifyReadCompleted call will.
677 // TODO(mmenke): Can this be combined with the error case?
678 OnDone(OK, false /* notify_done */);
679 }
680
681 if (!synchronous)
682 request_->NotifyReadCompleted(result);
683 }
684
ReadRawDataHelper(IOBuffer * buf,int buf_size,CompletionOnceCallback callback)685 int URLRequestJob::ReadRawDataHelper(IOBuffer* buf,
686 int buf_size,
687 CompletionOnceCallback callback) {
688 DCHECK(!raw_read_buffer_);
689
690 // Keep a pointer to the read buffer, so URLRequestJob::GatherRawReadStats()
691 // has access to it to log stats.
692 raw_read_buffer_ = buf;
693
694 // TODO(xunjieli): Make ReadRawData take in a callback rather than requiring
695 // subclass to call ReadRawDataComplete upon asynchronous completion.
696 int result = ReadRawData(buf, buf_size);
697
698 if (result != ERR_IO_PENDING) {
699 // If the read completes synchronously, either success or failure, invoke
700 // GatherRawReadStats so we can account for the completed read.
701 GatherRawReadStats(result);
702 } else {
703 read_raw_callback_ = std::move(callback);
704 }
705 return result;
706 }
707
CanFollowRedirect(const GURL & new_url)708 int URLRequestJob::CanFollowRedirect(const GURL& new_url) {
709 if (request_->redirect_limit_ <= 0) {
710 DVLOG(1) << "disallowing redirect: exceeds limit";
711 return ERR_TOO_MANY_REDIRECTS;
712 }
713
714 if (!new_url.is_valid())
715 return ERR_INVALID_REDIRECT;
716
717 if (!IsSafeRedirect(new_url)) {
718 DVLOG(1) << "disallowing redirect: unsafe protocol";
719 return ERR_UNSAFE_REDIRECT;
720 }
721
722 return OK;
723 }
724
FollowRedirect(const RedirectInfo & redirect_info,const absl::optional<std::vector<std::string>> & removed_headers,const absl::optional<net::HttpRequestHeaders> & modified_headers)725 void URLRequestJob::FollowRedirect(
726 const RedirectInfo& redirect_info,
727 const absl::optional<std::vector<std::string>>& removed_headers,
728 const absl::optional<net::HttpRequestHeaders>& modified_headers) {
729 request_->Redirect(redirect_info, removed_headers, modified_headers);
730 }
731
GatherRawReadStats(int bytes_read)732 void URLRequestJob::GatherRawReadStats(int bytes_read) {
733 DCHECK(raw_read_buffer_ || bytes_read == 0);
734 DCHECK_NE(ERR_IO_PENDING, bytes_read);
735
736 if (bytes_read > 0) {
737 // If there is a filter, bytes will be logged after the filter is applied.
738 if (source_stream_->type() != SourceStream::TYPE_NONE &&
739 request()->net_log().IsCapturing()) {
740 request()->net_log().AddByteTransferEvent(
741 NetLogEventType::URL_REQUEST_JOB_BYTES_READ, bytes_read,
742 raw_read_buffer_->data());
743 }
744 RecordBytesRead(bytes_read);
745 }
746 raw_read_buffer_ = nullptr;
747 }
748
RecordBytesRead(int bytes_read)749 void URLRequestJob::RecordBytesRead(int bytes_read) {
750 DCHECK_GT(bytes_read, 0);
751 prefilter_bytes_read_ += base::checked_cast<size_t>(bytes_read);
752
753 // On first read, notify NetworkQualityEstimator that response headers have
754 // been received.
755 // TODO(tbansal): Move this to url_request_http_job.cc. This may catch
756 // Service Worker jobs twice.
757 // If prefilter_bytes_read_ is equal to bytes_read, it indicates this is the
758 // first raw read of the response body. This is used as the signal that
759 // response headers have been received.
760 if (request_->context()->network_quality_estimator()) {
761 if (prefilter_bytes_read() == bytes_read) {
762 request_->context()->network_quality_estimator()->NotifyHeadersReceived(
763 *request_, prefilter_bytes_read());
764 } else {
765 request_->context()->network_quality_estimator()->NotifyBytesRead(
766 *request_, prefilter_bytes_read());
767 }
768 }
769
770 DVLOG(2) << __FUNCTION__ << "() "
771 << "\"" << request_->url().spec() << "\""
772 << " pre bytes read = " << bytes_read
773 << " pre total = " << prefilter_bytes_read()
774 << " post total = " << postfilter_bytes_read();
775 }
776
777 } // namespace net
778