Initial import to Tizen
[profile/ivi/python-twisted.git] / doc / core / howto / amp.html
1 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
2   <head>
3 <title>Twisted Documentation: <b>A</b>synchronous <b>M</b>essaging <b>P</b>rotocol Overview</title>
4 <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
5   </head>
6
7   <body bgcolor="white">
8     <h1 class="title"><b>A</b>synchronous <b>M</b>essaging <b>P</b>rotocol Overview</h1>
9     <div class="toc"><ol><li><a href="#auto0">Setting Up</a></li><li><a href="#auto1">Commands</a></li><li><a href="#auto2">Locators</a></li><li><a href="#auto3">Box Receivers</a></li></ol></div>
10     <div class="content">
11     <span/>
12
13     <p>The purpose of this guide is to describe the uses for and usage of <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.protocols.amp.html" title="twisted.protocols.amp">twisted.protocols.amp</a></code> beyond what is explained in the API documentation.  It will show you how to implement an AMP server which can respond to commands or interact directly with individual messages.  It will also show you how to implement an AMP client which can issue commands to a server.</p>
14
15     <p>AMP is a bidirectional command/response-oriented protocol intended to be extended with application-specific request types and handlers.  Various simple data types are supported and support for new data types can be added by applications.</p>
16
17     <h2>Setting Up<a name="auto0"/></h2>
18
19     <p>AMP runs over a stream-oriented connection-based protocol, such as TCP or SSL.  Before you can use any features of the AMP protocol, you need a connection.  The protocol class to use to establish an AMP connection is <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.protocols.amp.AMP.html" title="twisted.protocols.amp.AMP">AMP</a></code>.  Connection setup works as it does for almost all protocols in Twisted.  For example, you can set up a listening AMP server using a server endpoint:</p>
20
21     <div class="py-listing"><pre><p class="py-linenumber"> 1
22  2
23  3
24  4
25  5
26  6
27  7
28  8
29  9
30 10
31 11
32 12
33 13
34 14
35 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span>.<span class="py-src-variable">amp</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">AMP</span>
36 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
37 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Factory</span>
38 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">endpoints</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">TCP4ServerEndpoint</span>
39 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span>.<span class="py-src-variable">service</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Application</span>
40 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">StreamServerEndpointService</span>
41
42 <span class="py-src-variable">application</span> = <span class="py-src-variable">Application</span>(<span class="py-src-string">&quot;basic AMP server&quot;</span>)
43
44 <span class="py-src-variable">endpoint</span> = <span class="py-src-variable">TCP4ServerEndpoint</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-number">8750</span>)
45 <span class="py-src-variable">factory</span> = <span class="py-src-variable">Factory</span>()
46 <span class="py-src-variable">factory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">AMP</span>
47 <span class="py-src-variable">service</span> = <span class="py-src-variable">StreamServerEndpointService</span>(<span class="py-src-variable">endpoint</span>, <span class="py-src-variable">factory</span>)
48 <span class="py-src-variable">service</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">application</span>)
49 </pre><div class="caption">Source listing - <a href="listings/amp/basic_server.tac"><span class="filename">listings/amp/basic_server.tac</span></a></div></div>
50
51     <p>And you can connect to an AMP server using a client endpoint:</p>
52
53     <div class="py-listing"><pre><p class="py-linenumber"> 1
54  2
55  3
56  4
57  5
58  6
59  7
60  8
61  9
62 10
63 11
64 12
65 13
66 14
67 15
68 16
69 17
70 18
71 19
72 20
73 21
74 22
75 23
76 24
77 25
78 26
79 27
80 28
81 29
82 30
83 </p><span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
84     <span class="py-src-keyword">import</span> <span class="py-src-variable">basic_client</span>
85     <span class="py-src-keyword">raise</span> <span class="py-src-variable">SystemExit</span>(<span class="py-src-variable">basic_client</span>.<span class="py-src-variable">main</span>())
86
87 <span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
88
89 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span>.<span class="py-src-variable">log</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">startLogging</span>, <span class="py-src-variable">err</span>
90 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span>.<span class="py-src-variable">amp</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">AMP</span>
91 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
92 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Factory</span>
93 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">endpoints</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">TCP4ClientEndpoint</span>
94
95 <span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>():
96     <span class="py-src-variable">endpoint</span> = <span class="py-src-variable">TCP4ClientEndpoint</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-string">&quot;127.0.0.1&quot;</span>, <span class="py-src-number">8750</span>)
97     <span class="py-src-variable">factory</span> = <span class="py-src-variable">Factory</span>()
98     <span class="py-src-variable">factory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-variable">AMP</span>
99     <span class="py-src-keyword">return</span> <span class="py-src-variable">endpoint</span>.<span class="py-src-variable">connect</span>(<span class="py-src-variable">factory</span>)
100
101
102 <span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
103     <span class="py-src-variable">startLogging</span>(<span class="py-src-variable">stdout</span>)
104
105     <span class="py-src-variable">d</span> = <span class="py-src-variable">connect</span>()
106     <span class="py-src-variable">d</span>.<span class="py-src-variable">addErrback</span>(<span class="py-src-variable">err</span>, <span class="py-src-string">&quot;Connection failed&quot;</span>)
107     <span class="py-src-keyword">def</span> <span class="py-src-identifier">done</span>(<span class="py-src-parameter">ignored</span>):
108         <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
109     <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">done</span>)
110
111     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
112 </pre><div class="caption">Source listing - <a href="listings/amp/basic_client.py"><span class="filename">listings/amp/basic_client.py</span></a></div></div>
113
114     <h2>Commands<a name="auto1"/></h2>
115
116     <p>Either side of an AMP connection can issue a command to the other side.  Each kind of command is represented as a subclass of <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.protocols.amp.Command.html" title="twisted.protocols.amp.Command">Command</a></code>.  A <code>Command</code> defines arguments, response values, and error conditions.</p>
117
118     <pre class="python"><p class="py-linenumber"> 1
119  2
120  3
121  4
122  5
123  6
124  7
125  8
126  9
127 10
128 11
129 12
130 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span>.<span class="py-src-variable">amp</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Integer</span>, <span class="py-src-variable">String</span>, <span class="py-src-variable">Unicode</span>, <span class="py-src-variable">Command</span>
131
132 <span class="py-src-keyword">class</span> <span class="py-src-identifier">UsernameUnavailable</span>(<span class="py-src-parameter">Exception</span>):
133     <span class="py-src-keyword">pass</span>
134
135 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RegisterUser</span>(<span class="py-src-parameter">Command</span>):
136     <span class="py-src-variable">arguments</span> = [(<span class="py-src-string">'username'</span>, <span class="py-src-variable">Unicode</span>()),
137                  (<span class="py-src-string">'publickey'</span>, <span class="py-src-variable">String</span>())]
138
139     <span class="py-src-variable">response</span> = [(<span class="py-src-string">'uid'</span>, <span class="py-src-variable">Integer</span>())]
140
141     <span class="py-src-variable">errors</span> = {<span class="py-src-variable">UsernameUnavailable</span>: <span class="py-src-string">'username-unavailable'</span>}
142 </pre>
143
144     <p>The definition of the command's signature - its arguments, response, and possible error conditions - is separate from the implementation of the behavior to execute when the command is received.  The <code>Command</code> subclass only defines the former.</p>
145
146     <p>Commands are issued by calling <code>callRemote</code> on either side of the connection.  This method returns a <code>Deferred</code> which eventually fires with the result of the command.</p>
147
148     <div class="py-listing"><pre><p class="py-linenumber"> 1
149  2
150  3
151  4
152  5
153  6
154  7
155  8
156  9
157 10
158 11
159 12
160 13
161 14
162 15
163 16
164 17
165 18
166 19
167 20
168 21
169 22
170 23
171 24
172 25
173 26
174 27
175 28
176 29
177 30
178 31
179 32
180 33
181 34
182 35
183 36
184 37
185 38
186 39
187 40
188 41
189 42
190 43
191 44
192 45
193 46
194 47
195 48
196 </p><span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
197     <span class="py-src-keyword">import</span> <span class="py-src-variable">command_client</span>
198     <span class="py-src-keyword">raise</span> <span class="py-src-variable">SystemExit</span>(<span class="py-src-variable">command_client</span>.<span class="py-src-variable">main</span>())
199
200 <span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
201
202 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span>.<span class="py-src-variable">log</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">startLogging</span>, <span class="py-src-variable">err</span>
203 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span>.<span class="py-src-variable">amp</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Integer</span>, <span class="py-src-variable">String</span>, <span class="py-src-variable">Unicode</span>, <span class="py-src-variable">Command</span>
204 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
205
206 <span class="py-src-keyword">from</span> <span class="py-src-variable">basic_client</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">connect</span>
207
208 <span class="py-src-keyword">class</span> <span class="py-src-identifier">UsernameUnavailable</span>(<span class="py-src-parameter">Exception</span>):
209     <span class="py-src-keyword">pass</span>
210
211
212 <span class="py-src-keyword">class</span> <span class="py-src-identifier">RegisterUser</span>(<span class="py-src-parameter">Command</span>):
213     <span class="py-src-variable">arguments</span> = [(<span class="py-src-string">'username'</span>, <span class="py-src-variable">Unicode</span>()),
214                  (<span class="py-src-string">'publickey'</span>, <span class="py-src-variable">String</span>())]
215
216     <span class="py-src-variable">response</span> = [(<span class="py-src-string">'uid'</span>, <span class="py-src-variable">Integer</span>())]
217
218     <span class="py-src-variable">errors</span> = {<span class="py-src-variable">UsernameUnavailable</span>: <span class="py-src-string">'username-unavailable'</span>}
219
220
221 <span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
222     <span class="py-src-variable">startLogging</span>(<span class="py-src-variable">stdout</span>)
223
224     <span class="py-src-variable">d</span> = <span class="py-src-variable">connect</span>()
225     <span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">protocol</span>):
226         <span class="py-src-keyword">return</span> <span class="py-src-variable">protocol</span>.<span class="py-src-variable">callRemote</span>(
227             <span class="py-src-variable">RegisterUser</span>,
228             <span class="py-src-variable">username</span>=<span class="py-src-string">u'alice'</span>,
229             <span class="py-src-variable">publickey</span>=<span class="py-src-string">'ssh-rsa AAAAB3NzaC1yc2 alice@actinium'</span>)
230     <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
231
232     <span class="py-src-keyword">def</span> <span class="py-src-identifier">registered</span>(<span class="py-src-parameter">result</span>):
233         <span class="py-src-keyword">print</span> <span class="py-src-string">'Registration result:'</span>, <span class="py-src-variable">result</span>
234     <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">registered</span>)
235
236     <span class="py-src-variable">d</span>.<span class="py-src-variable">addErrback</span>(<span class="py-src-variable">err</span>, <span class="py-src-string">&quot;Failed to register&quot;</span>)
237
238     <span class="py-src-keyword">def</span> <span class="py-src-identifier">finished</span>(<span class="py-src-parameter">ignored</span>):
239         <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
240     <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">finished</span>)
241
242     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
243 </pre><div class="caption">Source listing - <a href="listings/amp/command_client.py"><span class="filename">listings/amp/command_client.py</span></a></div></div>
244
245     <h2>Locators<a name="auto2"/></h2>
246
247
248     <p>The logic for handling a command can be specified as an object separate from the <code>AMP</code> instance which interprets and formats bytes over the network.</p>
249
250     <pre class="python"><p class="py-linenumber"> 1
251  2
252  3
253  4
254  5
255  6
256  7
257  8
258  9
259 10
260 11
261 12
262 13
263 14
264 15
265 16
266 17
267 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span>.<span class="py-src-variable">amp</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">CommandLocator</span>
268 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span>.<span class="py-src-variable">filepath</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">FilePath</span>
269
270 <span class="py-src-keyword">class</span> <span class="py-src-identifier">UsernameUnavailable</span>(<span class="py-src-parameter">Exception</span>):
271     <span class="py-src-keyword">pass</span>
272
273 <span class="py-src-keyword">class</span> <span class="py-src-identifier">UserRegistration</span>(<span class="py-src-parameter">CommandLocator</span>):
274     <span class="py-src-variable">uidCounter</span> = <span class="py-src-number">0</span>
275
276     @<span class="py-src-variable">RegisterUser</span>.<span class="py-src-variable">responder</span>
277     <span class="py-src-keyword">def</span> <span class="py-src-identifier">register</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">username</span>, <span class="py-src-parameter">publickey</span>):
278         <span class="py-src-variable">path</span> = <span class="py-src-variable">FilePath</span>(<span class="py-src-variable">username</span>)
279         <span class="py-src-keyword">if</span> <span class="py-src-variable">path</span>.<span class="py-src-variable">exists</span>():
280             <span class="py-src-keyword">raise</span> <span class="py-src-variable">UsernameUnavailable</span>()
281         <span class="py-src-variable">self</span>.<span class="py-src-variable">uidCounter</span> += <span class="py-src-number">1</span>
282         <span class="py-src-variable">path</span>.<span class="py-src-variable">setContent</span>(<span class="py-src-string">'%d %s\n'</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">uidCounter</span>, <span class="py-src-variable">publickey</span>))
283         <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">uidCounter</span>
284 </pre>
285
286     <p>When you define a separate <code>CommandLocator</code> subclass, use it by passing an instance of it to the <code>AMP</code> initializer.</p>
287
288     <pre class="python"><p class="py-linenumber">1
289 2
290 </p><span class="py-src-variable">factory</span> = <span class="py-src-variable">Factory</span>()
291 <span class="py-src-variable">factory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-keyword">lambda</span>: <span class="py-src-variable">AMP</span>(<span class="py-src-variable">locator</span>=<span class="py-src-variable">UserRegistration</span>())
292 </pre>
293
294     <p>If no locator is passed in, <code>AMP</code> acts as its own locator.  Command responders can be defined on an <code>AMP</code> subclass, just as the responder was defined on the <code>UserRegistration</code> example above.</p>
295
296     <h2>Box Receivers<a name="auto3"/></h2>
297
298     <p>AMP conversations consist of an exchange of messages called <em>boxes</em>.  A <em>box</em> consists of a sequence of pairs of key and value (for example, the pair <code>username</code> and <code>alice</code>).  Boxes are generally represented as <code>dict</code> instances.  Normally boxes are passed back and forth to implement the command request/response features described above.  The logic for handling each box can be specified as an object separate from the <code>AMP</code> instance.</p>
299
300     <pre class="python"><p class="py-linenumber"> 1
301  2
302  3
303  4
304  5
305  6
306  7
307  8
308  9
309 10
310 11
311 12
312 13
313 14
314 15
315 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
316
317 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span>.<span class="py-src-variable">amp</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IBoxReceiver</span>
318
319 <span class="py-src-keyword">class</span> <span class="py-src-identifier">BoxReflector</span>(<span class="py-src-parameter">object</span>):
320     <span class="py-src-variable">implements</span>(<span class="py-src-variable">IBoxReceiver</span>)
321
322     <span class="py-src-keyword">def</span> <span class="py-src-identifier">startReceivingBoxes</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">boxSender</span>):
323         <span class="py-src-variable">self</span>.<span class="py-src-variable">boxSender</span> = <span class="py-src-variable">boxSender</span>
324
325     <span class="py-src-keyword">def</span> <span class="py-src-identifier">ampBoxReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">box</span>):
326         <span class="py-src-variable">self</span>.<span class="py-src-variable">boxSender</span>.<span class="py-src-variable">sendBox</span>(<span class="py-src-variable">box</span>)
327
328     <span class="py-src-keyword">def</span> <span class="py-src-identifier">stopReceivingBoxes</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">reason</span>):
329         <span class="py-src-variable">self</span>.<span class="py-src-variable">boxSender</span> = <span class="py-src-variable">None</span>
330 </pre>
331
332     <p>These methods parallel those of <code>IProtocol</code>.  Startup notification is given by <code>startReceivingBoxes</code>.  The argument passed to it is an <code>IBoxSender</code> provider, which can be used to send boxes back out over the network.  <code>ampBoxReceived</code> delivers notification for a complete box having been received.  And last, <code>stopReceivingBoxes</code> notifies the object that no more boxes will be received and no more can be sent.  The argument passed to it is a <code>Failure</code> which may contain details about what caused the conversation to end.</p>
333
334     <p>To use a custom <code>IBoxReceiver</code>, pass it to the <code>AMP</code> initializer.</p>
335
336     <pre class="python"><p class="py-linenumber">1
337 2
338 </p><span class="py-src-variable">factory</span> = <span class="py-src-variable">Factory</span>()
339 <span class="py-src-variable">factory</span>.<span class="py-src-variable">protocol</span> = <span class="py-src-keyword">lambda</span>: <span class="py-src-variable">AMP</span>(<span class="py-src-variable">boxReceiver</span>=<span class="py-src-variable">BoxReflector</span>())
340 </pre>
341
342     <p>If no box receiver is passed in, <code>AMP</code> acts as its own box receiver.  It handles boxes by treating them as command requests or responses and delivering them to the appropriate responder or as a result to a <code>callRemote</code> <code>Deferred</code>.</p>
343
344   </div>
345
346     <p><a href="index.html">Index</a></p>
347     <span class="version">Version: 12.1.0</span>
348   </body>
349 </html>