diff options
author | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-20 00:50:02 +0000 |
---|---|---|
committer | maksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-20 00:50:02 +0000 |
commit | b4d45877a4e0dcc77fff031cdd59ab46878b8c2f (patch) | |
tree | 7ad1c98f5a8e5e01c2b38f3676f770b8e4e920b3 /cloud_print | |
parent | b8f6d6a77991a11c28beb33b0cfc65a8de6cd0f5 (diff) | |
download | chromium_src-b4d45877a4e0dcc77fff031cdd59ab46878b8c2f.zip chromium_src-b4d45877a4e0dcc77fff031cdd59ab46878b8c2f.tar.gz chromium_src-b4d45877a4e0dcc77fff031cdd59ab46878b8c2f.tar.bz2 |
GCP2.0 Device: Adding advanced printing.
BUG=
Review URL: https://chromiumcodereview.appspot.com/23271004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@218380 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print')
-rw-r--r-- | cloud_print/gcp20/prototype/gcp20_device.cc | 27 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/local_print_job.cc | 6 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/local_print_job.h | 14 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/print_job_handler.cc | 223 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/print_job_handler.h | 66 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.cc | 39 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/printer.h | 19 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.cc | 114 | ||||
-rw-r--r-- | cloud_print/gcp20/prototype/privet_http_server.h | 32 |
9 files changed, 480 insertions, 60 deletions
diff --git a/cloud_print/gcp20/prototype/gcp20_device.cc b/cloud_print/gcp20/prototype/gcp20_device.cc index 2d2c1d5..57972a3 100644 --- a/cloud_print/gcp20/prototype/gcp20_device.cc +++ b/cloud_print/gcp20/prototype/gcp20_device.cc @@ -20,21 +20,22 @@ const char kHelpMessage[] = "usage: gcp20_device [switches] [options]\n" "\n" "switches:\n" - " --disable-confirmation disables confirmation of registration\n" - " --disable-method-check disables HTTP method checking (POST, GET)\n" - " --disable-x-token disables checking of X-Privet-Token " - "HTTP header\n" - " -h, --help prints this message\n" - " --no-announcement disables DNS announcements\n" - " --unicast-respond DNS responses will be sent in unicast " - "instead of multicast\n" + " --disable-confirmation disables confirmation of registration\n" + " --disable-method-check disables HTTP method checking (POST, GET)\n" + " --disable-x-token disables checking of X-Privet-Token " + "HTTP header\n" + " -h, --help prints this message\n" + " --no-announcement disables DNS announcements\n" + " --simulate-printing-errors simulates some errors for local printing\n" + " --unicast-respond DNS responses will be sent in unicast " + "instead of multicast\n" "\n" "options:\n" - " --domain-name=<name> sets, should ends with '.local'\n" - " --http-port=<value> sets port for HTTP server\n" - " --service-name=<name> sets DNS service name\n" - " --state-path=<path> sets path to file with registration state\n" - " --ttl=<value> sets TTL for DNS announcements\n" + " --domain-name=<name> sets, should ends with '.local'\n" + " --http-port=<value> sets port for HTTP server\n" + " --service-name=<name> sets DNS service name\n" + " --state-path=<path> sets path to file with registration state\n" + " --ttl=<value> sets TTL for DNS announcements\n" "\n" "WARNING: mDNS probing is not implemented\n"; diff --git a/cloud_print/gcp20/prototype/local_print_job.cc b/cloud_print/gcp20/prototype/local_print_job.cc index b795c30..cafdecf 100644 --- a/cloud_print/gcp20/prototype/local_print_job.cc +++ b/cloud_print/gcp20/prototype/local_print_job.cc @@ -10,3 +10,9 @@ LocalPrintJob::LocalPrintJob() : offline(false) { LocalPrintJob::~LocalPrintJob() { } +LocalPrintJob::Info::Info() : state(STATE_DRAFT), expires_in(-1) { +} + +LocalPrintJob::Info::~Info() { +} + diff --git a/cloud_print/gcp20/prototype/local_print_job.h b/cloud_print/gcp20/prototype/local_print_job.h index b28e024..4959aff 100644 --- a/cloud_print/gcp20/prototype/local_print_job.h +++ b/cloud_print/gcp20/prototype/local_print_job.h @@ -25,6 +25,20 @@ struct LocalPrintJob { SAVE_PRINTER_ERROR, }; + enum State { + STATE_DRAFT, + STATE_ABORTED, + STATE_DONE, + }; + + struct Info { + Info(); + ~Info(); + + State state; + int expires_in; + }; + LocalPrintJob(); ~LocalPrintJob(); diff --git a/cloud_print/gcp20/prototype/print_job_handler.cc b/cloud_print/gcp20/prototype/print_job_handler.cc index 7c28560..7bbc1af 100644 --- a/cloud_print/gcp20/prototype/print_job_handler.cc +++ b/cloud_print/gcp20/prototype/print_job_handler.cc @@ -4,22 +4,66 @@ #include "cloud_print/gcp20/prototype/print_job_handler.h" +#include "base/bind.h" +#include "base/command_line.h" #include "base/file_util.h" #include "base/format_macros.h" #include "base/guid.h" #include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/rand_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" namespace { -const int kLocalPrintJobInitExpiration = 5*60; // in seconds +const int kDraftExpirationSec = 10; +const int kLocalPrintJobExpirationSec = 20; +const int kErrorTimeoutSec = 30; + +// Errors simulation constants: +const double kPaperJamProbability = 0.2; +const int kTooManyDraftsTimeout = 10; +const int kMaxDrafts = 5; const base::FilePath::CharType kJobsPath[] = FILE_PATH_LITERAL("printjobs"); +bool ValidateTicket(const std::string& ticket) { + return true; +} + +std::string GenerateId() { + return StringToLowerASCII(base::GenerateGUID()); +} + } // namespace +struct PrintJobHandler::LocalPrintJobExtended { + LocalPrintJobExtended(const LocalPrintJob& job, const std::string& ticket) + : data(job), + ticket(ticket), + state(LocalPrintJob::STATE_DRAFT) {} + LocalPrintJobExtended() : state(LocalPrintJob::STATE_DRAFT) {} + ~LocalPrintJobExtended() {} + + LocalPrintJob data; + std::string ticket; + LocalPrintJob::State state; + base::Time expiration; +}; + +struct PrintJobHandler::LocalPrintJobDraft { + LocalPrintJobDraft() {} + LocalPrintJobDraft(const std::string& ticket, const base::Time& expiration) + : ticket(ticket), + expiration(expiration) {} + ~LocalPrintJobDraft() {} + + std::string ticket; + base::Time expiration; +}; + using base::StringPrintf; PrintJobHandler::PrintJobHandler() { @@ -28,11 +72,87 @@ PrintJobHandler::PrintJobHandler() { PrintJobHandler::~PrintJobHandler() { } +LocalPrintJob::CreateResult PrintJobHandler::CreatePrintJob( + const std::string& ticket, + std::string* job_id_out, + // TODO(maksymb): Use base::TimeDelta for timeout values + int* expires_in_out, + // TODO(maksymb): Use base::TimeDelta for timeout values + int* error_timeout_out, + std::string* error_description) { + if (!ValidateTicket(ticket)) + return LocalPrintJob::CREATE_INVALID_TICKET; + + // Let's simulate at least some errors just for testing. + if (CommandLine::ForCurrentProcess()->HasSwitch("simulate-printing-errors")) { + if (base::RandDouble() <= kPaperJamProbability) { + *error_description = "Paper jam, try again"; + return LocalPrintJob::CREATE_PRINTER_ERROR; + } + + if (drafts.size() > kMaxDrafts) { // Another simulation of error: business + *error_timeout_out = kTooManyDraftsTimeout; + return LocalPrintJob::CREATE_PRINTER_BUSY; + } + } + + std::string id = GenerateId(); + drafts[id] = LocalPrintJobDraft( + ticket, + base::Time::Now() + base::TimeDelta::FromSeconds(kDraftExpirationSec)); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&PrintJobHandler::ForgetDraft, AsWeakPtr(), id), + base::TimeDelta::FromSeconds(kDraftExpirationSec)); + + *job_id_out = id; + *expires_in_out = kDraftExpirationSec; + return LocalPrintJob::CREATE_SUCCESS; +} + LocalPrintJob::SaveResult PrintJobHandler::SaveLocalPrintJob( const LocalPrintJob& job, - std::string* job_id, - std::string* error_description, - int* timeout) { + std::string* job_id_out, + int* expires_in_out, + std::string* error_description_out, + int* timeout_out) { + std::string id; + int expires_in; + + switch (CreatePrintJob(std::string(), &id, &expires_in, + timeout_out, error_description_out)) { + case LocalPrintJob::CREATE_INVALID_TICKET: + NOTREACHED(); + return LocalPrintJob::SAVE_SUCCESS; + + case LocalPrintJob::CREATE_PRINTER_BUSY: + return LocalPrintJob::SAVE_PRINTER_BUSY; + + case LocalPrintJob::CREATE_PRINTER_ERROR: + return LocalPrintJob::SAVE_PRINTER_ERROR; + + case LocalPrintJob::CREATE_SUCCESS: + *job_id_out = id; + return CompleteLocalPrintJob(job, id, expires_in_out, + error_description_out, timeout_out); + + default: + NOTREACHED(); + return LocalPrintJob::SAVE_SUCCESS; + } +} + +LocalPrintJob::SaveResult PrintJobHandler::CompleteLocalPrintJob( + const LocalPrintJob& job, + const std::string& job_id, + int* expires_in_out, + std::string* error_description_out, + int* timeout_out) { + if (!drafts.count(job_id)) { + *timeout_out = kErrorTimeoutSec; + return LocalPrintJob::SAVE_INVALID_PRINT_JOB; + } + std::string suffix = StringPrintf("%s:%s", job.user_name.c_str(), job.client_name.c_str()); @@ -46,23 +166,50 @@ LocalPrintJob::SaveResult PrintJobHandler::SaveLocalPrintJob( } else if (job.content_type == "image/jpeg") { file_extension = "jpg"; } else { - error_description->clear(); + error_description_out->clear(); return LocalPrintJob::SAVE_INVALID_DOCUMENT_TYPE; } + CompleteDraft(job_id, job); + std::map<std::string, LocalPrintJobExtended>::iterator current_job = + jobs.find(job_id); - std::string id = StringToLowerASCII(base::GenerateGUID()); - if (!SavePrintJob(job.content, std::string(), - base::Time::Now(), - StringPrintf("%s", id.c_str()), - suffix, job.job_name, file_extension)) { - *error_description = "IO error"; + if (!SavePrintJob(current_job->second.data.content, + current_job->second.ticket, + base::Time::Now(), + StringPrintf("%s", job_id.c_str()), + suffix, + current_job->second.data.job_name, file_extension)) { + SetJobState(job_id, LocalPrintJob::STATE_ABORTED); + *error_description_out = "IO error"; return LocalPrintJob::SAVE_PRINTER_ERROR; } - *job_id = id; + SetJobState(job_id, LocalPrintJob::STATE_DONE); + *expires_in_out = static_cast<int>(GetJobExpiration(job_id).InSeconds()); return LocalPrintJob::SAVE_SUCCESS; } +bool PrintJobHandler::GetJobState(const std::string& id, + LocalPrintJob::Info* info_out) { + using base::Time; + + std::map<std::string, LocalPrintJobDraft>::iterator draft = drafts.find(id); + if (draft != drafts.end()) { + info_out->state = LocalPrintJob::STATE_DRAFT; + info_out->expires_in = + static_cast<int>((draft->second.expiration - Time::Now()).InSeconds()); + return true; + } + + std::map<std::string, LocalPrintJobExtended>::iterator job = jobs.find(id); + if (job != jobs.end()) { + info_out->state = job->second.state; + info_out->expires_in = static_cast<int>(GetJobExpiration(id).InSeconds()); + return true; + } + return false; +} + bool PrintJobHandler::SavePrintJob(const std::string& content, const std::string& ticket, const base::Time& create_time, @@ -123,3 +270,55 @@ bool PrintJobHandler::SavePrintJob(const std::string& content, return true; } +void PrintJobHandler::SetJobState(const std::string& id, + LocalPrintJob::State state) { + DCHECK(!drafts.count(id)) << "Draft should be completed at first"; + + std::map<std::string, LocalPrintJobExtended>::iterator job = jobs.find(id); + DCHECK(job != jobs.end()); + job->second.state = state; + switch (state) { + case LocalPrintJob::STATE_DRAFT: + NOTREACHED(); + break; + case LocalPrintJob::STATE_ABORTED: + case LocalPrintJob::STATE_DONE: + job->second.expiration = + base::Time::Now() + + base::TimeDelta::FromSeconds(kLocalPrintJobExpirationSec); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&PrintJobHandler::ForgetLocalJob, AsWeakPtr(), id), + base::TimeDelta::FromSeconds(kLocalPrintJobExpirationSec)); + break; + default: + NOTREACHED(); + } +} + +void PrintJobHandler::CompleteDraft(const std::string& id, + const LocalPrintJob& job) { + std::map<std::string, LocalPrintJobDraft>::iterator draft = drafts.find(id); + if (draft != drafts.end()) { + jobs[id] = LocalPrintJobExtended(job, draft->second.ticket); + drafts.erase(draft); + } +} + +// TODO(maksymb): Use base::Time for expiration +base::TimeDelta PrintJobHandler::GetJobExpiration(const std::string& id) const { + DCHECK(jobs.count(id)); + base::Time expiration = jobs.at(id).expiration; + if (expiration.is_null()) + return base::TimeDelta::FromSeconds(kLocalPrintJobExpirationSec); + return expiration - base::Time::Now(); +} + +void PrintJobHandler::ForgetDraft(const std::string& id) { + drafts.erase(id); +} + +void PrintJobHandler::ForgetLocalJob(const std::string& id) { + jobs.erase(id); +} + diff --git a/cloud_print/gcp20/prototype/print_job_handler.h b/cloud_print/gcp20/prototype/print_job_handler.h index fc48a1e..fe59c7f 100644 --- a/cloud_print/gcp20/prototype/print_job_handler.h +++ b/cloud_print/gcp20/prototype/print_job_handler.h @@ -5,9 +5,12 @@ #ifndef CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ #define CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ +#include <map> #include <string> #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "cloud_print/gcp20/prototype/local_print_job.h" namespace base { @@ -17,18 +20,39 @@ class Time; } // namespace base -class PrintJobHandler { +class PrintJobHandler : public base::SupportsWeakPtr<PrintJobHandler> { public: PrintJobHandler(); ~PrintJobHandler(); - LocalPrintJob::CreateResult CreatePrintJob(std::string* job_id, - int* expires_in); - LocalPrintJob::SaveResult SaveLocalPrintJob(const LocalPrintJob& job, - std::string* job_id, - std::string* error_description, - int* timeout); + // Creates printer job draft + LocalPrintJob::CreateResult CreatePrintJob( + const std::string& ticket, + std::string* job_id_out, + int* expires_in_out, + int* error_timeout_out, + std::string* error_description_out); + // Creates printer job with empty ticket and "prints" it + LocalPrintJob::SaveResult SaveLocalPrintJob( + const LocalPrintJob& job, + std::string* job_id_out, + int* expires_in_out, + std::string* error_description_out, + int* timeout_out); + + // Completes printer job from draft + LocalPrintJob::SaveResult CompleteLocalPrintJob( + const LocalPrintJob& job, + const std::string& job_id, + int* expires_in_out, + std::string* error_description_out, + int* timeout_out); + + // Gives info about job + bool GetJobState(const std::string& id, LocalPrintJob::Info* info_out); + + // Saving print job directly to drive bool SavePrintJob(const std::string& content, const std::string& ticket, const base::Time& create_time, @@ -38,6 +62,34 @@ class PrintJobHandler { const std::string& file_extension); private: + // Contains ticket info and job info together + struct LocalPrintJobExtended; + + // Contains job ticket + struct LocalPrintJobDraft; + + // Contains all unexpired drafts + std::map<std::string, LocalPrintJobDraft> drafts; // id -> draft + + // Contains all unexpired jobs + std::map<std::string, LocalPrintJobExtended> jobs; // id -> printjob + + // Changes job state and creates timeouts to delete old jobs from memory + void SetJobState(const std::string& id, LocalPrintJob::State); + + // Moves draft to jobs + void CompleteDraft(const std::string& id, const LocalPrintJob& job); + + // Calculates expiration for job + // TODO(maksymb): Use base::Time for expiration + base::TimeDelta GetJobExpiration(const std::string& id) const; + + // Erases draft from memory + void ForgetDraft(const std::string& id); + + // Erases job from memory + void ForgetLocalJob(const std::string& id); + DISALLOW_COPY_AND_ASSIGN(PrintJobHandler); }; diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc index 197f70d..9513979 100644 --- a/cloud_print/gcp20/prototype/printer.cc +++ b/cloud_print/gcp20/prototype/printer.cc @@ -342,12 +342,15 @@ void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { // TODO(maksymb): Create enum for available APIs and replace // this API text names with constants from enum. API text names should be only // known in PrivetHttpServer. - if (state_.registration_state == PrinterState::UNREGISTERED) { + if (!IsRegistered()) { info->api.push_back("/privet/register"); } else { - if (IsLocalPrintingAllowed()) - info->api.push_back("/privet/printer/submitdoc"); info->api.push_back("/privet/capabilities"); + if (IsLocalPrintingAllowed()) { + info->api.push_back("/privet/printer/createjob"); + info->api.push_back("/privet/printer/submitdoc"); + info->api.push_back("/privet/printer/jobstate"); + } } info->type.push_back("printer"); @@ -372,22 +375,36 @@ scoped_ptr<base::DictionaryValue> Printer::GetCapabilities() { return scoped_ptr<base::DictionaryValue>(dictionary_value->DeepCopy()); } -void Printer::CreateJob(const std::string& ticket) { - // TODO(maksymb): Implement advanced printing - NOTIMPLEMENTED(); +LocalPrintJob::CreateResult Printer::CreateJob(const std::string& ticket, + std::string* job_id, + int* expires_in, + int* error_timeout, + std::string* error_description) { + return print_job_handler_->CreatePrintJob(ticket, job_id, expires_in, + error_timeout, error_description); } LocalPrintJob::SaveResult Printer::SubmitDoc(const LocalPrintJob& job, std::string* job_id, + int* expires_in, std::string* error_description, int* timeout) { - return print_job_handler_->SaveLocalPrintJob(job, job_id, error_description, - timeout); + return print_job_handler_->SaveLocalPrintJob(job, job_id, expires_in, + error_description, timeout); +} + +LocalPrintJob::SaveResult Printer::SubmitDocWithId( + const LocalPrintJob& job, + const std::string& job_id, + int* expires_in, + std::string* error_description, + int* timeout) { + return print_job_handler_->CompleteLocalPrintJob(job, job_id, expires_in, + error_description, timeout); } -void Printer::GetJobStatus(int job_id) { - // TODO(maksymb): Implement advanced printing - NOTIMPLEMENTED(); +bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) { + return print_job_handler_->GetJobState(id, info); } void Printer::OnRegistrationStartResponseParsed( diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h index ccc97b3..69e8874 100644 --- a/cloud_print/gcp20/prototype/printer.h +++ b/cloud_print/gcp20/prototype/printer.h @@ -20,8 +20,6 @@ extern const base::FilePath::CharType kPrinterStatePath[]; -struct LocalPrintJob; - // This class maintains work of DNS-SD server, HTTP server and others. class Printer : public base::SupportsWeakPtr<Printer>, public PrivetHttpServer::Delegate, @@ -74,13 +72,26 @@ class Printer : public base::SupportsWeakPtr<Printer>, virtual bool IsLocalPrintingAllowed() const OVERRIDE; virtual bool CheckXPrivetTokenHeader(const std::string& token) const OVERRIDE; virtual scoped_ptr<base::DictionaryValue> GetCapabilities() OVERRIDE; - virtual void CreateJob(const std::string& ticket) OVERRIDE; + virtual LocalPrintJob::CreateResult CreateJob( + const std::string& ticket, + std::string* job_id, + int* expires_in, + int* error_timeout, + std::string* error_description) OVERRIDE; virtual LocalPrintJob::SaveResult SubmitDoc( const LocalPrintJob& job, std::string* job_id, + int* expires_in, + std::string* error_description, + int* timeout) OVERRIDE; + virtual LocalPrintJob::SaveResult SubmitDocWithId( + const LocalPrintJob& job, + const std::string& job_id, + int* expires_in, std::string* error_description, int* timeout) OVERRIDE; - virtual void GetJobStatus(int job_id) OVERRIDE; + virtual bool GetJobState(const std::string& id, + LocalPrintJob::Info* info) OVERRIDE; // CloudRequester::Delegate methods: virtual void OnRegistrationStartResponseParsed( diff --git a/cloud_print/gcp20/prototype/privet_http_server.cc b/cloud_print/gcp20/prototype/privet_http_server.cc index cb34b9a..4251a4fe 100644 --- a/cloud_print/gcp20/prototype/privet_http_server.cc +++ b/cloud_print/gcp20/prototype/privet_http_server.cc @@ -52,15 +52,36 @@ scoped_ptr<base::DictionaryValue> CreateErrorWithTimeout( return error.Pass(); } +// Converts state to string. +std::string LocalPrintJobStateToString(LocalPrintJob::State state) { + switch (state) { + case LocalPrintJob::STATE_DRAFT: + return "draft"; + break; + case LocalPrintJob::STATE_ABORTED: + return "done"; + break; + case LocalPrintJob::STATE_DONE: + return "done"; + break; + default: + NOTREACHED(); + return std::string(); + } +} + + // Returns |true| if |request| should be GET method. bool IsGetMethod(const std::string& request) { return request == kPrivetInfo || - request == kPrivetCapabilities; + request == kPrivetCapabilities || + request == kPrivetPrinterJobState; } // Returns |true| if |request| should be POST method. bool IsPostMethod(const std::string& request) { return request == kPrivetRegister || + request == kPrivetPrinterCreateJob || request == kPrivetPrinterSubmitDoc; } @@ -191,8 +212,12 @@ net::HttpStatusCode PrivetHttpServer::ProcessHttpRequest( json_response = ProcessRegister(url, &status_code); } else if (url.path() == kPrivetCapabilities) { json_response = ProcessCapabilities(&status_code); + } else if (url.path() == kPrivetPrinterCreateJob) { + json_response = ProcessCreateJob(url, info.data, &status_code); } else if (url.path() == kPrivetPrinterSubmitDoc) { json_response = ProcessSubmitDoc(url, info, &status_code); + } else if (url.path() == kPrivetPrinterJobState) { + json_response = ProcessJobState(url, &status_code); } else { NOTREACHED(); } @@ -255,6 +280,45 @@ scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessCapabilities( return delegate_->GetCapabilities(); } +scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessCreateJob( + const GURL& url, + const std::string& body, + net::HttpStatusCode* status_code) const { + if (!delegate_->IsRegistered() || !delegate_->IsLocalPrintingAllowed()) { + *status_code = net::HTTP_NOT_FOUND; + return scoped_ptr<base::DictionaryValue>(); + } + + std::string job_id; + // TODO(maksymb): Use base::Time for expiration + int expires_in = 0; + // TODO(maksymb): Use base::TimeDelta for timeout values + int error_timeout = -1; + std::string error_description; + + LocalPrintJob::CreateResult result = + delegate_->CreateJob(body, &job_id, &expires_in, + &error_timeout, &error_description); + + scoped_ptr<base::DictionaryValue> response; + *status_code = net::HTTP_OK; + switch (result) { + case LocalPrintJob::CREATE_SUCCESS: + response.reset(new DictionaryValue); + response->SetString("job_id", job_id); + response->SetInteger("expires_in", expires_in); + return response.Pass(); + + case LocalPrintJob::CREATE_INVALID_TICKET: + return CreateError("invalid_ticket"); + case LocalPrintJob::CREATE_PRINTER_BUSY: + return CreateErrorWithTimeout("printer_busy", error_timeout); + case LocalPrintJob::CREATE_PRINTER_ERROR: + return CreateErrorWithDescription("printer_error", error_description); + } + return scoped_ptr<base::DictionaryValue>(); +} + scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessSubmitDoc( const GURL& url, const net::HttpServerRequestInfo& info, @@ -264,24 +328,31 @@ scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessSubmitDoc( return scoped_ptr<base::DictionaryValue>(); } + using net::GetValueForKeyInQuery; + // Parse query LocalPrintJob job; std::string offline; - net::GetValueForKeyInQuery(url, "client_name", &job.client_name); - bool job_name_present = - net::GetValueForKeyInQuery(url, "job_name", &job.job_name); - net::GetValueForKeyInQuery(url, "user_name", &job.user_name); - net::GetValueForKeyInQuery(url, "offline", &offline); + std::string job_id; + bool job_name_present = GetValueForKeyInQuery(url, "job_name", &job.job_name); + bool job_id_present = GetValueForKeyInQuery(url, "job_id", &job_id); + GetValueForKeyInQuery(url, "client_name", &job.client_name); + GetValueForKeyInQuery(url, "user_name", &job.user_name); + GetValueForKeyInQuery(url, "offline", &offline); job.offline = (offline == "1"); job.content_type = info.GetHeaderValue("content-type"); job.content = info.data; // Call delegate - std::string job_id; + // TODO(maksymb): Use base::Time for expiration + int expires_in = 0; std::string error_description; int timeout; - LocalPrintJob::SaveResult status = - delegate_->SubmitDoc(job, &job_id, &error_description, &timeout); + LocalPrintJob::SaveResult status = job_id_present + ? delegate_->SubmitDocWithId(job, job_id, &expires_in, + &error_description, &timeout) + : delegate_->SubmitDoc(job, &job_id, &expires_in, + &error_description, &timeout); // Create response *status_code = net::HTTP_OK; @@ -289,7 +360,7 @@ scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessSubmitDoc( switch (status) { case LocalPrintJob::SAVE_SUCCESS: response->SetString("job_id", job_id); - response->SetInteger("expires_in", -1); + response->SetInteger("expires_in", expires_in); response->SetString("job_type", job.content_type); response->SetString( "job_size", @@ -299,7 +370,7 @@ scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessSubmitDoc( return response.Pass(); case LocalPrintJob::SAVE_INVALID_PRINT_JOB: - return CreateError("invalid_print_job"); + return CreateErrorWithTimeout("invalid_print_job", timeout); case LocalPrintJob::SAVE_INVALID_DOCUMENT_TYPE: return CreateError("invalid_document_type"); case LocalPrintJob::SAVE_INVALID_DOCUMENT: @@ -316,6 +387,27 @@ scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessSubmitDoc( } } +scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessJobState( + const GURL& url, + net::HttpStatusCode* status_code) const { + if (!delegate_->IsRegistered() || !delegate_->IsLocalPrintingAllowed()) { + *status_code = net::HTTP_NOT_FOUND; + return scoped_ptr<base::DictionaryValue>(); + } + + std::string job_id; + net::GetValueForKeyInQuery(url, "job_id", &job_id); + LocalPrintJob::Info info; + if (!delegate_->GetJobState(job_id, &info)) + return CreateError("invalid_print_job"); + + scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue); + response->SetString("job_id", job_id); + response->SetString("state", LocalPrintJobStateToString(info.state)); + response->SetInteger("expires_in", info.expires_in); + return response.Pass(); +} + scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessRegister( const GURL& url, net::HttpStatusCode* status_code) const { diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h index db113de..eb082c6 100644 --- a/cloud_print/gcp20/prototype/privet_http_server.h +++ b/cloud_print/gcp20/prototype/privet_http_server.h @@ -98,17 +98,33 @@ class PrivetHttpServer: public net::HttpServer::Delegate { virtual scoped_ptr<base::DictionaryValue> GetCapabilities() = 0; // Invoked for creating a job. - virtual void CreateJob(const std::string& ticket) = 0; + virtual LocalPrintJob::CreateResult CreateJob( + const std::string& ticket, + std::string* job_id, + int* expires_in, + // TODO(maksymb): Use base::TimeDelta for timeouts + int* error_timeout, + std::string* error_description) = 0; // Invoked for simple local printing. virtual LocalPrintJob::SaveResult SubmitDoc( const LocalPrintJob& job, std::string* job_id, + int* expires_in, + std::string* error_description, + int* timeout) = 0; + + // Invoked for advanced local printing. + virtual LocalPrintJob::SaveResult SubmitDocWithId( + const LocalPrintJob& job, + const std::string& job_id, + int* expires_in, std::string* error_description, int* timeout) = 0; // Invoked for getting job status. - virtual void GetJobStatus(int job_id) = 0; + virtual bool GetJobState(const std::string& job_id, + LocalPrintJob::Info* info) = 0; }; // Constructor doesn't start server. @@ -155,12 +171,24 @@ class PrivetHttpServer: public net::HttpServer::Delegate { // 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> ProcessCapabilities( net::HttpStatusCode* status_code) const; + + scoped_ptr<base::DictionaryValue> ProcessCreateJob( + const GURL& url, + const std::string& body, + net::HttpStatusCode* status_code) const; + scoped_ptr<base::DictionaryValue> ProcessSubmitDoc( const GURL& url, const net::HttpServerRequestInfo& info, net::HttpStatusCode* status_code) const; + + scoped_ptr<base::DictionaryValue> ProcessJobState( + const GURL& url, + net::HttpStatusCode* status_code) const; + scoped_ptr<base::DictionaryValue> ProcessRegister( const GURL& url, net::HttpStatusCode* status_code) const; |