summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorgeorgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-02 22:01:20 +0000
committergeorgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-02 22:01:20 +0000
commitdb16347063a2217641ddd992f1f029af24362a59 (patch)
tree9e74d716e342052d9c1989f13e110935d9a98763 /chrome
parent4af869aded61930f3085f28548a89daf4ab2ef0f (diff)
downloadchromium_src-db16347063a2217641ddd992f1f029af24362a59.zip
chromium_src-db16347063a2217641ddd992f1f029af24362a59.tar.gz
chromium_src-db16347063a2217641ddd992f1f029af24362a59.tar.bz2
Behaving nice with AutoFill servers: Adjusting upload rate, processing 500, 502 and 503 responses, etc.
TEST=Unit-tested + by setting up the response from AutoFill server. BUG=39921 Review URL: http://codereview.chromium.org/1535011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43531 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/autofill/autofill_download.cc124
-rw-r--r--chrome/browser/autofill/autofill_download.h41
-rw-r--r--chrome/browser/autofill/autofill_download_unittest.cc61
-rw-r--r--chrome/browser/autofill/autofill_manager.cc17
-rw-r--r--chrome/browser/autofill/autofill_xml_parser.h2
-rw-r--r--chrome/browser/autofill/autofill_xml_parser_unittest.cc55
-rw-r--r--chrome/browser/net/url_fetcher.cc16
-rw-r--r--chrome/browser/net/url_fetcher.h25
-rw-r--r--chrome/browser/net/url_fetcher_unittest.cc76
-rw-r--r--chrome/common/pref_names.cc6
-rw-r--r--chrome/common/pref_names.h3
11 files changed, 384 insertions, 42 deletions
diff --git a/chrome/browser/autofill/autofill_download.cc b/chrome/browser/autofill/autofill_download.cc
index d0ac8368..42d4a32 100644
--- a/chrome/browser/autofill/autofill_download.cc
+++ b/chrome/browser/autofill/autofill_download.cc
@@ -9,7 +9,11 @@
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/stl_util-inl.h"
+#include "chrome/browser/autofill/autofill_xml_parser.h"
+#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
+#include "chrome/common/pref_names.h"
+#include "net/http/http_response_headers.h"
#define DISABLED_REQUEST_URL "http://disabled"
@@ -18,21 +22,26 @@
#else
#define AUTO_FILL_QUERY_SERVER_REQUEST_URL DISABLED_REQUEST_URL
#define AUTO_FILL_UPLOAD_SERVER_REQUEST_URL DISABLED_REQUEST_URL
+#define AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER "SOMESERVER/"
#endif
-namespace {
-// We only send a fraction of the forms to upload server.
-// The rate for positive/negative matches potentially could be different.
-const double kAutoFillPositiveUploadRate = 0.01;
-const double kAutoFillNegativeUploadRate = 0.01;
-} // namespace
-
-AutoFillDownloadManager::AutoFillDownloadManager()
- : observer_(NULL),
- positive_upload_rate_(kAutoFillPositiveUploadRate),
- negative_upload_rate_(kAutoFillNegativeUploadRate),
+AutoFillDownloadManager::AutoFillDownloadManager(Profile* profile)
+ : profile_(profile),
+ observer_(NULL),
+ next_query_request_(base::Time::Now()),
+ next_upload_request_(base::Time::Now()),
+ positive_upload_rate_(0),
+ negative_upload_rate_(0),
fetcher_id_for_unittest_(0),
is_testing_(false) {
+ // |profile_| could be NULL in some unit-tests.
+ if (profile_) {
+ PrefService* preferences = profile_->GetPrefs();
+ positive_upload_rate_ =
+ preferences->GetReal(prefs::kAutoFillPositiveUploadRate);
+ negative_upload_rate_ =
+ preferences->GetReal(prefs::kAutoFillNegativeUploadRate);
+ }
}
AutoFillDownloadManager::~AutoFillDownloadManager() {
@@ -52,6 +61,10 @@ void AutoFillDownloadManager::SetObserver(
bool AutoFillDownloadManager::StartQueryRequest(
const ScopedVector<FormStructure>& forms) {
+ if (next_query_request_ > base::Time::Now()) {
+ // We are in back-off mode: do not do the request.
+ return false;
+ }
std::string form_xml;
FormStructure::EncodeQueryRequest(forms, &form_xml);
@@ -70,15 +83,18 @@ bool AutoFillDownloadManager::StartQueryRequest(
bool AutoFillDownloadManager::StartUploadRequest(
const FormStructure& form, bool form_was_matched) {
+ if (next_upload_request_ > base::Time::Now()) {
+ // We are in back-off mode: do not do the request.
+ return false;
+ }
+
// Check if we need to upload form.
- // TODO(georgey): adjust this values from returned XML.
- double upload_rate = form_was_matched ? positive_upload_rate_ :
- negative_upload_rate_;
+ double upload_rate = form_was_matched ? GetPositiveUploadRate() :
+ GetNegativeUploadRate();
if (base::RandDouble() > upload_rate) {
LOG(INFO) << "AutoFillDownloadManager: Upload request is ignored";
- if (observer_)
- observer_->OnUploadedAutoFillHeuristics(form.FormSignature());
- return true;
+ // If we ever need notification that upload was skipped, add it here.
+ return false;
}
std::string form_xml;
form.EncodeUploadRequest(form_was_matched, &form_xml);
@@ -109,6 +125,35 @@ bool AutoFillDownloadManager::CancelRequest(
return false;
}
+double AutoFillDownloadManager::GetPositiveUploadRate() const {
+ return positive_upload_rate_;
+}
+
+double AutoFillDownloadManager::GetNegativeUploadRate() const {
+ return negative_upload_rate_;
+}
+
+void AutoFillDownloadManager::SetPositiveUploadRate(double rate) {
+ if (rate == positive_upload_rate_)
+ return;
+ positive_upload_rate_ = rate;
+ DCHECK_GE(rate, 0.0);
+ DCHECK_LE(rate, 1.0);
+ DCHECK(profile_);
+ PrefService* preferences = profile_->GetPrefs();
+ preferences->SetReal(prefs::kAutoFillPositiveUploadRate, rate);
+}
+
+void AutoFillDownloadManager::SetNegativeUploadRate(double rate) {
+ if (rate == negative_upload_rate_)
+ return;
+ negative_upload_rate_ = rate;
+ DCHECK_GE(rate, 0.0);
+ DCHECK_LE(rate, 1.0);
+ DCHECK(profile_);
+ PrefService* preferences = profile_->GetPrefs();
+ preferences->SetReal(prefs::kAutoFillNegativeUploadRate, rate);
+}
bool AutoFillDownloadManager::StartRequest(
const std::string& form_xml,
@@ -131,6 +176,7 @@ bool AutoFillDownloadManager::StartRequest(
URLFetcher::POST,
this);
url_fetchers_[fetcher] = request_data;
+ fetcher->set_automatcally_retry_on_5xx(false);
fetcher->set_request_context(Profile::GetDefaultRequestContext());
fetcher->set_upload_data("text/plain", form_xml);
fetcher->Start();
@@ -150,8 +196,39 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY ?
"query" : "upload");
const int kHttpResponseOk = 200;
+ const int kHttpInternalServerError = 500;
+ const int kHttpBadGateway = 502;
+ const int kHttpServiceUnavailable = 503;
+
DCHECK(it->second.form_signatures.size());
if (response_code != kHttpResponseOk) {
+ bool back_off = false;
+ std::string server_header;
+ switch (response_code) {
+ case kHttpBadGateway:
+ if (!source->response_headers()->EnumerateHeader(NULL, "server",
+ &server_header) ||
+ StartsWithASCII(server_header.c_str(),
+ AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER,
+ false) != 0)
+ break;
+ // Bad getaway was received from AutoFill servers. Fall through to back
+ // off.
+ case kHttpInternalServerError:
+ case kHttpServiceUnavailable:
+ back_off = true;
+ break;
+ }
+
+ if (back_off) {
+ base::Time back_off_time(base::Time::Now() + source->backoff_delay());
+ if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) {
+ next_query_request_ = back_off_time;
+ } else {
+ next_upload_request_ = back_off_time;
+ }
+ }
+
LOG(INFO) << "AutoFillDownloadManager: " << type_of_request <<
" request has failed with response" << response_code;
if (observer_) {
@@ -166,7 +243,17 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
if (observer_)
observer_->OnLoadedAutoFillHeuristics(it->second.form_signatures, data);
} else {
- // TODO(georgey): adjust upload probabilities.
+ double new_positive_upload_rate = 0;
+ double new_negative_upload_rate = 0;
+ AutoFillUploadXmlParser parse_handler(&new_positive_upload_rate,
+ &new_negative_upload_rate);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(data.data(), data.length(), true);
+ if (parse_handler.succeeded()) {
+ SetPositiveUploadRate(new_positive_upload_rate);
+ SetNegativeUploadRate(new_negative_upload_rate);
+ }
+
if (observer_)
observer_->OnUploadedAutoFillHeuristics(it->second.form_signatures[0]);
}
@@ -175,4 +262,3 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
url_fetchers_.erase(it);
}
-
diff --git a/chrome/browser/autofill/autofill_download.h b/chrome/browser/autofill/autofill_download.h
index 620ac6f..a3511dd 100644
--- a/chrome/browser/autofill/autofill_download.h
+++ b/chrome/browser/autofill/autofill_download.h
@@ -12,11 +12,14 @@
#include "base/scoped_ptr.h"
#include "base/scoped_vector.h"
#include "base/string16.h"
+#include "base/time.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/autofill/field_types.h"
#include "chrome/browser/autofill/form_structure.h"
#include "chrome/browser/net/url_fetcher.h"
+class Profile;
+
// Handles getting and updating AutoFill heuristics.
class AutoFillDownloadManager : public URLFetcher::Delegate {
public:
@@ -25,6 +28,7 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
REQUEST_UPLOAD,
};
// An interface used to notify clients of AutoFillDownloadManager.
+ // Notifications are *not* guaranteed to be called.
class Observer {
public:
// Called when heuristic successfully received from server.
@@ -49,7 +53,8 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
virtual ~Observer() {}
};
- AutoFillDownloadManager();
+ // |profile| can be NULL in unit-tests only.
+ explicit AutoFillDownloadManager(Profile* profile);
virtual ~AutoFillDownloadManager();
// |observer| - observer to notify on successful completion or error.
@@ -61,8 +66,8 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
bool StartQueryRequest(const ScopedVector<FormStructure>& forms);
// Start upload request if necessary. The probability of request going
- // over the wire are |positive_upload_rate_| if it was matched by
- // AutoFill, |negative_download_rate_| otherwise. Observer will be called
+ // over the wire are GetPositiveUploadRate() if it was matched by
+ // AutoFill, GetNegativeUploadRate() otherwise. Observer will be called
// even if there was no actual trip over the wire.
// |form| - form sent in this request.
// |form_was_matched| - true if form was matched by the AutoFill.
@@ -76,15 +81,17 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
bool CancelRequest(const std::string& form_signature,
AutoFillRequestType request_type);
- void SetPositiveUploadRate(double rate) {
- DCHECK(rate >= 0.0 && rate <= 1.0);
- positive_upload_rate_ = rate;
- }
-
- void SetNegativeUploadRate(double rate) {
- DCHECK(rate >= 0.0 && rate <= 1.0);
- negative_upload_rate_ = rate;
- }
+ // Probability of the form upload. Between 0 (no upload) and 1 (upload all).
+ // GetPositiveUploadRate() is for matched forms,
+ // GetNegativeUploadRate() for non matched.
+ double GetPositiveUploadRate() const;
+ double GetNegativeUploadRate() const;
+ // These functions called very rarely outside of theunit-tests. With current
+ // percentages, they would be called once per 100 auto-fillable forms filled
+ // and submitted by user. The order of magnitude would remain similar in the
+ // future.
+ void SetPositiveUploadRate(double rate);
+ void SetNegativeUploadRate(double rate);
private:
friend class AutoFillDownloadTestHelper; // unit-test.
@@ -110,13 +117,21 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
const ResponseCookies& cookies,
const std::string& data);
+ // Profile for preference storage.
+ Profile* profile_;
+
// For each requested form for both query and upload we create a separate
// request and save its info. As url fetcher is identified by its address
// we use a map between fetchers and info.
std::map<URLFetcher*, FormRequestData> url_fetchers_;
AutoFillDownloadManager::Observer *observer_;
- // Probability of the form upload. Between 0 (no upload) and 1 (upload all).
+ // Time when next query/upload requests are allowed. If 50x HTTP received,
+ // exponential back off is initiated, so this times will be in the future
+ // for awhile.
+ base::Time next_query_request_;
+ base::Time next_upload_request_;
+
// |positive_upload_rate_| is for matched forms,
// |negative_upload_rate_| for non matched.
double positive_upload_rate_;
diff --git a/chrome/browser/autofill/autofill_download_unittest.cc b/chrome/browser/autofill/autofill_download_unittest.cc
index dcf692c..e1df3b8 100644
--- a/chrome/browser/autofill/autofill_download_unittest.cc
+++ b/chrome/browser/autofill/autofill_download_unittest.cc
@@ -7,6 +7,7 @@
#include "base/string_util.h"
#include "chrome/browser/autofill/autofill_download.h"
#include "chrome/browser/net/test_url_fetcher_factory.h"
+#include "chrome/test/testing_profile.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
@@ -24,7 +25,8 @@ using WebKit::WebInputElement;
// successful upload request, failed upload request.
class AutoFillDownloadTestHelper : public AutoFillDownloadManager::Observer {
public:
- AutoFillDownloadTestHelper() {
+ AutoFillDownloadTestHelper()
+ : download_manager(&profile) {
download_manager.SetObserver(this);
// For chromium builds forces Start*Request to actually execute.
download_manager.is_testing_ = true;
@@ -84,6 +86,7 @@ class AutoFillDownloadTestHelper : public AutoFillDownloadManager::Observer {
};
std::list<AutoFillDownloadTestHelper::ResponseData> responses_;
+ TestingProfile profile;
AutoFillDownloadManager download_manager;
};
@@ -175,6 +178,10 @@ TEST(AutoFillDownloadTest, QueryAndUploadTest) {
fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
200, ResponseCookies(),
std::string(responses[1]));
+ // After that upload rates would be adjusted to 0.5/0.3
+ EXPECT_DOUBLE_EQ(0.5, helper.download_manager.GetPositiveUploadRate());
+ EXPECT_DOUBLE_EQ(0.3, helper.download_manager.GetNegativeUploadRate());
+
fetcher = factory.GetFetcherByID(2);
ASSERT_TRUE(fetcher);
fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
@@ -214,15 +221,63 @@ TEST(AutoFillDownloadTest, QueryAndUploadTest) {
EXPECT_EQ(signature, helper.responses_.front().signature);
EXPECT_EQ(responses[0], helper.responses_.front().response);
helper.responses_.pop_front();
+
// Set upload to 0% so no new requests happen.
helper.download_manager.SetPositiveUploadRate(0.0);
helper.download_manager.SetNegativeUploadRate(0.0);
// No actual requests for the next two calls, as we set upload rate to 0%.
- EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
+ EXPECT_FALSE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
true));
- EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[1]),
+ EXPECT_FALSE(helper.download_manager.StartUploadRequest(*(form_structures[1]),
false));
+ fetcher = factory.GetFetcherByID(3);
+ EXPECT_EQ(NULL, fetcher);
+
+ // Verify DOS attack back-offs.
+ const int kBackOffTimeout = 10000;
+
+ // Request with id 3.
+ EXPECT_TRUE(helper.download_manager.StartQueryRequest(form_structures));
+ fetcher = factory.GetFetcherByID(3);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_backoff_delay(
+ base::TimeDelta::FromMilliseconds(kBackOffTimeout));
+ fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
+ 500, ResponseCookies(),
+ std::string(responses[0]));
+ EXPECT_EQ(AutoFillDownloadTestHelper::REQUEST_QUERY_FAILED,
+ helper.responses_.front().type_of_response);
+ EXPECT_EQ(500, helper.responses_.front().error);
+ // Expected response on non-query request is an empty string.
+ EXPECT_EQ(std::string(""), helper.responses_.front().response);
+ helper.responses_.pop_front();
+
+ // Query requests should be ignored for the next 10 seconds.
+ EXPECT_FALSE(helper.download_manager.StartQueryRequest(form_structures));
+ fetcher = factory.GetFetcherByID(4);
+ EXPECT_EQ(NULL, fetcher);
+
+ // Set upload to 100% so requests happen.
+ helper.download_manager.SetPositiveUploadRate(1.0);
+ // Request with id 4.
+ EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
+ true));
fetcher = factory.GetFetcherByID(4);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_backoff_delay(
+ base::TimeDelta::FromMilliseconds(kBackOffTimeout));
+ fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
+ 503, ResponseCookies(),
+ std::string(responses[2]));
+ EXPECT_EQ(AutoFillDownloadTestHelper::REQUEST_UPLOAD_FAILED,
+ helper.responses_.front().type_of_response);
+ EXPECT_EQ(503, helper.responses_.front().error);
+ helper.responses_.pop_front();
+
+ // Upload requests should be ignored for the next 10 seconds.
+ EXPECT_FALSE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
+ true));
+ fetcher = factory.GetFetcherByID(5);
EXPECT_EQ(NULL, fetcher);
// Make sure consumer of URLFetcher does the right thing.
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc
index 628cb9a..5bed021 100644
--- a/chrome/browser/autofill/autofill_manager.cc
+++ b/chrome/browser/autofill/autofill_manager.cc
@@ -21,9 +21,18 @@
#include "webkit/glue/form_field.h"
#include "webkit/glue/form_field_values.h"
+namespace {
+// We only send a fraction of the forms to upload server.
+// The rate for positive/negative matches potentially could be different.
+const double kAutoFillPositiveUploadRateDefaultValue = 0.01;
+const double kAutoFillNegativeUploadRateDefaultValue = 0.01;
+} // namespace
+
+
AutoFillManager::AutoFillManager(TabContents* tab_contents)
: tab_contents_(tab_contents),
personal_data_(NULL),
+ download_manager_(tab_contents_->profile()),
infobar_(NULL) {
DCHECK(tab_contents);
@@ -52,6 +61,11 @@ void AutoFillManager::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kAutoFillAuxiliaryProfilesEnabled, false);
prefs->RegisterStringPref(prefs::kAutoFillDefaultProfile, std::wstring());
prefs->RegisterStringPref(prefs::kAutoFillDefaultCreditCard, std::wstring());
+
+ prefs->RegisterRealPref(prefs::kAutoFillPositiveUploadRate,
+ kAutoFillPositiveUploadRateDefaultValue);
+ prefs->RegisterRealPref(prefs::kAutoFillNegativeUploadRate,
+ kAutoFillNegativeUploadRateDefaultValue);
}
void AutoFillManager::FormFieldValuesSubmitted(
@@ -461,5 +475,6 @@ bool AutoFillManager::IsAutoFillEnabled() {
AutoFillManager::AutoFillManager()
: tab_contents_(NULL),
- personal_data_(NULL) {
+ personal_data_(NULL),
+ download_manager_(NULL) {
}
diff --git a/chrome/browser/autofill/autofill_xml_parser.h b/chrome/browser/autofill/autofill_xml_parser.h
index 1fda2fe..69c662a 100644
--- a/chrome/browser/autofill/autofill_xml_parser.h
+++ b/chrome/browser/autofill/autofill_xml_parser.h
@@ -100,7 +100,7 @@ class AutoFillQueryXmlParser : public AutoFillXmlParser {
// the form matches what's in the users profile.
// The negative upload rate is typically much lower than the positive upload
// rate.
-class AutoFillUploadXmlParser : public buzz::XmlParseHandler {
+class AutoFillUploadXmlParser : public AutoFillXmlParser {
public:
AutoFillUploadXmlParser(double* positive_upload_rate,
double* negative_upload_rate);
diff --git a/chrome/browser/autofill/autofill_xml_parser_unittest.cc b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
index 7e1e55c..c06df83 100644
--- a/chrome/browser/autofill/autofill_xml_parser_unittest.cc
+++ b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
@@ -135,4 +135,59 @@ TEST(AutoFillQueryXmlParserTest, ParseErrors) {
EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
}
+// Test successfull upload response.
+TEST(AutoFillUploadXmlParser, TestSuccessfulResponse) {
+ std::string xml = "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"/>";
+ double positive = 0;
+ double negative = 0;
+ AutoFillUploadXmlParser parse_handler(&positive, &negative);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler.succeeded());
+ EXPECT_DOUBLE_EQ(0.5, positive);
+ EXPECT_DOUBLE_EQ(0.3, negative);
+}
+
+// Test failed upload response.
+TEST(AutoFillUploadXmlParser, TestFailedResponse) {
+ std::string xml = "<autofilluploadresponse positiveuploadrate=\"\" "
+ "negativeuploadrate=\"0.3\"/>";
+ double positive = 0;
+ double negative = 0;
+ scoped_ptr<AutoFillUploadXmlParser> parse_handler(
+ new AutoFillUploadXmlParser(&positive, &negative));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0.3, negative); // Partially parsed.
+ negative = 0;
+
+ xml = "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"";
+ parse_handler.reset(new AutoFillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+
+ xml = "bad data";
+ parse_handler.reset(new AutoFillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+
+ xml = "";
+ parse_handler.reset(new AutoFillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+}
+
} // namespace
diff --git a/chrome/browser/net/url_fetcher.cc b/chrome/browser/net/url_fetcher.cc
index a0a96eb..350dd50 100644
--- a/chrome/browser/net/url_fetcher.cc
+++ b/chrome/browser/net/url_fetcher.cc
@@ -110,7 +110,8 @@ URLFetcher::URLFetcher(const GURL& url,
RequestType request_type,
Delegate* d)
: ALLOW_THIS_IN_INITIALIZER_LIST(
- core_(new Core(this, url, request_type, d))) {
+ core_(new Core(this, url, request_type, d))),
+ automatically_retry_on_5xx_(true) {
}
URLFetcher::~URLFetcher() {
@@ -275,21 +276,24 @@ void URLFetcher::Core::OnCompletedURLRequest(const URLRequestStatus& status) {
if (response_code_ >= 500) {
// When encountering a server error, we will send the request again
// after backoff time.
- const int64 wait =
+ int64 back_off_time =
protect_entry_->UpdateBackoff(URLFetcherProtectEntry::FAILURE);
+ fetcher_->backoff_delay_ = base::TimeDelta::FromMilliseconds(back_off_time);
++num_retries_;
// Restarts the request if we still need to notify the delegate.
if (delegate_) {
- if (num_retries_ <= protect_entry_->max_retries()) {
+ if (fetcher_->automatically_retry_on_5xx_ &&
+ num_retries_ <= protect_entry_->max_retries()) {
ChromeThread::PostDelayedTask(
ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(this, &Core::StartURLRequest), wait);
+ NewRunnableMethod(this, &Core::StartURLRequest), back_off_time);
} else {
delegate_->OnURLFetchComplete(fetcher_, url_, status, response_code_,
cookies_, data_);
}
}
} else {
+ fetcher_->backoff_delay_ = base::TimeDelta();
protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SUCCESS);
if (delegate_)
delegate_->OnURLFetchComplete(fetcher_, url_, status, response_code_,
@@ -325,6 +329,10 @@ void URLFetcher::set_request_context(
core_->request_context_getter_ = request_context_getter;
}
+void URLFetcher::set_automatcally_retry_on_5xx(bool retry) {
+ automatically_retry_on_5xx_ = retry;
+}
+
net::HttpResponseHeaders* URLFetcher::response_headers() const {
return core_->response_headers_;
}
diff --git a/chrome/browser/net/url_fetcher.h b/chrome/browser/net/url_fetcher.h
index 695f808..b585124 100644
--- a/chrome/browser/net/url_fetcher.h
+++ b/chrome/browser/net/url_fetcher.h
@@ -15,6 +15,7 @@
#include "base/leak_tracker.h"
#include "base/message_loop.h"
#include "base/ref_counted.h"
+#include "base/time.h"
class GURL;
typedef std::vector<std::string> ResponseCookies;
@@ -135,6 +136,22 @@ class URLFetcher {
// request is started.
void set_request_context(URLRequestContextGetter* request_context_getter);
+ // If |retry| is false, 5xx responses will be propagated to the observer,
+ // if it is true URLFetcher will automatically re-execute the request,
+ // after backoff_delay() elapses. URLFetcher has it set to true by default.
+ void set_automatcally_retry_on_5xx(bool retry);
+
+ // Returns the back-off delay before the request will be retried,
+ // when a 5xx response was received.
+ base::TimeDelta backoff_delay() const { return backoff_delay_; }
+
+ // Sets the back-off delay, allowing to mock 5xx requests in unit-tests.
+#if defined(UNIT_TEST)
+ void set_backoff_delay(base::TimeDelta backoff_delay) {
+ backoff_delay_ = backoff_delay;
+ }
+#endif // defined(UNIT_TEST)
+
// Retrieve the response headers from the request. Must only be called after
// the OnURLFetchComplete callback has run.
virtual net::HttpResponseHeaders* response_headers() const;
@@ -162,6 +179,14 @@ class URLFetcher {
base::LeakTracker<URLFetcher> leak_tracker_;
+ // If |automatically_retry_on_5xx_| is false, 5xx responses will be
+ // propagated to the observer, if it is true URLFetcher will automatically
+ // re-execute the request, after the back-off delay has expired.
+ // true by default.
+ bool automatically_retry_on_5xx_;
+ // Back-off time delay. 0 by default.
+ base::TimeDelta backoff_delay_;
+
static bool g_interception_enabled;
DISALLOW_EVIL_CONSTRUCTORS(URLFetcher);
diff --git a/chrome/browser/net/url_fetcher_unittest.cc b/chrome/browser/net/url_fetcher_unittest.cc
index 664b319..079bad5 100644
--- a/chrome/browser/net/url_fetcher_unittest.cc
+++ b/chrome/browser/net/url_fetcher_unittest.cc
@@ -96,7 +96,7 @@ class URLFetcherHeadersTest : public URLFetcherTest {
const std::string& data);
};
-// Version of URLFetcherTest that tests overload proctection.
+// Version of URLFetcherTest that tests overload protection.
class URLFetcherProtectTest : public URLFetcherTest {
public:
virtual void CreateFetcher(const GURL& url);
@@ -111,6 +111,22 @@ class URLFetcherProtectTest : public URLFetcherTest {
Time start_time_;
};
+// Version of URLFetcherTest that tests overload protection, when responses
+// passed through.
+class URLFetcherProtectTestPassedThrough : public URLFetcherTest {
+ public:
+ virtual void CreateFetcher(const GURL& url);
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ private:
+ Time start_time_;
+};
+
// Version of URLFetcherTest that tests bad HTTPS requests.
class URLFetcherBadHTTPSTest : public URLFetcherTest {
public:
@@ -286,6 +302,41 @@ void URLFetcherProtectTest::OnURLFetchComplete(const URLFetcher* source,
}
}
+void URLFetcherProtectTestPassedThrough::CreateFetcher(const GURL& url) {
+ fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
+ fetcher_->set_request_context(new TestURLRequestContextGetter());
+ fetcher_->set_automatcally_retry_on_5xx(false);
+ start_time_ = Time::Now();
+ fetcher_->Start();
+}
+
+void URLFetcherProtectTestPassedThrough::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ const TimeDelta one_minute = TimeDelta::FromMilliseconds(60000);
+ if (response_code >= 500) {
+ // Now running ServerUnavailable test.
+ // It should get here on the first attempt, so almost immediately and
+ // *not* to attempt to execute all 11 requests (2.5 minutes).
+ EXPECT_TRUE(Time::Now() - start_time_ < one_minute);
+ EXPECT_TRUE(status.is_success());
+ // Check that suggested back off time is bigger than 0.
+ EXPECT_GT(fetcher_->backoff_delay().InMicroseconds(), 0);
+ EXPECT_FALSE(data.empty());
+ delete fetcher_;
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE, new MessageLoop::QuitTask());
+ } else {
+ // We should not get here!
+ FAIL();
+ }
+}
+
+
URLFetcherBadHTTPSTest::URLFetcherBadHTTPSTest() {
PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
cert_dir_ = cert_dir_.AppendASCII("chrome");
@@ -439,6 +490,29 @@ TEST_F(URLFetcherProtectTest, ServerUnavailable) {
MessageLoop::current()->Run();
}
+TEST_F(URLFetcherProtectTestPassedThrough, ServerUnavailablePropagateResponse) {
+ scoped_refptr<HTTPTestServer> server =
+ HTTPTestServer::CreateServer(L"chrome/test/data", NULL);
+ ASSERT_TRUE(NULL != server.get());
+ GURL url = GURL(server->TestServerPage("files/server-unavailable.html"));
+
+ // Registers an entry for test url. The backoff time is calculated by:
+ // new_backoff = 2.0 * old_backoff + 0
+ // and maximum backoff time is 256 milliseconds.
+ // Maximum retries allowed is set to 11.
+ URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
+ // Total time if *not* for not doing automatic backoff would be 150s.
+ // In reality it should be "as soon as server responds".
+ URLFetcherProtectEntry* entry =
+ new URLFetcherProtectEntry(200, 3, 11, 100, 2.0, 0, 150000);
+ manager->Register(url.host(), entry);
+
+ CreateFetcher(url);
+
+ MessageLoop::current()->Run();
+}
+
+
TEST_F(URLFetcherBadHTTPSTest, BadHTTPSTest) {
scoped_refptr<HTTPSTestServer> server =
HTTPSTestServer::CreateExpiredServer(kDocRoot);
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 8ae28c7..d1cb8b7 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -352,6 +352,12 @@ const wchar_t kAutoFillDefaultProfile[] = L"autofill.default_profile";
// The label of the default AutoFill credit card.
const wchar_t kAutoFillDefaultCreditCard[] = L"autofill.default_creditcard";
+// Double that indicates positive (for matched forms) upload rate.
+const wchar_t kAutoFillPositiveUploadRate[] = L"autofill.positive_upload_rate";
+
+// Double that indicates negative (for not matched forms) upload rate.
+const wchar_t kAutoFillNegativeUploadRate[] = L"autofill.negative_upload_rate";
+
// Dictionary that maps providers to lists of filter rules.
const wchar_t kPrivacyFilterRules[] = L"profile.privacy_filter_rules";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index d1ea830..44323e6 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -134,6 +134,9 @@ extern const wchar_t kAutoFillAuxiliaryProfilesEnabled[];
extern const wchar_t kAutoFillDialogPlacement[];
extern const wchar_t kAutoFillDefaultProfile[];
extern const wchar_t kAutoFillDefaultCreditCard[];
+extern const wchar_t kAutoFillPositiveUploadRate[];
+extern const wchar_t kAutoFillNegativeUploadRate[];
+
extern const wchar_t kPrivacyFilterRules[];
extern const wchar_t kUseVerticalTabs[];
extern const wchar_t kEnableTranslate[];