Home | History | Annotate | Download | only in local_discovery
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/local_discovery/privet_http_impl.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/rand_util.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "chrome/browser/local_discovery/privet_constants.h"
     15 #include "net/base/url_util.h"
     16 #include "url/gurl.h"
     17 
     18 namespace local_discovery {
     19 
     20 namespace {
     21 const char kUrlPlaceHolder[] = "http://host/";
     22 const char kPrivetRegisterActionArgName[] = "action";
     23 const char kPrivetRegisterUserArgName[] = "user";
     24 
     25 const char kPrivetURLKeyUserName[] = "user_name";
     26 const char kPrivetURLKeyClientName[] = "client_name";
     27 const char kPrivetURLKeyJobname[] = "job_name";
     28 const char kPrivetURLKeyOffline[] = "offline";
     29 const char kPrivetURLValueOffline[] = "1";
     30 const char kPrivetURLValueClientName[] = "Chrome";
     31 
     32 const char kPrivetContentTypePDF[] = "application/pdf";
     33 const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
     34 const char kPrivetContentTypeAny[] = "*/*";
     35 const char kPrivetContentTypeCJT[] = "application/json";
     36 
     37 const char kPrivetCDDKeySupportedContentTypes[] =
     38     "printer.supported_content_type";
     39 
     40 const char kPrivetCDDKeyContentType[] = "content_type";
     41 
     42 const char kPrivetKeyJobID[] = "job_id";
     43 
     44 const int kPrivetCancelationTimeoutSeconds = 3;
     45 
     46 const int kPrivetLocalPrintMaxRetries = 2;
     47 
     48 const int kPrivetLocalPrintDefaultTimeout = 5;
     49 
     50 GURL CreatePrivetURL(const std::string& path) {
     51   GURL url(kUrlPlaceHolder);
     52   GURL::Replacements replacements;
     53   replacements.SetPathStr(path);
     54   return url.ReplaceComponents(replacements);
     55 }
     56 
     57 GURL CreatePrivetRegisterURL(const std::string& action,
     58                              const std::string& user) {
     59   GURL url = CreatePrivetURL(kPrivetRegisterPath);
     60   url = net::AppendQueryParameter(url, kPrivetRegisterActionArgName, action);
     61   return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user);
     62 }
     63 
     64 }  // namespace
     65 
     66 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
     67     PrivetHTTPClientImpl* privet_client,
     68     PrivetInfoOperation::Delegate* delegate)
     69     : privet_client_(privet_client), delegate_(delegate) {
     70 }
     71 
     72 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
     73 }
     74 
     75 void PrivetInfoOperationImpl::Start() {
     76   url_fetcher_ = privet_client_->CreateURLFetcher(
     77       CreatePrivetURL(kPrivetInfoPath), net::URLFetcher::GET, this);
     78 
     79   url_fetcher_->DoNotRetryOnTransientError();
     80   url_fetcher_->AllowEmptyPrivetToken();
     81 
     82   url_fetcher_->Start();
     83 }
     84 
     85 PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
     86   return privet_client_;
     87 }
     88 
     89 void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
     90                                       PrivetURLFetcher::ErrorType error) {
     91   if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
     92     delegate_->OnPrivetInfoDone(this, fetcher->response_code(), NULL);
     93   } else {
     94     delegate_->OnPrivetInfoDone(this, kPrivetHTTPCodeInternalFailure, NULL);
     95   }
     96 }
     97 
     98 void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
     99                                            const base::DictionaryValue* value,
    100                                            bool has_error) {
    101   if (!has_error)
    102     privet_client_->CacheInfo(value);
    103   delegate_->OnPrivetInfoDone(this, fetcher->response_code(), value);
    104 }
    105 
    106 PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
    107     PrivetHTTPClientImpl* privet_client,
    108     const std::string& user,
    109     PrivetRegisterOperation::Delegate* delegate)
    110     : user_(user), delegate_(delegate), privet_client_(privet_client),
    111       ongoing_(false) {
    112 }
    113 
    114 PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
    115 }
    116 
    117 void PrivetRegisterOperationImpl::Start() {
    118   ongoing_ = true;
    119   next_response_handler_ =
    120       base::Bind(&PrivetRegisterOperationImpl::StartResponse,
    121                  base::Unretained(this));
    122   SendRequest(kPrivetActionStart);
    123 }
    124 
    125 void PrivetRegisterOperationImpl::Cancel() {
    126   url_fetcher_.reset();
    127 
    128   if (ongoing_) {
    129     // Owned by the message loop.
    130     Cancelation* cancelation = new Cancelation(privet_client_, user_);
    131 
    132     base::MessageLoop::current()->PostDelayedTask(
    133         FROM_HERE,
    134         base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
    135                    base::Owned(cancelation)),
    136         base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds));
    137 
    138     ongoing_ = false;
    139   }
    140 }
    141 
    142 void PrivetRegisterOperationImpl::CompleteRegistration() {
    143   next_response_handler_ =
    144       base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
    145                  base::Unretained(this));
    146   SendRequest(kPrivetActionComplete);
    147 }
    148 
    149 PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
    150   return privet_client_;
    151 }
    152 
    153 void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher,
    154                                           PrivetURLFetcher::ErrorType error) {
    155   ongoing_ = false;
    156   int visible_http_code = -1;
    157   FailureReason reason = FAILURE_NETWORK;
    158 
    159   if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
    160     visible_http_code = fetcher->response_code();
    161     reason = FAILURE_HTTP_ERROR;
    162   } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) {
    163     reason = FAILURE_MALFORMED_RESPONSE;
    164   } else if (error == PrivetURLFetcher::TOKEN_ERROR) {
    165     reason = FAILURE_TOKEN;
    166   } else if (error == PrivetURLFetcher::RETRY_ERROR) {
    167     reason = FAILURE_RETRY;
    168   }
    169 
    170   delegate_->OnPrivetRegisterError(this,
    171                                    current_action_,
    172                                    reason,
    173                                    visible_http_code,
    174                                    NULL);
    175 }
    176 
    177 void PrivetRegisterOperationImpl::OnParsedJson(
    178     PrivetURLFetcher* fetcher,
    179     const base::DictionaryValue* value,
    180     bool has_error) {
    181   if (has_error) {
    182     std::string error;
    183     value->GetString(kPrivetKeyError, &error);
    184 
    185     ongoing_ = false;
    186     delegate_->OnPrivetRegisterError(this,
    187                                      current_action_,
    188                                      FAILURE_JSON_ERROR,
    189                                      fetcher->response_code(),
    190                                      value);
    191     return;
    192   }
    193 
    194   // TODO(noamsml): Match the user&action with the user&action in the object,
    195   // and fail if different.
    196 
    197   next_response_handler_.Run(*value);
    198 }
    199 
    200 void PrivetRegisterOperationImpl::OnNeedPrivetToken(
    201     PrivetURLFetcher* fetcher,
    202     const PrivetURLFetcher::TokenCallback& callback) {
    203   privet_client_->RefreshPrivetToken(callback);
    204 }
    205 
    206 void PrivetRegisterOperationImpl::SendRequest(const std::string& action) {
    207   current_action_ = action;
    208   url_fetcher_ = privet_client_->CreateURLFetcher(
    209       CreatePrivetRegisterURL(action, user_), net::URLFetcher::POST, this);
    210   url_fetcher_->Start();
    211 }
    212 
    213 void PrivetRegisterOperationImpl::StartResponse(
    214     const base::DictionaryValue& value) {
    215   next_response_handler_ =
    216       base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse,
    217                  base::Unretained(this));
    218 
    219   SendRequest(kPrivetActionGetClaimToken);
    220 }
    221 
    222 void PrivetRegisterOperationImpl::GetClaimTokenResponse(
    223     const base::DictionaryValue& value) {
    224   std::string claimUrl;
    225   std::string claimToken;
    226   bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl);
    227   bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken);
    228   if (got_url || got_token) {
    229     delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl));
    230   } else {
    231     delegate_->OnPrivetRegisterError(this,
    232                                      current_action_,
    233                                      FAILURE_MALFORMED_RESPONSE,
    234                                      -1,
    235                                      NULL);
    236   }
    237 }
    238 
    239 void PrivetRegisterOperationImpl::CompleteResponse(
    240     const base::DictionaryValue& value) {
    241   std::string id;
    242   value.GetString(kPrivetKeyDeviceID, &id);
    243   ongoing_ = false;
    244   expected_id_ = id;
    245   StartInfoOperation();
    246 }
    247 
    248 void PrivetRegisterOperationImpl::OnPrivetInfoDone(
    249     PrivetInfoOperation* operation,
    250     int http_code,
    251     const base::DictionaryValue* value) {
    252   // TODO(noamsml): Simplify error case.
    253   if (!value) {
    254     delegate_->OnPrivetRegisterError(this,
    255                                      kPrivetActionNameInfo,
    256                                      FAILURE_NETWORK,
    257                                      -1,
    258                                      NULL);
    259     return;
    260   }
    261 
    262   if (!value->HasKey(kPrivetInfoKeyID)) {
    263     if (value->HasKey(kPrivetKeyError)) {
    264       delegate_->OnPrivetRegisterError(this,
    265                                        kPrivetActionNameInfo,
    266                                         FAILURE_JSON_ERROR,
    267                                        http_code,
    268                                        value);
    269     } else {
    270       delegate_->OnPrivetRegisterError(this,
    271                                        kPrivetActionNameInfo,
    272                                        FAILURE_MALFORMED_RESPONSE,
    273                                        -1,
    274                                        NULL);
    275     }
    276     return;
    277   }
    278 
    279   std::string id;
    280 
    281   if (!value->GetString(kPrivetInfoKeyID, &id) ||
    282       id != expected_id_) {
    283     delegate_->OnPrivetRegisterError(this,
    284                                      kPrivetActionNameInfo,
    285                                      FAILURE_MALFORMED_RESPONSE,
    286                                      -1,
    287                                      NULL);
    288   } else {
    289     delegate_->OnPrivetRegisterDone(this, id);
    290   }
    291 }
    292 
    293 void PrivetRegisterOperationImpl::StartInfoOperation() {
    294   info_operation_ = privet_client_->CreateInfoOperation(this);
    295   info_operation_->Start();
    296 }
    297 
    298 PrivetRegisterOperationImpl::Cancelation::Cancelation(
    299     PrivetHTTPClientImpl* privet_client,
    300     const std::string& user) {
    301   url_fetcher_ =
    302       privet_client->CreateURLFetcher(
    303           CreatePrivetRegisterURL(kPrivetActionCancel, user),
    304           net::URLFetcher::POST, this);
    305   url_fetcher_->DoNotRetryOnTransientError();
    306   url_fetcher_->Start();
    307 }
    308 
    309 PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
    310 }
    311 
    312 void PrivetRegisterOperationImpl::Cancelation::OnError(
    313     PrivetURLFetcher* fetcher,
    314     PrivetURLFetcher::ErrorType error) {
    315 }
    316 
    317 void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
    318     PrivetURLFetcher* fetcher,
    319     const base::DictionaryValue* value,
    320     bool has_error) {
    321 }
    322 
    323 void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
    324   // Nothing needs to be done, as base::Owned will delete this object,
    325   // this callback is just here to pass ownership of the Cancelation to
    326   // the message loop.
    327 }
    328 
    329 PrivetCapabilitiesOperationImpl::PrivetCapabilitiesOperationImpl(
    330     PrivetHTTPClientImpl* privet_client,
    331     PrivetCapabilitiesOperation::Delegate* delegate)
    332     : privet_client_(privet_client), delegate_(delegate) {
    333 }
    334 
    335 PrivetCapabilitiesOperationImpl::~PrivetCapabilitiesOperationImpl() {
    336 }
    337 
    338 void PrivetCapabilitiesOperationImpl::Start() {
    339   url_fetcher_ = privet_client_->CreateURLFetcher(
    340       CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this);
    341   url_fetcher_->DoNotRetryOnTransientError();
    342   url_fetcher_->Start();
    343 }
    344 
    345 PrivetHTTPClient* PrivetCapabilitiesOperationImpl::GetHTTPClient() {
    346   return privet_client_;
    347 }
    348 
    349 void PrivetCapabilitiesOperationImpl::OnError(
    350     PrivetURLFetcher* fetcher,
    351     PrivetURLFetcher::ErrorType error) {
    352   delegate_->OnPrivetCapabilities(this, -1, NULL);
    353 }
    354 
    355 void PrivetCapabilitiesOperationImpl::OnParsedJson(
    356     PrivetURLFetcher* fetcher,
    357     const base::DictionaryValue* value,
    358     bool has_error) {
    359   delegate_->OnPrivetCapabilities(this, fetcher->response_code(), value);
    360 }
    361 
    362 void PrivetCapabilitiesOperationImpl::OnNeedPrivetToken(
    363     PrivetURLFetcher* fetcher,
    364     const PrivetURLFetcher::TokenCallback& callback) {
    365   privet_client_->RefreshPrivetToken(callback);
    366 }
    367 
    368 PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl(
    369     PrivetHTTPClientImpl* privet_client,
    370     PrivetLocalPrintOperation::Delegate* delegate)
    371     : privet_client_(privet_client), delegate_(delegate),
    372       use_pdf_(false), has_capabilities_(false), has_extended_workflow_(false),
    373       started_(false), offline_(false), invalid_job_retries_(0),
    374       weak_factory_(this) {
    375 }
    376 
    377 PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
    378 }
    379 
    380 void PrivetLocalPrintOperationImpl::Start() {
    381   DCHECK(!started_);
    382 
    383   // We need to get the /info response so we can know which APIs are available.
    384   // TODO(noamsml): Use cached info when available.
    385   info_operation_ = privet_client_->CreateInfoOperation(this);
    386   info_operation_->Start();
    387 
    388   started_ = true;
    389 }
    390 
    391 void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
    392     PrivetInfoOperation* operation,
    393     int http_code,
    394     const base::DictionaryValue* value) {
    395   if (value && !value->HasKey(kPrivetKeyError)) {
    396     has_capabilities_ = false;
    397     has_extended_workflow_ = false;
    398     bool has_printing = false;
    399 
    400     const base::ListValue* api_list;
    401     if (value->GetList(kPrivetInfoKeyAPIList, &api_list)) {
    402       for (size_t i = 0; i < api_list->GetSize(); i++) {
    403         std::string api;
    404         api_list->GetString(i, &api);
    405         if (api == kPrivetCapabilitiesPath) {
    406           has_capabilities_ = true;
    407         } else if (api == kPrivetSubmitdocPath) {
    408           has_printing = true;
    409         } else if (api == kPrivetCreatejobPath) {
    410           has_extended_workflow_ = true;
    411         }
    412       }
    413     }
    414 
    415     if (!has_printing) {
    416       delegate_->OnPrivetPrintingError(this, -1);
    417       return;
    418     }
    419 
    420     StartInitialRequest();
    421   } else {
    422     delegate_->OnPrivetPrintingError(this, http_code);
    423   }
    424 }
    425 
    426 void PrivetLocalPrintOperationImpl::StartInitialRequest() {
    427   if (has_capabilities_) {
    428     GetCapabilities();
    429   } else {
    430     // Since we have no capabiltties, the only reasonable format we can
    431     // request is PWG Raster.
    432     use_pdf_ = false;
    433     StartConvertToPWG();
    434   }
    435 }
    436 
    437 void PrivetLocalPrintOperationImpl::GetCapabilities() {
    438   current_response_ = base::Bind(
    439       &PrivetLocalPrintOperationImpl::OnCapabilitiesResponse,
    440       base::Unretained(this));
    441 
    442   url_fetcher_= privet_client_->CreateURLFetcher(
    443       CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this);
    444   url_fetcher_->DoNotRetryOnTransientError();
    445 
    446   url_fetcher_->Start();
    447 }
    448 
    449 void PrivetLocalPrintOperationImpl::DoCreatejob() {
    450   current_response_ = base::Bind(
    451       &PrivetLocalPrintOperationImpl::OnCreatejobResponse,
    452       base::Unretained(this));
    453 
    454   url_fetcher_= privet_client_->CreateURLFetcher(
    455       CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this);
    456   url_fetcher_->SetUploadData(kPrivetContentTypeCJT, ticket_);
    457 
    458   url_fetcher_->Start();
    459 }
    460 
    461 void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
    462   current_response_ = base::Bind(
    463       &PrivetLocalPrintOperationImpl::OnSubmitdocResponse,
    464       base::Unretained(this));
    465 
    466   GURL url = CreatePrivetURL(kPrivetSubmitdocPath);
    467 
    468   url = net::AppendQueryParameter(url,
    469                                   kPrivetURLKeyClientName,
    470                                   kPrivetURLValueClientName);
    471 
    472   if (!user_.empty()) {
    473     url = net::AppendQueryParameter(url,
    474                                     kPrivetURLKeyUserName,
    475                                     user_);
    476   }
    477 
    478   if (!jobname_.empty()) {
    479     url = net::AppendQueryParameter(url,
    480                                     kPrivetURLKeyJobname,
    481                                     jobname_);
    482   }
    483 
    484   if (!jobid_.empty()) {
    485     url = net::AppendQueryParameter(url,
    486                                     kPrivetKeyJobID,
    487                                     jobid_);
    488   }
    489 
    490   if (offline_) {
    491     url = net::AppendQueryParameter(url,
    492                                     kPrivetURLKeyOffline,
    493                                     kPrivetURLValueOffline);
    494   }
    495 
    496   url_fetcher_= privet_client_->CreateURLFetcher(
    497       url, net::URLFetcher::POST, this);
    498 
    499   if (!use_pdf_) {
    500     url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster,
    501                                     pwg_file_path_);
    502   } else {
    503     // TODO(noamsml): Move to file-based upload data?
    504     std::string data_str((const char*)data_->front(), data_->size());
    505     url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str);
    506   }
    507 
    508   url_fetcher_->Start();
    509 }
    510 
    511 void PrivetLocalPrintOperationImpl::StartPrinting() {
    512   if (has_extended_workflow_ && !ticket_.empty() && jobid_.empty()) {
    513     DoCreatejob();
    514   } else {
    515     DoSubmitdoc();
    516   }
    517 }
    518 
    519 void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
    520   if (!pwg_raster_converter_)
    521     pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
    522   pwg_raster_converter_->Start(
    523       data_,
    524       conversion_settings_,
    525       base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
    526                  base::Unretained(this)));
    527 }
    528 
    529 void PrivetLocalPrintOperationImpl::OnCapabilitiesResponse(
    530     bool has_error,
    531     const base::DictionaryValue* value) {
    532   if (has_error) {
    533     delegate_->OnPrivetPrintingError(this, 200);
    534     return;
    535   }
    536 
    537   const base::ListValue* supported_content_types;
    538   use_pdf_ = false;
    539 
    540   if (value->GetList(kPrivetCDDKeySupportedContentTypes,
    541                      &supported_content_types)) {
    542     for (size_t i = 0; i < supported_content_types->GetSize(); i++) {
    543       const base::DictionaryValue* content_type_value;
    544       std::string content_type;
    545 
    546       if (supported_content_types->GetDictionary(i, &content_type_value) &&
    547           content_type_value->GetString(kPrivetCDDKeyContentType,
    548                                         &content_type) &&
    549           (content_type == kPrivetContentTypePDF ||
    550            content_type == kPrivetContentTypeAny) ) {
    551         use_pdf_ = true;
    552       }
    553     }
    554   }
    555 
    556   if (use_pdf_) {
    557     StartPrinting();
    558   } else {
    559     StartConvertToPWG();
    560   }
    561 }
    562 
    563 void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
    564     bool has_error,
    565     const base::DictionaryValue* value) {
    566   std::string error;
    567   // This error is only relevant in the case of extended workflow:
    568   // If the print job ID is invalid, retry createjob and submitdoc,
    569   // rather than simply retrying the current request.
    570   if (has_error && value->GetString(kPrivetKeyError, &error)) {
    571     if (has_extended_workflow_ &&
    572         error == kPrivetErrorInvalidPrintJob &&
    573         invalid_job_retries_ < kPrivetLocalPrintMaxRetries) {
    574       invalid_job_retries_++;
    575 
    576       int timeout = kPrivetLocalPrintDefaultTimeout;
    577       value->GetInteger(kPrivetKeyTimeout, &timeout);
    578 
    579       double random_scaling_factor =
    580           1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
    581 
    582       timeout = static_cast<int>(timeout * random_scaling_factor);
    583 
    584       timeout = std::max(timeout, kPrivetMinimumTimeout);
    585 
    586       base::MessageLoop::current()->PostDelayedTask(
    587           FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob,
    588                                 weak_factory_.GetWeakPtr()),
    589           base::TimeDelta::FromSeconds(timeout));
    590     } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) {
    591       use_pdf_ = false;
    592       StartConvertToPWG();
    593     } else {
    594       delegate_->OnPrivetPrintingError(this, 200);
    595     }
    596 
    597     return;
    598   }
    599 
    600   // If we've gotten this far, there are no errors, so we've effectively
    601   // succeeded.
    602   delegate_->OnPrivetPrintingDone(this);
    603 }
    604 
    605 void PrivetLocalPrintOperationImpl::OnCreatejobResponse(
    606     bool has_error,
    607     const base::DictionaryValue* value) {
    608   if (has_error) {
    609     delegate_->OnPrivetPrintingError(this, 200);
    610     return;
    611   }
    612 
    613   // Try to get job ID from value. If not, jobid_ will be empty and we will use
    614   // simple printing.
    615   value->GetString(kPrivetKeyJobID, &jobid_);
    616 
    617   DoSubmitdoc();
    618 }
    619 
    620 void PrivetLocalPrintOperationImpl::OnPWGRasterConverted(
    621     bool success,
    622     const base::FilePath& pwg_file_path) {
    623   if (!success) {
    624     delegate_->OnPrivetPrintingError(this, -1);
    625     return;
    626   }
    627 
    628   DCHECK(!pwg_file_path.empty());
    629 
    630   pwg_file_path_ = pwg_file_path;
    631   StartPrinting();
    632 }
    633 
    634 PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
    635   return privet_client_;
    636 }
    637 
    638 void PrivetLocalPrintOperationImpl::OnError(
    639     PrivetURLFetcher* fetcher,
    640     PrivetURLFetcher::ErrorType error) {
    641   delegate_->OnPrivetPrintingError(this, -1);
    642 }
    643 
    644 void PrivetLocalPrintOperationImpl::OnParsedJson(
    645     PrivetURLFetcher* fetcher,
    646     const base::DictionaryValue* value,
    647     bool has_error) {
    648   DCHECK(!current_response_.is_null());
    649   current_response_.Run(has_error, value);
    650 }
    651 
    652 void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
    653     PrivetURLFetcher* fetcher,
    654     const PrivetURLFetcher::TokenCallback& callback) {
    655   privet_client_->RefreshPrivetToken(callback);
    656 }
    657 
    658 void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) {
    659   DCHECK(!started_);
    660   data_ = data;
    661 }
    662 
    663 void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
    664   DCHECK(!started_);
    665   ticket_ = ticket;
    666 }
    667 
    668 void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
    669   DCHECK(!started_);
    670   user_= user;
    671 }
    672 
    673 void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
    674   DCHECK(!started_);
    675   jobname_ = jobname;
    676 }
    677 
    678 void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
    679   DCHECK(!started_);
    680   offline_ = offline;
    681 }
    682 
    683 void PrivetLocalPrintOperationImpl::SetConversionSettings(
    684     const printing::PdfRenderSettings& conversion_settings) {
    685   DCHECK(!started_);
    686   conversion_settings_ = conversion_settings;
    687 }
    688 
    689 void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
    690     scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
    691   pwg_raster_converter_ = pwg_raster_converter.Pass();
    692 }
    693 
    694 PrivetHTTPClientImpl::PrivetHTTPClientImpl(
    695     const std::string& name,
    696     const net::HostPortPair& host_port,
    697     net::URLRequestContextGetter* request_context)
    698     : name_(name),
    699       fetcher_factory_(request_context),
    700       host_port_(host_port) {
    701 }
    702 
    703 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
    704 }
    705 
    706 const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
    707   return cached_info_.get();
    708 }
    709 
    710 scoped_ptr<PrivetRegisterOperation>
    711 PrivetHTTPClientImpl::CreateRegisterOperation(
    712     const std::string& user,
    713     PrivetRegisterOperation::Delegate* delegate) {
    714   return scoped_ptr<PrivetRegisterOperation>(
    715       new PrivetRegisterOperationImpl(this, user, delegate));
    716 }
    717 
    718 scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
    719     PrivetInfoOperation::Delegate* delegate) {
    720   return scoped_ptr<PrivetInfoOperation>(
    721       new PrivetInfoOperationImpl(this, delegate));
    722 }
    723 
    724 scoped_ptr<PrivetCapabilitiesOperation>
    725 PrivetHTTPClientImpl::CreateCapabilitiesOperation(
    726     PrivetCapabilitiesOperation::Delegate* delegate) {
    727   return scoped_ptr<PrivetCapabilitiesOperation>(
    728       new PrivetCapabilitiesOperationImpl(this, delegate));
    729 }
    730 
    731 scoped_ptr<PrivetLocalPrintOperation>
    732 PrivetHTTPClientImpl::CreateLocalPrintOperation(
    733     PrivetLocalPrintOperation::Delegate* delegate) {
    734   return scoped_ptr<PrivetLocalPrintOperation>(
    735       new PrivetLocalPrintOperationImpl(this, delegate));
    736 }
    737 
    738 const std::string& PrivetHTTPClientImpl::GetName() {
    739   return name_;
    740 }
    741 
    742 scoped_ptr<PrivetURLFetcher> PrivetHTTPClientImpl::CreateURLFetcher(
    743     const GURL& url, net::URLFetcher::RequestType request_type,
    744     PrivetURLFetcher::Delegate* delegate) const {
    745   GURL::Replacements replacements;
    746   replacements.SetHostStr(host_port_.host());
    747   std::string port(base::IntToString(host_port_.port()));  // Keep string alive.
    748   replacements.SetPortStr(port);
    749   GURL url2 = url.ReplaceComponents(replacements);
    750   return fetcher_factory_.CreateURLFetcher(url.ReplaceComponents(replacements),
    751                                            request_type, delegate);
    752 }
    753 
    754 void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue* cached_info) {
    755   cached_info_.reset(cached_info->DeepCopy());
    756   std::string token;
    757   if (cached_info_->GetString(kPrivetInfoKeyToken, &token)) {
    758     fetcher_factory_.set_token(token);
    759   }
    760 }
    761 
    762 bool PrivetHTTPClientImpl::HasToken() const {
    763   return fetcher_factory_.get_token() != "";
    764 };
    765 
    766 void PrivetHTTPClientImpl::RefreshPrivetToken(
    767     const PrivetURLFetcher::TokenCallback& callback) {
    768   token_callbacks_.push_back(callback);
    769 
    770   if (!info_operation_) {
    771     info_operation_ = CreateInfoOperation(this);
    772     info_operation_->Start();
    773   }
    774 }
    775 
    776 void PrivetHTTPClientImpl::OnPrivetInfoDone(
    777     PrivetInfoOperation* operation,
    778     int http_code,
    779     const base::DictionaryValue* value) {
    780   info_operation_.reset();
    781   std::string token;
    782 
    783   // If this does not succeed, token will be empty, and an empty string
    784   // is our sentinel value, since empty X-Privet-Tokens are not allowed.
    785   if (value) {
    786     value->GetString(kPrivetInfoKeyToken, &token);
    787   }
    788 
    789   TokenCallbackVector token_callbacks;
    790   token_callbacks_.swap(token_callbacks);
    791 
    792   for (TokenCallbackVector::iterator i = token_callbacks.begin();
    793        i != token_callbacks.end(); i++) {
    794     i->Run(token);
    795   }
    796 }
    797 
    798 }  // namespace local_discovery
    799