asynchat — Asynchronous socket command/response handler

Source code:Lib/asynchat.py

Deprecated since version 3.6:Please useasyncio instead.


Note

This module exists for backwards compatibility only. For new code werecommend usingasyncio.

This module builds on theasyncore infrastructure, simplifyingasynchronous clients and servers and making it easier to handle protocolswhose elements are terminated by arbitrary strings, or are of variable length.asynchat defines the abstract classasync_chat that yousubclass, providing implementations of thecollect_incoming_data() andfound_terminator() methods. It uses the same asynchronous loop asasyncore, and the two types of channel,asyncore.dispatcherandasynchat.async_chat, can freely be mixed in the channel map.Typically anasyncore.dispatcher server channel generates newasynchat.async_chat channel objects as it receives incomingconnection requests.

classasynchat.async_chat

This class is an abstract subclass ofasyncore.dispatcher. To makepractical use of the code you must subclassasync_chat, providingmeaningfulcollect_incoming_data() andfound_terminator()methods.Theasyncore.dispatcher methods can be used, although not all makesense in a message/response context.

Likeasyncore.dispatcher,async_chat defines a set ofevents that are generated by an analysis of socket conditions after aselect() call. Once the polling loop has been started theasync_chat object’s methods are called by the event-processingframework with no action on the part of the programmer.

Two class attributes can be modified, to improve performance, or possiblyeven to conserve memory.

ac_in_buffer_size

The asynchronous input buffer size (default4096).

ac_out_buffer_size

The asynchronous output buffer size (default4096).

Unlikeasyncore.dispatcher,async_chat allows you todefine aFIFO queue ofproducers. A producer needhave only one method,more(), which should return data to betransmitted on the channel.The producer indicates exhaustion (i.e. that it contains no more data) byhaving itsmore() method return the empty bytes object. At this pointtheasync_chat object removes the producer from the queue and startsusing the next producer, if any. When the producer queue is empty thehandle_write() method does nothing. You use the channel object’sset_terminator() method to describe how to recognize the end of, oran important breakpoint in, an incoming transmission from the remoteendpoint.

To build a functioningasync_chat subclass your input methodscollect_incoming_data() andfound_terminator() must handle thedata that the channel receives asynchronously. The methods are describedbelow.

async_chat.close_when_done()

Pushes aNone on to the producer queue. When this producer is popped offthe queue it causes the channel to be closed.

async_chat.collect_incoming_data(data)

Called withdata holding an arbitrary amount of received data. Thedefault method, which must be overridden, raises aNotImplementedError exception.

async_chat.discard_buffers()

In emergencies this method will discard any data held in the input and/oroutput buffers and the producer queue.

async_chat.found_terminator()

Called when the incoming data stream matches the termination condition setbyset_terminator(). The default method, which must be overridden,raises aNotImplementedError exception. The buffered input datashould be available via an instance attribute.

async_chat.get_terminator()

Returns the current terminator for the channel.

async_chat.push(data)

Pushes data on to the channel’s queue to ensure its transmission.This is all you need to do to have the channel write the data out to thenetwork, although it is possible to use your own producers in more complexschemes to implement encryption and chunking, for example.

async_chat.push_with_producer(producer)

Takes a producer object and adds it to the producer queue associated withthe channel. When all currently-pushed producers have been exhausted thechannel will consume this producer’s data by calling itsmore()method and send the data to the remote endpoint.

async_chat.set_terminator(term)

Sets the terminating condition to be recognized on the channel.termmay be any of three types of value, corresponding to three different waysto handle incoming protocol data.

term

Description

string

Will callfound_terminator() when thestring is found in the input stream

integer

Will callfound_terminator() when theindicated number of characters have beenreceived

None

The channel continues to collect dataforever

Note that any data following the terminator will be available for readingby the channel afterfound_terminator() is called.

asynchat Example

The following partial example shows how HTTP requests can be read withasync_chat. A web server might create anhttp_request_handler object for each incoming client connection.Notice that initially the channel terminator is set to match the blank line atthe end of the HTTP headers, and a flag indicates that the headers are beingread.

Once the headers have been read, if the request is of type POST (indicatingthat further data are present in the input stream) then theContent-Length: header is used to set a numeric terminator to read theright amount of data from the channel.

Thehandle_request() method is called once all relevant input has beenmarshalled, after setting the channel terminator toNone to ensure thatany extraneous data sent by the web client are ignored.

importasynchatclasshttp_request_handler(asynchat.async_chat):def__init__(self,sock,addr,sessions,log):asynchat.async_chat.__init__(self,sock=sock)self.addr=addrself.sessions=sessionsself.ibuffer=[]self.obuffer=b""self.set_terminator(b"\r\n\r\n")self.reading_headers=Trueself.handling=Falseself.cgi_data=Noneself.log=logdefcollect_incoming_data(self,data):"""Buffer the data"""self.ibuffer.append(data)deffound_terminator(self):ifself.reading_headers:self.reading_headers=Falseself.parse_headers(b"".join(self.ibuffer))self.ibuffer=[]ifself.op.upper()==b"POST":clen=self.headers.getheader("content-length")self.set_terminator(int(clen))else:self.handling=Trueself.set_terminator(None)self.handle_request()elifnotself.handling:self.set_terminator(None)# browsers sometimes over-sendself.cgi_data=parse(self.headers,b"".join(self.ibuffer))self.handling=Trueself.ibuffer=[]self.handle_request()