OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/libjingle/session/sessionmanager.h" | |
12 | |
13 #include "webrtc/base/common.h" | |
14 #include "webrtc/base/helpers.h" | |
15 #include "webrtc/base/logging.h" | |
16 #include "webrtc/base/scoped_ptr.h" | |
17 #include "webrtc/base/stringencode.h" | |
18 #include "webrtc/libjingle/session/p2ptransportparser.h" | |
19 #include "webrtc/libjingle/session/sessionmessages.h" | |
20 #include "webrtc/libjingle/xmpp/constants.h" | |
21 #include "webrtc/libjingle/xmpp/jid.h" | |
22 #include "webrtc/p2p/base/constants.h" | |
23 | |
24 namespace cricket { | |
25 | |
26 bool BadMessage(const buzz::QName type, | |
27 const std::string& text, | |
28 MessageError* err) { | |
29 err->SetType(type); | |
30 err->SetText(text); | |
31 return false; | |
32 } | |
33 | |
34 | |
35 Session::Session(SessionManager* session_manager, | |
36 const std::string& local_name, | |
37 const std::string& initiator_name, | |
38 const std::string& sid, | |
39 const std::string& content_type, | |
40 SessionClient* client) | |
41 : BaseSession(session_manager->signaling_thread(), | |
42 session_manager->worker_thread(), | |
43 session_manager->port_allocator(), | |
44 sid, content_type, initiator_name == local_name) { | |
45 ASSERT(client != NULL); | |
46 session_manager_ = session_manager; | |
47 local_name_ = local_name; | |
48 initiator_name_ = initiator_name; | |
49 transport_parser_ = new P2PTransportParser(); | |
50 client_ = client; | |
51 initiate_acked_ = false; | |
52 current_protocol_ = PROTOCOL_HYBRID; | |
53 } | |
54 | |
55 Session::~Session() { | |
56 delete transport_parser_; | |
57 } | |
58 | |
59 bool Session::Initiate(const std::string& to, | |
60 const SessionDescription* sdesc) { | |
61 ASSERT(signaling_thread()->IsCurrent()); | |
62 SessionError error; | |
63 | |
64 // Only from STATE_INIT | |
65 if (state() != STATE_INIT) | |
66 return false; | |
67 | |
68 // Setup for signaling. | |
69 set_remote_name(to); | |
70 set_local_description(sdesc); | |
71 if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()), | |
72 &error)) { | |
73 LOG(LS_ERROR) << "Could not create transports: " << error.text; | |
74 return false; | |
75 } | |
76 | |
77 if (!SendInitiateMessage(sdesc, &error)) { | |
78 LOG(LS_ERROR) << "Could not send initiate message: " << error.text; | |
79 return false; | |
80 } | |
81 | |
82 // We need to connect transport proxy and impl here so that we can process | |
83 // the TransportDescriptions. | |
84 SpeculativelyConnectAllTransportChannels(); | |
85 | |
86 PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL); | |
87 SetState(Session::STATE_SENTINITIATE); | |
88 return true; | |
89 } | |
90 | |
91 bool Session::Accept(const SessionDescription* sdesc) { | |
92 ASSERT(signaling_thread()->IsCurrent()); | |
93 | |
94 // Only if just received initiate | |
95 if (state() != STATE_RECEIVEDINITIATE) | |
96 return false; | |
97 | |
98 // Setup for signaling. | |
99 set_local_description(sdesc); | |
100 | |
101 SessionError error; | |
102 if (!SendAcceptMessage(sdesc, &error)) { | |
103 LOG(LS_ERROR) << "Could not send accept message: " << error.text; | |
104 return false; | |
105 } | |
106 // TODO(juberti): Add BUNDLE support to transport-info messages. | |
107 PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL); | |
108 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. | |
109 SetState(Session::STATE_SENTACCEPT); | |
110 return true; | |
111 } | |
112 | |
113 bool Session::Reject(const std::string& reason) { | |
114 ASSERT(signaling_thread()->IsCurrent()); | |
115 | |
116 // Reject is sent in response to an initiate or modify, to reject the | |
117 // request | |
118 if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY) | |
119 return false; | |
120 | |
121 SessionError error; | |
122 if (!SendRejectMessage(reason, &error)) { | |
123 LOG(LS_ERROR) << "Could not send reject message: " << error.text; | |
124 return false; | |
125 } | |
126 | |
127 SetState(STATE_SENTREJECT); | |
128 return true; | |
129 } | |
130 | |
131 bool Session::TerminateWithReason(const std::string& reason) { | |
132 ASSERT(signaling_thread()->IsCurrent()); | |
133 | |
134 // Either side can terminate, at any time. | |
135 switch (state()) { | |
136 case STATE_SENTTERMINATE: | |
137 case STATE_RECEIVEDTERMINATE: | |
138 return false; | |
139 | |
140 case STATE_SENTREJECT: | |
141 case STATE_RECEIVEDREJECT: | |
142 // We don't need to send terminate if we sent or received a reject... | |
143 // it's implicit. | |
144 break; | |
145 | |
146 default: | |
147 SessionError error; | |
148 if (!SendTerminateMessage(reason, &error)) { | |
149 LOG(LS_ERROR) << "Could not send terminate message: " << error.text; | |
150 return false; | |
151 } | |
152 break; | |
153 } | |
154 | |
155 SetState(STATE_SENTTERMINATE); | |
156 return true; | |
157 } | |
158 | |
159 bool Session::SendInfoMessage(const XmlElements& elems, | |
160 const std::string& remote_name) { | |
161 ASSERT(signaling_thread()->IsCurrent()); | |
162 SessionError error; | |
163 if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) { | |
164 LOG(LS_ERROR) << "Could not send info message " << error.text; | |
165 return false; | |
166 } | |
167 return true; | |
168 } | |
169 | |
170 bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) { | |
171 XmlElements elems; | |
172 WriteError write_error; | |
173 if (!WriteDescriptionInfo(current_protocol_, | |
174 contents, | |
175 GetContentParsers(), | |
176 &elems, &write_error)) { | |
177 LOG(LS_ERROR) << "Could not write description info message: " | |
178 << write_error.text; | |
179 return false; | |
180 } | |
181 SessionError error; | |
182 if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) { | |
183 LOG(LS_ERROR) << "Could not send description info message: " | |
184 << error.text; | |
185 return false; | |
186 } | |
187 return true; | |
188 } | |
189 | |
190 TransportInfos Session::GetEmptyTransportInfos( | |
191 const ContentInfos& contents) const { | |
192 TransportInfos tinfos; | |
193 for (ContentInfos::const_iterator content = contents.begin(); | |
194 content != contents.end(); ++content) { | |
195 tinfos.push_back(TransportInfo(content->name, | |
196 TransportDescription(transport_type(), | |
197 std::string(), | |
198 std::string()))); | |
199 } | |
200 return tinfos; | |
201 } | |
202 | |
203 bool Session::OnRemoteCandidates( | |
204 const TransportInfos& tinfos, ParseError* error) { | |
205 for (TransportInfos::const_iterator tinfo = tinfos.begin(); | |
206 tinfo != tinfos.end(); ++tinfo) { | |
207 std::string str_error; | |
208 if (!BaseSession::OnRemoteCandidates( | |
209 tinfo->content_name, tinfo->description.candidates, &str_error)) { | |
210 return BadParse(str_error, error); | |
211 } | |
212 } | |
213 return true; | |
214 } | |
215 | |
216 bool Session::CreateTransportProxies(const TransportInfos& tinfos, | |
217 SessionError* error) { | |
218 for (TransportInfos::const_iterator tinfo = tinfos.begin(); | |
219 tinfo != tinfos.end(); ++tinfo) { | |
220 if (tinfo->description.transport_type != transport_type()) { | |
221 error->SetText("No supported transport in offer."); | |
222 return false; | |
223 } | |
224 | |
225 GetOrCreateTransportProxy(tinfo->content_name); | |
226 } | |
227 return true; | |
228 } | |
229 | |
230 TransportParserMap Session::GetTransportParsers() { | |
231 TransportParserMap parsers; | |
232 parsers[transport_type()] = transport_parser_; | |
233 return parsers; | |
234 } | |
235 | |
236 CandidateTranslatorMap Session::GetCandidateTranslators() { | |
237 CandidateTranslatorMap translators; | |
238 // NOTE: This technique makes it impossible to parse G-ICE | |
239 // candidates in session-initiate messages because the channels | |
240 // aren't yet created at that point. Since we don't use candidates | |
241 // in session-initiate messages, we should be OK. Once we switch to | |
242 // ICE, this translation shouldn't be necessary. | |
243 for (TransportMap::const_iterator iter = transport_proxies().begin(); | |
244 iter != transport_proxies().end(); ++iter) { | |
245 translators[iter->first] = iter->second; | |
246 } | |
247 return translators; | |
248 } | |
249 | |
250 ContentParserMap Session::GetContentParsers() { | |
251 ContentParserMap parsers; | |
252 parsers[content_type()] = client_; | |
253 // We need to be able parse both RTP-based and SCTP-based Jingle | |
254 // with the same client. | |
255 if (content_type() == NS_JINGLE_RTP) { | |
256 parsers[NS_JINGLE_DRAFT_SCTP] = client_; | |
257 } | |
258 return parsers; | |
259 } | |
260 | |
261 void Session::OnTransportRequestSignaling(Transport* transport) { | |
262 ASSERT(signaling_thread()->IsCurrent()); | |
263 TransportProxy* transproxy = GetTransportProxy(transport); | |
264 ASSERT(transproxy != NULL); | |
265 if (transproxy) { | |
266 // Reset candidate allocation status for the transport proxy. | |
267 transproxy->set_candidates_allocated(false); | |
268 } | |
269 SignalRequestSignaling(this); | |
270 } | |
271 | |
272 void Session::OnTransportConnecting(Transport* transport) { | |
273 // This is an indication that we should begin watching the writability | |
274 // state of the transport. | |
275 OnTransportWritable(transport); | |
276 } | |
277 | |
278 void Session::OnTransportWritable(Transport* transport) { | |
279 ASSERT(signaling_thread()->IsCurrent()); | |
280 | |
281 // If the transport is not writable, start a timer to make sure that it | |
282 // becomes writable within a reasonable amount of time. If it does not, we | |
283 // terminate since we can't actually send data. If the transport is writable, | |
284 // cancel the timer. Note that writability transitions may occur repeatedly | |
285 // during the lifetime of the session. | |
286 signaling_thread()->Clear(this, MSG_TIMEOUT); | |
287 if (transport->HasChannels() && !transport->writable()) { | |
288 signaling_thread()->PostDelayed( | |
289 session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); | |
290 } | |
291 } | |
292 | |
293 void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy, | |
294 const Candidates& candidates) { | |
295 ASSERT(signaling_thread()->IsCurrent()); | |
296 if (transproxy != NULL) { | |
297 if (initiator() && !initiate_acked_) { | |
298 // TODO: This is to work around server re-ordering | |
299 // messages. We send the candidates once the session-initiate | |
300 // is acked. Once we have fixed the server to guarantee message | |
301 // order, we can remove this case. | |
302 transproxy->AddUnsentCandidates(candidates); | |
303 } else { | |
304 if (!transproxy->negotiated()) { | |
305 transproxy->AddSentCandidates(candidates); | |
306 } | |
307 SessionError error; | |
308 if (!SendTransportInfoMessage(transproxy, candidates, &error)) { | |
309 LOG(LS_ERROR) << "Could not send transport info message: " | |
310 << error.text; | |
311 return; | |
312 } | |
313 } | |
314 } | |
315 } | |
316 | |
317 void Session::OnIncomingMessage(const SessionMessage& msg) { | |
318 ASSERT(signaling_thread()->IsCurrent()); | |
319 ASSERT(state() == STATE_INIT || msg.from == remote_name()); | |
320 | |
321 if (current_protocol_== PROTOCOL_HYBRID) { | |
322 if (msg.protocol == PROTOCOL_GINGLE) { | |
323 current_protocol_ = PROTOCOL_GINGLE; | |
324 } else { | |
325 current_protocol_ = PROTOCOL_JINGLE; | |
326 } | |
327 } | |
328 | |
329 bool valid = false; | |
330 MessageError error; | |
331 switch (msg.type) { | |
332 case ACTION_SESSION_INITIATE: | |
333 valid = OnInitiateMessage(msg, &error); | |
334 break; | |
335 case ACTION_SESSION_INFO: | |
336 valid = OnInfoMessage(msg); | |
337 break; | |
338 case ACTION_SESSION_ACCEPT: | |
339 valid = OnAcceptMessage(msg, &error); | |
340 break; | |
341 case ACTION_SESSION_REJECT: | |
342 valid = OnRejectMessage(msg, &error); | |
343 break; | |
344 case ACTION_SESSION_TERMINATE: | |
345 valid = OnTerminateMessage(msg, &error); | |
346 break; | |
347 case ACTION_TRANSPORT_INFO: | |
348 valid = OnTransportInfoMessage(msg, &error); | |
349 break; | |
350 case ACTION_TRANSPORT_ACCEPT: | |
351 valid = OnTransportAcceptMessage(msg, &error); | |
352 break; | |
353 case ACTION_DESCRIPTION_INFO: | |
354 valid = OnDescriptionInfoMessage(msg, &error); | |
355 break; | |
356 default: | |
357 valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST, | |
358 "unknown session message type", | |
359 &error); | |
360 } | |
361 | |
362 if (valid) { | |
363 SendAcknowledgementMessage(msg.stanza); | |
364 } else { | |
365 SignalErrorMessage(this, msg.stanza, error.type, | |
366 "modify", error.text, NULL); | |
367 } | |
368 } | |
369 | |
370 void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza, | |
371 const buzz::XmlElement* response_stanza, | |
372 const SessionMessage& msg) { | |
373 ASSERT(signaling_thread()->IsCurrent()); | |
374 | |
375 if (msg.type == ACTION_SESSION_INITIATE) { | |
376 OnInitiateAcked(); | |
377 } | |
378 } | |
379 | |
380 void Session::OnInitiateAcked() { | |
381 // TODO: This is to work around server re-ordering | |
382 // messages. We send the candidates once the session-initiate | |
383 // is acked. Once we have fixed the server to guarantee message | |
384 // order, we can remove this case. | |
385 if (!initiate_acked_) { | |
386 initiate_acked_ = true; | |
387 SessionError error; | |
388 SendAllUnsentTransportInfoMessages(&error); | |
389 } | |
390 } | |
391 | |
392 void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, | |
393 const buzz::XmlElement* error_stanza) { | |
394 ASSERT(signaling_thread()->IsCurrent()); | |
395 | |
396 SessionMessage msg; | |
397 ParseError parse_error; | |
398 if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) { | |
399 LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text | |
400 << ":" << orig_stanza; | |
401 return; | |
402 } | |
403 | |
404 // If the error is a session redirect, call OnRedirectError, which will | |
405 // continue the session with a new remote JID. | |
406 SessionRedirect redirect; | |
407 if (FindSessionRedirect(error_stanza, &redirect)) { | |
408 SessionError error; | |
409 if (!OnRedirectError(redirect, &error)) { | |
410 // TODO: Should we send a message back? The standard | |
411 // says nothing about it. | |
412 std::ostringstream desc; | |
413 desc << "Failed to redirect: " << error.text; | |
414 LOG(LS_ERROR) << desc.str(); | |
415 SetError(ERROR_RESPONSE, desc.str()); | |
416 } | |
417 return; | |
418 } | |
419 | |
420 std::string error_type = "cancel"; | |
421 | |
422 const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); | |
423 if (error) { | |
424 error_type = error->Attr(buzz::QN_TYPE); | |
425 | |
426 LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n" | |
427 << "in response to:\n" << orig_stanza->Str(); | |
428 } else { | |
429 // don't crash if <error> is missing | |
430 LOG(LS_ERROR) << "Session error without <error/> element, ignoring"; | |
431 return; | |
432 } | |
433 | |
434 if (msg.type == ACTION_TRANSPORT_INFO) { | |
435 // Transport messages frequently generate errors because they are sent right | |
436 // when we detect a network failure. For that reason, we ignore such | |
437 // errors, because if we do not establish writability again, we will | |
438 // terminate anyway. The exceptions are transport-specific error tags, | |
439 // which we pass on to the respective transport. | |
440 } else if ((error_type != "continue") && (error_type != "wait")) { | |
441 // We do not set an error if the other side said it is okay to continue | |
442 // (possibly after waiting). These errors can be ignored. | |
443 SetError(ERROR_RESPONSE, ""); | |
444 } | |
445 } | |
446 | |
447 bool Session::OnInitiateMessage(const SessionMessage& msg, | |
448 MessageError* error) { | |
449 if (!CheckState(STATE_INIT, error)) | |
450 return false; | |
451 | |
452 SessionInitiate init; | |
453 if (!ParseSessionInitiate(msg.protocol, msg.action_elem, | |
454 GetContentParsers(), GetTransportParsers(), | |
455 GetCandidateTranslators(), | |
456 &init, error)) | |
457 return false; | |
458 | |
459 SessionError session_error; | |
460 if (!CreateTransportProxies(init.transports, &session_error)) { | |
461 return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE, | |
462 session_error.text, error); | |
463 } | |
464 | |
465 set_remote_name(msg.from); | |
466 set_initiator_name(msg.initiator); | |
467 set_remote_description(new SessionDescription(init.ClearContents(), | |
468 init.transports, | |
469 init.groups)); | |
470 // Updating transport with TransportDescription. | |
471 PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL); | |
472 SetState(STATE_RECEIVEDINITIATE); | |
473 | |
474 // Users of Session may listen to state change and call Reject(). | |
475 if (state() != STATE_SENTREJECT) { | |
476 if (!OnRemoteCandidates(init.transports, error)) | |
477 return false; | |
478 | |
479 // TODO(juberti): Auto-generate and push down the local transport answer. | |
480 // This is necessary for trickling to work with RFC 5245 ICE. | |
481 } | |
482 return true; | |
483 } | |
484 | |
485 bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) { | |
486 if (!CheckState(STATE_SENTINITIATE, error)) | |
487 return false; | |
488 | |
489 SessionAccept accept; | |
490 if (!ParseSessionAccept(msg.protocol, msg.action_elem, | |
491 GetContentParsers(), GetTransportParsers(), | |
492 GetCandidateTranslators(), | |
493 &accept, error)) { | |
494 return false; | |
495 } | |
496 | |
497 // If we get an accept, we can assume the initiate has been | |
498 // received, even if we haven't gotten an IQ response. | |
499 OnInitiateAcked(); | |
500 | |
501 set_remote_description(new SessionDescription(accept.ClearContents(), | |
502 accept.transports, | |
503 accept.groups)); | |
504 // Updating transport with TransportDescription. | |
505 PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL); | |
506 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. | |
507 SetState(STATE_RECEIVEDACCEPT); | |
508 | |
509 if (!OnRemoteCandidates(accept.transports, error)) | |
510 return false; | |
511 | |
512 return true; | |
513 } | |
514 | |
515 bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) { | |
516 if (!CheckState(STATE_SENTINITIATE, error)) | |
517 return false; | |
518 | |
519 SetState(STATE_RECEIVEDREJECT); | |
520 return true; | |
521 } | |
522 | |
523 bool Session::OnInfoMessage(const SessionMessage& msg) { | |
524 SignalInfoMessage(this, msg.action_elem); | |
525 return true; | |
526 } | |
527 | |
528 bool Session::OnTerminateMessage(const SessionMessage& msg, | |
529 MessageError* error) { | |
530 SessionTerminate term; | |
531 if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error)) | |
532 return false; | |
533 | |
534 SignalReceivedTerminateReason(this, term.reason); | |
535 if (term.debug_reason != buzz::STR_EMPTY) { | |
536 LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason; | |
537 } | |
538 | |
539 SetState(STATE_RECEIVEDTERMINATE); | |
540 return true; | |
541 } | |
542 | |
543 bool Session::OnTransportInfoMessage(const SessionMessage& msg, | |
544 MessageError* error) { | |
545 TransportInfos tinfos; | |
546 if (!ParseTransportInfos(msg.protocol, msg.action_elem, | |
547 initiator_description()->contents(), | |
548 GetTransportParsers(), GetCandidateTranslators(), | |
549 &tinfos, error)) | |
550 return false; | |
551 | |
552 if (!OnRemoteCandidates(tinfos, error)) | |
553 return false; | |
554 | |
555 return true; | |
556 } | |
557 | |
558 bool Session::OnTransportAcceptMessage(const SessionMessage& msg, | |
559 MessageError* error) { | |
560 // TODO: Currently here only for compatibility with | |
561 // Gingle 1.1 clients (notably, Google Voice). | |
562 return true; | |
563 } | |
564 | |
565 bool Session::OnDescriptionInfoMessage(const SessionMessage& msg, | |
566 MessageError* error) { | |
567 if (!CheckState(STATE_INPROGRESS, error)) | |
568 return false; | |
569 | |
570 DescriptionInfo description_info; | |
571 if (!ParseDescriptionInfo(msg.protocol, msg.action_elem, | |
572 GetContentParsers(), GetTransportParsers(), | |
573 GetCandidateTranslators(), | |
574 &description_info, error)) { | |
575 return false; | |
576 } | |
577 | |
578 ContentInfos& updated_contents = description_info.contents; | |
579 | |
580 // TODO: Currently, reflector sends back | |
581 // video stream updates even for an audio-only call, which causes | |
582 // this to fail. Put this back once reflector is fixed. | |
583 // | |
584 // ContentInfos::iterator it; | |
585 // First, ensure all updates are valid before modifying remote_description_. | |
586 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { | |
587 // if (remote_description()->GetContentByName(it->name) == NULL) { | |
588 // return false; | |
589 // } | |
590 // } | |
591 | |
592 // TODO: We used to replace contents from an update, but | |
593 // that no longer works with partial updates. We need to figure out | |
594 // a way to merge patial updates into contents. For now, users of | |
595 // Session should listen to SignalRemoteDescriptionUpdate and handle | |
596 // updates. They should not expect remote_description to be the | |
597 // latest value. | |
598 // | |
599 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { | |
600 // remote_description()->RemoveContentByName(it->name); | |
601 // remote_description()->AddContent(it->name, it->type, it->description); | |
602 // } | |
603 // } | |
604 | |
605 SignalRemoteDescriptionUpdate(this, updated_contents); | |
606 return true; | |
607 } | |
608 | |
609 bool BareJidsEqual(const std::string& name1, | |
610 const std::string& name2) { | |
611 buzz::Jid jid1(name1); | |
612 buzz::Jid jid2(name2); | |
613 | |
614 return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2); | |
615 } | |
616 | |
617 bool Session::OnRedirectError(const SessionRedirect& redirect, | |
618 SessionError* error) { | |
619 MessageError message_error; | |
620 if (!CheckState(STATE_SENTINITIATE, &message_error)) { | |
621 return BadWrite(message_error.text, error); | |
622 } | |
623 | |
624 if (!BareJidsEqual(remote_name(), redirect.target)) | |
625 return BadWrite("Redirection not allowed: must be the same bare jid.", | |
626 error); | |
627 | |
628 // When we receive a redirect, we point the session at the new JID | |
629 // and resend the candidates. | |
630 set_remote_name(redirect.target); | |
631 return (SendInitiateMessage(local_description(), error) && | |
632 ResendAllTransportInfoMessages(error)); | |
633 } | |
634 | |
635 bool Session::CheckState(State expected, MessageError* error) { | |
636 if (state() != expected) { | |
637 // The server can deliver messages out of order/repeated for various | |
638 // reasons. For example, if the server does not recive our iq response, | |
639 // it could assume that the iq it sent was lost, and will then send | |
640 // it again. Ideally, we should implement reliable messaging with | |
641 // duplicate elimination. | |
642 return BadMessage(buzz::QN_STANZA_NOT_ALLOWED, | |
643 "message not allowed in current state", | |
644 error); | |
645 } | |
646 return true; | |
647 } | |
648 | |
649 void Session::SetError(Error error, const std::string& error_desc) { | |
650 BaseSession::SetError(error, error_desc); | |
651 if (error != ERROR_NONE) | |
652 signaling_thread()->Post(this, MSG_ERROR); | |
653 } | |
654 | |
655 void Session::OnMessage(rtc::Message* pmsg) { | |
656 // preserve this because BaseSession::OnMessage may modify it | |
657 State orig_state = state(); | |
658 | |
659 BaseSession::OnMessage(pmsg); | |
660 | |
661 switch (pmsg->message_id) { | |
662 case MSG_ERROR: | |
663 TerminateWithReason(STR_TERMINATE_ERROR); | |
664 break; | |
665 | |
666 case MSG_STATE: | |
667 switch (orig_state) { | |
668 case STATE_SENTREJECT: | |
669 case STATE_RECEIVEDREJECT: | |
670 // Assume clean termination. | |
671 Terminate(); | |
672 break; | |
673 | |
674 case STATE_SENTTERMINATE: | |
675 case STATE_RECEIVEDTERMINATE: | |
676 session_manager_->DestroySession(this); | |
677 break; | |
678 | |
679 default: | |
680 // Explicitly ignoring some states here. | |
681 break; | |
682 } | |
683 break; | |
684 } | |
685 } | |
686 | |
687 bool Session::SendInitiateMessage(const SessionDescription* sdesc, | |
688 SessionError* error) { | |
689 SessionInitiate init; | |
690 init.contents = sdesc->contents(); | |
691 init.transports = GetEmptyTransportInfos(init.contents); | |
692 init.groups = sdesc->groups(); | |
693 return SendMessage(ACTION_SESSION_INITIATE, init, error); | |
694 } | |
695 | |
696 bool Session::WriteSessionAction( | |
697 SignalingProtocol protocol, const SessionInitiate& init, | |
698 XmlElements* elems, WriteError* error) { | |
699 return WriteSessionInitiate(protocol, init.contents, init.transports, | |
700 GetContentParsers(), GetTransportParsers(), | |
701 GetCandidateTranslators(), init.groups, | |
702 elems, error); | |
703 } | |
704 | |
705 bool Session::SendAcceptMessage(const SessionDescription* sdesc, | |
706 SessionError* error) { | |
707 XmlElements elems; | |
708 if (!WriteSessionAccept(current_protocol_, | |
709 sdesc->contents(), | |
710 GetEmptyTransportInfos(sdesc->contents()), | |
711 GetContentParsers(), GetTransportParsers(), | |
712 GetCandidateTranslators(), sdesc->groups(), | |
713 &elems, error)) { | |
714 return false; | |
715 } | |
716 return SendMessage(ACTION_SESSION_ACCEPT, elems, error); | |
717 } | |
718 | |
719 bool Session::SendRejectMessage(const std::string& reason, | |
720 SessionError* error) { | |
721 SessionTerminate term(reason); | |
722 return SendMessage(ACTION_SESSION_REJECT, term, error); | |
723 } | |
724 | |
725 bool Session::SendTerminateMessage(const std::string& reason, | |
726 SessionError* error) { | |
727 SessionTerminate term(reason); | |
728 return SendMessage(ACTION_SESSION_TERMINATE, term, error); | |
729 } | |
730 | |
731 bool Session::WriteSessionAction(SignalingProtocol protocol, | |
732 const SessionTerminate& term, | |
733 XmlElements* elems, WriteError* error) { | |
734 WriteSessionTerminate(protocol, term, elems); | |
735 return true; | |
736 } | |
737 | |
738 bool Session::SendTransportInfoMessage(const TransportInfo& tinfo, | |
739 SessionError* error) { | |
740 return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error); | |
741 } | |
742 | |
743 bool Session::SendTransportInfoMessage(const TransportProxy* transproxy, | |
744 const Candidates& candidates, | |
745 SessionError* error) { | |
746 return SendTransportInfoMessage(TransportInfo(transproxy->content_name(), | |
747 TransportDescription(transproxy->type(), std::vector<std::string>(), | |
748 std::string(), std::string(), ICEMODE_FULL, | |
749 CONNECTIONROLE_NONE, NULL, candidates)), error); | |
750 } | |
751 | |
752 bool Session::WriteSessionAction(SignalingProtocol protocol, | |
753 const TransportInfo& tinfo, | |
754 XmlElements* elems, WriteError* error) { | |
755 TransportInfos tinfos; | |
756 tinfos.push_back(tinfo); | |
757 return WriteTransportInfos(protocol, tinfos, | |
758 GetTransportParsers(), GetCandidateTranslators(), | |
759 elems, error); | |
760 } | |
761 | |
762 bool Session::ResendAllTransportInfoMessages(SessionError* error) { | |
763 for (TransportMap::const_iterator iter = transport_proxies().begin(); | |
764 iter != transport_proxies().end(); ++iter) { | |
765 TransportProxy* transproxy = iter->second; | |
766 if (transproxy->sent_candidates().size() > 0) { | |
767 if (!SendTransportInfoMessage( | |
768 transproxy, transproxy->sent_candidates(), error)) { | |
769 LOG(LS_ERROR) << "Could not resend transport info messages: " | |
770 << error->text; | |
771 return false; | |
772 } | |
773 transproxy->ClearSentCandidates(); | |
774 } | |
775 } | |
776 return true; | |
777 } | |
778 | |
779 bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) { | |
780 for (TransportMap::const_iterator iter = transport_proxies().begin(); | |
781 iter != transport_proxies().end(); ++iter) { | |
782 TransportProxy* transproxy = iter->second; | |
783 if (transproxy->unsent_candidates().size() > 0) { | |
784 if (!SendTransportInfoMessage( | |
785 transproxy, transproxy->unsent_candidates(), error)) { | |
786 LOG(LS_ERROR) << "Could not send unsent transport info messages: " | |
787 << error->text; | |
788 return false; | |
789 } | |
790 transproxy->ClearUnsentCandidates(); | |
791 } | |
792 } | |
793 return true; | |
794 } | |
795 | |
796 bool Session::SendMessage(ActionType type, const XmlElements& action_elems, | |
797 SessionError* error) { | |
798 return SendMessage(type, action_elems, remote_name(), error); | |
799 } | |
800 | |
801 bool Session::SendMessage(ActionType type, const XmlElements& action_elems, | |
802 const std::string& remote_name, SessionError* error) { | |
803 rtc::scoped_ptr<buzz::XmlElement> stanza( | |
804 new buzz::XmlElement(buzz::QN_IQ)); | |
805 | |
806 SessionMessage msg(current_protocol_, type, id(), initiator_name()); | |
807 msg.to = remote_name; | |
808 WriteSessionMessage(msg, action_elems, stanza.get()); | |
809 | |
810 SignalOutgoingMessage(this, stanza.get()); | |
811 return true; | |
812 } | |
813 | |
814 template <typename Action> | |
815 bool Session::SendMessage(ActionType type, const Action& action, | |
816 SessionError* error) { | |
817 rtc::scoped_ptr<buzz::XmlElement> stanza( | |
818 new buzz::XmlElement(buzz::QN_IQ)); | |
819 if (!WriteActionMessage(type, action, stanza.get(), error)) | |
820 return false; | |
821 | |
822 SignalOutgoingMessage(this, stanza.get()); | |
823 return true; | |
824 } | |
825 | |
826 template <typename Action> | |
827 bool Session::WriteActionMessage(ActionType type, const Action& action, | |
828 buzz::XmlElement* stanza, | |
829 WriteError* error) { | |
830 if (current_protocol_ == PROTOCOL_HYBRID) { | |
831 if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error)) | |
832 return false; | |
833 if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error)) | |
834 return false; | |
835 } else { | |
836 if (!WriteActionMessage(current_protocol_, type, action, stanza, error)) | |
837 return false; | |
838 } | |
839 return true; | |
840 } | |
841 | |
842 template <typename Action> | |
843 bool Session::WriteActionMessage(SignalingProtocol protocol, | |
844 ActionType type, const Action& action, | |
845 buzz::XmlElement* stanza, WriteError* error) { | |
846 XmlElements action_elems; | |
847 if (!WriteSessionAction(protocol, action, &action_elems, error)) | |
848 return false; | |
849 | |
850 SessionMessage msg(protocol, type, id(), initiator_name()); | |
851 msg.to = remote_name(); | |
852 | |
853 WriteSessionMessage(msg, action_elems, stanza); | |
854 return true; | |
855 } | |
856 | |
857 void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) { | |
858 rtc::scoped_ptr<buzz::XmlElement> ack( | |
859 new buzz::XmlElement(buzz::QN_IQ)); | |
860 ack->SetAttr(buzz::QN_TO, remote_name()); | |
861 ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); | |
862 ack->SetAttr(buzz::QN_TYPE, "result"); | |
863 | |
864 SignalOutgoingMessage(this, ack.get()); | |
865 } | |
866 | |
867 SessionManager::SessionManager(PortAllocator *allocator, | |
868 rtc::Thread *worker) { | |
869 allocator_ = allocator; | |
870 signaling_thread_ = rtc::Thread::Current(); | |
871 if (worker == NULL) { | |
872 worker_thread_ = rtc::Thread::Current(); | |
873 } else { | |
874 worker_thread_ = worker; | |
875 } | |
876 timeout_ = 50; | |
877 } | |
878 | |
879 SessionManager::~SessionManager() { | |
880 // Note: Session::Terminate occurs asynchronously, so it's too late to | |
881 // delete them now. They better be all gone. | |
882 ASSERT(session_map_.empty()); | |
883 // TerminateAll(); | |
884 SignalDestroyed(); | |
885 } | |
886 | |
887 void SessionManager::AddClient(const std::string& content_type, | |
888 SessionClient* client) { | |
889 ASSERT(client_map_.find(content_type) == client_map_.end()); | |
890 client_map_[content_type] = client; | |
891 } | |
892 | |
893 void SessionManager::RemoveClient(const std::string& content_type) { | |
894 ClientMap::iterator iter = client_map_.find(content_type); | |
895 ASSERT(iter != client_map_.end()); | |
896 client_map_.erase(iter); | |
897 } | |
898 | |
899 SessionClient* SessionManager::GetClient(const std::string& content_type) { | |
900 ClientMap::iterator iter = client_map_.find(content_type); | |
901 return (iter != client_map_.end()) ? iter->second : NULL; | |
902 } | |
903 | |
904 Session* SessionManager::CreateSession(const std::string& local_name, | |
905 const std::string& content_type) { | |
906 std::string id; | |
907 return CreateSession(id, local_name, content_type); | |
908 } | |
909 | |
910 Session* SessionManager::CreateSession(const std::string& id, | |
911 const std::string& local_name, | |
912 const std::string& content_type) { | |
913 std::string sid = | |
914 id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id; | |
915 return CreateSession(local_name, local_name, sid, content_type, false); | |
916 } | |
917 | |
918 Session* SessionManager::CreateSession( | |
919 const std::string& local_name, const std::string& initiator_name, | |
920 const std::string& sid, const std::string& content_type, | |
921 bool received_initiate) { | |
922 SessionClient* client = GetClient(content_type); | |
923 ASSERT(client != NULL); | |
924 | |
925 Session* session = new Session(this, local_name, initiator_name, | |
926 sid, content_type, client); | |
927 session->SetIdentity(transport_desc_factory_.identity()); | |
928 session_map_[session->id()] = session; | |
929 session->SignalRequestSignaling.connect( | |
930 this, &SessionManager::OnRequestSignaling); | |
931 session->SignalOutgoingMessage.connect( | |
932 this, &SessionManager::OnOutgoingMessage); | |
933 session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage); | |
934 SignalSessionCreate(session, received_initiate); | |
935 session->client()->OnSessionCreate(session, received_initiate); | |
936 return session; | |
937 } | |
938 | |
939 void SessionManager::DestroySession(Session* session) { | |
940 if (session != NULL) { | |
941 SessionMap::iterator it = session_map_.find(session->id()); | |
942 if (it != session_map_.end()) { | |
943 SignalSessionDestroy(session); | |
944 session->client()->OnSessionDestroy(session); | |
945 session_map_.erase(it); | |
946 delete session; | |
947 } | |
948 } | |
949 } | |
950 | |
951 Session* SessionManager::GetSession(const std::string& sid) { | |
952 SessionMap::iterator it = session_map_.find(sid); | |
953 if (it != session_map_.end()) | |
954 return it->second; | |
955 return NULL; | |
956 } | |
957 | |
958 void SessionManager::TerminateAll() { | |
959 while (session_map_.begin() != session_map_.end()) { | |
960 Session* session = session_map_.begin()->second; | |
961 session->Terminate(); | |
962 } | |
963 } | |
964 | |
965 bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { | |
966 return cricket::IsSessionMessage(stanza); | |
967 } | |
968 | |
969 Session* SessionManager::FindSession(const std::string& sid, | |
970 const std::string& remote_name) { | |
971 SessionMap::iterator iter = session_map_.find(sid); | |
972 if (iter == session_map_.end()) | |
973 return NULL; | |
974 | |
975 Session* session = iter->second; | |
976 if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name())) | |
977 return NULL; | |
978 | |
979 return session; | |
980 } | |
981 | |
982 void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) { | |
983 SessionMessage msg; | |
984 ParseError error; | |
985 | |
986 if (!ParseSessionMessage(stanza, &msg, &error)) { | |
987 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
988 error.text, NULL); | |
989 return; | |
990 } | |
991 | |
992 Session* session = FindSession(msg.sid, msg.from); | |
993 if (session) { | |
994 session->OnIncomingMessage(msg); | |
995 return; | |
996 } | |
997 if (msg.type != ACTION_SESSION_INITIATE) { | |
998 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
999 "unknown session", NULL); | |
1000 return; | |
1001 } | |
1002 | |
1003 std::string content_type; | |
1004 if (!ParseContentType(msg.protocol, msg.action_elem, | |
1005 &content_type, &error)) { | |
1006 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
1007 error.text, NULL); | |
1008 return; | |
1009 } | |
1010 | |
1011 if (!GetClient(content_type)) { | |
1012 SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", | |
1013 "unknown content type: " + content_type, NULL); | |
1014 return; | |
1015 } | |
1016 | |
1017 session = CreateSession(msg.to, msg.initiator, msg.sid, | |
1018 content_type, true); | |
1019 session->OnIncomingMessage(msg); | |
1020 } | |
1021 | |
1022 void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza, | |
1023 const buzz::XmlElement* response_stanza) { | |
1024 if (orig_stanza == NULL || response_stanza == NULL) { | |
1025 return; | |
1026 } | |
1027 | |
1028 SessionMessage msg; | |
1029 ParseError error; | |
1030 if (!ParseSessionMessage(orig_stanza, &msg, &error)) { | |
1031 LOG(LS_WARNING) << "Error parsing incoming response: " << error.text | |
1032 << ":" << orig_stanza; | |
1033 return; | |
1034 } | |
1035 | |
1036 Session* session = FindSession(msg.sid, msg.to); | |
1037 if (!session) { | |
1038 // Also try the QN_FROM in the response stanza, in case we sent the request | |
1039 // to a bare JID but got the response from a full JID. | |
1040 std::string ack_from = response_stanza->Attr(buzz::QN_FROM); | |
1041 session = FindSession(msg.sid, ack_from); | |
1042 } | |
1043 if (session) { | |
1044 session->OnIncomingResponse(orig_stanza, response_stanza, msg); | |
1045 } | |
1046 } | |
1047 | |
1048 void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, | |
1049 const buzz::XmlElement* error_stanza) { | |
1050 SessionMessage msg; | |
1051 ParseError error; | |
1052 if (!ParseSessionMessage(orig_stanza, &msg, &error)) { | |
1053 return; // TODO: log somewhere? | |
1054 } | |
1055 | |
1056 Session* session = FindSession(msg.sid, msg.to); | |
1057 if (session) { | |
1058 rtc::scoped_ptr<buzz::XmlElement> synthetic_error; | |
1059 if (!error_stanza) { | |
1060 // A failed send is semantically equivalent to an error response, so we | |
1061 // can just turn the former into the latter. | |
1062 synthetic_error.reset( | |
1063 CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, | |
1064 "cancel", "Recipient did not respond", NULL)); | |
1065 error_stanza = synthetic_error.get(); | |
1066 } | |
1067 | |
1068 session->OnFailedSend(orig_stanza, error_stanza); | |
1069 } | |
1070 } | |
1071 | |
1072 void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, | |
1073 const buzz::QName& name, | |
1074 const std::string& type, | |
1075 const std::string& text, | |
1076 const buzz::XmlElement* extra_info) { | |
1077 rtc::scoped_ptr<buzz::XmlElement> msg( | |
1078 CreateErrorMessage(stanza, name, type, text, extra_info)); | |
1079 SignalOutgoingMessage(this, msg.get()); | |
1080 } | |
1081 | |
1082 buzz::XmlElement* SessionManager::CreateErrorMessage( | |
1083 const buzz::XmlElement* stanza, | |
1084 const buzz::QName& name, | |
1085 const std::string& type, | |
1086 const std::string& text, | |
1087 const buzz::XmlElement* extra_info) { | |
1088 buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); | |
1089 iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); | |
1090 iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); | |
1091 iq->SetAttr(buzz::QN_TYPE, "error"); | |
1092 | |
1093 CopyXmlChildren(stanza, iq); | |
1094 | |
1095 buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); | |
1096 error->SetAttr(buzz::QN_TYPE, type); | |
1097 iq->AddElement(error); | |
1098 | |
1099 // If the error name is not in the standard namespace, we have to first add | |
1100 // some error from that namespace. | |
1101 if (name.Namespace() != buzz::NS_STANZA) { | |
1102 error->AddElement( | |
1103 new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); | |
1104 } | |
1105 error->AddElement(new buzz::XmlElement(name)); | |
1106 | |
1107 if (extra_info) | |
1108 error->AddElement(new buzz::XmlElement(*extra_info)); | |
1109 | |
1110 if (text.size() > 0) { | |
1111 // It's okay to always use English here. This text is for debugging | |
1112 // purposes only. | |
1113 buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); | |
1114 text_elem->SetAttr(buzz::QN_XML_LANG, "en"); | |
1115 text_elem->SetBodyText(text); | |
1116 error->AddElement(text_elem); | |
1117 } | |
1118 | |
1119 // TODO: Should we include error codes as well for SIP compatibility? | |
1120 | |
1121 return iq; | |
1122 } | |
1123 | |
1124 void SessionManager::OnOutgoingMessage(Session* session, | |
1125 const buzz::XmlElement* stanza) { | |
1126 SignalOutgoingMessage(this, stanza); | |
1127 } | |
1128 | |
1129 void SessionManager::OnErrorMessage(BaseSession* session, | |
1130 const buzz::XmlElement* stanza, | |
1131 const buzz::QName& name, | |
1132 const std::string& type, | |
1133 const std::string& text, | |
1134 const buzz::XmlElement* extra_info) { | |
1135 SendErrorMessage(stanza, name, type, text, extra_info); | |
1136 } | |
1137 | |
1138 void SessionManager::OnSignalingReady() { | |
1139 for (SessionMap::iterator it = session_map_.begin(); | |
1140 it != session_map_.end(); | |
1141 ++it) { | |
1142 it->second->OnSignalingReady(); | |
1143 } | |
1144 } | |
1145 | |
1146 void SessionManager::OnRequestSignaling(Session* session) { | |
1147 SignalRequestSignaling(); | |
1148 } | |
1149 | |
1150 } // namespace cricket | |
OLD | NEW |