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