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