SipMSRPApi
Version 13 (Oliver Bril, 03/31/2009 02:46 pm)
1 | 1 | Adrian Georgescu | = MSRP API = |
---|---|---|---|
2 | 1 | Adrian Georgescu | |
3 | 1 | Adrian Georgescu | [[TOC(WikiStart, Sip*, depth=3)]] |
4 | 1 | Adrian Georgescu | |
5 | 1 | Adrian Georgescu | Message Session Relay Protocol (MSRP) is a protocol for transmitting a series of related instant messages in the context of a session. Message sessions are treated like any other media stream when set up via a rendezvous or session creation protocol such as the Session Initiation Protocol (SIP). |
6 | 1 | Adrian Georgescu | |
7 | 1 | Adrian Georgescu | * MSRP sessions are defined in [http://tools.ietf.org/html/rfc4975 RFC 4975]. |
8 | 1 | Adrian Georgescu | * MSRP relay extension used for NAT traversal of instant messaging and file transfer sessions is defined in [http://tools.ietf.org/html/rfc4976 RFC 4976]. |
9 | 1 | Adrian Georgescu | |
10 | 5 | Oliver Bril | The MSRP protocol is implemented by [http://devel.ag-projects.com/cgi-bin/darcsweb.cgi?r=python-msrplib;a=summary msrplib] Python package. On top of it, {{{sipsimple}}} provides higher level classes integrated into middleware notification and configuration systems: |
11 | 1 | Adrian Georgescu | |
12 | 4 | Oliver Bril | * {{{sipsimple.msrp.MSRPChat}}} |
13 | 4 | Oliver Bril | * {{{sipsimple.msrp.MSRPFileTransfer}}} |
14 | 1 | Adrian Georgescu | * {{{sipsimple.msrp.MSRPDesktopSharing}}} |
15 | 1 | Adrian Georgescu | |
16 | 11 | Adrian Georgescu | These classes are used internally by [wiki:SipMiddlewareApi#Session Session], which provides the necessary methods to access their features. The notifications posted by these classes are also handled internally by [wiki:SipMiddlewareApi#Session Session]. The notifications that are relevant to the user are then reposted by the Session instance. Refer to [wiki:SipMiddlewareApi#Session Session documentation] for details on the Session API. To communicate with the middleware, MSRP high level classes use the notification system provided by the [http://pypi.python.org/pypi/python-application python-application] package. |
17 | 10 | Adrian Georgescu | |
18 | 12 | Adrian Georgescu | == MSRPChat high level API == |
19 | 1 | Adrian Georgescu | |
20 | 10 | Adrian Georgescu | {{{sipsimple.msrp.MSRPChat}}} implements Instant Messaging over MSRP in the context of SIPSIMPLE middleware. This class |
21 | 1 | Adrian Georgescu | |
22 | 5 | Oliver Bril | * automatically wraps outgoing messages with Message/CPIM if that's necessary according to accept-types |
23 | 7 | Oliver Bril | * unwraps incoming Message/CPIM messages; for each incoming message, {{{MSRPChatGotMessage}}} is posted. |
24 | 5 | Oliver Bril | * plays notification sounds on received/sent message |
25 | 5 | Oliver Bril | |
26 | 5 | Oliver Bril | === methods === |
27 | 5 | Oliver Bril | |
28 | 5 | Oliver Bril | '''!__init!__'''(''self'', ''account'', ''remote_uri'', ''outgoing''):: |
29 | 5 | Oliver Bril | Initialize MSRPChat instance. |
30 | 5 | Oliver Bril | |
31 | 5 | Oliver Bril | '''initialize'''(''self''):: |
32 | 5 | Oliver Bril | Initialize the MSRP connection; connect to the relay if necessary. When done, fire MSRPChatDidInitialize (with 'sdpmedia' attribute, containing the appropriate 'SDPMedia' instance) |
33 | 5 | Oliver Bril | |
34 | 5 | Oliver Bril | '''start'''(''self'', ''remote_media''):: |
35 | 5 | Oliver Bril | Complete the MSRP connection establishment; this includes binding the MSRP session. [[BR]] |
36 | 5 | Oliver Bril | When done, fire MSRPChatDidStart. At this point each incoming message is posted as a {{{MSRPChatGotMessage}}} notification |
37 | 5 | Oliver Bril | |
38 | 5 | Oliver Bril | '''end'''(''self''):: |
39 | 5 | Oliver Bril | Close the MSRP connection or cleanup after initialize(), whatever is necessary. [[BR]] |
40 | 5 | Oliver Bril | Before doing anything post {{{MSRPChatWillEnd}}}. |
41 | 7 | Oliver Bril | When done, post {{{MSRPChatDidEnd}}}. If there was an error, post {{{MSRPChatDidFail}}}. |
42 | 5 | Oliver Bril | {{{MSRPChatDidEnd}}} will be posted anyway. |
43 | 5 | Oliver Bril | |
44 | 6 | Oliver Bril | '''send_message'''(''self'', ''content'', ''content_type''={{{'text/plain'}}}, ''to_uri''={{{None}}}, ''dt''={{{None}}}):: |
45 | 6 | Oliver Bril | Send IM message. Prefer Message/CPIM wrapper if it is supported. If called before the connection was established, the messages will be |
46 | 6 | Oliver Bril | queued until MSRPChatDidStart notification. |
47 | 6 | Oliver Bril | |
48 | 6 | Oliver Bril | Return generated MSRP chunk (MSRPData instance); to get Message-ID use its 'message_id' attribute. |
49 | 6 | Oliver Bril | |
50 | 6 | Oliver Bril | ''content'' str:[[BR]] |
51 | 6 | Oliver Bril | content of the message |
52 | 6 | Oliver Bril | |
53 | 6 | Oliver Bril | ''to_uri'' SIPURI:[[BR]] |
54 | 6 | Oliver Bril | "To" header of CPIM wrapper; use to override the default supplied to {{{__init__}}}. |
55 | 6 | Oliver Bril | May only differ from the one supplied in __init__ if the remote party supports private messages. If it does not, {{{MSRPChatError}}} will be raised; |
56 | 6 | Oliver Bril | |
57 | 6 | Oliver Bril | ''content_type'' str:[[BR]] |
58 | 6 | Oliver Bril | Content-Type of wrapped message if Message/CPIM is used (Content-Type of MSRP message is always Message/CPIM in that case); |
59 | 6 | Oliver Bril | otherwise, Content-Type of MSRP message. |
60 | 6 | Oliver Bril | |
61 | 6 | Oliver Bril | These MSRP headers are used to enable end-to-end success reports and to disable hop-to-hop successful responses: |
62 | 6 | Oliver Bril | {{{ |
63 | 6 | Oliver Bril | Failure-Report: partial |
64 | 6 | Oliver Bril | Success-Report: yes |
65 | 6 | Oliver Bril | }}} |
66 | 6 | Oliver Bril | |
67 | 5 | Oliver Bril | |
68 | 1 | Adrian Georgescu | === notifications === |
69 | 1 | Adrian Georgescu | |
70 | 7 | Oliver Bril | * MSRPChatDidInitialize |
71 | 7 | Oliver Bril | * MSRPChatDidStart |
72 | 7 | Oliver Bril | * MSRPChatWillEnd |
73 | 7 | Oliver Bril | * MSRPChatDidEnd |
74 | 7 | Oliver Bril | * MSRPChatDidFail |
75 | 7 | Oliver Bril | * MSRPChatGotMessage |
76 | 7 | Oliver Bril | - cpim_headers (dict) |
77 | 7 | Oliver Bril | - message (MSRPData) |
78 | 7 | Oliver Bril | - content (str) - the actual string that the remote user has typed |
79 | 7 | Oliver Bril | * MSRPChatDidDeliverMessage |
80 | 7 | Oliver Bril | * MSRPChatDidNotDeliverMessage |
81 | 5 | Oliver Bril | |
82 | 4 | Oliver Bril | == MSRPFileTransfer == |
83 | 4 | Oliver Bril | |
84 | 4 | Oliver Bril | == MSRPDesktopSharing == |
85 | 1 | Adrian Georgescu | |
86 | 12 | Adrian Georgescu | ---------------------- |
87 | 12 | Adrian Georgescu | |
88 | 12 | Adrian Georgescu | == msrplib low level API == |
89 | 4 | Oliver Bril | |
90 | 1 | Adrian Georgescu | {{{msrplib}}} is based upon [http://twistedmatrix.com twisted] and [http://devel.ag-projects.com/~denis/eventlet/ eventlet] and provides a set of |
91 | 1 | Adrian Georgescu | classes for establishing and managing MSRP connection. |
92 | 1 | Adrian Georgescu | |
93 | 1 | Adrian Georgescu | The library consist of the following modules: |
94 | 1 | Adrian Georgescu | |
95 | 1 | Adrian Georgescu | '''msrplib.transport''':: |
96 | 1 | Adrian Georgescu | Defines {{{MSRPTransport}}} class, which provides low level control over MSRP connection. |
97 | 2 | Redmine Admin | |
98 | 1 | Adrian Georgescu | '''msrplib.connect''':: |
99 | 1 | Adrian Georgescu | Defines means to establish a connection, bind it, and provide an initialized {{{MSRPTransport}}} instance. |
100 | 1 | Adrian Georgescu | |
101 | 1 | Adrian Georgescu | '''msrplib.session''':: |
102 | 1 | Adrian Georgescu | Defines {{{MSRPSession}}} class, which provides high level control over a MSRP connection. |
103 | 1 | Adrian Georgescu | |
104 | 1 | Adrian Georgescu | '''msrplib.protocol''':: |
105 | 1 | Adrian Georgescu | Provides representation and parsing of MSRP entities - chunks and URIs. |
106 | 1 | Adrian Georgescu | |
107 | 1 | Adrian Georgescu | '''msrplib.trafficlog''':: |
108 | 1 | Adrian Georgescu | Defines {{{Logger}}} class that is used through out the library to log the connection state. |
109 | 1 | Adrian Georgescu | |
110 | 4 | Oliver Bril | === Usage === |
111 | 1 | Adrian Georgescu | |
112 | 4 | Oliver Bril | ==== Establish a connection ==== |
113 | 3 | Oliver Bril | |
114 | 1 | Adrian Georgescu | {{{msrplib.connect}}} provides a number of classes to establish a connection, so the first |
115 | 1 | Adrian Georgescu | thing to do is to select which one applies to your situation: |
116 | 1 | Adrian Georgescu | |
117 | 1 | Adrian Georgescu | 1. Calling endpoint, not using a relay ({{{ConnectorDirect}}}) |
118 | 1 | Adrian Georgescu | 2. Answering endpoint, not using a relay ({{{AcceptorDirect}}}) |
119 | 1 | Adrian Georgescu | 3. Calling endpoint, using a relay ({{{ConnectorRelay}}}) |
120 | 1 | Adrian Georgescu | 4. Answering endpoint, using a relay ({{{AcceptorRelay}}}) |
121 | 1 | Adrian Georgescu | |
122 | 1 | Adrian Georgescu | The answering endpoint may skip using the relay if sure that it's accessible |
123 | 1 | Adrian Georgescu | directly. The calling endpoint is unlikely to need the relay. |
124 | 1 | Adrian Georgescu | |
125 | 1 | Adrian Georgescu | Once you have an instance of the right class (use the convenience functions |
126 | 1 | Adrian Georgescu | {{{get_connector()}}} and {{{get_acceptor()}}} to get one), the procedure to establish the |
127 | 1 | Adrian Georgescu | connection is the same: |
128 | 1 | Adrian Georgescu | |
129 | 1 | Adrian Georgescu | {{{ |
130 | 1 | Adrian Georgescu | full_local_path = connector.prepare() |
131 | 1 | Adrian Georgescu | try: |
132 | 1 | Adrian Georgescu | ... put full_local_path in SDP 'a:path' attribute |
133 | 1 | Adrian Georgescu | ... get full_remote_path from remote's 'a:path: attribute |
134 | 1 | Adrian Georgescu | ... (the order of the above steps is reversed if you're the |
135 | 1 | Adrian Georgescu | ... answering party, but that does not affect connector's usage) |
136 | 1 | Adrian Georgescu | msrptransport = connector.complete(full_remote_path) |
137 | 1 | Adrian Georgescu | finally: |
138 | 1 | Adrian Georgescu | connector.cleanup() |
139 | 1 | Adrian Georgescu | }}} |
140 | 1 | Adrian Georgescu | |
141 | 1 | Adrian Georgescu | To customize connection's parameters, create a new {{{protocol.URI}}} object and pass |
142 | 1 | Adrian Georgescu | it to prepare() function, e.g. |
143 | 1 | Adrian Georgescu | {{{ |
144 | 1 | Adrian Georgescu | local_uri = protocol.URI(use_tls=False, port=5000) |
145 | 1 | Adrian Georgescu | connector.prepare(local_uri) |
146 | 1 | Adrian Georgescu | }}} |
147 | 1 | Adrian Georgescu | |
148 | 1 | Adrian Georgescu | {{{prepare()}}} may update {{{local_uri}}} in place with the actual connection parameters |
149 | 1 | Adrian Georgescu | used (e.g. if you specified port=0). 'port' attribute of {{{local_uri}}} is currently |
150 | 1 | Adrian Georgescu | only respected by {{{AcceptorDirect}}}. |
151 | 1 | Adrian Georgescu | |
152 | 1 | Adrian Georgescu | Note that, acceptors and connectors are one-use only. Which means, that {{{AcceptorDirect}}} |
153 | 1 | Adrian Georgescu | will open a port just to handle one incoming connection and close it right after. |
154 | 1 | Adrian Georgescu | If your application behaves more like a server, i.e. opens a port and listens on it |
155 | 1 | Adrian Georgescu | constantly, use {{{MSRPServer}}} class. |
156 | 3 | Oliver Bril | |
157 | 4 | Oliver Bril | === Components === |
158 | 1 | Adrian Georgescu | |
159 | 4 | Oliver Bril | ==== a connector or acceptor ==== |
160 | 3 | Oliver Bril | |
161 | 8 | Oliver Bril | {{{msrplib.connect}}} provides 2 connectors (with and without relay) and 2 acceptors (likewise, with or without relay). All of them have the exact same interface, |
162 | 8 | Oliver Bril | |
163 | 8 | Oliver Bril | '''prepare'''(''self'', ''local_uri''={{{None}}}):: |
164 | 8 | Oliver Bril | Depending on type of the connector, use local_uri to prepare the MSRP connection, which means: |
165 | 8 | Oliver Bril | * connecting and authenticating at the relay if a relay is used ({{{ConnectorRelay}}} and {{{AcceptorRelay}}}) |
166 | 8 | Oliver Bril | * start listening on a local port for DirectAcceptor |
167 | 8 | Oliver Bril | |
168 | 8 | Oliver Bril | ''local_uri'' is used to specify the connection parameters, e.g. local port and local ip. |
169 | 8 | Oliver Bril | If not provided, suitable ''local_uri'' will be generated. |
170 | 8 | Oliver Bril | ''local_uri'' maybe updated in place by {{{prepare()}}} method if the real settings used are different from those specified. |
171 | 8 | Oliver Bril | |
172 | 8 | Oliver Bril | {{{prepare}}} returns a full local path - list of {{{protocol.URI}}} instances, suitable to be put in SDP {{{'a:path'}}} attribute. |
173 | 8 | Oliver Bril | |
174 | 8 | Oliver Bril | '''complete'''(''self'', ''full_remote_path''):: |
175 | 8 | Oliver Bril | Complete establishing the MSRP connection, which means |
176 | 8 | Oliver Bril | * establishing the connection if it wasn't already established ({{{ConnectorDirect}}}) |
177 | 8 | Oliver Bril | * bind the connection, i.e. exchange empty chunk to verify each other's From-Path and To-Path |
178 | 8 | Oliver Bril | |
179 | 8 | Oliver Bril | ''full_remote_path'' should be a list of {{{protocol.URI}}} instances, obtained by parsing {{{'a:path'}}} put in SDP by the remote party. |
180 | 8 | Oliver Bril | |
181 | 8 | Oliver Bril | {{{complete}}} returns {{{transport.MSRPTransport}}} instance, ready to read and send chunks. |
182 | 9 | Oliver Bril | |
183 | 9 | Oliver Bril | '''cleanup'''(''self''):: |
184 | 9 | Oliver Bril | Call this method to cleanup after {{{initialize()}}} if it's impossible to call {{{complete()}}} |
185 | 8 | Oliver Bril | |
186 | 1 | Adrian Georgescu | |
187 | 4 | Oliver Bril | ==== transport.MSRPTransport ==== |
188 | 1 | Adrian Georgescu | |
189 | 1 | Adrian Georgescu | Low level access to MSRP connection. |
190 | 1 | Adrian Georgescu | |
191 | 4 | Oliver Bril | ===== attributes ===== |
192 | 3 | Oliver Bril | |
193 | 4 | Oliver Bril | ===== methods ===== |
194 | 3 | Oliver Bril | |
195 | 3 | Oliver Bril | '''make_chunk'''(''self'', ''transaction_id''={{{None}}}, ''method''={{{'SEND'}}}, ''code''={{{None}}}, ''comment''={{{None}}}, ''data''={{{''}}}, ''contflag''={{{None}}}, ''start''={{{1}}}, ''end''={{{None}}}, ''length''={{{None}}}, ''message_id''={{{None}}}):: |
196 | 1 | Adrian Georgescu | Make a new chunk ({{{protocol.MSRPData}}} instance) with proper {{{From-Path}}}, {{{To-Path}}}, {{{Byte-Range}}} and {{{Message-ID}}} headers set up based on MSRPTransport's state and the parameters provided. Use ''data'' for payload, and ''start''/''end''/''length'' to generate {{{Byte-Range}}} header. Generate new random strings for default values of ''transaction_id'' and ''message_id''. |
197 | 1 | Adrian Georgescu | [[BR]]''contflag'':[[BR]] |
198 | 1 | Adrian Georgescu | MSRP chunk's continuation flag ({{{'$'}}}, {{{'+'}}} or {{{'#'}}}). Default is {{{'$'}}}, unless you have a partial {{{SEND}}} chunk, in which case it is {{{'+'}}} |
199 | 1 | Adrian Georgescu | |
200 | 4 | Oliver Bril | ==== session.MSRPSession ==== |
201 | 4 | Oliver Bril | ===== attributes ===== |
202 | 4 | Oliver Bril | ===== methods ===== |
203 | 1 | Adrian Georgescu | |
204 | 4 | Oliver Bril | ==== connect.MSRPServer ==== |
205 | 13 | Oliver Bril | Manage listening sockets. Bind incoming requests. |
206 | 13 | Oliver Bril | |
207 | 13 | Oliver Bril | MSRPServer solves the problem with AcceptorDirect: concurrent using of 2 |
208 | 13 | Oliver Bril | or more AcceptorDirect instances on the same non-zero port is not possible. |
209 | 13 | Oliver Bril | If you initialize() those instances, one after another, one will listen on |
210 | 13 | Oliver Bril | the socket and another will get BindError. |
211 | 13 | Oliver Bril | |
212 | 13 | Oliver Bril | MSRPServer avoids the problem by sharing the listening socket between multiple connections. |
213 | 13 | Oliver Bril | It has a slightly different interface from AcceptorDirect, so it cannot be considered a drop-in |
214 | 13 | Oliver Bril | replacement. |
215 | 13 | Oliver Bril | |
216 | 13 | Oliver Bril | '''prepare'''(''self'', ''local_uri''={{{None}}}, ''logger''={{{None}}}):: |
217 | 13 | Oliver Bril | Start a listening port specified by ''local_uri'' if there isn't one on that port/interface already. |
218 | 13 | Oliver Bril | Add ''local_uri'' to the list of expected URIs, so that incoming connections featuring this URI won't be rejected. |
219 | 13 | Oliver Bril | If ''logger'' is provided use it for this connection instead of the default one. |
220 | 13 | Oliver Bril | |
221 | 13 | Oliver Bril | '''complete'''(''self'', ''full_remote_path''):: |
222 | 13 | Oliver Bril | Wait until one of the incoming connections binds using provided ''full_remote_path''. |
223 | 13 | Oliver Bril | Return connected and bound {{{MSRPTransport}}} instance. |
224 | 13 | Oliver Bril | |
225 | 13 | Oliver Bril | If no such binding was made within {{{MSRPBindSessionTimeout.seconds}}}, raise {{{MSRPBindSessionTimeout}}}. |
226 | 13 | Oliver Bril | ''full_remote_path'' should be a list of {{{protocol.URI}}} instances, obtained by parsing {{{'a:path'}}} put in SDP by the remote party. |
227 | 13 | Oliver Bril | |
228 | 13 | Oliver Bril | '''cleanup'''(''self'', ''local_uri''):: |
229 | 13 | Oliver Bril | Remove ''local_uri'' from the list of expected URIs. |