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