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.
5 #include "chrome/browser/local_discovery/privet_http_impl.h"
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 "base/strings/stringprintf.h"
15 #include "chrome/browser/local_discovery/privet_constants.h"
16 #include "net/base/url_util.h"
19 namespace local_discovery
{
22 const char kUrlPlaceHolder
[] = "http://host/";
23 const char kPrivetRegisterActionArgName
[] = "action";
24 const char kPrivetRegisterUserArgName
[] = "user";
26 const char kPrivetURLKeyUser
[] = "user";
27 const char kPrivetURLKeyJobname
[] = "jobname";
28 const char kPrivetURLKeyOffline
[] = "offline";
29 const char kPrivetURLValueOffline
[] = "1";
31 const char kPrivetContentTypePDF
[] = "application/pdf";
32 const char kPrivetContentTypePWGRaster
[] = "image/pwg-raster";
33 const char kPrivetContentTypeAny
[] = "*/*";
34 const char kPrivetContentTypeCJT
[] = "application/json";
36 const char kPrivetStorageListPath
[] = "/privet/storage/list";
37 const char kPrivetStorageParamPathFormat
[] = "path=%s";
39 const char kPrivetCDDKeySupportedContentTypes
[] =
40 "printer.supported_content_type";
42 const char kPrivetCDDKeyContentType
[] = "content_type";
44 const char kPrivetKeyJobID
[] = "job_id";
46 const int kPrivetCancelationTimeoutSeconds
= 3;
48 const int kPrivetLocalPrintMaxRetries
= 2;
50 const int kPrivetLocalPrintDefaultTimeout
= 5;
52 GURL
CreatePrivetURL(const std::string
& path
) {
53 GURL
url(kUrlPlaceHolder
);
54 GURL::Replacements replacements
;
55 replacements
.SetPathStr(path
);
56 return url
.ReplaceComponents(replacements
);
59 GURL
CreatePrivetRegisterURL(const std::string
& action
,
60 const std::string
& user
) {
61 GURL url
= CreatePrivetURL(kPrivetRegisterPath
);
62 url
= net::AppendQueryParameter(url
, kPrivetRegisterActionArgName
, action
);
63 return net::AppendQueryParameter(url
, kPrivetRegisterUserArgName
, user
);
66 GURL
CreatePrivetParamURL(const std::string
& path
,
67 const std::string
& query_params
) {
68 GURL
url(kUrlPlaceHolder
);
69 GURL::Replacements replacements
;
70 replacements
.SetPathStr(path
);
71 if (!query_params
.empty()) {
72 replacements
.SetQueryStr(query_params
);
74 return url
.ReplaceComponents(replacements
);
79 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
80 PrivetHTTPClientImpl
* privet_client
,
81 const PrivetJSONOperation::ResultCallback
& callback
)
82 : privet_client_(privet_client
), callback_(callback
) {
85 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
88 void PrivetInfoOperationImpl::Start() {
89 url_fetcher_
= privet_client_
->CreateURLFetcher(
90 CreatePrivetURL(kPrivetInfoPath
), net::URLFetcher::GET
, this);
92 url_fetcher_
->DoNotRetryOnTransientError();
93 url_fetcher_
->AllowEmptyPrivetToken();
95 url_fetcher_
->Start();
98 PrivetHTTPClient
* PrivetInfoOperationImpl::GetHTTPClient() {
99 return privet_client_
;
102 void PrivetInfoOperationImpl::OnError(PrivetURLFetcher
* fetcher
,
103 PrivetURLFetcher::ErrorType error
) {
107 void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher
* fetcher
,
108 const base::DictionaryValue
* value
,
111 privet_client_
->CacheInfo(value
);
112 callback_
.Run(value
);
115 PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
116 PrivetHTTPClientImpl
* privet_client
,
117 const std::string
& user
,
118 PrivetRegisterOperation::Delegate
* delegate
)
119 : user_(user
), delegate_(delegate
), privet_client_(privet_client
),
123 PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
126 void PrivetRegisterOperationImpl::Start() {
128 next_response_handler_
=
129 base::Bind(&PrivetRegisterOperationImpl::StartResponse
,
130 base::Unretained(this));
131 SendRequest(kPrivetActionStart
);
134 void PrivetRegisterOperationImpl::Cancel() {
135 url_fetcher_
.reset();
138 // Owned by the message loop.
139 Cancelation
* cancelation
= new Cancelation(privet_client_
, user_
);
141 base::MessageLoop::current()->PostDelayedTask(
143 base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup
,
144 base::Owned(cancelation
)),
145 base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds
));
151 void PrivetRegisterOperationImpl::CompleteRegistration() {
152 next_response_handler_
=
153 base::Bind(&PrivetRegisterOperationImpl::CompleteResponse
,
154 base::Unretained(this));
155 SendRequest(kPrivetActionComplete
);
158 PrivetHTTPClient
* PrivetRegisterOperationImpl::GetHTTPClient() {
159 return privet_client_
;
162 void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher
* fetcher
,
163 PrivetURLFetcher::ErrorType error
) {
165 int visible_http_code
= -1;
166 FailureReason reason
= FAILURE_NETWORK
;
168 if (error
== PrivetURLFetcher::RESPONSE_CODE_ERROR
) {
169 visible_http_code
= fetcher
->response_code();
170 reason
= FAILURE_HTTP_ERROR
;
171 } else if (error
== PrivetURLFetcher::JSON_PARSE_ERROR
) {
172 reason
= FAILURE_MALFORMED_RESPONSE
;
173 } else if (error
== PrivetURLFetcher::TOKEN_ERROR
) {
174 reason
= FAILURE_TOKEN
;
175 } else if (error
== PrivetURLFetcher::RETRY_ERROR
) {
176 reason
= FAILURE_RETRY
;
179 delegate_
->OnPrivetRegisterError(this,
186 void PrivetRegisterOperationImpl::OnParsedJson(
187 PrivetURLFetcher
* fetcher
,
188 const base::DictionaryValue
* value
,
192 value
->GetString(kPrivetKeyError
, &error
);
195 delegate_
->OnPrivetRegisterError(this,
198 fetcher
->response_code(),
203 // TODO(noamsml): Match the user&action with the user&action in the object,
204 // and fail if different.
206 next_response_handler_
.Run(*value
);
209 void PrivetRegisterOperationImpl::OnNeedPrivetToken(
210 PrivetURLFetcher
* fetcher
,
211 const PrivetURLFetcher::TokenCallback
& callback
) {
212 privet_client_
->RefreshPrivetToken(callback
);
215 void PrivetRegisterOperationImpl::SendRequest(const std::string
& action
) {
216 current_action_
= action
;
217 url_fetcher_
= privet_client_
->CreateURLFetcher(
218 CreatePrivetRegisterURL(action
, user_
), net::URLFetcher::POST
, this);
219 url_fetcher_
->Start();
222 void PrivetRegisterOperationImpl::StartResponse(
223 const base::DictionaryValue
& value
) {
224 next_response_handler_
=
225 base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse
,
226 base::Unretained(this));
228 SendRequest(kPrivetActionGetClaimToken
);
231 void PrivetRegisterOperationImpl::GetClaimTokenResponse(
232 const base::DictionaryValue
& value
) {
233 std::string claimUrl
;
234 std::string claimToken
;
235 bool got_url
= value
.GetString(kPrivetKeyClaimURL
, &claimUrl
);
236 bool got_token
= value
.GetString(kPrivetKeyClaimToken
, &claimToken
);
237 if (got_url
|| got_token
) {
238 delegate_
->OnPrivetRegisterClaimToken(this, claimToken
, GURL(claimUrl
));
240 delegate_
->OnPrivetRegisterError(this,
242 FAILURE_MALFORMED_RESPONSE
,
248 void PrivetRegisterOperationImpl::CompleteResponse(
249 const base::DictionaryValue
& value
) {
251 value
.GetString(kPrivetKeyDeviceID
, &id
);
254 StartInfoOperation();
257 void PrivetRegisterOperationImpl::OnPrivetInfoDone(
258 const base::DictionaryValue
* value
) {
259 // TODO(noamsml): Simplify error case and depracate HTTP error value in
260 // OnPrivetRegisterError.
262 delegate_
->OnPrivetRegisterError(this,
263 kPrivetActionNameInfo
,
270 if (!value
->HasKey(kPrivetInfoKeyID
)) {
271 if (value
->HasKey(kPrivetKeyError
)) {
272 delegate_
->OnPrivetRegisterError(this,
273 kPrivetActionNameInfo
,
278 delegate_
->OnPrivetRegisterError(this,
279 kPrivetActionNameInfo
,
280 FAILURE_MALFORMED_RESPONSE
,
289 if (!value
->GetString(kPrivetInfoKeyID
, &id
) ||
290 id
!= expected_id_
) {
291 delegate_
->OnPrivetRegisterError(this,
292 kPrivetActionNameInfo
,
293 FAILURE_MALFORMED_RESPONSE
,
297 delegate_
->OnPrivetRegisterDone(this, id
);
301 void PrivetRegisterOperationImpl::StartInfoOperation() {
302 info_operation_
= privet_client_
->CreateInfoOperation(
303 base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone
,
304 base::Unretained(this)));
305 info_operation_
->Start();
308 PrivetRegisterOperationImpl::Cancelation::Cancelation(
309 PrivetHTTPClientImpl
* privet_client
,
310 const std::string
& user
) {
312 privet_client
->CreateURLFetcher(
313 CreatePrivetRegisterURL(kPrivetActionCancel
, user
),
314 net::URLFetcher::POST
, this);
315 url_fetcher_
->DoNotRetryOnTransientError();
316 url_fetcher_
->Start();
319 PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
322 void PrivetRegisterOperationImpl::Cancelation::OnError(
323 PrivetURLFetcher
* fetcher
,
324 PrivetURLFetcher::ErrorType error
) {
327 void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
328 PrivetURLFetcher
* fetcher
,
329 const base::DictionaryValue
* value
,
333 void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
334 // Nothing needs to be done, as base::Owned will delete this object,
335 // this callback is just here to pass ownership of the Cancelation to
339 PrivetJSONOperationImpl::PrivetJSONOperationImpl(
340 PrivetHTTPClientImpl
* privet_client
,
341 const std::string
& path
,
342 const std::string
& query_params
,
343 const PrivetJSONOperation::ResultCallback
& callback
)
344 : privet_client_(privet_client
), path_(path
), query_params_(query_params
),
345 callback_(callback
) {
348 PrivetJSONOperationImpl::~PrivetJSONOperationImpl() {
351 void PrivetJSONOperationImpl::Start() {
352 url_fetcher_
= privet_client_
->CreateURLFetcher(
353 CreatePrivetParamURL(path_
, query_params_
), net::URLFetcher::GET
, this);
354 url_fetcher_
->DoNotRetryOnTransientError();
355 url_fetcher_
->Start();
358 PrivetHTTPClient
* PrivetJSONOperationImpl::GetHTTPClient() {
359 return privet_client_
;
362 void PrivetJSONOperationImpl::OnError(
363 PrivetURLFetcher
* fetcher
,
364 PrivetURLFetcher::ErrorType error
) {
368 void PrivetJSONOperationImpl::OnParsedJson(
369 PrivetURLFetcher
* fetcher
,
370 const base::DictionaryValue
* value
,
372 callback_
.Run(value
);
375 void PrivetJSONOperationImpl::OnNeedPrivetToken(
376 PrivetURLFetcher
* fetcher
,
377 const PrivetURLFetcher::TokenCallback
& callback
) {
378 privet_client_
->RefreshPrivetToken(callback
);
381 PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl(
382 PrivetHTTPClientImpl
* privet_client
,
383 PrivetLocalPrintOperation::Delegate
* delegate
)
384 : privet_client_(privet_client
), delegate_(delegate
),
385 use_pdf_(false), has_capabilities_(false), has_extended_workflow_(false),
386 started_(false), offline_(false), invalid_job_retries_(0),
387 weak_factory_(this) {
390 PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
393 void PrivetLocalPrintOperationImpl::Start() {
396 // We need to get the /info response so we can know which APIs are available.
397 // TODO(noamsml): Use cached info when available.
398 info_operation_
= privet_client_
->CreateInfoOperation(
399 base::Bind(&PrivetLocalPrintOperationImpl::OnPrivetInfoDone
,
400 base::Unretained(this)));
401 info_operation_
->Start();
406 void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
407 const base::DictionaryValue
* value
) {
408 if (value
&& !value
->HasKey(kPrivetKeyError
)) {
409 has_capabilities_
= false;
410 has_extended_workflow_
= false;
411 bool has_printing
= false;
413 const base::ListValue
* api_list
;
414 if (value
->GetList(kPrivetInfoKeyAPIList
, &api_list
)) {
415 for (size_t i
= 0; i
< api_list
->GetSize(); i
++) {
417 api_list
->GetString(i
, &api
);
418 if (api
== kPrivetCapabilitiesPath
) {
419 has_capabilities_
= true;
420 } else if (api
== kPrivetSubmitdocPath
) {
422 } else if (api
== kPrivetCreatejobPath
) {
423 has_extended_workflow_
= true;
429 delegate_
->OnPrivetPrintingError(this, -1);
433 StartInitialRequest();
435 delegate_
->OnPrivetPrintingError(this, -1);
439 void PrivetLocalPrintOperationImpl::StartInitialRequest() {
440 if (has_capabilities_
) {
443 // Since we have no capabiltties, the only reasonable format we can
444 // request is PWG Raster.
450 void PrivetLocalPrintOperationImpl::GetCapabilities() {
451 current_response_
= base::Bind(
452 &PrivetLocalPrintOperationImpl::OnCapabilitiesResponse
,
453 base::Unretained(this));
455 url_fetcher_
= privet_client_
->CreateURLFetcher(
456 CreatePrivetURL(kPrivetCapabilitiesPath
), net::URLFetcher::GET
, this);
457 url_fetcher_
->DoNotRetryOnTransientError();
459 url_fetcher_
->Start();
462 void PrivetLocalPrintOperationImpl::DoCreatejob() {
463 current_response_
= base::Bind(
464 &PrivetLocalPrintOperationImpl::OnCreatejobResponse
,
465 base::Unretained(this));
467 url_fetcher_
= privet_client_
->CreateURLFetcher(
468 CreatePrivetURL(kPrivetCreatejobPath
), net::URLFetcher::POST
, this);
469 url_fetcher_
->SetUploadData(kPrivetContentTypeCJT
, ticket_
);
471 url_fetcher_
->Start();
474 void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
475 current_response_
= base::Bind(
476 &PrivetLocalPrintOperationImpl::OnSubmitdocResponse
,
477 base::Unretained(this));
479 GURL url
= CreatePrivetURL(kPrivetSubmitdocPath
);
481 if (!user_
.empty()) {
482 url
= net::AppendQueryParameter(url
,
487 if (!jobname_
.empty()) {
488 url
= net::AppendQueryParameter(url
,
489 kPrivetURLKeyJobname
,
493 if (!jobid_
.empty()) {
494 url
= net::AppendQueryParameter(url
,
500 url
= net::AppendQueryParameter(url
,
501 kPrivetURLKeyOffline
,
502 kPrivetURLValueOffline
);
505 url_fetcher_
= privet_client_
->CreateURLFetcher(
506 url
, net::URLFetcher::POST
, this);
509 url_fetcher_
->SetUploadFilePath(kPrivetContentTypePWGRaster
,
512 // TODO(noamsml): Move to file-based upload data?
513 std::string
data_str((const char*)data_
->front(), data_
->size());
514 url_fetcher_
->SetUploadData(kPrivetContentTypePDF
, data_str
);
517 url_fetcher_
->Start();
520 void PrivetLocalPrintOperationImpl::StartPrinting() {
521 if (has_extended_workflow_
&& !ticket_
.empty() && jobid_
.empty()) {
528 void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
529 if (!pwg_raster_converter_
)
530 pwg_raster_converter_
= PWGRasterConverter::CreateDefault();
531 pwg_raster_converter_
->Start(
533 conversion_settings_
,
534 base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted
,
535 base::Unretained(this)));
538 void PrivetLocalPrintOperationImpl::OnCapabilitiesResponse(
540 const base::DictionaryValue
* value
) {
542 delegate_
->OnPrivetPrintingError(this, 200);
546 const base::ListValue
* supported_content_types
;
549 if (value
->GetList(kPrivetCDDKeySupportedContentTypes
,
550 &supported_content_types
)) {
551 for (size_t i
= 0; i
< supported_content_types
->GetSize(); i
++) {
552 const base::DictionaryValue
* content_type_value
;
553 std::string content_type
;
555 if (supported_content_types
->GetDictionary(i
, &content_type_value
) &&
556 content_type_value
->GetString(kPrivetCDDKeyContentType
,
558 (content_type
== kPrivetContentTypePDF
||
559 content_type
== kPrivetContentTypeAny
) ) {
572 void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
574 const base::DictionaryValue
* value
) {
576 // This error is only relevant in the case of extended workflow:
577 // If the print job ID is invalid, retry createjob and submitdoc,
578 // rather than simply retrying the current request.
579 if (has_error
&& value
->GetString(kPrivetKeyError
, &error
)) {
580 if (has_extended_workflow_
&&
581 error
== kPrivetErrorInvalidPrintJob
&&
582 invalid_job_retries_
< kPrivetLocalPrintMaxRetries
) {
583 invalid_job_retries_
++;
585 int timeout
= kPrivetLocalPrintDefaultTimeout
;
586 value
->GetInteger(kPrivetKeyTimeout
, &timeout
);
588 double random_scaling_factor
=
589 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition
;
591 timeout
= static_cast<int>(timeout
* random_scaling_factor
);
593 timeout
= std::max(timeout
, kPrivetMinimumTimeout
);
595 base::MessageLoop::current()->PostDelayedTask(
596 FROM_HERE
, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob
,
597 weak_factory_
.GetWeakPtr()),
598 base::TimeDelta::FromSeconds(timeout
));
599 } else if (use_pdf_
&& error
== kPrivetErrorInvalidDocumentType
) {
603 delegate_
->OnPrivetPrintingError(this, 200);
609 // If we've gotten this far, there are no errors, so we've effectively
611 delegate_
->OnPrivetPrintingDone(this);
614 void PrivetLocalPrintOperationImpl::OnCreatejobResponse(
616 const base::DictionaryValue
* value
) {
618 delegate_
->OnPrivetPrintingError(this, 200);
622 // Try to get job ID from value. If not, jobid_ will be empty and we will use
624 value
->GetString(kPrivetKeyJobID
, &jobid_
);
629 void PrivetLocalPrintOperationImpl::OnPWGRasterConverted(
631 const base::FilePath
& pwg_file_path
) {
633 delegate_
->OnPrivetPrintingError(this, -1);
637 DCHECK(!pwg_file_path
.empty());
639 pwg_file_path_
= pwg_file_path
;
643 PrivetHTTPClient
* PrivetLocalPrintOperationImpl::GetHTTPClient() {
644 return privet_client_
;
647 void PrivetLocalPrintOperationImpl::OnError(
648 PrivetURLFetcher
* fetcher
,
649 PrivetURLFetcher::ErrorType error
) {
650 delegate_
->OnPrivetPrintingError(this, -1);
653 void PrivetLocalPrintOperationImpl::OnParsedJson(
654 PrivetURLFetcher
* fetcher
,
655 const base::DictionaryValue
* value
,
657 DCHECK(!current_response_
.is_null());
658 current_response_
.Run(has_error
, value
);
661 void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
662 PrivetURLFetcher
* fetcher
,
663 const PrivetURLFetcher::TokenCallback
& callback
) {
664 privet_client_
->RefreshPrivetToken(callback
);
667 void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes
* data
) {
672 void PrivetLocalPrintOperationImpl::SetTicket(const std::string
& ticket
) {
677 void PrivetLocalPrintOperationImpl::SetUsername(const std::string
& user
) {
682 void PrivetLocalPrintOperationImpl::SetJobname(const std::string
& jobname
) {
687 void PrivetLocalPrintOperationImpl::SetOffline(bool offline
) {
692 void PrivetLocalPrintOperationImpl::SetConversionSettings(
693 const printing::PdfRenderSettings
& conversion_settings
) {
695 conversion_settings_
= conversion_settings
;
698 void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
699 scoped_ptr
<PWGRasterConverter
> pwg_raster_converter
) {
700 pwg_raster_converter_
= pwg_raster_converter
.Pass();
703 PrivetHTTPClientImpl::PrivetHTTPClientImpl(
704 const std::string
& name
,
705 const net::HostPortPair
& host_port
,
706 net::URLRequestContextGetter
* request_context
)
708 fetcher_factory_(request_context
),
709 host_port_(host_port
) {
712 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
715 const base::DictionaryValue
* PrivetHTTPClientImpl::GetCachedInfo() const {
716 return cached_info_
.get();
719 scoped_ptr
<PrivetRegisterOperation
>
720 PrivetHTTPClientImpl::CreateRegisterOperation(
721 const std::string
& user
,
722 PrivetRegisterOperation::Delegate
* delegate
) {
723 return scoped_ptr
<PrivetRegisterOperation
>(
724 new PrivetRegisterOperationImpl(this, user
, delegate
));
727 scoped_ptr
<PrivetJSONOperation
> PrivetHTTPClientImpl::CreateInfoOperation(
728 const PrivetJSONOperation::ResultCallback
& callback
) {
729 return scoped_ptr
<PrivetJSONOperation
>(
730 new PrivetInfoOperationImpl(this, callback
));
733 scoped_ptr
<PrivetJSONOperation
>
734 PrivetHTTPClientImpl::CreateCapabilitiesOperation(
735 const PrivetJSONOperation::ResultCallback
& callback
) {
736 return scoped_ptr
<PrivetJSONOperation
>(
737 new PrivetJSONOperationImpl(this, kPrivetCapabilitiesPath
, "", callback
));
740 scoped_ptr
<PrivetLocalPrintOperation
>
741 PrivetHTTPClientImpl::CreateLocalPrintOperation(
742 PrivetLocalPrintOperation::Delegate
* delegate
) {
743 return scoped_ptr
<PrivetLocalPrintOperation
>(
744 new PrivetLocalPrintOperationImpl(this, delegate
));
747 scoped_ptr
<PrivetJSONOperation
>
748 PrivetHTTPClientImpl::CreateStorageListOperation(
749 const std::string
& path
,
750 const PrivetJSONOperation::ResultCallback
& callback
) {
751 std::string url_param
= base::StringPrintf(kPrivetStorageParamPathFormat
,
753 return scoped_ptr
<PrivetJSONOperation
>(
754 new PrivetJSONOperationImpl(this, kPrivetStorageListPath
, url_param
,
758 const std::string
& PrivetHTTPClientImpl::GetName() {
762 scoped_ptr
<PrivetURLFetcher
> PrivetHTTPClientImpl::CreateURLFetcher(
763 const GURL
& url
, net::URLFetcher::RequestType request_type
,
764 PrivetURLFetcher::Delegate
* delegate
) const {
765 GURL::Replacements replacements
;
766 replacements
.SetHostStr(host_port_
.host());
767 std::string
port(base::IntToString(host_port_
.port())); // Keep string alive.
768 replacements
.SetPortStr(port
);
769 return fetcher_factory_
.CreateURLFetcher(url
.ReplaceComponents(replacements
),
770 request_type
, delegate
);
773 void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue
* cached_info
) {
774 cached_info_
.reset(cached_info
->DeepCopy());
776 if (cached_info_
->GetString(kPrivetInfoKeyToken
, &token
)) {
777 fetcher_factory_
.set_token(token
);
781 bool PrivetHTTPClientImpl::HasToken() const {
782 return fetcher_factory_
.get_token() != "";
785 void PrivetHTTPClientImpl::RefreshPrivetToken(
786 const PrivetURLFetcher::TokenCallback
& callback
) {
787 token_callbacks_
.push_back(callback
);
789 if (!info_operation_
) {
790 info_operation_
= CreateInfoOperation(
791 base::Bind(&PrivetHTTPClientImpl::OnPrivetInfoDone
,
792 base::Unretained(this)));
793 info_operation_
->Start();
797 void PrivetHTTPClientImpl::OnPrivetInfoDone(
798 const base::DictionaryValue
* value
) {
799 info_operation_
.reset();
802 // If this does not succeed, token will be empty, and an empty string
803 // is our sentinel value, since empty X-Privet-Tokens are not allowed.
805 value
->GetString(kPrivetInfoKeyToken
, &token
);
808 TokenCallbackVector token_callbacks
;
809 token_callbacks_
.swap(token_callbacks
);
811 for (TokenCallbackVector::iterator i
= token_callbacks
.begin();
812 i
!= token_callbacks
.end(); i
++) {
817 } // namespace local_discovery