/*
 * Copyright (c) 2012 ICM Uniwersytet Warszawski All rights reserved.
 * See LICENCE.txt file for licensing information.
 */
package eu.emi.security.authn.x509.ocsp;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;

import static org.junit.Assert.*;

import org.apache.commons.io.FileUtils;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPReq;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cert.ocsp.SingleResp;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import eu.emi.security.authn.x509.RiskyIntegrationTests;
import eu.emi.security.authn.x509.X509Credential;
import eu.emi.security.authn.x509.helpers.ocsp.OCSPCachingClient;
import eu.emi.security.authn.x509.helpers.ocsp.OCSPClientImpl;
import eu.emi.security.authn.x509.helpers.ocsp.OCSPResponseStructure;
import eu.emi.security.authn.x509.helpers.ocsp.OCSPResult;
import eu.emi.security.authn.x509.impl.CertificateUtils;
import eu.emi.security.authn.x509.impl.CertificateUtils.Encoding;

public class CacheTest
{
	private static class MockOCSPClient extends OCSPClientImpl
	{
		public int fullQuery = 0;
		public int lowlevelQuery = 0;
		public int verifications = 0;
		
		@Override
		public OCSPResult queryForCertificate(URL responder, X509Certificate toCheckCert,
				X509Certificate issuerCert, X509Credential requester,
				boolean addNonce, int timeout) throws IOException, OCSPException
		{
			fullQuery++;
			return super.queryForCertificate(responder, toCheckCert, issuerCert, requester, addNonce, timeout);
		}

		@Override
		public OCSPReq createRequest(X509Certificate toCheckCert,
				X509Certificate issuerCert, X509Credential requester,
				boolean addNonce) throws OCSPException
		{
			return super.createRequest(toCheckCert, issuerCert, requester, addNonce);
		}

		@Override
		public OCSPResponseStructure send(URL responder, OCSPReq requestO, int timeout)
				throws IOException
		{
			lowlevelQuery++;
			return super.send(responder, requestO, timeout);
		}

		@Override
		public SingleResp verifyResponse(OCSPResp response, X509Certificate toCheckCert,
				X509Certificate issuerCert, byte[] checkNonce) throws OCSPException
		{
			verifications++;
			return super.verifyResponse(response, toCheckCert, issuerCert, checkNonce);
		}
	}
	
	@Test
	@Category(RiskyIntegrationTests.class)
	public void test() throws Exception
	{
		MockOCSPClient client = new MockOCSPClient();
		URL responder = new URL("http://ocsp.usertrust.com");
		
		FileInputStream fis = new FileInputStream("src/test/resources/ocsp/terena-ssl.pem");
		X509Certificate toCheck = CertificateUtils.loadCertificate(fis,	Encoding.PEM);
		fis = new FileInputStream("src/test/resources/ocsp/usertrust-ca.pem");
		X509Certificate issuerCert = CertificateUtils.loadCertificate(fis, Encoding.PEM);
		
		OCSPResult result;
		
		OCSPCachingClient notCaching = new OCSPCachingClient(-1, null, null);
		result = notCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(1, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(1, client.verifications);
		
		result = notCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(2, client.fullQuery);
		assertEquals(2, client.lowlevelQuery);
		assertEquals(2, client.verifications);
		
		
		OCSPCachingClient memCaching = new OCSPCachingClient(1000, null, null);
		client = new MockOCSPClient();

		result = memCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(1, client.verifications);
		
		result = memCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(1, client.verifications);

		Thread.sleep(1100);
		
		result = memCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(2, client.lowlevelQuery);
		assertEquals(2, client.verifications);
		
		File dir = new File("target/ocsp_cache");
		FileUtils.deleteDirectory(dir);
		dir.mkdirs();
		OCSPCachingClient diskCaching = new OCSPCachingClient(1000, dir, "ocspcache_");
		client = new MockOCSPClient();
		

		result = diskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(1, client.verifications);
		
		result = diskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(1, client.verifications);
		
		diskCaching.clearMemoryCache();
		
		result = diskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(2, client.verifications);
		
		diskCaching.clearMemoryCache();
		Thread.sleep(1100);
		
		result = diskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
		assertEquals(OCSPResult.Status.good, result.getStatus());
		assertEquals(0, client.fullQuery);
		assertEquals(2, client.lowlevelQuery);
		assertEquals(4, client.verifications);
	}
	
	@Test
	public void testErrorCaching() throws Exception
	{
		MockOCSPClient client = new MockOCSPClient();
		URL responder = new URL("http://127.100.100.100");
		
		FileInputStream fis = new FileInputStream("src/test/resources/ocsp/terena-ssl.pem");
		X509Certificate toCheck = CertificateUtils.loadCertificate(fis,	Encoding.PEM);
		fis = new FileInputStream("src/test/resources/ocsp/usertrust-ca.pem");
		X509Certificate issuerCert = CertificateUtils.loadCertificate(fis, Encoding.PEM);
		
		File diskCache = new File("target/ocspCache");
		diskCache.mkdirs();
		OCSPCachingClient memAndDiskCaching = new OCSPCachingClient(1000, diskCache, "cache_");
		client = new MockOCSPClient();

		try
		{
			memAndDiskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
			fail("Should get exception");
		} catch (UnknownHostException e)
		{
			//ok
		} catch (ConnectException e)
		{
			//also ok
		}
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(0, client.verifications);
		
		try
		{
			memAndDiskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
			fail("Should get exception");
		} catch (UnknownHostException e)
		{
			//ok
		} catch (ConnectException e)
		{
			//also ok
		}
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(0, client.verifications);
		
		memAndDiskCaching.clearMemoryCache();
		try
		{
			memAndDiskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
			fail("Should get exception");
		} catch (UnknownHostException e)
		{
			//ok
		} catch (ConnectException e)
		{
			//also ok
		}
		assertEquals(0, client.fullQuery);
		assertEquals(1, client.lowlevelQuery);
		assertEquals(0, client.verifications);
		
		
		Thread.sleep(1000);
		try
		{
			memAndDiskCaching.queryForCertificate(responder, 
				toCheck, issuerCert, null, false, 5000, client);
			fail("Should get exception");
		} catch (UnknownHostException e)
		{
			//ok
		} catch (ConnectException e)
		{
			//also ok
		}
		assertEquals(0, client.fullQuery);
		assertEquals(2, client.lowlevelQuery);
		assertEquals(0, client.verifications);
		
		FileUtils.deleteDirectory(diskCache);
	}
}













