diff options
author | georgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-02 22:01:20 +0000 |
---|---|---|
committer | georgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-02 22:01:20 +0000 |
commit | db16347063a2217641ddd992f1f029af24362a59 (patch) | |
tree | 9e74d716e342052d9c1989f13e110935d9a98763 /chrome | |
parent | 4af869aded61930f3085f28548a89daf4ab2ef0f (diff) | |
download | chromium_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.cc | 124 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_download.h | 41 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_download_unittest.cc | 61 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 17 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_xml_parser.h | 2 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_xml_parser_unittest.cc | 55 | ||||
-rw-r--r-- | chrome/browser/net/url_fetcher.cc | 16 | ||||
-rw-r--r-- | chrome/browser/net/url_fetcher.h | 25 | ||||
-rw-r--r-- | chrome/browser/net/url_fetcher_unittest.cc | 76 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 6 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 3 |
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[]; |