class TestTCPConnectOperation(unittest.TestCase): def setUp(self): self.test_op = TestOperation() self.sock = socket.socket() self.sock.bind(("localhost", 0)) self.sock.listen(0) self.shutdown = True self.test_op.start() def tearDown(self): if self.shutdown: self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() def accept(self): class Accept: def __init__(self): self.thread = None self.sock = None self.addr = None accept = Accept() def aux(accept): accept.sock, accept.addr = self.sock.accept() accept.thread = threading.Thread(target=aux, args=(accept,)) accept.thread.start() return accept def test_success(self): """ Test a successful connection. """ op = TCPConnectOperation(self.test_op, self.sock.getsockname()) op.callback = op_callback() op.start() accept = self.accept() while not op.callback.called: self.test_op.run_selector() accept.thread.join() try: op.callback.assert_called_once_with(op) self.assertIsNotNone(op.socket) op.socket.close() finally: accept.sock.close() self.assertTrue(self.test_op.is_done()) def test_gaierror(self): """ Test a connection that fails on socket.connect() because of a name resolution error. """ # See RFC 6761 for the .test TLD -- unless the DNS server is doing # something weird, this name should not resolve. op = TCPConnectOperation(self.test_op, ("example.test", 666)) op.callback = op_callback() op.start() op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.is_done()) def test_connection_refused(self): """ Test a connection that fails because the server refuses the connection. """ self.sock.shutdown(socket.SHUT_RD) self.shutdown = False op = TCPConnectOperation(self.test_op, self.sock.getsockname()) op.callback = op_callback() op.start() self.test_op.run_selector() op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with("Connection refused")) self.assertTrue(self.test_op.is_done()) @tests.timed_test @tests.root_test def test_timeout(self): """ Test a connection that times out and fails. """ with drop_connection(port=self.sock.getsockname()[1]): op = TCPConnectOperation(self.test_op, self.sock.getsockname(), 0.01) op.callback = op_callback() op.start() time.sleep(0.01) self.test_op.run_selector() op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with("Timed out")) self.assertTrue(self.test_op.is_done()) @tests.timed_test def test_connect_and_timeout(self): """ Test the case where the connection succeeds but the timerfd still fires before we call select. This is the case where the socket comes before the timerfd in the list of events, so the connection should succeed. """ op = TCPConnectOperation(self.test_op, self.sock.getsockname(), 0.01) op.callback = op_callback() op.start() accept = self.accept() time.sleep(0.01) while not op.callback.called: self.test_op.run_selector(priority="socket") try: op.callback.assert_called_once_with(op) self.assertIsNotNone(op.socket) op.socket.close() self.assertTrue(self.test_op.is_done()) finally: accept.sock.close() @tests.timed_test def test_timeout_and_connect(self): """ Test the same case as test_connect_and_timeout but where the timerfd event comes before the socket event. """ op = TCPConnectOperation(self.test_op, self.sock.getsockname(), 0.01) op.callback = op_callback() op.start() accept = self.accept() time.sleep(0.01) while not op.callback.called: self.test_op.run_selector(priority="timer") try: op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with("Timed out")) self.assertTrue(self.test_op.is_done()) finally: accept.sock.close()
class TestSSLHandshakeOperation(unittest.TestCase): def setUp(self): fd, self.keyfile = tempfile.mkstemp(prefix='test_ca_', suffix='.key') subprocess.check_call(['openssl', 'genpkey', '-algorithm', 'RSA', '-out', self.keyfile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.close(fd) fd, self.certfile = tempfile.mkstemp(prefix='test_ca_', suffix='.pem') subprocess.check_call(['openssl', 'req', '-x509', '-new', '-subj', '/CN=localhost', '-key', self.keyfile, '-out', self.certfile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.close(fd) fd, self.keyfile2 = tempfile.mkstemp(prefix='test_ca_', suffix='.key') subprocess.check_call(['openssl', 'genpkey', '-algorithm', 'RSA', '-out', self.keyfile2], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.close(fd) fd, self.certfile2 = tempfile.mkstemp(prefix='test_ca_', suffix='.pem') subprocess.check_call(['openssl', 'req', '-x509', '-new', '-subj', '/CN=localhost', '-key', self.keyfile2, '-out', self.certfile2], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.close(fd) self.test_op = TestOperation() self.sock = None self.test_op.start() def tearDown(self): if self.sock: if self.shutdown: self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() os.unlink(self.keyfile) os.unlink(self.certfile) os.unlink(self.keyfile2) os.unlink(self.certfile2) def start_server(self, wrap_ssl=True): self.sock = socket.socket() if wrap_ssl: context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile) self.sock = context.wrap_socket(self.sock, server_side=True) self.sock.bind(('localhost', 0)) self.sock.listen(0) self.shutdown = True def accept(self, send=None): class Accept: def __init__(self): self.thread = None self.sock = None self.addr = None accept = Accept() def aux(accept): try: accept.sock, accept.addr = self.sock.accept() if send: accept.sock.sendall(send) except ssl.SSLError: pass accept.thread = threading.Thread(target=aux, args=(accept,)) accept.thread.start() return accept def connect_client(self): sock = socket.socket() sock.connect(self.sock.getsockname()) sock.setblocking(False) return sock def test_success(self): """ Test a successful handshake. """ self.start_server() sock = self.connect_client() op = SSLHandshakeOperation(self.test_op, sock, 'localhost', self.certfile) op.callback = op_callback() op.start() accept = self.accept() while not op.callback.called: self.test_op.run_selector() accept.thread.join() try: op.callback.assert_called_once_with(op) self.assertIsNotNone(op.socket) op.socket.close() self.assertTrue(self.test_op.is_done()) finally: accept.sock.close() def test_certificate_verify(self): """ Test a handshake that fails because the certificate is not trusted. """ self.start_server() sock = self.connect_client() # Notice that we're using ca2.pem as the CA file here. op = SSLHandshakeOperation(self.test_op, sock, 'localhost', self.certfile2) op.callback = op_callback() op.start() accept = self.accept() while not op.callback.called: self.test_op.run_selector() accept.thread.join() try: op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with('CERTIFICATE_VERIFY_FAILED')) self.assertTrue(self.test_op.is_done()) finally: if accept.sock: accept.sock.close() def test_hostname_verify(self): """ Test a handshake that fails because the server hostname does not match. """ self.start_server() sock = self.connect_client() # We're passing 'localghost' instead of 'localhost' as the server # hostname. op = SSLHandshakeOperation(self.test_op, sock, 'localghost', self.certfile) op.callback = op_callback() op.start() accept = self.accept() while not op.callback.called: self.test_op.run_selector() accept.thread.join() try: op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with('hostname')) self.assertTrue(self.test_op.is_done()) finally: if accept.sock: accept.sock.close() def test_missing_cafile(self): """ Test a handshake that fails because the CA file doesn't exist. """ self.start_server() sock = self.connect_client() op = SSLHandshakeOperation(self.test_op, sock, 'localhost', tempfile.mktemp()) op.callback = op_callback() op.start() op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with('No such file')) self.assertTrue(self.test_op.is_done()) def test_invalid_handshake(self): """ Test a handshake that fails because the server does something invalid (e.g., the server is not actually using SSL). """ self.start_server(wrap_ssl=False) sock = self.connect_client() op = SSLHandshakeOperation(self.test_op, sock, 'localhost', self.certfile) op.callback = op_callback() op.start() accept = self.accept(send=b'* OK Hello\r\n') while not op.callback.called: self.test_op.run_selector() accept.thread.join() try: op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.is_done()) finally: if accept.sock: accept.sock.close() @tests.timed_test @tests.root_test def test_timeout(self): """ Test a handshake that fails because it times out. """ self.start_server() sock = self.connect_client() with drop_connection(port=self.sock.getsockname()[1]): op = SSLHandshakeOperation(self.test_op, sock, 'localhost', self.certfile, 0.01) op.callback = op_callback() op.start() time.sleep(0.01) self.test_op.run_selector() op.callback.assert_called_once_with(op) self.assertIsNone(op.socket) self.assertTrue(self.test_op.updated_with('Timed out')) self.assertTrue(self.test_op.is_done())