Revert "bpo-28533: Remove asyncore, asynchat, smtpd modules (GH-29521… · python/cpython@cf7eaa4

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+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()