2008-02-02 07:05:00 -04:00
|
|
|
"""
|
2008-05-24 15:31:28 -03:00
|
|
|
Test suite for SocketServer.py.
|
2008-02-02 07:05:00 -04:00
|
|
|
"""
|
2001-07-10 08:52:38 -03:00
|
|
|
|
2008-02-28 14:03:15 -04:00
|
|
|
import contextlib
|
2008-02-02 07:05:00 -04:00
|
|
|
import imp
|
2008-02-28 01:53:18 -04:00
|
|
|
import os
|
2001-07-10 08:52:38 -03:00
|
|
|
import select
|
2008-02-28 01:53:18 -04:00
|
|
|
import signal
|
|
|
|
import socket
|
|
|
|
import tempfile
|
2008-02-02 07:05:00 -04:00
|
|
|
import unittest
|
2008-05-24 15:31:28 -03:00
|
|
|
import SocketServer
|
2008-02-02 07:05:00 -04:00
|
|
|
|
|
|
|
import test.test_support
|
2009-10-27 18:27:24 -03:00
|
|
|
from test.test_support import reap_children, reap_threads, verbose
|
2010-04-27 20:55:59 -03:00
|
|
|
try:
|
|
|
|
import threading
|
|
|
|
except ImportError:
|
|
|
|
threading = None
|
2008-02-02 07:05:00 -04:00
|
|
|
|
|
|
|
test.test_support.requires("network")
|
2001-07-10 08:52:38 -03:00
|
|
|
|
2008-02-02 07:05:00 -04:00
|
|
|
TEST_STR = "hello world\n"
|
- Issue #2550: The approach used by client/server code for obtaining ports
to listen on in network-oriented tests has been refined in an effort to
facilitate running multiple instances of the entire regression test suite
in parallel without issue. test_support.bind_port() has been fixed such
that it will always return a unique port -- which wasn't always the case
with the previous implementation, especially if socket options had been
set that affected address reuse (i.e. SO_REUSEADDR, SO_REUSEPORT). The
new implementation of bind_port() will actually raise an exception if it
is passed an AF_INET/SOCK_STREAM socket with either the SO_REUSEADDR or
SO_REUSEPORT socket option set. Furthermore, if available, bind_port()
will set the SO_EXCLUSIVEADDRUSE option on the socket it's been passed.
This currently only applies to Windows. This option prevents any other
sockets from binding to the host/port we've bound to, thus removing the
possibility of the 'non-deterministic' behaviour, as Microsoft puts it,
that occurs when a second SOCK_STREAM socket binds and accepts to a
host/port that's already been bound by another socket. The optional
preferred port parameter to bind_port() has been removed. Under no
circumstances should tests be hard coding ports!
test_support.find_unused_port() has also been introduced, which will pass
a temporary socket object to bind_port() in order to obtain an unused port.
The temporary socket object is then closed and deleted, and the port is
returned. This method should only be used for obtaining an unused port
in order to pass to an external program (i.e. the -accept [port] argument
to openssl's s_server mode) or as a parameter to a server-oriented class
that doesn't give you direct access to the underlying socket used.
Finally, test_support.HOST has been introduced, which should be used for
the host argument of any relevant socket calls (i.e. bind and connect).
The following tests were updated to following the new conventions:
test_socket, test_smtplib, test_asyncore, test_ssl, test_httplib,
test_poplib, test_ftplib, test_telnetlib, test_socketserver,
test_asynchat and test_socket_ssl.
It is now possible for multiple instances of the regression test suite to
run in parallel without issue.
2008-04-08 20:47:30 -03:00
|
|
|
HOST = test.test_support.HOST
|
2008-02-02 07:05:00 -04:00
|
|
|
|
|
|
|
HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX")
|
|
|
|
HAVE_FORKING = hasattr(os, "fork") and os.name != "os2"
|
|
|
|
|
2008-03-05 02:19:56 -04:00
|
|
|
def signal_alarm(n):
|
|
|
|
"""Call signal.alarm when it exists (i.e. not on Windows)."""
|
|
|
|
if hasattr(signal, 'alarm'):
|
|
|
|
signal.alarm(n)
|
2001-07-10 08:52:38 -03:00
|
|
|
|
2008-02-02 07:05:00 -04:00
|
|
|
def receive(sock, n, timeout=20):
|
|
|
|
r, w, x = select.select([sock], [], [], timeout)
|
|
|
|
if sock in r:
|
|
|
|
return sock.recv(n)
|
|
|
|
else:
|
|
|
|
raise RuntimeError, "timed out on %r" % (sock,)
|
|
|
|
|
2008-02-03 19:57:24 -04:00
|
|
|
if HAVE_UNIX_SOCKETS:
|
2008-05-24 15:31:28 -03:00
|
|
|
class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
|
|
|
|
SocketServer.UnixStreamServer):
|
2008-02-03 19:57:24 -04:00
|
|
|
pass
|
|
|
|
|
2008-05-24 15:31:28 -03:00
|
|
|
class ForkingUnixDatagramServer(SocketServer.ForkingMixIn,
|
|
|
|
SocketServer.UnixDatagramServer):
|
2008-02-03 19:57:24 -04:00
|
|
|
pass
|
2008-02-02 07:05:00 -04:00
|
|
|
|
|
|
|
|
2008-02-28 14:03:15 -04:00
|
|
|
@contextlib.contextmanager
|
|
|
|
def simple_subprocess(testcase):
|
|
|
|
pid = os.fork()
|
|
|
|
if pid == 0:
|
|
|
|
# Don't throw an exception; it would be caught by the test harness.
|
|
|
|
os._exit(72)
|
|
|
|
yield None
|
|
|
|
pid2, status = os.waitpid(pid, 0)
|
|
|
|
testcase.assertEquals(pid2, pid)
|
|
|
|
testcase.assertEquals(72 << 8, status)
|
|
|
|
|
|
|
|
|
2008-02-02 07:05:00 -04:00
|
|
|
class SocketServerTest(unittest.TestCase):
|
|
|
|
"""Test all socket servers."""
|
|
|
|
|
|
|
|
def setUp(self):
|
2008-03-05 02:19:56 -04:00
|
|
|
signal_alarm(20) # Kill deadlocks after 20 seconds.
|
2008-02-02 07:05:00 -04:00
|
|
|
self.port_seed = 0
|
|
|
|
self.test_files = []
|
|
|
|
|
|
|
|
def tearDown(self):
|
2008-03-23 03:16:04 -03:00
|
|
|
signal_alarm(0) # Didn't deadlock.
|
2008-02-02 07:05:00 -04:00
|
|
|
reap_children()
|
|
|
|
|
|
|
|
for fn in self.test_files:
|
|
|
|
try:
|
|
|
|
os.remove(fn)
|
|
|
|
except os.error:
|
|
|
|
pass
|
|
|
|
self.test_files[:] = []
|
|
|
|
|
|
|
|
def pickaddr(self, proto):
|
|
|
|
if proto == socket.AF_INET:
|
2008-02-28 01:53:18 -04:00
|
|
|
return (HOST, 0)
|
2008-02-02 07:05:00 -04:00
|
|
|
else:
|
2008-02-28 01:53:18 -04:00
|
|
|
# XXX: We need a way to tell AF_UNIX to pick its own name
|
|
|
|
# like AF_INET provides port==0.
|
|
|
|
dir = None
|
|
|
|
if os.name == 'os2':
|
|
|
|
dir = '\socket'
|
|
|
|
fn = tempfile.mktemp(prefix='unix_socket.', dir=dir)
|
2008-02-02 07:05:00 -04:00
|
|
|
if os.name == 'os2':
|
|
|
|
# AF_UNIX socket names on OS/2 require a specific prefix
|
|
|
|
# which can't include a drive letter and must also use
|
|
|
|
# backslashes as directory separators
|
|
|
|
if fn[1] == ':':
|
|
|
|
fn = fn[2:]
|
|
|
|
if fn[0] in (os.sep, os.altsep):
|
|
|
|
fn = fn[1:]
|
|
|
|
if os.sep == '/':
|
|
|
|
fn = fn.replace(os.sep, os.altsep)
|
|
|
|
else:
|
|
|
|
fn = fn.replace(os.altsep, os.sep)
|
|
|
|
self.test_files.append(fn)
|
|
|
|
return fn
|
|
|
|
|
2008-03-07 02:22:15 -04:00
|
|
|
def make_server(self, addr, svrcls, hdlrbase):
|
|
|
|
class MyServer(svrcls):
|
|
|
|
def handle_error(self, request, client_address):
|
|
|
|
self.close_request(request)
|
|
|
|
self.server_close()
|
|
|
|
raise
|
|
|
|
|
2008-02-28 01:53:18 -04:00
|
|
|
class MyHandler(hdlrbase):
|
|
|
|
def handle(self):
|
|
|
|
line = self.rfile.readline()
|
|
|
|
self.wfile.write(line)
|
|
|
|
|
2008-03-07 02:22:15 -04:00
|
|
|
if verbose: print "creating server"
|
|
|
|
server = MyServer(addr, MyHandler)
|
|
|
|
self.assertEquals(server.server_address, server.socket.getsockname())
|
|
|
|
return server
|
|
|
|
|
2010-04-27 20:55:59 -03:00
|
|
|
@unittest.skipUnless(threading, 'Threading required for this test.')
|
2009-10-27 18:27:24 -03:00
|
|
|
@reap_threads
|
2008-03-07 02:22:15 -04:00
|
|
|
def run_server(self, svrcls, hdlrbase, testfunc):
|
|
|
|
server = self.make_server(self.pickaddr(svrcls.address_family),
|
|
|
|
svrcls, hdlrbase)
|
|
|
|
# We had the OS pick a port, so pull the real address out of
|
|
|
|
# the server.
|
|
|
|
addr = server.server_address
|
2008-02-28 01:53:18 -04:00
|
|
|
if verbose:
|
2008-03-07 02:22:15 -04:00
|
|
|
print "server created"
|
2008-02-28 01:53:18 -04:00
|
|
|
print "ADDR =", addr
|
|
|
|
print "CLASS =", svrcls
|
2008-03-07 02:22:15 -04:00
|
|
|
t = threading.Thread(
|
|
|
|
name='%s serving' % svrcls,
|
|
|
|
target=server.serve_forever,
|
|
|
|
# Short poll interval to make the test finish quickly.
|
|
|
|
# Time between requests is short enough that we won't wake
|
|
|
|
# up spuriously too many times.
|
|
|
|
kwargs={'poll_interval':0.01})
|
2008-08-18 15:01:43 -03:00
|
|
|
t.daemon = True # In case this function raises.
|
2008-02-28 01:53:18 -04:00
|
|
|
t.start()
|
|
|
|
if verbose: print "server running"
|
2008-03-07 02:22:15 -04:00
|
|
|
for i in range(3):
|
2008-02-28 01:53:18 -04:00
|
|
|
if verbose: print "test client", i
|
|
|
|
testfunc(svrcls.address_family, addr)
|
|
|
|
if verbose: print "waiting for server"
|
2008-03-07 02:22:15 -04:00
|
|
|
server.shutdown()
|
2008-02-28 01:53:18 -04:00
|
|
|
t.join()
|
|
|
|
if verbose: print "done"
|
2008-02-02 07:05:00 -04:00
|
|
|
|
|
|
|
def stream_examine(self, proto, addr):
|
|
|
|
s = socket.socket(proto, socket.SOCK_STREAM)
|
|
|
|
s.connect(addr)
|
|
|
|
s.sendall(TEST_STR)
|
|
|
|
buf = data = receive(s, 100)
|
|
|
|
while data and '\n' not in buf:
|
|
|
|
data = receive(s, 100)
|
|
|
|
buf += data
|
|
|
|
self.assertEquals(buf, TEST_STR)
|
|
|
|
s.close()
|
|
|
|
|
|
|
|
def dgram_examine(self, proto, addr):
|
|
|
|
s = socket.socket(proto, socket.SOCK_DGRAM)
|
|
|
|
s.sendto(TEST_STR, addr)
|
|
|
|
buf = data = receive(s, 100)
|
|
|
|
while data and '\n' not in buf:
|
|
|
|
data = receive(s, 100)
|
|
|
|
buf += data
|
|
|
|
self.assertEquals(buf, TEST_STR)
|
|
|
|
s.close()
|
|
|
|
|
2008-02-28 01:53:18 -04:00
|
|
|
def test_TCPServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.TCPServer,
|
|
|
|
SocketServer.StreamRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
self.stream_examine)
|
|
|
|
|
|
|
|
def test_ThreadingTCPServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.ThreadingTCPServer,
|
|
|
|
SocketServer.StreamRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
self.stream_examine)
|
|
|
|
|
|
|
|
if HAVE_FORKING:
|
2008-02-28 14:03:15 -04:00
|
|
|
def test_ForkingTCPServer(self):
|
|
|
|
with simple_subprocess(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.ForkingTCPServer,
|
|
|
|
SocketServer.StreamRequestHandler,
|
2008-02-28 14:03:15 -04:00
|
|
|
self.stream_examine)
|
2008-02-28 01:53:18 -04:00
|
|
|
|
|
|
|
if HAVE_UNIX_SOCKETS:
|
|
|
|
def test_UnixStreamServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.UnixStreamServer,
|
|
|
|
SocketServer.StreamRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
self.stream_examine)
|
|
|
|
|
|
|
|
def test_ThreadingUnixStreamServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.ThreadingUnixStreamServer,
|
|
|
|
SocketServer.StreamRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
self.stream_examine)
|
|
|
|
|
2008-02-02 07:05:00 -04:00
|
|
|
if HAVE_FORKING:
|
2008-02-28 01:53:18 -04:00
|
|
|
def test_ForkingUnixStreamServer(self):
|
2008-02-28 14:03:15 -04:00
|
|
|
with simple_subprocess(self):
|
|
|
|
self.run_server(ForkingUnixStreamServer,
|
2008-05-24 15:31:28 -03:00
|
|
|
SocketServer.StreamRequestHandler,
|
2008-02-28 14:03:15 -04:00
|
|
|
self.stream_examine)
|
2008-02-28 01:53:18 -04:00
|
|
|
|
|
|
|
def test_UDPServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.UDPServer,
|
|
|
|
SocketServer.DatagramRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
self.dgram_examine)
|
|
|
|
|
|
|
|
def test_ThreadingUDPServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.ThreadingUDPServer,
|
|
|
|
SocketServer.DatagramRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
self.dgram_examine)
|
|
|
|
|
|
|
|
if HAVE_FORKING:
|
|
|
|
def test_ForkingUDPServer(self):
|
2008-02-28 14:03:15 -04:00
|
|
|
with simple_subprocess(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
self.run_server(SocketServer.ForkingUDPServer,
|
|
|
|
SocketServer.DatagramRequestHandler,
|
2008-02-28 14:03:15 -04:00
|
|
|
self.dgram_examine)
|
2008-02-02 07:05:00 -04:00
|
|
|
|
|
|
|
# Alas, on Linux (at least) recvfrom() doesn't return a meaningful
|
|
|
|
# client address so this cannot work:
|
|
|
|
|
2008-02-28 01:53:18 -04:00
|
|
|
# if HAVE_UNIX_SOCKETS:
|
|
|
|
# def test_UnixDatagramServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
# self.run_server(SocketServer.UnixDatagramServer,
|
|
|
|
# SocketServer.DatagramRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
# self.dgram_examine)
|
|
|
|
#
|
|
|
|
# def test_ThreadingUnixDatagramServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
# self.run_server(SocketServer.ThreadingUnixDatagramServer,
|
|
|
|
# SocketServer.DatagramRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
# self.dgram_examine)
|
|
|
|
#
|
2008-02-02 07:05:00 -04:00
|
|
|
# if HAVE_FORKING:
|
2008-02-28 01:53:18 -04:00
|
|
|
# def test_ForkingUnixDatagramServer(self):
|
2008-05-24 15:31:28 -03:00
|
|
|
# self.run_server(SocketServer.ForkingUnixDatagramServer,
|
|
|
|
# SocketServer.DatagramRequestHandler,
|
2008-02-28 01:53:18 -04:00
|
|
|
# self.dgram_examine)
|
2008-02-02 07:05:00 -04:00
|
|
|
|
2010-04-25 18:40:32 -03:00
|
|
|
@reap_threads
|
|
|
|
def test_shutdown(self):
|
|
|
|
# Issue #2302: shutdown() should always succeed in making an
|
|
|
|
# other thread leave serve_forever().
|
|
|
|
class MyServer(SocketServer.TCPServer):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class MyHandler(SocketServer.StreamRequestHandler):
|
|
|
|
pass
|
|
|
|
|
|
|
|
threads = []
|
|
|
|
for i in range(20):
|
|
|
|
s = MyServer((HOST, 0), MyHandler)
|
|
|
|
t = threading.Thread(
|
|
|
|
name='MyServer serving',
|
|
|
|
target=s.serve_forever,
|
|
|
|
kwargs={'poll_interval':0.01})
|
|
|
|
t.daemon = True # In case this function raises.
|
|
|
|
threads.append((t, s))
|
|
|
|
for t, s in threads:
|
|
|
|
t.start()
|
|
|
|
s.shutdown()
|
|
|
|
for t, s in threads:
|
|
|
|
t.join()
|
|
|
|
|
2001-07-10 08:52:38 -03:00
|
|
|
|
2001-09-17 20:56:20 -03:00
|
|
|
def test_main():
|
|
|
|
if imp.lock_held():
|
2008-02-02 07:05:00 -04:00
|
|
|
# If the import lock is held, the threads will hang
|
2009-03-26 18:10:30 -03:00
|
|
|
raise unittest.SkipTest("can't run when import lock is held")
|
2001-09-17 20:56:20 -03:00
|
|
|
|
2008-02-02 07:05:00 -04:00
|
|
|
test.test_support.run_unittest(SocketServerTest)
|
2001-07-10 08:52:38 -03:00
|
|
|
|
2001-09-17 20:56:20 -03:00
|
|
|
if __name__ == "__main__":
|
|
|
|
test_main()
|
2008-03-19 21:58:44 -03:00
|
|
|
signal_alarm(3) # Shutdown shouldn't take more than 3 seconds.
|