Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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/pepper_session.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/rand_util.h" | |
| 9 #include "base/stl_util.h" | |
| 10 #include "base/string_number_conversions.h" | |
| 11 #include "remoting/base/constants.h" | |
| 12 #include "remoting/jingle_glue/iq_request.h" | |
| 13 #include "remoting/protocol/content_description.h" | |
| 14 #include "remoting/protocol/jingle_messages.h" | |
| 15 #include "remoting/protocol/pepper_session_manager.h" | |
| 16 #include "remoting/protocol/pepper_stream_channel.h" | |
| 17 #include "third_party/libjingle/source/talk/p2p/base/candidate.h" | |
| 18 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" | |
| 19 | |
| 20 using buzz::XmlElement; | |
| 21 | |
| 22 namespace remoting { | |
| 23 namespace protocol { | |
| 24 | |
| 25 namespace { | |
| 26 const int kTransportInfoSendDelayMs = 2; | |
|
Wez
2011/09/09 23:39:12
Why 2ms? If the issue is that there may be severa
Sergey Ulanov
2011/09/12 19:50:50
Yes, this is so that we send fewer transport-info
| |
| 27 } // namespace | |
| 28 | |
| 29 PepperSession::PepperSession(PepperSessionManager* session_manager) | |
| 30 : session_manager_(session_manager), | |
| 31 state_(INITIALIZING), | |
| 32 error_(ERROR_NO_ERROR) { | |
| 33 } | |
| 34 | |
| 35 PepperSession::~PepperSession() { | |
| 36 control_channel_socket_.reset(); | |
| 37 event_channel_socket_.reset(); | |
| 38 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end()); | |
| 39 session_manager_->SessionDestroyed(this); | |
| 40 } | |
| 41 | |
| 42 PepperSession::Error PepperSession::error() { | |
| 43 return error_; | |
| 44 } | |
| 45 | |
| 46 void PepperSession::SetStateChangeCallback(StateChangeCallback* callback) { | |
| 47 state_change_callback_.reset(callback); | |
| 48 } | |
| 49 | |
| 50 void PepperSession::StartConnection( | |
| 51 const std::string& host_jid, | |
| 52 const std::string& host_public_key, | |
| 53 const std::string& client_token, | |
| 54 CandidateSessionConfig* config, | |
| 55 Session::StateChangeCallback* state_change_callback) { | |
| 56 host_jid_ = host_jid; | |
| 57 host_public_key_ = host_public_key; | |
| 58 initiator_token_ = client_token; | |
| 59 candidate_config_.reset(config); | |
| 60 state_change_callback_.reset(state_change_callback); | |
| 61 | |
| 62 session_id_ = base::Int64ToString(base::RandGenerator(kint64max)); | |
|
Wez
2011/09/09 23:39:12
Add a comment to explain why it's OK for our Id to
Sergey Ulanov
2011/09/12 19:50:50
Done.
| |
| 63 | |
| 64 JingleMessage message(host_jid_, JingleMessage::SESSION_INITIATE, | |
| 65 session_id_); | |
| 66 message.from = session_manager_->local_jid_; | |
| 67 message.description.reset( | |
| 68 new ContentDescription(candidate_config_->Clone(), initiator_token_, "")); | |
| 69 initiate_request_.reset(session_manager_->CreateIqRequest()); | |
| 70 initiate_request_->set_callback(base::Bind( | |
| 71 &PepperSession::OnSessionInitiateResponse, base::Unretained(this))); | |
| 72 initiate_request_->SendIq(message.ToXml()); | |
|
Wez
2011/09/09 23:39:12
Add a comment e.g. "Send the session-initiate stan
Sergey Ulanov
2011/09/12 19:50:50
Done.
| |
| 73 | |
| 74 SetState(CONNECTING); | |
| 75 } | |
| 76 | |
| 77 void PepperSession::OnSessionInitiateResponse( | |
| 78 const buzz::XmlElement* response) { | |
| 79 const std::string& type = response->Attr(buzz::QName("", "type")); | |
| 80 if (type != "result") { | |
| 81 LOG(ERROR) << "Received error in response to session-initiate message: \"" | |
| 82 << response->Str() | |
| 83 << "\" Terminating the session."; | |
| 84 | |
| 85 // TODO(sergeyu): There may be different reasons for error | |
| 86 // here. Parse the response stanza to find failure reason. | |
| 87 OnError(ERROR_HOST_IS_OFFLINE); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 void PepperSession::OnError(Error error) { | |
| 92 error_ = error; | |
| 93 CloseInternal(true); | |
| 94 } | |
| 95 | |
| 96 void PepperSession::CreateStreamChannel( | |
| 97 const std::string& name, | |
| 98 const StreamChannelCallback& callback) { | |
| 99 DCHECK(!channels_[name]); | |
| 100 | |
| 101 PepperStreamChannel* channel = new PepperStreamChannel(this, name, callback); | |
| 102 channels_[name] = channel; | |
| 103 channel->Connect(session_manager_->pp_instance_, | |
| 104 session_manager_->transport_config_, remote_cert_); | |
| 105 } | |
| 106 | |
| 107 void PepperSession::CreateDatagramChannel( | |
| 108 const std::string& name, | |
| 109 const DatagramChannelCallback& callback) { | |
| 110 // TODO(sergeyu): Implement datagram channel support. | |
| 111 NOTREACHED(); | |
| 112 } | |
| 113 | |
| 114 net::Socket* PepperSession::control_channel() { | |
| 115 DCHECK(CalledOnValidThread()); | |
| 116 return control_channel_socket_.get(); | |
| 117 } | |
| 118 | |
| 119 net::Socket* PepperSession::event_channel() { | |
| 120 DCHECK(CalledOnValidThread()); | |
| 121 return event_channel_socket_.get(); | |
| 122 } | |
| 123 | |
| 124 const std::string& PepperSession::jid() { | |
| 125 DCHECK(CalledOnValidThread()); | |
| 126 return host_jid_; | |
| 127 } | |
| 128 | |
| 129 const CandidateSessionConfig* PepperSession::candidate_config() { | |
| 130 DCHECK(CalledOnValidThread()); | |
| 131 return candidate_config_.get(); | |
| 132 } | |
| 133 | |
| 134 const SessionConfig* PepperSession::config() { | |
|
Wez
2011/09/09 23:39:12
Check CalledOnValidThread()?
Sergey Ulanov
2011/09/12 19:50:50
Done.
| |
| 135 return config_.get(); | |
| 136 } | |
| 137 | |
| 138 void PepperSession::set_config(const SessionConfig* config) { | |
|
Wez
2011/09/09 23:39:12
Here too?
Sergey Ulanov
2011/09/12 19:50:50
Done.
| |
| 139 // set_config() should never be called on the client. | |
| 140 NOTREACHED(); | |
| 141 } | |
| 142 | |
| 143 const std::string& PepperSession::initiator_token() { | |
| 144 DCHECK(CalledOnValidThread()); | |
| 145 return initiator_token_; | |
| 146 } | |
| 147 | |
| 148 void PepperSession::set_initiator_token(const std::string& initiator_token) { | |
| 149 DCHECK(CalledOnValidThread()); | |
| 150 initiator_token_ = initiator_token; | |
| 151 } | |
| 152 | |
| 153 const std::string& PepperSession::receiver_token() { | |
| 154 DCHECK(CalledOnValidThread()); | |
| 155 return receiver_token_; | |
| 156 } | |
| 157 | |
| 158 void PepperSession::set_receiver_token(const std::string& receiver_token) { | |
|
Wez
2011/09/09 23:39:12
And again?
Sergey Ulanov
2011/09/12 19:50:50
Done.
| |
| 159 // set_receiver_token() should not be called on the client side. | |
| 160 NOTREACHED(); | |
| 161 } | |
| 162 | |
| 163 void PepperSession::set_shared_secret(const std::string& secret) { | |
| 164 DCHECK(CalledOnValidThread()); | |
| 165 shared_secret_ = secret; | |
| 166 } | |
| 167 | |
| 168 const std::string& PepperSession::shared_secret() { | |
| 169 DCHECK(CalledOnValidThread()); | |
| 170 return shared_secret_; | |
| 171 } | |
| 172 | |
| 173 void PepperSession::Close() { | |
| 174 DCHECK(CalledOnValidThread()); | |
| 175 | |
| 176 if (state_ == CONNECTING || state_ == CONNECTED || | |
| 177 state_ == CONNECTED_CHANNELS) { | |
| 178 // Send session-terminate message. | |
| 179 JingleMessage message(host_jid_, JingleMessage::SESSION_TERMINATE, | |
| 180 session_id_); | |
| 181 scoped_ptr<IqRequest> terminate_request( | |
| 182 session_manager_->CreateIqRequest()); | |
| 183 terminate_request->SendIq(message.ToXml()); | |
| 184 } | |
| 185 | |
| 186 CloseInternal(false); | |
| 187 } | |
| 188 | |
| 189 void PepperSession::OnIncomingMessage(const JingleMessage& message, | |
| 190 JingleMessageReply* reply) { | |
| 191 DCHECK(CalledOnValidThread()); | |
| 192 | |
| 193 if (message.from != host_jid_) { | |
| 194 // Ignore messages received from a different Jid. | |
| 195 *reply = JingleMessageReply(JingleMessageReply::INVALID_SID); | |
|
Wez
2011/09/09 23:39:12
Can that ever happen?
Sergey Ulanov
2011/09/12 19:50:50
Yes if somebody guesses SID and tries to spoof ses
| |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 switch (message.action) { | |
| 200 case JingleMessage::SESSION_ACCEPT: | |
| 201 OnAccept(message, reply); | |
| 202 break; | |
| 203 | |
| 204 case JingleMessage::TRANSPORT_INFO: | |
| 205 ProcessTransportInfo(message); | |
| 206 break; | |
| 207 | |
| 208 case JingleMessage::SESSION_REJECT: | |
| 209 OnReject(message, reply); | |
| 210 break; | |
| 211 | |
| 212 case JingleMessage::SESSION_TERMINATE: | |
| 213 OnTerminate(message, reply); | |
| 214 break; | |
| 215 | |
| 216 default: | |
| 217 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 void PepperSession::OnAccept(const JingleMessage& message, | |
| 222 JingleMessageReply* reply) { | |
| 223 if (state_ != CONNECTING) { | |
| 224 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST); | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 if (!InitializeConfigFromDescription(message.description.get())) { | |
| 229 OnError(ERROR_INCOMPATIBLE_PROTOCOL); | |
| 230 return; | |
| 231 } | |
| 232 | |
| 233 CreateChannels(); | |
| 234 SetState(CONNECTED); | |
| 235 | |
| 236 // In case there is transport information in the accept message. | |
| 237 ProcessTransportInfo(message); | |
| 238 } | |
| 239 | |
| 240 void PepperSession::ProcessTransportInfo(const JingleMessage& message) { | |
| 241 for (std::list<cricket::Candidate>::const_iterator it = | |
| 242 message.candidates.begin(); | |
| 243 it != message.candidates.end(); ++it) { | |
| 244 ChannelsMap::iterator channel = channels_.find(it->name()); | |
| 245 if (channel == channels_.end()) { | |
| 246 LOG(WARNING) << "Received candidate for unknown channel " << it->name(); | |
| 247 continue; | |
| 248 } | |
| 249 channel->second->AddRemoveCandidate(*it); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void PepperSession::OnReject(const JingleMessage& message, | |
| 254 JingleMessageReply* reply) { | |
| 255 if (state_ != CONNECTING) { | |
| 256 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST); | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 // TODO(sergeyu): Pass exact rejection reason from reply and pass it | |
|
Wez
2011/09/09 23:39:12
nit: You mean "parse exact..."?
Sergey Ulanov
2011/09/12 19:50:50
Done.
| |
| 261 // to OnError(). | |
| 262 OnError(ERROR_SESSION_REJECTED); | |
| 263 } | |
| 264 | |
| 265 void PepperSession::OnTerminate(const JingleMessage& message, | |
| 266 JingleMessageReply* reply) { | |
| 267 if (state_ == CONNECTING) { | |
| 268 // If we are not connected yet, then interpret terminate message | |
| 269 // as rejection. | |
| 270 OnError(ERROR_SESSION_REJECTED); | |
| 271 return; | |
| 272 } | |
| 273 | |
| 274 CloseInternal(false); | |
| 275 } | |
| 276 | |
| 277 bool PepperSession::InitializeConfigFromDescription( | |
| 278 const ContentDescription* description) { | |
| 279 DCHECK(description); | |
| 280 | |
| 281 remote_cert_ = description->certificate(); | |
| 282 if (remote_cert_.empty()) { | |
| 283 LOG(ERROR) << "session-accept does not specify certificate"; | |
| 284 return false; | |
| 285 } | |
| 286 | |
| 287 config_.reset(description->config()->GetFinalConfig()); | |
| 288 if (!config_.get()) { | |
| 289 LOG(ERROR) << "session-accept does not specify configuration"; | |
| 290 return false; | |
| 291 } | |
| 292 if (!candidate_config()->IsSupported(config_.get())) { | |
| 293 LOG(ERROR) << "session-accept specifies an invalid configuration"; | |
| 294 return false; | |
| 295 } | |
| 296 | |
| 297 return true; | |
| 298 } | |
| 299 | |
| 300 void PepperSession::AddLocalCandidate(const cricket::Candidate& candidate) { | |
| 301 pending_candidates_.push_back(candidate); | |
| 302 | |
| 303 if (!transport_infos_timer_.IsRunning()) { | |
| 304 // Delay sending the new candidates in case we get more candidates | |
| 305 // that we can send in one message. | |
| 306 transport_infos_timer_.Start( | |
| 307 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs), | |
| 308 this, &PepperSession::SendTransportInfo); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 void PepperSession::OnDeleteChannel(PepperChannel* channel) { | |
| 313 ChannelsMap::iterator it = channels_.find(channel->name()); | |
| 314 DCHECK_EQ(it->second, channel); | |
| 315 channels_.erase(it); | |
| 316 } | |
| 317 | |
| 318 void PepperSession::SendTransportInfo() { | |
| 319 JingleMessage message(host_jid_, JingleMessage::TRANSPORT_INFO, session_id_); | |
| 320 message.candidates.swap(pending_candidates_); | |
| 321 scoped_ptr<IqRequest> request(session_manager_->CreateIqRequest()); | |
| 322 request->SendIq(message.ToXml()); | |
| 323 } | |
| 324 | |
| 325 void PepperSession::CreateChannels() { | |
| 326 CreateStreamChannel( | |
| 327 kControlChannelName, | |
| 328 base::Bind(&PepperSession::OnChannelConnected, | |
| 329 base::Unretained(this), &control_channel_socket_)); | |
| 330 CreateStreamChannel( | |
| 331 kEventChannelName, | |
| 332 base::Bind(&PepperSession::OnChannelConnected, | |
| 333 base::Unretained(this), &event_channel_socket_)); | |
| 334 } | |
| 335 | |
| 336 void PepperSession::OnChannelConnected( | |
| 337 scoped_ptr<net::Socket>* socket_container, | |
| 338 net::StreamSocket* socket) { | |
| 339 if (!socket) { | |
| 340 LOG(ERROR) << "Failed to connect control or events channel. " | |
| 341 << "Terminating connection"; | |
| 342 OnError(ERROR_CHANNEL_CONNECTION_FAILURE); | |
| 343 return; | |
| 344 } | |
| 345 | |
| 346 socket_container->reset(socket); | |
| 347 | |
| 348 if (control_channel_socket_.get() && event_channel_socket_.get()) | |
| 349 SetState(CONNECTED_CHANNELS); | |
| 350 } | |
| 351 | |
| 352 void PepperSession::CloseInternal(bool failed) { | |
| 353 DCHECK(CalledOnValidThread()); | |
| 354 | |
| 355 if (state_ != FAILED && state_ != CLOSED) { | |
| 356 control_channel_socket_.reset(); | |
| 357 event_channel_socket_.reset(); | |
| 358 | |
| 359 // Inform the StateChangeCallback, so calling code knows not to | |
| 360 // touch any channels. Needs to be done in the end because the | |
|
Wez
2011/09/09 23:39:12
nit: "... in the end ..."?
Sergey Ulanov
2011/09/12 19:50:50
Removed the comment.
| |
| 361 // session may be deleted in response to this event. | |
| 362 if (failed) | |
| 363 SetState(FAILED); | |
| 364 else | |
| 365 SetState(CLOSED); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 void PepperSession::SetState(State new_state) { | |
| 370 DCHECK(CalledOnValidThread()); | |
| 371 | |
| 372 if (new_state != state_) { | |
| 373 DCHECK_NE(state_, CLOSED); | |
| 374 DCHECK_NE(state_, FAILED); | |
| 375 | |
| 376 state_ = new_state; | |
| 377 if (state_change_callback_.get()) | |
| 378 state_change_callback_->Run(new_state); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 } // namespace protocol | |
| 383 } // namespace remoting | |
| OLD | NEW |