summaryrefslogtreecommitdiff
path: root/p2p/base/session.h
blob: 756bbe0ad24f4361cb497ac289cfb58319dd8632 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
/*
 * libjingle
 * Copyright 2004--2005, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef TALK_P2P_BASE_SESSION_H_
#define TALK_P2P_BASE_SESSION_H_

#include <list>
#include <map>
#include <string>
#include <vector>

#include "talk/p2p/base/parsing.h"
#include "talk/p2p/base/port.h"
#include "talk/p2p/base/sessionclient.h"
#include "talk/p2p/base/sessionmanager.h"
#include "talk/p2p/base/sessionmessages.h"
#include "talk/p2p/base/transport.h"
#include "talk/xmllite/xmlelement.h"
#include "talk/xmpp/constants.h"
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/base/socketaddress.h"

namespace cricket {

class BaseSession;
class P2PTransportChannel;
class Transport;
class TransportChannel;
class TransportChannelProxy;
class TransportChannelImpl;

typedef rtc::RefCountedObject<rtc::scoped_ptr<Transport> >
TransportWrapper;

// Used for errors that will send back a specific error message to the
// remote peer.  We add "type" to the errors because it's needed for
// SignalErrorMessage.
struct MessageError : ParseError {
  buzz::QName type;

  // if unset, assume type is a parse error
  MessageError() : ParseError(), type(buzz::QN_STANZA_BAD_REQUEST) {}

  void SetType(const buzz::QName type) {
    this->type = type;
  }
};

// Used for errors that may be returned by public session methods that
// can fail.
// TODO: Use this error in Session::Initiate and
// Session::Accept.
struct SessionError : WriteError {
};

// Bundles a Transport and ChannelMap together. ChannelMap is used to
// create transport channels before receiving or sending a session
// initiate, and for speculatively connecting channels.  Previously, a
// session had one ChannelMap and transport.  Now, with multiple
// transports per session, we need multiple ChannelMaps as well.

typedef std::map<int, TransportChannelProxy*> ChannelMap;

class TransportProxy : public sigslot::has_slots<>,
                       public CandidateTranslator {
 public:
  TransportProxy(
      rtc::Thread* worker_thread,
      const std::string& sid,
      const std::string& content_name,
      TransportWrapper* transport)
      : worker_thread_(worker_thread),
        sid_(sid),
        content_name_(content_name),
        transport_(transport),
        connecting_(false),
        negotiated_(false),
        sent_candidates_(false),
        candidates_allocated_(false),
        local_description_set_(false),
        remote_description_set_(false) {
    transport_->get()->SignalCandidatesReady.connect(
        this, &TransportProxy::OnTransportCandidatesReady);
  }
  ~TransportProxy();

  const std::string& content_name() const { return content_name_; }
  // TODO(juberti): It's not good form to expose the object you're wrapping,
  // since callers can mutate it. Can we make this return a const Transport*?
  Transport* impl() const { return transport_->get(); }

  const std::string& type() const;
  bool negotiated() const { return negotiated_; }
  const Candidates& sent_candidates() const { return sent_candidates_; }
  const Candidates& unsent_candidates() const { return unsent_candidates_; }
  bool candidates_allocated() const { return candidates_allocated_; }
  void set_candidates_allocated(bool allocated) {
    candidates_allocated_ = allocated;
  }

  TransportChannel* GetChannel(int component);
  TransportChannel* CreateChannel(const std::string& channel_name,
                                  int component);
  bool HasChannel(int component);
  void DestroyChannel(int component);

  void AddSentCandidates(const Candidates& candidates);
  void AddUnsentCandidates(const Candidates& candidates);
  void ClearSentCandidates() { sent_candidates_.clear(); }
  void ClearUnsentCandidates() { unsent_candidates_.clear(); }

  // Start the connection process for any channels, creating impls if needed.
  void ConnectChannels();
  // Hook up impls to the proxy channels. Doesn't change connect state.
  void CompleteNegotiation();

  // Mux this proxy onto the specified proxy's transport.
  bool SetupMux(TransportProxy* proxy);

  // Simple functions that thunk down to the same functions on Transport.
  void SetIceRole(IceRole role);
  void SetIdentity(rtc::SSLIdentity* identity);
  bool SetLocalTransportDescription(const TransportDescription& description,
                                    ContentAction action,
                                    std::string* error_desc);
  bool SetRemoteTransportDescription(const TransportDescription& description,
                                     ContentAction action,
                                     std::string* error_desc);
  void OnSignalingReady();
  bool OnRemoteCandidates(const Candidates& candidates, std::string* error);

  // CandidateTranslator methods.
  virtual bool GetChannelNameFromComponent(
      int component, std::string* channel_name) const;
  virtual bool GetComponentFromChannelName(
      const std::string& channel_name, int* component) const;

  // Called when a transport signals that it has new candidates.
  void OnTransportCandidatesReady(cricket::Transport* transport,
                                  const Candidates& candidates) {
    SignalCandidatesReady(this, candidates);
  }

  bool local_description_set() const {
    return local_description_set_;
  }
  bool remote_description_set() const {
    return remote_description_set_;
  }

  // Handles sending of ready candidates and receiving of remote candidates.
  sigslot::signal2<TransportProxy*,
                         const std::vector<Candidate>&> SignalCandidatesReady;

 private:
  TransportChannelProxy* GetChannelProxy(int component) const;
  TransportChannelProxy* GetChannelProxyByName(const std::string& name) const;

  TransportChannelImpl* GetOrCreateChannelProxyImpl(int component);
  TransportChannelImpl* GetOrCreateChannelProxyImpl_w(int component);

  // Manipulators of transportchannelimpl in channel proxy.
  void SetupChannelProxy(int component,
                           TransportChannelProxy* proxy);
  void SetupChannelProxy_w(int component,
                             TransportChannelProxy* proxy);
  void ReplaceChannelProxyImpl(TransportChannelProxy* proxy,
                               TransportChannelImpl* impl);
  void ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy,
                                 TransportChannelImpl* impl);

  rtc::Thread* const worker_thread_;
  const std::string sid_;
  const std::string content_name_;
  rtc::scoped_refptr<TransportWrapper> transport_;
  bool connecting_;
  bool negotiated_;
  ChannelMap channels_;
  Candidates sent_candidates_;
  Candidates unsent_candidates_;
  bool candidates_allocated_;
  bool local_description_set_;
  bool remote_description_set_;
};

typedef std::map<std::string, TransportProxy*> TransportMap;

// Statistics for all the transports of this session.
typedef std::map<std::string, TransportStats> TransportStatsMap;
typedef std::map<std::string, std::string> ProxyTransportMap;

struct SessionStats {
  ProxyTransportMap proxy_to_transport;
  TransportStatsMap transport_stats;
};

// A BaseSession manages general session state. This includes negotiation
// of both the application-level and network-level protocols:  the former
// defines what will be sent and the latter defines how it will be sent.  Each
// network-level protocol is represented by a Transport object.  Each Transport
// participates in the network-level negotiation.  The individual streams of
// packets are represented by TransportChannels.  The application-level protocol
// is represented by SessionDecription objects.
class BaseSession : public sigslot::has_slots<>,
                    public rtc::MessageHandler {
 public:
  enum {
    MSG_TIMEOUT = 0,
    MSG_ERROR,
    MSG_STATE,
  };

  enum State {
    STATE_INIT = 0,
    STATE_SENTINITIATE,       // sent initiate, waiting for Accept or Reject
    STATE_RECEIVEDINITIATE,   // received an initiate. Call Accept or Reject
    STATE_SENTPRACCEPT,       // sent provisional Accept
    STATE_SENTACCEPT,         // sent accept. begin connecting transport
    STATE_RECEIVEDPRACCEPT,   // received provisional Accept, waiting for Accept
    STATE_RECEIVEDACCEPT,     // received accept. begin connecting transport
    STATE_SENTMODIFY,         // sent modify, waiting for Accept or Reject
    STATE_RECEIVEDMODIFY,     // received modify, call Accept or Reject
    STATE_SENTREJECT,         // sent reject after receiving initiate
    STATE_RECEIVEDREJECT,     // received reject after sending initiate
    STATE_SENTREDIRECT,       // sent direct after receiving initiate
    STATE_SENTTERMINATE,      // sent terminate (any time / either side)
    STATE_RECEIVEDTERMINATE,  // received terminate (any time / either side)
    STATE_INPROGRESS,         // session accepted and in progress
    STATE_DEINIT,             // session is being destroyed
  };

  enum Error {
    ERROR_NONE = 0,       // no error
    ERROR_TIME = 1,       // no response to signaling
    ERROR_RESPONSE = 2,   // error during signaling
    ERROR_NETWORK = 3,    // network error, could not allocate network resources
    ERROR_CONTENT = 4,    // channel errors in SetLocalContent/SetRemoteContent
    ERROR_TRANSPORT = 5,  // transport error of some kind
  };

  // Convert State to a readable string.
  static std::string StateToString(State state);

  BaseSession(rtc::Thread* signaling_thread,
              rtc::Thread* worker_thread,
              PortAllocator* port_allocator,
              const std::string& sid,
              const std::string& content_type,
              bool initiator);
  virtual ~BaseSession();

  // These are const to allow them to be called from const methods.
  rtc::Thread* signaling_thread() const { return signaling_thread_; }
  rtc::Thread* worker_thread() const { return worker_thread_; }
  PortAllocator* port_allocator() const { return port_allocator_; }

  // The ID of this session.
  const std::string& id() const { return sid_; }

  // TODO(juberti): This data is largely redundant, as it can now be obtained
  // from local/remote_description(). Remove these functions and members.
  // Returns the XML namespace identifying the type of this session.
  const std::string& content_type() const { return content_type_; }
  // Returns the XML namespace identifying the transport used for this session.
  const std::string& transport_type() const { return transport_type_; }

  // Indicates whether we initiated this session.
  bool initiator() const { return initiator_; }

  // Returns the application-level description given by our client.
  // If we are the recipient, this will be NULL until we send an accept.
  const SessionDescription* local_description() const;

  // Returns the application-level description given by the other client.
  // If we are the initiator, this will be NULL until we receive an accept.
  const SessionDescription* remote_description() const;

  SessionDescription* remote_description();

  // Takes ownership of SessionDescription*
  void set_local_description(const SessionDescription* sdesc);

  // Takes ownership of SessionDescription*
  void set_remote_description(SessionDescription* sdesc);

  const SessionDescription* initiator_description() const;

  // Returns the current state of the session.  See the enum above for details.
  // Each time the state changes, we will fire this signal.
  State state() const { return state_; }
  sigslot::signal2<BaseSession* , State> SignalState;

  // Returns the last error in the session.  See the enum above for details.
  // Each time the an error occurs, we will fire this signal.
  Error error() const { return error_; }
  const std::string& error_desc() const { return error_desc_; }
  sigslot::signal2<BaseSession* , Error> SignalError;

  // Updates the state, signaling if necessary.
  virtual void SetState(State state);

  // Updates the error state, signaling if necessary.
  // TODO(ronghuawu): remove the SetError method that doesn't take |error_desc|.
  virtual void SetError(Error error, const std::string& error_desc);

  // Fired when the remote description is updated, with the updated
  // contents.
  sigslot::signal2<BaseSession* , const ContentInfos&>
      SignalRemoteDescriptionUpdate;

  // Fired when SetState is called (regardless if there's a state change), which
  // indicates the session description might have be updated.
  sigslot::signal2<BaseSession*, ContentAction> SignalNewLocalDescription;

  // Fired when SetState is called (regardless if there's a state change), which
  // indicates the session description might have be updated.
  sigslot::signal2<BaseSession*, ContentAction> SignalNewRemoteDescription;

  // Returns the transport that has been negotiated or NULL if
  // negotiation is still in progress.
  virtual Transport* GetTransport(const std::string& content_name);

  // Creates a new channel with the given names.  This method may be called
  // immediately after creating the session.  However, the actual
  // implementation may not be fixed until transport negotiation completes.
  // This will usually be called from the worker thread, but that
  // shouldn't be an issue since the main thread will be blocked in
  // Send when doing so.
  virtual TransportChannel* CreateChannel(const std::string& content_name,
                                          const std::string& channel_name,
                                          int component);

  // Returns the channel with the given names.
  virtual TransportChannel* GetChannel(const std::string& content_name,
                                       int component);

  // Destroys the channel with the given names.
  // This will usually be called from the worker thread, but that
  // shouldn't be an issue since the main thread will be blocked in
  // Send when doing so.
  virtual void DestroyChannel(const std::string& content_name,
                              int component);

  // Returns stats for all channels of all transports.
  // This avoids exposing the internal structures used to track them.
  virtual bool GetStats(SessionStats* stats);

  rtc::SSLIdentity* identity() { return identity_; }

 protected:
  // Specifies the identity to use in this session.
  bool SetIdentity(rtc::SSLIdentity* identity);

  bool PushdownTransportDescription(ContentSource source,
                                    ContentAction action,
                                    std::string* error_desc);
  void set_initiator(bool initiator) { initiator_ = initiator; }

  const TransportMap& transport_proxies() const { return transports_; }
  // Get a TransportProxy by content_name or transport. NULL if not found.
  TransportProxy* GetTransportProxy(const std::string& content_name);
  TransportProxy* GetTransportProxy(const Transport* transport);
  TransportProxy* GetFirstTransportProxy();
  void DestroyTransportProxy(const std::string& content_name);
  // TransportProxy is owned by session.  Return proxy just for convenience.
  TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
  // Creates the actual transport object. Overridable for testing.
  virtual Transport* CreateTransport(const std::string& content_name);

  void OnSignalingReady();
  void SpeculativelyConnectAllTransportChannels();
  // Helper method to provide remote candidates to the transport.
  bool OnRemoteCandidates(const std::string& content_name,
                          const Candidates& candidates,
                          std::string* error);

  // This method will mux transport channels by content_name.
  // First content is used for muxing.
  bool MaybeEnableMuxingSupport();

  // Called when a transport requests signaling.
  virtual void OnTransportRequestSignaling(Transport* transport) {
  }

  // Called when the first channel of a transport begins connecting.  We use
  // this to start a timer, to make sure that the connection completes in a
  // reasonable amount of time.
  virtual void OnTransportConnecting(Transport* transport) {
  }

  // Called when a transport changes its writable state.  We track this to make
  // sure that the transport becomes writable within a reasonable amount of
  // time.  If this does not occur, we signal an error.
  virtual void OnTransportWritable(Transport* transport) {
  }
  virtual void OnTransportReadable(Transport* transport) {
  }

  // Called when a transport has found its steady-state connections.
  virtual void OnTransportCompleted(Transport* transport) {
  }

  // Called when a transport has failed permanently.
  virtual void OnTransportFailed(Transport* transport) {
  }

  // Called when a transport signals that it has new candidates.
  virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy,
                                               const Candidates& candidates) {
  }

  // Called when a transport signals that it found an error in an incoming
  // message.
  virtual void OnTransportSendError(Transport* transport,
                                    const buzz::XmlElement* stanza,
                                    const buzz::QName& name,
                                    const std::string& type,
                                    const std::string& text,
                                    const buzz::XmlElement* extra_info) {
  }

  virtual void OnTransportRouteChange(
      Transport* transport,
      int component,
      const cricket::Candidate& remote_candidate) {
  }

  virtual void OnTransportCandidatesAllocationDone(Transport* transport);

  // Called when all transport channels allocated required candidates.
  // This method should be used as an indication of candidates gathering process
  // is completed and application can now send local candidates list to remote.
  virtual void OnCandidatesAllocationDone() {
  }

  // Handles the ice role change callback from Transport. This must be
  // propagated to all the transports.
  virtual void OnRoleConflict();

  // Handles messages posted to us.
  virtual void OnMessage(rtc::Message *pmsg);

 protected:
  State state_;
  Error error_;
  std::string error_desc_;

 private:
  // Helper methods to push local and remote transport descriptions.
  bool PushdownLocalTransportDescription(
      const SessionDescription* sdesc, ContentAction action,
      std::string* error_desc);
  bool PushdownRemoteTransportDescription(
      const SessionDescription* sdesc, ContentAction action,
      std::string* error_desc);

  bool IsCandidateAllocationDone() const;
  void MaybeCandidateAllocationDone();

  // This method will delete the Transport and TransportChannelImpls and
  // replace those with the selected Transport objects. Selection is done
  // based on the content_name and in this case first MediaContent information
  // is used for mux.
  bool SetSelectedProxy(const std::string& content_name,
                        const ContentGroup* muxed_group);
  // Log session state.
  void LogState(State old_state, State new_state);

  // Returns true and the TransportInfo of the given |content_name|
  // from |description|. Returns false if it's not available.
  static bool GetTransportDescription(const SessionDescription* description,
                                      const std::string& content_name,
                                      TransportDescription* info);

  // Fires the new description signal according to the current state.
  void SignalNewDescription();

  // Gets the ContentAction and ContentSource according to the session state.
  bool GetContentAction(ContentAction* action, ContentSource* source);

  rtc::Thread* const signaling_thread_;
  rtc::Thread* const worker_thread_;
  PortAllocator* const port_allocator_;
  const std::string sid_;
  const std::string content_type_;
  const std::string transport_type_;
  bool initiator_;
  rtc::SSLIdentity* identity_;
  rtc::scoped_ptr<const SessionDescription> local_description_;
  rtc::scoped_ptr<SessionDescription> remote_description_;
  uint64 ice_tiebreaker_;
  // This flag will be set to true after the first role switch. This flag
  // will enable us to stop any role switch during the call.
  bool role_switch_;
  TransportMap transports_;
};

// A specific Session created by the SessionManager, using XMPP for protocol.
class Session : public BaseSession {
 public:
  // Returns the manager that created and owns this session.
  SessionManager* session_manager() const { return session_manager_; }

  // Returns the client that is handling the application data of this session.
  SessionClient* client() const { return client_; }

    // Returns the JID of this client.
  const std::string& local_name() const { return local_name_; }

  // Returns the JID of the other peer in this session.
  const std::string& remote_name() const { return remote_name_; }

  // Set the JID of the other peer in this session.
  // Typically the remote_name_ is set when the session is initiated.
  // However, sometimes (e.g when a proxy is used) the peer name is
  // known after the BaseSession has been initiated and it must be updated
  // explicitly.
  void set_remote_name(const std::string& name) { remote_name_ = name; }

  // Set the JID of the initiator of this session. Allows for the overriding
  // of the initiator to be a third-party, eg. the MUC JID when creating p2p
  // sessions.
  void set_initiator_name(const std::string& name) { initiator_name_ = name; }

  // Indicates the JID of the entity who initiated this session.
  // In special cases, may be different than both local_name and remote_name.
  const std::string& initiator_name() const { return initiator_name_; }

  SignalingProtocol current_protocol() const { return current_protocol_; }

  void set_current_protocol(SignalingProtocol protocol) {
    current_protocol_ = protocol;
  }

  // Updates the error state, signaling if necessary.
  virtual void SetError(Error error, const std::string& error_desc);

  // When the session needs to send signaling messages, it beings by requesting
  // signaling.  The client should handle this by calling OnSignalingReady once
  // it is ready to send the messages.
  // (These are called only by SessionManager.)
  sigslot::signal1<Session*> SignalRequestSignaling;
  void OnSignalingReady() { BaseSession::OnSignalingReady(); }

  // Takes ownership of session description.
  // TODO: Add an error argument to pass back to the caller.
  bool Initiate(const std::string& to,
                const SessionDescription* sdesc);

  // When we receive an initiate, we create a session in the
  // RECEIVEDINITIATE state and respond by accepting or rejecting.
  // Takes ownership of session description.
  // TODO: Add an error argument to pass back to the caller.
  bool Accept(const SessionDescription* sdesc);
  bool Reject(const std::string& reason);
  bool Terminate() {
    return TerminateWithReason(STR_TERMINATE_SUCCESS);
  }
  bool TerminateWithReason(const std::string& reason);
  // Fired whenever we receive a terminate message along with a reason
  sigslot::signal2<Session*, const std::string&> SignalReceivedTerminateReason;

  // The two clients in the session may also send one another
  // arbitrary XML messages, which are called "info" messages. Sending
  // takes ownership of the given elements.  The signal does not; the
  // parent element will be deleted after the signal.
  bool SendInfoMessage(const XmlElements& elems,
                       const std::string& remote_name);
  bool SendDescriptionInfoMessage(const ContentInfos& contents);
  sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage;

 private:
  // Creates or destroys a session.  (These are called only SessionManager.)
  Session(SessionManager *session_manager,
          const std::string& local_name, const std::string& initiator_name,
          const std::string& sid, const std::string& content_type,
          SessionClient* client);
  ~Session();
  // For each transport info, create a transport proxy.  Can fail for
  // incompatible transport types.
  bool CreateTransportProxies(const TransportInfos& tinfos,
                              SessionError* error);
  bool OnRemoteCandidates(const TransportInfos& tinfos,
                          ParseError* error);
  // Returns a TransportInfo without candidates for each content name.
  // Uses the transport_type_ of the session.
  TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const;

    // Maps passed to serialization functions.
  TransportParserMap GetTransportParsers();
  ContentParserMap GetContentParsers();
  CandidateTranslatorMap GetCandidateTranslators();

  virtual void OnTransportRequestSignaling(Transport* transport);
  virtual void OnTransportConnecting(Transport* transport);
  virtual void OnTransportWritable(Transport* transport);
  virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy,
                                               const Candidates& candidates);
  virtual void OnTransportSendError(Transport* transport,
                                    const buzz::XmlElement* stanza,
                                    const buzz::QName& name,
                                    const std::string& type,
                                    const std::string& text,
                                    const buzz::XmlElement* extra_info);
  virtual void OnMessage(rtc::Message *pmsg);

  // Send various kinds of session messages.
  bool SendInitiateMessage(const SessionDescription* sdesc,
                           SessionError* error);
  bool SendAcceptMessage(const SessionDescription* sdesc, SessionError* error);
  bool SendRejectMessage(const std::string& reason, SessionError* error);
  bool SendTerminateMessage(const std::string& reason, SessionError* error);
  bool SendTransportInfoMessage(const TransportInfo& tinfo,
                                SessionError* error);
  bool SendTransportInfoMessage(const TransportProxy* transproxy,
                                const Candidates& candidates,
                                SessionError* error);

  bool ResendAllTransportInfoMessages(SessionError* error);
  bool SendAllUnsentTransportInfoMessages(SessionError* error);

  // All versions of SendMessage send a message of the given type to
  // the other client.  Can pass either a set of elements or an
  // "action", which must have a WriteSessionAction method to go along
  // with it.  Sending with an action supports sending a "hybrid"
  // message.  Sending with elements must be sent as Jingle or Gingle.

  // When passing elems, must be either Jingle or Gingle protocol.
  // Takes ownership of action_elems.
  bool SendMessage(ActionType type, const XmlElements& action_elems,
                   SessionError* error);
  // Sends a messge, but overrides the remote name.
  bool SendMessage(ActionType type, const XmlElements& action_elems,
                   const std::string& remote_name,
                   SessionError* error);
  // When passing an action, may be Hybrid protocol.
  template <typename Action>
  bool SendMessage(ActionType type, const Action& action,
                   SessionError* error);

  // Helper methods to write the session message stanza.
  template <typename Action>
  bool WriteActionMessage(ActionType type, const Action& action,
                          buzz::XmlElement* stanza, WriteError* error);
  template <typename Action>
  bool WriteActionMessage(SignalingProtocol protocol,
                          ActionType type, const Action& action,
                          buzz::XmlElement* stanza, WriteError* error);

  // Sending messages in hybrid form requires being able to write them
  // on a per-protocol basis with a common method signature, which all
  // of these have.
  bool WriteSessionAction(SignalingProtocol protocol,
                          const SessionInitiate& init,
                          XmlElements* elems, WriteError* error);
  bool WriteSessionAction(SignalingProtocol protocol,
                          const TransportInfo& tinfo,
                          XmlElements* elems, WriteError* error);
  bool WriteSessionAction(SignalingProtocol protocol,
                          const SessionTerminate& term,
                          XmlElements* elems, WriteError* error);

  // Sends a message back to the other client indicating that we have received
  // and accepted their message.
  void SendAcknowledgementMessage(const buzz::XmlElement* stanza);

  // Once signaling is ready, the session will use this signal to request the
  // sending of each message.  When messages are received by the other client,
  // they should be handed to OnIncomingMessage.
  // (These are called only by SessionManager.)
  sigslot::signal2<Session* , const buzz::XmlElement*> SignalOutgoingMessage;
  void OnIncomingMessage(const SessionMessage& msg);

  void OnIncomingResponse(const buzz::XmlElement* orig_stanza,
                          const buzz::XmlElement* response_stanza,
                          const SessionMessage& msg);
  void OnInitiateAcked();
  void OnFailedSend(const buzz::XmlElement* orig_stanza,
                    const buzz::XmlElement* error_stanza);

  // Invoked when an error is found in an incoming message.  This is translated
  // into the appropriate XMPP response by SessionManager.
  sigslot::signal6<BaseSession*,
                   const buzz::XmlElement*,
                   const buzz::QName&,
                   const std::string&,
                   const std::string&,
                   const buzz::XmlElement*> SignalErrorMessage;

  // Handlers for the various types of messages.  These functions may take
  // pointers to the whole stanza or to just the session element.
  bool OnInitiateMessage(const SessionMessage& msg, MessageError* error);
  bool OnAcceptMessage(const SessionMessage& msg, MessageError* error);
  bool OnRejectMessage(const SessionMessage& msg, MessageError* error);
  bool OnInfoMessage(const SessionMessage& msg);
  bool OnTerminateMessage(const SessionMessage& msg, MessageError* error);
  bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error);
  bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error);
  bool OnDescriptionInfoMessage(const SessionMessage& msg, MessageError* error);
  bool OnRedirectError(const SessionRedirect& redirect, SessionError* error);

  // Verifies that we are in the appropriate state to receive this message.
  bool CheckState(State state, MessageError* error);

  SessionManager* session_manager_;
  bool initiate_acked_;
  std::string local_name_;
  std::string initiator_name_;
  std::string remote_name_;
  SessionClient* client_;
  TransportParser* transport_parser_;
  // Keeps track of what protocol we are speaking.
  SignalingProtocol current_protocol_;

  friend class SessionManager;  // For access to constructor, destructor,
                                // and signaling related methods.
};

}  // namespace cricket

#endif  // TALK_P2P_BASE_SESSION_H_