Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: blimp/client/session/assignment_source.cc

Issue 1687393002: Add assigner support to Blimp (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "blimp/client/session/assignment_source.h" 5 #include "blimp/client/session/assignment_source.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
9 #include "base/location.h" 11 #include "base/location.h"
10 #include "base/numerics/safe_conversions.h" 12 #include "base/numerics/safe_conversions.h"
11 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
14 #include "base/values.h"
12 #include "blimp/client/app/blimp_client_switches.h" 15 #include "blimp/client/app/blimp_client_switches.h"
16 #include "blimp/client/session/blimp_client_url_request_context_getter.h"
17 #include "blimp/common/protocol_version.h"
13 #include "net/base/ip_address.h" 18 #include "net/base/ip_address.h"
14 #include "net/base/ip_endpoint.h" 19 #include "net/base/ip_endpoint.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/url_util.h"
22 #include "net/http/http_status_code.h"
23 #include "net/url_request/url_fetcher.h"
15 24
16 namespace blimp { 25 namespace blimp {
17 namespace { 26 namespace {
18 27
19 // TODO(kmarshall): Take values from configuration data. 28 // TODO(kmarshall): Take values from configuration data.
20 const char kDummyClientToken[] = "MyVoiceIsMyPassport"; 29 const char kDummyClientToken[] = "MyVoiceIsMyPassport";
21 const std::string kDefaultBlimpletIPAddress = "127.0.0.1"; 30 const char kDefaultBlimpletIPAddress[] = "127.0.0.1";
22 const uint16_t kDefaultBlimpletTCPPort = 25467; 31 const uint16_t kDefaultBlimpletTCPPort = 25467;
23 32
24 net::IPAddress GetBlimpletIPAddress() { 33 // Potential assigner URLs.
34 const char kProdBlimpAssignerURL[] =
Kevin M 2016/02/12 19:45:26 kDefaultAssignerURL?
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
35 "https://blimp-pa.googleapis.com/v1/assignment";
36
37 // Assignment request JSON keys.
38 const char kProtocolVersionKey[] = "protocol_version";
39
40 // Assignment response JSON keys.
41 const char kClientTokenKey[] = "clientToken";
42 const char kHostKey[] = "host";
43 const char kPortKey[] = "port";
44 const char kCertificateFingerprintKey[] = "certificateFingerprint";
45 const char kCertificateKey[] = "certificate";
46
47 bool HasCustomBlimpletEndpoint() {
48 return base::CommandLine::ForCurrentProcess()->HasSwitch(
49 switches::kBlimpletHost) &&
50 base::CommandLine::ForCurrentProcess()->HasSwitch(
51 switches::kBlimpletTCPPort);
52 }
53
54 net::IPAddress GetCustomBlimpletIPAddress() {
25 std::string host; 55 std::string host;
26 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 56 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
27 switches::kBlimpletHost)) { 57 switches::kBlimpletHost)) {
28 host = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 58 host = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
29 switches::kBlimpletHost); 59 switches::kBlimpletHost);
30 } else { 60 } else {
31 host = kDefaultBlimpletIPAddress; 61 host = kDefaultBlimpletIPAddress;
32 } 62 }
33 net::IPAddress ip_address; 63 net::IPAddress ip_address;
34 if (!net::IPAddress::FromIPLiteral(host, &ip_address)) 64 if (!net::IPAddress::FromIPLiteral(host, &ip_address))
35 CHECK(false) << "Invalid BlimpletAssignment host " << host; 65 CHECK(false) << "Invalid BlimpletAssignment host " << host;
36 return ip_address; 66 return ip_address;
37 } 67 }
38 68
39 uint16_t GetBlimpletTCPPort() { 69 uint16_t GetCustomBlimpletTCPPort() {
40 if (base::CommandLine::ForCurrentProcess()->HasSwitch( 70 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
41 switches::kBlimpletTCPPort)) { 71 switches::kBlimpletTCPPort)) {
42 std::string port_str = 72 std::string port_str =
43 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 73 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
44 switches::kBlimpletTCPPort); 74 switches::kBlimpletTCPPort);
45 uint port_64t; 75 uint port_64t;
46 if (!base::StringToUint(port_str, &port_64t) || 76 if (!base::StringToUint(port_str, &port_64t) ||
47 !base::IsValueInRangeForNumericType<uint16_t>(port_64t)) { 77 !base::IsValueInRangeForNumericType<uint16_t>(port_64t)) {
48 CHECK(false) << "Invalid BlimpletAssignment port " << port_str; 78 CHECK(false) << "Invalid BlimpletAssignment port " << port_str;
49 } 79 }
50 return base::checked_cast<uint16_t>(port_64t); 80 return base::checked_cast<uint16_t>(port_64t);
51 } else { 81 } else {
52 return kDefaultBlimpletTCPPort; 82 return kDefaultBlimpletTCPPort;
53 } 83 }
54 } 84 }
55 85
86 GURL GetBlimpAssignerURL() {
87 // TODO(dtrainor): Add a way to specify another assigner.
88 return GURL(kProdBlimpAssignerURL);
89 }
90
56 } // namespace 91 } // namespace
57 92
58 namespace client { 93 namespace client {
59 94
60 AssignmentSource::AssignmentSource( 95 AssignmentSource::AssignmentSource(
61 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner) 96 const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
62 : main_task_runner_(main_task_runner) {} 97 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
98 : main_task_runner_(main_task_runner),
99 url_request_context_(
100 new BlimpClientURLRequestContextGetter(io_task_runner)) {}
63 101
64 AssignmentSource::~AssignmentSource() {} 102 AssignmentSource::~AssignmentSource() {}
65 103
66 void AssignmentSource::GetAssignment(const AssignmentCallback& callback) { 104 void AssignmentSource::GetAssignment(const std::string& token,
105 const AssignmentCallback& callback) {
67 DCHECK(main_task_runner_->BelongsToCurrentThread()); 106 DCHECK(main_task_runner_->BelongsToCurrentThread());
68 Assignment assignment; 107
69 assignment.ip_endpoint = 108 // Cancel any outstanding callback.
70 net::IPEndPoint(GetBlimpletIPAddress(), GetBlimpletTCPPort()); 109 if (callback_)
Kevin M 2016/02/12 19:45:26 Use !callback_.is_null() and base::ResetAndReturn
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
71 assignment.client_token = kDummyClientToken; 110 RespondWithError(ASSIGNMENT_SOURCE_RESULT_SERVER_INTERRUPTED);
72 main_task_runner_->PostTask(FROM_HERE, base::Bind(callback, assignment)); 111 callback_.reset(new AssignmentCallback(callback));
Kevin M 2016/02/12 19:45:26 You can just use =
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
112
113 // Attempt to build a custom assignment from command line arguments.
114 if (HasCustomBlimpletEndpoint()) {
Kevin M 2016/02/12 19:45:26 Can you turn HasCustom...() into GetCustom...() an
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
115 Assignment assignment;
116 assignment.ip_endpoint = net::IPEndPoint(GetCustomBlimpletIPAddress(),
117 GetCustomBlimpletTCPPort());
118 assignment.client_token = kDummyClientToken;
119
120 // Post the result so that the behavior of this function is consistent.
121 main_task_runner_->PostTask(
122 FROM_HERE,
123 base::Bind(*callback_, ASSIGNMENT_SOURCE_RESULT_OKAY, assignment));
124 callback_.reset();
125 return;
126 }
127
128 // Call out to the network for a real assignment. Build the network request
129 // to hit the assigner.
130 url_fetcher_ = net::URLFetcher::Create(GetBlimpAssignerURL(),
131 net::URLFetcher::POST, this);
132 url_fetcher_->SetRequestContext(url_request_context_.get());
133 url_fetcher_->SetAutomaticallyRetryOn5xx(false);
134 url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(0);
135 url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
136 net::LOAD_DO_NOT_SAVE_COOKIES |
137 net::LOAD_DO_NOT_SEND_COOKIES);
138 url_fetcher_->AddExtraRequestHeader("Authorization: Bearer " + token);
139
140 // Write the JSON for the request data.
141 base::DictionaryValue dictionary;
142 dictionary.SetString(kProtocolVersionKey, blimp::kEngineVersion);
143 std::string json;
144 base::JSONWriter::Write(dictionary, &json);
145 url_fetcher_->SetUploadData("application/json", json);
146
147 url_fetcher_->Start();
148 }
149
150 void AssignmentSource::OnURLFetchComplete(const net::URLFetcher* source) {
151 DCHECK(callback_.get());
Kevin M 2016/02/12 19:45:26 Could there be two concurrent URL fetches underway
David Trainor- moved to gerrit 2016/02/17 21:09:47 Luckily the URLFetcher won't notify it's delegate
152
153 if (!source->GetStatus().is_success()) {
154 RespondWithError(ASSIGNMENT_SOURCE_RESULT_NETWORK_FAILURE);
155 return;
156 }
157
158 switch (source->GetResponseCode()) {
159 case net::HTTP_OK: {
160 // Grab the response from the assigner request.
161 std::string response;
162 if (!source->GetResponseAsString(&response)) {
Kevin M 2016/02/12 19:45:26 Since the outcome is the same for all these cases,
David Trainor- moved to gerrit 2016/02/17 21:09:47 I like idea I'll try it out. If we have unit test
163 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
164 return;
165 }
166
167 // Attempt to interpret the response as JSON and treat it as a dictionary.
168 scoped_ptr<base::Value> json = base::JSONReader::Read(response);
169 if (!json) {
170 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
171 return;
172 }
173
174 const base::DictionaryValue* dict;
175 if (!json->GetAsDictionary(&dict)) {
176 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
177 return;
178 }
179
180 // Validate that all the expected fields are present.
181 std::string client_token;
182 std::string host;
183 int port;
184 std::string cert_fingerprint;
185 std::string cert;
186 if (!dict->GetString(kClientTokenKey, &client_token) ||
Kevin M 2016/02/12 19:45:26 !(x && y && z...)?
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
187 !dict->GetString(kHostKey, &host) ||
188 !dict->GetInteger(kPortKey, &port) ||
189 !dict->GetString(kCertificateFingerprintKey, &cert_fingerprint) ||
190 !dict->GetString(kCertificateKey, &cert)) {
191 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
192 return;
193 }
194
195 net::IPAddress ip_address;
196 if (!net::IPAddress::FromIPLiteral(host, &ip_address)) {
Kevin M 2016/02/12 19:45:26 Rebase; this method was just renamed!
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
197 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
198 return;
199 }
200
201 if (!base::IsValueInRangeForNumericType<uint16_t>(port)) {
202 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
203 return;
204 }
205
206 Assignment assignment;
207 assignment.ip_endpoint = net::IPEndPoint(ip_address, port);
208 assignment.client_token = client_token;
209 assignment.certificate = cert;
210 assignment.certificate_fingerprint = cert_fingerprint;
211
212 RespondWithAssignment(assignment);
213 } break;
Kevin M 2016/02/12 19:45:26 This is a huge switch case. I think it muddles the
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
214 case net::HTTP_BAD_REQUEST:
215 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_REQUEST);
216 break;
217 case net::HTTP_UNAUTHORIZED:
218 RespondWithError(ASSIGNMENT_SOURCE_RESULT_EXPIRED_ACCESS_TOKEN);
219 break;
220 case net::HTTP_FORBIDDEN:
221 RespondWithError(ASSIGNMENT_SOURCE_RESULT_USER_INVALID);
222 break;
223 case 429: // Too Many Requests
224 RespondWithError(ASSIGNMENT_SOURCE_RESULT_OUT_OF_VMS);
225 break;
226 case net::HTTP_INTERNAL_SERVER_ERROR:
227 RespondWithError(ASSIGNMENT_SOURCE_RESULT_SERVER_ERROR);
228 break;
229 default:
230 RespondWithError(ASSIGNMENT_SOURCE_RESULT_BAD_RESPONSE);
231 break;
232 }
233 }
234
235 void AssignmentSource::RespondWithAssignment(const Assignment& assignment) {
Kevin M 2016/02/12 19:45:27 Only one call site; is it necessary to put this in
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
236 DCHECK(callback_.get());
237 callback_->Run(ASSIGNMENT_SOURCE_RESULT_OKAY, assignment);
Kevin M 2016/02/12 19:45:26 ASSIGNMENT_SOURCE_OK for consistency with the spel
Kevin M 2016/02/12 19:45:26 Use base::ResetAndReturn().Run() instead of Run an
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
238 callback_.reset();
239 }
240
241 void AssignmentSource::RespondWithError(AssignmentSourceResult result) {
Kevin M 2016/02/12 19:45:26 I think the logic here is trivial enough to warran
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
242 DCHECK(result != ASSIGNMENT_SOURCE_RESULT_OKAY);
Kevin M 2016/02/12 19:45:26 DCHECK_NE
David Trainor- moved to gerrit 2016/02/17 21:09:47 Acknowledged.
243 DCHECK(callback_.get());
244 callback_->Run(result, Assignment());
Kevin M 2016/02/12 19:45:26 Ditto for ResetAndReturn
David Trainor- moved to gerrit 2016/02/17 21:09:47 Done.
245 callback_.reset();
73 } 246 }
74 247
75 } // namespace client 248 } // namespace client
76 } // namespace blimp 249 } // namespace blimp
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698