Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 1 | // 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 Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 5 | #include "remoting/test/ftl_signaling_playground.h" |
| 6 | |
Yuwei Huang | 30bac9e | 2019-03-01 21:28:14 | [diff] [blame] | 7 | #include <inttypes.h> |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 8 | #include <string> |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 9 | #include <utility> |
Yuwei Huang | 98655b9 | 2019-03-04 23:41:41 | [diff] [blame^] | 10 | #include <vector> |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 11 | |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 12 | #include "base/base64.h" |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 13 | #include "base/bind.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 14 | #include "base/bind_helpers.h" |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 15 | #include "base/command_line.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 16 | #include "base/files/file_path.h" |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 17 | #include "base/guid.h" |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 18 | #include "base/logging.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 19 | #include "base/path_service.h" |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 20 | #include "base/strings/string_number_conversions.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 21 | #include "base/strings/stringprintf.h" |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 22 | #include "remoting/base/fake_oauth_token_getter.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 23 | #include "remoting/base/oauth_token_getter_impl.h" |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 24 | #include "remoting/signaling/ftl_client.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 25 | #include "remoting/test/test_oauth_token_factory.h" |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 26 | #include "remoting/test/test_token_storage.h" |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 27 | |
| 28 | namespace { |
| 29 | |
| 30 | constexpr char kSwitchNameHelp[] = "help"; |
| 31 | constexpr char kSwitchNameAuthCode[] = "code"; |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 32 | constexpr char kSwitchNameUsername[] = "username"; |
| 33 | constexpr char kSwitchNameStoragePath[] = "storage-path"; |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 34 | |
| 35 | // Reads a newline-terminated string from stdin. |
| 36 | std::string ReadString() { |
| 37 | const int kMaxLen = 1024; |
| 38 | std::string str(kMaxLen, 0); |
| 39 | char* result = fgets(&str[0], kMaxLen, stdin); |
| 40 | if (!result) |
| 41 | return std::string(); |
| 42 | size_t newline_index = str.find('\n'); |
| 43 | if (newline_index != std::string::npos) |
| 44 | str[newline_index] = '\0'; |
| 45 | str.resize(strlen(&str[0])); |
| 46 | return str; |
| 47 | } |
| 48 | |
| 49 | // Read the value of |switch_name| from command line if it exists, otherwise |
| 50 | // read from stdin. |
| 51 | std::string ReadStringFromCommandLineOrStdin(const std::string& switch_name, |
| 52 | const std::string& read_prompt) { |
| 53 | base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| 54 | if (command_line->HasSwitch(switch_name)) { |
| 55 | return command_line->GetSwitchValueASCII(switch_name); |
| 56 | } |
| 57 | printf("%s", read_prompt.c_str()); |
| 58 | return ReadString(); |
| 59 | } |
| 60 | |
| 61 | } // namespace |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 62 | |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 63 | namespace remoting { |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 64 | |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 65 | FtlSignalingPlayground::FtlSignalingPlayground() : weak_factory_(this) {} |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 66 | |
| 67 | FtlSignalingPlayground::~FtlSignalingPlayground() = default; |
| 68 | |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 69 | bool FtlSignalingPlayground::ShouldPrintHelp() { |
| 70 | return base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchNameHelp); |
| 71 | } |
| 72 | |
| 73 | void FtlSignalingPlayground::PrintHelp() { |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 74 | printf( |
| 75 | "Usage: %s [--code=<auth-code>] [--storage-path=<storage-path>] " |
| 76 | "[--username=<[email protected]>]\n", |
| 77 | base::CommandLine::ForCurrentProcess() |
| 78 | ->GetProgram() |
| 79 | .MaybeAsASCII() |
| 80 | .c_str()); |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | void FtlSignalingPlayground::StartAndAuthenticate() { |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 84 | DCHECK(!storage_); |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 85 | DCHECK(!token_getter_factory_); |
| 86 | DCHECK(!token_getter_); |
| 87 | DCHECK(!client_); |
| 88 | |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 89 | token_getter_factory_ = std::make_unique<TestOAuthTokenGetterFactory>(); |
| 90 | |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 91 | base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| 92 | std::string username = cmd_line->GetSwitchValueASCII(kSwitchNameUsername); |
| 93 | base::FilePath storage_path = |
| 94 | cmd_line->GetSwitchValuePath(kSwitchNameStoragePath); |
| 95 | storage_ = test::TestTokenStorage::OnDisk(username, storage_path); |
| 96 | |
| 97 | std::string access_token = storage_->FetchAccessToken(); |
| 98 | if (access_token.empty()) { |
| 99 | AuthenticateAndResetClient(); |
| 100 | } else { |
| 101 | VLOG(0) << "Reusing access token: " << access_token; |
| 102 | token_getter_ = std::make_unique<FakeOAuthTokenGetter>( |
| 103 | OAuthTokenGetter::Status::SUCCESS, "[email protected]", |
| 104 | access_token); |
| 105 | client_ = std::make_unique<FtlClient>(token_getter_.get()); |
| 106 | } |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 107 | |
| 108 | StartLoop(); |
| 109 | } |
| 110 | |
| 111 | void FtlSignalingPlayground::StartLoop() { |
| 112 | while (true) { |
| 113 | printf( |
| 114 | "\nOptions:\n" |
| 115 | " 1. GetIceServer\n" |
| 116 | " 2. SignInGaia\n" |
Yuwei Huang | 98655b9 | 2019-03-04 23:41:41 | [diff] [blame^] | 117 | " 3. PullMessages\n" |
| 118 | " 4. Quit\n\n" |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 119 | "Your choice [number]: "); |
| 120 | int choice = 0; |
| 121 | base::StringToInt(ReadString(), &choice); |
| 122 | base::RunLoop run_loop; |
| 123 | switch (choice) { |
| 124 | case 1: |
| 125 | GetIceServer(run_loop.QuitClosure()); |
| 126 | break; |
| 127 | case 2: |
| 128 | SignInGaia(run_loop.QuitClosure()); |
| 129 | break; |
| 130 | case 3: |
Yuwei Huang | 98655b9 | 2019-03-04 23:41:41 | [diff] [blame^] | 131 | PullMessages(run_loop.QuitClosure()); |
| 132 | break; |
| 133 | case 4: |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 134 | return; |
| 135 | default: |
| 136 | fprintf(stderr, "Unknown option\n"); |
| 137 | continue; |
| 138 | } |
| 139 | run_loop.Run(); |
| 140 | } |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 141 | } |
| 142 | |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 143 | void FtlSignalingPlayground::AuthenticateAndResetClient() { |
| 144 | static const std::string read_auth_code_prompt = base::StringPrintf( |
| 145 | "Please authenticate at:\n\n" |
| 146 | " %s\n\n" |
| 147 | "Enter the auth code: ", |
| 148 | TestOAuthTokenGetterFactory::GetAuthorizationCodeUri().c_str()); |
| 149 | std::string auth_code = ReadStringFromCommandLineOrStdin( |
| 150 | kSwitchNameAuthCode, read_auth_code_prompt); |
| 151 | |
| 152 | // Make sure we don't try to reuse an auth code. |
| 153 | base::CommandLine::ForCurrentProcess()->RemoveSwitch(kSwitchNameAuthCode); |
| 154 | |
| 155 | // We can't get back the refresh token since we have first-party scope, so |
| 156 | // we are not trying to store it. |
| 157 | token_getter_ = token_getter_factory_->CreateFromIntermediateCredentials( |
| 158 | auth_code, |
| 159 | base::DoNothing::Repeatedly<const std::string&, const std::string&>()); |
| 160 | |
| 161 | // Get the access token so that we can reuse it for next time. |
| 162 | base::OnceClosure on_access_token_done = base::DoNothing::Once(); |
| 163 | base::RunLoop run_loop; |
| 164 | if (!base::RunLoop::IsRunningOnCurrentThread()) { |
| 165 | on_access_token_done = run_loop.QuitClosure(); |
| 166 | } |
| 167 | token_getter_->CallWithToken(base::BindOnce( |
| 168 | &FtlSignalingPlayground::OnAccessToken, weak_factory_.GetWeakPtr(), |
| 169 | std::move(on_access_token_done))); |
| 170 | if (!base::RunLoop::IsRunningOnCurrentThread()) { |
| 171 | run_loop.Run(); |
| 172 | } |
| 173 | |
| 174 | client_ = std::make_unique<FtlClient>(token_getter_.get()); |
| 175 | } |
| 176 | |
| 177 | void FtlSignalingPlayground::OnAccessToken(base::OnceClosure on_done, |
| 178 | OAuthTokenGetter::Status status, |
| 179 | const std::string& user_email, |
| 180 | const std::string& access_token) { |
| 181 | DCHECK(status == OAuthTokenGetter::Status::SUCCESS); |
| 182 | VLOG(0) << "Received access_token: " << access_token; |
| 183 | storage_->StoreAccessToken(access_token); |
| 184 | std::move(on_done).Run(); |
| 185 | } |
| 186 | |
| 187 | void FtlSignalingPlayground::HandleGrpcStatusError(const grpc::Status& status) { |
| 188 | DCHECK(!status.ok()); |
| 189 | LOG(ERROR) << "RPC failed. Code=" << status.error_code() << ", " |
| 190 | << "Message=" << status.error_message(); |
| 191 | switch (status.error_code()) { |
| 192 | case grpc::StatusCode::UNAVAILABLE: |
| 193 | VLOG(0) |
| 194 | << "Set the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable " |
| 195 | << "to third_party/grpc/src/etc/roots.pem if gRPC cannot locate the " |
| 196 | << "root certificates."; |
| 197 | break; |
| 198 | case grpc::StatusCode::UNAUTHENTICATED: |
| 199 | VLOG(0) << "Grpc request failed to authenticate. " |
| 200 | << "Trying to reauthenticate..."; |
| 201 | AuthenticateAndResetClient(); |
| 202 | break; |
| 203 | default: |
| 204 | break; |
| 205 | } |
| 206 | } |
| 207 | |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 208 | void FtlSignalingPlayground::GetIceServer(base::OnceClosure on_done) { |
Yuwei Huang | bdc4a97 | 2019-03-01 17:43:06 | [diff] [blame] | 209 | DCHECK(client_); |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 210 | client_->GetIceServer( |
| 211 | base::BindOnce(&FtlSignalingPlayground::OnGetIceServerResponse, |
| 212 | weak_factory_.GetWeakPtr(), std::move(on_done))); |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 213 | VLOG(0) << "Running GetIceServer..."; |
| 214 | } |
| 215 | |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 216 | void FtlSignalingPlayground::OnGetIceServerResponse( |
| 217 | base::OnceClosure on_done, |
| 218 | grpc::Status status, |
| 219 | const ftl::GetICEServerResponse& response) { |
| 220 | if (status.ok()) { |
Yuwei Huang | 30bac9e | 2019-03-01 21:28:14 | [diff] [blame] | 221 | printf("Ice transport policy: %s\n", |
| 222 | response.ice_config().ice_transport_policy().c_str()); |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 223 | for (const ftl::ICEServerList& server : |
| 224 | response.ice_config().ice_servers()) { |
Yuwei Huang | 30bac9e | 2019-03-01 21:28:14 | [diff] [blame] | 225 | printf( |
| 226 | "ICE server:\n" |
| 227 | " hostname=%s\n" |
| 228 | " username=%s\n" |
| 229 | " credential=%s\n" |
| 230 | " max_rate_kbps=%" PRId64 "\n", |
| 231 | server.hostname().c_str(), server.username().c_str(), |
| 232 | server.credential().c_str(), server.max_rate_kbps()); |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 233 | for (const std::string& url : server.urls()) { |
Yuwei Huang | 30bac9e | 2019-03-01 21:28:14 | [diff] [blame] | 234 | printf(" url=%s\n", url.c_str()); |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 235 | } |
| 236 | } |
| 237 | } else { |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 238 | HandleGrpcStatusError(status); |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 239 | } |
| 240 | std::move(on_done).Run(); |
| 241 | } |
| 242 | |
| 243 | void FtlSignalingPlayground::SignInGaia(base::OnceClosure on_done) { |
| 244 | DCHECK(client_); |
| 245 | VLOG(0) << "Running SignInGaia..."; |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 246 | // TODO(yuweih): Logic should be cleaned up and moved out of the playground. |
| 247 | std::string device_id = storage_->FetchDeviceId(); |
| 248 | if (device_id.empty()) { |
| 249 | device_id = "crd-web-" + base::GenerateGUID(); |
| 250 | VLOG(0) << "Generated new device_id: " << device_id; |
| 251 | storage_->StoreDeviceId(device_id); |
| 252 | } else { |
| 253 | VLOG(0) << "Read device_id: " << device_id; |
| 254 | } |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 255 | VLOG(0) << "Using sign_in_gaia_mode: DEFAULT_CREATE_ACCOUNT"; |
| 256 | client_->SignInGaia( |
| 257 | device_id, ftl::SignInGaiaMode_Value_DEFAULT_CREATE_ACCOUNT, |
| 258 | base::BindOnce(&FtlSignalingPlayground::OnSignInGaiaResponse, |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 259 | weak_factory_.GetWeakPtr(), std::move(on_done))); |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 260 | } |
| 261 | |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 262 | void FtlSignalingPlayground::OnSignInGaiaResponse( |
| 263 | base::OnceClosure on_done, |
| 264 | grpc::Status status, |
| 265 | const ftl::SignInGaiaResponse& response) { |
| 266 | if (status.ok()) { |
| 267 | // TODO(yuweih): Allow loading auth token directly from command line. |
| 268 | std::string registration_id_base64; |
| 269 | std::string auth_token_base64; |
| 270 | base::Base64Encode(response.registration_id(), ®istration_id_base64); |
| 271 | base::Base64Encode(response.auth_token().payload(), &auth_token_base64); |
| 272 | printf( |
| 273 | "registration_id(base64)=%s\n" |
| 274 | "auth_token.payload(base64)=%s\n" |
| 275 | "auth_token.expires_in=%" PRId64 "\n", |
| 276 | registration_id_base64.c_str(), auth_token_base64.c_str(), |
| 277 | response.auth_token().expires_in()); |
Yuwei Huang | 98655b9 | 2019-03-04 23:41:41 | [diff] [blame^] | 278 | client_->SetAuthToken(response.auth_token().payload()); |
| 279 | VLOG(0) << "Auth token set on FtlClient"; |
| 280 | } else { |
| 281 | HandleGrpcStatusError(status); |
| 282 | } |
| 283 | std::move(on_done).Run(); |
| 284 | } |
| 285 | |
| 286 | void FtlSignalingPlayground::PullMessages(base::OnceClosure on_done) { |
| 287 | DCHECK(client_); |
| 288 | VLOG(0) << "Running PullMessages..."; |
| 289 | client_->PullMessages( |
| 290 | base::BindOnce(&FtlSignalingPlayground::OnPullMessagesResponse, |
| 291 | weak_factory_.GetWeakPtr(), std::move(on_done))); |
| 292 | } |
| 293 | |
| 294 | void FtlSignalingPlayground::OnPullMessagesResponse( |
| 295 | base::OnceClosure on_done, |
| 296 | grpc::Status status, |
| 297 | const ftl::PullMessagesResponse& response) { |
| 298 | if (!status.ok()) { |
| 299 | if (status.error_code() == grpc::StatusCode::UNAUTHENTICATED) { |
| 300 | fprintf(stderr, "Please run SignInGaia first\n"); |
| 301 | } else { |
| 302 | HandleGrpcStatusError(status); |
| 303 | } |
| 304 | std::move(on_done).Run(); |
| 305 | return; |
| 306 | } |
| 307 | |
| 308 | std::vector<ftl::ReceiverMessage> receiver_messages; |
| 309 | printf("pull_all=%d\n", response.pulled_all()); |
| 310 | for (const auto& message : response.messages()) { |
| 311 | printf( |
| 312 | "Message:\n" |
| 313 | " message_type=%d\n" |
| 314 | " message_id=%s\n" |
| 315 | " sender_id.id=%s\n" |
| 316 | " receiver_id.id=%s\n", |
| 317 | message.message_type(), message.message_id().c_str(), |
| 318 | message.sender_id().id().c_str(), message.receiver_id().id().c_str()); |
| 319 | |
| 320 | if (message.message_type() == |
| 321 | ftl::InboxMessage_MessageType_CHROMOTING_MESSAGE) { |
| 322 | ftl::ChromotingMessage chromoting_message; |
| 323 | chromoting_message.ParseFromString(message.message()); |
| 324 | printf(" message(ChromotingMessage deserialized)=%s\n", |
| 325 | chromoting_message.message().c_str()); |
| 326 | } else { |
| 327 | std::string message_base64; |
| 328 | base::Base64Encode(message.message(), &message_base64); |
| 329 | printf(" message(base64)=%s\n", message_base64.c_str()); |
| 330 | } |
| 331 | |
| 332 | ftl::ReceiverMessage receiver_message; |
| 333 | receiver_message.set_message_id(message.message_id()); |
| 334 | receiver_message.set_allocated_receiver_id( |
| 335 | new ftl::Id(message.receiver_id())); |
| 336 | receiver_messages.push_back(std::move(receiver_message)); |
| 337 | } |
| 338 | |
| 339 | if (receiver_messages.empty()) { |
| 340 | VLOG(0) << "No message has been received"; |
| 341 | std::move(on_done).Run(); |
| 342 | return; |
| 343 | } |
| 344 | |
| 345 | // TODO(yuweih): Might need retry logic. |
| 346 | VLOG(0) << "Acking " << receiver_messages.size() << " messages"; |
| 347 | client_->AckMessages( |
| 348 | receiver_messages, |
| 349 | base::BindOnce(&FtlSignalingPlayground::OnAckMessagesResponse, |
| 350 | weak_factory_.GetWeakPtr(), std::move(on_done))); |
| 351 | } |
| 352 | |
| 353 | void FtlSignalingPlayground::OnAckMessagesResponse( |
| 354 | base::OnceClosure on_done, |
| 355 | grpc::Status status, |
| 356 | const ftl::AckMessagesResponse& response) { |
| 357 | if (status.ok()) { |
| 358 | VLOG(0) << "Messages acked"; |
Yuwei Huang | aae881f | 2019-03-01 23:10:27 | [diff] [blame] | 359 | } else { |
Yuwei Huang | 946cebd4 | 2019-03-04 22:14:20 | [diff] [blame] | 360 | HandleGrpcStatusError(status); |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 361 | } |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 362 | std::move(on_done).Run(); |
| 363 | } |
Yuwei Huang | 5a84f953 | 2019-02-20 18:29:01 | [diff] [blame] | 364 | |
Yuwei Huang | 575c32ee | 2019-02-27 04:41:56 | [diff] [blame] | 365 | } // namespace remoting |