blob: 79f3038ecf40c27f25a913361dc006f543d5012b [file] [log] [blame]
Georg Brandl61fdd712008-02-02 11:05:00 +00001"""
2Test suite for SocketServer.py.
3"""
Guido van Rossum39f1b362001-07-10 11:52:38 +00004
Georg Brandl61fdd712008-02-02 11:05:00 +00005import os
Guido van Rossum39f1b362001-07-10 11:52:38 +00006import socket
Neal Norwitz909eb122006-06-12 02:13:21 +00007import errno
Georg Brandl61fdd712008-02-02 11:05:00 +00008import imp
Guido van Rossum39f1b362001-07-10 11:52:38 +00009import select
10import time
11import threading
Georg Brandl61fdd712008-02-02 11:05:00 +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
Georg Brandl61fdd712008-02-02 11:05:00 +000024TEST_STR = "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
Guido van Rossum39f1b362001-07-10 11:52:38 +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:
Walter Dörwald70a6b492004-02-12 17:35:32 +000044 raise RuntimeError, "timed out on %r" % (sock,)
Guido van Rossum39f1b362001-07-10 11:52:38 +000045
Guido van Rossum39f1b362001-07-10 11:52:38 +000046
Georg Brandl61fdd712008-02-02 11:05:00 +000047class MyStreamHandler(MyMixinHandler, SocketServer.StreamRequestHandler):
48 pass
49
50class MyDatagramHandler(MyMixinHandler,
51 SocketServer.DatagramRequestHandler):
52 pass
53
Amaury Forgeot d'Arc72a65762008-02-03 23:57:24 +000054if HAVE_UNIX_SOCKETS:
55 class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
56 SocketServer.UnixStreamServer):
57 pass
Georg Brandl61fdd712008-02-02 11:05:00 +000058
Amaury Forgeot d'Arc72a65762008-02-03 23:57:24 +000059 class ForkingUnixDatagramServer(SocketServer.ForkingMixIn,
60 SocketServer.UnixDatagramServer):
61 pass
Georg Brandl61fdd712008-02-02 11:05:00 +000062
63
64class MyMixinServer:
65 def serve_a_few(self):
66 for i in range(NREQ):
67 self.handle_request()
68
69 def handle_error(self, request, client_address):
70 self.close_request(request)
71 self.server_close()
72 raise
73
Guido van Rossum39f1b362001-07-10 11:52:38 +000074
75class ServerThread(threading.Thread):
76 def __init__(self, addr, svrcls, hdlrcls):
77 threading.Thread.__init__(self)
78 self.__addr = addr
79 self.__svrcls = svrcls
80 self.__hdlrcls = hdlrcls
Guido van Rossumb1bb01e2007-04-04 17:43:02 +000081 self.ready = threading.Event()
Georg Brandl61fdd712008-02-02 11:05:00 +000082
Guido van Rossum39f1b362001-07-10 11:52:38 +000083 def run(self):
84 class svrcls(MyMixinServer, self.__svrcls):
85 pass
86 if verbose: print "thread: creating server"
87 svr = svrcls(self.__addr, self.__hdlrcls)
Neal Norwitz909eb122006-06-12 02:13:21 +000088 # pull the address out of the server in case it changed
89 # this can happen if another process is using the port
Collin Winter3351aa72007-03-10 14:33:32 +000090 addr = svr.server_address
Neal Norwitz909eb122006-06-12 02:13:21 +000091 if addr:
92 self.__addr = addr
Collin Winter3351aa72007-03-10 14:33:32 +000093 if self.__addr != svr.socket.getsockname():
94 raise RuntimeError('server_address was %s, expected %s' %
95 (self.__addr, svr.socket.getsockname()))
Guido van Rossumb1bb01e2007-04-04 17:43:02 +000096 self.ready.set()
Guido van Rossum39f1b362001-07-10 11:52:38 +000097 if verbose: print "thread: serving three times"
98 svr.serve_a_few()
99 if verbose: print "thread: done"
100
Guido van Rossum39f1b362001-07-10 11:52:38 +0000101
Georg Brandl61fdd712008-02-02 11:05:00 +0000102class ForgivingTCPServer(SocketServer.TCPServer):
Neal Norwitz909eb122006-06-12 02:13:21 +0000103 # prevent errors if another process is using the port we want
104 def server_bind(self):
105 host, default_port = self.server_address
106 # this code shamelessly stolen from test.test_support
107 # the ports were changed to protect the innocent
108 import sys
109 for port in [default_port, 3434, 8798, 23833]:
110 try:
111 self.server_address = host, port
Georg Brandl61fdd712008-02-02 11:05:00 +0000112 SocketServer.TCPServer.server_bind(self)
Neal Norwitz909eb122006-06-12 02:13:21 +0000113 break
114 except socket.error, (err, msg):
115 if err != errno.EADDRINUSE:
116 raise
Georg Brandl61fdd712008-02-02 11:05:00 +0000117 print >> sys.__stderr__, \
118 "WARNING: failed to listen on port %d, trying another: " % port
Neal Norwitz909eb122006-06-12 02:13:21 +0000119
Guido van Rossum39f1b362001-07-10 11:52:38 +0000120
Georg Brandl61fdd712008-02-02 11:05:00 +0000121class SocketServerTest(unittest.TestCase):
122 """Test all socket servers."""
Guido van Rossum39f1b362001-07-10 11:52:38 +0000123
Georg Brandl61fdd712008-02-02 11:05:00 +0000124 def setUp(self):
125 self.port_seed = 0
126 self.test_files = []
Neal Norwitzb476fdf2006-08-15 04:58:28 +0000127
Georg Brandl61fdd712008-02-02 11:05:00 +0000128 def tearDown(self):
Georg Brandl461ed872008-02-03 00:04:50 +0000129 time.sleep(DELAY)
Georg Brandl61fdd712008-02-02 11:05:00 +0000130 reap_children()
131
132 for fn in self.test_files:
133 try:
134 os.remove(fn)
135 except os.error:
136 pass
137 self.test_files[:] = []
138
139 def pickport(self):
140 self.port_seed += 1
141 return 10000 + (os.getpid() % 1000)*10 + self.port_seed
142
143 def pickaddr(self, proto):
144 if proto == socket.AF_INET:
145 return (HOST, self.pickport())
146 else:
147 fn = TEST_FILE + str(self.pickport())
148 if os.name == 'os2':
149 # AF_UNIX socket names on OS/2 require a specific prefix
150 # which can't include a drive letter and must also use
151 # backslashes as directory separators
152 if fn[1] == ':':
153 fn = fn[2:]
154 if fn[0] in (os.sep, os.altsep):
155 fn = fn[1:]
156 fn = os.path.join('\socket', fn)
157 if os.sep == '/':
158 fn = fn.replace(os.sep, os.altsep)
159 else:
160 fn = fn.replace(os.altsep, os.sep)
161 self.test_files.append(fn)
162 return fn
163
164 def run_servers(self, proto, servers, hdlrcls, testfunc):
165 for svrcls in servers:
166 addr = self.pickaddr(proto)
167 if verbose:
168 print "ADDR =", addr
169 print "CLASS =", svrcls
170 t = ServerThread(addr, svrcls, hdlrcls)
171 if verbose: print "server created"
172 t.start()
173 if verbose: print "server running"
174 for i in range(NREQ):
175 t.ready.wait(10*DELAY)
176 self.assert_(t.ready.isSet(),
177 "Server not ready within a reasonable time")
178 if verbose: print "test client", i
179 testfunc(proto, addr)
180 if verbose: print "waiting for server"
181 t.join()
182 if verbose: print "done"
183
184 def stream_examine(self, proto, addr):
185 s = socket.socket(proto, socket.SOCK_STREAM)
186 s.connect(addr)
187 s.sendall(TEST_STR)
188 buf = data = receive(s, 100)
189 while data and '\n' not in buf:
190 data = receive(s, 100)
191 buf += data
192 self.assertEquals(buf, TEST_STR)
193 s.close()
194
195 def dgram_examine(self, proto, addr):
196 s = socket.socket(proto, socket.SOCK_DGRAM)
197 s.sendto(TEST_STR, addr)
198 buf = data = receive(s, 100)
199 while data and '\n' not in buf:
200 data = receive(s, 100)
201 buf += data
202 self.assertEquals(buf, TEST_STR)
203 s.close()
204
205 def test_TCPServers(self):
206 # Test SocketServer.TCPServer
207 servers = [ForgivingTCPServer, SocketServer.ThreadingTCPServer]
208 if HAVE_FORKING:
209 servers.append(SocketServer.ForkingTCPServer)
210 self.run_servers(socket.AF_INET, servers,
211 MyStreamHandler, self.stream_examine)
212
213 def test_UDPServers(self):
Georg Brandl461ed872008-02-03 00:04:50 +0000214 # Test SocketServer.UDPServer
Georg Brandl61fdd712008-02-02 11:05:00 +0000215 servers = [SocketServer.UDPServer,
216 SocketServer.ThreadingUDPServer]
217 if HAVE_FORKING:
218 servers.append(SocketServer.ForkingUDPServer)
219 self.run_servers(socket.AF_INET, servers, MyDatagramHandler,
220 self.dgram_examine)
221
222 def test_stream_servers(self):
223 # Test SocketServer's stream servers
224 if not HAVE_UNIX_SOCKETS:
225 return
226 servers = [SocketServer.UnixStreamServer,
227 SocketServer.ThreadingUnixStreamServer]
228 if HAVE_FORKING:
229 servers.append(ForkingUnixStreamServer)
230 self.run_servers(socket.AF_UNIX, servers, MyStreamHandler,
231 self.stream_examine)
232
233 # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
234 # client address so this cannot work:
235
236 # def test_dgram_servers(self):
237 # # Test SocketServer.UnixDatagramServer
238 # if not HAVE_UNIX_SOCKETS:
239 # return
240 # servers = [SocketServer.UnixDatagramServer,
241 # SocketServer.ThreadingUnixDatagramServer]
242 # if HAVE_FORKING:
243 # servers.append(ForkingUnixDatagramServer)
244 # self.run_servers(socket.AF_UNIX, servers, MyDatagramHandler,
245 # self.dgram_examine)
246
Guido van Rossum39f1b362001-07-10 11:52:38 +0000247
Tim Petersa9f6f222001-09-17 23:56:20 +0000248def test_main():
Tim Petersa9f6f222001-09-17 23:56:20 +0000249 if imp.lock_held():
Georg Brandl61fdd712008-02-02 11:05:00 +0000250 # If the import lock is held, the threads will hang
Tim Petersa9f6f222001-09-17 23:56:20 +0000251 raise TestSkipped("can't run when import lock is held")
252
Georg Brandl61fdd712008-02-02 11:05:00 +0000253 test.test_support.run_unittest(SocketServerTest)
Guido van Rossum39f1b362001-07-10 11:52:38 +0000254
Tim Petersa9f6f222001-09-17 23:56:20 +0000255if __name__ == "__main__":
256 test_main()