Home | History | Annotate | Download | only in library
      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 (a] nightmare.com>
      7 .. sectionauthor:: Steve Holden <sholden (a] holdenweb.com>
      8 
      9 **Source code:** :source:`Lib/asynchat.py`
     10 
     11 --------------
     12 
     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
     23 connection 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 
    145 asynchat - 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 
    181 asynchat Example
    182 ----------------
    183 
    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
    189 read.
    190 
    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.
    195 
    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. ::
    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