root/remoting/host/chromoting_host.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_factory_
  2. Start
  3. AddStatusObserver
  4. RemoveStatusObserver
  5. RejectAuthenticatingClient
  6. SetAuthenticatorFactory
  7. SetEnableCurtaining
  8. SetMaximumSessionDuration
  9. OnSessionAuthenticated
  10. OnSessionChannelsConnected
  11. OnSessionAuthenticationFailed
  12. OnSessionClosed
  13. OnSessionSequenceNumber
  14. OnSessionRouteChange
  15. OnSessionManagerReady
  16. OnIncomingSession
  17. set_protocol_config
  18. DisconnectAllClients

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/chromoting_host.h"

#include <algorithm>

#include "base/bind.h"
#include "base/callback.h"
#include "base/message_loop/message_loop_proxy.h"
#include "build/build_config.h"
#include "remoting/base/constants.h"
#include "remoting/base/logging.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/host_config.h"
#include "remoting/host/input_injector.h"
#include "remoting/protocol/connection_to_client.h"
#include "remoting/protocol/client_stub.h"
#include "remoting/protocol/host_stub.h"
#include "remoting/protocol/input_stub.h"
#include "remoting/protocol/session_config.h"

using remoting::protocol::ConnectionToClient;
using remoting::protocol::InputStub;

namespace remoting {

namespace {

const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
  // Number of initial errors (in sequence) to ignore before applying
  // exponential back-off rules.
  0,

  // Initial delay for exponential back-off in ms.
  2000,

  // Factor by which the waiting time will be multiplied.
  2,

  // Fuzzing percentage. ex: 10% will spread requests randomly
  // between 90%-100% of the calculated time.
  0,

  // Maximum amount of time we are willing to delay our request in ms.
  -1,

  // Time to keep an entry from being discarded even when it
  // has no significant state, -1 to never discard.
  -1,

  // Don't use initial delay unless the last request was an error.
  false,
};

}  // namespace

ChromotingHost::ChromotingHost(
    SignalStrategy* signal_strategy,
    DesktopEnvironmentFactory* desktop_environment_factory,
    scoped_ptr<protocol::SessionManager> session_manager,
    scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
    : desktop_environment_factory_(desktop_environment_factory),
      session_manager_(session_manager.Pass()),
      audio_task_runner_(audio_task_runner),
      input_task_runner_(input_task_runner),
      video_capture_task_runner_(video_capture_task_runner),
      video_encode_task_runner_(video_encode_task_runner),
      network_task_runner_(network_task_runner),
      ui_task_runner_(ui_task_runner),
      signal_strategy_(signal_strategy),
      started_(false),
      protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
      login_backoff_(&kDefaultBackoffPolicy),
      authenticating_client_(false),
      reject_authenticating_client_(false),
      enable_curtaining_(false),
      weak_factory_(this) {
  DCHECK(network_task_runner_->BelongsToCurrentThread());
  DCHECK(signal_strategy);

  // VP9 encode is not yet supported.
  protocol::CandidateSessionConfig::DisableVideoCodec(
      protocol_config_.get(), protocol::ChannelConfig::CODEC_VP9);

  if (!desktop_environment_factory_->SupportsAudioCapture()) {
    protocol::CandidateSessionConfig::DisableAudioChannel(
        protocol_config_.get());
  }
}

ChromotingHost::~ChromotingHost() {
  DCHECK(CalledOnValidThread());

  // Disconnect all of the clients.
  while (!clients_.empty()) {
    clients_.front()->DisconnectSession();
  }

  // Destroy the session manager to make sure that |signal_strategy_| does not
  // have any listeners registered.
  session_manager_.reset();

  // Notify observers.
  if (started_)
    FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown());
}

void ChromotingHost::Start(const std::string& host_owner) {
  DCHECK(CalledOnValidThread());
  DCHECK(!started_);

  HOST_LOG << "Starting host";
  started_ = true;
  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(host_owner));

  // Start the SessionManager, supplying this ChromotingHost as the listener.
  session_manager_->Init(signal_strategy_, this);
}

void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
  DCHECK(CalledOnValidThread());
  status_observers_.AddObserver(observer);
}

void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
  DCHECK(CalledOnValidThread());
  status_observers_.RemoveObserver(observer);
}

void ChromotingHost::RejectAuthenticatingClient() {
  DCHECK(authenticating_client_);
  reject_authenticating_client_ = true;
}

void ChromotingHost::SetAuthenticatorFactory(
    scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) {
  DCHECK(CalledOnValidThread());
  session_manager_->set_authenticator_factory(authenticator_factory.Pass());
}

void ChromotingHost::SetEnableCurtaining(bool enable) {
  DCHECK(network_task_runner_->BelongsToCurrentThread());

  if (enable_curtaining_ == enable)
    return;

  enable_curtaining_ = enable;
  desktop_environment_factory_->SetEnableCurtaining(enable_curtaining_);

  // Disconnect all existing clients because they might be running not
  // curtained.
  // TODO(alexeypa): fix this such that the curtain is applied to the not
  // curtained sessions or disconnect only the client connected to not
  // curtained sessions.
  if (enable_curtaining_)
    DisconnectAllClients();
}

void ChromotingHost::SetMaximumSessionDuration(
    const base::TimeDelta& max_session_duration) {
  max_session_duration_ = max_session_duration;
}

////////////////////////////////////////////////////////////////////////////
// protocol::ClientSession::EventHandler implementation.
bool ChromotingHost::OnSessionAuthenticated(ClientSession* client) {
  DCHECK(CalledOnValidThread());

  login_backoff_.Reset();

  // Disconnect all other clients. |it| should be advanced before Disconnect()
  // is called to avoid it becoming invalid when the client is removed from
  // the list.
  ClientList::iterator it = clients_.begin();
  while (it != clients_.end()) {
    ClientSession* other_client = *it++;
    if (other_client != client)
      other_client->DisconnectSession();
  }

  // Disconnects above must have destroyed all other clients.
  DCHECK_EQ(clients_.size(), 1U);

  // Notify observers that there is at least one authenticated client.
  const std::string& jid = client->client_jid();

  reject_authenticating_client_ = false;

  authenticating_client_ = true;
  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
                    OnClientAuthenticated(jid));
  authenticating_client_ = false;

  return !reject_authenticating_client_;
}

void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) {
  DCHECK(CalledOnValidThread());

  // Notify observers.
  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
                    OnClientConnected(client->client_jid()));
}

void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) {
  DCHECK(CalledOnValidThread());

  // Notify observers.
  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
                    OnAccessDenied(client->client_jid()));
}

void ChromotingHost::OnSessionClosed(ClientSession* client) {
  DCHECK(CalledOnValidThread());

  ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client);
  CHECK(it != clients_.end());

  if (client->is_authenticated()) {
    FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
                      OnClientDisconnected(client->client_jid()));
  }

  clients_.erase(it);
  delete client;
}

void ChromotingHost::OnSessionSequenceNumber(ClientSession* session,
                                             int64 sequence_number) {
  DCHECK(CalledOnValidThread());
}

void ChromotingHost::OnSessionRouteChange(
    ClientSession* session,
    const std::string& channel_name,
    const protocol::TransportRoute& route) {
  DCHECK(CalledOnValidThread());
  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
                    OnClientRouteChange(session->client_jid(), channel_name,
                                        route));
}

void ChromotingHost::OnSessionManagerReady() {
  DCHECK(CalledOnValidThread());
  // Don't need to do anything here, just wait for incoming
  // connections.
}

void ChromotingHost::OnIncomingSession(
      protocol::Session* session,
      protocol::SessionManager::IncomingSessionResponse* response) {
  DCHECK(CalledOnValidThread());

  if (!started_) {
    *response = protocol::SessionManager::DECLINE;
    return;
  }

  if (login_backoff_.ShouldRejectRequest()) {
    *response = protocol::SessionManager::OVERLOAD;
    return;
  }

  // We treat each incoming connection as a failure to authenticate,
  // and clear the backoff when a connection successfully
  // authenticates. This allows the backoff to protect from parallel
  // connection attempts as well as sequential ones.
  login_backoff_.InformOfRequest(false);

  protocol::SessionConfig config;
  if (!protocol_config_->Select(session->candidate_config(), &config)) {
    LOG(WARNING) << "Rejecting connection from " << session->jid()
                 << " because no compatible configuration has been found.";
    *response = protocol::SessionManager::INCOMPATIBLE;
    return;
  }

  session->set_config(config);

  *response = protocol::SessionManager::ACCEPT;

  HOST_LOG << "Client connected: " << session->jid();

  // Create a client object.
  scoped_ptr<protocol::ConnectionToClient> connection(
      new protocol::ConnectionToClient(session));
  ClientSession* client = new ClientSession(
      this,
      audio_task_runner_,
      input_task_runner_,
      video_capture_task_runner_,
      video_encode_task_runner_,
      network_task_runner_,
      ui_task_runner_,
      connection.Pass(),
      desktop_environment_factory_,
      max_session_duration_,
      pairing_registry_);
  clients_.push_back(client);
}

void ChromotingHost::set_protocol_config(
    scoped_ptr<protocol::CandidateSessionConfig> config) {
  DCHECK(CalledOnValidThread());
  DCHECK(config.get());
  DCHECK(!started_);
  protocol_config_ = config.Pass();
}

void ChromotingHost::DisconnectAllClients() {
  DCHECK(CalledOnValidThread());

  while (!clients_.empty()) {
    size_t size = clients_.size();
    clients_.front()->DisconnectSession();
    CHECK_EQ(clients_.size(), size - 1);
  }
}

}  // namespace remoting

/* [<][>][^][v][top][bottom][index][help] */