summaryrefslogtreecommitdiffstats
path: root/cloud_print
diff options
context:
space:
mode:
authormaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-26 11:24:40 +0000
committermaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-26 11:24:40 +0000
commit12bfcb19c36e0afb3f445de1464b836b549647c6 (patch)
tree93e7e545c34eb3cd41220ece73e4036f3a06f271 /cloud_print
parent9e93a9be5f8ef5e623e8d836c00e2cd6216173e5 (diff)
downloadchromium_src-12bfcb19c36e0afb3f445de1464b836b549647c6.zip
chromium_src-12bfcb19c36e0afb3f445de1464b836b549647c6.tar.gz
chromium_src-12bfcb19c36e0afb3f445de1464b836b549647c6.tar.bz2
GCP2.0 Device: Receiving printjobs.
New request (with timeouts). BUG= Review URL: https://chromiumcodereview.appspot.com/19866002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213839 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print')
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_request.cc118
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_request.h79
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_requester.cc293
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_requester.h117
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_response_parser.cc92
-rw-r--r--cloud_print/gcp20/prototype/cloud_print_response_parser.h32
-rw-r--r--cloud_print/gcp20/prototype/dns_sd_server.h6
-rw-r--r--cloud_print/gcp20/prototype/gcp20_device.gyp4
-rw-r--r--cloud_print/gcp20/prototype/print_job_handler.cc63
-rw-r--r--cloud_print/gcp20/prototype/print_job_handler.h27
-rw-r--r--cloud_print/gcp20/prototype/printer.cc145
-rw-r--r--cloud_print/gcp20/prototype/printer.h50
-rw-r--r--cloud_print/gcp20/prototype/privet_http_server.cc3
-rw-r--r--cloud_print/gcp20/prototype/privet_http_server.h3
14 files changed, 880 insertions, 152 deletions
diff --git a/cloud_print/gcp20/prototype/cloud_print_request.cc b/cloud_print/gcp20/prototype/cloud_print_request.cc
new file mode 100644
index 0000000..2d36dcc
--- /dev/null
+++ b/cloud_print/gcp20/prototype/cloud_print_request.cc
@@ -0,0 +1,118 @@
+// Copyright 2013 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 "cloud_print/gcp20/prototype/cloud_print_request.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using net::URLFetcher;
+using base::MessageLoop;
+
+namespace {
+
+const uint32 kDefaultTimeout = 20; // in seconds
+
+} // namespace
+
+CloudPrintRequest::CloudPrintRequest(const GURL& url,
+ URLFetcher::RequestType method,
+ Delegate* delegate)
+ : fetcher_(URLFetcher::Create(url, method, this)),
+ delegate_(delegate) {
+ int load_flags = fetcher_->GetLoadFlags();
+ load_flags |= net::LOAD_DO_NOT_SEND_COOKIES;
+ load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES;
+ fetcher_->SetLoadFlags(load_flags);
+
+ fetcher_->AddExtraRequestHeader("X-CloudPrint-Proxy: \"\"");
+}
+
+CloudPrintRequest::~CloudPrintRequest() {
+}
+
+scoped_ptr<CloudPrintRequest> CloudPrintRequest::CreateGet(
+ const GURL& url,
+ Delegate* delegate) {
+ return scoped_ptr<CloudPrintRequest>(
+ new CloudPrintRequest(url, URLFetcher::GET, delegate));
+}
+
+scoped_ptr<CloudPrintRequest> CloudPrintRequest::CreatePost(
+ const GURL& url,
+ const std::string& content,
+ const std::string& mimetype,
+ Delegate* delegate) {
+ scoped_ptr<CloudPrintRequest> request(
+ new CloudPrintRequest(url, URLFetcher::POST, delegate));
+ request->fetcher_->SetUploadData(mimetype, content);
+ return request.Pass();
+}
+
+void CloudPrintRequest::Run(
+ const std::string& access_token,
+ scoped_refptr<net::URLRequestContextGetter> context_getter) {
+
+ if (!access_token.empty())
+ fetcher_->AddExtraRequestHeader(base::StringPrintf(
+ "Authorization: Bearer \"%s\"", access_token.c_str()));
+
+ fetcher_->SetRequestContext(context_getter);
+ fetcher_->Start();
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&CloudPrintRequest::OnRequestTimeout, AsWeakPtr()),
+ base::TimeDelta::FromSeconds(kDefaultTimeout));
+}
+
+void CloudPrintRequest::AddHeader(const std::string& header) {
+ fetcher_->AddExtraRequestHeader(header);
+}
+
+void CloudPrintRequest::OnRequestTimeout() {
+ if (!fetcher_)
+ return;
+ fetcher_.reset();
+ LOG(WARNING) << "Request timeout reached.";
+
+ DCHECK(delegate_);
+ delegate_->OnFetchTimeoutReached(); // After this object can be deleted.
+ // Do *NOT* access members after this
+ // call.
+}
+
+void CloudPrintRequest::OnURLFetchComplete(const URLFetcher* source) {
+ DCHECK(source == fetcher_.get());
+ std::string response;
+ source->GetResponseAsString(&response);
+
+ int http_code = fetcher_->GetResponseCode();
+ fetcher_.reset();
+ VLOG(3) << response;
+
+ DCHECK(delegate_);
+ if (http_code == net::HTTP_OK) {
+ delegate_->OnFetchComplete(response); // After this object can be deleted.
+ // Do *NOT* access members after
+ // this call.
+
+ } else {
+ // TODO(maksymb): Add |server_http_code| and |server_api| info for errors.
+ delegate_->OnFetchError("dummy", -1, http_code); // After this object can
+ // be deleted.
+ // Do *NOT* access members
+ // after this call.
+
+ NOTIMPLEMENTED() << "HTTP code: " << http_code;
+ }
+}
+
diff --git a/cloud_print/gcp20/prototype/cloud_print_request.h b/cloud_print/gcp20/prototype/cloud_print_request.h
new file mode 100644
index 0000000..5adfe35
--- /dev/null
+++ b/cloud_print/gcp20/prototype/cloud_print_request.h
@@ -0,0 +1,79 @@
+// Copyright 2013 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 CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+// Request to CloudPrint with timeout control.
+// Delegate should delete this object once it is deleting.
+class CloudPrintRequest : public net::URLFetcherDelegate,
+ public base::SupportsWeakPtr<CloudPrintRequest> {
+ public:
+ class Delegate {
+ public:
+ Delegate() {}
+ virtual ~Delegate() {}
+
+ // Invoked when |fetcher_| finished fetching successfully.
+ // Use for erasing instance of CloudPrintRequest class.
+ virtual void OnFetchComplete(const std::string& response) = 0;
+
+ // Invoked when |fetcher_| finished fetching successfully.
+ // Use for erasing instance of CloudPrintRequest class.
+ virtual void OnFetchError(const std::string& server_api,
+ int server_code,
+ int server_http_code) = 0;
+
+ // Invoked when timeout is reached.
+ // Use for erasing instance of CloudPrintRequest class.
+ virtual void OnFetchTimeoutReached() = 0;
+ };
+
+ virtual ~CloudPrintRequest();
+
+ // Creates GET request.
+ static scoped_ptr<CloudPrintRequest> CreateGet(const GURL& url,
+ Delegate* delegate);
+
+ // Creates POST request.
+ static scoped_ptr<CloudPrintRequest> CreatePost(const GURL& url,
+ const std::string& content,
+ const std::string& mimetype,
+ Delegate* delegate);
+
+ // Starts request. Once fetch was completed, parser will be called.
+ void Run(const std::string& access_token,
+ scoped_refptr<net::URLRequestContextGetter> context_getter);
+
+ // Add header to request.
+ void AddHeader(const std::string& header);
+
+ private:
+ CloudPrintRequest(const GURL& url,
+ net::URLFetcher::RequestType method,
+ Delegate* delegate);
+
+ // net::URLFetcherDelegate methods:
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // Method for handling timeout.
+ void OnRequestTimeout();
+
+ scoped_ptr<net::URLFetcher> fetcher_;
+
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintRequest);
+};
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_
+
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc
index b0c6169..f0d1926 100644
--- a/cloud_print/gcp20/prototype/cloud_print_requester.cc
+++ b/cloud_print/gcp20/prototype/cloud_print_requester.cc
@@ -4,18 +4,18 @@
#include "cloud_print/gcp20/prototype/cloud_print_requester.h"
+#include "base/bind.h"
#include "base/md5.h"
#include "base/message_loop/message_loop.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
-#include "cloud_print/gcp20/prototype/cloud_print_response_parser.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/url_util.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
+#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
const char kCloudPrintUrl[] = "https://www.google.com/cloudprint";
@@ -34,8 +34,23 @@ GURL CreateRegisterUrl() {
return GURL(std::string(kCloudPrintUrl) + "/register");
}
+GURL CreateFetchUrl(const std::string& device_id) {
+ GURL url(std::string(kCloudPrintUrl) + "/fetch");
+ url = net::AppendQueryParameter(url, "printerid", device_id);
+ return url;
+}
+
+GURL CreateControlUrl(const std::string& job_id, const std::string& status) {
+ GURL url(std::string(kCloudPrintUrl) + "/control");
+ url = net::AppendQueryParameter(url, "jobid", job_id);
+ url = net::AppendQueryParameter(url, "status", status);
+ return url;
+}
+
} // namespace
+using cloud_print_response_parser::Job;
+
// Used to return a dummy context, which lives on the message loop
// given in the constructor.
class CloudPrintURLRequestContextGetter : public net::URLRequestContextGetter {
@@ -89,7 +104,11 @@ CloudPrintRequester::CloudPrintRequester(
CloudPrintRequester::~CloudPrintRequester() {
}
-bool CloudPrintRequester::StartRegistration(const std::string& proxy_id,
+bool CloudPrintRequester::IsBusy() const {
+ return request_ || gaia_;
+}
+
+void CloudPrintRequester::StartRegistration(const std::string& proxy_id,
const std::string& device_name,
const std::string& user,
const std::string& cdd) {
@@ -100,65 +119,114 @@ bool CloudPrintRequester::StartRegistration(const std::string& proxy_id,
"---------------------------%08X%08X", r1, r2);
std::string data;
- net::AddMultipartValueForUpload(kProxyIdValue, proxy_id,
- mime_boundary, std::string(), &data);
- net::AddMultipartValueForUpload(kPrinterNameValue, device_name,
- mime_boundary, std::string(), &data);
- net::AddMultipartValueForUpload("use_cdd", "true",
- mime_boundary, std::string(), &data);
- net::AddMultipartValueForUpload(kPrinterNameValue, device_name,
- mime_boundary, std::string(), &data);
- net::AddMultipartValueForUpload(kPrinterCapsValue, cdd,
- mime_boundary, "application/json", &data);
+ std::string data_mimetype;
+ data_mimetype = "multipart/form-data; boundary=" + mime_boundary;
+
+ net::AddMultipartValueForUpload(kProxyIdValue, proxy_id, mime_boundary,
+ std::string(), &data);
+ net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
+ std::string(), &data);
+ net::AddMultipartValueForUpload("use_cdd", "true", mime_boundary,
+ std::string(), &data);
+ net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary,
+ std::string(), &data);
+ net::AddMultipartValueForUpload(kPrinterCapsValue, cdd, mime_boundary,
+ "application/json", &data);
net::AddMultipartValueForUpload(kPrinterCapsHashValue, base::MD5String(cdd),
mime_boundary, std::string(), &data);
net::AddMultipartValueForUpload(kPrinterUserValue, user,
mime_boundary, std::string(), &data);
net::AddMultipartFinalDelimiterForUpload(mime_boundary, &data);
- std::string mime_type("multipart/form-data; boundary=" + mime_boundary);
+ request_ = CreatePost(
+ CreateRegisterUrl(),
+ data,
+ data_mimetype,
+ base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr()));
+ request_->Run(access_token_, context_getter_);
+}
+
+void CloudPrintRequester::CompleteRegistration() {
+ request_ = CreateGet(
+ GURL(polling_url_ + oauth_client_info_.client_id),
+ base::Bind(&CloudPrintRequester::ParseRegisterComplete, AsWeakPtr()));
+ request_->Run(access_token_, context_getter_);
+}
+
+void CloudPrintRequester::FetchPrintJobs(const std::string& refresh_token,
+ const std::string& device_id) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ if (IsBusy())
+ return;
- if (!CreateRequest(
- CreateRegisterUrl(), net::URLFetcher::POST,
- base::Bind(&CloudPrintRequester::ParseRegisterStartResponse,
- AsWeakPtr()))) {
- return false;
+ if (access_token_.empty()) {
+ UpdateAccesstoken(refresh_token);
+ return;
}
- fetcher_->SetUploadData(mime_type, data);
- fetcher_->Start();
- return true;
+ VLOG(3) << "Function: " << __FUNCTION__ <<
+ ": request created";
+ request_ = CreateGet(
+ CreateFetchUrl(device_id),
+ base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr()));
+ request_->Run(access_token_, context_getter_);
}
-bool CloudPrintRequester::CompleteRegistration() {
- if (!CreateRequest(
- GURL(polling_url_ + oauth_client_info_.client_id),
- net::URLFetcher::GET,
- base::Bind(&CloudPrintRequester::ParseRegisterCompleteResponse,
- AsWeakPtr()))) {
- return false;
- }
- fetcher_->Start();
+void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ DCHECK(!IsBusy());
+ gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
+ gaia_->RefreshToken(oauth_client_info_, refresh_token,
+ std::vector<std::string>(), kGaiaMaxRetries, this);
+ delegate_->OnServerError("Access token requested.");
+}
- return true;
+void CloudPrintRequester::RequestPrintJob(const Job& job) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ current_print_job_.reset(new Job(job));
+ request_ = CreateGet(
+ CreateControlUrl(current_print_job_->job_id, "IN_PROGRESS"),
+ base::Bind(&CloudPrintRequester::ParsePrintJobInProgress, AsWeakPtr()));
+ request_->Run(access_token_, context_getter_);
}
-void CloudPrintRequester::OnURLFetchComplete(const net::URLFetcher* source) {
- std::string response;
- source->GetResponseAsString(&response);
- fetcher_.reset();
- VLOG(1) << response;
+void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ request_ = CreateGet(
+ CreateControlUrl(job_id, "DONE"),
+ base::Bind(&CloudPrintRequester::ParsePrintJobDone, AsWeakPtr()));
+ request_->Run(access_token_, context_getter_);
+}
- // TODO(maksymb): Add |server_http_code| and |server_api| info for errors.
- if (!parse_response_callback_.is_null()) {
- parse_response_callback_.Run(response);
- parse_response_callback_.Reset();
- }
+void CloudPrintRequester::OnFetchComplete(const std::string& response) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ ParserCallback callback = parser_callback_;
+ EraseRequest();
+ callback.Run(response);
+}
+
+void CloudPrintRequester::OnFetchError(const std::string& server_api,
+ int server_code,
+ int server_http_code) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ EraseRequest();
+ current_print_job_.reset();
+ delegate_->OnServerError("Fetch error");
+ NOTIMPLEMENTED();
+}
+
+void CloudPrintRequester::OnFetchTimeoutReached() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ EraseRequest();
+ current_print_job_.reset();
+ delegate_->OnNetworkError();
}
void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token,
const std::string& access_token,
int expires_in_seconds) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ gaia_.reset();
access_token_ = access_token;
delegate_->OnGetAuthCodeResponseParsed(refresh_token);
}
@@ -166,42 +234,53 @@ void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token,
void CloudPrintRequester::OnRefreshTokenResponse(
const std::string& access_token,
int expires_in_seconds) {
- NOTIMPLEMENTED();
+ VLOG(3) << "Function: " << __FUNCTION__;
+ gaia_.reset();
+ access_token_ = access_token;
+ LOG(INFO) << "New accesstoken: " << access_token;
}
void CloudPrintRequester::OnOAuthError() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ gaia_.reset();
NOTIMPLEMENTED();
}
void CloudPrintRequester::OnNetworkError(int response_code) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ gaia_.reset();
NOTIMPLEMENTED();
}
-bool CloudPrintRequester::CreateRequest(const GURL& url,
- net::URLFetcher::RequestType method,
- const ParserCallback& callback) {
- if (fetcher_)
- return false; // Only one query is supported.
-
- fetcher_.reset(net::URLFetcher::Create(url, method, this));
- fetcher_->SetRequestContext(context_getter_);
-
- int load_flags = fetcher_->GetLoadFlags();
- load_flags |= net::LOAD_DO_NOT_SEND_COOKIES;
- load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES;
- fetcher_->SetLoadFlags(load_flags);
-
- fetcher_->AddExtraRequestHeader("X-CloudPrint-Proxy: \"\"");
- if (!access_token_.empty())
- fetcher_->AddExtraRequestHeader("Authorization: Bearer " + access_token_);
+scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet(
+ const GURL& url,
+ const ParserCallback& parser_callback) {
+ DCHECK(!IsBusy());
+ DCHECK(parser_callback_.is_null());
+ parser_callback_ = parser_callback;
+ return CloudPrintRequest::CreateGet(url, this);
+}
- parse_response_callback_ = callback;
+scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreatePost(
+ const GURL& url,
+ const std::string& content,
+ const std::string& mimetype,
+ const ParserCallback& parser_callback) {
+ DCHECK(!IsBusy());
+ DCHECK(parser_callback_.is_null());
+ parser_callback_ = parser_callback;
+ return CloudPrintRequest::CreatePost(url, content, mimetype, this);
+}
- return true;
+void CloudPrintRequester::EraseRequest() {
+ DCHECK(request_);
+ DCHECK(!parser_callback_.is_null());
+ request_.reset();
+ parser_callback_.Reset();
}
-void CloudPrintRequester::ParseRegisterStartResponse(
- const std::string& response) {
+void CloudPrintRequester::ParseRegisterStart(const std::string& response) {
+ std::string error_description;
std::string polling_url;
std::string registration_token;
std::string complete_invite_url;
@@ -209,36 +288,88 @@ void CloudPrintRequester::ParseRegisterStartResponse(
bool success = cloud_print_response_parser::ParseRegisterStartResponse(
response,
- base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError,
- base::Unretained(delegate_)),
+ &error_description,
&polling_url,
&registration_token,
&complete_invite_url,
&device_id);
- if (!success)
- return;
-
- polling_url_ = polling_url;
- delegate_->OnRegistrationStartResponseParsed(registration_token,
- complete_invite_url, device_id);
+ if (success) {
+ polling_url_ = polling_url;
+ delegate_->OnRegistrationStartResponseParsed(registration_token,
+ complete_invite_url,
+ device_id);
+ } else {
+ delegate_->OnRegistrationError(error_description);
+ }
}
-void CloudPrintRequester::ParseRegisterCompleteResponse(
- const std::string& response) {
+void CloudPrintRequester::ParseRegisterComplete(const std::string& response) {
+ std::string error_description;
std::string authorization_code;
bool success = cloud_print_response_parser::ParseRegisterCompleteResponse(
response,
- base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError,
- base::Unretained(delegate_)),
+ &error_description,
&authorization_code);
- if (!success)
- return;
+ if (success) {
+ gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
+ gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
+ kGaiaMaxRetries, this);
+ } else {
+ delegate_->OnRegistrationError(error_description);
+ }
+}
- gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get()));
- gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code,
- kGaiaMaxRetries, this);
+void CloudPrintRequester::ParseFetch(const std::string& response) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+
+ std::string error_description;
+ std::vector<Job> list;
+ bool success = cloud_print_response_parser::ParseFetchResponse(
+ response,
+ &error_description,
+ &list);
+
+ if (success) {
+ delegate_->OnPrintJobsAvailable(list);
+ } else {
+ delegate_->OnServerError(error_description);
+ }
+}
+
+void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ current_print_job_->ticket = response;
+
+ DCHECK(current_print_job_);
+ request_ = CreateGet(
+ GURL(current_print_job_->file_url),
+ base::Bind(&CloudPrintRequester::ParseGetPrintJobData, AsWeakPtr()));
+ request_->AddHeader("Accept: \"application/pdf\"");
+ request_->Run(access_token_, context_getter_);
+}
+
+void CloudPrintRequester::ParseGetPrintJobData(const std::string& response) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ current_print_job_->file = response;
+ DCHECK(current_print_job_);
+ delegate_->OnPrintJobDownloaded(*current_print_job_);
+}
+
+void CloudPrintRequester::ParsePrintJobDone(const std::string& response) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ current_print_job_.reset();
+ delegate_->OnPrintJobDone();
+}
+
+void CloudPrintRequester::ParsePrintJobInProgress(const std::string& response) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ DCHECK(current_print_job_);
+ request_ = CreateGet(
+ GURL(current_print_job_->ticket_url),
+ base::Bind(&CloudPrintRequester::ParseGetPrintJobTicket, AsWeakPtr()));
+ request_->Run(access_token_, context_getter_);
}
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h
index 663ea7b..49031ad 100644
--- a/cloud_print/gcp20/prototype/cloud_print_requester.h
+++ b/cloud_print/gcp20/prototype/cloud_print_requester.h
@@ -5,28 +5,26 @@
#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_
#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_
-#include <map>
#include <string>
+#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
+#include "cloud_print/gcp20/prototype/cloud_print_request.h"
+#include "cloud_print/gcp20/prototype/cloud_print_response_parser.h"
#include "google_apis/gaia/gaia_oauth_client.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
-
-typedef base::Callback<void(const std::string&)> ParserCallback;
class CloudPrintURLRequestContextGetter;
+class GURL;
extern const char kCloudPrintUrl[];
// Class for requesting CloudPrint server and parsing responses.
class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>,
- public net::URLFetcherDelegate,
- public gaia::GaiaOAuthClient::Delegate {
+ public gaia::GaiaOAuthClient::Delegate,
+ public CloudPrintRequest::Delegate {
public:
class Delegate {
public:
@@ -48,6 +46,23 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>,
// Invoked when server respond with |"success" = false| or we cannot parse
// response.
virtual void OnRegistrationError(const std::string& description) = 0;
+
+ // Invoked when network connection cannot be established.
+ virtual void OnNetworkError() = 0;
+
+ // Invoked when server error is received or cannot parse json response.
+ virtual void OnServerError(const std::string& description) = 0;
+
+ // Invoked when fetch response was received.
+ virtual void OnPrintJobsAvailable(
+ const std::vector<cloud_print_response_parser::Job>& jobs) = 0;
+
+ // Invoked when printjob is finally downloaded and available for printing.
+ virtual void OnPrintJobDownloaded(
+ const cloud_print_response_parser::Job& job) = 0;
+
+ // Invoked when printjob is marked as done on CloudPrint server.
+ virtual void OnPrintJobDone() = 0;
};
// Creates and initializes objects.
@@ -57,20 +72,42 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>,
// Destroys the object.
virtual ~CloudPrintRequester();
+ // Returns |true| if either |gaia| or |request| is awaiting for response.
+ bool IsBusy() const;
+
// Creates query to server for starting registration.
- bool StartRegistration(const std::string& proxy_id,
+ void StartRegistration(const std::string& proxy_id,
const std::string& device_name,
- const std::string& user,
- const std::string& cdd);
+ const std::string& user, const std::string& cdd);
// Creates request for completing registration and receiving refresh token.
- bool CompleteRegistration();
+ void CompleteRegistration();
+
+ // Creates request for fetching printjobs.
+ void FetchPrintJobs(const std::string& refresh_token,
+ const std::string& device_id);
+
+ // Creates request for updating accesstoken.
+ // TODO(maksymb): Handle expiration of accesstoken.
+ void UpdateAccesstoken(const std::string& refresh_token);
+
+ // Creates chain of requests for requesting printjob.
+ void RequestPrintJob(const cloud_print_response_parser::Job& job);
+
+ // Reports server that printjob has been printed.
+ void SendPrintJobDone(const std::string& job_id);
private:
- // net::URLFetcherDelegate
- virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+ typedef base::Callback<void(const std::string&)> ParserCallback;
+
+ // CloudPrintRequester::Delegate methods:
+ virtual void OnFetchComplete(const std::string& response) OVERRIDE;
+ virtual void OnFetchError(const std::string& server_api,
+ int server_code,
+ int server_http_code) OVERRIDE;
+ virtual void OnFetchTimeoutReached() OVERRIDE;
- // gaia::GaiaOAuthClient::Delegate
+ // gaia::GaiaOAuthClient::Delegate methods:
virtual void OnGetTokensResponse(const std::string& refresh_token,
const std::string& access_token,
int expires_in_seconds) OVERRIDE;
@@ -79,26 +116,48 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>,
virtual void OnOAuthError() OVERRIDE;
virtual void OnNetworkError(int response_code) OVERRIDE;
- // Creates request with given |url| and |method|. When response is received
- // callback is called.
- // TODO(maksymb): Add timeout control for request.
- bool CreateRequest(const GURL& url,
- net::URLFetcher::RequestType method,
- const ParserCallback& callback) WARN_UNUSED_RESULT;
+ // Creates GET request.
+ scoped_ptr<CloudPrintRequest> CreateGet(const GURL& url,
+ const ParserCallback& callback);
+
+ // Creates POST request.
+ scoped_ptr<CloudPrintRequest> CreatePost(const GURL& url,
+ const std::string& content,
+ const std::string& mimetype,
+ const ParserCallback& callback);
+
+ // Deletes all info about current request.
+ void EraseRequest();
// Parses register-start server response.
- void ParseRegisterStartResponse(const std::string& response);
+ void ParseRegisterStart(const std::string& response);
// Parses register-complete server response. Initializes gaia (OAuth client)
// and receives refresh token.
- void ParseRegisterCompleteResponse(const std::string& response);
+ void ParseRegisterComplete(const std::string& response);
+
+ // Parses fetch printjobs server response.
+ void ParseFetch(const std::string& response);
- // Fetcher contains |NULL| if no server response is awaiting. Otherwise wait
- // until URLFetchComplete will be called and close connection.
- scoped_ptr<net::URLFetcher> fetcher_;
+ // Invoked after receiving printjob ticket.
+ void ParseGetPrintJobTicket(const std::string& response);
- // Callback for parsing server response.
- ParserCallback parse_response_callback_;
+ // Invoked after receiving printjob file.
+ void ParseGetPrintJobData(const std::string& response);
+
+ // Invoked after marking printjob as DONE.
+ void ParsePrintJobDone(const std::string& response);
+
+ // Invoked after marking printjob as IN_PROGRESS.
+ void ParsePrintJobInProgress(const std::string& response);
+
+ // |request| contains |NULL| if no server response is awaiting. Otherwise wait
+ // until callback will be called will be called and close connection.
+ scoped_ptr<CloudPrintRequest> request_;
+
+ // Contains information about current printjob. Information is filled by
+ // CloudPrint server responses.
+ scoped_ptr<cloud_print_response_parser::Job> current_print_job_;
// Privet context getter.
scoped_refptr<CloudPrintURLRequestContextGetter> context_getter_;
@@ -115,6 +174,8 @@ class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>,
// OAuth client.
scoped_ptr<gaia::GaiaOAuthClient> gaia_;
+ ParserCallback parser_callback_;
+
Delegate* delegate_;
DISALLOW_COPY_AND_ASSIGN(CloudPrintRequester);
diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc
index fa2c5f7..d4da3c3 100644
--- a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc
+++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc
@@ -10,28 +10,34 @@
namespace cloud_print_response_parser {
+Job::Job() {
+}
+
+Job::~Job() {
+}
+
// Parses json base::Value to base::DictionaryValue and checks |success| status.
// Returns |true| on success.
bool GetJsonDictinaryAndCheckSuccess(base::Value* json,
- const ErrorCallback error_callback,
+ std::string* error_description,
base::DictionaryValue** dictionary) {
base::DictionaryValue* response_dictionary = NULL;
if (!json || !json->GetAsDictionary(&response_dictionary)) {
- error_callback.Run("No JSON dictionary response received.");
+ *error_description = "No JSON dictionary response received.";
return false;
}
bool success = false;
if (!response_dictionary->GetBoolean("success", &success)) {
- error_callback.Run("Cannot parse success state.");
+ *error_description = "Cannot parse success state.";
return false;
}
if (!success) {
std::string message;
response_dictionary->GetString("message", &message);
- error_callback.Run(message);
+ *error_description = message;
return false;
}
@@ -40,14 +46,14 @@ bool GetJsonDictinaryAndCheckSuccess(base::Value* json,
}
bool ParseRegisterStartResponse(const std::string& response,
- const ErrorCallback error_callback,
+ std::string* error_description,
std::string* polling_url_result,
std::string* registration_token_result,
std::string* complete_invite_url_result,
std::string* device_id_result) {
scoped_ptr<base::Value> json(base::JSONReader::Read(response));
base::DictionaryValue* response_dictionary = NULL;
- if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_callback,
+ if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
&response_dictionary)) {
return false;
}
@@ -55,49 +61,49 @@ bool ParseRegisterStartResponse(const std::string& response,
std::string registration_token;
if (!response_dictionary->GetString("registration_token",
&registration_token)) {
- error_callback.Run("No registration_token specified.");
+ *error_description = "No registration_token specified.";
return false;
}
std::string complete_invite_url;
if (!response_dictionary->GetString("complete_invite_url",
&complete_invite_url)) {
- error_callback.Run("No complete_invite_url specified.");
+ *error_description = "No complete_invite_url specified.";
return false;
}
std::string polling_url;
if (!response_dictionary->GetString("polling_url", &polling_url)) {
- error_callback.Run("No polling_url specified.");
+ *error_description = "No polling_url specified.";
return false;
}
base::ListValue* list = NULL;
if (!response_dictionary->GetList("printers", &list)) {
- error_callback.Run("No printers list specified.");
+ *error_description = "No printers list specified.";
return false;
}
base::Value* printer_value = NULL;
if (!list->Get(0, &printer_value)) {
- error_callback.Run("Printers list is empty.");
+ *error_description = "Printers list is empty.";
return false;
}
base::DictionaryValue* printer = NULL;
if (!printer_value->GetAsDictionary(&printer)) {
- error_callback.Run("Printer is not a json-dictionary.");
+ *error_description = "Printer is not a json-dictionary.";
return false;
}
std::string device_id;
if (!printer->GetString("id", &device_id)) {
- error_callback.Run("No id specified.");
+ *error_description = "No id specified.";
return false;
}
if (device_id.empty()) {
- error_callback.Run("id is empty.");
+ *error_description = "id is empty.";
return false;
}
@@ -109,11 +115,11 @@ bool ParseRegisterStartResponse(const std::string& response,
}
bool ParseRegisterCompleteResponse(const std::string& response,
- const ErrorCallback error_callback,
+ std::string* error_description,
std::string* authorization_code_result) {
scoped_ptr<base::Value> json(base::JSONReader::Read(response));
base::DictionaryValue* response_dictionary = NULL;
- if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_callback,
+ if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
&response_dictionary)) {
return false;
}
@@ -121,7 +127,7 @@ bool ParseRegisterCompleteResponse(const std::string& response,
std::string authorization_code;
if (!response_dictionary->GetString("authorization_code",
&authorization_code)) {
- error_callback.Run("Cannot parse authorization_code.");
+ *error_description = "Cannot parse authorization_code.";
return false;
}
@@ -129,5 +135,57 @@ bool ParseRegisterCompleteResponse(const std::string& response,
return true;
}
+bool ParseFetchResponse(const std::string& response,
+ std::string* error_description,
+ std::vector<Job>* list_result) {
+ scoped_ptr<base::Value> json(base::JSONReader::Read(response));
+ base::DictionaryValue* response_dictionary = NULL;
+
+ if (!json || !json->GetAsDictionary(&response_dictionary)) {
+ *error_description = "No JSON dictionary response received.";
+ return false;
+ }
+
+ bool success = false;
+ if (!response_dictionary->GetBoolean("success", &success)) {
+ *error_description = "Cannot parse success state.";
+ return false;
+ }
+
+ if (!success) { // Let's suppose we have no jobs to proceed.
+ list_result->clear();
+ return true;
+ }
+
+ if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
+ &response_dictionary)) {
+ return false;
+ }
+
+ base::ListValue* jobs = NULL;
+ if (!response_dictionary->GetList("jobs", &jobs)) {
+ *error_description = "Cannot parse jobs list.";
+ return false;
+ }
+
+ std::vector<Job> list(jobs->GetSize());
+ for (size_t idx = 0; idx < list.size(); ++idx) {
+ base::DictionaryValue* job = NULL;
+ jobs->GetDictionary(idx, &job);
+ if (!job->GetString("id", &list[idx].job_id) ||
+ !job->GetString("createTime", &list[idx].create_time) ||
+ !job->GetString("fileUrl", &list[idx].file_url) ||
+ !job->GetString("ticketUrl", &list[idx].ticket_url) ||
+ !job->GetString("title", &list[idx].title)) {
+ *error_description = "Cannot parse job info.";
+ return false;
+ }
+ }
+
+ *list_result = list;
+
+ return true;
+}
+
} // namespace cloud_print_response_parser
diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.h b/cloud_print/gcp20/prototype/cloud_print_response_parser.h
index 1178301..b691934 100644
--- a/cloud_print/gcp20/prototype/cloud_print_response_parser.h
+++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.h
@@ -6,6 +6,7 @@
#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_
#include <string>
+#include <vector>
#include "base/callback.h"
@@ -17,14 +18,26 @@ class DictionaryValue;
namespace cloud_print_response_parser {
-// Callback for handling parse errors.
-typedef base::Callback<void(const std::string&)> ErrorCallback;
+struct Job {
+ Job();
+ ~Job();
+
+ std::string job_id;
+ std::string create_time;
+ std::string file_url;
+ std::string ticket_url;
+ std::string title;
+
+ // Downloaded data:
+ std::string file;
+ std::string ticket;
+};
// Parses CloudPrint register start response to out parameters.
// Returns |true| on success. Callback is called with description as a parameter
-// when parsing failed.
+// when parsing is failed.
bool ParseRegisterStartResponse(const std::string& response,
- const ErrorCallback error_callback,
+ std::string* error_description,
std::string* polling_url_result,
std::string* registration_token_result,
std::string* complete_invite_url_result,
@@ -32,11 +45,18 @@ bool ParseRegisterStartResponse(const std::string& response,
// Parses CloudPrint register complete response to out parameters.
// Returns |true| on success. Callback is called with description as a parameter
-// when parsing failed.
+// when parsing is failed.
bool ParseRegisterCompleteResponse(const std::string& response,
- const ErrorCallback error_callback,
+ std::string* error_description,
std::string* authorization_code_result);
+// Parses CloudPrint fetch response to out parameters.
+// Returns |true| on success. Callback is called with description as a parameter
+// when parsing is failed.
+bool ParseFetchResponse(const std::string& response,
+ std::string* error_description,
+ std::vector<Job>* list_result);
+
} // namespace cloud_print_response_parser
#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_
diff --git a/cloud_print/gcp20/prototype/dns_sd_server.h b/cloud_print/gcp20/prototype/dns_sd_server.h
index a0d0128..f9aa540 100644
--- a/cloud_print/gcp20/prototype/dns_sd_server.h
+++ b/cloud_print/gcp20/prototype/dns_sd_server.h
@@ -37,7 +37,8 @@ class DnsSdServer : public base::SupportsWeakPtr<DnsSdServer> {
// Starts the server. Returns |true| if server works. Also sends
// announcement.
- bool Start(const ServiceParameters& serv_params, uint32 full_ttl,
+ bool Start(const ServiceParameters& serv_params,
+ uint32 full_ttl,
const std::vector<std::string>& metadata) WARN_UNUSED_RESULT;
// Sends announcement if server works.
@@ -57,7 +58,8 @@ class DnsSdServer : public base::SupportsWeakPtr<DnsSdServer> {
bool CreateSocket();
// Processes single query.
- void ProccessQuery(uint32 current_ttl, const DnsQueryRecord& query,
+ void ProccessQuery(uint32 current_ttl,
+ const DnsQueryRecord& query,
DnsResponseBuilder* builder) const;
// Processes DNS message.
diff --git a/cloud_print/gcp20/prototype/gcp20_device.gyp b/cloud_print/gcp20/prototype/gcp20_device.gyp
index bb83e45..e6f1777 100644
--- a/cloud_print/gcp20/prototype/gcp20_device.gyp
+++ b/cloud_print/gcp20/prototype/gcp20_device.gyp
@@ -29,6 +29,8 @@
'sources': [
'cloud_print_response_parser.cc',
'cloud_print_response_parser.h',
+ 'cloud_print_request.cc',
+ 'cloud_print_request.h',
'cloud_print_requester.cc',
'cloud_print_requester.h',
'conio_posix.cc',
@@ -41,6 +43,8 @@
'dns_response_builder.h',
'dns_sd_server.cc',
'dns_sd_server.h',
+ 'print_job_handler.cc',
+ 'print_job_handler.h',
'printer.cc',
'printer.h',
'privet_http_server.cc',
diff --git a/cloud_print/gcp20/prototype/print_job_handler.cc b/cloud_print/gcp20/prototype/print_job_handler.cc
new file mode 100644
index 0000000..c7549e4
--- /dev/null
+++ b/cloud_print/gcp20/prototype/print_job_handler.cc
@@ -0,0 +1,63 @@
+// Copyright 2013 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 "cloud_print/gcp20/prototype/print_job_handler.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+const base::FilePath::CharType kJobsPath[] = FILE_PATH_LITERAL("printjobs");
+
+} // namespace
+
+PrintJobHandler::PrintJobHandler() {
+}
+
+PrintJobHandler::~PrintJobHandler() {
+}
+
+bool PrintJobHandler::SavePrintJob(const std::string& data,
+ const std::string& ticket,
+ const std::string& job_name,
+ const std::string& title) {
+ VLOG(1) << "Printing printjob: \"" + title + "\"";
+ base::FilePath directory(kJobsPath);
+
+ using file_util::CreateDirectory;
+
+ if (!base::DirectoryExists(directory) && !CreateDirectory(directory)) {
+ LOG(WARNING) << "Cannot create directory: " << directory.value();
+ return false;
+ }
+
+ directory = directory.AppendASCII(job_name);
+
+ if (!base::DirectoryExists(directory) && !CreateDirectory(directory)) {
+ LOG(WARNING) << "Cannot create directory: " << directory.value();
+ return false;
+ }
+
+ int written = file_util::WriteFile(directory.AppendASCII("ticket.xml"),
+ ticket.data(),
+ static_cast<int>(ticket.size()));
+ if (static_cast<size_t>(written) != ticket.size()) {
+ LOG(WARNING) << "Cannot save ticket.";
+ return false;
+ }
+
+ written = file_util::WriteFile(directory.AppendASCII("data.pdf"),
+ data.data(),
+ static_cast<int>(data.size()));
+ if (static_cast<size_t>(written) != data.size()) {
+ LOG(WARNING) << "Cannot save data.";
+ return false;
+ }
+
+ LOG(INFO) << "Saved printjob: " << job_name;
+ return true;
+}
+
diff --git a/cloud_print/gcp20/prototype/print_job_handler.h b/cloud_print/gcp20/prototype/print_job_handler.h
new file mode 100644
index 0000000..594e5a2
--- /dev/null
+++ b/cloud_print/gcp20/prototype/print_job_handler.h
@@ -0,0 +1,27 @@
+// Copyright 2013 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 CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_
+#define CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class PrintJobHandler {
+ public:
+ PrintJobHandler();
+ ~PrintJobHandler();
+
+ bool SavePrintJob(const std::string& data,
+ const std::string& ticket,
+ const std::string& job_name,
+ const std::string& title);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrintJobHandler);
+};
+
+#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_
+
diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc
index 0aaf652..56bcad4 100644
--- a/cloud_print/gcp20/prototype/printer.cc
+++ b/cloud_print/gcp20/prototype/printer.cc
@@ -8,12 +8,14 @@
#include <string>
#include <vector>
+#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
#include "cloud_print/gcp20/prototype/command_line_reader.h"
#include "cloud_print/gcp20/prototype/service_parameters.h"
#include "cloud_print/gcp20/prototype/special_io.h"
@@ -36,6 +38,9 @@ const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you "
"agree and any other to discard\n";
const int64 kUserConfirmationTimeout = 30; // in seconds
+const uint32 kReconnectTimeout = 5; // in seconds
+const uint32 kPrintJobsTimeout = 10; // in seconds
+
const char kCdd[] =
"{\n"
" 'version': '1.0',\n"
@@ -92,6 +97,8 @@ net::IPAddressNumber GetLocalIp(const std::string& interface_name,
} // namespace
+using cloud_print_response_parser::Job;
+
Printer::RegistrationInfo::RegistrationInfo()
: state(DEV_REG_UNREGISTERED),
confirmation_state(CONFIRMATION_PENDING) {
@@ -100,7 +107,7 @@ Printer::RegistrationInfo::RegistrationInfo()
Printer::RegistrationInfo::~RegistrationInfo() {
}
-Printer::Printer() : http_server_(this) {
+Printer::Printer() : http_server_(this), connection_state_(OFFLINE) {
}
Printer::~Printer() {
@@ -147,6 +154,10 @@ bool Printer::Start() {
xtoken_ = XPrivetToken();
starttime_ = base::Time::Now();
+ print_job_handler_.reset(new PrintJobHandler);
+ connection_state_ = CONNECTING;
+ WakeUp();
+
return true;
}
@@ -154,10 +165,20 @@ bool Printer::IsOnline() const {
return requester_;
}
+void Printer::WakeUp() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+
+ if (!IsRegistered())
+ return;
+
+ FetchPrintJobs();
+}
+
void Printer::Stop() {
dns_server_.Shutdown();
http_server_.Shutdown();
requester_.reset();
+ print_job_handler_.reset();
}
PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
@@ -185,14 +206,6 @@ PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart(
return PrivetHttpServer::REG_ERROR_OK;
}
-bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
- return xtoken_.CheckValidXToken(token);
-}
-
-bool Printer::IsRegistered() const {
- return reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED;
-}
-
PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken(
const std::string& user,
std::string* token,
@@ -284,7 +297,7 @@ void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
info->url = kCloudPrintUrl;
info->id = reg_info_.device_id;
info->device_state = "idle";
- info->connection_state = "offline";
+ info->connection_state = ConnectionStateToString(connection_state_);
info->manufacturer = "Google";
info->model = "Prototype";
info->serial_number = "2.3.5.7.13.17.19.31.61.89.107.127.521.607.1279.2203";
@@ -299,6 +312,14 @@ void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) {
info->type.push_back("printer");
}
+bool Printer::IsRegistered() const {
+ return reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED;
+}
+
+bool Printer::CheckXPrivetTokenHeader(const std::string& token) const {
+ return xtoken_.CheckValidXToken(token);
+}
+
void Printer::OnRegistrationStartResponseParsed(
const std::string& registration_token,
const std::string& complete_invite_url,
@@ -313,6 +334,7 @@ void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token) {
reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED;
reg_info_.refresh_token = refresh_token;
SaveToFile(base::FilePath(kPrinterStatePath));
+ FetchPrintJobs();
}
void Printer::OnRegistrationError(const std::string& description) {
@@ -323,6 +345,52 @@ void Printer::OnRegistrationError(const std::string& description) {
reg_info_.error_description = description;
}
+void Printer::OnServerError(const std::string& description) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ LOG(ERROR) << "Server error: " << description;
+
+ PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout));
+}
+
+void Printer::OnNetworkError() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ ChangeState(OFFLINE);
+ PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout));
+}
+
+void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ ChangeState(ONLINE);
+
+ LOG(INFO) << "Available printjobs: " << jobs.size();
+
+ if (jobs.empty()) {
+ PostDelayedWakeUp(base::TimeDelta::FromSeconds(kPrintJobsTimeout));
+ return;
+ }
+
+ // TODO(maksymb): After finishing XMPP add 'Printjobs available' flag.
+ LOG(INFO) << "Downloading first printjob.";
+ requester_->RequestPrintJob(jobs[0]);
+ return;
+}
+
+void Printer::OnPrintJobDownloaded(const Job& job) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ print_job_handler_->SavePrintJob(
+ job.file,
+ job.ticket,
+ base::StringPrintf("%s.%s", job.create_time.c_str(), job.job_id.c_str()),
+ job.title);
+ requester_->SendPrintJobDone(job.job_id);
+}
+
+void Printer::OnPrintJobDone() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ // TODO(maksymb): Replace PostTask with with XMPP notifications.
+ PostWakeUp();
+}
+
PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors(
const std::string& user) const {
DCHECK(!IsRegistered());
@@ -375,11 +443,24 @@ std::vector<std::string> Printer::CreateTxt() const {
txt.push_back("url=" + std::string(kCloudPrintUrl));
txt.push_back("type=printer");
txt.push_back("id=" + reg_info_.device_id);
- txt.push_back("cs=offline");
+ txt.push_back("cs=" + ConnectionStateToString(connection_state_));
return txt;
}
+void Printer::FetchPrintJobs() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+
+ if (!IsRegistered())
+ return;
+
+ if (requester_->IsBusy()) {
+ PostDelayedWakeUp(base::TimeDelta::FromSeconds(kReconnectTimeout));
+ } else {
+ requester_->FetchPrintJobs(reg_info_.refresh_token, reg_info_.device_id);
+ }
+}
+
void Printer::SaveToFile(const base::FilePath& file_path) const {
base::DictionaryValue json;
// TODO(maksymb): Get rid of in-place constants.
@@ -461,6 +542,21 @@ bool Printer::LoadFromFile(const base::FilePath& file_path) {
return true;
}
+void Printer::PostWakeUp() {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&Printer::WakeUp, AsWeakPtr()));
+}
+
+void Printer::PostDelayedWakeUp(const base::TimeDelta& delay) {
+ VLOG(3) << "Function: " << __FUNCTION__;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&Printer::WakeUp, AsWeakPtr()),
+ delay);
+}
+
PrivetHttpServer::RegistrationErrorStatus
Printer::ConfirmationToRegistrationError(
RegistrationInfo::ConfirmationState state) {
@@ -480,3 +576,30 @@ PrivetHttpServer::RegistrationErrorStatus
}
}
+std::string Printer::ConnectionStateToString(ConnectionState state) const {
+ switch (state) {
+ case OFFLINE:
+ return "offline";
+ case ONLINE:
+ return "online";
+ case CONNECTING:
+ return "connecting";
+ case NOT_CONFIGURED:
+ return "not-configured";
+
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+bool Printer::ChangeState(ConnectionState new_state) {
+ if (connection_state_ == new_state)
+ return false;
+
+ VLOG(1) << "Printer is now " << ConnectionStateToString(new_state);
+ connection_state_ = new_state;
+ dns_server_.UpdateMetadata(CreateTxt());
+ return true;
+}
+
diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h
index 9dba787..3f402b9 100644
--- a/cloud_print/gcp20/prototype/printer.h
+++ b/cloud_print/gcp20/prototype/printer.h
@@ -8,13 +8,17 @@
#include <string>
#include <vector>
+#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "cloud_print/gcp20/prototype/cloud_print_requester.h"
#include "cloud_print/gcp20/prototype/dns_sd_server.h"
+#include "cloud_print/gcp20/prototype/print_job_handler.h"
#include "cloud_print/gcp20/prototype/privet_http_server.h"
#include "cloud_print/gcp20/prototype/x_privet_token.h"
-// This class maintain work of DNS-SD server, HTTP server and others.
+extern const base::FilePath::CharType kPrinterStatePath[];
+
+// This class maintains work of DNS-SD server, HTTP server and others.
class Printer : public base::SupportsWeakPtr<Printer>,
public PrivetHttpServer::Delegate,
public CloudPrintRequester::Delegate {
@@ -31,6 +35,9 @@ class Printer : public base::SupportsWeakPtr<Printer>,
// Returns true if printer was started.
bool IsOnline() const;
+ // Method for trying to reconnecting to server.
+ void WakeUp();
+
// Stops all servers.
void Stop();
@@ -79,6 +86,13 @@ class Printer : public base::SupportsWeakPtr<Printer>,
REG_ACTION_CANCEL
};
+ enum ConnectionState {
+ NOT_CONFIGURED,
+ OFFLINE,
+ ONLINE,
+ CONNECTING
+ };
+
// PrivetHttpServer::Delegate methods:
virtual PrivetHttpServer::RegistrationErrorStatus RegistrationStart(
const std::string& user) OVERRIDE;
@@ -104,6 +118,13 @@ class Printer : public base::SupportsWeakPtr<Printer>,
virtual void OnGetAuthCodeResponseParsed(
const std::string& refresh_token) OVERRIDE;
virtual void OnRegistrationError(const std::string& description) OVERRIDE;
+ virtual void OnServerError(const std::string& description) OVERRIDE;
+ virtual void OnNetworkError() OVERRIDE;
+ virtual void OnPrintJobsAvailable(
+ const std::vector<cloud_print_response_parser::Job>& jobs) OVERRIDE;
+ virtual void OnPrintJobDownloaded(
+ const cloud_print_response_parser::Job& job) OVERRIDE;
+ virtual void OnPrintJobDone() OVERRIDE;
// Checks if register call is called correctly (|user| is correct,
// error is no set etc). Returns |false| if error status is put into |status|.
@@ -111,23 +132,37 @@ class Printer : public base::SupportsWeakPtr<Printer>,
PrivetHttpServer::RegistrationErrorStatus CheckCommonRegErrors(
const std::string& user) const;
- // Generates ProxyId for this device.
- std::string GenerateProxyId() const;
-
// Checks if confirmation was received.
void WaitUserConfirmation(base::Time valid_until);
+ // Generates ProxyId for this device.
+ std::string GenerateProxyId() const;
+
// Creates data for DNS TXT respond.
std::vector<std::string> CreateTxt() const;
+ // Ask CloudPrint server for printjobs.
+ void FetchPrintJobs();
+
// Saving and loading registration info from file.
void SaveToFile(const base::FilePath& file_path) const;
bool LoadFromFile(const base::FilePath& file_path);
+ // Adds |WakeUp| method to the MessageLoop.
+ void PostWakeUp();
+
+ // Adds |WakeUp| method to the MessageLoop with certain |delay|.
+ void PostDelayedWakeUp(const base::TimeDelta& delay);
+
// Converts errors.
PrivetHttpServer::RegistrationErrorStatus ConfirmationToRegistrationError(
RegistrationInfo::ConfirmationState state);
+ std::string ConnectionStateToString(ConnectionState state) const;
+
+ // Changes state and update info in DNS server.
+ bool ChangeState(ConnectionState new_state);
+
RegistrationInfo reg_info_;
// Contains DNS-SD server.
@@ -136,11 +171,16 @@ class Printer : public base::SupportsWeakPtr<Printer>,
// Contains Privet HTTP server.
PrivetHttpServer http_server_;
- // Contains Cloud Print client.
+ // Connection state of device.
+ ConnectionState connection_state_;
+
+ // Contains CloudPrint client.
scoped_ptr<CloudPrintRequester> requester_;
XPrivetToken xtoken_;
+ scoped_ptr<PrintJobHandler> print_job_handler_;
+
// Uses for calculating uptime.
base::Time starttime_;
diff --git a/cloud_print/gcp20/prototype/privet_http_server.cc b/cloud_print/gcp20/prototype/privet_http_server.cc
index 33fd2ab..6c08029 100644
--- a/cloud_print/gcp20/prototype/privet_http_server.cc
+++ b/cloud_print/gcp20/prototype/privet_http_server.cc
@@ -133,7 +133,8 @@ void PrivetHttpServer::OnHttpRequest(int connection_id,
}
void PrivetHttpServer::OnWebSocketRequest(
- int connection_id, const net::HttpServerRequestInfo& info) {
+ int connection_id,
+ const net::HttpServerRequestInfo& info) {
}
void PrivetHttpServer::OnWebSocketMessage(int connection_id,
diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h
index 84a464c..7c043b0 100644
--- a/cloud_print/gcp20/prototype/privet_http_server.h
+++ b/cloud_print/gcp20/prototype/privet_http_server.h
@@ -123,7 +123,8 @@ class PrivetHttpServer: public net::HttpServer::Delegate {
// Returns |true| if |request| should be done with correct |method|.
// Otherwise sends |Invalid method| error.
// Also checks support of |request| by this server.
- bool ValidateRequestMethod(int connection_id, const std::string& request,
+ bool ValidateRequestMethod(int connection_id,
+ const std::string& request,
const std::string& method);
// Processes http request after all preparations (XPrivetHeader check,