diff options
author | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-19 22:14:14 +0000 |
---|---|---|
committer | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-19 22:14:14 +0000 |
commit | 6676bf444b999c661a5289f1b33c0bcc04712d5c (patch) | |
tree | 1217411def2d28478d7789441e957744a78fb872 | |
parent | 6d2143e075b4026b12d16288a3b3732ff73cc602 (diff) | |
download | chromium_src-6676bf444b999c661a5289f1b33c0bcc04712d5c.zip chromium_src-6676bf444b999c661a5289f1b33c0bcc04712d5c.tar.gz chromium_src-6676bf444b999c661a5289f1b33c0bcc04712d5c.tar.bz2 |
Finished registration to Google Cloud Print.
No saving state. No network error handling. No /privet/reset.
Fixed bug with empty command line arguments.
BUG=
Review URL: https://chromiumcodereview.appspot.com/17921002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212667 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | cloud_print/DEPS | 1 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_requester.cc | 246 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_requester.h | 120 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_response_parser.cc | 133 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/cloud_print_response_parser.h | 43 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/dns_response_builder.cc | 3 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/dns_response_builder.h | 8 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/dns_sd_server.h | 6 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/gcp20_device.cc | 28 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/gcp20_device.gyp | 17 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.cc | 231 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.h | 84 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.cc | 255 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.h | 135 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/service_parameters.cc | 3 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/service_parameters.h | 2 |
16 files changed, 1271 insertions, 44 deletions
diff --git a/cloud_print/DEPS b/cloud_print/DEPS index f7eacc3..1df0543 100644 --- a/cloud_print/DEPS +++ b/cloud_print/DEPS @@ -5,4 +5,5 @@ include_rules = [ "+grit", "+net", "+printing", + "+url", ] diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc new file mode 100644 index 0000000..857b3de --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_requester.cc @@ -0,0 +1,246 @@ +// 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_requester.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 "url/gurl.h" + +namespace { + +const char kProxyIdValue[] = "proxy"; +const char kPrinterNameValue[] = "printer"; +const char kPrinterCapsValue[] = "capabilities"; +const char kPrinterCapsHashValue[] = "capsHash"; +const char kPrinterUserValue[] = "user"; + +const int kGaiaMaxRetries = 3; + +std::string GetCloudPrintUrl() { + return "https://www.google.com/cloudprint"; +} + +GURL CreateRegisterUrl() { + return GURL(GetCloudPrintUrl() + "/register"); +} + +} // namespace + +// Used to return a dummy context, which lives on the message loop +// given in the constructor. +class CloudPrintURLRequestContextGetter : public net::URLRequestContextGetter { + public: + // |task_runner| must not be NULL. + explicit CloudPrintURLRequestContextGetter( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + DCHECK(task_runner); + network_task_runner_ = task_runner; + } + + // URLRequestContextGetter implementation. + virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { + if (!context_) { + net::URLRequestContextBuilder builder; +#if defined(OS_LINUX) || defined(OS_ANDROID) + builder.set_proxy_config_service( + new net::ProxyConfigServiceFixed(net::ProxyConfig())); +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + context_.reset(builder.Build()); + } + return context_.get(); + } + + virtual scoped_refptr<base::SingleThreadTaskRunner> + GetNetworkTaskRunner() const OVERRIDE { + return network_task_runner_; + } + + protected: + virtual ~CloudPrintURLRequestContextGetter() {} + + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; + scoped_ptr<net::URLRequestContext> context_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintURLRequestContextGetter); +}; + +CloudPrintRequester::CloudPrintRequester( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + Delegate* delegate) + : context_getter_(new CloudPrintURLRequestContextGetter(task_runner)), + delegate_(delegate) { + oauth_client_info_.client_id = + google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT); + oauth_client_info_.client_secret = + google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT); + oauth_client_info_.redirect_uri = "oob"; +} + +CloudPrintRequester::~CloudPrintRequester() { +} + +bool CloudPrintRequester::StartRegistration(const std::string& proxy_id, + const std::string& device_name, + const std::string& user, + const std::string& cdd) { + std::string mime_boundary; + int r1 = base::RandInt(0, kint32max); + int r2 = base::RandInt(0, kint32max); + base::SStringPrintf(&mime_boundary, + "---------------------------%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); + 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); + + if (!CreateRequest( + CreateRegisterUrl(), net::URLFetcher::POST, + base::Bind(&CloudPrintRequester::ParseRegisterStartResponse, + base::Unretained(this)))) { + return false; + } + fetcher_->SetUploadData(mime_type, data); + fetcher_->Start(); + + return true; +} + +bool CloudPrintRequester::CompleteRegistration() { + if (!CreateRequest( + GURL(polling_url_ + oauth_client_info_.client_id), + net::URLFetcher::GET, + base::Bind(&CloudPrintRequester::ParseRegisterCompleteResponse, + base::Unretained(this)))) { + return false; + } + fetcher_->Start(); + + return true; +} + +void CloudPrintRequester::OnURLFetchComplete(const net::URLFetcher* source) { + std::string response; + source->GetResponseAsString(&response); + fetcher_.reset(); + VLOG(1) << response; + + // 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::OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds) { + access_token_ = access_token; + delegate_->OnGetAuthCodeResponseParsed(refresh_token); +} + +void CloudPrintRequester::OnRefreshTokenResponse( + const std::string& access_token, + int expires_in_seconds) { + NOTIMPLEMENTED(); +} + +void CloudPrintRequester::OnOAuthError() { + NOTIMPLEMENTED(); +} + +void CloudPrintRequester::OnNetworkError(int response_code) { + 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_); + + parse_response_callback_ = callback; + + return true; +} + +void CloudPrintRequester::ParseRegisterStartResponse( + const std::string& response) { + std::string polling_url; + std::string registration_token; + std::string complete_invite_url; + std::string device_id; + + bool success = cloud_print_response_parser::ParseRegisterStartResponse( + response, + base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError, + base::Unretained(delegate_)), + &polling_url, + ®istration_token, + &complete_invite_url, + &device_id); + + if (!success) + return; + + polling_url_ = polling_url; + delegate_->OnRegistrationStartResponseParsed(registration_token, + complete_invite_url, device_id); +} + +void CloudPrintRequester::ParseRegisterCompleteResponse( + const std::string& response) { + std::string authorization_code; + + bool success = cloud_print_response_parser::ParseRegisterCompleteResponse( + response, + base::Bind(&CloudPrintRequester::Delegate::OnRegistrationError, + base::Unretained(delegate_)), + &authorization_code); + + if (!success) + return; + + gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get())); + gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code, + kGaiaMaxRetries, this); +} + diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h new file mode 100644 index 0000000..345bfce --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_requester.h @@ -0,0 +1,120 @@ +// 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_REQUESTER_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/values.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 for requesting CloudPrint server and parsing responses. +class CloudPrintRequester : public net::URLFetcherDelegate, + public gaia::GaiaOAuthClient::Delegate { + public: + class Delegate { + public: + Delegate() {} + virtual ~Delegate() {} + + // Invoked when server respond for registration-start query and response is + // successfully parsed. + virtual void OnRegistrationStartResponseParsed( + const std::string& registration_token, + const std::string& complete_invite_url, + const std::string& device_id) = 0; + + // Invoked when server respond for registration-getAuthCode query and + // response is successfully parsed. + virtual void OnGetAuthCodeResponseParsed( + const std::string& refresh_token) = 0; + + // Invoked when server respond with |"success" = false| or we cannot parse + // response. + virtual void OnRegistrationError(const std::string& description) = 0; + }; + + // Creates and initializes objects. + CloudPrintRequester(scoped_refptr<base::SingleThreadTaskRunner> task_runner, + Delegate* delegate); + + // Destroys the object. + virtual ~CloudPrintRequester(); + + // Creates query to server for starting registration. + bool StartRegistration(const std::string& proxy_id, + const std::string& device_name, + const std::string& user, + const std::string& cdd); + + // Creates request for completing registration and receiving refresh token. + bool CompleteRegistration(); + + private: + // net::URLFetcherDelegate + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + + // gaia::GaiaOAuthClient::Delegate + virtual void OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds) OVERRIDE; + virtual void OnRefreshTokenResponse(const std::string& access_token, + int expires_in_seconds) OVERRIDE; + 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; + + // Parses register-start server response. + void ParseRegisterStartResponse(const std::string& response); + + // Parses register-complete server response. Initializes gaia (OAuth client) + // and receives refresh token. + void ParseRegisterCompleteResponse(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_; + + // Callback for parsing server response. + ParserCallback parse_response_callback_; + + // Privet context getter. + scoped_refptr<CloudPrintURLRequestContextGetter> context_getter_; + + // URL for completing registration and receiving OAuth account. + std::string polling_url_; + + // Last valid access_token. + std::string access_token_; + + // OAuth client information (client_id, client_secret, etc). + gaia::OAuthClientInfo oauth_client_info_; + + // OAuth client. + scoped_ptr<gaia::GaiaOAuthClient> gaia_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintRequester); +}; + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ + diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc new file mode 100644 index 0000000..fa2c5f7 --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc @@ -0,0 +1,133 @@ +// 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_response_parser.h" + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/values.h" + +namespace cloud_print_response_parser { + +// Parses json base::Value to base::DictionaryValue and checks |success| status. +// Returns |true| on success. +bool GetJsonDictinaryAndCheckSuccess(base::Value* json, + const ErrorCallback error_callback, + base::DictionaryValue** dictionary) { + base::DictionaryValue* response_dictionary = NULL; + + if (!json || !json->GetAsDictionary(&response_dictionary)) { + error_callback.Run("No JSON dictionary response received."); + return false; + } + + bool success = false; + if (!response_dictionary->GetBoolean("success", &success)) { + error_callback.Run("Cannot parse success state."); + return false; + } + + if (!success) { + std::string message; + response_dictionary->GetString("message", &message); + error_callback.Run(message); + return false; + } + + *dictionary = response_dictionary; + return true; +} + +bool ParseRegisterStartResponse(const std::string& response, + const ErrorCallback error_callback, + 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, + &response_dictionary)) { + return false; + } + + std::string registration_token; + if (!response_dictionary->GetString("registration_token", + ®istration_token)) { + error_callback.Run("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."); + return false; + } + + std::string polling_url; + if (!response_dictionary->GetString("polling_url", &polling_url)) { + error_callback.Run("No polling_url specified."); + return false; + } + + base::ListValue* list = NULL; + if (!response_dictionary->GetList("printers", &list)) { + error_callback.Run("No printers list specified."); + return false; + } + + base::Value* printer_value = NULL; + if (!list->Get(0, &printer_value)) { + error_callback.Run("Printers list is empty."); + return false; + } + + base::DictionaryValue* printer = NULL; + if (!printer_value->GetAsDictionary(&printer)) { + error_callback.Run("Printer is not a json-dictionary."); + return false; + } + + std::string device_id; + if (!printer->GetString("id", &device_id)) { + error_callback.Run("No id specified."); + return false; + } + + if (device_id.empty()) { + error_callback.Run("id is empty."); + return false; + } + + *polling_url_result = polling_url; + *registration_token_result = registration_token; + *complete_invite_url_result = complete_invite_url; + *device_id_result = device_id; + return true; +} + +bool ParseRegisterCompleteResponse(const std::string& response, + const ErrorCallback error_callback, + 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, + &response_dictionary)) { + return false; + } + + std::string authorization_code; + if (!response_dictionary->GetString("authorization_code", + &authorization_code)) { + error_callback.Run("Cannot parse authorization_code."); + return false; + } + + *authorization_code_result = authorization_code; + 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 new file mode 100644 index 0000000..1178301 --- /dev/null +++ b/cloud_print/gcp20/prototype/cloud_print_response_parser.h @@ -0,0 +1,43 @@ +// 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_RESPONSE_PARSER_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ + +#include <string> + +#include "base/callback.h" + +namespace base { + +class DictionaryValue; + +} // namespace base + +namespace cloud_print_response_parser { + +// Callback for handling parse errors. +typedef base::Callback<void(const std::string&)> ErrorCallback; + +// Parses CloudPrint register start response to out parameters. +// Returns |true| on success. Callback is called with description as a parameter +// when parsing failed. +bool ParseRegisterStartResponse(const std::string& response, + const ErrorCallback error_callback, + std::string* polling_url_result, + std::string* registration_token_result, + std::string* complete_invite_url_result, + std::string* device_id_result); + +// Parses CloudPrint register complete response to out parameters. +// Returns |true| on success. Callback is called with description as a parameter +// when parsing failed. +bool ParseRegisterCompleteResponse(const std::string& response, + const ErrorCallback error_callback, + std::string* authorization_code_result); + +} // namespace cloud_print_response_parser + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ + diff --git a/cloud_print/gcp20/prototype/dns_response_builder.cc b/cloud_print/gcp20/prototype/dns_response_builder.cc index 2bcd43f..c5ad602 100644 --- a/cloud_print/gcp20/prototype/dns_response_builder.cc +++ b/cloud_print/gcp20/prototype/dns_response_builder.cc @@ -34,6 +34,9 @@ DnsResponseBuilder::DnsResponseBuilder(uint16 id) { header_.arcount = 0; } +DnsResponseBuilder::~DnsResponseBuilder() { +} + void DnsResponseBuilder::AppendPtr(const std::string& service_type, uint32 ttl, const std::string& service_name) { std::string rdata; diff --git a/cloud_print/gcp20/prototype/dns_response_builder.h b/cloud_print/gcp20/prototype/dns_response_builder.h index 430a7e1..0d4a3eb 100644 --- a/cloud_print/gcp20/prototype/dns_response_builder.h +++ b/cloud_print/gcp20/prototype/dns_response_builder.h @@ -13,8 +13,10 @@ #include "net/dns/dns_protocol.h" namespace net { - class IOBufferWithSize; -} + +class IOBufferWithSize; + +} // namespace net // Record for storing response data. struct DnsResponseRecord { @@ -35,7 +37,7 @@ class DnsResponseBuilder { explicit DnsResponseBuilder(uint16 id); // Destroys the object. - ~DnsResponseBuilder() {} + ~DnsResponseBuilder(); // Methods for appending different types of responses to packet. void AppendPtr(const std::string& service_type, uint32 ttl, diff --git a/cloud_print/gcp20/prototype/dns_sd_server.h b/cloud_print/gcp20/prototype/dns_sd_server.h index d5ef793..a0dbb64 100644 --- a/cloud_print/gcp20/prototype/dns_sd_server.h +++ b/cloud_print/gcp20/prototype/dns_sd_server.h @@ -15,8 +15,10 @@ #include "net/udp/udp_socket.h" namespace net { - class IOBufferWithSize; -} + +class IOBufferWithSize; + +} // namespace net struct DnsQueryRecord; class DnsResponseBuilder; diff --git a/cloud_print/gcp20/prototype/gcp20_device.cc b/cloud_print/gcp20/prototype/gcp20_device.cc index cbd0c86..d58adc5 100644 --- a/cloud_print/gcp20/prototype/gcp20_device.cc +++ b/cloud_print/gcp20/prototype/gcp20_device.cc @@ -16,28 +16,23 @@ namespace { -Printer printer; -base::AtExitManager at_exit; - -void StopPrinter() { - printer.Stop(); -} - -void StartPrinter() { - bool success = printer.Start(); +void StartPrinter(Printer* printer) { + bool success = printer->Start(); DCHECK(success); - - at_exit.RegisterTask(base::Bind(&StopPrinter)); } +base::RunLoop* g_runner = NULL; + void OnAbort(int val) { - StopPrinter(); - base::MessageLoop::current()->Quit(); + if (g_runner) + g_runner->Quit(); } } // namespace int main(int argc, char* argv[]) { + base::AtExitManager at_exit; + Printer printer; CommandLine::Init(argc, argv); logging::LoggingSettings settings; @@ -47,9 +42,14 @@ int main(int argc, char* argv[]) { signal(SIGINT, OnAbort); // Handle Ctrl+C signal. base::MessageLoop loop(base::MessageLoop::TYPE_IO); - base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&StartPrinter)); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&StartPrinter, &printer)); base::RunLoop runner; + g_runner = &runner; runner.Run(); + g_runner = NULL; + printer.Stop(); return 0; } + diff --git a/cloud_print/gcp20/prototype/gcp20_device.gyp b/cloud_print/gcp20/prototype/gcp20_device.gyp index 8324b8b..c380d20 100644 --- a/cloud_print/gcp20/prototype/gcp20_device.gyp +++ b/cloud_print/gcp20/prototype/gcp20_device.gyp @@ -20,9 +20,17 @@ 'type': 'static_library', 'dependencies': [ '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/google_apis/google_apis.gyp:google_apis', + '<(DEPTH)/net/net.gyp:http_server', '<(DEPTH)/net/net.gyp:net', + '<(DEPTH)/url/url.gyp:url_lib', + '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', ], 'sources': [ + 'cloud_print_response_parser.cc', + 'cloud_print_response_parser.h', + 'cloud_print_requester.cc', + 'cloud_print_requester.h', 'dns_packet_parser.cc', 'dns_packet_parser.h', 'dns_response_builder.cc', @@ -32,6 +40,8 @@ 'gcp20_device.cc', 'printer.cc', 'printer.h', + 'privet_http_server.cc', + 'privet_http_server.h', 'service_parameters.cc', 'service_parameters.h', ], @@ -49,9 +59,10 @@ 'VCLinkerTool': { 'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE 'AdditionalDependencies': [ - 'secur32.lib', - 'httpapi.lib', - 'Ws2_32.lib', +# TODO(maksymb): Check which of whis libs is needed. + 'secur32.lib', + 'httpapi.lib', + 'Ws2_32.lib', ], }, }, diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc index 4d39fe8..dcbef5a 100644 --- a/cloud_print/gcp20/prototype/printer.cc +++ b/cloud_print/gcp20/prototype/printer.cc @@ -8,9 +8,12 @@ #include <vector> #include "base/command_line.h" +#include "base/file_util.h" +#include "base/guid.h" #include "base/strings/string_number_conversions.h" #include "cloud_print/gcp20/prototype/service_parameters.h" #include "net/base/net_util.h" +#include "net/base/url_util.h" namespace { @@ -18,9 +21,44 @@ const char* kServiceType = "_privet._tcp.local"; const char* kServiceNamePrefix = "first_gcp20_device"; const char* kServiceDomainName = "my-privet-device.local"; +const char* kPrinterName = "Google GCP2.0 Prototype"; + const uint16 kDefaultTTL = 60*60; const uint16 kDefaultHttpPort = 10101; +const char* kCdd = +"{\n" +" 'version': '1.0',\n" +" 'printer': {\n" +" 'vendor_capability': [\n" +" {\n" +" 'id': 'psk:MediaType',\n" +" 'display_name': 'Media Type',\n" +" 'type': 'SELECT',\n" +" 'select_cap': {\n" +" 'option': [\n" +" {\n" +" 'value': 'psk:Plain',\n" +" 'display_name': 'Plain Paper',\n" +" 'is_default': true\n" +" },\n" +" {\n" +" 'value': 'ns0000:Glossy',\n" +" 'display_name': 'Glossy Photo',\n" +" 'is_default': false\n" +" }\n" +" ]\n" +" }\n" +" }\n" +" ],\n" +" 'reverse_order': { 'default': false }\n" +" }\n" +"}\n"; + +std::string GenerateProxyId() { + return "{" + base::GenerateGUID() + "}"; +} + uint16 ReadHttpPortFromCommandLine() { uint32 http_port_tmp = kDefaultHttpPort; @@ -76,7 +114,13 @@ net::IPAddressNumber GetLocalIp(const std::string& interface_name, } // namespace -Printer::Printer() : initialized_(false) { +Printer::RegistrationInfo::RegistrationInfo() : state(DEV_REG_UNREGISTERED) { +} + +Printer::RegistrationInfo::~RegistrationInfo() { +} + +Printer::Printer() : http_server_(this) { } Printer::~Printer() { @@ -84,7 +128,7 @@ Printer::~Printer() { } bool Printer::Start() { - if (initialized_) + if (IsOnline()) return true; // TODO(maksymb): Add switch for command line to control interface name. @@ -95,35 +139,190 @@ bool Printer::Start() { } VLOG(1) << "Local address: " << net::IPAddressToString(ip); + uint16 port = ReadHttpPortFromCommandLine(); + + // TODO(maksymb): Add loading state from drive. + reg_info_ = RegistrationInfo(); + // Starting DNS-SD server. - initialized_ = dns_server_.Start( - ServiceParameters(kServiceType, - kServiceNamePrefix, - kServiceDomainName, - ip, - ReadHttpPortFromCommandLine()), + bool success = dns_server_.Start( + ServiceParameters(kServiceType, kServiceNamePrefix, kServiceDomainName, + ip, port), ReadTtlFromCommandLine(), CreateTxt()); - return initialized_; + if (!success) + return false; + + // Starting HTTP server. + success = http_server_.Start(port); + if (!success) + return false; + // TODO(maksymb): Check what happened if DNS server will start but HTTP + // server won't. + + // Creating Cloud Requester. + requester_.reset( + new CloudPrintRequester( + base::MessageLoop::current()->message_loop_proxy(), + this)); + + return true; } -void Printer::Stop() { - if (!initialized_) - return; +bool Printer::IsOnline() const { + return requester_; +} +void Printer::Stop() { dns_server_.Shutdown(); + http_server_.Shutdown(); + requester_.reset(); +} + +PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( + const std::string& user) { + PrivetHttpServer::RegistrationErrorStatus status; + if (!CheckRegistrationState(user, false, &status)) + return status; + + if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED) + return PrivetHttpServer::REG_ERROR_INVALID_ACTION; + + reg_info_ = RegistrationInfo(); + reg_info_.user = user; + reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_STARTED; + + requester_->StartRegistration(GenerateProxyId(), kPrinterName, user, kCdd); + + return PrivetHttpServer::REG_ERROR_OK; +} + +PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken( + const std::string& user, + std::string* token, + std::string* claim_url) { + PrivetHttpServer::RegistrationErrorStatus status; + if (!CheckRegistrationState(user, false, &status)) + return status; + + // TODO(maksymb): Add user confirmation. + + if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_STARTED) + return PrivetHttpServer::REG_ERROR_DEVICE_BUSY; + + if (reg_info_.state == + RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY) { + *token = reg_info_.registration_token; + *claim_url = reg_info_.complete_invite_url; + return PrivetHttpServer::REG_ERROR_OK; + } + + return PrivetHttpServer::REG_ERROR_INVALID_ACTION; +} + +PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete( + const std::string& user, + std::string* device_id) { + PrivetHttpServer::RegistrationErrorStatus status; + if (!CheckRegistrationState(user, false, &status)) + return status; + + if (reg_info_.state != + RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY) + return PrivetHttpServer::REG_ERROR_INVALID_ACTION; + + reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_COMPLETING; + requester_->CompleteRegistration(); + + *device_id = reg_info_.device_id; + + return PrivetHttpServer::REG_ERROR_OK; +} + +PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel( + const std::string& user) { + PrivetHttpServer::RegistrationErrorStatus status; + if (!CheckRegistrationState(user, true, &status)) + return status; + + if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED) + return PrivetHttpServer::REG_ERROR_INVALID_ACTION; + + reg_info_ = RegistrationInfo(); + return PrivetHttpServer::REG_ERROR_OK; +} + +void Printer::GetRegistrationServerError(std::string* description) { + DCHECK_EQ(reg_info_.state, RegistrationInfo::DEV_REG_REGISTRATION_ERROR) << + "Method shouldn't be called when not needed."; + + *description = reg_info_.error_description; +} + +void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { + *info = PrivetHttpServer::DeviceInfo(); + info->version = "1.0"; + info->manufacturer = "Google"; + if (reg_info_.state == RegistrationInfo::DEV_REG_UNREGISTERED) + info->api.push_back("/privet/register"); +} + +void Printer::OnRegistrationStartResponseParsed( + const std::string& registration_token, + const std::string& complete_invite_url, + const std::string& device_id) { + reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_CLAIM_TOKEN_READY; + reg_info_.device_id = device_id; + reg_info_.registration_token = registration_token; + reg_info_.complete_invite_url = complete_invite_url; +} + +void Printer::OnGetAuthCodeResponseParsed(const std::string& refresh_token) { + reg_info_.state = RegistrationInfo::DEV_REG_REGISTERED; + reg_info_.refresh_token = refresh_token; +} + +void Printer::OnRegistrationError(const std::string& description) { + LOG(ERROR) << "server_error: " << description; + + // TODO(maksymb): Implement waiting after error and timeout of registration. + reg_info_.state = RegistrationInfo::DEV_REG_REGISTRATION_ERROR; + reg_info_.error_description = description; +} + +bool Printer::CheckRegistrationState( + const std::string& user, + bool ignore_error, + PrivetHttpServer::RegistrationErrorStatus* status) const { + if (reg_info_.state == RegistrationInfo::DEV_REG_REGISTERED) { + *status = PrivetHttpServer::REG_ERROR_REGISTERED; + return false; + } + + if (reg_info_.state != RegistrationInfo::DEV_REG_UNREGISTERED && + user != reg_info_.user) { + *status = PrivetHttpServer::REG_ERROR_DEVICE_BUSY; + return false; + } + + if (!ignore_error && + reg_info_.state == RegistrationInfo::DEV_REG_REGISTRATION_ERROR) { + *status = PrivetHttpServer::REG_ERROR_SERVER_ERROR; + return false; + } + + return true; } std::vector<std::string> Printer::CreateTxt() const { std::vector<std::string> txt; - txt.push_back("txtvers=1"); - txt.push_back("ty=Google Cloud Ready Printer GCP2.0 Prototype"); - txt.push_back("note=Virtual printer"); + txt.push_back("ty=" + std::string(kPrinterName)); + txt.push_back("note="); txt.push_back("url=https://www.google.com/cloudprint"); txt.push_back("type=printer"); - txt.push_back("id="); + txt.push_back("id=" + reg_info_.device_id); txt.push_back("cs=offline"); return txt; diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h index d2d72b6..86c58b0 100644 --- a/cloud_print/gcp20/prototype/printer.h +++ b/cloud_print/gcp20/prototype/printer.h @@ -8,33 +8,107 @@ #include <string> #include <vector> +#include "cloud_print/gcp20/prototype/cloud_print_requester.h" #include "cloud_print/gcp20/prototype/dns_sd_server.h" +#include "cloud_print/gcp20/prototype/privet_http_server.h" -// This class will maintain work of DNS-SD server, HTTP server and others. -class Printer { +// This class maintain work of DNS-SD server, HTTP server and others. +class Printer : public PrivetHttpServer::Delegate, + public CloudPrintRequester::Delegate { public: // Constructs uninitialized object. Printer(); // Destroys the object. - ~Printer(); + virtual ~Printer(); // Starts all servers. bool Start(); + // Returns true if printer was started. + bool IsOnline() const; + // Stops all servers. void Stop(); private: + struct RegistrationInfo { + enum RegistrationState { + DEV_REG_UNREGISTERED = 0, + DEV_REG_REGISTRATION_STARTED, + DEV_REG_REGISTRATION_CLAIM_TOKEN_READY, + DEV_REG_REGISTRATION_COMPLETING, + DEV_REG_REGISTRATION_ERROR, + DEV_REG_REGISTERED, + }; + + RegistrationInfo(); + ~RegistrationInfo(); + + std::string user; + std::string refresh_token; + std::string device_id; + RegistrationState state; + + std::string registration_token; + std::string complete_invite_url; + + // Contains error response if |DEV_REG_REGISTRATION_ERROR| is set. + std::string error_description; + }; + + enum RegistrationAction { + REG_ACTION_START, + REG_ACTION_GET_CLAIM_TOKEN, + REG_ACTION_COMPLETE, + REG_ACTION_CANCEL + }; + + // PrivetHttpServer::Delegate methods: + virtual PrivetHttpServer::RegistrationErrorStatus RegistrationStart( + const std::string& user) OVERRIDE; + virtual PrivetHttpServer::RegistrationErrorStatus RegistrationGetClaimToken( + const std::string& user, + std::string* token, + std::string* claim_url) OVERRIDE; + virtual PrivetHttpServer::RegistrationErrorStatus RegistrationComplete( + const std::string& user, + std::string* device_id) OVERRIDE; + virtual PrivetHttpServer::RegistrationErrorStatus RegistrationCancel( + const std::string& user) OVERRIDE; + virtual void GetRegistrationServerError(std::string* description) OVERRIDE; + virtual void CreateInfo(PrivetHttpServer::DeviceInfo* info) OVERRIDE; + + // CloudRequester::Delegate methods: + virtual void OnRegistrationStartResponseParsed( + const std::string& registration_token, + const std::string& complete_invite_url, + const std::string& device_id) OVERRIDE; + virtual void OnGetAuthCodeResponseParsed( + const std::string& refresh_token) OVERRIDE; + virtual void OnRegistrationError(const std::string& description) OVERRIDE; + + // Checks if register call is called correctly (|user| is correct, + // error is no set etc). + bool CheckRegistrationState( + const std::string& user, + bool ignore_error, + PrivetHttpServer::RegistrationErrorStatus* status) const; + // Creates data for DNS TXT respond. std::vector<std::string> CreateTxt() const; - // Contains |true| if initialized. - bool initialized_; + RegistrationInfo reg_info_; // Contains DNS-SD server. DnsSdServer dns_server_; + // Contains Privet HTTP server. + PrivetHttpServer http_server_; + + // Contains Cloud Print client. + scoped_ptr<CloudPrintRequester> requester_; + DISALLOW_COPY_AND_ASSIGN(Printer); }; diff --git a/cloud_print/gcp20/prototype/privet_http_server.cc b/cloud_print/gcp20/prototype/privet_http_server.cc new file mode 100644 index 0000000..2114360 --- /dev/null +++ b/cloud_print/gcp20/prototype/privet_http_server.cc @@ -0,0 +1,255 @@ +// 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/privet_http_server.h" + +#include "base/json/json_writer.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/url_util.h" +#include "net/socket/tcp_listen_socket.h" +#include "url/gurl.h" + +namespace { + +// {"error":|error_type|} +scoped_ptr<base::DictionaryValue> CreateError(const std::string& error_type) { + scoped_ptr<base::DictionaryValue> error(new base::DictionaryValue); + error->SetString("error", error_type); + + return error.Pass(); +} + +// {"error":|error_type|, "description":|description|} +scoped_ptr<base::DictionaryValue> CreateErrorWithDescription( + const std::string& error_type, + const std::string& description) { + scoped_ptr<base::DictionaryValue> error(CreateError(error_type)); + error->SetString("description", description); + return error.Pass(); +} + +// {"error":|error_type|, "timeout":|timout|} +scoped_ptr<base::DictionaryValue> CreateErrorWithTimeout( + const std::string& error_type, + int timeout) { + scoped_ptr<base::DictionaryValue> error(CreateError(error_type)); + error->SetInteger("timeout", timeout); + return error.Pass(); +} + +} // namespace + +PrivetHttpServer::DeviceInfo::DeviceInfo() { +} + +PrivetHttpServer::DeviceInfo::~DeviceInfo() { +} + +PrivetHttpServer::PrivetHttpServer(Delegate* delegate) + : port_(0), + delegate_(delegate) { +} + +PrivetHttpServer::~PrivetHttpServer() { + Shutdown(); +} + +bool PrivetHttpServer::Start(uint16 port) { + if (server_) + return true; + + net::TCPListenSocketFactory factory("0.0.0.0", port); + server_ = new net::HttpServer(factory, this); + net::IPEndPoint address; + + if (server_->GetLocalAddress(&address) != net::OK) { + NOTREACHED() << "Cannot start HTTP server"; + } else { + VLOG(1) << "Address of HTTP server: " << address.ToString(); + } + + return true; +} + +void PrivetHttpServer::Shutdown() { + if (!server_) + return; + + server_ = NULL; +} + +void PrivetHttpServer::OnHttpRequest(int connection_id, + const net::HttpServerRequestInfo& info) { + VLOG(1) << "Processing HTTP request: " << info.path; + GURL url("http://host" + info.path); + + // TODO(maksymb): Add checking for X-PrivetToken. + std::string response; + net::HttpStatusCode status_code = ProcessHttpRequest(url, &response); + + server_->Send(connection_id, status_code, response, "text/plain"); +} + +void PrivetHttpServer::OnWebSocketRequest( + int connection_id, const net::HttpServerRequestInfo& info) { +} + +void PrivetHttpServer::OnWebSocketMessage(int connection_id, + const std::string& data) { +} + +void PrivetHttpServer::OnClose(int connection_id) { +} + +net::HttpStatusCode PrivetHttpServer::ProcessHttpRequest( + const GURL& url, + std::string* response) { + net::HttpStatusCode status_code = net::HTTP_OK; + scoped_ptr<base::DictionaryValue> json_response; + + if (url.path() == "/privet/info") { + json_response = ProcessInfo(&status_code); + } else if (url.path() == "/privet/register") { + json_response = ProcessRegister(url, &status_code); + } else { + response->clear(); + return net::HTTP_NOT_FOUND; + } + + if (!json_response) { + response->clear(); + return status_code; + } + + base::JSONWriter::WriteWithOptions(json_response.get(), + base::JSONWriter::OPTIONS_PRETTY_PRINT, + response); + return status_code; +} + +// Privet API methods: + +scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessInfo( + net::HttpStatusCode* status_code) const { + + DeviceInfo info; + delegate_->CreateInfo(&info); + + scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue); + response->SetString("version", info.version); + response->SetString("manufacturer", info.manufacturer); + + base::ListValue api; + for (size_t i = 0; i < info.api.size(); ++i) + api.AppendString(info.api[i]); + response->Set("api", api.DeepCopy()); + + *status_code = net::HTTP_OK; + return response.Pass(); +} + +scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessRegister( + const GURL& url, + net::HttpStatusCode* status_code) { + // TODO(maksymb): Add saving state to drive. + + std::string action; + if (!net::GetValueForKeyInQuery(url, "action", &action)) { + *status_code = net::HTTP_BAD_REQUEST; + return scoped_ptr<base::DictionaryValue>(); + } + + // TODO(maksymb): Is there a possibility |user| to be empty? + std::string user; + if (!net::GetValueForKeyInQuery(url, "user", &user) || user.empty()) { + *status_code = net::HTTP_BAD_REQUEST; + return scoped_ptr<base::DictionaryValue>(); + } + + RegistrationErrorStatus status = REG_ERROR_NO_RESULT; + scoped_ptr<base::DictionaryValue> response(new DictionaryValue); + response->SetString("action", action); + response->SetString("user", user); + + if (action == "start") + status = delegate_->RegistrationStart(user); + + if (action == "getClaimToken") { + std::string token; + std::string claim_url; + status = delegate_->RegistrationGetClaimToken(user, &token, &claim_url); + response->SetString("token", token); + response->SetString("claim_url", claim_url); + } + + if (action == "complete") { + std::string device_id; + status = delegate_->RegistrationComplete(user, &device_id); + response->SetString("device_id", device_id); + } + + if (action == "cancel") + status = delegate_->RegistrationCancel(user); + + if (status != REG_ERROR_OK) + response.reset(); + + ProcessRegistrationStatus(status, status_code, &response); + return response.Pass(); +} + +void PrivetHttpServer::ProcessRegistrationStatus( + RegistrationErrorStatus status, + net::HttpStatusCode *status_code, + scoped_ptr<base::DictionaryValue>* current_response) const { + switch (status) { + case REG_ERROR_OK: + *status_code = net::HTTP_OK; + DCHECK(*current_response) << "Response shouldn't be empty."; + break; + case REG_ERROR_NO_RESULT: + *status_code = net::HTTP_BAD_REQUEST; + current_response->reset(); + break; + case REG_ERROR_REGISTERED: + *status_code = net::HTTP_NOT_FOUND; + current_response->reset(); + break; + + case REG_ERROR_DEVICE_BUSY: + *status_code = net::HTTP_OK; + *current_response = CreateErrorWithTimeout("device_busy", 30); + break; + case REG_ERROR_PENDING_USER_ACTION: + *status_code = net::HTTP_OK; + *current_response = CreateErrorWithTimeout("pending_user_action", 30); + break; + case REG_ERROR_USER_CANCEL: + *status_code = net::HTTP_OK; + *current_response = CreateError("user_cancel"); + break; + case REG_ERROR_CONFIRMATION_TIMEOUT: + *status_code = net::HTTP_OK; + *current_response = CreateError("confirmation_timeout"); + break; + case REG_ERROR_INVALID_ACTION: + *status_code = net::HTTP_OK; + *current_response = CreateError("invalid_action"); + break; + case REG_ERROR_SERVER_ERROR: { + *status_code = net::HTTP_OK; + std::string description; + delegate_->GetRegistrationServerError(&description); + *current_response = CreateErrorWithDescription("server_error", + description); + break; + } + + default: + NOTREACHED(); + }; +} + + diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h new file mode 100644 index 0000000..b412bad --- /dev/null +++ b/cloud_print/gcp20/prototype/privet_http_server.h @@ -0,0 +1,135 @@ +// 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_PRIVET_HTTP_SERVER_H_ +#define CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/values.h" +#include "net/server/http_server.h" +#include "net/server/http_server_request_info.h" + +class GURL; + +// HTTP server for receiving . +class PrivetHttpServer: public net::HttpServer::Delegate { + public: + // TODO(maksymb): Move this enum to some namespace instead of this class. + enum RegistrationErrorStatus { + REG_ERROR_OK, + REG_ERROR_NO_RESULT, // default value, never set. + REG_ERROR_REGISTERED, + + REG_ERROR_DEVICE_BUSY, + REG_ERROR_PENDING_USER_ACTION, + REG_ERROR_USER_CANCEL, + REG_ERROR_CONFIRMATION_TIMEOUT, + REG_ERROR_INVALID_ACTION, + REG_ERROR_SERVER_ERROR + }; + + // TODO(maksymb): Move this struct to some namespace instead of this class. + struct DeviceInfo { + DeviceInfo(); + ~DeviceInfo(); + + std::string version; + std::string manufacturer; + std::vector<std::string> api; + }; + + class Delegate { + public: + Delegate() {} + + virtual ~Delegate() {} + + // Invoked when registration is starting. + virtual RegistrationErrorStatus RegistrationStart( + const std::string& user) = 0; + + // Invoked when claimtoken is needed. + virtual RegistrationErrorStatus RegistrationGetClaimToken( + const std::string& user, + std::string* token, + std::string* claim_url) = 0; + + // Invoked when registration is going to be completed. + virtual RegistrationErrorStatus RegistrationComplete( + const std::string& user, + std::string* device_id) = 0; + + // Invoked when client asked for cancelling the registration. + virtual RegistrationErrorStatus RegistrationCancel( + const std::string& user) = 0; + + // Invoked for receiving server error details. + virtual void GetRegistrationServerError(std::string* description) = 0; + + // Invoked if /privet/info is called. + virtual void CreateInfo(DeviceInfo* info) = 0; + }; + + // Constructor doesn't start server. + explicit PrivetHttpServer(Delegate* delegate); + + // Destroys the object. + virtual ~PrivetHttpServer(); + + // Starts HTTP server: start listening port |port| for HTTP requests. + bool Start(uint16 port); + + // Stops HTTP server. + void Shutdown(); + + private: + // net::HttpServer::Delegate methods: + virtual void OnHttpRequest( + int connection_id, + const net::HttpServerRequestInfo& info) OVERRIDE; + virtual void OnWebSocketRequest( + int connection_id, + const net::HttpServerRequestInfo& info) OVERRIDE; + virtual void OnWebSocketMessage(int connection_id, + const std::string& data) OVERRIDE; + virtual void OnClose(int connection_id) OVERRIDE; + + // Processes http request after all preparations (XPrivetHeader check, + // data handling etc.) + net::HttpStatusCode ProcessHttpRequest(const GURL& url, + std::string* response); + + // Pivet API methods. Return reference to NULL if output should be empty. + scoped_ptr<base::DictionaryValue> ProcessInfo( + net::HttpStatusCode* status_code) const; + scoped_ptr<base::DictionaryValue> ProcessReset( + net::HttpStatusCode* status_code); + scoped_ptr<base::DictionaryValue> ProcessRegister( + const GURL& url, + net::HttpStatusCode* status_code); + + // Proccesses current status and depending on it replaces (or not) + // |current_response| with error or empty response. + void ProcessRegistrationStatus( + RegistrationErrorStatus status, + net::HttpStatusCode *status_code, + scoped_ptr<base::DictionaryValue>* current_response) const; + + // Port for listening. + + uint16 port_; + + // Contains encapsulated object for listening for requests. + scoped_refptr<net::HttpServer> server_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(PrivetHttpServer); +}; + +#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_ + diff --git a/cloud_print/gcp20/prototype/service_parameters.cc b/cloud_print/gcp20/prototype/service_parameters.cc index e32581e..7433a89 100644 --- a/cloud_print/gcp20/prototype/service_parameters.cc +++ b/cloud_print/gcp20/prototype/service_parameters.cc @@ -7,6 +7,9 @@ ServiceParameters::ServiceParameters() : http_port_(0) { } +ServiceParameters::~ServiceParameters() { +} + ServiceParameters::ServiceParameters(const std::string& service_type, const std::string& service_name_prefix, const std::string& service_domain_name, diff --git a/cloud_print/gcp20/prototype/service_parameters.h b/cloud_print/gcp20/prototype/service_parameters.h index 79582ad..f86f870 100644 --- a/cloud_print/gcp20/prototype/service_parameters.h +++ b/cloud_print/gcp20/prototype/service_parameters.h @@ -13,7 +13,7 @@ struct ServiceParameters { ServiceParameters(); - ~ServiceParameters() {} + ~ServiceParameters(); ServiceParameters(const std::string& service_type, const std::string& service_name_prefix, |