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

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

Issue 2157133003: Move blimp AssignmentSource to core (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@move-blimp-client-switches-to-core
Patch Set: Fix gn checks Created 4 years, 5 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "blimp/client/session/assignment_source.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/command_line.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/location.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/task_runner_util.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/values.h"
20 #include "blimp/client/core/blimp_client_switches.h"
21 #include "blimp/common/get_client_token.h"
22 #include "blimp/common/protocol_version.h"
23 #include "components/safe_json/safe_json_parser.h"
24 #include "net/base/ip_address.h"
25 #include "net/base/ip_endpoint.h"
26 #include "net/base/load_flags.h"
27 #include "net/base/net_errors.h"
28 #include "net/http/http_status_code.h"
29 #include "net/proxy/proxy_config_service.h"
30 #include "net/proxy/proxy_service.h"
31 #include "net/url_request/url_fetcher.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_context_builder.h"
34 #include "net/url_request/url_request_context_getter.h"
35
36 namespace blimp {
37 namespace client {
38
39 namespace {
40
41 // Assignment request JSON keys.
42 const char kProtocolVersionKey[] = "protocol_version";
43
44 // Assignment response JSON keys.
45 const char kClientTokenKey[] = "clientToken";
46 const char kHostKey[] = "host";
47 const char kPortKey[] = "port";
48 const char kCertificateKey[] = "certificate";
49
50 // Possible arguments for the "--engine-transport" command line parameter.
51 const char kSSLTransportValue[] = "ssl";
52 const char kTCPTransportValue[] = "tcp";
53
54 class SimpleURLRequestContextGetter : public net::URLRequestContextGetter {
55 public:
56 SimpleURLRequestContextGetter(
57 scoped_refptr<base::SingleThreadTaskRunner> io_loop_task_runner)
58 : io_loop_task_runner_(std::move(io_loop_task_runner)),
59 proxy_config_service_(net::ProxyService::CreateSystemProxyConfigService(
60 io_loop_task_runner_,
61 io_loop_task_runner_)) {}
62
63 // net::URLRequestContextGetter implementation.
64 net::URLRequestContext* GetURLRequestContext() override {
65 if (!url_request_context_) {
66 net::URLRequestContextBuilder builder;
67 builder.set_proxy_config_service(std::move(proxy_config_service_));
68 builder.DisableHttpCache();
69 url_request_context_ = builder.Build();
70 }
71
72 return url_request_context_.get();
73 }
74
75 scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
76 const override {
77 return io_loop_task_runner_;
78 }
79
80 private:
81 ~SimpleURLRequestContextGetter() override {}
82
83 scoped_refptr<base::SingleThreadTaskRunner> io_loop_task_runner_;
84 std::unique_ptr<net::URLRequestContext> url_request_context_;
85
86 // Temporary storage for the ProxyConfigService, which needs to be created on
87 // the main thread but cleared on the IO thread. This will be built in the
88 // constructor and cleared on the IO thread. Due to the usage of this class
89 // this is safe.
90 std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
91
92 DISALLOW_COPY_AND_ASSIGN(SimpleURLRequestContextGetter);
93 };
94
95 bool IsValidIpPortNumber(unsigned port) {
96 return port > 0 && port <= 65535;
97 }
98
99 // Populates an Assignment using command-line parameters, if provided.
100 // Returns a null Assignment if no parameters were set.
101 // Must be called on a thread suitable for file IO.
102 Assignment GetAssignmentFromCommandLine() {
103 Assignment assignment;
104
105 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
106 assignment.client_token = GetClientToken(*cmd_line);
107 CHECK(!assignment.client_token.empty()) << "No client token provided.";
108
109 unsigned port_parsed = 0;
110 if (!base::StringToUint(
111 cmd_line->GetSwitchValueASCII(switches::kEnginePort),
112 &port_parsed) || !IsValidIpPortNumber(port_parsed)) {
113 DLOG(FATAL) << "--engine-port must be a value between 1 and 65535.";
114 return Assignment();
115 }
116
117 net::IPAddress ip_address;
118 std::string ip_str = cmd_line->GetSwitchValueASCII(switches::kEngineIP);
119 if (!ip_address.AssignFromIPLiteral(ip_str)) {
120 DLOG(FATAL) << "Invalid engine IP " << ip_str;
121 return Assignment();
122 }
123 assignment.engine_endpoint =
124 net::IPEndPoint(ip_address, base::checked_cast<uint16_t>(port_parsed));
125
126 std::string transport_str =
127 cmd_line->GetSwitchValueASCII(switches::kEngineTransport);
128 if (transport_str == kSSLTransportValue) {
129 assignment.transport_protocol = Assignment::TransportProtocol::SSL;
130 } else if (transport_str == kTCPTransportValue) {
131 assignment.transport_protocol = Assignment::TransportProtocol::TCP;
132 } else {
133 DLOG(FATAL) << "Invalid engine transport " << transport_str;
134 return Assignment();
135 }
136
137 scoped_refptr<net::X509Certificate> cert;
138 if (assignment.transport_protocol == Assignment::TransportProtocol::SSL) {
139 base::FilePath cert_path =
140 cmd_line->GetSwitchValuePath(switches::kEngineCertPath);
141 if (cert_path.empty()) {
142 DLOG(FATAL) << "Missing required parameter --"
143 << switches::kEngineCertPath << ".";
144 return Assignment();
145 }
146 std::string cert_str;
147 if (!base::ReadFileToString(cert_path, &cert_str)) {
148 DLOG(FATAL) << "Couldn't read from file: "
149 << cert_path.LossyDisplayName();
150 return Assignment();
151 }
152 net::CertificateList cert_list =
153 net::X509Certificate::CreateCertificateListFromBytes(
154 cert_str.data(), cert_str.size(),
155 net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
156 DLOG_IF(FATAL, (cert_list.size() != 1u))
157 << "Only one cert is allowed in PEM cert list.";
158 assignment.cert = std::move(cert_list[0]);
159 }
160
161 if (!assignment.IsValid()) {
162 DLOG(FATAL) << "Invalid command-line assignment.";
163 return Assignment();
164 }
165
166 return assignment;
167 }
168
169 } // namespace
170
171 Assignment::Assignment() : transport_protocol(TransportProtocol::UNKNOWN) {}
172
173 Assignment::Assignment(const Assignment& other) = default;
174
175 Assignment::~Assignment() {}
176
177 bool Assignment::IsValid() const {
178 if (engine_endpoint.address().empty() || engine_endpoint.port() == 0 ||
179 transport_protocol == TransportProtocol::UNKNOWN) {
180 return false;
181 }
182 if (transport_protocol == TransportProtocol::SSL && !cert) {
183 return false;
184 }
185 return true;
186 }
187
188 AssignmentSource::AssignmentSource(
189 const GURL& assigner_endpoint,
190 const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
191 const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
192 : assigner_endpoint_(assigner_endpoint),
193 file_task_runner_(std::move(file_task_runner)),
194 url_request_context_(
195 new SimpleURLRequestContextGetter(network_task_runner)),
196 weak_factory_(this) {
197 DCHECK(assigner_endpoint_.is_valid());
198 }
199
200 AssignmentSource::~AssignmentSource() {}
201
202 void AssignmentSource::GetAssignment(const std::string& client_auth_token,
203 const AssignmentCallback& callback) {
204 DCHECK(callback_.is_null());
205 callback_ = AssignmentCallback(callback);
206
207 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEngineIP)) {
208 base::PostTaskAndReplyWithResult(
209 file_task_runner_.get(), FROM_HERE,
210 base::Bind(&GetAssignmentFromCommandLine),
211 base::Bind(&AssignmentSource::OnGetAssignmentFromCommandLineDone,
212 weak_factory_.GetWeakPtr(), client_auth_token));
213 } else {
214 QueryAssigner(client_auth_token);
215 }
216 }
217
218 void AssignmentSource::OnGetAssignmentFromCommandLineDone(
219 const std::string& client_auth_token,
220 Assignment parsed_assignment) {
221 // If GetAssignmentFromCommandLine succeeded, then return its output.
222 if (parsed_assignment.IsValid()) {
223 base::ResetAndReturn(&callback_)
224 .Run(AssignmentSource::RESULT_OK, parsed_assignment);
225 return;
226 }
227
228 // If no assignment was passed via the command line, then fall back on
229 // querying the Assigner service.
230 QueryAssigner(client_auth_token);
231 }
232
233 void AssignmentSource::QueryAssigner(const std::string& client_auth_token) {
234 // Call out to the network for a real assignment. Build the network request
235 // to hit the assigner.
236 url_fetcher_ =
237 net::URLFetcher::Create(assigner_endpoint_, net::URLFetcher::POST, this);
238 url_fetcher_->SetRequestContext(url_request_context_.get());
239 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
240 net::LOAD_DO_NOT_SEND_COOKIES);
241 url_fetcher_->AddExtraRequestHeader("Authorization: Bearer " +
242 client_auth_token);
243
244 // Write the JSON for the request data.
245 base::DictionaryValue dictionary;
246 dictionary.SetString(kProtocolVersionKey,
247 base::IntToString(kProtocolVersion));
248 std::string json;
249 base::JSONWriter::Write(dictionary, &json);
250 url_fetcher_->SetUploadData("application/json", json);
251 url_fetcher_->Start();
252 }
253
254 void AssignmentSource::OnURLFetchComplete(const net::URLFetcher* source) {
255 DCHECK(!callback_.is_null());
256 DCHECK_EQ(url_fetcher_.get(), source);
257
258 if (!source->GetStatus().is_success()) {
259 DVLOG(1) << "Assignment request failed due to network error: "
260 << net::ErrorToString(source->GetStatus().error());
261 base::ResetAndReturn(&callback_)
262 .Run(AssignmentSource::Result::RESULT_NETWORK_FAILURE, Assignment());
263 return;
264 }
265
266 switch (source->GetResponseCode()) {
267 case net::HTTP_OK:
268 ParseAssignerResponse();
269 break;
270 case net::HTTP_BAD_REQUEST:
271 base::ResetAndReturn(&callback_)
272 .Run(AssignmentSource::Result::RESULT_BAD_REQUEST, Assignment());
273 break;
274 case net::HTTP_UNAUTHORIZED:
275 base::ResetAndReturn(&callback_)
276 .Run(AssignmentSource::Result::RESULT_EXPIRED_ACCESS_TOKEN,
277 Assignment());
278 break;
279 case net::HTTP_FORBIDDEN:
280 base::ResetAndReturn(&callback_)
281 .Run(AssignmentSource::Result::RESULT_USER_INVALID, Assignment());
282 break;
283 case 429: // Too Many Requests
284 base::ResetAndReturn(&callback_)
285 .Run(AssignmentSource::Result::RESULT_OUT_OF_VMS, Assignment());
286 break;
287 case net::HTTP_INTERNAL_SERVER_ERROR:
288 base::ResetAndReturn(&callback_)
289 .Run(AssignmentSource::Result::RESULT_SERVER_ERROR, Assignment());
290 break;
291 default:
292 base::ResetAndReturn(&callback_)
293 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
294 break;
295 }
296 }
297
298 void AssignmentSource::ParseAssignerResponse() {
299 DCHECK(url_fetcher_.get());
300 DCHECK(url_fetcher_->GetStatus().is_success());
301 DCHECK_EQ(net::HTTP_OK, url_fetcher_->GetResponseCode());
302
303 // Grab the response from the assigner request.
304 std::string response;
305 if (!url_fetcher_->GetResponseAsString(&response)) {
306 base::ResetAndReturn(&callback_)
307 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
308 return;
309 }
310
311 safe_json::SafeJsonParser::Parse(
312 response,
313 base::Bind(&AssignmentSource::OnJsonParsed, weak_factory_.GetWeakPtr()),
314 base::Bind(&AssignmentSource::OnJsonParseError,
315 weak_factory_.GetWeakPtr()));
316 }
317
318 void AssignmentSource::OnJsonParsed(std::unique_ptr<base::Value> json) {
319 const base::DictionaryValue* dict;
320 if (!json->GetAsDictionary(&dict)) {
321 base::ResetAndReturn(&callback_)
322 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
323 return;
324 }
325
326 // Validate that all the expected fields are present.
327 std::string client_token;
328 std::string host;
329 int port;
330 std::string cert_str;
331 if (!(dict->GetString(kClientTokenKey, &client_token) &&
332 dict->GetString(kHostKey, &host) && dict->GetInteger(kPortKey, &port) &&
333 dict->GetString(kCertificateKey, &cert_str))) {
334 base::ResetAndReturn(&callback_)
335 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
336 return;
337 }
338
339 net::IPAddress ip_address;
340 if (!ip_address.AssignFromIPLiteral(host)) {
341 base::ResetAndReturn(&callback_)
342 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
343 return;
344 }
345
346 if (!base::IsValueInRangeForNumericType<uint16_t>(port)) {
347 base::ResetAndReturn(&callback_)
348 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
349 return;
350 }
351
352 net::CertificateList cert_list =
353 net::X509Certificate::CreateCertificateListFromBytes(
354 cert_str.data(), cert_str.size(),
355 net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
356 if (cert_list.size() != 1) {
357 base::ResetAndReturn(&callback_)
358 .Run(AssignmentSource::Result::RESULT_INVALID_CERT, Assignment());
359 return;
360 }
361
362 // The assigner assumes SSL-only and all engines it assigns only communicate
363 // over SSL.
364 Assignment assignment;
365 assignment.transport_protocol = Assignment::TransportProtocol::SSL;
366 assignment.engine_endpoint = net::IPEndPoint(ip_address, port);
367 assignment.client_token = client_token;
368 assignment.cert = std::move(cert_list[0]);
369
370 base::ResetAndReturn(&callback_)
371 .Run(AssignmentSource::Result::RESULT_OK, assignment);
372 }
373
374 void AssignmentSource::OnJsonParseError(const std::string& error) {
375 DLOG(ERROR) << "Error while parsing assigner JSON: " << error;
376 base::ResetAndReturn(&callback_)
377 .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment());
378 }
379
380 } // namespace client
381 } // namespace blimp
OLDNEW
« no previous file with comments | « blimp/client/session/assignment_source.h ('k') | blimp/client/session/assignment_source_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698