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 
      7 .. moduleauthor:: Sam Rushing <rushing (a] nightmare.com>
      8 .. sectionauthor:: Steve Holden <sholden (a] holdenweb.com>
      9 
     10 **Source code:** :source:`Lib/asynchat.py`
     11 
     12 .. deprecated:: 3.6
     13    Please use :mod:`asyncio` instead.
     14 
     15 --------------
     16 
     17 .. note::
     18 
     19    This module exists for backwards compatibility only.  For new code we
     20    recommend using :mod:`asyncio`.
     21 
     22 This module builds on the :mod:`asyncore` infrastructure, simplifying
     23 asynchronous clients and servers and making it easier to handle protocols
     24 whose elements are terminated by arbitrary strings, or are of variable length.
     25 :mod:`asynchat` defines the abstract class :class:`async_chat` that you
     26 subclass, providing implementations of the :meth:`collect_incoming_data` and
     27 :meth:`found_terminator` methods. It uses the same asynchronous loop as
     28 :mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher`
     29 and :class:`asynchat.async_chat`, can freely be mixed in the channel map.
     30 Typically an :class:`asyncore.dispatcher` server channel generates new
     31 :class:`asynchat.async_chat` channel objects as it receives incoming
     32 connection requests.
     33 
     34 
     35 .. class:: async_chat()
     36 
     37    This class is an abstract subclass of :class:`asyncore.dispatcher`. To make
     38    practical use of the code you must subclass :class:`async_chat`, providing
     39    meaningful :meth:`collect_incoming_data` and :meth:`found_terminator`
     40    methods.
     41    The :class:`asyncore.dispatcher` methods can be used, although not all make
     42    sense in a message/response context.
     43 
     44    Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of
     45    events that are generated by an analysis of socket conditions after a
     46    :c:func:`select` call. Once the polling loop has been started the
     47    :class:`async_chat` object's methods are called by the event-processing
     48    framework with no action on the part of the programmer.
     49 
     50    Two class attributes can be modified, to improve performance, or possibly
     51    even to conserve memory.
     52 
     53 
     54    .. data:: ac_in_buffer_size
     55 
     56       The asynchronous input buffer size (default ``4096``).
     57 
     58 
     59    .. data:: ac_out_buffer_size
     60 
     61       The asynchronous output buffer size (default ``4096``).
     62 
     63    Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to
     64    define a :abbr:`FIFO (first-in, first-out)` queue of *producers*. A producer need
     65    have only one method, :meth:`more`, which should return data to be
     66    transmitted on the channel.
     67    The producer indicates exhaustion (*i.e.* that it contains no more data) by
     68    having its :meth:`more` method return the empty bytes object. At this point
     69    the :class:`async_chat` object removes the producer from the queue and starts
     70    using the next producer, if any. When the producer queue is empty the
     71    :meth:`handle_write` method does nothing. You use the channel object's
     72    :meth:`set_terminator` method to describe how to recognize the end of, or
     73    an important breakpoint in, an incoming transmission from the remote
     74    endpoint.
     75 
     76    To build a functioning :class:`async_chat` subclass your  input methods
     77    :meth:`collect_incoming_data` and :meth:`found_terminator` must handle the
     78    data that the channel receives asynchronously. The methods are described
     79    below.
     80 
     81 
     82 .. method:: async_chat.close_when_done()
     83 
     84    Pushes a ``None`` on to the producer queue. When this producer is popped off
     85    the queue it causes the channel to be closed.
     86 
     87 
     88 .. method:: async_chat.collect_incoming_data(data)
     89 
     90    Called with *data* holding an arbitrary amount of received data.  The
     91    default method, which must be overridden, raises a
     92    :exc:`NotImplementedError` exception.
     93 
     94 
     95 .. method:: async_chat.discard_buffers()
     96 
     97    In emergencies this method will discard any data held in the input and/or
     98    output buffers and the producer queue.
     99 
    100 
    101 .. method:: async_chat.found_terminator()
    102 
    103    Called when the incoming data stream  matches the termination condition set
    104    by :meth:`set_terminator`. The default method, which must be overridden,
    105    raises a :exc:`NotImplementedError` exception. The buffered input data
    106    should be available via an instance attribute.
    107 
    108 
    109 .. method:: async_chat.get_terminator()
    110 
    111    Returns the current terminator for the channel.
    112 
    113 
    114 .. method:: async_chat.push(data)
    115 
    116    Pushes data on to the channel's queue to ensure its transmission.
    117    This is all you need to do to have the channel write the data out to the
    118    network, although it is possible to use your own producers in more complex
    119    schemes to implement encryption and chunking, for example.
    120 
    121 
    122 .. method:: async_chat.push_with_producer(producer)
    123 
    124    Takes a producer object and adds it to the producer queue associated with
    125    the channel.  When all currently-pushed producers have been exhausted the
    126    channel will consume this producer's data by calling its :meth:`more`
    127    method and send the data to the remote endpoint.
    128 
    129 
    130 .. method:: async_chat.set_terminator(term)
    131 
    132    Sets the terminating condition to be recognized on the channel.  ``term``
    133    may be any of three types of value, corresponding to three different ways
    134    to handle incoming protocol data.
    135 
    136    +-----------+---------------------------------------------+
    137    | term      | Description                                 |
    138    +===========+=============================================+
    139    | *string*  | Will call :meth:`found_terminator` when the |
    140    |           | string is found in the input stream         |
    141    +-----------+---------------------------------------------+
    142    | *integer* | Will call :meth:`found_terminator` when the |
    143    |           | indicated number of characters have been    |
    144    |           | received                                    |
    145    +-----------+---------------------------------------------+
    146    | ``None``  | The channel continues to collect data       |
    147    |           | forever                                     |
    148    +-----------+---------------------------------------------+
    149 
    150    Note that any data following the terminator will be available for reading
    151    by the channel after :meth:`found_terminator` is called.
    152 
    153 
    154 .. _asynchat-example:
    155 
    156 asynchat Example
    157 ----------------
    158 
    159 The following partial example shows how HTTP requests can be read with
    160 :class:`async_chat`.  A web server might create an
    161 :class:`http_request_handler` object for each incoming client connection.
    162 Notice that initially the channel terminator is set to match the blank line at
    163 the end of the HTTP headers, and a flag indicates that the headers are being
    164 read.
    165 
    166 Once the headers have been read, if the request is of type POST (indicating
    167 that further data are present in the input stream) then the
    168 ``Content-Length:`` header is used to set a numeric terminator to read the
    169 right amount of data from the channel.
    170 
    171 The :meth:`handle_request` method is called once all relevant input has been
    172 marshalled, after setting the channel terminator to ``None`` to ensure that
    173 any extraneous data sent by the web client are ignored. ::
    174 
    175 
    176    import asynchat
    177 
    178    class http_request_handler(asynchat.async_chat):
    179 
    180        def __init__(self, sock, addr, sessions, log):
    181            asynchat.async_chat.__init__(self, sock=sock)
    182            self.addr = addr
    183            self.sessions = sessions
    184            self.ibuffer = []
    185            self.obuffer = b""
    186            self.set_terminator(b"\r\n\r\n")
    187            self.reading_headers = True
    188            self.handling = False
    189            self.cgi_data = None
    190            self.log = log
    191 
    192        def collect_incoming_data(self, data):
    193            """Buffer the data"""
    194            self.ibuffer.append(data)
    195 
    196        def found_terminator(self):
    197            if self.reading_headers:
    198                self.reading_headers = False
    199                self.parse_headers(b"".join(self.ibuffer))
    200                self.ibuffer = []
    201                if self.op.upper() == b"POST":
    202                    clen = self.headers.getheader("content-length")
    203                    self.set_terminator(int(clen))
    204                else:
    205                    self.handling = True
    206                    self.set_terminator(None)
    207                    self.handle_request()
    208            elif not self.handling:
    209                self.set_terminator(None)  # browsers sometimes over-send
    210                self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
    211                self.handling = True
    212                self.ibuffer = []
    213                self.handle_request()
    214