chromoting_host.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 <algorithm> 8 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/logging.h" 12#include "base/message_loop/message_loop_proxy.h" 13#include "build/build_config.h" 14#include "remoting/base/constants.h" 15#include "remoting/host/chromoting_host_context.h" 16#include "remoting/host/desktop_environment.h" 17#include "remoting/host/host_config.h" 18#include "remoting/host/input_injector.h" 19#include "remoting/protocol/connection_to_client.h" 20#include "remoting/protocol/client_stub.h" 21#include "remoting/protocol/host_stub.h" 22#include "remoting/protocol/input_stub.h" 23#include "remoting/protocol/session_config.h" 24 25using remoting::protocol::ConnectionToClient; 26using remoting::protocol::InputStub; 27 28namespace remoting { 29 30namespace { 31 32const net::BackoffEntry::Policy kDefaultBackoffPolicy = { 33 // Number of initial errors (in sequence) to ignore before applying 34 // exponential back-off rules. 35 0, 36 37 // Initial delay for exponential back-off in ms. 38 2000, 39 40 // Factor by which the waiting time will be multiplied. 41 2, 42 43 // Fuzzing percentage. ex: 10% will spread requests randomly 44 // between 90%-100% of the calculated time. 45 0, 46 47 // Maximum amount of time we are willing to delay our request in ms. 48 -1, 49 50 // Time to keep an entry from being discarded even when it 51 // has no significant state, -1 to never discard. 52 -1, 53 54 // Don't use initial delay unless the last request was an error. 55 false, 56}; 57 58} // namespace 59 60ChromotingHost::ChromotingHost( 61 SignalStrategy* signal_strategy, 62 DesktopEnvironmentFactory* desktop_environment_factory, 63 scoped_ptr<protocol::SessionManager> session_manager, 64 scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, 65 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 66 scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner, 67 scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner, 68 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, 69 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) 70 : desktop_environment_factory_(desktop_environment_factory), 71 session_manager_(session_manager.Pass()), 72 audio_task_runner_(audio_task_runner), 73 input_task_runner_(input_task_runner), 74 video_capture_task_runner_(video_capture_task_runner), 75 video_encode_task_runner_(video_encode_task_runner), 76 network_task_runner_(network_task_runner), 77 ui_task_runner_(ui_task_runner), 78 signal_strategy_(signal_strategy), 79 started_(false), 80 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), 81 login_backoff_(&kDefaultBackoffPolicy), 82 authenticating_client_(false), 83 reject_authenticating_client_(false), 84 enable_curtaining_(false), 85 weak_factory_(this) { 86 DCHECK(network_task_runner_->BelongsToCurrentThread()); 87 DCHECK(signal_strategy); 88 89 if (!desktop_environment_factory_->SupportsAudioCapture()) { 90 protocol::CandidateSessionConfig::DisableAudioChannel( 91 protocol_config_.get()); 92 } 93} 94 95ChromotingHost::~ChromotingHost() { 96 DCHECK(CalledOnValidThread()); 97 98 // Disconnect all of the clients. 99 while (!clients_.empty()) { 100 clients_.front()->DisconnectSession(); 101 } 102 103 // Destroy the session manager to make sure that |signal_strategy_| does not 104 // have any listeners registered. 105 session_manager_.reset(); 106 107 // Notify observers. 108 if (started_) 109 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown()); 110} 111 112void ChromotingHost::Start(const std::string& xmpp_login) { 113 DCHECK(CalledOnValidThread()); 114 DCHECK(!started_); 115 116 LOG(INFO) << "Starting host"; 117 started_ = true; 118 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(xmpp_login)); 119 120 // Start the SessionManager, supplying this ChromotingHost as the listener. 121 session_manager_->Init(signal_strategy_, this); 122} 123 124void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { 125 DCHECK(CalledOnValidThread()); 126 status_observers_.AddObserver(observer); 127} 128 129void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { 130 DCHECK(CalledOnValidThread()); 131 status_observers_.RemoveObserver(observer); 132} 133 134void ChromotingHost::RejectAuthenticatingClient() { 135 DCHECK(authenticating_client_); 136 reject_authenticating_client_ = true; 137} 138 139void ChromotingHost::SetAuthenticatorFactory( 140 scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) { 141 DCHECK(CalledOnValidThread()); 142 session_manager_->set_authenticator_factory(authenticator_factory.Pass()); 143} 144 145void ChromotingHost::SetEnableCurtaining(bool enable) { 146 DCHECK(network_task_runner_->BelongsToCurrentThread()); 147 148 if (enable_curtaining_ == enable) 149 return; 150 151 enable_curtaining_ = enable; 152 desktop_environment_factory_->SetEnableCurtaining(enable_curtaining_); 153 154 // Disconnect all existing clients because they might be running not 155 // curtained. 156 // TODO(alexeypa): fix this such that the curtain is applied to the not 157 // curtained sessions or disconnect only the client connected to not 158 // curtained sessions. 159 if (enable_curtaining_) 160 DisconnectAllClients(); 161} 162 163void ChromotingHost::SetMaximumSessionDuration( 164 const base::TimeDelta& max_session_duration) { 165 max_session_duration_ = max_session_duration; 166} 167 168//////////////////////////////////////////////////////////////////////////// 169// protocol::ClientSession::EventHandler implementation. 170bool ChromotingHost::OnSessionAuthenticated(ClientSession* client) { 171 DCHECK(CalledOnValidThread()); 172 173 login_backoff_.Reset(); 174 175 // Disconnect all other clients. |it| should be advanced before Disconnect() 176 // is called to avoid it becoming invalid when the client is removed from 177 // the list. 178 ClientList::iterator it = clients_.begin(); 179 while (it != clients_.end()) { 180 ClientSession* other_client = *it++; 181 if (other_client != client) 182 other_client->DisconnectSession(); 183 } 184 185 // Disconnects above must have destroyed all other clients. 186 DCHECK_EQ(clients_.size(), 1U); 187 188 // Notify observers that there is at least one authenticated client. 189 const std::string& jid = client->client_jid(); 190 191 reject_authenticating_client_ = false; 192 193 authenticating_client_ = true; 194 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 195 OnClientAuthenticated(jid)); 196 authenticating_client_ = false; 197 198 return !reject_authenticating_client_; 199} 200 201void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { 202 DCHECK(CalledOnValidThread()); 203 204 // Notify observers. 205 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 206 OnClientConnected(client->client_jid())); 207} 208 209void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { 210 DCHECK(CalledOnValidThread()); 211 212 // Notify observers. 213 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 214 OnAccessDenied(client->client_jid())); 215} 216 217void ChromotingHost::OnSessionClosed(ClientSession* client) { 218 DCHECK(CalledOnValidThread()); 219 220 ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client); 221 CHECK(it != clients_.end()); 222 223 if (client->is_authenticated()) { 224 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 225 OnClientDisconnected(client->client_jid())); 226 } 227 228 clients_.erase(it); 229 delete client; 230} 231 232void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, 233 int64 sequence_number) { 234 DCHECK(CalledOnValidThread()); 235} 236 237void ChromotingHost::OnSessionRouteChange( 238 ClientSession* session, 239 const std::string& channel_name, 240 const protocol::TransportRoute& route) { 241 DCHECK(CalledOnValidThread()); 242 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 243 OnClientRouteChange(session->client_jid(), channel_name, 244 route)); 245} 246 247void ChromotingHost::OnSessionManagerReady() { 248 DCHECK(CalledOnValidThread()); 249 // Don't need to do anything here, just wait for incoming 250 // connections. 251} 252 253void ChromotingHost::OnIncomingSession( 254 protocol::Session* session, 255 protocol::SessionManager::IncomingSessionResponse* response) { 256 DCHECK(CalledOnValidThread()); 257 258 if (!started_) { 259 *response = protocol::SessionManager::DECLINE; 260 return; 261 } 262 263 if (login_backoff_.ShouldRejectRequest()) { 264 *response = protocol::SessionManager::OVERLOAD; 265 return; 266 } 267 268 // We treat each incoming connection as a failure to authenticate, 269 // and clear the backoff when a connection successfully 270 // authenticates. This allows the backoff to protect from parallel 271 // connection attempts as well as sequential ones. 272 login_backoff_.InformOfRequest(false); 273 274 protocol::SessionConfig config; 275 if (!protocol_config_->Select(session->candidate_config(), &config)) { 276 LOG(WARNING) << "Rejecting connection from " << session->jid() 277 << " because no compatible configuration has been found."; 278 *response = protocol::SessionManager::INCOMPATIBLE; 279 return; 280 } 281 282 session->set_config(config); 283 284 *response = protocol::SessionManager::ACCEPT; 285 286 LOG(INFO) << "Client connected: " << session->jid(); 287 288 // Create a client object. 289 scoped_ptr<protocol::ConnectionToClient> connection( 290 new protocol::ConnectionToClient(session)); 291 ClientSession* client = new ClientSession( 292 this, 293 audio_task_runner_, 294 input_task_runner_, 295 video_capture_task_runner_, 296 video_encode_task_runner_, 297 network_task_runner_, 298 ui_task_runner_, 299 connection.Pass(), 300 desktop_environment_factory_, 301 max_session_duration_, 302 pairing_registry_); 303 clients_.push_back(client); 304} 305 306void ChromotingHost::set_protocol_config( 307 scoped_ptr<protocol::CandidateSessionConfig> config) { 308 DCHECK(CalledOnValidThread()); 309 DCHECK(config.get()); 310 DCHECK(!started_); 311 protocol_config_ = config.Pass(); 312} 313 314void ChromotingHost::DisconnectAllClients() { 315 DCHECK(CalledOnValidThread()); 316 317 while (!clients_.empty()) { 318 size_t size = clients_.size(); 319 clients_.front()->DisconnectSession(); 320 CHECK_EQ(clients_.size(), size - 1); 321 } 322} 323 324} // namespace remoting 325