1 // Copyright 2015 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 "remoting/host/gcd_rest_client.h"
8 #include "base/callback_helpers.h"
9 #include "base/json/json_writer.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/time/default_clock.h"
13 #include "base/values.h"
14 #include "net/url_request/url_fetcher.h"
15 #include "remoting/base/logging.h"
19 GcdRestClient::GcdRestClient(const std::string
& gcd_base_url
,
20 const std::string
& gcd_device_id
,
21 const scoped_refptr
<net::URLRequestContextGetter
>&
22 url_request_context_getter
,
23 OAuthTokenGetter
* token_getter
)
24 : gcd_base_url_(gcd_base_url
),
25 gcd_device_id_(gcd_device_id
),
26 url_request_context_getter_(url_request_context_getter
),
27 token_getter_(token_getter
),
28 clock_(new base::DefaultClock
) {
31 GcdRestClient::~GcdRestClient() {
34 void GcdRestClient::PatchState(
35 scoped_ptr
<base::DictionaryValue
> patch_details
,
36 const GcdRestClient::ResultCallback
& callback
) {
37 DCHECK(!HasPendingRequest());
39 // Construct a status update message in the format GCD expects. The
40 // message looks like this, where "..." is filled in from
51 // Note that |now| is deliberately using a double to hold an integer
52 // value because |DictionaryValue| doesn't support int64 values, and
53 // GCD doesn't accept fractional values.
54 double now
= clock_
->Now().ToJavaTime();
55 scoped_ptr
<base::DictionaryValue
> patch_dict(new base::DictionaryValue
);
56 patch_dict
->SetDouble("requestTimeMs", now
);
57 scoped_ptr
<base::ListValue
> patch_list(new base::ListValue
);
58 base::DictionaryValue
* patch_item
= new base::DictionaryValue
;
59 patch_list
->Append(patch_item
);
60 patch_item
->Set("patch", patch_details
.Pass());
61 patch_item
->SetDouble("timeMs", now
);
62 patch_dict
->Set("patches", patch_list
.Pass());
64 // Stringify the message.
65 std::string patch_string
;
66 if (!base::JSONWriter::Write(*patch_dict
, &patch_string
)) {
67 LOG(ERROR
) << "Error building GCD device state patch.";
68 callback
.Run(OTHER_ERROR
);
71 DLOG(INFO
) << "sending state patch: " << patch_string
;
74 gcd_base_url_
+ "/devices/" + gcd_device_id_
+ "/patchState";
76 // Prepare an HTTP request to issue once an auth token is available.
79 net::URLFetcher::Create(GURL(url
), net::URLFetcher::POST
, this);
80 url_fetcher_
->SetUploadData("application/json", patch_string
);
81 if (url_request_context_getter_
) {
82 url_fetcher_
->SetRequestContext(url_request_context_getter_
.get());
85 token_getter_
->CallWithToken(
86 base::Bind(&GcdRestClient::OnTokenReceived
, base::Unretained(this)));
89 void GcdRestClient::OnTokenReceived(OAuthTokenGetter::Status status
,
90 const std::string
& user_email
,
91 const std::string
& access_token
) {
92 DCHECK(HasPendingRequest());
94 if (status
!= OAuthTokenGetter::SUCCESS
) {
95 LOG(ERROR
) << "Error getting OAuth token for GCD request: "
96 << url_fetcher_
->GetOriginalURL();
97 if (status
== OAuthTokenGetter::NETWORK_ERROR
) {
98 FinishCurrentRequest(NETWORK_ERROR
);
100 FinishCurrentRequest(OTHER_ERROR
);
105 url_fetcher_
->SetExtraRequestHeaders(
106 "Authorization: Bearer " + access_token
);
107 url_fetcher_
->Start();
110 void GcdRestClient::FinishCurrentRequest(Result result
) {
111 DCHECK(HasPendingRequest());
112 url_fetcher_
.reset();
113 base::ResetAndReturn(&callback_
).Run(result
);
116 void GcdRestClient::OnURLFetchComplete(const net::URLFetcher
* source
) {
117 DCHECK(HasPendingRequest());
119 const GURL
& request_url
= url_fetcher_
->GetOriginalURL();
120 Result status
= OTHER_ERROR
;
121 int response
= source
->GetResponseCode();
122 if (response
>= 200 && response
< 300) {
123 DLOG(INFO
) << "GCD request succeeded:" << request_url
;
125 } else if (response
== 404) {
126 LOG(WARNING
) << "Host not found (" << response
127 << ") fetching URL: " << request_url
;
128 status
= NO_SUCH_HOST
;
129 } else if (response
== 0) {
130 LOG(ERROR
) << "Network error (" << response
131 << ") fetching URL: " << request_url
;
132 status
= NETWORK_ERROR
;
134 LOG(ERROR
) << "Error (" << response
<< ") fetching URL: " << request_url
;
137 FinishCurrentRequest(status
);
140 } // namespace remoting