autofill_download_manager.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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.
4
5#include "components/autofill/core/browser/autofill_download_manager.h"
6
7#include "base/logging.h"
8#include "base/prefs/pref_service.h"
9#include "base/rand_util.h"
10#include "base/stl_util.h"
11#include "base/strings/string_util.h"
12#include "components/autofill/core/browser/autofill_driver.h"
13#include "components/autofill/core/browser/autofill_metrics.h"
14#include "components/autofill/core/browser/autofill_xml_parser.h"
15#include "components/autofill/core/browser/form_structure.h"
16#include "components/autofill/core/common/autofill_pref_names.h"
17#include "net/base/load_flags.h"
18#include "net/http/http_response_headers.h"
19#include "net/url_request/url_fetcher.h"
20#include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
21#include "url/gurl.h"
22
23namespace autofill {
24
25namespace {
26
27const char kAutofillQueryServerNameStartInHeader[] = "GFE/";
28const size_t kMaxFormCacheSize = 16;
29
30#if defined(GOOGLE_CHROME_BUILD)
31const char kClientName[] = "Google Chrome";
32#else
33const char kClientName[] = "Chromium";
34#endif  // defined(GOOGLE_CHROME_BUILD)
35
36std::string RequestTypeToString(AutofillDownloadManager::RequestType type) {
37  switch (type) {
38    case AutofillDownloadManager::REQUEST_QUERY:
39      return "query";
40    case AutofillDownloadManager::REQUEST_UPLOAD:
41      return "upload";
42  }
43  NOTREACHED();
44  return std::string();
45}
46
47GURL GetRequestUrl(AutofillDownloadManager::RequestType request_type) {
48  return GURL("https://clients1.google.com/tbproxy/af/" +
49              RequestTypeToString(request_type) + "?client=" + kClientName);
50}
51
52}  // namespace
53
54struct AutofillDownloadManager::FormRequestData {
55  std::vector<std::string> form_signatures;
56  RequestType request_type;
57};
58
59AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver,
60                                                 PrefService* pref_service,
61                                                 Observer* observer)
62    : driver_(driver),
63      pref_service_(pref_service),
64      observer_(observer),
65      max_form_cache_size_(kMaxFormCacheSize),
66      next_query_request_(base::Time::Now()),
67      next_upload_request_(base::Time::Now()),
68      positive_upload_rate_(0),
69      negative_upload_rate_(0),
70      fetcher_id_for_unittest_(0) {
71  DCHECK(observer_);
72  positive_upload_rate_ =
73      pref_service_->GetDouble(prefs::kAutofillPositiveUploadRate);
74  negative_upload_rate_ =
75      pref_service_->GetDouble(prefs::kAutofillNegativeUploadRate);
76}
77
78AutofillDownloadManager::~AutofillDownloadManager() {
79  STLDeleteContainerPairFirstPointers(url_fetchers_.begin(),
80                                      url_fetchers_.end());
81}
82
83bool AutofillDownloadManager::StartQueryRequest(
84    const std::vector<FormStructure*>& forms,
85    const AutofillMetrics& metric_logger) {
86  if (next_query_request_ > base::Time::Now()) {
87    // We are in back-off mode: do not do the request.
88    return false;
89  }
90  std::string form_xml;
91  FormRequestData request_data;
92  if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures,
93                                         &form_xml)) {
94    return false;
95  }
96
97  request_data.request_type = AutofillDownloadManager::REQUEST_QUERY;
98  metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_SENT);
99
100  std::string query_data;
101  if (CheckCacheForQueryRequest(request_data.form_signatures, &query_data)) {
102    DVLOG(1) << "AutofillDownloadManager: query request has been retrieved "
103             << "from the cache, form signatures: "
104             << GetCombinedSignature(request_data.form_signatures);
105    observer_->OnLoadedServerPredictions(query_data);
106    return true;
107  }
108
109  return StartRequest(form_xml, request_data);
110}
111
112bool AutofillDownloadManager::StartUploadRequest(
113    const FormStructure& form,
114    bool form_was_autofilled,
115    const ServerFieldTypeSet& available_field_types) {
116  std::string form_xml;
117  if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled,
118                                &form_xml))
119    return false;
120
121  if (next_upload_request_ > base::Time::Now()) {
122    // We are in back-off mode: do not do the request.
123    DVLOG(1) << "AutofillDownloadManager: Upload request is throttled.";
124    return false;
125  }
126
127  // Flip a coin to see if we should upload this form.
128  double upload_rate = form_was_autofilled ? GetPositiveUploadRate() :
129                                             GetNegativeUploadRate();
130  if (form.upload_required() == UPLOAD_NOT_REQUIRED ||
131      (form.upload_required() == USE_UPLOAD_RATES &&
132       base::RandDouble() > upload_rate)) {
133    DVLOG(1) << "AutofillDownloadManager: Upload request is ignored.";
134    // If we ever need notification that upload was skipped, add it here.
135    return false;
136  }
137
138  FormRequestData request_data;
139  request_data.form_signatures.push_back(form.FormSignature());
140  request_data.request_type = AutofillDownloadManager::REQUEST_UPLOAD;
141
142  return StartRequest(form_xml, request_data);
143}
144
145double AutofillDownloadManager::GetPositiveUploadRate() const {
146  return positive_upload_rate_;
147}
148
149double AutofillDownloadManager::GetNegativeUploadRate() const {
150  return negative_upload_rate_;
151}
152
153void AutofillDownloadManager::SetPositiveUploadRate(double rate) {
154  if (rate == positive_upload_rate_)
155    return;
156  positive_upload_rate_ = rate;
157  DCHECK_GE(rate, 0.0);
158  DCHECK_LE(rate, 1.0);
159  pref_service_->SetDouble(prefs::kAutofillPositiveUploadRate, rate);
160}
161
162void AutofillDownloadManager::SetNegativeUploadRate(double rate) {
163  if (rate == negative_upload_rate_)
164    return;
165  negative_upload_rate_ = rate;
166  DCHECK_GE(rate, 0.0);
167  DCHECK_LE(rate, 1.0);
168  pref_service_->SetDouble(prefs::kAutofillNegativeUploadRate, rate);
169}
170
171bool AutofillDownloadManager::StartRequest(
172    const std::string& form_xml,
173    const FormRequestData& request_data) {
174  net::URLRequestContextGetter* request_context =
175      driver_->GetURLRequestContext();
176  DCHECK(request_context);
177  GURL request_url = GetRequestUrl(request_data.request_type);
178
179  // Id is ignored for regular chrome, in unit test id's for fake fetcher
180  // factory will be 0, 1, 2, ...
181  net::URLFetcher* fetcher = net::URLFetcher::Create(
182      fetcher_id_for_unittest_++, request_url, net::URLFetcher::POST,
183      this);
184  url_fetchers_[fetcher] = request_data;
185  fetcher->SetAutomaticallyRetryOn5xx(false);
186  fetcher->SetRequestContext(request_context);
187  fetcher->SetUploadData("text/plain", form_xml);
188  fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
189                        net::LOAD_DO_NOT_SEND_COOKIES);
190  fetcher->Start();
191
192  DVLOG(1) << "Sending AutofillDownloadManager "
193           << RequestTypeToString(request_data.request_type)
194           << " request: " << form_xml;
195
196  return true;
197}
198
199void AutofillDownloadManager::CacheQueryRequest(
200    const std::vector<std::string>& forms_in_query,
201    const std::string& query_data) {
202  std::string signature = GetCombinedSignature(forms_in_query);
203  for (QueryRequestCache::iterator it = cached_forms_.begin();
204       it != cached_forms_.end(); ++it) {
205    if (it->first == signature) {
206      // We hit the cache, move to the first position and return.
207      std::pair<std::string, std::string> data = *it;
208      cached_forms_.erase(it);
209      cached_forms_.push_front(data);
210      return;
211    }
212  }
213  std::pair<std::string, std::string> data;
214  data.first = signature;
215  data.second = query_data;
216  cached_forms_.push_front(data);
217  while (cached_forms_.size() > max_form_cache_size_)
218    cached_forms_.pop_back();
219}
220
221bool AutofillDownloadManager::CheckCacheForQueryRequest(
222    const std::vector<std::string>& forms_in_query,
223    std::string* query_data) const {
224  std::string signature = GetCombinedSignature(forms_in_query);
225  for (QueryRequestCache::const_iterator it = cached_forms_.begin();
226       it != cached_forms_.end(); ++it) {
227    if (it->first == signature) {
228      // We hit the cache, fill the data and return.
229      *query_data = it->second;
230      return true;
231    }
232  }
233  return false;
234}
235
236std::string AutofillDownloadManager::GetCombinedSignature(
237    const std::vector<std::string>& forms_in_query) const {
238  size_t total_size = forms_in_query.size();
239  for (size_t i = 0; i < forms_in_query.size(); ++i)
240    total_size += forms_in_query[i].length();
241  std::string signature;
242
243  signature.reserve(total_size);
244
245  for (size_t i = 0; i < forms_in_query.size(); ++i) {
246    if (i)
247      signature.append(",");
248    signature.append(forms_in_query[i]);
249  }
250  return signature;
251}
252
253void AutofillDownloadManager::OnURLFetchComplete(
254    const net::URLFetcher* source) {
255  std::map<net::URLFetcher *, FormRequestData>::iterator it =
256      url_fetchers_.find(const_cast<net::URLFetcher*>(source));
257  if (it == url_fetchers_.end()) {
258    // Looks like crash on Mac is possibly caused with callback entering here
259    // with unknown fetcher when network is refreshed.
260    return;
261  }
262  std::string request_type(RequestTypeToString(it->second.request_type));
263  const int kHttpResponseOk = 200;
264  const int kHttpInternalServerError = 500;
265  const int kHttpBadGateway = 502;
266  const int kHttpServiceUnavailable = 503;
267
268  CHECK(it->second.form_signatures.size());
269  if (source->GetResponseCode() != kHttpResponseOk) {
270    bool back_off = false;
271    std::string server_header;
272    switch (source->GetResponseCode()) {
273      case kHttpBadGateway:
274        if (!source->GetResponseHeaders()->EnumerateHeader(NULL, "server",
275                                                           &server_header) ||
276            StartsWithASCII(server_header.c_str(),
277                            kAutofillQueryServerNameStartInHeader,
278                            false) != 0)
279          break;
280        // Bad gateway was received from Autofill servers. Fall through to back
281        // off.
282      case kHttpInternalServerError:
283      case kHttpServiceUnavailable:
284        back_off = true;
285        break;
286    }
287
288    if (back_off) {
289      base::Time back_off_time(base::Time::Now() + source->GetBackoffDelay());
290      if (it->second.request_type == AutofillDownloadManager::REQUEST_QUERY) {
291        next_query_request_ = back_off_time;
292      } else {
293        next_upload_request_ = back_off_time;
294      }
295    }
296
297    DVLOG(1) << "AutofillDownloadManager: " << request_type
298             << " request has failed with response "
299             << source->GetResponseCode();
300    observer_->OnServerRequestError(it->second.form_signatures[0],
301                                    it->second.request_type,
302                                    source->GetResponseCode());
303  } else {
304    std::string response_body;
305    source->GetResponseAsString(&response_body);
306    DVLOG(1) << "AutofillDownloadManager: " << request_type
307             << " request has succeeded with response body: " << response_body;
308    if (it->second.request_type == AutofillDownloadManager::REQUEST_QUERY) {
309      CacheQueryRequest(it->second.form_signatures, response_body);
310      observer_->OnLoadedServerPredictions(response_body);
311    } else {
312      double new_positive_upload_rate = 0;
313      double new_negative_upload_rate = 0;
314      AutofillUploadXmlParser parse_handler(&new_positive_upload_rate,
315                                            &new_negative_upload_rate);
316      buzz::XmlParser parser(&parse_handler);
317      parser.Parse(response_body.data(), response_body.length(), true);
318      if (parse_handler.succeeded()) {
319        SetPositiveUploadRate(new_positive_upload_rate);
320        SetNegativeUploadRate(new_negative_upload_rate);
321      }
322
323      observer_->OnUploadedPossibleFieldTypes();
324    }
325  }
326  delete it->first;
327  url_fetchers_.erase(it);
328}
329
330}  // namespace autofill
331