blob: 94ceee77015d5f2aa33062686b8c421cdcb8518a [file] [log] [blame]
// Copyright 2015 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/ice_transport.h"
#include "base/bind.h"
#include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/channel_multiplexer.h"
#include "remoting/protocol/pseudotcp_channel_factory.h"
#include "remoting/protocol/secure_channel_factory.h"
#include "remoting/protocol/stream_channel_factory.h"
#include "remoting/protocol/stream_message_pipe_adapter.h"
#include "remoting/protocol/transport_context.h"
namespace remoting {
namespace protocol {
// Delay after candidate creation before sending transport-info message to
// accumulate multiple candidates. This is an optimization to reduce number of
// transport-info messages.
const int kTransportInfoSendDelayMs = 20;
// Name of the multiplexed channel.
static const char kMuxChannelName[] = "mux";
IceTransport::IceTransport(scoped_refptr<TransportContext> transport_context,
EventHandler* event_handler)
: transport_context_(transport_context),
event_handler_(event_handler),
weak_factory_(this) {
transport_context_->set_relay_mode(TransportContext::RelayMode::GTURN);
transport_context->Prepare();
}
IceTransport::~IceTransport() {
channel_multiplexer_.reset();
DCHECK(channels_.empty());
}
void IceTransport::Start(
Authenticator* authenticator,
SendTransportInfoCallback send_transport_info_callback) {
DCHECK(!pseudotcp_channel_factory_);
send_transport_info_callback_ = std::move(send_transport_info_callback);
pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
secure_channel_factory_.reset(new SecureChannelFactory(
pseudotcp_channel_factory_.get(), authenticator));
message_channel_factory_.reset(new StreamMessageChannelFactoryAdapter(
secure_channel_factory_.get(),
base::Bind(&IceTransport::OnChannelError, weak_factory_.GetWeakPtr())));
}
bool IceTransport::ProcessTransportInfo(buzz::XmlElement* transport_info_xml) {
IceTransportInfo transport_info;
if (!transport_info.ParseXml(transport_info_xml))
return false;
for (auto it = transport_info.ice_credentials.begin();
it != transport_info.ice_credentials.end(); ++it) {
ChannelsMap::iterator channel = channels_.find(it->channel);
if (channel != channels_.end()) {
channel->second->SetRemoteCredentials(it->ufrag, it->password);
} else {
// Transport info was received before the channel was created.
// This could happen due to messages being reordered on the wire.
pending_remote_ice_credentials_.push_back(*it);
}
}
for (auto it = transport_info.candidates.begin();
it != transport_info.candidates.end(); ++it) {
ChannelsMap::iterator channel = channels_.find(it->name);
if (channel != channels_.end()) {
channel->second->AddRemoteCandidate(it->candidate);
} else {
// Transport info was received before the channel was created.
// This could happen due to messages being reordered on the wire.
pending_remote_candidates_.push_back(*it);
}
}
return true;
}
MessageChannelFactory* IceTransport::GetChannelFactory() {
return message_channel_factory_.get();
}
MessageChannelFactory* IceTransport::GetMultiplexedChannelFactory() {
if (!channel_multiplexer_) {
channel_multiplexer_.reset(
new ChannelMultiplexer(secure_channel_factory_.get(), kMuxChannelName));
mux_channel_factory_.reset(new StreamMessageChannelFactoryAdapter(
channel_multiplexer_.get(),
base::Bind(&IceTransport::OnChannelError, weak_factory_.GetWeakPtr())));
}
return mux_channel_factory_.get();
}
void IceTransport::CreateChannel(const std::string& name,
const ChannelCreatedCallback& callback) {
DCHECK(!channels_[name]);
scoped_ptr<IceTransportChannel> channel(
new IceTransportChannel(transport_context_));
channel->Connect(name, this, callback);
AddPendingRemoteTransportInfo(channel.get());
channels_[name] = channel.release();
}
void IceTransport::CancelChannelCreation(const std::string& name) {
ChannelsMap::iterator it = channels_.find(name);
if (it != channels_.end()) {
DCHECK(!it->second->is_connected());
delete it->second;
DCHECK(channels_.find(name) == channels_.end());
}
}
void IceTransport::AddPendingRemoteTransportInfo(IceTransportChannel* channel) {
std::list<IceTransportInfo::IceCredentials>::iterator credentials =
pending_remote_ice_credentials_.begin();
while (credentials != pending_remote_ice_credentials_.end()) {
if (credentials->channel == channel->name()) {
channel->SetRemoteCredentials(credentials->ufrag, credentials->password);
credentials = pending_remote_ice_credentials_.erase(credentials);
} else {
++credentials;
}
}
std::list<IceTransportInfo::NamedCandidate>::iterator candidate =
pending_remote_candidates_.begin();
while (candidate != pending_remote_candidates_.end()) {
if (candidate->name == channel->name()) {
channel->AddRemoteCandidate(candidate->candidate);
candidate = pending_remote_candidates_.erase(candidate);
} else {
++candidate;
}
}
}
void IceTransport::OnChannelIceCredentials(IceTransportChannel* channel,
const std::string& ufrag,
const std::string& password) {
EnsurePendingTransportInfoMessage();
pending_transport_info_message_->ice_credentials.push_back(
IceTransportInfo::IceCredentials(channel->name(), ufrag, password));
}
void IceTransport::OnChannelCandidate(IceTransportChannel* channel,
const cricket::Candidate& candidate) {
EnsurePendingTransportInfoMessage();
pending_transport_info_message_->candidates.push_back(
IceTransportInfo::NamedCandidate(channel->name(), candidate));
}
void IceTransport::OnChannelRouteChange(IceTransportChannel* channel,
const TransportRoute& route) {
if (event_handler_)
event_handler_->OnIceTransportRouteChange(channel->name(), route);
}
void IceTransport::OnChannelFailed(IceTransportChannel* channel) {
event_handler_->OnIceTransportError(CHANNEL_CONNECTION_ERROR);
}
void IceTransport::OnChannelDeleted(IceTransportChannel* channel) {
ChannelsMap::iterator it = channels_.find(channel->name());
DCHECK_EQ(it->second, channel);
channels_.erase(it);
}
void IceTransport::EnsurePendingTransportInfoMessage() {
// |transport_info_timer_| must be running iff
// |pending_transport_info_message_| exists.
DCHECK_EQ(pending_transport_info_message_ != nullptr,
transport_info_timer_.IsRunning());
if (!pending_transport_info_message_) {
pending_transport_info_message_.reset(new IceTransportInfo());
// Delay sending the new candidates in case we get more candidates
// that we can send in one message.
transport_info_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
this, &IceTransport::SendTransportInfo);
}
}
void IceTransport::SendTransportInfo() {
DCHECK(pending_transport_info_message_);
scoped_ptr<buzz::XmlElement> transport_info_xml =
pending_transport_info_message_->ToXml();
pending_transport_info_message_.reset();
send_transport_info_callback_.Run(std::move(transport_info_xml));
}
void IceTransport::OnChannelError(int error) {
LOG(ERROR) << "Data channel failed, error=" << error;
event_handler_->OnIceTransportError(CHANNEL_CONNECTION_ERROR);
}
} // namespace protocol
} // namespace remoting