blob: 67615c24a97c772505ac1c6fc27852a49d3ef141 [file] [log] [blame]
Yuwei Huang5a84f9532019-02-20 18:29:011// Copyright 2019 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
Yuwei Huangbdc4a972019-03-01 17:43:065#include "remoting/test/ftl_signaling_playground.h"
6
Yuwei Huang30bac9e2019-03-01 21:28:147#include <inttypes.h>
Yuwei Huangaae881f2019-03-01 23:10:278#include <string>
Yuwei Huangbdc4a972019-03-01 17:43:069#include <utility>
Yuwei Huang98655b92019-03-04 23:41:4110#include <vector>
Yuwei Huangbdc4a972019-03-01 17:43:0611
Yuwei Huangaae881f2019-03-01 23:10:2712#include "base/base64.h"
Yuwei Huang575c32ee2019-02-27 04:41:5613#include "base/bind.h"
Yuwei Huangbdc4a972019-03-01 17:43:0614#include "base/bind_helpers.h"
Yuwei Huang5a84f9532019-02-20 18:29:0115#include "base/command_line.h"
Yuwei Huangbdc4a972019-03-01 17:43:0616#include "base/files/file_path.h"
Yuwei Huangaae881f2019-03-01 23:10:2717#include "base/guid.h"
Yuwei Huang5a84f9532019-02-20 18:29:0118#include "base/logging.h"
Yuwei Huangbdc4a972019-03-01 17:43:0619#include "base/path_service.h"
Yuwei Huangaae881f2019-03-01 23:10:2720#include "base/strings/string_number_conversions.h"
Yuwei Huangbdc4a972019-03-01 17:43:0621#include "base/strings/stringprintf.h"
Yuwei Huangb8e1f2d2019-03-22 19:40:2622#include "base/task/post_task.h"
Yuwei Huang946cebd42019-03-04 22:14:2023#include "remoting/base/fake_oauth_token_getter.h"
Yuwei Huangbdc4a972019-03-01 17:43:0624#include "remoting/base/oauth_token_getter_impl.h"
Yuwei Huange5b94452019-03-23 00:26:0525#include "remoting/signaling/ftl_device_id_provider.h"
Yuwei Huang3f1fcd362019-03-11 20:27:1726#include "remoting/signaling/ftl_services.grpc.pb.h"
Yuwei Huangbdc4a972019-03-01 17:43:0627#include "remoting/test/test_oauth_token_factory.h"
Yuwei Huang946cebd42019-03-04 22:14:2028#include "remoting/test/test_token_storage.h"
Yuwei Huangbdc4a972019-03-01 17:43:0629
30namespace {
31
32constexpr char kSwitchNameHelp[] = "help";
33constexpr char kSwitchNameAuthCode[] = "code";
Yuwei Huang946cebd42019-03-04 22:14:2034constexpr char kSwitchNameUsername[] = "username";
35constexpr char kSwitchNameStoragePath[] = "storage-path";
Yuwei Huangbdc4a972019-03-01 17:43:0636
Yuwei Huangbdc4a972019-03-01 17:43:0637// Reads a newline-terminated string from stdin.
38std::string ReadString() {
39 const int kMaxLen = 1024;
40 std::string str(kMaxLen, 0);
41 char* result = fgets(&str[0], kMaxLen, stdin);
42 if (!result)
43 return std::string();
44 size_t newline_index = str.find('\n');
45 if (newline_index != std::string::npos)
46 str[newline_index] = '\0';
47 str.resize(strlen(&str[0]));
48 return str;
49}
50
51// Read the value of |switch_name| from command line if it exists, otherwise
52// read from stdin.
53std::string ReadStringFromCommandLineOrStdin(const std::string& switch_name,
54 const std::string& read_prompt) {
55 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
56 if (command_line->HasSwitch(switch_name)) {
57 return command_line->GetSwitchValueASCII(switch_name);
58 }
59 printf("%s", read_prompt.c_str());
60 return ReadString();
61}
62
Yuwei Huangb8e1f2d2019-03-22 19:40:2663// Wait for the user to press enter key on an anonymous thread and calls
64// |on_done| once it is done.
65void WaitForEnterKey(base::OnceClosure on_done) {
66 base::PostTaskWithTraitsAndReply(FROM_HERE, {base::MayBlock()},
67 base::BindOnce([]() { getchar(); }),
68 std::move(on_done));
69}
70
71void PrintGrpcStatusError(const grpc::Status& status) {
72 DCHECK(!status.ok());
73 fprintf(stderr, "RPC failed. Code=%d, Message=%s\n", status.error_code(),
74 status.error_message().c_str());
75 switch (status.error_code()) {
76 case grpc::StatusCode::UNAVAILABLE:
77 fprintf(stderr,
78 "Set the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable "
79 "to third_party/grpc/src/etc/roots.pem if gRPC cannot locate the "
80 "root certificates.\n");
81 break;
82 case grpc::StatusCode::UNAUTHENTICATED:
83 fprintf(
84 stderr,
85 "Request is unauthenticated. Make sure you have run SignInGaia.\n");
86 break;
87 default:
88 break;
89 }
90}
91
Yuwei Huangbdc4a972019-03-01 17:43:0692} // namespace
Yuwei Huang5a84f9532019-02-20 18:29:0193
Yuwei Huang575c32ee2019-02-27 04:41:5694namespace remoting {
Yuwei Huang5a84f9532019-02-20 18:29:0195
Yuwei Huang946cebd42019-03-04 22:14:2096FtlSignalingPlayground::FtlSignalingPlayground() : weak_factory_(this) {}
Yuwei Huang575c32ee2019-02-27 04:41:5697
98FtlSignalingPlayground::~FtlSignalingPlayground() = default;
99
Yuwei Huangbdc4a972019-03-01 17:43:06100bool FtlSignalingPlayground::ShouldPrintHelp() {
101 return base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchNameHelp);
102}
103
104void FtlSignalingPlayground::PrintHelp() {
Yuwei Huang946cebd42019-03-04 22:14:20105 printf(
106 "Usage: %s [--code=<auth-code>] [--storage-path=<storage-path>] "
107 "[--username=<[email protected]>]\n",
108 base::CommandLine::ForCurrentProcess()
109 ->GetProgram()
110 .MaybeAsASCII()
111 .c_str());
Yuwei Huangbdc4a972019-03-01 17:43:06112}
113
114void FtlSignalingPlayground::StartAndAuthenticate() {
Yuwei Huang946cebd42019-03-04 22:14:20115 DCHECK(!storage_);
Yuwei Huangbdc4a972019-03-01 17:43:06116 DCHECK(!token_getter_factory_);
117 DCHECK(!token_getter_);
Yuwei Huang3f1fcd362019-03-11 20:27:17118 DCHECK(!ftl_context_);
Yuwei Huangbdc4a972019-03-01 17:43:06119
Yuwei Huangbdc4a972019-03-01 17:43:06120 token_getter_factory_ = std::make_unique<TestOAuthTokenGetterFactory>();
121
Yuwei Huang946cebd42019-03-04 22:14:20122 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
123 std::string username = cmd_line->GetSwitchValueASCII(kSwitchNameUsername);
124 base::FilePath storage_path =
125 cmd_line->GetSwitchValuePath(kSwitchNameStoragePath);
126 storage_ = test::TestTokenStorage::OnDisk(username, storage_path);
127
128 std::string access_token = storage_->FetchAccessToken();
129 if (access_token.empty()) {
Yuwei Huang3f1fcd362019-03-11 20:27:17130 AuthenticateAndResetServices();
Yuwei Huang946cebd42019-03-04 22:14:20131 } else {
132 VLOG(0) << "Reusing access token: " << access_token;
133 token_getter_ = std::make_unique<FakeOAuthTokenGetter>(
134 OAuthTokenGetter::Status::SUCCESS, "[email protected]",
135 access_token);
Yuwei Huang3f1fcd362019-03-11 20:27:17136 ResetServices();
Yuwei Huang946cebd42019-03-04 22:14:20137 }
Yuwei Huangaae881f2019-03-01 23:10:27138
139 StartLoop();
140}
141
142void FtlSignalingPlayground::StartLoop() {
143 while (true) {
144 printf(
145 "\nOptions:\n"
Yuwei Huangb8e1f2d2019-03-22 19:40:26146 " 1. SignInGaia\n"
147 " 2. GetIceServer\n"
Yuwei Huang98655b92019-03-04 23:41:41148 " 3. PullMessages\n"
Yuwei Huangb8e1f2d2019-03-22 19:40:26149 " 4. ReceiveMessages\n"
Yuwei Huang4de60092019-03-22 21:19:44150 " 5. SendMessage\n"
151 " 6. Quit\n\n"
Yuwei Huangaae881f2019-03-01 23:10:27152 "Your choice [number]: ");
153 int choice = 0;
154 base::StringToInt(ReadString(), &choice);
155 base::RunLoop run_loop;
156 switch (choice) {
157 case 1:
Yuwei Huangb8e1f2d2019-03-22 19:40:26158 SignInGaia(run_loop.QuitClosure());
Yuwei Huangaae881f2019-03-01 23:10:27159 break;
160 case 2:
Yuwei Huangb8e1f2d2019-03-22 19:40:26161 GetIceServer(run_loop.QuitClosure());
Yuwei Huangaae881f2019-03-01 23:10:27162 break;
163 case 3:
Yuwei Huang98655b92019-03-04 23:41:41164 PullMessages(run_loop.QuitClosure());
165 break;
166 case 4:
Yuwei Huangb8e1f2d2019-03-22 19:40:26167 StartReceivingMessages(run_loop.QuitWhenIdleClosure());
168 break;
169 case 5:
Yuwei Huang4de60092019-03-22 21:19:44170 SendMessage(run_loop.QuitClosure());
171 break;
172 case 6:
Yuwei Huangaae881f2019-03-01 23:10:27173 return;
174 default:
175 fprintf(stderr, "Unknown option\n");
176 continue;
177 }
178 run_loop.Run();
179 }
Yuwei Huangbdc4a972019-03-01 17:43:06180}
181
Yuwei Huang3f1fcd362019-03-11 20:27:17182void FtlSignalingPlayground::ResetServices() {
183 ftl_context_ = std::make_unique<FtlGrpcContext>(token_getter_.get());
184 peer_to_peer_stub_ = PeerToPeer::NewStub(ftl_context_->channel());
Yuwei Huang968994cb2019-03-21 00:55:27185
186 message_subscription_.reset();
187 messaging_client_ = std::make_unique<FtlMessagingClient>(ftl_context_.get());
188 message_subscription_ = messaging_client_->RegisterMessageCallback(
189 base::BindRepeating(&FtlSignalingPlayground::OnMessageReceived,
190 weak_factory_.GetWeakPtr()));
Yuwei Huange5b94452019-03-23 00:26:05191 registration_manager_ = std::make_unique<FtlRegistrationManager>(
192 ftl_context_.get(),
193 std::make_unique<FtlDeviceIdProvider>(storage_.get()));
Yuwei Huang3f1fcd362019-03-11 20:27:17194}
195
196void FtlSignalingPlayground::AuthenticateAndResetServices() {
Yuwei Huang946cebd42019-03-04 22:14:20197 static const std::string read_auth_code_prompt = base::StringPrintf(
198 "Please authenticate at:\n\n"
199 " %s\n\n"
200 "Enter the auth code: ",
201 TestOAuthTokenGetterFactory::GetAuthorizationCodeUri().c_str());
202 std::string auth_code = ReadStringFromCommandLineOrStdin(
203 kSwitchNameAuthCode, read_auth_code_prompt);
204
205 // Make sure we don't try to reuse an auth code.
206 base::CommandLine::ForCurrentProcess()->RemoveSwitch(kSwitchNameAuthCode);
207
208 // We can't get back the refresh token since we have first-party scope, so
209 // we are not trying to store it.
210 token_getter_ = token_getter_factory_->CreateFromIntermediateCredentials(
211 auth_code,
212 base::DoNothing::Repeatedly<const std::string&, const std::string&>());
213
214 // Get the access token so that we can reuse it for next time.
215 base::OnceClosure on_access_token_done = base::DoNothing::Once();
216 base::RunLoop run_loop;
217 if (!base::RunLoop::IsRunningOnCurrentThread()) {
218 on_access_token_done = run_loop.QuitClosure();
219 }
220 token_getter_->CallWithToken(base::BindOnce(
221 &FtlSignalingPlayground::OnAccessToken, weak_factory_.GetWeakPtr(),
222 std::move(on_access_token_done)));
223 if (!base::RunLoop::IsRunningOnCurrentThread()) {
224 run_loop.Run();
225 }
226
Yuwei Huang3f1fcd362019-03-11 20:27:17227 ResetServices();
Yuwei Huang946cebd42019-03-04 22:14:20228}
229
230void FtlSignalingPlayground::OnAccessToken(base::OnceClosure on_done,
231 OAuthTokenGetter::Status status,
232 const std::string& user_email,
233 const std::string& access_token) {
234 DCHECK(status == OAuthTokenGetter::Status::SUCCESS);
235 VLOG(0) << "Received access_token: " << access_token;
236 storage_->StoreAccessToken(access_token);
237 std::move(on_done).Run();
238}
239
Yuwei Huang575c32ee2019-02-27 04:41:56240void FtlSignalingPlayground::GetIceServer(base::OnceClosure on_done) {
Yuwei Huang3f1fcd362019-03-11 20:27:17241 DCHECK(peer_to_peer_stub_);
242 VLOG(0) << "Running GetIceServer...";
243 ftl_context_->ExecuteRpc(
244 base::BindOnce(&PeerToPeer::Stub::AsyncGetICEServer,
245 base::Unretained(peer_to_peer_stub_.get())),
246 ftl::GetICEServerRequest(),
Yuwei Huang946cebd42019-03-04 22:14:20247 base::BindOnce(&FtlSignalingPlayground::OnGetIceServerResponse,
248 weak_factory_.GetWeakPtr(), std::move(on_done)));
Yuwei Huang575c32ee2019-02-27 04:41:56249}
250
Yuwei Huang575c32ee2019-02-27 04:41:56251void FtlSignalingPlayground::OnGetIceServerResponse(
252 base::OnceClosure on_done,
Yuwei Huang81bbc1a2019-03-08 20:46:06253 const grpc::Status& status,
Yuwei Huang575c32ee2019-02-27 04:41:56254 const ftl::GetICEServerResponse& response) {
255 if (status.ok()) {
Yuwei Huang30bac9e2019-03-01 21:28:14256 printf("Ice transport policy: %s\n",
257 response.ice_config().ice_transport_policy().c_str());
Yuwei Huang575c32ee2019-02-27 04:41:56258 for (const ftl::ICEServerList& server :
259 response.ice_config().ice_servers()) {
Yuwei Huang30bac9e2019-03-01 21:28:14260 printf(
261 "ICE server:\n"
262 " hostname=%s\n"
263 " username=%s\n"
264 " credential=%s\n"
265 " max_rate_kbps=%" PRId64 "\n",
266 server.hostname().c_str(), server.username().c_str(),
267 server.credential().c_str(), server.max_rate_kbps());
Yuwei Huang575c32ee2019-02-27 04:41:56268 for (const std::string& url : server.urls()) {
Yuwei Huang30bac9e2019-03-01 21:28:14269 printf(" url=%s\n", url.c_str());
Yuwei Huang575c32ee2019-02-27 04:41:56270 }
271 }
272 } else {
Yuwei Huangb8e1f2d2019-03-22 19:40:26273 PrintGrpcStatusError(status);
Yuwei Huangaae881f2019-03-01 23:10:27274 }
275 std::move(on_done).Run();
276}
277
278void FtlSignalingPlayground::SignInGaia(base::OnceClosure on_done) {
Yuwei Huange5b94452019-03-23 00:26:05279 DCHECK(registration_manager_);
Yuwei Huangaae881f2019-03-01 23:10:27280 VLOG(0) << "Running SignInGaia...";
Yuwei Huange5b94452019-03-23 00:26:05281 registration_manager_->SignInGaia(
Yuwei Huangaae881f2019-03-01 23:10:27282 base::BindOnce(&FtlSignalingPlayground::OnSignInGaiaResponse,
Yuwei Huang946cebd42019-03-04 22:14:20283 weak_factory_.GetWeakPtr(), std::move(on_done)));
Yuwei Huangaae881f2019-03-01 23:10:27284}
285
Yuwei Huange5b94452019-03-23 00:26:05286void FtlSignalingPlayground::OnSignInGaiaResponse(base::OnceClosure on_done,
287 const grpc::Status& status) {
Yuwei Huangaae881f2019-03-01 23:10:27288 if (status.ok()) {
Yuwei Huangaae881f2019-03-01 23:10:27289 std::string registration_id_base64;
Yuwei Huange5b94452019-03-23 00:26:05290 base::Base64Encode(registration_manager_->registration_id(),
291 &registration_id_base64);
292 printf("Service signed in. registration_id(base64)=%s\n",
293 registration_id_base64.c_str());
Yuwei Huang98655b92019-03-04 23:41:41294 } else {
Yuwei Huangb8e1f2d2019-03-22 19:40:26295 if (status.error_code() == grpc::StatusCode::UNAUTHENTICATED) {
296 VLOG(0) << "Grpc request failed to authenticate. "
297 << "Trying to reauthenticate...";
298 AuthenticateAndResetServices();
299 } else {
300 PrintGrpcStatusError(status);
301 }
Yuwei Huang98655b92019-03-04 23:41:41302 }
303 std::move(on_done).Run();
304}
305
306void FtlSignalingPlayground::PullMessages(base::OnceClosure on_done) {
Yuwei Huang968994cb2019-03-21 00:55:27307 DCHECK(messaging_client_);
Yuwei Huang98655b92019-03-04 23:41:41308 VLOG(0) << "Running PullMessages...";
Yuwei Huang3f1fcd362019-03-11 20:27:17309
Yuwei Huang968994cb2019-03-21 00:55:27310 messaging_client_->PullMessages(
Yuwei Huang98655b92019-03-04 23:41:41311 base::BindOnce(&FtlSignalingPlayground::OnPullMessagesResponse,
312 weak_factory_.GetWeakPtr(), std::move(on_done)));
313}
314
315void FtlSignalingPlayground::OnPullMessagesResponse(
316 base::OnceClosure on_done,
Yuwei Huang968994cb2019-03-21 00:55:27317 const grpc::Status& status) {
Yuwei Huang98655b92019-03-04 23:41:41318 if (!status.ok()) {
Yuwei Huangb8e1f2d2019-03-22 19:40:26319 PrintGrpcStatusError(status);
Yuwei Huang5a84f9532019-02-20 18:29:01320 }
Yuwei Huang575c32ee2019-02-27 04:41:56321 std::move(on_done).Run();
322}
Yuwei Huang5a84f9532019-02-20 18:29:01323
Yuwei Huang4de60092019-03-22 21:19:44324void FtlSignalingPlayground::SendMessage(base::OnceClosure on_done) {
325 DCHECK(messaging_client_);
326 VLOG(0) << "Running SendMessage...";
327
328 printf("Receiver ID: ");
329 std::string receiver_id = ReadString();
330
331 printf("Receiver registration ID (base64, optional): ");
332 std::string registration_id_base64 = ReadString();
333
334 std::string registration_id;
335 bool success = base::Base64Decode(registration_id_base64, &registration_id);
336 if (!success) {
337 fprintf(stderr, "Your input can't be base64 decoded.\n");
338 std::move(on_done).Run();
339 return;
340 }
341 DoSendMessage(receiver_id, registration_id, std::move(on_done), true);
342}
343
344void FtlSignalingPlayground::DoSendMessage(const std::string& receiver_id,
345 const std::string& registration_id,
346 base::OnceClosure on_done,
347 bool should_keep_running) {
348 if (!should_keep_running) {
349 std::move(on_done).Run();
350 return;
351 }
352
353 printf("Message (enter nothing to quit): ");
354 std::string message = ReadString();
355
356 if (message.empty()) {
357 std::move(on_done).Run();
358 return;
359 }
360
361 auto on_continue = base::BindOnce(&FtlSignalingPlayground::DoSendMessage,
362 weak_factory_.GetWeakPtr(), receiver_id,
363 registration_id, std::move(on_done));
364
365 messaging_client_->SendMessage(
366 receiver_id, registration_id, message,
367 base::BindOnce(&FtlSignalingPlayground::OnSendMessageResponse,
368 weak_factory_.GetWeakPtr(), std::move(on_continue)));
369}
370
371void FtlSignalingPlayground::OnSendMessageResponse(
372 base::OnceCallback<void(bool)> on_continue,
373 const grpc::Status& status) {
374 if (!status.ok()) {
375 PrintGrpcStatusError(status);
376 } else {
377 printf("Message successfully sent.\n");
378 }
379 std::move(on_continue).Run(status.ok());
380}
381
Yuwei Huangb8e1f2d2019-03-22 19:40:26382void FtlSignalingPlayground::StartReceivingMessages(base::OnceClosure on_done) {
383 VLOG(0) << "Running StartReceivingMessages...";
384 messaging_client_->StartReceivingMessages(
385 base::BindOnce(&FtlSignalingPlayground::OnStartReceivingMessagesDone,
386 weak_factory_.GetWeakPtr(), std::move(on_done)));
387}
388
389void FtlSignalingPlayground::StopReceivingMessages(base::OnceClosure on_done) {
390 messaging_client_->StopReceivingMessages();
391 std::move(on_done).Run();
392}
393
Yuwei Huang968994cb2019-03-21 00:55:27394void FtlSignalingPlayground::OnMessageReceived(const std::string& sender_id,
395 const std::string& message) {
396 printf(
397 "Received message:\n"
398 " Sender ID=%s\n"
399 " Message=%s\n",
400 sender_id.c_str(), message.c_str());
401}
402
Yuwei Huangb8e1f2d2019-03-22 19:40:26403void FtlSignalingPlayground::OnStartReceivingMessagesDone(
404 base::OnceClosure on_done,
405 const grpc::Status& status) {
406 if (status.error_code() == grpc::StatusCode::CANCELLED) {
407 printf("ReceiveMessages stream canceled by client.\n");
408 std::move(on_done).Run();
409 return;
410 }
411 if (!status.ok()) {
412 PrintGrpcStatusError(status);
413 std::move(on_done).Run();
414 return;
415 }
416 printf("Started receiving messages. Press enter to stop streaming...\n");
417 WaitForEnterKey(base::BindOnce(&FtlSignalingPlayground::StopReceivingMessages,
418 weak_factory_.GetWeakPtr(),
419 std::move(on_done)));
420}
421
Yuwei Huang575c32ee2019-02-27 04:41:56422} // namespace remoting