blob: f870e12c72c60dd76b971a02a0ad17e19505841f [file] [log] [blame]
Georg Brandl8ec7f652007-08-15 14:28:01 +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
Fred Drakec9b71632007-10-05 02:46:12 +000012asynchronous clients and servers and making it easier to handle protocols
13whose elements are terminated by arbitrary strings, or are of variable length.
Georg Brandl8ec7f652007-08-15 14:28:01 +000014: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
Fred Drakec9b71632007-10-05 02:46:12 +000017:mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher`
18and :class:`asynchat.async_chat`, can freely be mixed in the channel map.
19Typically an :class:`asyncore.dispatcher` server channel generates new
20:class:`asynchat.async_chat` channel objects as it receives incoming
21connection requests.
Georg Brandl8ec7f652007-08-15 14:28:01 +000022
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
Fred Drakec9b71632007-10-05 02:46:12 +000028 meaningful :meth:`collect_incoming_data` and :meth:`found_terminator`
29 methods.
Georg Brandl8ec7f652007-08-15 14:28:01 +000030 The :class:`asyncore.dispatcher` methods can be used, although not all make
31 sense in a message/response context.
32
Fred Drakec9b71632007-10-05 02:46:12 +000033 Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of
34 events that are generated by an analysis of socket conditions after a
35 :cfunc:`select` call. Once the polling loop has been started the
36 :class:`async_chat` object's methods are called by the event-processing
37 framework with no action on the part of the programmer.
Georg Brandl8ec7f652007-08-15 14:28:01 +000038
Fred Drakec9b71632007-10-05 02:46:12 +000039 Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to
40 define a first-in-first-out queue (fifo) of *producers*. A producer need
41 have only one method, :meth:`more`, which should return data to be
42 transmitted on the channel.
Georg Brandl8ec7f652007-08-15 14:28:01 +000043 The producer indicates exhaustion (*i.e.* that it contains no more data) by
44 having its :meth:`more` method return the empty string. At this point the
Fred Drakec9b71632007-10-05 02:46:12 +000045 :class:`async_chat` object removes the producer from the fifo and starts
46 using the next producer, if any. When the producer fifo is empty the
Georg Brandl8ec7f652007-08-15 14:28:01 +000047 :meth:`handle_write` method does nothing. You use the channel object's
Fred Drakec9b71632007-10-05 02:46:12 +000048 :meth:`set_terminator` method to describe how to recognize the end of, or
49 an important breakpoint in, an incoming transmission from the remote
50 endpoint.
Georg Brandl8ec7f652007-08-15 14:28:01 +000051
52 To build a functioning :class:`async_chat` subclass your input methods
Fred Drakec9b71632007-10-05 02:46:12 +000053 :meth:`collect_incoming_data` and :meth:`found_terminator` must handle the
54 data that the channel receives asynchronously. The methods are described
55 below.
Georg Brandl8ec7f652007-08-15 14:28:01 +000056
57
58.. method:: async_chat.close_when_done()
59
Fred Drakec9b71632007-10-05 02:46:12 +000060 Pushes a ``None`` on to the producer fifo. When this producer is popped off
61 the fifo it causes the channel to be closed.
Georg Brandl8ec7f652007-08-15 14:28:01 +000062
63
64.. method:: async_chat.collect_incoming_data(data)
65
Fred Drakec9b71632007-10-05 02:46:12 +000066 Called with *data* holding an arbitrary amount of received data. The
67 default method, which must be overridden, raises a
68 :exc:`NotImplementedError` exception.
Georg Brandl8ec7f652007-08-15 14:28:01 +000069
70
71.. method:: async_chat.discard_buffers()
72
Fred Drakec9b71632007-10-05 02:46:12 +000073 In emergencies this method will discard any data held in the input and/or
74 output buffers and the producer fifo.
Georg Brandl8ec7f652007-08-15 14:28:01 +000075
76
77.. method:: async_chat.found_terminator()
78
Fred Drakec9b71632007-10-05 02:46:12 +000079 Called when the incoming data stream matches the termination condition set
80 by :meth:`set_terminator`. The default method, which must be overridden,
81 raises a :exc:`NotImplementedError` exception. The buffered input data
82 should be available via an instance attribute.
Georg Brandl8ec7f652007-08-15 14:28:01 +000083
84
85.. method:: async_chat.get_terminator()
86
87 Returns the current terminator for the channel.
88
89
90.. method:: async_chat.handle_close()
91
92 Called when the channel is closed. The default method silently closes the
93 channel's socket.
94
95
96.. method:: async_chat.handle_read()
97
Fred Drakec9b71632007-10-05 02:46:12 +000098 Called when a read event fires on the channel's socket in the asynchronous
99 loop. The default method checks for the termination condition established
100 by :meth:`set_terminator`, which can be either the appearance of a
101 particular string in the input stream or the receipt of a particular number
102 of characters. When the terminator is found, :meth:`handle_read` calls the
103 :meth:`found_terminator` method after calling :meth:`collect_incoming_data`
104 with any data preceding the terminating condition.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000105
106
107.. method:: async_chat.handle_write()
108
Fred Drakec9b71632007-10-05 02:46:12 +0000109 Called when the application may write data to the channel. The default
110 method calls the :meth:`initiate_send` method, which in turn will call
111 :meth:`refill_buffer` to collect data from the producer fifo associated
112 with the channel.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000113
114
115.. method:: async_chat.push(data)
116
Fred Drakec9b71632007-10-05 02:46:12 +0000117 Creates a :class:`simple_producer` object (*see below*) containing the data
118 and pushes it on to the channel's ``producer_fifo`` to ensure its
119 transmission. This is all you need to do to have the channel write the
120 data out to the network, although it is possible to use your own producers
121 in more complex schemes to implement encryption and chunking, for example.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000122
123
124.. method:: async_chat.push_with_producer(producer)
125
Fred Drakec9b71632007-10-05 02:46:12 +0000126 Takes a producer object and adds it to the producer fifo associated with
127 the channel. When all currently-pushed producers have been exhausted the
128 channel will consume this producer's data by calling its :meth:`more`
129 method and send the data to the remote endpoint.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000130
131
132.. method:: async_chat.readable()
133
Fred Drakec9b71632007-10-05 02:46:12 +0000134 Should return ``True`` for the channel to be included in the set of
135 channels tested by the :cfunc:`select` loop for readability.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000136
137
138.. method:: async_chat.refill_buffer()
139
Fred Drakec9b71632007-10-05 02:46:12 +0000140 Refills the output buffer by calling the :meth:`more` method of the
141 producer at the head of the fifo. If it is exhausted then the producer is
142 popped off the fifo and the next producer is activated. If the current
143 producer is, or becomes, ``None`` then the channel is closed.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000144
145
146.. method:: async_chat.set_terminator(term)
147
Fred Drakec9b71632007-10-05 02:46:12 +0000148 Sets the terminating condition to be recognized on the channel. ``term``
149 may be any of three types of value, corresponding to three different ways
150 to handle incoming protocol data.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000151
152 +-----------+---------------------------------------------+
153 | term | Description |
154 +===========+=============================================+
155 | *string* | Will call :meth:`found_terminator` when the |
156 | | string is found in the input stream |
157 +-----------+---------------------------------------------+
158 | *integer* | Will call :meth:`found_terminator` when the |
159 | | indicated number of characters have been |
160 | | received |
161 +-----------+---------------------------------------------+
162 | ``None`` | The channel continues to collect data |
163 | | forever |
164 +-----------+---------------------------------------------+
165
Fred Drakec9b71632007-10-05 02:46:12 +0000166 Note that any data following the terminator will be available for reading
167 by the channel after :meth:`found_terminator` is called.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000168
169
170.. method:: async_chat.writable()
171
172 Should return ``True`` as long as items remain on the producer fifo, or the
173 channel is connected and the channel's output buffer is non-empty.
174
175
176asynchat - Auxiliary Classes and Functions
177------------------------------------------
178
179
180.. class:: simple_producer(data[, buffer_size=512])
181
Fred Drakec9b71632007-10-05 02:46:12 +0000182 A :class:`simple_producer` takes a chunk of data and an optional buffer
183 size. Repeated calls to its :meth:`more` method yield successive chunks of
184 the data no larger than *buffer_size*.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000185
186
187.. method:: simple_producer.more()
188
Fred Drakec9b71632007-10-05 02:46:12 +0000189 Produces the next chunk of information from the producer, or returns the
190 empty string.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000191
192
193.. class:: fifo([list=None])
194
Fred Drakec9b71632007-10-05 02:46:12 +0000195 Each channel maintains a :class:`fifo` holding data which has been pushed
196 by the application but not yet popped for writing to the channel. A
197 :class:`fifo` is a list used to hold data and/or producers until they are
198 required. If the *list* argument is provided then it should contain
199 producers or data items to be written to the channel.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000200
201
202.. method:: fifo.is_empty()
203
Fred Drakec9b71632007-10-05 02:46:12 +0000204 Returns ``True`` if and only if the fifo is empty.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000205
206
207.. method:: fifo.first()
208
209 Returns the least-recently :meth:`push`\ ed item from the fifo.
210
211
212.. method:: fifo.push(data)
213
Fred Drakec9b71632007-10-05 02:46:12 +0000214 Adds the given data (which may be a string or a producer object) to the
215 producer fifo.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000216
217
218.. method:: fifo.pop()
219
Fred Drakec9b71632007-10-05 02:46:12 +0000220 If the fifo is not empty, returns ``True, first()``, deleting the popped
221 item. Returns ``False, None`` for an empty fifo.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000222
223The :mod:`asynchat` module also defines one utility function, which may be of
224use in network and textual analysis operations.
225
226
227.. function:: find_prefix_at_end(haystack, needle)
228
Fred Drakec9b71632007-10-05 02:46:12 +0000229 Returns ``True`` if string *haystack* ends with any non-empty prefix of
230 string *needle*.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000231
232
233.. _asynchat-example:
234
235asynchat Example
236----------------
237
238The following partial example shows how HTTP requests can be read with
Fred Drakec9b71632007-10-05 02:46:12 +0000239:class:`async_chat`. A web server might create an
240:class:`http_request_handler` object for each incoming client connection.
241Notice that initially the channel terminator is set to match the blank line at
242the end of the HTTP headers, and a flag indicates that the headers are being
243read.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000244
Fred Drakec9b71632007-10-05 02:46:12 +0000245Once the headers have been read, if the request is of type POST (indicating
246that further data are present in the input stream) then the
247``Content-Length:`` header is used to set a numeric terminator to read the
248right amount of data from the channel.
Georg Brandl8ec7f652007-08-15 14:28:01 +0000249
250The :meth:`handle_request` method is called once all relevant input has been
Fred Drakec9b71632007-10-05 02:46:12 +0000251marshalled, after setting the channel terminator to ``None`` to ensure that
252any extraneous data sent by the web client are ignored. ::
Georg Brandl8ec7f652007-08-15 14:28:01 +0000253
254 class http_request_handler(asynchat.async_chat):
255
256 def __init__(self, conn, addr, sessions, log):
257 asynchat.async_chat.__init__(self, conn=conn)
258 self.addr = addr
259 self.sessions = sessions
260 self.ibuffer = []
261 self.obuffer = ""
262 self.set_terminator("\r\n\r\n")
263 self.reading_headers = True
264 self.handling = False
265 self.cgi_data = None
266 self.log = log
267
268 def collect_incoming_data(self, data):
269 """Buffer the data"""
270 self.ibuffer.append(data)
271
272 def found_terminator(self):
273 if self.reading_headers:
274 self.reading_headers = False
275 self.parse_headers("".join(self.ibuffer))
276 self.ibuffer = []
277 if self.op.upper() == "POST":
278 clen = self.headers.getheader("content-length")
279 self.set_terminator(int(clen))
280 else:
281 self.handling = True
282 self.set_terminator(None)
283 self.handle_request()
284 elif not self.handling:
285 self.set_terminator(None) # browsers sometimes over-send
286 self.cgi_data = parse(self.headers, "".join(self.ibuffer))
287 self.handling = True
288 self.ibuffer = []
289 self.handle_request()