blob: 07dd7780444c2a5d0036111c598ab96cc3ca8ef4 [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 +000012import unittest
13import SocketServer
14
15import test.test_support
16from test.test_support import reap_children, verbose, TestSkipped
17from test.test_support import TESTFN as TEST_FILE
18
19test.test_support.requires("network")
Guido van Rossum39f1b362001-07-10 11:52:38 +000020
21NREQ = 3
22DELAY = 0.5
Georg Brandl61fdd712008-02-02 11:05:00 +000023TEST_STR = "hello world\n"
24HOST = "localhost"
25
26HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX")
27HAVE_FORKING = hasattr(os, "fork") and os.name != "os2"
28
Guido van Rossum39f1b362001-07-10 11:52:38 +000029
30class MyMixinHandler:
31 def handle(self):
32 time.sleep(DELAY)
33 line = self.rfile.readline()
34 time.sleep(DELAY)
35 self.wfile.write(line)
36
Guido van Rossum39f1b362001-07-10 11:52:38 +000037
38def receive(sock, n, timeout=20):
39 r, w, x = select.select([sock], [], [], timeout)
40 if sock in r:
41 return sock.recv(n)
42 else:
Walter Dörwald70a6b492004-02-12 17:35:32 +000043 raise RuntimeError, "timed out on %r" % (sock,)
Guido van Rossum39f1b362001-07-10 11:52:38 +000044
Guido van Rossum39f1b362001-07-10 11:52:38 +000045
Georg Brandl61fdd712008-02-02 11:05:00 +000046class MyStreamHandler(MyMixinHandler, SocketServer.StreamRequestHandler):
47 pass
48
49class MyDatagramHandler(MyMixinHandler,
50 SocketServer.DatagramRequestHandler):
51 pass
52
Amaury Forgeot d'Arc72a65762008-02-03 23:57:24 +000053if HAVE_UNIX_SOCKETS:
54 class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
55 SocketServer.UnixStreamServer):
56 pass
Georg Brandl61fdd712008-02-02 11:05:00 +000057
Amaury Forgeot d'Arc72a65762008-02-03 23:57:24 +000058 class ForkingUnixDatagramServer(SocketServer.ForkingMixIn,
59 SocketServer.UnixDatagramServer):
60 pass
Georg Brandl61fdd712008-02-02 11:05:00 +000061
62
63class MyMixinServer:
64 def serve_a_few(self):
65 for i in range(NREQ):
66 self.handle_request()
67
68 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 +000073
74class ServerThread(threading.Thread):
75 def __init__(self, addr, svrcls, hdlrcls):
76 threading.Thread.__init__(self)
77 self.__addr = addr
78 self.__svrcls = svrcls
79 self.__hdlrcls = hdlrcls
Guido van Rossumb1bb01e2007-04-04 17:43:02 +000080 self.ready = threading.Event()
Georg Brandl61fdd712008-02-02 11:05:00 +000081
Guido van Rossum39f1b362001-07-10 11:52:38 +000082 def run(self):
83 class svrcls(MyMixinServer, self.__svrcls):
84 pass
85 if verbose: print "thread: creating server"
86 svr = svrcls(self.__addr, self.__hdlrcls)
Neal Norwitz909eb122006-06-12 02:13:21 +000087 # pull the address out of the server in case it changed
88 # this can happen if another process is using the port
Collin Winter3351aa72007-03-10 14:33:32 +000089 addr = svr.server_address
Neal Norwitz909eb122006-06-12 02:13:21 +000090 if addr:
91 self.__addr = addr
Collin Winter3351aa72007-03-10 14:33:32 +000092 if self.__addr != svr.socket.getsockname():
93 raise RuntimeError('server_address was %s, expected %s' %
94 (self.__addr, svr.socket.getsockname()))
Guido van Rossumb1bb01e2007-04-04 17:43:02 +000095 self.ready.set()
Guido van Rossum39f1b362001-07-10 11:52:38 +000096 if verbose: print "thread: serving three times"
97 svr.serve_a_few()
98 if verbose: print "thread: done"
99
Guido van Rossum39f1b362001-07-10 11:52:38 +0000100
Georg Brandl61fdd712008-02-02 11:05:00 +0000101class ForgivingTCPServer(SocketServer.TCPServer):
Neal Norwitz909eb122006-06-12 02:13:21 +0000102 # prevent errors if another process is using the port we want
103 def server_bind(self):
104 host, default_port = self.server_address
105 # this code shamelessly stolen from test.test_support
106 # the ports were changed to protect the innocent
107 import sys
108 for port in [default_port, 3434, 8798, 23833]:
109 try:
110 self.server_address = host, port
Georg Brandl61fdd712008-02-02 11:05:00 +0000111 SocketServer.TCPServer.server_bind(self)
Neal Norwitz909eb122006-06-12 02:13:21 +0000112 break
113 except socket.error, (err, msg):
114 if err != errno.EADDRINUSE:
115 raise
Georg Brandl61fdd712008-02-02 11:05:00 +0000116 print >> sys.__stderr__, \
117 "WARNING: failed to listen on port %d, trying another: " % port
Neal Norwitz909eb122006-06-12 02:13:21 +0000118
Guido van Rossum39f1b362001-07-10 11:52:38 +0000119
Georg Brandl61fdd712008-02-02 11:05:00 +0000120class SocketServerTest(unittest.TestCase):
121 """Test all socket servers."""
Guido van Rossum39f1b362001-07-10 11:52:38 +0000122
Georg Brandl61fdd712008-02-02 11:05:00 +0000123 def setUp(self):
124 self.port_seed = 0
125 self.test_files = []
Neal Norwitzb476fdf2006-08-15 04:58:28 +0000126
Georg Brandl61fdd712008-02-02 11:05:00 +0000127 def tearDown(self):
Georg Brandl461ed872008-02-03 00:04:50 +0000128 time.sleep(DELAY)
Georg Brandl61fdd712008-02-02 11:05:00 +0000129 reap_children()
130
131 for fn in self.test_files:
132 try:
133 os.remove(fn)
134 except os.error:
135 pass
136 self.test_files[:] = []
137
138 def pickport(self):
139 self.port_seed += 1
140 return 10000 + (os.getpid() % 1000)*10 + self.port_seed
141
142 def pickaddr(self, proto):
143 if proto == socket.AF_INET:
144 return (HOST, self.pickport())
145 else:
146 fn = TEST_FILE + str(self.pickport())
147 if os.name == 'os2':
148 # AF_UNIX socket names on OS/2 require a specific prefix
149 # which can't include a drive letter and must also use
150 # backslashes as directory separators
151 if fn[1] == ':':
152 fn = fn[2:]
153 if fn[0] in (os.sep, os.altsep):
154 fn = fn[1:]
155 fn = os.path.join('\socket', fn)
156 if os.sep == '/':
157 fn = fn.replace(os.sep, os.altsep)
158 else:
159 fn = fn.replace(os.altsep, os.sep)
160 self.test_files.append(fn)
161 return fn
162
163 def run_servers(self, proto, servers, hdlrcls, testfunc):
164 for svrcls in servers:
165 addr = self.pickaddr(proto)
166 if verbose:
167 print "ADDR =", addr
168 print "CLASS =", svrcls
169 t = ServerThread(addr, svrcls, hdlrcls)
170 if verbose: print "server created"
171 t.start()
172 if verbose: print "server running"
173 for i in range(NREQ):
174 t.ready.wait(10*DELAY)
175 self.assert_(t.ready.isSet(),
176 "Server not ready within a reasonable time")
177 if verbose: print "test client", i
178 testfunc(proto, addr)
179 if verbose: print "waiting for server"
180 t.join()
181 if verbose: print "done"
182
183 def stream_examine(self, proto, addr):
184 s = socket.socket(proto, socket.SOCK_STREAM)
185 s.connect(addr)
186 s.sendall(TEST_STR)
187 buf = data = receive(s, 100)
188 while data and '\n' not in buf:
189 data = receive(s, 100)
190 buf += data
191 self.assertEquals(buf, TEST_STR)
192 s.close()
193
194 def dgram_examine(self, proto, addr):
195 s = socket.socket(proto, socket.SOCK_DGRAM)
196 s.sendto(TEST_STR, addr)
197 buf = data = receive(s, 100)
198 while data and '\n' not in buf:
199 data = receive(s, 100)
200 buf += data
201 self.assertEquals(buf, TEST_STR)
202 s.close()
203
204 def test_TCPServers(self):
205 # Test SocketServer.TCPServer
206 servers = [ForgivingTCPServer, SocketServer.ThreadingTCPServer]
207 if HAVE_FORKING:
208 servers.append(SocketServer.ForkingTCPServer)
209 self.run_servers(socket.AF_INET, servers,
210 MyStreamHandler, self.stream_examine)
211
212 def test_UDPServers(self):
Georg Brandl461ed872008-02-03 00:04:50 +0000213 # Test SocketServer.UDPServer
Georg Brandl61fdd712008-02-02 11:05:00 +0000214 servers = [SocketServer.UDPServer,
215 SocketServer.ThreadingUDPServer]
216 if HAVE_FORKING:
217 servers.append(SocketServer.ForkingUDPServer)
218 self.run_servers(socket.AF_INET, servers, MyDatagramHandler,
219 self.dgram_examine)
220
221 def test_stream_servers(self):
222 # Test SocketServer's stream servers
223 if not HAVE_UNIX_SOCKETS:
224 return
225 servers = [SocketServer.UnixStreamServer,
226 SocketServer.ThreadingUnixStreamServer]
227 if HAVE_FORKING:
228 servers.append(ForkingUnixStreamServer)
229 self.run_servers(socket.AF_UNIX, servers, MyStreamHandler,
230 self.stream_examine)
231
232 # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
233 # client address so this cannot work:
234
235 # def test_dgram_servers(self):
236 # # Test SocketServer.UnixDatagramServer
237 # if not HAVE_UNIX_SOCKETS:
238 # return
239 # servers = [SocketServer.UnixDatagramServer,
240 # SocketServer.ThreadingUnixDatagramServer]
241 # if HAVE_FORKING:
242 # servers.append(ForkingUnixDatagramServer)
243 # self.run_servers(socket.AF_UNIX, servers, MyDatagramHandler,
244 # self.dgram_examine)
245
Guido van Rossum39f1b362001-07-10 11:52:38 +0000246
Tim Petersa9f6f222001-09-17 23:56:20 +0000247def test_main():
Tim Petersa9f6f222001-09-17 23:56:20 +0000248 if imp.lock_held():
Georg Brandl61fdd712008-02-02 11:05:00 +0000249 # If the import lock is held, the threads will hang
Tim Petersa9f6f222001-09-17 23:56:20 +0000250 raise TestSkipped("can't run when import lock is held")
251
Georg Brandl61fdd712008-02-02 11:05:00 +0000252 test.test_support.run_unittest(SocketServerTest)
Guido van Rossum39f1b362001-07-10 11:52:38 +0000253
Tim Petersa9f6f222001-09-17 23:56:20 +0000254if __name__ == "__main__":
255 test_main()