blob: b66eea2282da9062456c297edf1f373e0c6bac03 [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
54class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
55 SocketServer.UnixStreamServer):
56 pass
57
58class ForkingUnixDatagramServer(SocketServer.ForkingMixIn,
59 SocketServer.UnixDatagramServer):
60 pass
61
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):
128 reap_children()
129
130 for fn in self.test_files:
131 try:
132 os.remove(fn)
133 except os.error:
134 pass
135 self.test_files[:] = []
136
137 def pickport(self):
138 self.port_seed += 1
139 return 10000 + (os.getpid() % 1000)*10 + self.port_seed
140
141 def pickaddr(self, proto):
142 if proto == socket.AF_INET:
143 return (HOST, self.pickport())
144 else:
145 fn = TEST_FILE + str(self.pickport())
146 if os.name == 'os2':
147 # AF_UNIX socket names on OS/2 require a specific prefix
148 # which can't include a drive letter and must also use
149 # backslashes as directory separators
150 if fn[1] == ':':
151 fn = fn[2:]
152 if fn[0] in (os.sep, os.altsep):
153 fn = fn[1:]
154 fn = os.path.join('\socket', fn)
155 if os.sep == '/':
156 fn = fn.replace(os.sep, os.altsep)
157 else:
158 fn = fn.replace(os.altsep, os.sep)
159 self.test_files.append(fn)
160 return fn
161
162 def run_servers(self, proto, servers, hdlrcls, testfunc):
163 for svrcls in servers:
164 addr = self.pickaddr(proto)
165 if verbose:
166 print "ADDR =", addr
167 print "CLASS =", svrcls
168 t = ServerThread(addr, svrcls, hdlrcls)
169 if verbose: print "server created"
170 t.start()
171 if verbose: print "server running"
172 for i in range(NREQ):
173 t.ready.wait(10*DELAY)
174 self.assert_(t.ready.isSet(),
175 "Server not ready within a reasonable time")
176 if verbose: print "test client", i
177 testfunc(proto, addr)
178 if verbose: print "waiting for server"
179 t.join()
180 if verbose: print "done"
181
182 def stream_examine(self, proto, addr):
183 s = socket.socket(proto, socket.SOCK_STREAM)
184 s.connect(addr)
185 s.sendall(TEST_STR)
186 buf = data = receive(s, 100)
187 while data and '\n' not in buf:
188 data = receive(s, 100)
189 buf += data
190 self.assertEquals(buf, TEST_STR)
191 s.close()
192
193 def dgram_examine(self, proto, addr):
194 s = socket.socket(proto, socket.SOCK_DGRAM)
195 s.sendto(TEST_STR, addr)
196 buf = data = receive(s, 100)
197 while data and '\n' not in buf:
198 data = receive(s, 100)
199 buf += data
200 self.assertEquals(buf, TEST_STR)
201 s.close()
202
203 def test_TCPServers(self):
204 # Test SocketServer.TCPServer
205 servers = [ForgivingTCPServer, SocketServer.ThreadingTCPServer]
206 if HAVE_FORKING:
207 servers.append(SocketServer.ForkingTCPServer)
208 self.run_servers(socket.AF_INET, servers,
209 MyStreamHandler, self.stream_examine)
210
211 def test_UDPServers(self):
212 # Test SocketServer.UPDServer
213 servers = [SocketServer.UDPServer,
214 SocketServer.ThreadingUDPServer]
215 if HAVE_FORKING:
216 servers.append(SocketServer.ForkingUDPServer)
217 self.run_servers(socket.AF_INET, servers, MyDatagramHandler,
218 self.dgram_examine)
219
220 def test_stream_servers(self):
221 # Test SocketServer's stream servers
222 if not HAVE_UNIX_SOCKETS:
223 return
224 servers = [SocketServer.UnixStreamServer,
225 SocketServer.ThreadingUnixStreamServer]
226 if HAVE_FORKING:
227 servers.append(ForkingUnixStreamServer)
228 self.run_servers(socket.AF_UNIX, servers, MyStreamHandler,
229 self.stream_examine)
230
231 # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
232 # client address so this cannot work:
233
234 # def test_dgram_servers(self):
235 # # Test SocketServer.UnixDatagramServer
236 # if not HAVE_UNIX_SOCKETS:
237 # return
238 # servers = [SocketServer.UnixDatagramServer,
239 # SocketServer.ThreadingUnixDatagramServer]
240 # if HAVE_FORKING:
241 # servers.append(ForkingUnixDatagramServer)
242 # self.run_servers(socket.AF_UNIX, servers, MyDatagramHandler,
243 # self.dgram_examine)
244
Guido van Rossum39f1b362001-07-10 11:52:38 +0000245
Tim Petersa9f6f222001-09-17 23:56:20 +0000246def test_main():
Tim Petersa9f6f222001-09-17 23:56:20 +0000247 if imp.lock_held():
Georg Brandl61fdd712008-02-02 11:05:00 +0000248 # If the import lock is held, the threads will hang
Tim Petersa9f6f222001-09-17 23:56:20 +0000249 raise TestSkipped("can't run when import lock is held")
250
Georg Brandl61fdd712008-02-02 11:05:00 +0000251 test.test_support.run_unittest(SocketServerTest)
Guido van Rossum39f1b362001-07-10 11:52:38 +0000252
Tim Petersa9f6f222001-09-17 23:56:20 +0000253if __name__ == "__main__":
254 test_main()