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