diff options
author | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-29 22:02:50 +0000 |
---|---|---|
committer | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-29 22:02:50 +0000 |
commit | 130d0a44d678b53bed5678de48a87834d193e27f (patch) | |
tree | 89691c2dbe2105c5fde2b011e1874e5071035a29 /chrome | |
parent | 591a792d1a074fefdb9321f547b9209ebfc9e85a (diff) | |
download | chromium_src-130d0a44d678b53bed5678de48a87834d193e27f.zip chromium_src-130d0a44d678b53bed5678de48a87834d193e27f.tar.gz chromium_src-130d0a44d678b53bed5678de48a87834d193e27f.tar.bz2 |
Re-landing issue 4202006 (http://codereview.chromium.org/4202006/show) which was reverted because of a Valgrind leak. Fixed the memory leak in the unit-test.
BUG=None.
TEST=Unit-tests, Valgrind bots.
Review URL: http://codereview.chromium.org/4165013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64496 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_consts.cc | 4 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_consts.h | 13 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_helpers.cc | 42 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_helpers.h | 23 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_proxy_backend.cc | 300 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_proxy_backend.h | 3 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_url_fetcher.cc | 146 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_url_fetcher.h | 118 | ||||
-rw-r--r-- | chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc | 353 | ||||
-rw-r--r-- | chrome/service/cloud_print/job_status_updater.cc | 47 | ||||
-rw-r--r-- | chrome/service/cloud_print/job_status_updater.h | 19 | ||||
-rw-r--r-- | chrome/service/cloud_print/printer_job_handler.cc | 402 | ||||
-rw-r--r-- | chrome/service/cloud_print/printer_job_handler.h | 147 |
15 files changed, 1047 insertions, 573 deletions
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index b1e8ebb..453138e 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1131,6 +1131,8 @@ 'service/cloud_print/cloud_print_proxy.h', 'service/cloud_print/cloud_print_proxy_backend.cc', 'service/cloud_print/cloud_print_proxy_backend.h', + 'service/cloud_print/cloud_print_url_fetcher.cc', + 'service/cloud_print/cloud_print_url_fetcher.h', 'service/cloud_print/job_status_updater.cc', 'service/cloud_print/job_status_updater.h', 'service/cloud_print/print_system_dummy.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index c91a8ac..602d213 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1595,6 +1595,7 @@ 'renderer/spellchecker/spellcheck_unittest.cc', 'renderer/spellchecker/spellcheck_worditerator_unittest.cc', 'service/cloud_print/cloud_print_helpers_unittest.cc', + 'service/cloud_print/cloud_print_url_fetcher_unittest.cc', 'service/service_process_unittest.cc', 'test/browser_with_test_window_test.cc', 'test/browser_with_test_window_test.h', diff --git a/chrome/service/cloud_print/cloud_print_consts.cc b/chrome/service/cloud_print/cloud_print_consts.cc index 7513875..6272a6d 100644 --- a/chrome/service/cloud_print/cloud_print_consts.cc +++ b/chrome/service/cloud_print/cloud_print_consts.cc @@ -39,3 +39,7 @@ const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com"; // certain requests. const char kChromeCloudPrintProxyHeader[] = "X-Google-CloudPrint-Proxy: Chrome"; +// The request retry policy names. These strings are not valid hostnames, they +// are just string keys. +const char kCloudPrintAPIRetryPolicy[] = "cloudprint.google.com/api"; +const char kJobDataRetryPolicy[] = "cloudprint.google.com/jobdata"; diff --git a/chrome/service/cloud_print/cloud_print_consts.h b/chrome/service/cloud_print/cloud_print_consts.h index 532f717..3c2fcc8 100644 --- a/chrome/service/cloud_print/cloud_print_consts.h +++ b/chrome/service/cloud_print/cloud_print_consts.h @@ -36,11 +36,14 @@ extern const char kCloudPrintGaiaServiceId[]; extern const char kSyncGaiaServiceId[]; extern const char kCloudPrintPushNotificationsSource[]; extern const char kChromeCloudPrintProxyHeader[]; -// Max interval between retrying connection to the server -const int64 kMaxRetryInterval = 5*60*1000; // 5 minutes in millseconds -const int64 kBaseRetryInterval = 5*1000; // 5 seconds -const int kMaxRetryCount = 2; -const int64 kJobStatusUpdateInterval = 10*1000; // 10 seconds +extern const char kCloudPrintAPIRetryPolicy[]; +extern const char kJobDataRetryPolicy[]; + +// Max retry count for job data fetch requests. +const int kJobDataMaxRetryCount = 5; +// Look at CloudPrintProxyBackend::Core::CreateDefaultRetryPolicy for default +// values of the request retry policy. + // When we don't have XMPP notifications available, we resort to polling for // print jobs. We choose a random interval in seconds between these 2 values. const int kMinJobPollIntervalSecs = 5*60; // 5 minutes in seconds diff --git a/chrome/service/cloud_print/cloud_print_helpers.cc b/chrome/service/cloud_print/cloud_print_helpers.cc index fc132672..d03ddd0 100644 --- a/chrome/service/cloud_print/cloud_print_helpers.cc +++ b/chrome/service/cloud_print/cloud_print_helpers.cc @@ -14,7 +14,6 @@ #include "base/values.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/common/net/url_fetcher.h" -#include "chrome/service/net/service_url_request_context.h" #include "chrome/service/service_process.h" std::string StringFromJobStatus(cloud_print::PrintJobStatus status) { @@ -138,14 +137,12 @@ bool CloudPrintHelpers::ParseResponseJSON( const std::string& response_data, bool* succeeded, DictionaryValue** response_dict) { scoped_ptr<Value> message_value(base::JSONReader::Read(response_data, false)); - if (!message_value.get()) { - NOTREACHED(); + if (!message_value.get()) return false; - } - if (!message_value->IsType(Value::TYPE_DICTIONARY)) { - NOTREACHED(); + + if (!message_value->IsType(Value::TYPE_DICTIONARY)) return false; - } + scoped_ptr<DictionaryValue> response_dict_local( static_cast<DictionaryValue*>(message_value.release())); if (succeeded) @@ -155,37 +152,6 @@ bool CloudPrintHelpers::ParseResponseJSON( return true; } -void CloudPrintHelpers::PrepCloudPrintRequest(URLFetcher* request, - const std::string& auth_token) { - DCHECK(g_service_process); - request->set_request_context(new ServiceURLRequestContextGetter()); - std::string headers = "Authorization: GoogleLogin auth="; - headers += auth_token; - headers += "\r\n"; - headers += kChromeCloudPrintProxyHeader; - request->set_extra_request_headers(headers); -} - -void CloudPrintHelpers::HandleServerError(int* error_count, int max_retry_count, - int64 max_retry_interval, - int64 base_retry_interval, - Task* task_to_retry, - Task* task_on_give_up) { - (*error_count)++; - if ((-1 != max_retry_count) && (*error_count > max_retry_count)) { - if (task_on_give_up) { - MessageLoop::current()->PostTask(FROM_HERE, task_on_give_up); - } - } else { - int64 retry_interval = base_retry_interval * (*error_count); - if ((-1 != max_retry_interval) && (retry_interval > max_retry_interval)) { - retry_interval = max_retry_interval; - } - MessageLoop::current()->PostDelayedTask(FROM_HERE, task_to_retry, - retry_interval); - } -} - void CloudPrintHelpers::AddMultipartValueForUpload( const std::string& value_name, const std::string& value, const std::string& mime_boundary, const std::string& content_type, diff --git a/chrome/service/cloud_print/cloud_print_helpers.h b/chrome/service/cloud_print/cloud_print_helpers.h index c0bd61d..8e51f7a 100644 --- a/chrome/service/cloud_print/cloud_print_helpers.h +++ b/chrome/service/cloud_print/cloud_print_helpers.h @@ -40,28 +40,7 @@ class CloudPrintHelpers { // Returns the response as a dictionary value. static bool ParseResponseJSON(const std::string& response_data, bool* succeeded, DictionaryValue** response_dict); - // Sets up common parameters for a cloud print request - // (such as the GAIA auth token in the request headers, the request context - // etc). - static void PrepCloudPrintRequest(URLFetcher* request, - const std::string& auth_token); - // Strictly speaking, this helper method is not specific to cloud printing. - // It handles the logic to retry tasks when the server returns an error. - // The parameters are as below: - // |error_count| Contains the current number of consecutive failed attempts. - // This method increments it (in/out) - // |max_retry_count| Number of retries before giving up. -1 implies no limit. - // |max_retry_interval| Maximum amount of time (in ms) we are willing to - // wait between retries. -1 implies no limit. - // |base_retry_interval| Starting value of the retry interval. This - // method progressively increases the interval on each retry. - // |task_to_retry| The task to be retried. - // |task_on_give_up| Task to be performed when we give up. This is only - // valid when max_retry_count is not -1. It can be NULL. - static void HandleServerError(int* error_count, int max_retry_count, - int64 max_retry_interval, - int64 base_retry_interval, - Task* task_to_retry, Task* task_on_give_up); + // Prepares one value as part of a multi-part upload request. static void AddMultipartValueForUpload( const std::string& value_name, const std::string& value, diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.cc b/chrome/service/cloud_print/cloud_print_proxy_backend.cc index 38fa3d4..99158d3 100644 --- a/chrome/service/cloud_print/cloud_print_proxy_backend.cc +++ b/chrome/service/cloud_print/cloud_print_proxy_backend.cc @@ -14,9 +14,10 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" -#include "chrome/common/net/http_return.h" +#include "chrome/common/net/url_fetcher_protect.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/cloud_print/cloud_print_helpers.h" +#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" #include "chrome/service/cloud_print/printer_job_handler.h" #include "chrome/service/gaia/service_gaia_authenticator.h" #include "chrome/service/service_process.h" @@ -29,7 +30,7 @@ // The real guts of CloudPrintProxyBackend, to keep the public client API clean. class CloudPrintProxyBackend::Core : public base::RefCountedThreadSafe<CloudPrintProxyBackend::Core>, - public URLFetcherDelegate, + public CloudPrintURLFetcherDelegate, public cloud_print::PrintServerWatcherDelegate, public PrinterJobHandlerDelegate, public notifier::TalkMediator::Delegate { @@ -62,12 +63,15 @@ class CloudPrintProxyBackend::Core void DoRegisterSelectedPrinters( const printing::PrinterList& printer_list); - // URLFetcher::Delegate implementation. - virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); + // CloudPrintURLFetcher::Delegate implementation. + virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + virtual void OnRequestAuthError(); + // cloud_print::PrintServerWatcherDelegate implementation virtual void OnPrinterAdded(); // PrinterJobHandler::Delegate implementation @@ -82,24 +86,26 @@ class CloudPrintProxyBackend::Core const IncomingNotificationData& notification_data); virtual void OnOutgoingNotification(); - protected: + private: // Prototype for a response handler. - typedef void (CloudPrintProxyBackend::Core::*ResponseHandler)( - const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, int response_code, - const ResponseCookies& cookies, const std::string& data); + typedef CloudPrintURLFetcher::ResponseAction + (CloudPrintProxyBackend::Core::*ResponseHandler)( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); // Begin response handlers - void HandlePrinterListResponse(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - void HandleRegisterPrinterResponse(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); + CloudPrintURLFetcher::ResponseAction HandlePrinterListResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandleRegisterPrinterResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); // End response handlers // NotifyXXX is how the Core communicates with the frontend across @@ -112,6 +118,8 @@ class CloudPrintProxyBackend::Core const std::string& email); void NotifyAuthenticationFailed(); + URLFetcherProtectEntry* CreateDefaultRetryPolicy(); + // Starts a new printer registration process. void StartRegistration(); // Ends the printer registration process. @@ -122,7 +130,6 @@ class CloudPrintProxyBackend::Core // Retrieves the list of registered printers for this user/proxy combination // from the cloud print server. void GetRegisteredPrinters(); - void HandleServerError(Task* task_to_retry); // Removes the given printer from the list. Returns false if the printer // did not exist in the list. bool RemovePrinterFromList(const std::string& printer_name); @@ -152,16 +159,14 @@ class CloudPrintProxyBackend::Core // user a chance to further trim the list. When the frontend gives us the // final list we make a copy into this so that we can start registering. printing::PrinterList printer_list_; - // The URLFetcher instance for the current request - scoped_ptr<URLFetcher> request_; + // The CloudPrintURLFetcher instance for the current request. + scoped_refptr<CloudPrintURLFetcher> request_; // The index of the nex printer to be uploaded. size_t next_upload_index_; // The unique id for this proxy std::string proxy_id_; // The GAIA auth token std::string auth_token_; - // The number of consecutive times that connecting to the server failed. - int server_error_count_; // Cached info about the last printer that we tried to upload. We cache this // so we won't have to requery the printer if the upload fails and we need // to retry. @@ -248,10 +253,13 @@ void CloudPrintProxyBackend::RegisterPrinters( CloudPrintProxyBackend::Core::Core(CloudPrintProxyBackend* backend, const GURL& cloud_print_server_url, const DictionaryValue* print_system_settings) - : backend_(backend), cloud_print_server_url_(cloud_print_server_url), - next_upload_index_(0), server_error_count_(0), - next_response_handler_(NULL), new_printers_available_(false), - notifications_enabled_(false), job_poll_scheduled_(false) { + : backend_(backend), + cloud_print_server_url_(cloud_print_server_url), + next_upload_index_(0), + next_response_handler_(NULL), + new_printers_available_(false), + notifications_enabled_(false), + job_poll_scheduled_(false) { if (print_system_settings) { // It is possible to have no print settings specified. print_system_settings_.reset( @@ -342,14 +350,41 @@ void CloudPrintProxyBackend::Core::DoInitializeWithToken( print_server_watcher_->StartWatching(this); proxy_id_ = proxy_id; + + // Register the request retry policies for cloud print APIs and job data + // requests. + URLFetcherProtectManager::GetInstance()->Register( + kCloudPrintAPIRetryPolicy, CreateDefaultRetryPolicy()); + URLFetcherProtectManager::GetInstance()->Register( + kJobDataRetryPolicy, CreateDefaultRetryPolicy())->SetMaxRetries( + kJobDataMaxRetryCount); + StartRegistration(); } +URLFetcherProtectEntry* +CloudPrintProxyBackend::Core::CreateDefaultRetryPolicy() { + // Times are in milliseconds. + const int kSlidingWindowPeriod = 2000; + const int kMaxSendThreshold = 20; + const int kMaxRetries = -1; + const int kInitialTimeout = 100; + const double kMultiplier = 2.0; + const int kConstantFactor = 100; + const int kMaximumTimeout = 5*60*1000; + return new URLFetcherProtectEntry(kSlidingWindowPeriod, + kMaxSendThreshold, + kMaxRetries, + kInitialTimeout, + kMultiplier, + kConstantFactor, + kMaximumTimeout); +} + void CloudPrintProxyBackend::Core::StartRegistration() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); printer_list_.clear(); print_system_->GetPrintBackend()->EnumeratePrinters(&printer_list_); - server_error_count_ = 0; // Now we need to ask the server about printers that were registered on the // server so that we can trim this list. GetRegisteredPrinters(); @@ -357,7 +392,7 @@ void CloudPrintProxyBackend::Core::StartRegistration() { void CloudPrintProxyBackend::Core::EndRegistration() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - request_.reset(); + request_ = NULL; if (new_printers_available_) { new_printers_available_ = false; StartRegistration(); @@ -380,7 +415,7 @@ void CloudPrintProxyBackend::Core::DoShutdown() { // Important to delete the TalkMediator on this thread. talk_mediator_.reset(); notifications_enabled_ = false; - request_.reset(); + request_ = NULL; MessageLoop::current()->QuitNow(); } @@ -389,7 +424,6 @@ void CloudPrintProxyBackend::Core::DoRegisterSelectedPrinters( DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); if (!print_system_.get()) return; // No print system available. - server_error_count_ = 0; printer_list_.assign(printer_list.begin(), printer_list.end()); next_upload_index_ = 0; RegisterNextPrinter(); @@ -397,15 +431,14 @@ void CloudPrintProxyBackend::Core::DoRegisterSelectedPrinters( void CloudPrintProxyBackend::Core::GetRegisteredPrinters() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - request_.reset( - new URLFetcher( - CloudPrintHelpers::GetUrlForPrinterList(cloud_print_server_url_, - proxy_id_), - URLFetcher::GET, this)); - CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); + GURL printer_list_url = + CloudPrintHelpers::GetUrlForPrinterList(cloud_print_server_url_, + proxy_id_); next_response_handler_ = &CloudPrintProxyBackend::Core::HandlePrinterListResponse; - request_->Start(); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest(printer_list_url, this, auth_token_, + kCloudPrintAPIRetryPolicy); } void CloudPrintProxyBackend::Core::RegisterNextPrinter() { @@ -465,16 +498,16 @@ void CloudPrintProxyBackend::Core::RegisterNextPrinter() { post_data.append("--" + mime_boundary + "--\r\n"); std::string mime_type("multipart/form-data; boundary="); mime_type += mime_boundary; - request_.reset( - new URLFetcher( - CloudPrintHelpers::GetUrlForPrinterRegistration( - cloud_print_server_url_), - URLFetcher::POST, this)); - CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); - request_->set_upload_data(mime_type, post_data); + GURL register_url = CloudPrintHelpers::GetUrlForPrinterRegistration( + cloud_print_server_url_); + next_response_handler_ = &CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse; - request_->Start(); + request_ = new CloudPrintURLFetcher; + request_->StartPostRequest(register_url, this, auth_token_, + kCloudPrintAPIRetryPolicy, mime_type, + post_data); + } else { LOG(ERROR) << "CP_PROXY: Failed to get printer info for: " << info.printer_name; @@ -521,23 +554,19 @@ void CloudPrintProxyBackend::Core::ScheduleJobPoll() { } } -// URLFetcher::Delegate implementation. -void CloudPrintProxyBackend::Core::OnURLFetchComplete( - const URLFetcher* source, const GURL& url, const URLRequestStatus& status, - int response_code, const ResponseCookies& cookies, - const std::string& data) { - DCHECK(source == request_.get()); - // If we get an auth error, we need to give up right away and notify the - // frontend loop. - if (RC_FORBIDDEN == response_code) { - backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, - &Core::NotifyAuthenticationFailed)); - } else { - // We need a next response handler - DCHECK(next_response_handler_); - (this->*next_response_handler_)(source, url, status, response_code, - cookies, data); - } +// CloudPrintURLFetcher::Delegate implementation. +CloudPrintURLFetcher::ResponseAction +CloudPrintProxyBackend::Core::HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + DCHECK(next_response_handler_); + return (this->*next_response_handler_)(source, url, json_data, succeeded); +} + +void CloudPrintProxyBackend::Core::OnRequestAuthError() { + OnAuthError(); } void CloudPrintProxyBackend::Core::NotifyPrinterListAvailable( @@ -560,53 +589,44 @@ void CloudPrintProxyBackend::Core::NotifyAuthenticationFailed() { backend_->frontend_->OnAuthenticationFailed(); } -void CloudPrintProxyBackend::Core::HandlePrinterListResponse( - const URLFetcher* source, const GURL& url, const URLRequestStatus& status, - int response_code, const ResponseCookies& cookies, - const std::string& data) { +CloudPrintURLFetcher::ResponseAction +CloudPrintProxyBackend::Core::HandlePrinterListResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - bool succeeded = false; - if (status.is_success() && response_code == 200) { - server_error_count_ = 0; - // Parse the response JSON for the list of printers already registered. - DictionaryValue* response_dict_temp = NULL; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, - &response_dict_temp); - scoped_ptr<DictionaryValue> response_dict; - response_dict.reset(response_dict_temp); - if (succeeded) { - DCHECK(response_dict.get()); - ListValue* printer_list = NULL; - response_dict->GetList(kPrinterListValue, &printer_list); - // There may be no "printers" value in the JSON - if (printer_list) { - for (size_t index = 0; index < printer_list->GetSize(); index++) { - DictionaryValue* printer_data = NULL; - if (printer_list->GetDictionary(index, &printer_data)) { - std::string printer_name; - printer_data->GetString(kNameValue, &printer_name); - RemovePrinterFromList(printer_name); - InitJobHandlerForPrinter(printer_data); - } else { - NOTREACHED(); - } - } - } - MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release()); - if (!printer_list_.empty()) { - // Let the frontend know that we have a list of printers available. - backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, - &Core::NotifyPrinterListAvailable, printer_list_)); + if (!succeeded) { + NOTREACHED(); + return CloudPrintURLFetcher::RETRY_REQUEST; + } + ListValue* printer_list = NULL; + json_data->GetList(kPrinterListValue, &printer_list); + // There may be no "printers" value in the JSON + if (printer_list) { + for (size_t index = 0; index < printer_list->GetSize(); index++) { + DictionaryValue* printer_data = NULL; + if (printer_list->GetDictionary(index, &printer_data)) { + std::string printer_name; + printer_data->GetString(kNameValue, &printer_name); + RemovePrinterFromList(printer_name); + InitJobHandlerForPrinter(printer_data); } else { - // No more work to be done here. - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &Core::EndRegistration)); + NOTREACHED(); } } } - - if (!succeeded) - HandleServerError(NewRunnableMethod(this, &Core::GetRegisteredPrinters)); + request_ = NULL; + if (!printer_list_.empty()) { + // Let the frontend know that we have a list of printers available. + backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &Core::NotifyPrinterListAvailable, printer_list_)); + } else { + // No more work to be done here. + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(this, &Core::EndRegistration)); + } + return CloudPrintURLFetcher::STOP_PROCESSING; } void CloudPrintProxyBackend::Core::InitJobHandlerForPrinter( @@ -655,46 +675,30 @@ void CloudPrintProxyBackend::Core::InitJobHandlerForPrinter( } } -void CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse( - const URLFetcher* source, const GURL& url, const URLRequestStatus& status, - int response_code, const ResponseCookies& cookies, - const std::string& data) { +CloudPrintURLFetcher::ResponseAction +CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Handle register printer response, code: " - << response_code; - Task* next_task = - NewRunnableMethod(this, - &CloudPrintProxyBackend::Core::RegisterNextPrinter); - if (status.is_success() && (response_code == 200)) { - bool succeeded = false; - DictionaryValue* response_dict = NULL; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); - if (succeeded) { - DCHECK(response_dict); - ListValue* printer_list = NULL; - response_dict->GetList(kPrinterListValue, &printer_list); - // There should be a "printers" value in the JSON - DCHECK(printer_list); - if (printer_list) { - DictionaryValue* printer_data = NULL; - if (printer_list->GetDictionary(0, &printer_data)) - InitJobHandlerForPrinter(printer_data); - } + if (succeeded) { + ListValue* printer_list = NULL; + json_data->GetList(kPrinterListValue, &printer_list); + // There should be a "printers" value in the JSON + DCHECK(printer_list); + if (printer_list) { + DictionaryValue* printer_data = NULL; + if (printer_list->GetDictionary(0, &printer_data)) + InitJobHandlerForPrinter(printer_data); } - server_error_count_ = 0; - next_upload_index_++; - MessageLoop::current()->PostTask(FROM_HERE, next_task); - } else { - HandleServerError(next_task); } -} - -void CloudPrintProxyBackend::Core::HandleServerError(Task* task_to_retry) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Server error."; - CloudPrintHelpers::HandleServerError( - &server_error_count_, -1, kMaxRetryInterval, kBaseRetryInterval, - task_to_retry, NULL); + next_upload_index_++; + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &CloudPrintProxyBackend::Core::RegisterNextPrinter)); + return CloudPrintURLFetcher::STOP_PROCESSING; } bool CloudPrintProxyBackend::Core::RemovePrinterFromList( diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.h b/chrome/service/cloud_print/cloud_print_proxy_backend.h index 931fd91..7e82190 100644 --- a/chrome/service/cloud_print/cloud_print_proxy_backend.h +++ b/chrome/service/cloud_print/cloud_print_proxy_backend.h @@ -9,11 +9,10 @@ #include <string> #include "base/thread.h" -#include "chrome/common/net/url_fetcher.h" -#include "googleurl/src/gurl.h" #include "printing/backend/print_backend.h" class CloudPrintProxyService; +class GURL; class DictionaryValue; // CloudPrintProxyFrontend is the interface used by CloudPrintProxyBackend to diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.cc b/chrome/service/cloud_print/cloud_print_url_fetcher.cc new file mode 100644 index 0000000..ff560d8 --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_url_fetcher.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" + +#include "base/values.h" +#include "chrome/common/net/http_return.h" +#include "chrome/common/net/url_fetcher_protect.h" +#include "chrome/service/cloud_print/cloud_print_consts.h" +#include "chrome/service/cloud_print/cloud_print_helpers.h" +#include "chrome/service/net/service_url_request_context.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_status.h" + +CloudPrintURLFetcher::CloudPrintURLFetcher() + : protect_entry_(NULL), num_retries_(0) { +} + +void CloudPrintURLFetcher::StartGetRequest(const GURL& url, + Delegate* delegate, + const std::string& auth_token, + const std::string& retry_policy) { + StartRequestHelper(url, URLFetcher::GET, delegate, auth_token, retry_policy, + std::string(), std::string()); +} + +void CloudPrintURLFetcher::StartPostRequest( + const GURL& url, + Delegate* delegate, + const std::string& auth_token, + const std::string& retry_policy, + const std::string& post_data_mime_type, + const std::string& post_data) { + StartRequestHelper(url, URLFetcher::POST, delegate, auth_token, retry_policy, + post_data_mime_type, post_data); +} + + // URLFetcher::Delegate implementation. +void CloudPrintURLFetcher::OnURLFetchComplete( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + VLOG(1) << "CP_PROXY: OnURLFetchComplete, url: " << url + << ", response code: " << response_code; + // Make sure we stay alive through the body of this function. + scoped_refptr<CloudPrintURLFetcher> keep_alive(this); + ResponseAction action = delegate_->HandleRawResponse(source, + url, + status, + response_code, + cookies, + data); + if (action == CONTINUE_PROCESSING) { + // If there was an auth error, we are done. + if (RC_FORBIDDEN == response_code) { + delegate_->OnRequestAuthError(); + return; + } + // We need to retry on all network errors. + if (!status.is_success() || (response_code != 200)) + action = RETRY_REQUEST; + else + action = delegate_->HandleRawData(source, url, data); + + if (action == CONTINUE_PROCESSING) { + // If the delegate is not interested in handling the raw response data, + // we assume that a JSON response is expected. If we do not get a JSON + // response, we will retry (to handle the case where we got redirected + // to a non-cloudprint-server URL eg. for authentication). + bool succeeded = false; + DictionaryValue* response_dict = NULL; + CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); + if (response_dict) + action = delegate_->HandleJSONData(source, + url, + response_dict, + succeeded); + else + action = RETRY_REQUEST; + } + } + // Retry the request if needed. + if (action == RETRY_REQUEST) { + int64 back_off_time = + protect_entry_->UpdateBackoff(URLFetcherProtectEntry::FAILURE); + ++num_retries_; + int max_retries = protect_entry_->max_retries(); + if ((-1 != max_retries) && (num_retries_ > max_retries)) { + // Retry limit reached. Give up. + delegate_->OnRequestGiveUp(); + } else { + // Either no retry limit specified or retry limit has not yet been + // reached. Try again. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &CloudPrintURLFetcher::StartRequestNow), + back_off_time); + } + } else { + protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SUCCESS); + } +} + +void CloudPrintURLFetcher::StartRequestHelper( + const GURL& url, + URLFetcher::RequestType request_type, + Delegate* delegate, + const std::string& auth_token, + const std::string& retry_policy, + const std::string& post_data_mime_type, + const std::string& post_data) { + DCHECK(delegate); + request_.reset(new URLFetcher(url, request_type, this)); + request_->set_request_context(GetRequestContextGetter()); + // Since we implement our own retry logic, disable the retry in URLFetcher. + request_->set_automatically_retry_on_5xx(false); + delegate_ = delegate; + std::string headers = "Authorization: GoogleLogin auth="; + headers += auth_token; + headers += "\r\n"; + headers += kChromeCloudPrintProxyHeader; + request_->set_extra_request_headers(headers); + if (request_type == URLFetcher::POST) { + request_->set_upload_data(post_data_mime_type, post_data); + } + // Initialize the retry policy for this request. + protect_entry_ = + URLFetcherProtectManager::GetInstance()->Register(retry_policy); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &CloudPrintURLFetcher::StartRequestNow), + protect_entry_->UpdateBackoff(URLFetcherProtectEntry::SEND)); +} + +void CloudPrintURLFetcher::StartRequestNow() { + request_->Start(); +} + +URLRequestContextGetter* CloudPrintURLFetcher::GetRequestContextGetter() { + return new ServiceURLRequestContextGetter(); +} + diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.h b/chrome/service/cloud_print/cloud_print_url_fetcher.h new file mode 100644 index 0000000..5ed69fc --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_url_fetcher.h @@ -0,0 +1,118 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_URL_FETCHER_H_ +#define CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_URL_FETCHER_H_ +#pragma once + +#include <string> + +#include "chrome/common/net/url_fetcher.h" + +class DictionaryValue; +class GURL; +class URLFetcherProtectEntry; +class URLRequestStatus; + +// A wrapper around URLFetcher for CloudPrint. URLFetcher applies retry logic +// only on HTTP response codes >= 500. In the cloud print case, we want to +// retry on all network errors. In addition, we want to treat non-JSON responses +// (for all CloudPrint APIs that expect JSON responses) as errors and they +// must also be retried. Also URLFetcher uses the host name of the URL as the +// key for applying the retry policy. In our case, we want to apply one global +// policy for many requests (not necessarily scoped by hostname). +class CloudPrintURLFetcher + : public base::RefCountedThreadSafe<CloudPrintURLFetcher>, + public URLFetcher::Delegate { + public: + enum ResponseAction { + CONTINUE_PROCESSING, + STOP_PROCESSING, + RETRY_REQUEST, + }; + class Delegate { + public: + virtual ~Delegate() { } + // Override this to handle the raw response as it is available. No response + // error checking is done before this method is called. If the delegate + // returns CONTINUE_PROCESSING, we will then check for network + // errors. Most implementations will not override this. + virtual ResponseAction HandleRawResponse(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + return CONTINUE_PROCESSING; + } + // This will be invoked only if HandleRawResponse returns + // CONTINUE_PROCESSING AND if there are no network errors and the HTTP + // response code is 200. The delegate implementation returns + // CONTINUE_PROCESSING if it does not want to handle the raw data itself. + // Handling the raw data is needed when the expected response is NOT JSON + // (like in the case of a print ticket response or a print job download + // response). + virtual ResponseAction HandleRawData(const URLFetcher* source, + const GURL& url, + const std::string& data) { + return CONTINUE_PROCESSING; + } + // This will be invoked only if HandleRawResponse and HandleRawData return + // CONTINUE_PROCESSING AND if the response contains a valid JSON dictionary. + // |succeeded| is the value of the "success" field in the response JSON. + virtual ResponseAction HandleJSONData(const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + return CONTINUE_PROCESSING; + } + // Invoked when the retry limit for this request has been reached (if there + // was a retry limit - a limit of -1 implies no limit). + virtual void OnRequestGiveUp() { } + // Invoked when the request returns a 403 error (applicable only when + // HandleRawResponse returns CONTINUE_PROCESSING) + virtual void OnRequestAuthError() = 0; + }; + CloudPrintURLFetcher(); + + void StartGetRequest(const GURL& url, + Delegate* delegate, + const std::string& auth_token, + const std::string& retry_policy); + void StartPostRequest(const GURL& url, + Delegate* delegate, + const std::string& auth_token, + const std::string& retry_policy, + const std::string& post_data_mime_type, + const std::string& post_data); + + // URLFetcher::Delegate implementation. + virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + protected: + // Virtual for testing. + virtual URLRequestContextGetter* GetRequestContextGetter(); + + private: + void StartRequestHelper(const GURL& url, + URLFetcher::RequestType request_type, + Delegate* delegate, + const std::string& auth_token, + const std::string& retry_policy, + const std::string& post_data_mime_type, + const std::string& post_data); + void StartRequestNow(); + + scoped_ptr<URLFetcher> request_; + Delegate* delegate_; + URLFetcherProtectEntry* protect_entry_; + int num_retries_; +}; + +typedef CloudPrintURLFetcher::Delegate CloudPrintURLFetcherDelegate; + +#endif // CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_URL_FETCHER_H_ diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc new file mode 100644 index 0000000..94684be --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc @@ -0,0 +1,353 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" +#include "base/message_loop_proxy.h" +#include "base/ref_counted.h" +#include "base/thread.h" +#include "base/waitable_event.h" +#include "chrome/common/net/url_fetcher_protect.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "chrome/service/service_process.h" +#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" +#include "googleurl/src/gurl.h" +#include "net/test/test_server.h" +#include "net/url_request/url_request_unittest.h" +#include "net/url_request/url_request_status.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; + +namespace { + +const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data"); + +int g_request_context_getter_instances = 0; +class TestURLRequestContextGetter : public URLRequestContextGetter { + public: + explicit TestURLRequestContextGetter( + base::MessageLoopProxy* io_message_loop_proxy) + : io_message_loop_proxy_(io_message_loop_proxy) { + g_request_context_getter_instances++; + } + virtual URLRequestContext* GetURLRequestContext() { + if (!context_) + context_ = new TestURLRequestContext(); + return context_; + } + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { + return io_message_loop_proxy_; + } + + protected: + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + + private: + ~TestURLRequestContextGetter() { + g_request_context_getter_instances--; + } + + scoped_refptr<URLRequestContext> context_; +}; + +class TestCloudPrintURLFetcher : public CloudPrintURLFetcher { + public: + explicit TestCloudPrintURLFetcher( + base::MessageLoopProxy* io_message_loop_proxy) + : io_message_loop_proxy_(io_message_loop_proxy) { + } + + virtual URLRequestContextGetter* GetRequestContextGetter() { + return new TestURLRequestContextGetter(io_message_loop_proxy_.get()); + } + private: + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; +}; + +class CloudPrintURLFetcherTest : public testing::Test, + public CloudPrintURLFetcher::Delegate { + public: + CloudPrintURLFetcherTest() : fetcher_(NULL) { } + + // Creates a URLFetcher, using the program's main thread to do IO. + virtual void CreateFetcher(const GURL& url, const std::string& retry_policy); + + // CloudPrintURLFetcher::Delegate + virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + virtual void OnRequestAuthError() { + ADD_FAILURE(); + } + + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() { + return io_message_loop_proxy_; + } + + protected: + virtual void SetUp() { + testing::Test::SetUp(); + + io_message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); + } + + virtual void TearDown() { + fetcher_ = NULL; + // Deleting the fetcher causes a task to be posted to the IO thread to + // release references to the URLRequestContextGetter. We need to run all + // pending tasks to execute that (this is the IO thread). + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(0, g_request_context_getter_instances); + } + + // URLFetcher is designed to run on the main UI thread, but in our tests + // we assume that the current thread is the IO thread where the URLFetcher + // dispatches its requests to. When we wish to simulate being used from + // a UI thread, we dispatch a worker thread to do so. + MessageLoopForIO io_loop_; + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + std::string retry_policy_; + Time start_time_; + scoped_refptr<CloudPrintURLFetcher> fetcher_; +}; + +class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest { + public: + CloudPrintURLFetcherBasicTest() + : handle_raw_response_(false), handle_raw_data_(false) { } + // CloudPrintURLFetcher::Delegate + virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + virtual CloudPrintURLFetcher::ResponseAction HandleRawData( + const URLFetcher* source, + const GURL& url, + const std::string& data); + + virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + void SetHandleRawResponse(bool handle_raw_response) { + handle_raw_response_ = handle_raw_response; + } + void SetHandleRawData(bool handle_raw_data) { + handle_raw_data_ = handle_raw_data; + } + private: + bool handle_raw_response_; + bool handle_raw_data_; +}; + +// Version of CloudPrintURLFetcherTest that tests overload protection. +class CloudPrintURLFetcherOverloadTest : public CloudPrintURLFetcherTest { + public: + CloudPrintURLFetcherOverloadTest() : response_count_(0) { + } + + // CloudPrintURLFetcher::Delegate + virtual CloudPrintURLFetcher::ResponseAction HandleRawData( + const URLFetcher* source, + const GURL& url, + const std::string& data); + + private: + int response_count_; +}; + +// Version of CloudPrintURLFetcherTest that tests backoff protection. +class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest { + public: + CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) { + } + + // CloudPrintURLFetcher::Delegate + virtual CloudPrintURLFetcher::ResponseAction HandleRawData( + const URLFetcher* source, + const GURL& url, + const std::string& data); + + virtual void OnRequestGiveUp(); + + private: + int response_count_; +}; + + +void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url, + const std::string& retry_policy) { + fetcher_ = new TestCloudPrintURLFetcher(io_message_loop_proxy()); + retry_policy_ = retry_policy; + start_time_ = Time::Now(); + fetcher_->StartGetRequest(url, this, "", retry_policy_); +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintURLFetcherTest::HandleRawResponse( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + EXPECT_TRUE(status.is_success()); + EXPECT_EQ(200, response_code); // HTTP OK + EXPECT_FALSE(data.empty()); + return CloudPrintURLFetcher::CONTINUE_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintURLFetcherBasicTest::HandleRawResponse( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + EXPECT_TRUE(status.is_success()); + EXPECT_EQ(200, response_code); // HTTP OK + EXPECT_FALSE(data.empty()); + + if (handle_raw_response_) { + // If the current message loop is not the IO loop, it will be shut down when + // the main loop returns and this thread subsequently goes out of scope. + io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + return CloudPrintURLFetcher::STOP_PROCESSING; + } + return CloudPrintURLFetcher::CONTINUE_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintURLFetcherBasicTest::HandleRawData( + const URLFetcher* source, + const GURL& url, + const std::string& data) { + // We should never get here if we returned true in HandleRawResponse + EXPECT_FALSE(handle_raw_response_); + if (handle_raw_data_) { + io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + return CloudPrintURLFetcher::STOP_PROCESSING; + } + return CloudPrintURLFetcher::CONTINUE_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintURLFetcherBasicTest::HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + // We should never get here if we returned true in one of the above methods. + EXPECT_FALSE(handle_raw_response_); + EXPECT_FALSE(handle_raw_data_); + io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintURLFetcherOverloadTest::HandleRawData(const URLFetcher* source, + const GURL& url, + const std::string& data) { + const TimeDelta one_second = TimeDelta::FromMilliseconds(1000); + response_count_++; + if (response_count_ < 20) { + fetcher_->StartGetRequest(url, this, "", retry_policy_); + } else { + // We have already sent 20 requests continuously. And we expect that + // it takes more than 1 second due to the overload pretection settings. + EXPECT_TRUE(Time::Now() - start_time_ >= one_second); + io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintURLFetcherRetryBackoffTest::HandleRawData(const URLFetcher* source, + const GURL& url, + const std::string& data) { + response_count_++; + // First attempt + 11 retries = 12 total responses. + EXPECT_LE(response_count_, 12); + return CloudPrintURLFetcher::RETRY_REQUEST; +} + +void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() { + const TimeDelta one_second = TimeDelta::FromMilliseconds(1000); + // It takes more than 1 second to finish all 11 requests. + EXPECT_TRUE(Time::Now() - start_time_ >= one_second); + io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) { + net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); + ASSERT_TRUE(test_server.Start()); + SetHandleRawResponse(true); + + CreateFetcher(test_server.GetURL("echo"), "DummyRetryPolicy"); + MessageLoop::current()->Run(); +} + +TEST_F(CloudPrintURLFetcherBasicTest, HandleRawData) { + net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); + ASSERT_TRUE(test_server.Start()); + + SetHandleRawData(true); + CreateFetcher(test_server.GetURL("echo"), "DummyRetryPolicy"); + MessageLoop::current()->Run(); +} + +TEST_F(CloudPrintURLFetcherOverloadTest, Protect) { + net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); + ASSERT_TRUE(test_server.Start()); + + GURL url(test_server.GetURL("defaultresponse")); + + // Registers an entry for test url. It only allows 3 requests to be sent + // in 200 milliseconds. + std::string retry_policy = "OverloadTestPolicy"; + URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); + URLFetcherProtectEntry* entry = + new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256); + manager->Register(retry_policy, entry); + + CreateFetcher(url, retry_policy); + + MessageLoop::current()->Run(); +} + +TEST_F(CloudPrintURLFetcherRetryBackoffTest, GiveUp) { + net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); + ASSERT_TRUE(test_server.Start()); + + GURL url(test_server.GetURL("defaultresponse")); + + // 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. + std::string retry_policy = "BackoffTestPolicy"; + URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); + URLFetcherProtectEntry* entry = + new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256); + manager->Register(retry_policy, entry); + + CreateFetcher(url, retry_policy); + + MessageLoop::current()->Run(); +} + +} // namespace. diff --git a/chrome/service/cloud_print/job_status_updater.cc b/chrome/service/cloud_print/job_status_updater.cc index 1f36b2a..4719a6d 100644 --- a/chrome/service/cloud_print/job_status_updater.cc +++ b/chrome/service/cloud_print/job_status_updater.cc @@ -58,45 +58,36 @@ void JobStatusUpdater::UpdateStatus() { } } if (need_update) { - GURL update_url = CloudPrintHelpers::GetUrlForJobStatusUpdate( - cloud_print_server_url_, job_id_, last_job_details_); - request_.reset(new URLFetcher(update_url, URLFetcher::GET, this)); - CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); - request_->Start(); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest( + CloudPrintHelpers::GetUrlForJobStatusUpdate( + cloud_print_server_url_, job_id_, last_job_details_), + this, auth_token_, kCloudPrintAPIRetryPolicy); } } } void JobStatusUpdater::Stop() { - request_.reset(); + request_ = NULL; DCHECK(delegate_); stopped_ = true; delegate_->OnJobCompleted(this); } -// URLFetcher::Delegate implementation. -void JobStatusUpdater::OnURLFetchComplete(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { - // If there was an auth error, we are done. - if (RC_FORBIDDEN == response_code) { - if (delegate_) { - delegate_->OnAuthError(); - } - return; - } - int64 next_update_interval = kJobStatusUpdateInterval; - if (!status.is_success() || (response_code != 200)) { - next_update_interval *= 10; - MessageLoop::current()->PostDelayedTask( - FROM_HERE, NewRunnableMethod(this, &JobStatusUpdater::UpdateStatus), - next_update_interval); - } else if (last_job_details_.status == - cloud_print::PRINT_JOB_STATUS_COMPLETED) { +// CloudPrintURLFetcher::Delegate implementation. +CloudPrintURLFetcher::ResponseAction JobStatusUpdater::HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + if (last_job_details_.status == cloud_print::PRINT_JOB_STATUS_COMPLETED) { MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &JobStatusUpdater::Stop)); } + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +void JobStatusUpdater::OnRequestAuthError() { + if (delegate_) + delegate_->OnAuthError(); } diff --git a/chrome/service/cloud_print/job_status_updater.h b/chrome/service/cloud_print/job_status_updater.h index 6e111cd..1cf4c0b 100644 --- a/chrome/service/cloud_print/job_status_updater.h +++ b/chrome/service/cloud_print/job_status_updater.h @@ -11,8 +11,8 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/thread.h" +#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" #include "chrome/service/cloud_print/print_system.h" -#include "chrome/common/net/url_fetcher.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request_status.h" @@ -21,7 +21,7 @@ // object releases the reference to itself which should cause it to // self-destruct. class JobStatusUpdater : public base::RefCountedThreadSafe<JobStatusUpdater>, - public URLFetcher::Delegate { + public CloudPrintURLFetcherDelegate { public: class Delegate { public: @@ -45,19 +45,20 @@ class JobStatusUpdater : public base::RefCountedThreadSafe<JobStatusUpdater>, void UpdateStatus(); void Stop(); - // URLFetcher::Delegate implementation. - virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); + // CloudPrintURLFetcher::Delegate implementation. + virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + virtual void OnRequestAuthError(); private: std::string printer_name_; std::string job_id_; cloud_print::PlatformJobId local_job_id_; cloud_print::PrintJobDetails last_job_details_; - scoped_ptr<URLFetcher> request_; + scoped_refptr<CloudPrintURLFetcher> request_; std::string auth_token_; GURL cloud_print_server_url_; scoped_refptr<cloud_print::PrintSystem> print_system_; diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc index 9d4f46f..f351c5c 100644 --- a/chrome/service/cloud_print/printer_job_handler.cc +++ b/chrome/service/cloud_print/printer_job_handler.cc @@ -31,8 +31,8 @@ PrinterJobHandler::PrinterJobHandler( cloud_print_server_url_(cloud_print_server_url), delegate_(delegate), local_job_id_(-1), - next_response_handler_(NULL), - next_failure_handler_(NULL), + next_json_data_handler_(NULL), + next_data_handler_(NULL), server_error_count_(0), print_thread_("Chrome_CloudPrintJobPrintThread"), job_handler_message_loop_proxy_( @@ -66,7 +66,7 @@ PrinterJobHandler::~PrinterJobHandler() { void PrinterJobHandler::Reset() { print_data_url_.clear(); job_details_.Clear(); - request_.reset(); + request_ = NULL; print_thread_.Stop(); } @@ -86,11 +86,12 @@ void PrinterJobHandler::Start() { if (printer_delete_pending_) { printer_delete_pending_ = false; task_in_progress_ = true; - MakeServerRequest( + SetNextJSONHandler(&PrinterJobHandler::HandlePrinterDeleteResponse); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest( CloudPrintHelpers::GetUrlForPrinterDelete( cloud_print_server_url_, printer_info_cloud_.printer_id), - &PrinterJobHandler::HandlePrinterDeleteResponse, - &PrinterJobHandler::Stop); + this, auth_token_, kCloudPrintAPIRetryPolicy); } if (!task_in_progress_ && printer_update_pending_) { printer_update_pending_ = false; @@ -100,11 +101,12 @@ void PrinterJobHandler::Start() { task_in_progress_ = true; server_job_available_ = false; // We need to fetch any pending jobs for this printer - MakeServerRequest( + SetNextJSONHandler(&PrinterJobHandler::HandleJobMetadataResponse); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest( CloudPrintHelpers::GetUrlForJobFetch( cloud_print_server_url_, printer_info_cloud_.printer_id), - &PrinterJobHandler::HandleJobMetadataResponse, - &PrinterJobHandler::Stop); + this, auth_token_, kCloudPrintAPIRetryPolicy); } } } @@ -199,50 +201,47 @@ bool PrinterJobHandler::UpdatePrinterInfo() { post_data.append("--" + mime_boundary + "--\r\n"); std::string mime_type("multipart/form-data; boundary="); mime_type += mime_boundary; - request_.reset( - new URLFetcher(CloudPrintHelpers::GetUrlForPrinterUpdate( - cloud_print_server_url_, - printer_info_cloud_.printer_id), - URLFetcher::POST, this)); - CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); - request_->set_upload_data(mime_type, post_data); - next_response_handler_ = &PrinterJobHandler::HandlePrinterUpdateResponse; - next_failure_handler_ = &PrinterJobHandler::Stop; - request_->Start(); + SetNextJSONHandler(&PrinterJobHandler::HandlePrinterUpdateResponse); + request_ = new CloudPrintURLFetcher; + request_->StartPostRequest( + CloudPrintHelpers::GetUrlForPrinterUpdate( + cloud_print_server_url_, printer_info_cloud_.printer_id), + this, auth_token_, kCloudPrintAPIRetryPolicy, mime_type, post_data); ret = true; } return ret; } -// URLFetcher::Delegate implementation. -void PrinterJobHandler::OnURLFetchComplete( +// CloudPrintURLFetcher::Delegate implementation. +CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleRawData( const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, const std::string& data) { - VLOG(1) << "CP_PROXY: Printer job handler, OnURLFetchComplete, url: " << url - << ", response code: " << response_code; - // If there was an auth error, we are done. - if (RC_FORBIDDEN == response_code) { - OnAuthError(); - return; - } - if (!shutting_down_) { - DCHECK(source == request_.get()); - // We need a next response handler because we are strictly a sequential - // state machine. We need each response handler to tell us which state to - // advance to next. - DCHECK(next_response_handler_); - if (!(this->*next_response_handler_)(source, url, status, - response_code, cookies, data)) { - // By contract, if the response handler returns false, it wants us to - // retry the request (upto the usual limit after which we give up and - // send the state machine to the Stop state); - HandleServerError(url); - } - } + if (!next_data_handler_) + return CloudPrintURLFetcher::CONTINUE_PROCESSING; + return (this->*next_data_handler_)(source, url, data); +} + +CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + DCHECK(next_json_data_handler_); + return (this->*next_json_data_handler_)(source, + url, + json_data, + succeeded); +} + +void PrinterJobHandler::OnRequestGiveUp() { + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PrinterJobHandler::Stop)); +} + +void PrinterJobHandler::OnRequestAuthError() { + OnAuthError(); } // JobStatusUpdater::Delegate implementation @@ -291,163 +290,101 @@ void PrinterJobHandler::OnJobChanged() { } } -bool PrinterJobHandler::HandlePrinterUpdateResponse( +// Begin Response handlers +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandlePrinterUpdateResponse( const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { - bool ret = false; + DictionaryValue* json_data, + bool succeeded) { VLOG(1) << "CP_PROXY: Handle printer update response, id: " << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (status.is_success() && (response_code == 200)) { - bool succeeded = false; - DictionaryValue* response_dict = NULL; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); - // If we get valid JSON back, we are done. - if (NULL != response_dict) { - ret = true; - } - } - if (ret) { - // We are done here. Go to the Stop state - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); - } else { - // Since we failed to update the server, set the flag again. - printer_update_pending_ = true; - } - return ret; + // We are done here. Go to the Stop state + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); + return CloudPrintURLFetcher::STOP_PROCESSING; } -bool PrinterJobHandler::HandlePrinterDeleteResponse( +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandlePrinterDeleteResponse( const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { - bool ret = false; + DictionaryValue* json_data, + bool succeeded) { VLOG(1) << "CP_PROXY: Handler printer delete response, id: " << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (status.is_success() && (response_code == 200)) { - bool succeeded = false; - DictionaryValue* response_dict = NULL; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); - // If we get valid JSON back, we are done. - if (NULL != response_dict) { - ret = true; - } - } - if (ret) { - // The printer has been deleted. Shutdown the handler class. - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Shutdown)); - } else { - // Since we failed to update the server, set the flag again. - printer_delete_pending_ = true; - } - return ret; + // The printer has been deleted. Shutdown the handler class. + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Shutdown)); + return CloudPrintURLFetcher::STOP_PROCESSING; } -bool PrinterJobHandler::HandleJobMetadataResponse( +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandleJobMetadataResponse( const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { + DictionaryValue* json_data, + bool succeeded) { VLOG(1) << "CP_PROXY: Handle job metadata response, id: " << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (!status.is_success() || (response_code != 200)) { - return false; - } - bool succeeded = false; - DictionaryValue* response_dict = NULL; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); - if (NULL == response_dict) { - // If we did not get a valid JSON response, we need to retry. - return false; - } - Task* next_task = NULL; + bool job_available = false; if (succeeded) { ListValue* job_list = NULL; - response_dict->GetList(kJobListValue, &job_list); + json_data->GetList(kJobListValue, &job_list); if (job_list) { // Even though it is a job list, for now we are only interested in the // first job DictionaryValue* job_data = NULL; if (job_list->GetDictionary(0, &job_data)) { + job_available = true; job_data->GetString(kIdValue, &job_details_.job_id_); job_data->GetString(kTitleValue, &job_details_.job_title_); std::string print_ticket_url; job_data->GetString(kTicketUrlValue, &print_ticket_url); job_data->GetString(kFileUrlValue, &print_data_url_); - next_task = NewRunnableMethod( - this, &PrinterJobHandler::MakeServerRequest, - GURL(print_ticket_url.c_str()), - &PrinterJobHandler::HandlePrintTicketResponse, - &PrinterJobHandler::FailedFetchingJobData); + SetNextDataHandler(&PrinterJobHandler::HandlePrintTicketResponse); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest(GURL(print_ticket_url.c_str()), + this, + auth_token_, + kCloudPrintAPIRetryPolicy); } } } - if (!next_task) { - // If we got a valid JSON but there were no jobs, we are done - next_task = NewRunnableMethod(this, &PrinterJobHandler::Stop); - } - delete response_dict; - DCHECK(next_task); - MessageLoop::current()->PostTask(FROM_HERE, next_task); - return true; + // If no jobs are available, go to the Stop state. + if (!job_available) + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); + return CloudPrintURLFetcher::STOP_PROCESSING; } -bool PrinterJobHandler::HandlePrintTicketResponse( - const URLFetcher* source, const GURL& url, const URLRequestStatus& status, - int response_code, const ResponseCookies& cookies, - const std::string& data) { +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandlePrintTicketResponse(const URLFetcher* source, + const GURL& url, + const std::string& data) { VLOG(1) << "CP_PROXY: Handle print ticket response, id: " << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (!status.is_success() || (response_code != 200)) { - return false; - } if (print_system_->ValidatePrintTicket(printer_info_.printer_name, data)) { job_details_.print_ticket_ = data; - MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &PrinterJobHandler::MakeServerRequest, - GURL(print_data_url_.c_str()), - &PrinterJobHandler::HandlePrintDataResponse, - &PrinterJobHandler::FailedFetchingJobData)); + SetNextDataHandler(&PrinterJobHandler::HandlePrintDataResponse); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest(GURL(print_data_url_.c_str()), + this, + auth_token_, + kJobDataRetryPolicy); } else { // The print ticket was not valid. We are done here. FailedFetchingJobData(); } - return true; + return CloudPrintURLFetcher::STOP_PROCESSING; } -bool PrinterJobHandler::HandlePrintDataResponse(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandlePrintDataResponse(const URLFetcher* source, + const GURL& url, + const std::string& data) { VLOG(1) << "CP_PROXY: Handle print data response, id: " << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (!status.is_success() || (response_code != 200)) { - return false; - } Task* next_task = NULL; if (file_util::CreateTemporaryFile(&job_details_.print_data_file_path_)) { int ret = file_util::WriteFile(job_details_.print_data_file_path_, @@ -467,13 +404,54 @@ bool PrinterJobHandler::HandlePrintDataResponse(const URLFetcher* source, JOB_DOWNLOAD_FAILED); } MessageLoop::current()->PostTask(FROM_HERE, next_task); - return true; + return CloudPrintURLFetcher::STOP_PROCESSING; } +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandleSuccessStatusUpdateResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + VLOG(1) << "CP_PROXY: Handle success status update response, id: " + << printer_info_cloud_.printer_id; + // The print job has been spooled locally. We now need to create an object + // that monitors the status of the job and updates the server. + scoped_refptr<JobStatusUpdater> job_status_updater = + new JobStatusUpdater(printer_info_.printer_name, job_details_.job_id_, + local_job_id_, auth_token_, cloud_print_server_url_, + print_system_.get(), this); + job_status_updater_list_.push_back(job_status_updater); + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(job_status_updater.get(), + &JobStatusUpdater::UpdateStatus)); + if (succeeded) { + // Since we just printed successfully, we want to look for more jobs. + server_job_available_ = true; + } + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +PrinterJobHandler::HandleFailureStatusUpdateResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + VLOG(1) << "CP_PROXY: Handle failure status update response, id: " + << printer_info_cloud_.printer_id; + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); + return CloudPrintURLFetcher::STOP_PROCESSING; +} +// End Response handlers + void PrinterJobHandler::StartPrinting() { VLOG(1) << "CP_PROXY: Start printing, id: " << printer_info_cloud_.printer_id; // We are done with the request object for now. - request_.reset(); + request_ = NULL; if (!shutting_down_) { if (!print_thread_.Start()) { JobFailed(PRINT_FAILED); @@ -519,17 +497,6 @@ void PrinterJobHandler::Shutdown() { } } -void PrinterJobHandler::HandleServerError(const GURL& url) { - VLOG(1) << "CP_PROXY: Handle server error, printer id: " - << printer_info_cloud_.printer_id << ", url: " << url; - Task* task_to_retry = NewRunnableMethod(this, - &PrinterJobHandler::FetchURL, url); - Task* task_on_give_up = NewRunnableMethod(this, next_failure_handler_); - CloudPrintHelpers::HandleServerError(&server_error_count_, kMaxRetryCount, - -1, kBaseRetryInterval, task_to_retry, - task_on_give_up); -} - void PrinterJobHandler::UpdateJobStatus(cloud_print::PrintJobStatus status, PrintJobError error) { VLOG(1) << "CP_PROXY: Update job status, id: " @@ -538,98 +505,33 @@ void PrinterJobHandler::UpdateJobStatus(cloud_print::PrintJobStatus status, if (!job_details_.job_id_.empty()) { VLOG(1) << "CP_PROXY: Updating status, job id: " << job_details_.job_id_ << ", status: " << status; - - ResponseHandler response_handler = NULL; if (error == SUCCESS) { - response_handler = - &PrinterJobHandler::HandleSuccessStatusUpdateResponse; + SetNextJSONHandler( + &PrinterJobHandler::HandleSuccessStatusUpdateResponse); } else { - response_handler = - &PrinterJobHandler::HandleFailureStatusUpdateResponse; + SetNextJSONHandler( + &PrinterJobHandler::HandleFailureStatusUpdateResponse); } - MakeServerRequest( + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest( CloudPrintHelpers::GetUrlForJobStatusUpdate(cloud_print_server_url_, job_details_.job_id_, status), - response_handler, - &PrinterJobHandler::Stop); + this, + auth_token_, + kCloudPrintAPIRetryPolicy); } } } -bool PrinterJobHandler::HandleSuccessStatusUpdateResponse( - const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { - VLOG(1) << "CP_PROXY: Handle success status update response, id: " - << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (!status.is_success() || (response_code != 200)) { - return false; - } - // The print job has been spooled locally. We now need to create an object - // that monitors the status of the job and updates the server. - scoped_refptr<JobStatusUpdater> job_status_updater = - new JobStatusUpdater(printer_info_.printer_name, job_details_.job_id_, - local_job_id_, auth_token_, cloud_print_server_url_, - print_system_.get(), this); - job_status_updater_list_.push_back(job_status_updater); - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(job_status_updater.get(), - &JobStatusUpdater::UpdateStatus)); - bool succeeded = false; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, NULL); - if (succeeded) { - // Since we just printed successfully, we want to look for more jobs. - server_job_available_ = true; - } - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); - return true; -} - -bool PrinterJobHandler::HandleFailureStatusUpdateResponse( - const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { - VLOG(1) << "CP_PROXY: Handle failure status update response, id: " - << printer_info_cloud_.printer_id; - // If there was a network error or a non-200 response (which, for our purposes - // is the same as a network error), we want to retry. - if (!status.is_success() || (response_code != 200)) { - return false; - } - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); - return true; -} - -void PrinterJobHandler::MakeServerRequest(const GURL& url, - ResponseHandler response_handler, - FailureHandler failure_handler) { - VLOG(1) << "CP_PROXY: Printer job handle, make server request, id: " - << printer_info_cloud_.printer_id << ", url: " << url; - if (!shutting_down_) { - server_error_count_ = 0; - // Set up the next response handler - next_response_handler_ = response_handler; - next_failure_handler_ = failure_handler; - FetchURL(url); - } +void PrinterJobHandler::SetNextJSONHandler(JSONDataHandler handler) { + next_json_data_handler_ = handler; + next_data_handler_ = NULL; } -void PrinterJobHandler::FetchURL(const GURL& url) { - VLOG(1) << "CP_PROXY: PrinterJobHandler::FetchURL, url: " << url; - request_.reset(new URLFetcher(url, URLFetcher::GET, this)); - CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); - request_->Start(); +void PrinterJobHandler::SetNextDataHandler(DataHandler handler) { + next_data_handler_ = handler; + next_json_data_handler_ = NULL; } bool PrinterJobHandler::HavePendingTasks() { diff --git a/chrome/service/cloud_print/printer_job_handler.h b/chrome/service/cloud_print/printer_job_handler.h index 02c7be3..3290581 100644 --- a/chrome/service/cloud_print/printer_job_handler.h +++ b/chrome/service/cloud_print/printer_job_handler.h @@ -13,12 +13,13 @@ #include "base/ref_counted.h" #include "base/message_loop_proxy.h" #include "base/thread.h" +#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" #include "chrome/service/cloud_print/job_status_updater.h" -#include "chrome/common/net/url_fetcher.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request_status.h" #include "printing/backend/print_backend.h" +class URLFetcher; // A class that handles cloud print jobs for a particular printer. This class // imlements a state machine that transitions from Start to various states. The // various states are shown in the below diagram. @@ -58,10 +59,8 @@ // Stop // (If there are pending tasks go back to Start) -typedef URLFetcher::Delegate URLFetcherDelegate; - class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, - public URLFetcherDelegate, + public CloudPrintURLFetcherDelegate, public JobStatusUpdaterDelegate, public cloud_print::PrinterWatcherDelegate, public cloud_print::JobSpoolerDelegate { @@ -120,12 +119,19 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, // Begin Delegate implementations - // URLFetcher::Delegate implementation. - virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); + // CloudPrintURLFetcher::Delegate implementation. + virtual CloudPrintURLFetcher::ResponseAction HandleRawData( + const URLFetcher* source, + const GURL& url, + const std::string& data); + virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + virtual void OnRequestGiveUp(); + virtual void OnRequestAuthError(); + // JobStatusUpdater::Delegate implementation virtual bool OnJobCompleted(JobStatusUpdater* updater); virtual void OnAuthError(); @@ -143,55 +149,57 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, // End Delegate implementations private: - // Prototype for a response handler. The return value indicates whether the - // request should be retried, false means "retry", true means "do not retry" - typedef bool (PrinterJobHandler::*ResponseHandler)( - const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, int response_code, - const ResponseCookies& cookies, const std::string& data); - // Prototype for a failure handler. This handler will be executed if all - // attempts to fetch url failed. - typedef void (PrinterJobHandler::*FailureHandler)(); + // Prototype for a JSON data handler. + typedef CloudPrintURLFetcher::ResponseAction + (PrinterJobHandler::*JSONDataHandler)(const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + // Prototype for a data handler. + typedef CloudPrintURLFetcher::ResponseAction + (PrinterJobHandler::*DataHandler)(const URLFetcher* source, + const GURL& url, + const std::string& data); // Begin request handlers for each state in the state machine - bool HandlePrinterUpdateResponse(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - bool HandlePrinterDeleteResponse(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - bool HandleJobMetadataResponse(const URLFetcher* source, const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - bool HandlePrintTicketResponse(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - bool HandlePrintDataResponse(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - bool HandleSuccessStatusUpdateResponse(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - bool HandleFailureStatusUpdateResponse(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); + CloudPrintURLFetcher::ResponseAction HandlePrinterUpdateResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandlePrinterDeleteResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandleJobMetadataResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandlePrintTicketResponse( + const URLFetcher* source, + const GURL& url, + const std::string& data); + + CloudPrintURLFetcher::ResponseAction HandlePrintDataResponse( + const URLFetcher* source, + const GURL& url, + const std::string& data); + + CloudPrintURLFetcher::ResponseAction HandleSuccessStatusUpdateResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandleFailureStatusUpdateResponse( + const URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); // End request handlers for each state in the state machine // Start the state machine. Based on the flags set this could mean updating @@ -208,16 +216,10 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, void Reset(); void UpdateJobStatus(cloud_print::PrintJobStatus status, PrintJobError error); - // This function should be used to go from one state to another. It will - // retry to fetch url (up to a limit) if response handler will return false. - // Calling this function will zero failure counter. - void MakeServerRequest(const GURL& url, - ResponseHandler response_handler, - FailureHandler failure_handler); - // This function should be used ONLY from MakeServerRequest and - // HandleServerError. It is using stored handlers and failure counter - // to decide which handler to call. - void FetchURL(const GURL& url); + // Sets the next response handler to the specifed JSON data handler. + void SetNextJSONHandler(JSONDataHandler handler); + // Sets the next response handler to the specifed data handler. + void SetNextDataHandler(DataHandler handler); void JobFailed(PrintJobError error); void JobSpooled(cloud_print::PlatformJobId local_job_id); @@ -230,7 +232,7 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, void DoPrint(const JobDetails& job_details, const std::string& printer_name); - scoped_ptr<URLFetcher> request_; + scoped_refptr<CloudPrintURLFetcher> request_; scoped_refptr<cloud_print::PrintSystem> print_system_; printing::PrinterBasicInfo printer_info_; PrinterInfoFromCloud printer_info_cloud_; @@ -242,8 +244,11 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, // Once the job has been spooled to the local spooler, this specifies the // job id of the job on the local spooler. cloud_print::PlatformJobId local_job_id_; - ResponseHandler next_response_handler_; - FailureHandler next_failure_handler_; + + // The next response handler can either be a JSONDataHandler or a + // DataHandler (depending on the current request being made). + JSONDataHandler next_json_data_handler_; + DataHandler next_data_handler_; // The number of consecutive times that connecting to the server failed. int server_error_count_; // The thread on which the actual print operation happens |