1
:mod:`asynchat` --- Asynchronous socket command/response handler
2
================================================================
5
:synopsis: Support for asynchronous command/response protocols.
6
.. moduleauthor:: Sam Rushing <rushing@nightmare.com>
7
.. sectionauthor:: Steve Holden <sholden@holdenweb.com>
9
**Source code:** :source:`Lib/asynchat.py`
13
This module builds on the :mod:`asyncore` infrastructure, simplifying
14
asynchronous clients and servers and making it easier to handle protocols
15
whose elements are terminated by arbitrary strings, or are of variable length.
16
:mod:`asynchat` defines the abstract class :class:`async_chat` that you
17
subclass, providing implementations of the :meth:`collect_incoming_data` and
18
:meth:`found_terminator` methods. It uses the same asynchronous loop as
19
:mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher`
20
and :class:`asynchat.async_chat`, can freely be mixed in the channel map.
21
Typically an :class:`asyncore.dispatcher` server channel generates new
22
:class:`asynchat.async_chat` channel objects as it receives incoming
26
.. class:: async_chat()
28
This class is an abstract subclass of :class:`asyncore.dispatcher`. To make
29
practical use of the code you must subclass :class:`async_chat`, providing
30
meaningful :meth:`collect_incoming_data` and :meth:`found_terminator`
32
The :class:`asyncore.dispatcher` methods can be used, although not all make
33
sense in a message/response context.
35
Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of
36
events that are generated by an analysis of socket conditions after a
37
:c:func:`select` call. Once the polling loop has been started the
38
:class:`async_chat` object's methods are called by the event-processing
39
framework with no action on the part of the programmer.
41
Two class attributes can be modified, to improve performance, or possibly
42
even to conserve memory.
45
.. data:: ac_in_buffer_size
47
The asynchronous input buffer size (default ``4096``).
50
.. data:: ac_out_buffer_size
52
The asynchronous output buffer size (default ``4096``).
54
Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to
55
define a first-in-first-out queue (fifo) of *producers*. A producer need
56
have only one method, :meth:`more`, which should return data to be
57
transmitted on the channel.
58
The producer indicates exhaustion (*i.e.* that it contains no more data) by
59
having its :meth:`more` method return the empty string. At this point the
60
:class:`async_chat` object removes the producer from the fifo and starts
61
using the next producer, if any. When the producer fifo is empty the
62
:meth:`handle_write` method does nothing. You use the channel object's
63
:meth:`set_terminator` method to describe how to recognize the end of, or
64
an important breakpoint in, an incoming transmission from the remote
67
To build a functioning :class:`async_chat` subclass your input methods
68
:meth:`collect_incoming_data` and :meth:`found_terminator` must handle the
69
data that the channel receives asynchronously. The methods are described
73
.. method:: async_chat.close_when_done()
75
Pushes a ``None`` on to the producer fifo. When this producer is popped off
76
the fifo it causes the channel to be closed.
79
.. method:: async_chat.collect_incoming_data(data)
81
Called with *data* holding an arbitrary amount of received data. The
82
default method, which must be overridden, raises a
83
:exc:`NotImplementedError` exception.
86
.. method:: async_chat.discard_buffers()
88
In emergencies this method will discard any data held in the input and/or
89
output buffers and the producer fifo.
92
.. method:: async_chat.found_terminator()
94
Called when the incoming data stream matches the termination condition set
95
by :meth:`set_terminator`. The default method, which must be overridden,
96
raises a :exc:`NotImplementedError` exception. The buffered input data
97
should be available via an instance attribute.
100
.. method:: async_chat.get_terminator()
102
Returns the current terminator for the channel.
105
.. method:: async_chat.push(data)
107
Pushes data on to the channel's fifo to ensure its transmission.
108
This is all you need to do to have the channel write the data out to the
109
network, although it is possible to use your own producers in more complex
110
schemes to implement encryption and chunking, for example.
113
.. method:: async_chat.push_with_producer(producer)
115
Takes a producer object and adds it to the producer fifo associated with
116
the channel. When all currently-pushed producers have been exhausted the
117
channel will consume this producer's data by calling its :meth:`more`
118
method and send the data to the remote endpoint.
121
.. method:: async_chat.set_terminator(term)
123
Sets the terminating condition to be recognized on the channel. ``term``
124
may be any of three types of value, corresponding to three different ways
125
to handle incoming protocol data.
127
+-----------+---------------------------------------------+
128
| term | Description |
129
+===========+=============================================+
130
| *string* | Will call :meth:`found_terminator` when the |
131
| | string is found in the input stream |
132
+-----------+---------------------------------------------+
133
| *integer* | Will call :meth:`found_terminator` when the |
134
| | indicated number of characters have been |
136
+-----------+---------------------------------------------+
137
| ``None`` | The channel continues to collect data |
139
+-----------+---------------------------------------------+
141
Note that any data following the terminator will be available for reading
142
by the channel after :meth:`found_terminator` is called.
145
asynchat - Auxiliary Classes
146
------------------------------------------
148
.. class:: fifo([list=None])
150
A :class:`fifo` holding data which has been pushed by the application but
151
not yet popped for writing to the channel. A :class:`fifo` is a list used
152
to hold data and/or producers until they are required. If the *list*
153
argument is provided then it should contain producers or data items to be
154
written to the channel.
157
.. method:: is_empty()
159
Returns ``True`` if and only if the fifo is empty.
164
Returns the least-recently :meth:`push`\ ed item from the fifo.
167
.. method:: push(data)
169
Adds the given data (which may be a string or a producer object) to the
175
If the fifo is not empty, returns ``True, first()``, deleting the popped
176
item. Returns ``False, None`` for an empty fifo.
179
.. _asynchat-example:
184
The following partial example shows how HTTP requests can be read with
185
:class:`async_chat`. A web server might create an
186
:class:`http_request_handler` object for each incoming client connection.
187
Notice that initially the channel terminator is set to match the blank line at
188
the end of the HTTP headers, and a flag indicates that the headers are being
191
Once the headers have been read, if the request is of type POST (indicating
192
that further data are present in the input stream) then the
193
``Content-Length:`` header is used to set a numeric terminator to read the
194
right amount of data from the channel.
196
The :meth:`handle_request` method is called once all relevant input has been
197
marshalled, after setting the channel terminator to ``None`` to ensure that
198
any extraneous data sent by the web client are ignored. ::
200
class http_request_handler(asynchat.async_chat):
202
def __init__(self, sock, addr, sessions, log):
203
asynchat.async_chat.__init__(self, sock=sock)
205
self.sessions = sessions
208
self.set_terminator("\r\n\r\n")
209
self.reading_headers = True
210
self.handling = False
214
def collect_incoming_data(self, data):
215
"""Buffer the data"""
216
self.ibuffer.append(data)
218
def found_terminator(self):
219
if self.reading_headers:
220
self.reading_headers = False
221
self.parse_headers("".join(self.ibuffer))
223
if self.op.upper() == "POST":
224
clen = self.headers.getheader("content-length")
225
self.set_terminator(int(clen))
228
self.set_terminator(None)
229
self.handle_request()
230
elif not self.handling:
231
self.set_terminator(None) # browsers sometimes over-send
232
self.cgi_data = parse(self.headers, "".join(self.ibuffer))
235
self.handle_request()