Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Unified Diff: content/browser/appcache/appcache_update_url_fetcher.cc

Issue 2978373002: Move the URLFetcher class used by the AppCacheUpdateJob class into its own file. (Closed)
Patch Set: Move forward declaration for URLFetcher outside the AppCacheUpdateJob class to fix compile failures. Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/appcache/appcache_update_url_fetcher.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/appcache/appcache_update_url_fetcher.cc
diff --git a/content/browser/appcache/appcache_update_url_fetcher.cc b/content/browser/appcache/appcache_update_url_fetcher.cc
new file mode 100644
index 0000000000000000000000000000000000000000..018ffc6eb08d9d8f6488daead39ece7c7df82383
--- /dev/null
+++ b/content/browser/appcache/appcache_update_url_fetcher.cc
@@ -0,0 +1,305 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/appcache/appcache_update_url_fetcher.h"
+
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/url_request_context.h"
+
+namespace content {
+
+namespace {
+
+const int kMax503Retries = 3;
+
+constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+ net::DefineNetworkTrafficAnnotation("appcache_update_job", R"(
+ semantics {
+ sender: "HTML5 AppCache System"
+ description:
+ "Web pages can include a link to a manifest file which lists "
+ "resources to be cached for offline access. The AppCache system"
+ "retrieves those resources in the background."
+ trigger:
+ "User visits a web page containing a <html manifest=manifestUrl> "
+ "tag, or navigates to a document retrieved from an existing appcache "
+ "and some resource should be updated."
+ data: "None"
+ destination: WEBSITE
+ }
+ policy {
+ cookies_allowed: true
+ cookies_store: "user"
+ setting:
+ "Users can control this feature via the 'Cookies' setting under "
+ "'Privacy, Content settings'. If cookies are disabled for a single "
+ "site, appcaches are disabled for the site only. If they are totally "
+ "disabled, all appcache requests will be stopped."
+ chrome_policy {
+ DefaultCookiesSetting {
+ policy_options {mode: MANDATORY}
+ DefaultCookiesSetting: 2
+ }
+ }
+ })");
+
+} // namespace
+
+// Helper class to fetch resources. Depending on the fetch type,
+// can either fetch to an in-memory string or write the response
+// data out to the disk cache.
+URLFetcher::URLFetcher(const GURL& url,
+ FetchType fetch_type,
+ AppCacheUpdateJob* job,
+ int buffer_size)
+ : url_(url),
+ job_(job),
+ fetch_type_(fetch_type),
+ retry_503_attempts_(0),
+ buffer_(new net::IOBuffer(buffer_size)),
+ request_(
+ job->service_->request_context()->CreateRequest(url,
+ net::DEFAULT_PRIORITY,
+ this,
+ kTrafficAnnotation)),
+ result_(AppCacheUpdateJob::UPDATE_OK),
+ redirect_response_code_(-1),
+ buffer_size_(buffer_size) {}
+
+URLFetcher::~URLFetcher() {
+ // To defend against URLRequest calling delegate methods during
+ // destruction, we test for a !request_ in those methods.
+ std::unique_ptr<net::URLRequest> temp = std::move(request_);
+}
+
+void URLFetcher::Start() {
+ request_->set_first_party_for_cookies(job_->manifest_url_);
+ request_->set_initiator(url::Origin(job_->manifest_url_));
+ if (fetch_type_ == MANIFEST_FETCH && job_->doing_full_update_check_)
+ request_->SetLoadFlags(request_->load_flags() | net::LOAD_BYPASS_CACHE);
+ else if (existing_response_headers_.get())
+ AddConditionalHeaders(existing_response_headers_.get());
+ request_->Start();
+}
+
+void URLFetcher::OnReceivedRedirect(net::URLRequest* request,
+ const net::RedirectInfo& redirect_info,
+ bool* defer_redirect) {
+ if (!request_)
+ return;
+ DCHECK_EQ(request_.get(), request);
+ // Redirect is not allowed by the update process.
+ job_->MadeProgress();
+ redirect_response_code_ = request->GetResponseCode();
+ request->Cancel();
+ result_ = AppCacheUpdateJob::REDIRECT_ERROR;
+ OnResponseCompleted(net::ERR_ABORTED);
+}
+
+void URLFetcher::OnResponseStarted(net::URLRequest* request, int net_error) {
+ if (!request_)
+ return;
+ DCHECK_EQ(request_.get(), request);
+ DCHECK_NE(net::ERR_IO_PENDING, net_error);
+
+ int response_code = -1;
+ if (net_error == net::OK) {
+ response_code = request->GetResponseCode();
+ job_->MadeProgress();
+ }
+
+ if ((response_code / 100) != 2) {
+ if (response_code > 0)
+ result_ = AppCacheUpdateJob::SERVER_ERROR;
+ else
+ result_ = AppCacheUpdateJob::NETWORK_ERROR;
+ OnResponseCompleted(net_error);
+ return;
+ }
+
+ if (url_.SchemeIsCryptographic()) {
+ // Do not cache content with cert errors.
+ // Also, we willfully violate the HTML5 spec at this point in order
+ // to support the appcaching of cross-origin HTTPS resources.
+ // We've opted for a milder constraint and allow caching unless
+ // the resource has a "no-store" header. A spec change has been
+ // requested on the whatwg list.
+ // See http://code.google.com/p/chromium/issues/detail?id=69594
+ // TODO(michaeln): Consider doing this for cross-origin HTTP too.
+ const net::HttpNetworkSession::Params* session_params =
+ request->context()->GetNetworkSessionParams();
+ bool ignore_cert_errors =
+ session_params && session_params->ignore_certificate_errors;
+ if ((net::IsCertStatusError(request->ssl_info().cert_status) &&
+ !ignore_cert_errors) ||
+ (url_.GetOrigin() != job_->manifest_url_.GetOrigin() &&
+ request->response_headers()->HasHeaderValue("cache-control",
+ "no-store"))) {
+ DCHECK_EQ(-1, redirect_response_code_);
+ request->Cancel();
+ result_ = AppCacheUpdateJob::SECURITY_ERROR;
+ OnResponseCompleted(net::ERR_ABORTED);
+ return;
+ }
+ }
+
+ // Write response info to storage for URL fetches. Wait for async write
+ // completion before reading any response data.
+ if (fetch_type_ == URL_FETCH || fetch_type_ == MASTER_ENTRY_FETCH) {
+ response_writer_.reset(job_->CreateResponseWriter());
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(
+ new net::HttpResponseInfo(request->response_info())));
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
+ } else {
+ ReadResponseData();
+ }
+}
+
+void URLFetcher::OnReadCompleted(net::URLRequest* request, int bytes_read) {
+ if (!request_)
+ return;
+ DCHECK_NE(net::ERR_IO_PENDING, bytes_read);
+ DCHECK_EQ(request_.get(), request);
+ bool data_consumed = true;
+ if (bytes_read > 0) {
+ job_->MadeProgress();
+ data_consumed = ConsumeResponseData(bytes_read);
+ if (data_consumed) {
+ while (true) {
+ bytes_read = request->Read(buffer_.get(), buffer_size_);
+ if (bytes_read <= 0)
+ break;
+ data_consumed = ConsumeResponseData(bytes_read);
+ if (!data_consumed)
+ break;
+ }
+ }
+ }
+
+ if (data_consumed && bytes_read != net::ERR_IO_PENDING) {
+ DCHECK_EQ(AppCacheUpdateJob::UPDATE_OK, result_);
+ OnResponseCompleted(bytes_read);
+ }
+}
+
+void URLFetcher::AddConditionalHeaders(
+ const net::HttpResponseHeaders* headers) {
+ DCHECK(request_);
+ DCHECK(headers);
+ net::HttpRequestHeaders extra_headers;
+
+ // Add If-Modified-Since header if response info has Last-Modified header.
+ const std::string last_modified = "Last-Modified";
+ std::string last_modified_value;
+ headers->EnumerateHeader(nullptr, last_modified, &last_modified_value);
+ if (!last_modified_value.empty()) {
+ extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince,
+ last_modified_value);
+ }
+
+ // Add If-None-Match header if response info has ETag header.
+ const std::string etag = "ETag";
+ std::string etag_value;
+ headers->EnumerateHeader(nullptr, etag, &etag_value);
+ if (!etag_value.empty()) {
+ extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch, etag_value);
+ }
+ if (!extra_headers.IsEmpty())
+ request_->SetExtraRequestHeaders(extra_headers);
+}
+
+void URLFetcher::OnWriteComplete(int result) {
+ if (result < 0) {
+ request_->Cancel();
+ result_ = AppCacheUpdateJob::DISKCACHE_ERROR;
+ OnResponseCompleted(net::ERR_ABORTED);
+ return;
+ }
+ ReadResponseData();
+}
+
+void URLFetcher::ReadResponseData() {
+ AppCacheUpdateJob::InternalUpdateState state = job_->internal_state_;
+ if (state == AppCacheUpdateJob::CACHE_FAILURE ||
+ state == AppCacheUpdateJob::CANCELLED ||
+ state == AppCacheUpdateJob::COMPLETED) {
+ return;
+ }
+ int bytes_read = request_->Read(buffer_.get(), buffer_size_);
+ if (bytes_read != net::ERR_IO_PENDING)
+ OnReadCompleted(request_.get(), bytes_read);
+}
+
+// Returns false if response data is processed asynchronously, in which
+// case ReadResponseData will be invoked when it is safe to continue
+// reading more response data from the request.
+bool URLFetcher::ConsumeResponseData(int bytes_read) {
+ DCHECK_GT(bytes_read, 0);
+ switch (fetch_type_) {
+ case MANIFEST_FETCH:
+ case MANIFEST_REFETCH:
+ manifest_data_.append(buffer_->data(), bytes_read);
+ break;
+ case URL_FETCH:
+ case MASTER_ENTRY_FETCH:
+ DCHECK(response_writer_.get());
+ response_writer_->WriteData(
+ buffer_.get(), bytes_read,
+ base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
+ return false; // wait for async write completion to continue reading
+ default:
+ NOTREACHED();
+ }
+ return true;
+}
+
+void URLFetcher::OnResponseCompleted(int net_error) {
+ if (net_error == net::OK)
+ job_->MadeProgress();
+
+ // Retry for 503s where retry-after is 0.
+ if (net_error == net::OK && request_->GetResponseCode() == 503 &&
+ MaybeRetryRequest()) {
+ return;
+ }
+
+ switch (fetch_type_) {
+ case MANIFEST_FETCH:
+ job_->HandleManifestFetchCompleted(this, net_error);
+ break;
+ case URL_FETCH:
+ job_->HandleUrlFetchCompleted(this, net_error);
+ break;
+ case MASTER_ENTRY_FETCH:
+ job_->HandleMasterEntryFetchCompleted(this, net_error);
+ break;
+ case MANIFEST_REFETCH:
+ job_->HandleManifestRefetchCompleted(this, net_error);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ delete this;
+}
+
+bool URLFetcher::MaybeRetryRequest() {
+ if (retry_503_attempts_ >= kMax503Retries ||
+ !request_->response_headers()->HasHeaderValue("retry-after", "0")) {
+ return false;
+ }
+ ++retry_503_attempts_;
+ result_ = AppCacheUpdateJob::UPDATE_OK;
+ request_ = job_->service_->request_context()->CreateRequest(
+ url_, net::DEFAULT_PRIORITY, this, kTrafficAnnotation);
+ Start();
+ return true;
+}
+
+} // namespace content.
« no previous file with comments | « content/browser/appcache/appcache_update_url_fetcher.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698