connection_to_host.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/protocol/connection_to_host.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/location.h" 10#include "remoting/base/constants.h" 11#include "remoting/jingle_glue/signal_strategy.h" 12#include "remoting/protocol/audio_reader.h" 13#include "remoting/protocol/audio_stub.h" 14#include "remoting/protocol/auth_util.h" 15#include "remoting/protocol/authenticator.h" 16#include "remoting/protocol/client_control_dispatcher.h" 17#include "remoting/protocol/client_event_dispatcher.h" 18#include "remoting/protocol/client_stub.h" 19#include "remoting/protocol/clipboard_stub.h" 20#include "remoting/protocol/errors.h" 21#include "remoting/protocol/jingle_session_manager.h" 22#include "remoting/protocol/transport.h" 23#include "remoting/protocol/video_reader.h" 24#include "remoting/protocol/video_stub.h" 25#include "remoting/protocol/util.h" 26 27namespace remoting { 28namespace protocol { 29 30ConnectionToHost::ConnectionToHost( 31 bool allow_nat_traversal) 32 : allow_nat_traversal_(allow_nat_traversal), 33 event_callback_(NULL), 34 client_stub_(NULL), 35 clipboard_stub_(NULL), 36 video_stub_(NULL), 37 audio_stub_(NULL), 38 signal_strategy_(NULL), 39 state_(INITIALIZING), 40 error_(OK) { 41} 42 43ConnectionToHost::~ConnectionToHost() { 44 CloseChannels(); 45 46 if (session_.get()) 47 session_.reset(); 48 49 if (session_manager_.get()) 50 session_manager_.reset(); 51 52 if (signal_strategy_) 53 signal_strategy_->RemoveListener(this); 54} 55 56ClipboardStub* ConnectionToHost::clipboard_stub() { 57 return &clipboard_forwarder_; 58} 59 60HostStub* ConnectionToHost::host_stub() { 61 // TODO(wez): Add a HostFilter class, equivalent to input filter. 62 return control_dispatcher_.get(); 63} 64 65InputStub* ConnectionToHost::input_stub() { 66 return &event_forwarder_; 67} 68 69void ConnectionToHost::Connect(SignalStrategy* signal_strategy, 70 const std::string& host_jid, 71 const std::string& host_public_key, 72 scoped_ptr<TransportFactory> transport_factory, 73 scoped_ptr<Authenticator> authenticator, 74 HostEventCallback* event_callback, 75 ClientStub* client_stub, 76 ClipboardStub* clipboard_stub, 77 VideoStub* video_stub, 78 AudioStub* audio_stub) { 79 signal_strategy_ = signal_strategy; 80 event_callback_ = event_callback; 81 client_stub_ = client_stub; 82 clipboard_stub_ = clipboard_stub; 83 video_stub_ = video_stub; 84 audio_stub_ = audio_stub; 85 authenticator_ = authenticator.Pass(); 86 87 // Save jid of the host. The actual connection is created later after 88 // |signal_strategy_| is connected. 89 host_jid_ = host_jid; 90 host_public_key_ = host_public_key; 91 92 signal_strategy_->AddListener(this); 93 signal_strategy_->Connect(); 94 95 session_manager_.reset(new JingleSessionManager( 96 transport_factory.Pass(), allow_nat_traversal_)); 97 session_manager_->Init(signal_strategy_, this); 98 99 SetState(CONNECTING, OK); 100} 101 102const SessionConfig& ConnectionToHost::config() { 103 return session_->config(); 104} 105 106void ConnectionToHost::OnSignalStrategyStateChange( 107 SignalStrategy::State state) { 108 DCHECK(CalledOnValidThread()); 109 DCHECK(event_callback_); 110 111 if (state == SignalStrategy::CONNECTED) { 112 VLOG(1) << "Connected as: " << signal_strategy_->GetLocalJid(); 113 } else if (state == SignalStrategy::DISCONNECTED) { 114 VLOG(1) << "Connection closed."; 115 CloseOnError(SIGNALING_ERROR); 116 } 117} 118 119bool ConnectionToHost::OnSignalStrategyIncomingStanza( 120 const buzz::XmlElement* stanza) { 121 return false; 122} 123 124void ConnectionToHost::OnSessionManagerReady() { 125 DCHECK(CalledOnValidThread()); 126 127 // After SessionManager is initialized we can try to connect to the host. 128 scoped_ptr<CandidateSessionConfig> candidate_config = 129 CandidateSessionConfig::CreateDefault(); 130 session_ = session_manager_->Connect( 131 host_jid_, authenticator_.Pass(), candidate_config.Pass()); 132 session_->SetEventHandler(this); 133} 134 135void ConnectionToHost::OnIncomingSession( 136 Session* session, 137 SessionManager::IncomingSessionResponse* response) { 138 DCHECK(CalledOnValidThread()); 139 // Client always rejects incoming sessions. 140 *response = SessionManager::DECLINE; 141} 142 143void ConnectionToHost::OnSessionStateChange( 144 Session::State state) { 145 DCHECK(CalledOnValidThread()); 146 DCHECK(event_callback_); 147 148 switch (state) { 149 case Session::INITIALIZING: 150 case Session::CONNECTING: 151 case Session::ACCEPTING: 152 case Session::CONNECTED: 153 // Don't care about these events. 154 break; 155 156 case Session::AUTHENTICATED: 157 SetState(AUTHENTICATED, OK); 158 159 control_dispatcher_.reset(new ClientControlDispatcher()); 160 control_dispatcher_->Init( 161 session_.get(), session_->config().control_config(), 162 base::Bind(&ConnectionToHost::OnChannelInitialized, 163 base::Unretained(this))); 164 control_dispatcher_->set_client_stub(client_stub_); 165 control_dispatcher_->set_clipboard_stub(clipboard_stub_); 166 167 event_dispatcher_.reset(new ClientEventDispatcher()); 168 event_dispatcher_->Init( 169 session_.get(), session_->config().event_config(), 170 base::Bind(&ConnectionToHost::OnChannelInitialized, 171 base::Unretained(this))); 172 173 video_reader_ = VideoReader::Create(session_->config()); 174 video_reader_->Init(session_.get(), video_stub_, base::Bind( 175 &ConnectionToHost::OnChannelInitialized, base::Unretained(this))); 176 177 audio_reader_ = AudioReader::Create(session_->config()); 178 if (audio_reader_.get()) { 179 audio_reader_->Init( 180 session_.get(), session_->config().audio_config(), 181 base::Bind(&ConnectionToHost::OnChannelInitialized, 182 base::Unretained(this))); 183 audio_reader_->set_audio_stub(audio_stub_); 184 } 185 break; 186 187 case Session::CLOSED: 188 CloseChannels(); 189 SetState(CLOSED, OK); 190 break; 191 192 case Session::FAILED: 193 // If we were connected then treat signaling timeout error as if 194 // the connection was closed by the peer. 195 // 196 // TODO(sergeyu): This logic belongs to the webapp, but we 197 // currently don't expose this error code to the webapp, and it 198 // would be hard to add it because client plugin and webapp 199 // versions may not be in sync. It should be easy to do after we 200 // are finished moving the client plugin to NaCl. 201 if (state_ == CONNECTED && session_->error() == SIGNALING_TIMEOUT) { 202 CloseChannels(); 203 SetState(CLOSED, OK); 204 } else { 205 CloseOnError(session_->error()); 206 } 207 break; 208 } 209} 210 211void ConnectionToHost::OnSessionRouteChange(const std::string& channel_name, 212 const TransportRoute& route) { 213 LOG(INFO) << "Using " << TransportRoute::GetTypeString(route.type) 214 << " connection for " << channel_name << " channel"; 215} 216 217void ConnectionToHost::OnSessionChannelReady(const std::string& channel_name, 218 bool ready) { 219 if (ready) { 220 not_ready_channels_.erase(channel_name); 221 } else if (!ready) { 222 not_ready_channels_.insert(channel_name); 223 } 224 225 event_callback_->OnConnectionReady(not_ready_channels_.empty()); 226} 227 228ConnectionToHost::State ConnectionToHost::state() const { 229 return state_; 230} 231 232void ConnectionToHost::OnChannelInitialized(bool successful) { 233 if (!successful) { 234 LOG(ERROR) << "Failed to connect video channel"; 235 CloseOnError(CHANNEL_CONNECTION_ERROR); 236 return; 237 } 238 239 NotifyIfChannelsReady(); 240} 241 242void ConnectionToHost::NotifyIfChannelsReady() { 243 if (!control_dispatcher_.get() || !control_dispatcher_->is_connected()) 244 return; 245 if (!event_dispatcher_.get() || !event_dispatcher_->is_connected()) 246 return; 247 if (!video_reader_.get() || !video_reader_->is_connected()) 248 return; 249 if ((!audio_reader_.get() || !audio_reader_->is_connected()) && 250 session_->config().is_audio_enabled()) { 251 return; 252 } 253 if (state_ != AUTHENTICATED) 254 return; 255 256 // Start forwarding clipboard and input events. 257 clipboard_forwarder_.set_clipboard_stub(control_dispatcher_.get()); 258 event_forwarder_.set_input_stub(event_dispatcher_.get()); 259 SetState(CONNECTED, OK); 260} 261 262void ConnectionToHost::CloseOnError(ErrorCode error) { 263 CloseChannels(); 264 SetState(FAILED, error); 265} 266 267void ConnectionToHost::CloseChannels() { 268 control_dispatcher_.reset(); 269 event_dispatcher_.reset(); 270 clipboard_forwarder_.set_clipboard_stub(NULL); 271 event_forwarder_.set_input_stub(NULL); 272 video_reader_.reset(); 273 audio_reader_.reset(); 274} 275 276void ConnectionToHost::SetState(State state, ErrorCode error) { 277 DCHECK(CalledOnValidThread()); 278 // |error| should be specified only when |state| is set to FAILED. 279 DCHECK(state == FAILED || error == OK); 280 281 if (state != state_) { 282 state_ = state; 283 error_ = error; 284 event_callback_->OnConnectionState(state_, error_); 285 } 286} 287 288} // namespace protocol 289} // namespace remoting 290