blob: ce8505777a2efee207e49a6db106b9fd0c662772 [file] [log] [blame]
R. David Murraya21e4ca2009-03-31 23:16:50 +00001# test asynchat
Guido van Rossum66172522001-04-06 16:32:22 +00002
R. David Murraya21e4ca2009-03-31 23:16:50 +00003from test import support
4
Victor Stinnerfd5d1b52014-07-08 00:16:54 +02005import asynchat
6import asyncore
Victor Stinner45cff662014-07-24 18:49:36 +02007import errno
Victor Stinnerfd5d1b52014-07-08 00:16:54 +02008import socket
Guido van Rossum806c2462007-08-06 23:33:07 +00009import sys
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020010import threading
Victor Stinnerfd5d1b52014-07-08 00:16:54 +020011import time
12import unittest
Victor Stinner45cff662014-07-24 18:49:36 +020013import unittest.mock
Guido van Rossum66172522001-04-06 16:32:22 +000014
Benjamin Petersonee8712c2008-05-20 21:35:26 +000015HOST = support.HOST
Guido van Rossum806c2462007-08-06 23:33:07 +000016SERVER_QUIT = b'QUIT\n'
Guido van Rossum66172522001-04-06 16:32:22 +000017
Guido van Rossum66172522001-04-06 16:32:22 +000018
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020019class echo_server(threading.Thread):
20 # parameter to determine the number of bytes passed back to the
21 # client each send
22 chunk_size = 1
Christian Heimesaf98da12008-01-27 15:18:18 +000023
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020024 def __init__(self, event):
25 threading.Thread.__init__(self)
26 self.event = event
27 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
28 self.port = support.bind_port(self.sock)
29 # This will be set if the client wants us to wait before echoing
30 # data back.
31 self.start_resend_event = None
Guido van Rossum806c2462007-08-06 23:33:07 +000032
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020033 def run(self):
34 self.sock.listen()
35 self.event.set()
36 conn, client = self.sock.accept()
37 self.buffer = b""
38 # collect data until quit message is seen
39 while SERVER_QUIT not in self.buffer:
40 data = conn.recv(1)
41 if not data:
42 break
43 self.buffer = self.buffer + data
Guido van Rossum806c2462007-08-06 23:33:07 +000044
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020045 # remove the SERVER_QUIT message
46 self.buffer = self.buffer.replace(SERVER_QUIT, b'')
Collin Winter8641c562010-03-17 23:49:15 +000047
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020048 if self.start_resend_event:
49 self.start_resend_event.wait()
50
51 # re-send entire set of collected data
52 try:
53 # this may fail on some tests, such as test_close_when_done,
54 # since the client closes the channel when it's done sending
55 while self.buffer:
56 n = conn.send(self.buffer[:self.chunk_size])
57 time.sleep(0.001)
58 self.buffer = self.buffer[n:]
59 except:
60 pass
61
62 conn.close()
63 self.sock.close()
64
65class echo_client(asynchat.async_chat):
66
67 def __init__(self, terminator, server_port):
68 asynchat.async_chat.__init__(self)
69 self.contents = []
70 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
71 self.connect((HOST, server_port))
72 self.set_terminator(terminator)
73 self.buffer = b""
74
Adam Johnson892221b2019-11-19 19:45:20 +000075 def handle_connect(self):
76 pass
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020077
Adam Johnson892221b2019-11-19 19:45:20 +000078 if sys.platform == 'darwin':
79 # select.poll returns a select.POLLHUP at the end of the tests
80 # on darwin, so just ignore it
81 def handle_expt(self):
82 pass
Guido van Rossum806c2462007-08-06 23:33:07 +000083
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020084 def collect_incoming_data(self, data):
85 self.buffer += data
Guido van Rossum66172522001-04-06 16:32:22 +000086
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020087 def found_terminator(self):
88 self.contents.append(self.buffer)
89 self.buffer = b""
Guido van Rossum66172522001-04-06 16:32:22 +000090
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020091def start_echo_server():
92 event = threading.Event()
93 s = echo_server(event)
94 s.start()
95 event.wait()
96 event.clear()
97 time.sleep(0.01) # Give server time to start accepting.
98 return s, event
Guido van Rossum66172522001-04-06 16:32:22 +000099
Guido van Rossum66172522001-04-06 16:32:22 +0000100
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000101class TestAsynchat(unittest.TestCase):
Guido van Rossum806c2462007-08-06 23:33:07 +0000102 usepoll = False
103
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200104 def setUp(self):
Antoine Pitroue03866f2009-10-30 17:58:27 +0000105 self._threads = support.threading_setup()
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000106
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200107 def tearDown(self):
Antoine Pitroue03866f2009-10-30 17:58:27 +0000108 support.threading_cleanup(*self._threads)
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000109
Guido van Rossum806c2462007-08-06 23:33:07 +0000110 def line_terminator_check(self, term, server_chunk):
Christian Heimesaf98da12008-01-27 15:18:18 +0000111 event = threading.Event()
112 s = echo_server(event)
Guido van Rossum806c2462007-08-06 23:33:07 +0000113 s.chunk_size = server_chunk
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000114 s.start()
Christian Heimesaf98da12008-01-27 15:18:18 +0000115 event.wait()
116 event.clear()
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200117 time.sleep(0.01) # Give server time to start accepting.
Christian Heimes5e696852008-04-09 08:37:03 +0000118 c = echo_client(term, s.port)
Guido van Rossum806c2462007-08-06 23:33:07 +0000119 c.push(b"hello ")
Josiah Carlsond74900e2008-07-07 04:15:08 +0000120 c.push(b"world" + term)
121 c.push(b"I'm not dead yet!" + term)
Guido van Rossum806c2462007-08-06 23:33:07 +0000122 c.push(SERVER_QUIT)
123 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100124 support.join_thread(s)
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000125
Guido van Rossum806c2462007-08-06 23:33:07 +0000126 self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"])
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000127
Guido van Rossum806c2462007-08-06 23:33:07 +0000128 # the line terminator tests below check receiving variously-sized
129 # chunks back from the server in order to exercise all branches of
130 # async_chat.handle_read
131
132 def test_line_terminator1(self):
133 # test one-character terminator
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200134 for l in (1, 2, 3):
Josiah Carlsond74900e2008-07-07 04:15:08 +0000135 self.line_terminator_check(b'\n', l)
Guido van Rossum806c2462007-08-06 23:33:07 +0000136
137 def test_line_terminator2(self):
138 # test two-character terminator
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200139 for l in (1, 2, 3):
Josiah Carlsond74900e2008-07-07 04:15:08 +0000140 self.line_terminator_check(b'\r\n', l)
Guido van Rossum806c2462007-08-06 23:33:07 +0000141
142 def test_line_terminator3(self):
143 # test three-character terminator
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200144 for l in (1, 2, 3):
Josiah Carlsond74900e2008-07-07 04:15:08 +0000145 self.line_terminator_check(b'qqq', l)
Guido van Rossum806c2462007-08-06 23:33:07 +0000146
147 def numeric_terminator_check(self, termlen):
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000148 # Try reading a fixed number of bytes
Christian Heimesaf98da12008-01-27 15:18:18 +0000149 s, event = start_echo_server()
Christian Heimes5e696852008-04-09 08:37:03 +0000150 c = echo_client(termlen, s.port)
Guido van Rossum806c2462007-08-06 23:33:07 +0000151 data = b"hello world, I'm not dead yet!\n"
152 c.push(data)
153 c.push(SERVER_QUIT)
154 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100155 support.join_thread(s)
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000156
Guido van Rossum806c2462007-08-06 23:33:07 +0000157 self.assertEqual(c.contents, [data[:termlen]])
158
159 def test_numeric_terminator1(self):
160 # check that ints & longs both work (since type is
161 # explicitly checked in async_chat.handle_read)
162 self.numeric_terminator_check(1)
163
164 def test_numeric_terminator2(self):
165 self.numeric_terminator_check(6)
166
167 def test_none_terminator(self):
168 # Try reading a fixed number of bytes
Christian Heimesaf98da12008-01-27 15:18:18 +0000169 s, event = start_echo_server()
Christian Heimes5e696852008-04-09 08:37:03 +0000170 c = echo_client(None, s.port)
Guido van Rossum806c2462007-08-06 23:33:07 +0000171 data = b"hello world, I'm not dead yet!\n"
172 c.push(data)
173 c.push(SERVER_QUIT)
174 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100175 support.join_thread(s)
Guido van Rossum806c2462007-08-06 23:33:07 +0000176
177 self.assertEqual(c.contents, [])
178 self.assertEqual(c.buffer, data)
179
180 def test_simple_producer(self):
Christian Heimesaf98da12008-01-27 15:18:18 +0000181 s, event = start_echo_server()
Christian Heimes5e696852008-04-09 08:37:03 +0000182 c = echo_client(b'\n', s.port)
Guido van Rossum806c2462007-08-06 23:33:07 +0000183 data = b"hello world\nI'm not dead yet!\n"
184 p = asynchat.simple_producer(data+SERVER_QUIT, buffer_size=8)
185 c.push_with_producer(p)
186 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100187 support.join_thread(s)
Guido van Rossum806c2462007-08-06 23:33:07 +0000188
189 self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"])
190
191 def test_string_producer(self):
Christian Heimesaf98da12008-01-27 15:18:18 +0000192 s, event = start_echo_server()
Christian Heimes5e696852008-04-09 08:37:03 +0000193 c = echo_client(b'\n', s.port)
Guido van Rossum806c2462007-08-06 23:33:07 +0000194 data = b"hello world\nI'm not dead yet!\n"
195 c.push_with_producer(data+SERVER_QUIT)
196 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100197 support.join_thread(s)
Guido van Rossum806c2462007-08-06 23:33:07 +0000198
199 self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"])
200
201 def test_empty_line(self):
202 # checks that empty lines are handled correctly
Christian Heimesaf98da12008-01-27 15:18:18 +0000203 s, event = start_echo_server()
Christian Heimes5e696852008-04-09 08:37:03 +0000204 c = echo_client(b'\n', s.port)
Josiah Carlsond74900e2008-07-07 04:15:08 +0000205 c.push(b"hello world\n\nI'm not dead yet!\n")
Guido van Rossum806c2462007-08-06 23:33:07 +0000206 c.push(SERVER_QUIT)
207 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100208 support.join_thread(s)
Guido van Rossum806c2462007-08-06 23:33:07 +0000209
210 self.assertEqual(c.contents,
211 [b"hello world", b"", b"I'm not dead yet!"])
212
213 def test_close_when_done(self):
Christian Heimesaf98da12008-01-27 15:18:18 +0000214 s, event = start_echo_server()
Collin Winter8641c562010-03-17 23:49:15 +0000215 s.start_resend_event = threading.Event()
Christian Heimes5e696852008-04-09 08:37:03 +0000216 c = echo_client(b'\n', s.port)
Josiah Carlsond74900e2008-07-07 04:15:08 +0000217 c.push(b"hello world\nI'm not dead yet!\n")
Guido van Rossum806c2462007-08-06 23:33:07 +0000218 c.push(SERVER_QUIT)
219 c.close_when_done()
220 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Collin Winter8641c562010-03-17 23:49:15 +0000221
222 # Only allow the server to start echoing data back to the client after
223 # the client has closed its connection. This prevents a race condition
224 # where the server echoes all of its data before we can check that it
225 # got any down below.
226 s.start_resend_event.set()
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100227 support.join_thread(s)
Guido van Rossum806c2462007-08-06 23:33:07 +0000228
229 self.assertEqual(c.contents, [])
230 # the server might have been able to send a byte or two back, but this
231 # at least checks that it received something and didn't just fail
232 # (which could still result in the client not having received anything)
Alexandre Vassalotti953f5582009-07-22 21:29:01 +0000233 self.assertGreater(len(s.buffer), 0)
Guido van Rossum806c2462007-08-06 23:33:07 +0000234
Victor Stinnerd9e810a2014-07-08 00:00:30 +0200235 def test_push(self):
236 # Issue #12523: push() should raise a TypeError if it doesn't get
237 # a bytes string
238 s, event = start_echo_server()
239 c = echo_client(b'\n', s.port)
240 data = b'bytes\n'
241 c.push(data)
242 c.push(bytearray(data))
243 c.push(memoryview(data))
244 self.assertRaises(TypeError, c.push, 10)
245 self.assertRaises(TypeError, c.push, 'unicode')
246 c.push(SERVER_QUIT)
247 asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100248 support.join_thread(s)
Victor Stinnerd9e810a2014-07-08 00:00:30 +0200249 self.assertEqual(c.contents, [b'bytes', b'bytes', b'bytes'])
250
Guido van Rossum806c2462007-08-06 23:33:07 +0000251
252class TestAsynchat_WithPoll(TestAsynchat):
253 usepoll = True
254
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200255
Victor Stinner45cff662014-07-24 18:49:36 +0200256class TestAsynchatMocked(unittest.TestCase):
257 def test_blockingioerror(self):
258 # Issue #16133: handle_read() must ignore BlockingIOError
259 sock = unittest.mock.Mock()
260 sock.recv.side_effect = BlockingIOError(errno.EAGAIN)
261
262 dispatcher = asynchat.async_chat()
263 dispatcher.set_socket(sock)
264 self.addCleanup(dispatcher.del_channel)
265
266 with unittest.mock.patch.object(dispatcher, 'handle_error') as error:
267 dispatcher.handle_read()
268 self.assertFalse(error.called)
269
270
Guido van Rossum806c2462007-08-06 23:33:07 +0000271class TestHelperFunctions(unittest.TestCase):
272 def test_find_prefix_at_end(self):
273 self.assertEqual(asynchat.find_prefix_at_end("qwerty\r", "\r\n"), 1)
274 self.assertEqual(asynchat.find_prefix_at_end("qwertydkjf", "\r\n"), 0)
275
Victor Stinnerfd5d1b52014-07-08 00:16:54 +0200276
Victor Stinner630a4f62014-07-08 00:26:36 +0200277class TestNotConnected(unittest.TestCase):
278 def test_disallow_negative_terminator(self):
279 # Issue #11259
280 client = asynchat.async_chat()
281 self.assertRaises(ValueError, client.set_terminator, -1)
282
283
284
Andrew M. Kuchling5ac25342005-06-09 14:56:31 +0000285if __name__ == "__main__":
Brett Cannon3e9a9ae2013-06-12 21:25:59 -0400286 unittest.main()