blob: 99ed00fc001363d594946fc7296ce16012583ff0 [file] [log] [blame]
Thomas Woutersed03b412007-08-28 21:37:11 +00001# Test the support for SSL and sockets
2
3import sys
4import unittest
5from test import test_support
6import socket
Bill Janssen6e027db2007-11-15 22:23:56 +00007import select
Thomas Woutersed03b412007-08-28 21:37:11 +00008import errno
Thomas Woutersed03b412007-08-28 21:37:11 +00009import subprocess
10import time
11import os
12import pprint
Thomas Wouters1b7f8912007-09-19 03:06:30 +000013import urllib, urlparse
Thomas Woutersed03b412007-08-28 21:37:11 +000014import shutil
15import traceback
Bill Janssen54cc54c2007-12-14 22:08:56 +000016import asyncore
Thomas Woutersed03b412007-08-28 21:37:11 +000017
Thomas Wouters1b7f8912007-09-19 03:06:30 +000018from BaseHTTPServer import HTTPServer
19from SimpleHTTPServer import SimpleHTTPRequestHandler
20
Thomas Woutersed03b412007-08-28 21:37:11 +000021# Optionally test SSL support, if we have it in the tested platform
22skip_expected = False
23try:
24 import ssl
25except ImportError:
26 skip_expected = True
27
Christian Heimes5e696852008-04-09 08:37:03 +000028HOST = test_support.HOST
Thomas Woutersed03b412007-08-28 21:37:11 +000029CERTFILE = None
Thomas Wouters1b7f8912007-09-19 03:06:30 +000030SVN_PYTHON_ORG_ROOT_CERT = None
Thomas Woutersed03b412007-08-28 21:37:11 +000031
Thomas Woutersed03b412007-08-28 21:37:11 +000032def handle_error(prefix):
33 exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
Thomas Wouters1b7f8912007-09-19 03:06:30 +000034 if test_support.verbose:
35 sys.stdout.write(prefix + exc_format)
Thomas Woutersed03b412007-08-28 21:37:11 +000036
37
38class BasicTests(unittest.TestCase):
39
Georg Brandlfceab5a2008-01-19 20:08:23 +000040 def testSSLconnect(self):
41 if not test_support.is_resource_enabled('network'):
42 return
43 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
44 cert_reqs=ssl.CERT_NONE)
45 s.connect(("svn.python.org", 443))
46 c = s.getpeercert()
47 if c:
48 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
49 s.close()
50
51 # this should fail because we have no verification certs
52 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
53 cert_reqs=ssl.CERT_REQUIRED)
54 try:
55 s.connect(("svn.python.org", 443))
56 except ssl.SSLError:
57 pass
58 finally:
59 s.close()
60
Thomas Wouters1b7f8912007-09-19 03:06:30 +000061 def testCrucialConstants(self):
62 ssl.PROTOCOL_SSLv2
63 ssl.PROTOCOL_SSLv23
64 ssl.PROTOCOL_SSLv3
65 ssl.PROTOCOL_TLSv1
66 ssl.CERT_NONE
67 ssl.CERT_OPTIONAL
68 ssl.CERT_REQUIRED
Thomas Woutersed03b412007-08-28 21:37:11 +000069
Thomas Wouters1b7f8912007-09-19 03:06:30 +000070 def testRAND(self):
71 v = ssl.RAND_status()
72 if test_support.verbose:
73 sys.stdout.write("\n RAND_status is %d (%s)\n"
74 % (v, (v and "sufficient randomness") or
75 "insufficient randomness"))
Thomas Woutersed03b412007-08-28 21:37:11 +000076 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000077 ssl.RAND_egd(1)
78 except TypeError:
79 pass
Thomas Woutersed03b412007-08-28 21:37:11 +000080 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +000081 print("didn't raise TypeError")
82 ssl.RAND_add("this is a random string", 75.0)
Thomas Woutersed03b412007-08-28 21:37:11 +000083
Thomas Wouters1b7f8912007-09-19 03:06:30 +000084 def testParseCert(self):
85 # note that this uses an 'unofficial' function in _ssl.c,
86 # provided solely for this test, to exercise the certificate
87 # parsing code
88 p = ssl._ssl._test_decode_cert(CERTFILE, False)
89 if test_support.verbose:
90 sys.stdout.write("\n" + pprint.pformat(p) + "\n")
Thomas Woutersed03b412007-08-28 21:37:11 +000091
Thomas Wouters1b7f8912007-09-19 03:06:30 +000092 def testDERtoPEM(self):
93
94 pem = open(SVN_PYTHON_ORG_ROOT_CERT, 'r').read()
95 d1 = ssl.PEM_cert_to_DER_cert(pem)
96 p2 = ssl.DER_cert_to_PEM_cert(d1)
97 d2 = ssl.PEM_cert_to_DER_cert(p2)
98 if (d1 != d2):
99 raise test_support.TestFailed("PEM-to-DER or DER-to-PEM translation failed")
100
Bill Janssen6e027db2007-11-15 22:23:56 +0000101class NetworkedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000102
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000103 def testConnect(self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000104 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
105 cert_reqs=ssl.CERT_NONE)
106 s.connect(("svn.python.org", 443))
107 c = s.getpeercert()
108 if c:
109 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
110 s.close()
111
112 # this should fail because we have no verification certs
113 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
114 cert_reqs=ssl.CERT_REQUIRED)
Thomas Woutersed03b412007-08-28 21:37:11 +0000115 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000116 s.connect(("svn.python.org", 443))
117 except ssl.SSLError:
118 pass
119 finally:
120 s.close()
121
122 # this should succeed because we specify the root cert
123 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
124 cert_reqs=ssl.CERT_REQUIRED,
125 ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
126 try:
127 s.connect(("svn.python.org", 443))
128 except ssl.SSLError as x:
129 raise test_support.TestFailed("Unexpected exception %s" % x)
130 finally:
131 s.close()
132
Bill Janssen6e027db2007-11-15 22:23:56 +0000133 def testNonBlockingHandshake(self):
134 s = socket.socket(socket.AF_INET)
135 s.connect(("svn.python.org", 443))
136 s.setblocking(False)
137 s = ssl.wrap_socket(s,
138 cert_reqs=ssl.CERT_NONE,
139 do_handshake_on_connect=False)
140 count = 0
141 while True:
142 try:
143 count += 1
144 s.do_handshake()
145 break
146 except ssl.SSLError as err:
147 if err.args[0] == ssl.SSL_ERROR_WANT_READ:
148 select.select([s], [], [])
149 elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
150 select.select([], [s], [])
151 else:
152 raise
153 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000154 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000155 sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000156
Bill Janssen54cc54c2007-12-14 22:08:56 +0000157 def testFetchServerCert(self):
158
159 pem = ssl.get_server_certificate(("svn.python.org", 443))
160 if not pem:
161 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
162
163 return
164
165 try:
166 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE)
167 except ssl.SSLError as x:
168 #should fail
169 if test_support.verbose:
170 sys.stdout.write("%s\n" % x)
171 else:
172 raise test_support.TestFailed("Got server certificate %s for svn.python.org!" % pem)
173
174 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
175 if not pem:
176 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
177 if test_support.verbose:
178 sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem)
179
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000180
181try:
182 import threading
183except ImportError:
184 _have_threads = False
185else:
186
187 _have_threads = True
188
189 class ThreadedEchoServer(threading.Thread):
190
191 class ConnectionHandler(threading.Thread):
192
193 """A mildly complicated class, because we want it to work both
194 with and without the SSL wrapper around the socket connection, so
195 that we can test the STARTTLS functionality."""
196
Bill Janssen6e027db2007-11-15 22:23:56 +0000197 def __init__(self, server, connsock, addr):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000198 self.server = server
199 self.running = False
200 self.sock = connsock
Bill Janssen6e027db2007-11-15 22:23:56 +0000201 self.addr = addr
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000202 self.sock.setblocking(1)
203 self.sslconn = None
204 threading.Thread.__init__(self)
205 self.setDaemon(True)
206
207 def wrap_conn (self):
208 try:
209 self.sslconn = ssl.wrap_socket(self.sock, server_side=True,
210 certfile=self.server.certificate,
211 ssl_version=self.server.protocol,
212 ca_certs=self.server.cacerts,
213 cert_reqs=self.server.certreqs)
214 except:
215 if self.server.chatty:
Bill Janssen6e027db2007-11-15 22:23:56 +0000216 handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000217 if not self.server.expect_bad_connects:
218 # here, we want to stop the server, because this shouldn't
219 # happen in the context of our test case
220 self.running = False
221 # normally, we'd just stop here, but for the test
222 # harness, we want to stop the server
223 self.server.stop()
Bill Janssen6e027db2007-11-15 22:23:56 +0000224 self.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000225 return False
226
227 else:
228 if self.server.certreqs == ssl.CERT_REQUIRED:
229 cert = self.sslconn.getpeercert()
230 if test_support.verbose and self.server.chatty:
231 sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
232 cert_binary = self.sslconn.getpeercert(True)
233 if test_support.verbose and self.server.chatty:
234 sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n")
235 cipher = self.sslconn.cipher()
236 if test_support.verbose and self.server.chatty:
237 sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n")
238 return True
239
240 def read(self):
241 if self.sslconn:
242 return self.sslconn.read()
243 else:
244 return self.sock.recv(1024)
245
246 def write(self, bytes):
247 if self.sslconn:
248 return self.sslconn.write(bytes)
249 else:
250 return self.sock.send(bytes)
251
252 def close(self):
253 if self.sslconn:
254 self.sslconn.close()
255 else:
256 self.sock.close()
257
258 def run (self):
259 self.running = True
260 if not self.server.starttls_server:
261 if not self.wrap_conn():
262 return
263 while self.running:
264 try:
265 msg = self.read()
Bill Janssen6e027db2007-11-15 22:23:56 +0000266 amsg = (msg and str(msg, 'ASCII', 'strict')) or ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000267 if not msg:
268 # eof, so quit this handler
269 self.running = False
270 self.close()
Bill Janssen6e027db2007-11-15 22:23:56 +0000271 elif amsg.strip() == 'over':
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000272 if test_support.verbose and self.server.connectionchatty:
273 sys.stdout.write(" server: client closed connection\n")
274 self.close()
275 return
Bill Janssen6e027db2007-11-15 22:23:56 +0000276 elif (self.server.starttls_server and
277 amsg.strip() == 'STARTTLS'):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000278 if test_support.verbose and self.server.connectionchatty:
279 sys.stdout.write(" server: read STARTTLS from client, sending OK...\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000280 self.write("OK\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000281 if not self.wrap_conn():
282 return
283 else:
284 if (test_support.verbose and
285 self.server.connectionchatty):
286 ctype = (self.sslconn and "encrypted") or "unencrypted"
287 sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n"
288 % (repr(msg), ctype, repr(msg.lower()), ctype))
Bill Janssen6e027db2007-11-15 22:23:56 +0000289 self.write(amsg.lower().encode('ASCII', 'strict'))
290 except socket.error:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000291 if self.server.chatty:
292 handle_error("Test server failure:\n")
293 self.close()
294 self.running = False
295 # normally, we'd just stop here, but for the test
296 # harness, we want to stop the server
297 self.server.stop()
298 except:
299 handle_error('')
300
Christian Heimes5e696852008-04-09 08:37:03 +0000301 def __init__(self, certificate, ssl_version=None,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000302 certreqs=None, cacerts=None, expect_bad_connects=False,
303 chatty=True, connectionchatty=False, starttls_server=False):
304 if ssl_version is None:
305 ssl_version = ssl.PROTOCOL_TLSv1
306 if certreqs is None:
307 certreqs = ssl.CERT_NONE
308 self.certificate = certificate
309 self.protocol = ssl_version
310 self.certreqs = certreqs
311 self.cacerts = cacerts
312 self.expect_bad_connects = expect_bad_connects
313 self.chatty = chatty
314 self.connectionchatty = connectionchatty
315 self.starttls_server = starttls_server
316 self.sock = socket.socket()
Christian Heimes5e696852008-04-09 08:37:03 +0000317 self.port = test_support.bind_port(self.sock)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000318 self.flag = None
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000319 self.active = False
320 threading.Thread.__init__(self)
321 self.setDaemon(False)
322
323 def start (self, flag=None):
324 self.flag = flag
325 threading.Thread.start(self)
326
327 def run (self):
328 self.sock.settimeout(0.5)
329 self.sock.listen(5)
330 self.active = True
331 if self.flag:
332 # signal an event
333 self.flag.set()
334 while self.active:
335 try:
336 newconn, connaddr = self.sock.accept()
337 if test_support.verbose and self.chatty:
338 sys.stdout.write(' server: new connection from '
Bill Janssen6e027db2007-11-15 22:23:56 +0000339 + repr(connaddr) + '\n')
340 handler = self.ConnectionHandler(self, newconn, connaddr)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000341 handler.start()
342 except socket.timeout:
343 pass
344 except KeyboardInterrupt:
345 self.stop()
346 except:
347 if self.chatty:
348 handle_error("Test server failure:\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000349 self.sock.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000350
351 def stop (self):
352 self.active = False
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000353
Bill Janssen54cc54c2007-12-14 22:08:56 +0000354 class OurHTTPSServer(threading.Thread):
355
356 # This one's based on HTTPServer, which is based on SocketServer
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000357
358 class HTTPSServer(HTTPServer):
359
360 def __init__(self, server_address, RequestHandlerClass, certfile):
361
362 HTTPServer.__init__(self, server_address, RequestHandlerClass)
363 # we assume the certfile contains both private key and certificate
364 self.certfile = certfile
365 self.active = False
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000366 self.active_lock = threading.Lock()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000367 self.allow_reuse_address = True
368
Bill Janssen6e027db2007-11-15 22:23:56 +0000369 def __str__(self):
370 return ('<%s %s:%s>' %
371 (self.__class__.__name__,
372 self.server_name,
373 self.server_port))
374
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000375 def get_request (self):
376 # override this to wrap socket with SSL
377 sock, addr = self.socket.accept()
378 sslconn = ssl.wrap_socket(sock, server_side=True,
379 certfile=self.certfile)
380 return sslconn, addr
381
382 # The methods overridden below this are mainly so that we
383 # can run it in a thread and be able to stop it from another
384 # You probably wouldn't need them in other uses.
385
386 def server_activate(self):
387 # We want to run this in a thread for testing purposes,
388 # so we override this to set timeout, so that we get
389 # a chance to stop the server
390 self.socket.settimeout(0.5)
391 HTTPServer.server_activate(self)
392
393 def serve_forever(self):
394 # We want this to run in a thread, so we use a slightly
395 # modified version of "forever".
396 self.active = True
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000397 while 1:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000398 try:
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000399 # We need to lock while handling the request.
400 # Another thread can close the socket after self.active
401 # has been checked and before the request is handled.
402 # This causes an exception when using the closed socket.
403 with self.active_lock:
404 if not self.active:
405 break
406 self.handle_request()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000407 except socket.timeout:
408 pass
409 except KeyboardInterrupt:
410 self.server_close()
411 return
412 except:
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000413 sys.stdout.write(''.join(traceback.format_exception(*sys.exc_info())))
414 break
Neal Norwitzf9ff5f02008-03-31 05:39:26 +0000415 time.sleep(0.1)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000416
417 def server_close(self):
418 # Again, we want this to run in a thread, so we need to override
419 # close to clear the "active" flag, so that serve_forever() will
420 # terminate.
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000421 with self.active_lock:
422 HTTPServer.server_close(self)
423 self.active = False
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000424
425 class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):
426
427 # need to override translate_path to get a known root,
428 # instead of using os.curdir, since the test could be
429 # run from anywhere
430
431 server_version = "TestHTTPS/1.0"
432
433 root = None
434
435 def translate_path(self, path):
436 """Translate a /-separated PATH to the local filename syntax.
437
438 Components that mean special things to the local file system
439 (e.g. drive or directory names) are ignored. (XXX They should
440 probably be diagnosed.)
441
442 """
443 # abandon query parameters
444 path = urlparse.urlparse(path)[2]
445 path = os.path.normpath(urllib.unquote(path))
446 words = path.split('/')
447 words = filter(None, words)
448 path = self.root
449 for word in words:
450 drive, word = os.path.splitdrive(word)
451 head, word = os.path.split(word)
452 if word in self.root: continue
453 path = os.path.join(path, word)
454 return path
455
456 def log_message(self, format, *args):
457
458 # we override this to suppress logging unless "verbose"
459
Thomas Wouters89d996e2007-09-08 17:39:28 +0000460 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000461 sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" %
462 (self.server.server_address,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000463 self.server.server_port,
464 self.request.cipher(),
465 self.log_date_time_string(),
466 format%args))
Thomas Woutersed03b412007-08-28 21:37:11 +0000467
468
Christian Heimes5e696852008-04-09 08:37:03 +0000469 def __init__(self, certfile):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000470 self.flag = None
471 self.active = False
472 self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0]
Christian Heimes5e696852008-04-09 08:37:03 +0000473 self.port = test_support.find_unused_port()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000474 self.server = self.HTTPSServer(
Christian Heimes5e696852008-04-09 08:37:03 +0000475 (HOST, self.port), self.RootedHTTPRequestHandler, certfile)
Thomas Woutersed03b412007-08-28 21:37:11 +0000476 threading.Thread.__init__(self)
477 self.setDaemon(True)
478
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000479 def __str__(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000480 return "<%s %s>" % (self.__class__.__name__, self.server)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000481
482 def start (self, flag=None):
483 self.flag = flag
484 threading.Thread.start(self)
485
Thomas Woutersed03b412007-08-28 21:37:11 +0000486 def run (self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000487 self.active = True
488 if self.flag:
489 self.flag.set()
490 self.server.serve_forever()
491 self.active = False
492
493 def stop (self):
494 self.active = False
495 self.server.server_close()
496
497
Bill Janssen54cc54c2007-12-14 22:08:56 +0000498 class AsyncoreEchoServer(threading.Thread):
499
500 # this one's based on asyncore.dispatcher
501
502 class EchoServer (asyncore.dispatcher):
503
504 class ConnectionHandler (asyncore.dispatcher_with_send):
505
506 def __init__(self, conn, certfile):
507 self.socket = ssl.wrap_socket(conn, server_side=True,
508 certfile=certfile,
509 do_handshake_on_connect=False)
510 asyncore.dispatcher_with_send.__init__(self, self.socket)
511 # now we have to do the handshake
512 # we'll just do it the easy way, and block the connection
513 # till it's finished. If we were doing it right, we'd
514 # do this in multiple calls to handle_read...
515 self.do_handshake(block=True)
516
517 def readable(self):
518 if isinstance(self.socket, ssl.SSLSocket):
519 while self.socket.pending() > 0:
520 self.handle_read_event()
521 return True
522
523 def handle_read(self):
524 data = self.recv(1024)
525 if test_support.verbose:
526 sys.stdout.write(" server: read %s from client\n" % repr(data))
527 if not data:
528 self.close()
529 else:
530 self.send(str(data, 'ASCII', 'strict').lower().encode('ASCII', 'strict'))
531
532 def handle_close(self):
533 if test_support.verbose:
534 sys.stdout.write(" server: closed connection %s\n" % self.socket)
535
536 def handle_error(self):
537 raise
538
539 def __init__(self, port, certfile):
540 self.port = port
541 self.certfile = certfile
542 asyncore.dispatcher.__init__(self)
543 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
544 self.bind(('', port))
545 self.listen(5)
546
547 def handle_accept(self):
548 sock_obj, addr = self.accept()
549 if test_support.verbose:
550 sys.stdout.write(" server: new connection from %s:%s\n" %addr)
551 self.ConnectionHandler(sock_obj, self.certfile)
552
553 def handle_error(self):
554 raise
555
556 def __init__(self, port, certfile):
557 self.flag = None
558 self.active = False
559 self.server = self.EchoServer(port, certfile)
560 threading.Thread.__init__(self)
561 self.setDaemon(True)
562
563 def __str__(self):
564 return "<%s %s>" % (self.__class__.__name__, self.server)
565
566 def start (self, flag=None):
567 self.flag = flag
568 threading.Thread.start(self)
569
570 def run (self):
571 self.active = True
572 if self.flag:
573 self.flag.set()
574 while self.active:
575 try:
576 asyncore.loop(1)
577 except:
578 pass
579
580 def stop (self):
581 self.active = False
582 self.server.close()
583
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000584 def badCertTest (certfile):
Christian Heimes5e696852008-04-09 08:37:03 +0000585 server = ThreadedEchoServer(CERTFILE,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000586 certreqs=ssl.CERT_REQUIRED,
Bill Janssen6e027db2007-11-15 22:23:56 +0000587 cacerts=CERTFILE, chatty=False,
588 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000589 flag = threading.Event()
590 server.start(flag)
591 # wait for it to start
592 flag.wait()
593 # try to connect
594 try:
Thomas Woutersed03b412007-08-28 21:37:11 +0000595 try:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000596 s = ssl.wrap_socket(socket.socket(),
597 certfile=certfile,
598 ssl_version=ssl.PROTOCOL_TLSv1)
Christian Heimes5e696852008-04-09 08:37:03 +0000599 s.connect((HOST, server.port))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000600 except ssl.SSLError as x:
Thomas Woutersed03b412007-08-28 21:37:11 +0000601 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000602 sys.stdout.write("\nSSLError is %s\n" % x)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000603 else:
604 raise test_support.TestFailed(
605 "Use of invalid cert should have failed!")
606 finally:
607 server.stop()
608 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000609
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000610 def serverParamsTest (certfile, protocol, certreqs, cacertsfile,
Bill Janssen6e027db2007-11-15 22:23:56 +0000611 client_certfile, client_protocol=None,
612 indata="FOO\n",
613 chatty=False, connectionchatty=False):
Thomas Woutersed03b412007-08-28 21:37:11 +0000614
Christian Heimes5e696852008-04-09 08:37:03 +0000615 server = ThreadedEchoServer(certfile,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000616 certreqs=certreqs,
617 ssl_version=protocol,
618 cacerts=cacertsfile,
619 chatty=chatty,
Bill Janssen6e027db2007-11-15 22:23:56 +0000620 connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000621 flag = threading.Event()
622 server.start(flag)
623 # wait for it to start
624 flag.wait()
625 # try to connect
626 if client_protocol is None:
627 client_protocol = protocol
628 try:
Bill Janssen6e027db2007-11-15 22:23:56 +0000629 s = ssl.wrap_socket(socket.socket(),
630 certfile=client_certfile,
631 ca_certs=cacertsfile,
632 cert_reqs=certreqs,
633 ssl_version=client_protocol)
Christian Heimes5e696852008-04-09 08:37:03 +0000634 s.connect((HOST, server.port))
Bill Janssen6e027db2007-11-15 22:23:56 +0000635 except ssl.SSLError as x:
636 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
637 except Exception as x:
638 raise test_support.TestFailed("Unexpected exception: " + str(x))
639 else:
640 if connectionchatty:
641 if test_support.verbose:
642 sys.stdout.write(
643 " client: sending %s...\n" % (repr(indata)))
Christian Heimes5e696852008-04-09 08:37:03 +0000644 s.write(indata)
Bill Janssen6e027db2007-11-15 22:23:56 +0000645 outdata = s.read()
646 if connectionchatty:
647 if test_support.verbose:
648 sys.stdout.write(" client: read %s\n" % repr(outdata))
Bill Janssen6e027db2007-11-15 22:23:56 +0000649 if outdata != indata.lower():
650 raise test_support.TestFailed(
651 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
Christian Heimes5e696852008-04-09 08:37:03 +0000652 % (outdata[:min(len(outdata),20)], len(outdata),
653 indata[:min(len(indata),20)].lower(), len(indata)))
654 s.write("over\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000655 if connectionchatty:
656 if test_support.verbose:
657 sys.stdout.write(" client: closing connection.\n")
658 s.close()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000659 finally:
660 server.stop()
661 server.join()
Thomas Woutersed03b412007-08-28 21:37:11 +0000662
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000663 def tryProtocolCombo (server_protocol,
664 client_protocol,
665 expectedToWork,
666 certsreqs=None):
Thomas Woutersed03b412007-08-28 21:37:11 +0000667
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000668 if certsreqs is None:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000669 certsreqs = ssl.CERT_NONE
Thomas Woutersed03b412007-08-28 21:37:11 +0000670
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000671 if certsreqs == ssl.CERT_NONE:
672 certtype = "CERT_NONE"
673 elif certsreqs == ssl.CERT_OPTIONAL:
674 certtype = "CERT_OPTIONAL"
675 elif certsreqs == ssl.CERT_REQUIRED:
676 certtype = "CERT_REQUIRED"
Thomas Woutersed03b412007-08-28 21:37:11 +0000677 if test_support.verbose:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000678 formatstr = (expectedToWork and " %s->%s %s\n") or " {%s->%s} %s\n"
679 sys.stdout.write(formatstr %
680 (ssl.get_protocol_name(client_protocol),
681 ssl.get_protocol_name(server_protocol),
682 certtype))
683 try:
684 serverParamsTest(CERTFILE, server_protocol, certsreqs,
Bill Janssen6e027db2007-11-15 22:23:56 +0000685 CERTFILE, CERTFILE, client_protocol,
686 chatty=False, connectionchatty=False)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000687 except test_support.TestFailed:
688 if expectedToWork:
689 raise
690 else:
691 if not expectedToWork:
692 raise test_support.TestFailed(
693 "Client protocol %s succeeded with server protocol %s!"
694 % (ssl.get_protocol_name(client_protocol),
695 ssl.get_protocol_name(server_protocol)))
696
697
Bill Janssen6e027db2007-11-15 22:23:56 +0000698 class ThreadedTests(unittest.TestCase):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000699
Christian Heimes5e696852008-04-09 08:37:03 +0000700 def testRudeShutdown(self):
701
702 listener_ready = threading.Event()
703 listener_gone = threading.Event()
704 port = test_support.find_unused_port()
705
706 # `listener` runs in a thread. It opens a socket listening on
707 # PORT, and sits in an accept() until the main thread connects.
708 # Then it rudely closes the socket, and sets Event `listener_gone`
709 # to let the main thread know the socket is gone.
710 def listener():
711 s = socket.socket()
712 s.bind((HOST, port))
713 s.listen(5)
714 listener_ready.set()
715 s.accept()
716 s = None # reclaim the socket object, which also closes it
717 listener_gone.set()
718
719 def connector():
720 listener_ready.wait()
721 s = socket.socket()
722 s.connect((HOST, port))
723 listener_gone.wait()
724 try:
725 ssl_sock = ssl.wrap_socket(s)
726 except IOError:
727 pass
728 else:
729 raise test_support.TestFailed(
730 'connecting to closed SSL socket should have failed')
731
732 t = threading.Thread(target=listener)
733 t.start()
734 connector()
735 t.join()
736
737 def testEcho(self):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000738
739 if test_support.verbose:
740 sys.stdout.write("\n")
741 serverParamsTest(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,
742 CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,
743 chatty=True, connectionchatty=True)
744
745 def testReadCert(self):
746
747 if test_support.verbose:
748 sys.stdout.write("\n")
749 s2 = socket.socket()
Christian Heimes5e696852008-04-09 08:37:03 +0000750 server = ThreadedEchoServer(CERTFILE,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000751 certreqs=ssl.CERT_NONE,
752 ssl_version=ssl.PROTOCOL_SSLv23,
753 cacerts=CERTFILE,
754 chatty=False)
755 flag = threading.Event()
756 server.start(flag)
757 # wait for it to start
758 flag.wait()
759 # try to connect
760 try:
761 try:
762 s = ssl.wrap_socket(socket.socket(),
763 certfile=CERTFILE,
764 ca_certs=CERTFILE,
765 cert_reqs=ssl.CERT_REQUIRED,
766 ssl_version=ssl.PROTOCOL_SSLv23)
Christian Heimes5e696852008-04-09 08:37:03 +0000767 s.connect((HOST, server.port))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000768 except ssl.SSLError as x:
769 raise test_support.TestFailed(
770 "Unexpected SSL error: " + str(x))
771 except Exception as x:
772 raise test_support.TestFailed(
773 "Unexpected exception: " + str(x))
774 else:
775 if not s:
776 raise test_support.TestFailed(
777 "Can't SSL-handshake with test server")
778 cert = s.getpeercert()
779 if not cert:
780 raise test_support.TestFailed(
781 "Can't get peer certificate.")
782 cipher = s.cipher()
783 if test_support.verbose:
784 sys.stdout.write(pprint.pformat(cert) + '\n')
785 sys.stdout.write("Connection cipher is " + str(cipher) + '.\n')
Bill Janssen6e027db2007-11-15 22:23:56 +0000786 if 'subject' not in cert:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000787 raise test_support.TestFailed(
788 "No subject field in certificate: %s." %
789 pprint.pformat(cert))
790 if ((('organizationName', 'Python Software Foundation'),)
791 not in cert['subject']):
792 raise test_support.TestFailed(
793 "Missing or invalid 'organizationName' field in certificate subject; "
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000794 "should be 'Python Software Foundation'.")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000795 s.close()
796 finally:
797 server.stop()
798 server.join()
799
800 def testNULLcert(self):
801 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
802 "nullcert.pem"))
803 def testMalformedCert(self):
804 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
805 "badcert.pem"))
806 def testMalformedKey(self):
807 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
808 "badkey.pem"))
809
810 def testProtocolSSL2(self):
811 if test_support.verbose:
812 sys.stdout.write("\n")
813 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True)
814 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL)
815 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED)
816 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
817 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
818 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
819
820 def testProtocolSSL23(self):
821 if test_support.verbose:
822 sys.stdout.write("\n")
823 try:
824 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True)
825 except test_support.TestFailed as x:
826 # this fails on some older versions of OpenSSL (0.9.7l, for instance)
827 if test_support.verbose:
828 sys.stdout.write(
829 " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"
830 % str(x))
831 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)
832 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)
833 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)
834
835 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
836 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)
837 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
838
839 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
840 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
841 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
842
843 def testProtocolSSL3(self):
844 if test_support.verbose:
845 sys.stdout.write("\n")
846 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
847 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
848 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
849 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
850 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
851 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
852
853 def testProtocolTLS1(self):
854 if test_support.verbose:
855 sys.stdout.write("\n")
856 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
857 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
858 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
859 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
860 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
861 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
862
863 def testSTARTTLS (self):
864
865 msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4")
866
Christian Heimes5e696852008-04-09 08:37:03 +0000867 server = ThreadedEchoServer(CERTFILE,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000868 ssl_version=ssl.PROTOCOL_TLSv1,
869 starttls_server=True,
870 chatty=True,
871 connectionchatty=True)
872 flag = threading.Event()
873 server.start(flag)
874 # wait for it to start
875 flag.wait()
876 # try to connect
877 wrapped = False
878 try:
879 try:
880 s = socket.socket()
881 s.setblocking(1)
Christian Heimes5e696852008-04-09 08:37:03 +0000882 s.connect((HOST, server.port))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000883 except Exception as x:
884 raise test_support.TestFailed("Unexpected exception: " + str(x))
885 else:
886 if test_support.verbose:
887 sys.stdout.write("\n")
888 for indata in msgs:
Bill Janssen6e027db2007-11-15 22:23:56 +0000889 msg = indata.encode('ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000890 if test_support.verbose:
891 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000892 " client: sending %s...\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000893 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000894 conn.write(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000895 outdata = conn.read()
896 else:
Bill Janssen6e027db2007-11-15 22:23:56 +0000897 s.send(msg)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000898 outdata = s.recv(1024)
899 if (indata == "STARTTLS" and
Bill Janssen6e027db2007-11-15 22:23:56 +0000900 str(outdata, 'ASCII', 'replace').strip().lower().startswith("ok")):
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000901 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000902 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000903 sys.stdout.write(
904 " client: read %s from server, starting TLS...\n"
Bill Janssen6e027db2007-11-15 22:23:56 +0000905 % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000906 conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
907
908 wrapped = True
909 else:
910 if test_support.verbose:
Bill Janssen6e027db2007-11-15 22:23:56 +0000911 msg = str(outdata, 'ASCII', 'replace')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000912 sys.stdout.write(
Bill Janssen6e027db2007-11-15 22:23:56 +0000913 " client: read %s from server\n" % repr(msg))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000914 if test_support.verbose:
915 sys.stdout.write(" client: closing connection.\n")
916 if wrapped:
Bill Janssen6e027db2007-11-15 22:23:56 +0000917 conn.write("over\n".encode("ASCII", "strict"))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000918 else:
919 s.send("over\n")
Bill Janssen6e027db2007-11-15 22:23:56 +0000920 if wrapped:
921 conn.close()
922 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000923 s.close()
924 finally:
925 server.stop()
926 server.join()
927
Bill Janssen54cc54c2007-12-14 22:08:56 +0000928 def testSocketServer(self):
Bill Janssen6e027db2007-11-15 22:23:56 +0000929
Christian Heimes5e696852008-04-09 08:37:03 +0000930
931 server = AsyncoreHTTPSServer(CERTFILE)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000932 flag = threading.Event()
933 server.start(flag)
934 # wait for it to start
935 flag.wait()
936 # try to connect
937 try:
938 if test_support.verbose:
939 sys.stdout.write('\n')
940 d1 = open(CERTFILE, 'rb').read()
941 d2 = ''
942 # now fetch the same data from the HTTPS server
Christian Heimes5e696852008-04-09 08:37:03 +0000943 url = 'https://%s:%d/%s' % (
944 HOST, server.port, os.path.split(CERTFILE)[1])
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000945 f = urllib.urlopen(url)
946 dlen = f.info().getheader("content-length")
947 if dlen and (int(dlen) > 0):
948 d2 = f.read(int(dlen))
949 if test_support.verbose:
950 sys.stdout.write(
951 " client: read %d bytes from remote server '%s'\n"
952 % (len(d2), server))
953 f.close()
954 except:
955 msg = ''.join(traceback.format_exception(*sys.exc_info()))
956 if test_support.verbose:
957 sys.stdout.write('\n' + msg)
958 raise test_support.TestFailed(msg)
959 else:
960 if not (d1 == d2):
Bill Janssen6e027db2007-11-15 22:23:56 +0000961 print("d1 is", len(d1), repr(d1))
962 print("d2 is", len(d2), repr(d2))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000963 raise test_support.TestFailed(
964 "Couldn't fetch data from HTTPS server")
965 finally:
Neal Norwitzf9ff5f02008-03-31 05:39:26 +0000966 if test_support.verbose:
967 sys.stdout.write('stopping server\n')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000968 server.stop()
Neal Norwitzf9ff5f02008-03-31 05:39:26 +0000969 if test_support.verbose:
970 sys.stdout.write('joining thread\n')
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000971 server.join()
972
Thomas Woutersed03b412007-08-28 21:37:11 +0000973def test_main(verbose=False):
974 if skip_expected:
Thomas Wouters89d996e2007-09-08 17:39:28 +0000975 raise test_support.TestSkipped("No SSL support")
Thomas Woutersed03b412007-08-28 21:37:11 +0000976
Christian Heimes5e696852008-04-09 08:37:03 +0000977 global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT
Thomas Woutersed03b412007-08-28 21:37:11 +0000978 CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
979 "keycert.pem")
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000980 SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
981 os.path.dirname(__file__) or os.curdir,
982 "https_svn_python_org_root.pem")
983
984 if (not os.path.exists(CERTFILE) or
985 not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
986 raise test_support.TestFailed("Can't read certificate files!")
Bill Janssen6e027db2007-11-15 22:23:56 +0000987
Thomas Woutersed03b412007-08-28 21:37:11 +0000988 tests = [BasicTests]
989
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000990 if test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +0000991 tests.append(NetworkedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000992
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000993 if _have_threads:
994 thread_info = test_support.threading_setup()
995 if thread_info and test_support.is_resource_enabled('network'):
Bill Janssen6e027db2007-11-15 22:23:56 +0000996 tests.append(ThreadedTests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000997
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000998 test_support.run_unittest(*tests)
Thomas Woutersed03b412007-08-28 21:37:11 +0000999
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001000 if _have_threads:
1001 test_support.threading_cleanup(*thread_info)
Thomas Woutersed03b412007-08-28 21:37:11 +00001002
1003if __name__ == "__main__":
1004 test_main()