blob: 5ae8e6df6eab2ca0e0ec58a90ee0ec2e077744f6 [file] [log] [blame]
Christian Heimes292d3512008-02-03 16:51:08 +00001"""
2Test suite for SocketServer.py.
3"""
Guido van Rossum39f1b362001-07-10 11:52:38 +00004
Christian Heimes292d3512008-02-03 16:51:08 +00005import os
Guido van Rossum39f1b362001-07-10 11:52:38 +00006import socket
Thomas Wouters0e3f5912006-08-11 14:57:12 +00007import errno
Christian Heimes292d3512008-02-03 16:51:08 +00008import imp
Guido van Rossum39f1b362001-07-10 11:52:38 +00009import select
10import time
11import threading
Christian Heimes292d3512008-02-03 16:51:08 +000012from functools import wraps
13import unittest
14import SocketServer
15
16import test.test_support
17from test.test_support import reap_children, verbose, TestSkipped
18from test.test_support import TESTFN as TEST_FILE
19
20test.test_support.requires("network")
Guido van Rossum39f1b362001-07-10 11:52:38 +000021
22NREQ = 3
23DELAY = 0.5
Christian Heimes292d3512008-02-03 16:51:08 +000024TEST_STR = b"hello world\n"
25HOST = "localhost"
26
27HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX")
28HAVE_FORKING = hasattr(os, "fork") and os.name != "os2"
29
Guido van Rossum39f1b362001-07-10 11:52:38 +000030
31class MyMixinHandler:
32 def handle(self):
33 time.sleep(DELAY)
34 line = self.rfile.readline()
35 time.sleep(DELAY)
36 self.wfile.write(line)
37
Christian Heimes292d3512008-02-03 16:51:08 +000038
39def receive(sock, n, timeout=20):
40 r, w, x = select.select([sock], [], [], timeout)
41 if sock in r:
42 return sock.recv(n)
43 else:
44 raise RuntimeError("timed out on %r" % (sock,))
45
46
47class MyStreamHandler(MyMixinHandler, SocketServer.StreamRequestHandler):
Guido van Rossum39f1b362001-07-10 11:52:38 +000048 pass
49
Christian Heimes292d3512008-02-03 16:51:08 +000050class MyDatagramHandler(MyMixinHandler,
51 SocketServer.DatagramRequestHandler):
Guido van Rossum39f1b362001-07-10 11:52:38 +000052 pass
53
Christian Heimes292d3512008-02-03 16:51:08 +000054class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
55 SocketServer.UnixStreamServer):
56 pass
57
58class ForkingUnixDatagramServer(SocketServer.ForkingMixIn,
59 SocketServer.UnixDatagramServer):
60 pass
61
62
Guido van Rossum39f1b362001-07-10 11:52:38 +000063class MyMixinServer:
64 def serve_a_few(self):
65 for i in range(NREQ):
66 self.handle_request()
Christian Heimes292d3512008-02-03 16:51:08 +000067
Guido van Rossum39f1b362001-07-10 11:52:38 +000068 def handle_error(self, request, client_address):
69 self.close_request(request)
70 self.server_close()
71 raise
72
Guido van Rossum39f1b362001-07-10 11:52:38 +000073def receive(sock, n, timeout=20):
74 r, w, x = select.select([sock], [], [], timeout)
75 if sock in r:
76 return sock.recv(n)
77 else:
Collin Winter3add4d72007-08-29 23:37:32 +000078 raise RuntimeError("timed out on %r" % (sock,))
Guido van Rossum39f1b362001-07-10 11:52:38 +000079
80def testdgram(proto, addr):
81 s = socket.socket(proto, socket.SOCK_DGRAM)
82 s.sendto(teststring, addr)
83 buf = data = receive(s, 100)
Guido van Rossum15863ea2007-08-03 19:03:39 +000084 while data and b'\n' not in buf:
Guido van Rossum39f1b362001-07-10 11:52:38 +000085 data = receive(s, 100)
86 buf += data
87 verify(buf == teststring)
88 s.close()
89
90def teststream(proto, addr):
91 s = socket.socket(proto, socket.SOCK_STREAM)
92 s.connect(addr)
Guido van Rossum9bd14012001-10-29 07:18:02 +000093 s.sendall(teststring)
Guido van Rossum39f1b362001-07-10 11:52:38 +000094 buf = data = receive(s, 100)
Guido van Rossum15863ea2007-08-03 19:03:39 +000095 while data and b'\n' not in buf:
Guido van Rossum39f1b362001-07-10 11:52:38 +000096 data = receive(s, 100)
97 buf += data
98 verify(buf == teststring)
99 s.close()
100
101class ServerThread(threading.Thread):
102 def __init__(self, addr, svrcls, hdlrcls):
103 threading.Thread.__init__(self)
104 self.__addr = addr
105 self.__svrcls = svrcls
106 self.__hdlrcls = hdlrcls
Guido van Rossumd8faa362007-04-27 19:54:29 +0000107 self.ready = threading.Event()
Christian Heimes292d3512008-02-03 16:51:08 +0000108
Guido van Rossum39f1b362001-07-10 11:52:38 +0000109 def run(self):
110 class svrcls(MyMixinServer, self.__svrcls):
111 pass
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000112 if verbose: print("thread: creating server")
Guido van Rossum39f1b362001-07-10 11:52:38 +0000113 svr = svrcls(self.__addr, self.__hdlrcls)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000114 # pull the address out of the server in case it changed
115 # this can happen if another process is using the port
Guido van Rossumd8faa362007-04-27 19:54:29 +0000116 addr = svr.server_address
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000117 if addr:
118 self.__addr = addr
Guido van Rossumd8faa362007-04-27 19:54:29 +0000119 if self.__addr != svr.socket.getsockname():
120 raise RuntimeError('server_address was %s, expected %s' %
121 (self.__addr, svr.socket.getsockname()))
122 self.ready.set()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000123 if verbose: print("thread: serving three times")
Guido van Rossum39f1b362001-07-10 11:52:38 +0000124 svr.serve_a_few()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000125 if verbose: print("thread: done")
Guido van Rossum39f1b362001-07-10 11:52:38 +0000126
Guido van Rossum39f1b362001-07-10 11:52:38 +0000127
Christian Heimes292d3512008-02-03 16:51:08 +0000128class ForgivingTCPServer(SocketServer.TCPServer):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000129 # prevent errors if another process is using the port we want
130 def server_bind(self):
131 host, default_port = self.server_address
132 # this code shamelessly stolen from test.test_support
133 # the ports were changed to protect the innocent
134 import sys
135 for port in [default_port, 3434, 8798, 23833]:
136 try:
137 self.server_address = host, port
Christian Heimes292d3512008-02-03 16:51:08 +0000138 SocketServer.TCPServer.server_bind(self)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000139 break
Guido van Rossumb940e112007-01-10 16:19:56 +0000140 except socket.error as e:
141 (err, msg) = e
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000142 if err != errno.EADDRINUSE:
143 raise
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000144 print(' WARNING: failed to listen on port %d, trying another' % port, file=sys.__stderr__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000145
Christian Heimes292d3512008-02-03 16:51:08 +0000146class SocketServerTest(unittest.TestCase):
147 """Test all socket servers."""
Guido van Rossum39f1b362001-07-10 11:52:38 +0000148
Christian Heimes292d3512008-02-03 16:51:08 +0000149 def setUp(self):
150 self.port_seed = 0
151 self.test_files = []
Guido van Rossum39f1b362001-07-10 11:52:38 +0000152
Christian Heimes292d3512008-02-03 16:51:08 +0000153 def tearDown(self):
154 time.sleep(DELAY)
155 reap_children()
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000156
Christian Heimes292d3512008-02-03 16:51:08 +0000157 for fn in self.test_files:
158 try:
159 os.remove(fn)
160 except os.error:
161 pass
162 self.test_files[:] = []
163
164 def pickport(self):
165 self.port_seed += 1
166 return 10000 + (os.getpid() % 1000)*10 + self.port_seed
167
168 def pickaddr(self, proto):
169 if proto == socket.AF_INET:
170 return (HOST, self.pickport())
171 else:
172 fn = TEST_FILE + str(self.pickport())
173 if os.name == 'os2':
174 # AF_UNIX socket names on OS/2 require a specific prefix
175 # which can't include a drive letter and must also use
176 # backslashes as directory separators
177 if fn[1] == ':':
178 fn = fn[2:]
179 if fn[0] in (os.sep, os.altsep):
180 fn = fn[1:]
181 fn = os.path.join('\socket', fn)
182 if os.sep == '/':
183 fn = fn.replace(os.sep, os.altsep)
184 else:
185 fn = fn.replace(os.altsep, os.sep)
186 self.test_files.append(fn)
187 return fn
188
189 def run_servers(self, proto, servers, hdlrcls, testfunc):
190 for svrcls in servers:
191 addr = self.pickaddr(proto)
192 if verbose:
193 print "ADDR =", addr
194 print "CLASS =", svrcls
195 t = ServerThread(addr, svrcls, hdlrcls)
196 if verbose: print "server created"
197 t.start()
198 if verbose: print "server running"
199 for i in range(NREQ):
200 t.ready.wait(10*DELAY)
201 self.assert_(t.ready.isSet(),
202 "Server not ready within a reasonable time")
203 if verbose: print "test client", i
204 testfunc(proto, addr)
205 if verbose: print "waiting for server"
206 t.join()
207 if verbose: print "done"
208
209 def stream_examine(self, proto, addr):
210 s = socket.socket(proto, socket.SOCK_STREAM)
211 s.connect(addr)
212 s.sendall(TEST_STR)
213 buf = data = receive(s, 100)
214 while data and '\n' not in buf:
215 data = receive(s, 100)
216 buf += data
217 self.assertEquals(buf, TEST_STR)
218 s.close()
219
220 def dgram_examine(self, proto, addr):
221 s = socket.socket(proto, socket.SOCK_DGRAM)
222 s.sendto(TEST_STR, addr)
223 buf = data = receive(s, 100)
224 while data and '\n' not in buf:
225 data = receive(s, 100)
226 buf += data
227 self.assertEquals(buf, TEST_STR)
228 s.close()
229
230 def test_TCPServers(self):
231 # Test SocketServer.TCPServer
232 servers = [ForgivingTCPServer, SocketServer.ThreadingTCPServer]
233 if HAVE_FORKING:
234 servers.append(SocketServer.ForkingTCPServer)
235 self.run_servers(socket.AF_INET, servers,
236 MyStreamHandler, self.stream_examine)
237
238 def test_UDPServers(self):
239 # Test SocketServer.UDPServer
240 servers = [SocketServer.UDPServer,
241 SocketServer.ThreadingUDPServer]
242 if HAVE_FORKING:
243 servers.append(SocketServer.ForkingUDPServer)
244 self.run_servers(socket.AF_INET, servers, MyDatagramHandler,
245 self.dgram_examine)
246
247 def test_stream_servers(self):
248 # Test SocketServer's stream servers
249 if not HAVE_UNIX_SOCKETS:
250 return
251 servers = [SocketServer.UnixStreamServer,
252 SocketServer.ThreadingUnixStreamServer]
253 if HAVE_FORKING:
254 servers.append(ForkingUnixStreamServer)
255 self.run_servers(socket.AF_UNIX, servers, MyStreamHandler,
256 self.stream_examine)
257
258 # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
259 # client address so this cannot work:
260
261 # def test_dgram_servers(self):
262 # # Test SocketServer.UnixDatagramServer
263 # if not HAVE_UNIX_SOCKETS:
264 # return
265 # servers = [SocketServer.UnixDatagramServer,
266 # SocketServer.ThreadingUnixDatagramServer]
267 # if HAVE_FORKING:
268 # servers.append(ForkingUnixDatagramServer)
269 # self.run_servers(socket.AF_UNIX, servers, MyDatagramHandler,
270 # self.dgram_examine)
271
Guido van Rossum39f1b362001-07-10 11:52:38 +0000272
Tim Petersa9f6f222001-09-17 23:56:20 +0000273def test_main():
Tim Petersa9f6f222001-09-17 23:56:20 +0000274 if imp.lock_held():
Christian Heimes292d3512008-02-03 16:51:08 +0000275 # If the import lock is held, the threads will hang
Tim Petersa9f6f222001-09-17 23:56:20 +0000276 raise TestSkipped("can't run when import lock is held")
277
Christian Heimes292d3512008-02-03 16:51:08 +0000278 test.test_support.run_unittest(SocketServerTest)
Guido van Rossum39f1b362001-07-10 11:52:38 +0000279
Tim Petersa9f6f222001-09-17 23:56:20 +0000280if __name__ == "__main__":
281 test_main()