• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1:mod:`asynchat` --- Asynchronous socket command/response handler
2================================================================
3
4.. module:: asynchat
5   :synopsis: Support for asynchronous command/response protocols.
6.. moduleauthor:: Sam Rushing <rushing@nightmare.com>
7.. sectionauthor:: Steve Holden <sholden@holdenweb.com>
8
9**Source code:** :source:`Lib/asynchat.py`
10
11--------------
12
13This module builds on the :mod:`asyncore` infrastructure, simplifying
14asynchronous clients and servers and making it easier to handle protocols
15whose elements are terminated by arbitrary strings, or are of variable length.
16:mod:`asynchat` defines the abstract class :class:`async_chat` that you
17subclass, 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`
20and :class:`asynchat.async_chat`, can freely be mixed in the channel map.
21Typically an :class:`asyncore.dispatcher` server channel generates new
22:class:`asynchat.async_chat` channel objects as it receives incoming
23connection requests.
24
25
26.. class:: async_chat()
27
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`
31   methods.
32   The :class:`asyncore.dispatcher` methods can be used, although not all make
33   sense in a message/response context.
34
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.
40
41   Two class attributes can be modified, to improve performance, or possibly
42   even to conserve memory.
43
44
45   .. data:: ac_in_buffer_size
46
47      The asynchronous input buffer size (default ``4096``).
48
49
50   .. data:: ac_out_buffer_size
51
52      The asynchronous output buffer size (default ``4096``).
53
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
65   endpoint.
66
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
70   below.
71
72
73.. method:: async_chat.close_when_done()
74
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.
77
78
79.. method:: async_chat.collect_incoming_data(data)
80
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.
84
85
86.. method:: async_chat.discard_buffers()
87
88   In emergencies this method will discard any data held in the input and/or
89   output buffers and the producer fifo.
90
91
92.. method:: async_chat.found_terminator()
93
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.
98
99
100.. method:: async_chat.get_terminator()
101
102   Returns the current terminator for the channel.
103
104
105.. method:: async_chat.push(data)
106
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.
111
112
113.. method:: async_chat.push_with_producer(producer)
114
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.
119
120
121.. method:: async_chat.set_terminator(term)
122
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.
126
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    |
135   |           | received                                    |
136   +-----------+---------------------------------------------+
137   | ``None``  | The channel continues to collect data       |
138   |           | forever                                     |
139   +-----------+---------------------------------------------+
140
141   Note that any data following the terminator will be available for reading
142   by the channel after :meth:`found_terminator` is called.
143
144
145asynchat - Auxiliary Classes
146------------------------------------------
147
148.. class:: fifo([list=None])
149
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.
155
156
157   .. method:: is_empty()
158
159      Returns ``True`` if and only if the fifo is empty.
160
161
162   .. method:: first()
163
164      Returns the least-recently :meth:`push`\ ed item from the fifo.
165
166
167   .. method:: push(data)
168
169      Adds the given data (which may be a string or a producer object) to the
170      producer fifo.
171
172
173   .. method:: pop()
174
175      If the fifo is not empty, returns ``True, first()``, deleting the popped
176      item.  Returns ``False, None`` for an empty fifo.
177
178
179.. _asynchat-example:
180
181asynchat Example
182----------------
183
184The 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.
187Notice that initially the channel terminator is set to match the blank line at
188the end of the HTTP headers, and a flag indicates that the headers are being
189read.
190
191Once the headers have been read, if the request is of type POST (indicating
192that further data are present in the input stream) then the
193``Content-Length:`` header is used to set a numeric terminator to read the
194right amount of data from the channel.
195
196The :meth:`handle_request` method is called once all relevant input has been
197marshalled, after setting the channel terminator to ``None`` to ensure that
198any extraneous data sent by the web client are ignored. ::
199
200   class http_request_handler(asynchat.async_chat):
201
202       def __init__(self, sock, addr, sessions, log):
203           asynchat.async_chat.__init__(self, sock=sock)
204           self.addr = addr
205           self.sessions = sessions
206           self.ibuffer = []
207           self.obuffer = ""
208           self.set_terminator("\r\n\r\n")
209           self.reading_headers = True
210           self.handling = False
211           self.cgi_data = None
212           self.log = log
213
214       def collect_incoming_data(self, data):
215           """Buffer the data"""
216           self.ibuffer.append(data)
217
218       def found_terminator(self):
219           if self.reading_headers:
220               self.reading_headers = False
221               self.parse_headers("".join(self.ibuffer))
222               self.ibuffer = []
223               if self.op.upper() == "POST":
224                   clen = self.headers.getheader("content-length")
225                   self.set_terminator(int(clen))
226               else:
227                   self.handling = True
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))
233               self.handling = True
234               self.ibuffer = []
235               self.handle_request()
236