blob: 5eb438ae16a8cdd1a29c44e081dda49f224af57b [file] [log] [blame]
// Copyright (c) 2011 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/protocol/connection_to_host.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/message_loop_proxy.h"
#include "remoting/base/constants.h"
#include "remoting/jingle_glue/javascript_signal_strategy.h"
#include "remoting/jingle_glue/xmpp_signal_strategy.h"
#include "remoting/protocol/auth_token_utils.h"
#include "remoting/protocol/client_control_dispatcher.h"
#include "remoting/protocol/client_event_dispatcher.h"
#include "remoting/protocol/client_stub.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/pepper_session_manager.h"
#include "remoting/protocol/video_reader.h"
#include "remoting/protocol/video_stub.h"
#include "remoting/protocol/util.h"
namespace remoting {
namespace protocol {
ConnectionToHost::ConnectionToHost(
base::MessageLoopProxy* message_loop,
pp::Instance* pp_instance,
bool allow_nat_traversal)
: message_loop_(message_loop),
pp_instance_(pp_instance),
allow_nat_traversal_(allow_nat_traversal),
event_callback_(NULL),
client_stub_(NULL),
video_stub_(NULL),
state_(CONNECTING),
error_(OK),
control_connected_(false),
input_connected_(false),
video_connected_(false) {
}
ConnectionToHost::~ConnectionToHost() {
}
InputStub* ConnectionToHost::input_stub() {
return input_dispatcher_.get();
}
HostStub* ConnectionToHost::host_stub() {
return control_dispatcher_.get();
}
void ConnectionToHost::Connect(scoped_refptr<XmppProxy> xmpp_proxy,
const std::string& your_jid,
const std::string& host_jid,
const std::string& host_public_key,
const std::string& access_code,
HostEventCallback* event_callback,
ClientStub* client_stub,
VideoStub* video_stub) {
event_callback_ = event_callback;
client_stub_ = client_stub;
video_stub_ = video_stub;
access_code_ = access_code;
// Save jid of the host. The actual connection is created later after
// |signal_strategy_| is connected.
host_jid_ = host_jid;
host_public_key_ = host_public_key;
JavascriptSignalStrategy* strategy = new JavascriptSignalStrategy(your_jid);
strategy->AttachXmppProxy(xmpp_proxy);
signal_strategy_.reset(strategy);
signal_strategy_->Init(this);
}
void ConnectionToHost::Disconnect(const base::Closure& shutdown_task) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(
FROM_HERE, base::Bind(&ConnectionToHost::Disconnect,
base::Unretained(this), shutdown_task));
return;
}
CloseChannels();
if (session_.get())
session_.reset();
if (session_manager_.get())
session_manager_.reset();
if (signal_strategy_.get())
signal_strategy_.reset();
shutdown_task.Run();
}
void ConnectionToHost::InitSession() {
DCHECK(message_loop_->BelongsToCurrentThread());
session_manager_.reset(new PepperSessionManager(pp_instance_));
session_manager_->Init(
local_jid_, signal_strategy_.get(), this, NULL, "", allow_nat_traversal_);
}
const SessionConfig& ConnectionToHost::config() {
return session_->config();
}
void ConnectionToHost::OnStateChange(
SignalStrategy::StatusObserver::State state) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(event_callback_);
if (state == SignalStrategy::StatusObserver::CONNECTED) {
VLOG(1) << "Connected as: " << local_jid_;
InitSession();
} else if (state == SignalStrategy::StatusObserver::CLOSED) {
VLOG(1) << "Connection closed.";
CloseOnError(NETWORK_FAILURE);
}
}
void ConnectionToHost::OnJidChange(const std::string& full_jid) {
DCHECK(message_loop_->BelongsToCurrentThread());
local_jid_ = full_jid;
}
void ConnectionToHost::OnSessionManagerInitialized() {
DCHECK(message_loop_->BelongsToCurrentThread());
// After SessionManager is initialized we can try to connect to the host.
CandidateSessionConfig* candidate_config =
CandidateSessionConfig::CreateDefault();
std::string client_token =
protocol::GenerateSupportAuthToken(local_jid_, access_code_);
session_.reset(session_manager_->Connect(
host_jid_, host_public_key_, client_token, candidate_config,
base::Bind(&ConnectionToHost::OnSessionStateChange,
base::Unretained(this))));
// Set the shared-secret for securing SSL channels.
session_->set_shared_secret(access_code_);
}
void ConnectionToHost::OnIncomingSession(
Session* session,
SessionManager::IncomingSessionResponse* response) {
DCHECK(message_loop_->BelongsToCurrentThread());
// Client always rejects incoming sessions.
*response = SessionManager::DECLINE;
}
ConnectionToHost::State ConnectionToHost::state() const {
return state_;
}
void ConnectionToHost::OnSessionStateChange(
Session::State state) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(event_callback_);
switch (state) {
case Session::FAILED:
switch (session_->error()) {
case Session::PEER_IS_OFFLINE:
CloseOnError(HOST_IS_OFFLINE);
break;
case Session::SESSION_REJECTED:
CloseOnError(SESSION_REJECTED);
break;
case Session::INCOMPATIBLE_PROTOCOL:
CloseOnError(INCOMPATIBLE_PROTOCOL);
break;
case Session::CHANNEL_CONNECTION_ERROR:
CloseOnError(NETWORK_FAILURE);
break;
case Session::OK:
DLOG(FATAL) << "Error code isn't set";
CloseOnError(NETWORK_FAILURE);
}
break;
case Session::CLOSED:
CloseChannels();
SetState(CLOSED, OK);
break;
case Session::CONNECTED:
video_reader_.reset(
VideoReader::Create(message_loop_, session_->config()));
video_reader_->Init(
session_.get(), video_stub_,
base::Bind(&ConnectionToHost::OnVideoChannelInitialized,
base::Unretained(this)));
break;
case Session::CONNECTED_CHANNELS:
control_dispatcher_.reset(new ClientControlDispatcher());
control_dispatcher_->Init(session_.get());
control_dispatcher_->set_client_stub(client_stub_);
input_dispatcher_.reset(new ClientEventDispatcher());
input_dispatcher_->Init(session_.get());
control_connected_ = true;
input_connected_ = true;
NotifyIfChannelsReady();
break;
default:
// Ignore the other states by default.
break;
}
}
void ConnectionToHost::OnVideoChannelInitialized(bool successful) {
if (!successful) {
LOG(ERROR) << "Failed to connect video channel";
CloseOnError(NETWORK_FAILURE);
return;
}
video_connected_ = true;
NotifyIfChannelsReady();
}
void ConnectionToHost::NotifyIfChannelsReady() {
if (control_connected_ && input_connected_ && video_connected_ &&
state_ == CONNECTING) {
SetState(CONNECTED, OK);
SetState(AUTHENTICATED, OK);
}
}
void ConnectionToHost::CloseOnError(Error error) {
CloseChannels();
SetState(FAILED, error);
}
void ConnectionToHost::CloseChannels() {
control_dispatcher_.reset();
input_dispatcher_.reset();
video_reader_.reset();
}
void ConnectionToHost::SetState(State state, Error error) {
DCHECK(message_loop_->BelongsToCurrentThread());
// |error| should be specified only when |state| is set to FAILED.
DCHECK(state == FAILED || error == OK);
if (state != state_) {
state_ = state;
error_ = error;
event_callback_->OnConnectionState(state_, error_);
}
}
} // namespace protocol
} // namespace remoting