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

``

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

`