import unittest
import web
from web.wsgiserver import CherryPyWSGIServer
import threading
from clientjob.secure_connection import SecureConnection
from web.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
import string
import random
import os
import sys
import time
import config
from ssl import SSLError

class EchoView:
    '''
    This is a view for the test server which prints the argument given to it
    '''
    def GET(self, arg):
        return arg

class TestSecureConnection(unittest.TestCase):
    def setUp(self):
        self.ssl_certificate = os.path.join('ssl', 'localhost.pem')
        self.ssl_private_key = os.path.join('ssl', 'localhost.key')
        self.client_CA = os.path.join('ssl', 'ca-cert.pem')

    def launch_server(self, port):
        '''
        Launches a test server which always prints the args in the url requested
        '''
        CherryPyWSGIServer.ssl_adapter = pyOpenSSLAdapter(self.ssl_certificate,
            self.ssl_private_key, None, self.client_CA)

        urls = ("/(.*)", "EchoView")
        # the following is the only way to tell web.py which port to start the server..
        app = web.application(urls, globals()).wsgifunc()
        server = CherryPyWSGIServer(('127.0.0.1', port), app, server_name='localhost')
        thread = threading.Thread(target=server.start)
        thread.start()
        return server

    def test_secure_connection(self):
        '''
        Request a random url from the test serverand checks the response,
        using the secure connection with a valid certificate signed by the CA.
        '''
        port=8081
        server = self.launch_server(port)
        while not server.ready:
            time.sleep(1) # let the server start in the thread
        connection = SecureConnection('127.0.0.1', port)
        random_string = ''.join(random.choice(string.letters) for i in xrange(40))
        connection.request('GET', '/' + random_string)
        response = connection.getresponse()
        self.assertEqual(response.status, 200)
        self.assertEqual(response.read(), random_string)
        server.stop()

    def test_insecure_client(self):
        '''
        Similar to testSecureConnection, but this time the client has an invalid
        (self-signed) certificate and thus the server should reject the connection.
        '''
        port = 8082
        server = self.launch_server(port)
        while not server.ready:
            time.sleep(1) # let the server start in the thread

        # use an invalid (self-signed) certificate in the client
        connection = SecureConnection("127.0.0.1", port,
            cert_file = os.path.join('ssl', 'self-signed-localhost.cert'))

        random_string = ''.join(random.choice(string.letters) for i in xrange(40))

        # As it uses a self-signed certificate, it should raise an SSLError exception,
        # otherwise the test should fail
        try:
            connection.request("GET", "/" + random_string)
            server.stop()
            raise Exception
        except SSLError:
            # Here we could check the excapt SSLError, problem is we could only verify
            # the string it contains and it might vary from version to version, so we
            # just verify that an SSLError exception is raised
            server.stop()

    def test_insecure_server(self):
        '''
        Similar to testInsecureConnection, but now it's the server the one using an
        invalid (self-signed) certificate, and thus the client should reject the connection.
        '''
        port = 8083

        # use an invalid (self-signed) certificate in the server
        self.ssl_certificate = os.path.join('ssl', 'self-signed-localhost.cert')

        server = self.launch_server(port)
        while not server.ready:
            time.sleep(1) # let the server start in the thread
        connection = SecureConnection("127.0.0.1", port)
        random_string = ''.join(random.choice(string.letters) for i in xrange(40))
        try:
            connection.request("GET", "/" + random_string)
            server.stop()
            raise Exception
        except SSLError:
            # See same case in testInsecureClient
            server.stop()

def suite():
   suite = unittest.TestSuite()
   suite.addTest(unittest.makeSuite(TestSecureConnection))
   return suite
