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@nightmare.com> 8.. sectionauthor:: Steve Holden <sholden@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 22This module builds on the :mod:`asyncore` infrastructure, simplifying 23asynchronous clients and servers and making it easier to handle protocols 24whose elements are terminated by arbitrary strings, or are of variable length. 25:mod:`asynchat` defines the abstract class :class:`async_chat` that you 26subclass, 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` 29and :class:`asynchat.async_chat`, can freely be mixed in the channel map. 30Typically an :class:`asyncore.dispatcher` server channel generates new 31:class:`asynchat.async_chat` channel objects as it receives incoming 32connection 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 156asynchat Example 157---------------- 158 159The 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. 162Notice that initially the channel terminator is set to match the blank line at 163the end of the HTTP headers, and a flag indicates that the headers are being 164read. 165 166Once the headers have been read, if the request is of type POST (indicating 167that further data are present in the input stream) then the 168``Content-Length:`` header is used to set a numeric terminator to read the 169right amount of data from the channel. 170 171The :meth:`handle_request` method is called once all relevant input has been 172marshalled, after setting the channel terminator to ``None`` to ensure that 173any 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