Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(644)

Side by Side Diff: remoting/protocol/pepper_session.cc

Issue 7778022: Chromoting protocol implementation based on P2P Transport API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: - Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/protocol/pepper_session.h ('k') | remoting/protocol/pepper_session_manager.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 // Delay after candidate creation before sending transport-info
27 // message. This is neccessary to be able to pack multiple candidates
28 // into one transport-info messages. The value needs to be greater
29 // than zero because ports are opened asynchronously in the browser
30 // process.
31 const int kTransportInfoSendDelayMs = 2;
32 } // namespace
33
34 PepperSession::PepperSession(PepperSessionManager* session_manager)
35 : session_manager_(session_manager),
36 state_(INITIALIZING),
37 error_(ERROR_NO_ERROR) {
38 }
39
40 PepperSession::~PepperSession() {
41 control_channel_socket_.reset();
42 event_channel_socket_.reset();
43 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
44 session_manager_->SessionDestroyed(this);
45 }
46
47 PepperSession::Error PepperSession::error() {
48 DCHECK(CalledOnValidThread());
49 return error_;
50 }
51
52 void PepperSession::SetStateChangeCallback(StateChangeCallback* callback) {
53 DCHECK(CalledOnValidThread());
54 state_change_callback_.reset(callback);
55 }
56
57 void PepperSession::StartConnection(
58 const std::string& peer_jid,
59 const std::string& peer_public_key,
60 const std::string& client_token,
61 CandidateSessionConfig* config,
62 Session::StateChangeCallback* state_change_callback) {
63 DCHECK(CalledOnValidThread());
64
65 peer_jid_ = peer_jid;
66 peer_public_key_ = peer_public_key;
67 initiator_token_ = client_token;
68 candidate_config_.reset(config);
69 state_change_callback_.reset(state_change_callback);
70
71 // Generate random session ID. There are usually not more than 1
72 // concurrent session per host, so a random 64-bit integer provides
73 // enough entropy. In the worst case connection will fail when two
74 // clients generate the same session ID concurrently.
75 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
76
77 // Send session-initiate message.
78 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
79 session_id_);
80 message.from = session_manager_->local_jid_;
81 message.description.reset(
82 new ContentDescription(candidate_config_->Clone(), initiator_token_, ""));
83 initiate_request_.reset(session_manager_->CreateIqRequest());
84 initiate_request_->set_callback(base::Bind(
85 &PepperSession::OnSessionInitiateResponse, base::Unretained(this)));
86 initiate_request_->SendIq(message.ToXml());
87
88 SetState(CONNECTING);
89 }
90
91 void PepperSession::OnSessionInitiateResponse(
92 const buzz::XmlElement* response) {
93 const std::string& type = response->Attr(buzz::QName("", "type"));
94 if (type != "result") {
95 LOG(ERROR) << "Received error in response to session-initiate message: \""
96 << response->Str()
97 << "\" Terminating the session.";
98
99 // TODO(sergeyu): There may be different reasons for error
100 // here. Parse the response stanza to find failure reason.
101 OnError(ERROR_PEER_IS_OFFLINE);
102 }
103 }
104
105 void PepperSession::OnError(Error error) {
106 error_ = error;
107 CloseInternal(true);
108 }
109
110 void PepperSession::CreateStreamChannel(
111 const std::string& name,
112 const StreamChannelCallback& callback) {
113 DCHECK(!channels_[name]);
114
115 PepperStreamChannel* channel = new PepperStreamChannel(this, name, callback);
116 channels_[name] = channel;
117 channel->Connect(session_manager_->pp_instance_,
118 session_manager_->transport_config_, remote_cert_);
119 }
120
121 void PepperSession::CreateDatagramChannel(
122 const std::string& name,
123 const DatagramChannelCallback& callback) {
124 // TODO(sergeyu): Implement datagram channel support.
125 NOTREACHED();
126 }
127
128 net::Socket* PepperSession::control_channel() {
129 DCHECK(CalledOnValidThread());
130 return control_channel_socket_.get();
131 }
132
133 net::Socket* PepperSession::event_channel() {
134 DCHECK(CalledOnValidThread());
135 return event_channel_socket_.get();
136 }
137
138 const std::string& PepperSession::jid() {
139 DCHECK(CalledOnValidThread());
140 return peer_jid_;
141 }
142
143 const CandidateSessionConfig* PepperSession::candidate_config() {
144 DCHECK(CalledOnValidThread());
145 return candidate_config_.get();
146 }
147
148 const SessionConfig& PepperSession::config() {
149 DCHECK(CalledOnValidThread());
150 return config_;
151 }
152
153 void PepperSession::set_config(const SessionConfig& config) {
154 DCHECK(CalledOnValidThread());
155 // set_config() should never be called on the client.
156 NOTREACHED();
157 }
158
159 const std::string& PepperSession::initiator_token() {
160 DCHECK(CalledOnValidThread());
161 return initiator_token_;
162 }
163
164 void PepperSession::set_initiator_token(const std::string& initiator_token) {
165 DCHECK(CalledOnValidThread());
166 initiator_token_ = initiator_token;
167 }
168
169 const std::string& PepperSession::receiver_token() {
170 DCHECK(CalledOnValidThread());
171 return receiver_token_;
172 }
173
174 void PepperSession::set_receiver_token(const std::string& receiver_token) {
175 DCHECK(CalledOnValidThread());
176 // set_receiver_token() should not be called on the client side.
177 NOTREACHED();
178 }
179
180 void PepperSession::set_shared_secret(const std::string& secret) {
181 DCHECK(CalledOnValidThread());
182 shared_secret_ = secret;
183 }
184
185 const std::string& PepperSession::shared_secret() {
186 DCHECK(CalledOnValidThread());
187 return shared_secret_;
188 }
189
190 void PepperSession::Close() {
191 DCHECK(CalledOnValidThread());
192
193 if (state_ == CONNECTING || state_ == CONNECTED ||
194 state_ == CONNECTED_CHANNELS) {
195 // Send session-terminate message.
196 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
197 session_id_);
198 scoped_ptr<IqRequest> terminate_request(
199 session_manager_->CreateIqRequest());
200 terminate_request->SendIq(message.ToXml());
201 }
202
203 CloseInternal(false);
204 }
205
206 void PepperSession::OnIncomingMessage(const JingleMessage& message,
207 JingleMessageReply* reply) {
208 DCHECK(CalledOnValidThread());
209
210 if (message.from != peer_jid_) {
211 // Ignore messages received from a different Jid.
212 *reply = JingleMessageReply(JingleMessageReply::INVALID_SID);
213 return;
214 }
215
216 switch (message.action) {
217 case JingleMessage::SESSION_ACCEPT:
218 OnAccept(message, reply);
219 break;
220
221 case JingleMessage::TRANSPORT_INFO:
222 ProcessTransportInfo(message);
223 break;
224
225 case JingleMessage::SESSION_REJECT:
226 OnReject(message, reply);
227 break;
228
229 case JingleMessage::SESSION_TERMINATE:
230 OnTerminate(message, reply);
231 break;
232
233 default:
234 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST);
235 }
236 }
237
238 void PepperSession::OnAccept(const JingleMessage& message,
239 JingleMessageReply* reply) {
240 if (state_ != CONNECTING) {
241 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST);
242 return;
243 }
244
245 if (!InitializeConfigFromDescription(message.description.get())) {
246 OnError(ERROR_INCOMPATIBLE_PROTOCOL);
247 return;
248 }
249
250 CreateChannels();
251 SetState(CONNECTED);
252
253 // In case there is transport information in the accept message.
254 ProcessTransportInfo(message);
255 }
256
257 void PepperSession::ProcessTransportInfo(const JingleMessage& message) {
258 for (std::list<cricket::Candidate>::const_iterator it =
259 message.candidates.begin();
260 it != message.candidates.end(); ++it) {
261 ChannelsMap::iterator channel = channels_.find(it->name());
262 if (channel == channels_.end()) {
263 LOG(WARNING) << "Received candidate for unknown channel " << it->name();
264 continue;
265 }
266 channel->second->AddRemoveCandidate(*it);
267 }
268 }
269
270 void PepperSession::OnReject(const JingleMessage& message,
271 JingleMessageReply* reply) {
272 if (state_ != CONNECTING) {
273 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST);
274 return;
275 }
276
277 // TODO(sergeyu): Parse exact rejection reason from reply and pass it
278 // to OnError().
279 OnError(ERROR_SESSION_REJECTED);
280 }
281
282 void PepperSession::OnTerminate(const JingleMessage& message,
283 JingleMessageReply* reply) {
284 if (state_ == CONNECTING) {
285 // If we are not connected yet, then interpret terminate message
286 // as rejection.
287 OnError(ERROR_SESSION_REJECTED);
288 return;
289 }
290
291 CloseInternal(false);
292 }
293
294 bool PepperSession::InitializeConfigFromDescription(
295 const ContentDescription* description) {
296 DCHECK(description);
297
298 remote_cert_ = description->certificate();
299 if (remote_cert_.empty()) {
300 LOG(ERROR) << "session-accept does not specify certificate";
301 return false;
302 }
303
304 if (!description->config()->GetFinalConfig(&config_)) {
305 LOG(ERROR) << "session-accept does not specify configuration";
306 return false;
307 }
308 if (!candidate_config()->IsSupported(config_)) {
309 LOG(ERROR) << "session-accept specifies an invalid configuration";
310 return false;
311 }
312
313 return true;
314 }
315
316 void PepperSession::AddLocalCandidate(const cricket::Candidate& candidate) {
317 pending_candidates_.push_back(candidate);
318
319 if (!transport_infos_timer_.IsRunning()) {
320 // Delay sending the new candidates in case we get more candidates
321 // that we can send in one message.
322 transport_infos_timer_.Start(
323 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
324 this, &PepperSession::SendTransportInfo);
325 }
326 }
327
328 void PepperSession::OnDeleteChannel(PepperChannel* channel) {
329 ChannelsMap::iterator it = channels_.find(channel->name());
330 DCHECK_EQ(it->second, channel);
331 channels_.erase(it);
332 }
333
334 void PepperSession::SendTransportInfo() {
335 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
336 message.candidates.swap(pending_candidates_);
337 scoped_ptr<IqRequest> request(session_manager_->CreateIqRequest());
338 request->SendIq(message.ToXml());
339 }
340
341 void PepperSession::CreateChannels() {
342 CreateStreamChannel(
343 kControlChannelName,
344 base::Bind(&PepperSession::OnChannelConnected,
345 base::Unretained(this), &control_channel_socket_));
346 CreateStreamChannel(
347 kEventChannelName,
348 base::Bind(&PepperSession::OnChannelConnected,
349 base::Unretained(this), &event_channel_socket_));
350 }
351
352 void PepperSession::OnChannelConnected(
353 scoped_ptr<net::Socket>* socket_container,
354 net::StreamSocket* socket) {
355 if (!socket) {
356 LOG(ERROR) << "Failed to connect control or events channel. "
357 << "Terminating connection";
358 OnError(ERROR_CHANNEL_CONNECTION_FAILURE);
359 return;
360 }
361
362 socket_container->reset(socket);
363
364 if (control_channel_socket_.get() && event_channel_socket_.get())
365 SetState(CONNECTED_CHANNELS);
366 }
367
368 void PepperSession::CloseInternal(bool failed) {
369 DCHECK(CalledOnValidThread());
370
371 if (state_ != FAILED && state_ != CLOSED) {
372 control_channel_socket_.reset();
373 event_channel_socket_.reset();
374
375 if (failed)
376 SetState(FAILED);
377 else
378 SetState(CLOSED);
379 }
380 }
381
382 void PepperSession::SetState(State new_state) {
383 DCHECK(CalledOnValidThread());
384
385 if (new_state != state_) {
386 DCHECK_NE(state_, CLOSED);
387 DCHECK_NE(state_, FAILED);
388
389 state_ = new_state;
390 if (state_change_callback_.get())
391 state_change_callback_->Run(new_state);
392 }
393 }
394
395 } // namespace protocol
396 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/protocol/pepper_session.h ('k') | remoting/protocol/pepper_session_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698