cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / components / password_manager / core / browser / affiliation_fetcher.cc
blob943724bfab0f5020bd30dc1fb80c48746fa0cefa
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/password_manager/core/browser/affiliation_fetcher.h"
7 #include "base/metrics/histogram_macros.h"
8 #include "base/metrics/sparse_histogram.h"
9 #include "components/password_manager/core/browser/affiliation_api.pb.h"
10 #include "components/password_manager/core/browser/affiliation_utils.h"
11 #include "components/password_manager/core/browser/test_affiliation_fetcher_factory.h"
12 #include "google_apis/google_api_keys.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/url_util.h"
15 #include "net/http/http_status_code.h"
16 #include "net/url_request/url_fetcher.h"
17 #include "net/url_request/url_request_context_getter.h"
18 #include "url/gurl.h"
20 namespace password_manager {
22 namespace {
24 // Enumeration listing the possible outcomes of fetching affiliation information
25 // from the Affiliation API. This is used in UMA histograms, so do not change
26 // existing values, only add new values at the end.
27 enum AffiliationFetchResult {
28 AFFILIATION_FETCH_RESULT_SUCCESS,
29 AFFILIATION_FETCH_RESULT_FAILURE,
30 AFFILIATION_FETCH_RESULT_MALFORMED,
31 AFFILIATION_FETCH_RESULT_MAX
34 // Records the given fetch |result| into the respective UMA histogram, as well
35 // as the response and error codes of |fetcher| if it is non-null.
36 void ReportStatistics(AffiliationFetchResult result,
37 const net::URLFetcher* fetcher) {
38 UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult",
39 result, AFFILIATION_FETCH_RESULT_MAX);
40 if (fetcher) {
41 UMA_HISTOGRAM_SPARSE_SLOWLY(
42 "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
43 fetcher->GetResponseCode());
44 // Network error codes are negative. See: src/net/base/net_error_list.h.
45 UMA_HISTOGRAM_SPARSE_SLOWLY(
46 "PasswordManager.AffiliationFetcher.FetchErrorCode",
47 -fetcher->GetStatus().error());
51 } // namespace
53 static TestAffiliationFetcherFactory* g_testing_factory = nullptr;
55 AffiliationFetcher::AffiliationFetcher(
56 net::URLRequestContextGetter* request_context_getter,
57 const std::vector<FacetURI>& facet_uris,
58 AffiliationFetcherDelegate* delegate)
59 : request_context_getter_(request_context_getter),
60 requested_facet_uris_(facet_uris),
61 delegate_(delegate) {
62 for (const FacetURI& uri : requested_facet_uris_) {
63 DCHECK(uri.is_valid());
67 AffiliationFetcher::~AffiliationFetcher() {
70 // static
71 AffiliationFetcher* AffiliationFetcher::Create(
72 net::URLRequestContextGetter* context_getter,
73 const std::vector<FacetURI>& facet_uris,
74 AffiliationFetcherDelegate* delegate) {
75 if (g_testing_factory) {
76 return g_testing_factory->CreateInstance(context_getter, facet_uris,
77 delegate);
79 return new AffiliationFetcher(context_getter, facet_uris, delegate);
82 // static
83 void AffiliationFetcher::SetFactoryForTesting(
84 TestAffiliationFetcherFactory* factory) {
85 g_testing_factory = factory;
88 void AffiliationFetcher::StartRequest() {
89 DCHECK(!fetcher_);
91 fetcher_ =
92 net::URLFetcher::Create(BuildQueryURL(), net::URLFetcher::POST, this);
93 fetcher_->SetRequestContext(request_context_getter_.get());
94 fetcher_->SetUploadData("application/x-protobuf", PreparePayload());
95 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
96 net::LOAD_DO_NOT_SEND_COOKIES |
97 net::LOAD_DO_NOT_SEND_AUTH_DATA |
98 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE);
99 fetcher_->SetAutomaticallyRetryOn5xx(false);
100 fetcher_->SetAutomaticallyRetryOnNetworkChanges(0);
101 fetcher_->Start();
104 GURL AffiliationFetcher::BuildQueryURL() const {
105 return net::AppendQueryParameter(
106 GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"),
107 "key", google_apis::GetAPIKey());
110 std::string AffiliationFetcher::PreparePayload() const {
111 affiliation_pb::LookupAffiliationRequest lookup_request;
112 for (const FacetURI& uri : requested_facet_uris_)
113 lookup_request.add_facet(uri.canonical_spec());
115 std::string serialized_request;
116 bool success = lookup_request.SerializeToString(&serialized_request);
117 DCHECK(success);
118 return serialized_request;
121 bool AffiliationFetcher::ParseResponse(
122 AffiliationFetcherDelegate::Result* result) const {
123 // This function parses the response protocol buffer message for a list of
124 // equivalence classes, and stores them into |results| after performing some
125 // validation and sanitization steps to make sure that the contract of
126 // AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
127 // * The server response will not have anything for facets that are not
128 // affiliated with any other facet, while |result| must have them.
129 // * The server response might contain future, unknown kinds of facet URIs,
130 // while |result| must contain only those that are FacetURI::is_valid().
131 // * The server response being ill-formed or self-inconsistent (in the sense
132 // that there are overlapping equivalence classes) is indicative of server
133 // side issues likely not remedied by re-fetching. Report failure in this
134 // case so the caller can be notified and it can act accordingly.
135 // * The |result| will be free of duplicate or empty equivalence classes.
137 std::string serialized_response;
138 if (!fetcher_->GetResponseAsString(&serialized_response)) {
139 NOTREACHED();
142 affiliation_pb::LookupAffiliationResponse response;
143 if (!response.ParseFromString(serialized_response))
144 return false;
146 result->reserve(requested_facet_uris_.size());
148 std::map<FacetURI, size_t> facet_uri_to_class_index;
149 for (int i = 0; i < response.affiliation_size(); ++i) {
150 const affiliation_pb::Affiliation& equivalence_class(
151 response.affiliation(i));
153 AffiliatedFacets affiliated_uris;
154 for (int j = 0; j < equivalence_class.facet_size(); ++j) {
155 const std::string& uri_spec(equivalence_class.facet(j).id());
156 FacetURI uri = FacetURI::FromPotentiallyInvalidSpec(uri_spec);
157 // Ignore potential future kinds of facet URIs (e.g. for new platforms).
158 if (!uri.is_valid())
159 continue;
160 affiliated_uris.push_back(uri);
163 // Be lenient and ignore empty (after filtering) equivalence classes.
164 if (affiliated_uris.empty())
165 continue;
167 // Ignore equivalence classes that are duplicates of earlier ones. However,
168 // fail in the case of a partial overlap, which violates the invariant that
169 // affiliations must form an equivalence relation.
170 for (const FacetURI& uri : affiliated_uris) {
171 if (!facet_uri_to_class_index.count(uri))
172 facet_uri_to_class_index[uri] = result->size();
173 if (facet_uri_to_class_index[uri] !=
174 facet_uri_to_class_index[affiliated_uris[0]]) {
175 return false;
179 // Filter out duplicate equivalence classes in the response.
180 if (facet_uri_to_class_index[affiliated_uris[0]] == result->size())
181 result->push_back(affiliated_uris);
184 // Synthesize an equivalence class (of size one) for each facet that did not
185 // appear in the server response due to not being affiliated with any others.
186 for (const FacetURI& uri : requested_facet_uris_) {
187 if (!facet_uri_to_class_index.count(uri))
188 result->push_back(AffiliatedFacets(1, uri));
191 return true;
194 void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
195 DCHECK_EQ(source, fetcher_.get());
197 // Note that invoking the |delegate_| may destroy |this| synchronously, so the
198 // invocation must happen last.
199 scoped_ptr<AffiliationFetcherDelegate::Result> result_data(
200 new AffiliationFetcherDelegate::Result);
201 if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
202 fetcher_->GetResponseCode() == net::HTTP_OK) {
203 if (ParseResponse(result_data.get())) {
204 ReportStatistics(AFFILIATION_FETCH_RESULT_SUCCESS, nullptr);
205 delegate_->OnFetchSucceeded(result_data.Pass());
206 } else {
207 ReportStatistics(AFFILIATION_FETCH_RESULT_MALFORMED, nullptr);
208 delegate_->OnMalformedResponse();
210 } else {
211 ReportStatistics(AFFILIATION_FETCH_RESULT_FAILURE, fetcher_.get());
212 delegate_->OnFetchFailed();
216 } // namespace password_manager