summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorsanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-29 22:02:50 +0000
committersanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-29 22:02:50 +0000
commit130d0a44d678b53bed5678de48a87834d193e27f (patch)
tree89691c2dbe2105c5fde2b011e1874e5071035a29 /chrome
parent591a792d1a074fefdb9321f547b9209ebfc9e85a (diff)
downloadchromium_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.gyp2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/service/cloud_print/cloud_print_consts.cc4
-rw-r--r--chrome/service/cloud_print/cloud_print_consts.h13
-rw-r--r--chrome/service/cloud_print/cloud_print_helpers.cc42
-rw-r--r--chrome/service/cloud_print/cloud_print_helpers.h23
-rw-r--r--chrome/service/cloud_print/cloud_print_proxy_backend.cc300
-rw-r--r--chrome/service/cloud_print/cloud_print_proxy_backend.h3
-rw-r--r--chrome/service/cloud_print/cloud_print_url_fetcher.cc146
-rw-r--r--chrome/service/cloud_print/cloud_print_url_fetcher.h118
-rw-r--r--chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc353
-rw-r--r--chrome/service/cloud_print/job_status_updater.cc47
-rw-r--r--chrome/service/cloud_print/job_status_updater.h19
-rw-r--r--chrome/service/cloud_print/printer_job_handler.cc402
-rw-r--r--chrome/service/cloud_print/printer_job_handler.h147
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