blob: b651c40399c3b26f21e69e5a0b3bd8e5eefba7e1 [file] [log] [blame]
Georg Brandl116aa622007-08-15 14:28:22 +00001
2:mod:`asynchat` --- Asynchronous socket command/response handler
3================================================================
4
5.. module:: asynchat
6 :synopsis: Support for asynchronous command/response protocols.
7.. moduleauthor:: Sam Rushing <rushing@nightmare.com>
8.. sectionauthor:: Steve Holden <sholden@holdenweb.com>
9
10
11This module builds on the :mod:`asyncore` infrastructure, simplifying
12asynchronous clients and servers and making it easier to handle protocols whose
13elements are terminated by arbitrary strings, or are of variable length.
14:mod:`asynchat` defines the abstract class :class:`async_chat` that you
15subclass, providing implementations of the :meth:`collect_incoming_data` and
16:meth:`found_terminator` methods. It uses the same asynchronous loop as
17:mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher` and
18:class:`asynchat.async_chat`, can freely be mixed in the channel map. Typically
19an :class:`asyncore.dispatcher` server channel generates new
20:class:`asynchat.async_chat` channel objects as it receives incoming connection
21requests.
22
23
24.. class:: async_chat()
25
26 This class is an abstract subclass of :class:`asyncore.dispatcher`. To make
27 practical use of the code you must subclass :class:`async_chat`, providing
28 meaningful :meth:`collect_incoming_data` and :meth:`found_terminator` methods.
29 The :class:`asyncore.dispatcher` methods can be used, although not all make
30 sense in a message/response context.
31
32 Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of events
33 that are generated by an analysis of socket conditions after a :cfunc:`select`
34 call. Once the polling loop has been started the :class:`async_chat` object's
35 methods are called by the event-processing framework with no action on the part
36 of the programmer.
37
38 Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to define a
39 first-in-first-out queue (fifo) of *producers*. A producer need have only one
40 method, :meth:`more`, which should return data to be transmitted on the channel.
41 The producer indicates exhaustion (*i.e.* that it contains no more data) by
42 having its :meth:`more` method return the empty string. At this point the
43 :class:`async_chat` object removes the producer from the fifo and starts using
44 the next producer, if any. When the producer fifo is empty the
45 :meth:`handle_write` method does nothing. You use the channel object's
46 :meth:`set_terminator` method to describe how to recognize the end of, or an
47 important breakpoint in, an incoming transmission from the remote endpoint.
48
49 To build a functioning :class:`async_chat` subclass your input methods
50 :meth:`collect_incoming_data` and :meth:`found_terminator` must handle the data
51 that the channel receives asynchronously. The methods are described below.
52
53
54.. method:: async_chat.close_when_done()
55
56 Pushes a ``None`` on to the producer fifo. When this producer is popped off the
57 fifo it causes the channel to be closed.
58
59
60.. method:: async_chat.collect_incoming_data(data)
61
62 Called with *data* holding an arbitrary amount of received data. The default
63 method, which must be overridden, raises a :exc:`NotImplementedError` exception.
64
65
66.. method:: async_chat.discard_buffers()
67
68 In emergencies this method will discard any data held in the input and/or output
69 buffers and the producer fifo.
70
71
72.. method:: async_chat.found_terminator()
73
74 Called when the incoming data stream matches the termination condition set by
75 :meth:`set_terminator`. The default method, which must be overridden, raises a
76 :exc:`NotImplementedError` exception. The buffered input data should be
77 available via an instance attribute.
78
79
80.. method:: async_chat.get_terminator()
81
82 Returns the current terminator for the channel.
83
84
85.. method:: async_chat.handle_close()
86
87 Called when the channel is closed. The default method silently closes the
88 channel's socket.
89
90
91.. method:: async_chat.handle_read()
92
93 Called when a read event fires on the channel's socket in the asynchronous loop.
94 The default method checks for the termination condition established by
95 :meth:`set_terminator`, which can be either the appearance of a particular
96 string in the input stream or the receipt of a particular number of characters.
97 When the terminator is found, :meth:`handle_read` calls the
98 :meth:`found_terminator` method after calling :meth:`collect_incoming_data` with
99 any data preceding the terminating condition.
100
101
102.. method:: async_chat.handle_write()
103
104 Called when the application may write data to the channel. The default method
105 calls the :meth:`initiate_send` method, which in turn will call
106 :meth:`refill_buffer` to collect data from the producer fifo associated with the
107 channel.
108
109
110.. method:: async_chat.push(data)
111
112 Creates a :class:`simple_producer` object (*see below*) containing the data and
113 pushes it on to the channel's ``producer_fifo`` to ensure its transmission. This
114 is all you need to do to have the channel write the data out to the network,
115 although it is possible to use your own producers in more complex schemes to
116 implement encryption and chunking, for example.
117
118
119.. method:: async_chat.push_with_producer(producer)
120
121 Takes a producer object and adds it to the producer fifo associated with the
122 channel. When all currently-pushed producers have been exhausted the channel
123 will consume this producer's data by calling its :meth:`more` method and send
124 the data to the remote endpoint.
125
126
127.. method:: async_chat.readable()
128
129 Should return ``True`` for the channel to be included in the set of channels
130 tested by the :cfunc:`select` loop for readability.
131
132
133.. method:: async_chat.refill_buffer()
134
135 Refills the output buffer by calling the :meth:`more` method of the producer at
136 the head of the fifo. If it is exhausted then the producer is popped off the
137 fifo and the next producer is activated. If the current producer is, or becomes,
138 ``None`` then the channel is closed.
139
140
141.. method:: async_chat.set_terminator(term)
142
143 Sets the terminating condition to be recognised on the channel. ``term`` may be
144 any of three types of value, corresponding to three different ways to handle
145 incoming protocol data.
146
147 +-----------+---------------------------------------------+
148 | term | Description |
149 +===========+=============================================+
150 | *string* | Will call :meth:`found_terminator` when the |
151 | | string is found in the input stream |
152 +-----------+---------------------------------------------+
153 | *integer* | Will call :meth:`found_terminator` when the |
154 | | indicated number of characters have been |
155 | | received |
156 +-----------+---------------------------------------------+
157 | ``None`` | The channel continues to collect data |
158 | | forever |
159 +-----------+---------------------------------------------+
160
161 Note that any data following the terminator will be available for reading by the
162 channel after :meth:`found_terminator` is called.
163
164
165.. method:: async_chat.writable()
166
167 Should return ``True`` as long as items remain on the producer fifo, or the
168 channel is connected and the channel's output buffer is non-empty.
169
170
171asynchat - Auxiliary Classes and Functions
172------------------------------------------
173
174
175.. class:: simple_producer(data[, buffer_size=512])
176
177 A :class:`simple_producer` takes a chunk of data and an optional buffer size.
178 Repeated calls to its :meth:`more` method yield successive chunks of the data no
179 larger than *buffer_size*.
180
181
182.. method:: simple_producer.more()
183
184 Produces the next chunk of information from the producer, or returns the empty
185 string.
186
187
188.. class:: fifo([list=None])
189
190 Each channel maintains a :class:`fifo` holding data which has been pushed by the
191 application but not yet popped for writing to the channel. A :class:`fifo` is a
192 list used to hold data and/or producers until they are required. If the *list*
193 argument is provided then it should contain producers or data items to be
194 written to the channel.
195
196
197.. method:: fifo.is_empty()
198
199 Returns ``True`` iff the fifo is empty.
200
201
202.. method:: fifo.first()
203
204 Returns the least-recently :meth:`push`\ ed item from the fifo.
205
206
207.. method:: fifo.push(data)
208
209 Adds the given data (which may be a string or a producer object) to the producer
210 fifo.
211
212
213.. method:: fifo.pop()
214
215 If the fifo is not empty, returns ``True, first()``, deleting the popped item.
216 Returns ``False, None`` for an empty fifo.
217
218The :mod:`asynchat` module also defines one utility function, which may be of
219use in network and textual analysis operations.
220
221
222.. function:: find_prefix_at_end(haystack, needle)
223
224 Returns ``True`` if string *haystack* ends with any non-empty prefix of string
225 *needle*.
226
227
228.. _asynchat-example:
229
230asynchat Example
231----------------
232
233The following partial example shows how HTTP requests can be read with
234:class:`async_chat`. A web server might create an :class:`http_request_handler`
235object for each incoming client connection. Notice that initially the channel
236terminator is set to match the blank line at the end of the HTTP headers, and a
237flag indicates that the headers are being read.
238
239Once the headers have been read, if the request is of type POST (indicating that
240further data are present in the input stream) then the ``Content-Length:``
241header is used to set a numeric terminator to read the right amount of data from
242the channel.
243
244The :meth:`handle_request` method is called once all relevant input has been
245marshalled, after setting the channel terminator to ``None`` to ensure that any
246extraneous data sent by the web client are ignored. ::
247
248 class http_request_handler(asynchat.async_chat):
249
250 def __init__(self, conn, addr, sessions, log):
251 asynchat.async_chat.__init__(self, conn=conn)
252 self.addr = addr
253 self.sessions = sessions
254 self.ibuffer = []
255 self.obuffer = ""
256 self.set_terminator("\r\n\r\n")
257 self.reading_headers = True
258 self.handling = False
259 self.cgi_data = None
260 self.log = log
261
262 def collect_incoming_data(self, data):
263 """Buffer the data"""
264 self.ibuffer.append(data)
265
266 def found_terminator(self):
267 if self.reading_headers:
268 self.reading_headers = False
269 self.parse_headers("".join(self.ibuffer))
270 self.ibuffer = []
271 if self.op.upper() == "POST":
272 clen = self.headers.getheader("content-length")
273 self.set_terminator(int(clen))
274 else:
275 self.handling = True
276 self.set_terminator(None)
277 self.handle_request()
278 elif not self.handling:
279 self.set_terminator(None) # browsers sometimes over-send
280 self.cgi_data = parse(self.headers, "".join(self.ibuffer))
281 self.handling = True
282 self.ibuffer = []
283 self.handle_request()
284