chromoting_host.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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 "remoting/host/chromoting_host.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/logging.h" 10#include "base/message_loop_proxy.h" 11#include "build/build_config.h" 12#include "remoting/base/constants.h" 13#include "remoting/host/chromoting_host_context.h" 14#include "remoting/host/desktop_environment_factory.h" 15#include "remoting/host/event_executor.h" 16#include "remoting/host/host_config.h" 17#include "remoting/protocol/connection_to_client.h" 18#include "remoting/protocol/client_stub.h" 19#include "remoting/protocol/host_stub.h" 20#include "remoting/protocol/input_stub.h" 21#include "remoting/protocol/session_config.h" 22 23using remoting::protocol::ConnectionToClient; 24using remoting::protocol::InputStub; 25 26namespace remoting { 27 28namespace { 29 30const net::BackoffEntry::Policy kDefaultBackoffPolicy = { 31 // Number of initial errors (in sequence) to ignore before applying 32 // exponential back-off rules. 33 0, 34 35 // Initial delay for exponential back-off in ms. 36 2000, 37 38 // Factor by which the waiting time will be multiplied. 39 2, 40 41 // Fuzzing percentage. ex: 10% will spread requests randomly 42 // between 90%-100% of the calculated time. 43 0, 44 45 // Maximum amount of time we are willing to delay our request in ms. 46 -1, 47 48 // Time to keep an entry from being discarded even when it 49 // has no significant state, -1 to never discard. 50 -1, 51 52 // Don't use initial delay unless the last request was an error. 53 false, 54}; 55 56} // namespace 57 58ChromotingHost::ChromotingHost( 59 SignalStrategy* signal_strategy, 60 DesktopEnvironmentFactory* desktop_environment_factory, 61 scoped_ptr<protocol::SessionManager> session_manager, 62 scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, 63 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, 64 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, 65 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner) 66 : desktop_environment_factory_(desktop_environment_factory), 67 session_manager_(session_manager.Pass()), 68 audio_task_runner_(audio_task_runner), 69 capture_task_runner_(capture_task_runner), 70 encode_task_runner_(encode_task_runner), 71 network_task_runner_(network_task_runner), 72 signal_strategy_(signal_strategy), 73 clients_count_(0), 74 state_(kInitial), 75 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), 76 login_backoff_(&kDefaultBackoffPolicy), 77 authenticating_client_(false), 78 reject_authenticating_client_(false) { 79 DCHECK(signal_strategy); 80 DCHECK(network_task_runner_->BelongsToCurrentThread()); 81 82 if (!desktop_environment_factory_->SupportsAudioCapture()) { 83 protocol::CandidateSessionConfig::DisableAudioChannel( 84 protocol_config_.get()); 85 } 86} 87 88ChromotingHost::~ChromotingHost() { 89 DCHECK(clients_.empty()); 90} 91 92void ChromotingHost::Start(const std::string& xmpp_login) { 93 DCHECK(network_task_runner_->BelongsToCurrentThread()); 94 95 LOG(INFO) << "Starting host"; 96 97 // Make sure this object is not started. 98 if (state_ != kInitial) 99 return; 100 state_ = kStarted; 101 102 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 103 OnStart(xmpp_login)); 104 105 // Start the SessionManager, supplying this ChromotingHost as the listener. 106 session_manager_->Init(signal_strategy_, this); 107} 108 109// This method is called when we need to destroy the host process. 110void ChromotingHost::Shutdown(const base::Closure& shutdown_task) { 111 if (!network_task_runner_->BelongsToCurrentThread()) { 112 network_task_runner_->PostTask( 113 FROM_HERE, base::Bind(&ChromotingHost::Shutdown, this, shutdown_task)); 114 return; 115 } 116 117 switch (state_) { 118 case kInitial: 119 case kStopped: 120 // Nothing to do if we are not started. 121 state_ = kStopped; 122 if (!shutdown_task.is_null()) 123 network_task_runner_->PostTask(FROM_HERE, shutdown_task); 124 break; 125 126 case kStopping: 127 // We are already stopping. Just save the task. 128 if (!shutdown_task.is_null()) 129 shutdown_tasks_.push_back(shutdown_task); 130 break; 131 132 case kStarted: 133 if (!shutdown_task.is_null()) 134 shutdown_tasks_.push_back(shutdown_task); 135 state_ = kStopping; 136 137 // Disconnect all of the clients. 138 while (!clients_.empty()) { 139 clients_.front()->Disconnect(); 140 } 141 142 // Run the remaining shutdown tasks. 143 if (state_ == kStopping && !clients_count_) 144 ShutdownFinish(); 145 146 break; 147 } 148} 149 150void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { 151 DCHECK(network_task_runner_->BelongsToCurrentThread()); 152 status_observers_.AddObserver(observer); 153} 154 155void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { 156 DCHECK(network_task_runner_->BelongsToCurrentThread()); 157 status_observers_.RemoveObserver(observer); 158} 159 160void ChromotingHost::RejectAuthenticatingClient() { 161 DCHECK(authenticating_client_); 162 reject_authenticating_client_ = true; 163} 164 165void ChromotingHost::SetAuthenticatorFactory( 166 scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) { 167 DCHECK(network_task_runner_->BelongsToCurrentThread()); 168 session_manager_->set_authenticator_factory(authenticator_factory.Pass()); 169} 170 171void ChromotingHost::SetMaximumSessionDuration( 172 const base::TimeDelta& max_session_duration) { 173 max_session_duration_ = max_session_duration; 174} 175 176//////////////////////////////////////////////////////////////////////////// 177// protocol::ClientSession::EventHandler implementation. 178void ChromotingHost::OnSessionAuthenticated(ClientSession* client) { 179 DCHECK(network_task_runner_->BelongsToCurrentThread()); 180 181 login_backoff_.Reset(); 182 183 // Disconnect all other clients. 184 // Iterate over a copy of the list of clients, to avoid mutating the list 185 // while iterating over it. 186 ClientList clients_copy(clients_); 187 for (ClientList::const_iterator other_client = clients_copy.begin(); 188 other_client != clients_copy.end(); ++other_client) { 189 if (other_client->get() != client) { 190 (*other_client)->Disconnect(); 191 } 192 } 193 194 // Disconnects above must have destroyed all other clients and |recorder_|. 195 DCHECK_EQ(clients_.size(), 1U); 196 197 // Notify observers that there is at least one authenticated client. 198 const std::string& jid = client->client_jid(); 199 200 reject_authenticating_client_ = false; 201 202 authenticating_client_ = true; 203 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 204 OnClientAuthenticated(jid)); 205 authenticating_client_ = false; 206 207 if (reject_authenticating_client_) { 208 client->Disconnect(); 209 } 210} 211 212void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { 213 DCHECK(network_task_runner_->BelongsToCurrentThread()); 214 215 // Notify observers. 216 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 217 OnClientConnected(client->client_jid())); 218} 219 220void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { 221 DCHECK(network_task_runner_->BelongsToCurrentThread()); 222 223 // Notify observers. 224 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 225 OnAccessDenied(client->client_jid())); 226} 227 228void ChromotingHost::OnSessionClosed(ClientSession* client) { 229 DCHECK(network_task_runner_->BelongsToCurrentThread()); 230 231 ClientList::iterator it = clients_.begin(); 232 for (; it != clients_.end(); ++it) { 233 if (it->get() == client) { 234 break; 235 } 236 } 237 CHECK(it != clients_.end()); 238 239 if (client->is_authenticated()) { 240 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 241 OnClientDisconnected(client->client_jid())); 242 } 243 244 client->Stop(base::Bind(&ChromotingHost::OnClientStopped, this)); 245 clients_.erase(it); 246} 247 248void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, 249 int64 sequence_number) { 250 DCHECK(network_task_runner_->BelongsToCurrentThread()); 251} 252 253void ChromotingHost::OnSessionRouteChange( 254 ClientSession* session, 255 const std::string& channel_name, 256 const protocol::TransportRoute& route) { 257 DCHECK(network_task_runner_->BelongsToCurrentThread()); 258 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 259 OnClientRouteChange(session->client_jid(), channel_name, 260 route)); 261} 262 263void ChromotingHost::OnClientDimensionsChanged(ClientSession* session, 264 const SkISize& size) { 265 DCHECK(network_task_runner_->BelongsToCurrentThread()); 266 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 267 OnClientDimensionsChanged(session->client_jid(), size)); 268} 269 270void ChromotingHost::OnSessionManagerReady() { 271 DCHECK(network_task_runner_->BelongsToCurrentThread()); 272 // Don't need to do anything here, just wait for incoming 273 // connections. 274} 275 276void ChromotingHost::OnIncomingSession( 277 protocol::Session* session, 278 protocol::SessionManager::IncomingSessionResponse* response) { 279 DCHECK(network_task_runner_->BelongsToCurrentThread()); 280 281 if (state_ != kStarted) { 282 *response = protocol::SessionManager::DECLINE; 283 return; 284 } 285 286 if (login_backoff_.ShouldRejectRequest()) { 287 *response = protocol::SessionManager::OVERLOAD; 288 return; 289 } 290 291 // We treat each incoming connection as a failure to authenticate, 292 // and clear the backoff when a connection successfully 293 // authenticates. This allows the backoff to protect from parallel 294 // connection attempts as well as sequential ones. 295 login_backoff_.InformOfRequest(false); 296 297 protocol::SessionConfig config; 298 if (!protocol_config_->Select(session->candidate_config(), &config)) { 299 LOG(WARNING) << "Rejecting connection from " << session->jid() 300 << " because no compatible configuration has been found."; 301 *response = protocol::SessionManager::INCOMPATIBLE; 302 return; 303 } 304 305 session->set_config(config); 306 307 *response = protocol::SessionManager::ACCEPT; 308 309 LOG(INFO) << "Client connected: " << session->jid(); 310 311 // Create a client object. 312 scoped_ptr<protocol::ConnectionToClient> connection( 313 new protocol::ConnectionToClient(session)); 314 scoped_refptr<ClientSession> client = new ClientSession( 315 this, 316 audio_task_runner_, 317 capture_task_runner_, 318 encode_task_runner_, 319 network_task_runner_, 320 connection.Pass(), 321 desktop_environment_factory_, 322 max_session_duration_); 323 clients_.push_back(client); 324 clients_count_++; 325} 326 327void ChromotingHost::set_protocol_config( 328 scoped_ptr<protocol::CandidateSessionConfig> config) { 329 DCHECK(network_task_runner_->BelongsToCurrentThread()); 330 DCHECK(config.get()); 331 DCHECK_EQ(state_, kInitial); 332 protocol_config_ = config.Pass(); 333} 334 335void ChromotingHost::OnLocalMouseMoved(const SkIPoint& new_pos) { 336 if (!network_task_runner_->BelongsToCurrentThread()) { 337 network_task_runner_->PostTask( 338 FROM_HERE, base::Bind(&ChromotingHost::OnLocalMouseMoved, 339 this, new_pos)); 340 return; 341 } 342 343 ClientList::iterator client; 344 for (client = clients_.begin(); client != clients_.end(); ++client) { 345 (*client)->LocalMouseMoved(new_pos); 346 } 347} 348 349void ChromotingHost::PauseSession(bool pause) { 350 if (!network_task_runner_->BelongsToCurrentThread()) { 351 network_task_runner_->PostTask( 352 FROM_HERE, base::Bind(&ChromotingHost::PauseSession, this, pause)); 353 return; 354 } 355 356 ClientList::iterator client; 357 for (client = clients_.begin(); client != clients_.end(); ++client) { 358 (*client)->SetDisableInputs(pause); 359 } 360} 361 362void ChromotingHost::DisconnectAllClients() { 363 if (!network_task_runner_->BelongsToCurrentThread()) { 364 network_task_runner_->PostTask( 365 FROM_HERE, base::Bind(&ChromotingHost::DisconnectAllClients, this)); 366 return; 367 } 368 369 while (!clients_.empty()) { 370 size_t size = clients_.size(); 371 clients_.front()->Disconnect(); 372 CHECK_EQ(clients_.size(), size - 1); 373 } 374} 375 376void ChromotingHost::DisconnectClient(DesktopEnvironment* desktop_environment) { 377 DCHECK(network_task_runner_->BelongsToCurrentThread()); 378 379 for (ClientList::iterator i = clients_.begin(); i != clients_.end(); ++i) { 380 if ((*i)->desktop_environment() == desktop_environment) { 381 (*i)->Disconnect(); 382 break; 383 } 384 } 385} 386 387void ChromotingHost::SetUiStrings(const UiStrings& ui_strings) { 388 DCHECK(network_task_runner_->BelongsToCurrentThread()); 389 DCHECK_EQ(state_, kInitial); 390 391 ui_strings_ = ui_strings; 392} 393 394void ChromotingHost::OnClientStopped() { 395 DCHECK(network_task_runner_->BelongsToCurrentThread()); 396 397 --clients_count_; 398 if (state_ == kStopping && !clients_count_) 399 ShutdownFinish(); 400} 401 402void ChromotingHost::ShutdownFinish() { 403 DCHECK(network_task_runner_->BelongsToCurrentThread()); 404 DCHECK_EQ(state_, kStopping); 405 406 state_ = kStopped; 407 408 // Destroy session manager. 409 session_manager_.reset(); 410 411 // Clear |desktop_environment_factory_| and |signal_strategy_| to 412 // ensure we don't try to touch them after running shutdown tasks 413 desktop_environment_factory_ = NULL; 414 signal_strategy_ = NULL; 415 416 // Keep reference to |this|, so that we don't get destroyed while 417 // sending notifications. 418 scoped_refptr<ChromotingHost> self(this); 419 420 // Notify observers. 421 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 422 OnShutdown()); 423 424 for (std::vector<base::Closure>::iterator it = shutdown_tasks_.begin(); 425 it != shutdown_tasks_.end(); ++it) { 426 it->Run(); 427 } 428 shutdown_tasks_.clear(); 429} 430 431} // namespace remoting 432