Commit a0613aae authored by justcoding121's avatar justcoding121

restore certificate create task cache for burst requests

parent 4966b6ff
...@@ -11,11 +11,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -11,11 +11,6 @@ namespace Titanium.Web.Proxy.Network
{ {
internal X509Certificate2 Certificate { get; set; } internal X509Certificate2 Certificate { get; set; }
/// <summary>
/// Certificate creation task.
/// </summary>
internal Task<X509Certificate2> CreationTask { get; set; }
/// <summary> /// <summary>
/// Last time this certificate was used. /// Last time this certificate was used.
/// Useful in determining its cache lifetime. /// Useful in determining its cache lifetime.
......
...@@ -44,11 +44,21 @@ namespace Titanium.Web.Proxy.Network ...@@ -44,11 +44,21 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// Cache dictionary /// Cache dictionary
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, CachedCertificate> cachedCertificates; private readonly ConcurrentDictionary<string, CachedCertificate> cachedCertificates
= new ConcurrentDictionary<string, CachedCertificate>();
private readonly CancellationTokenSource clearCertificatesTokenSource; /// <summary>
/// A list of pending certificate creation tasks.
/// Usefull to prevent multiple threads working on same certificate generation
/// when burst certificate generation requests happen for same certificate.
/// </summary>
private readonly ConcurrentDictionary<string, Task<X509Certificate2>> pendingCertificateCreationTasks
= new ConcurrentDictionary<string, Task<X509Certificate2>>();
private readonly CancellationTokenSource clearCertificatesTokenSource
= new CancellationTokenSource();
private readonly object rootCertCreationLock; private readonly object rootCertCreationLock = new object();
private ICertificateMaker certEngine; private ICertificateMaker certEngine;
...@@ -60,7 +70,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -60,7 +70,7 @@ namespace Titanium.Web.Proxy.Network
private string rootCertificateName; private string rootCertificateName;
private ICertificateCache certificateCache; private ICertificateCache certificateCache = new DefaultCertificateDiskCache();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CertificateManager"/> class. /// Initializes a new instance of the <see cref="CertificateManager"/> class.
...@@ -99,14 +109,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -99,14 +109,6 @@ namespace Titanium.Web.Proxy.Network
} }
CertificateEngine = CertificateEngine.BouncyCastle; CertificateEngine = CertificateEngine.BouncyCastle;
cachedCertificates = new ConcurrentDictionary<string, CachedCertificate>();
clearCertificatesTokenSource = new CancellationTokenSource();
certificateCache = new DefaultCertificateDiskCache();
rootCertCreationLock = new object();
} }
/// <summary> /// <summary>
...@@ -225,8 +227,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -225,8 +227,9 @@ namespace Titanium.Web.Proxy.Network
public bool SaveFakeCertificates { get; set; } = false; public bool SaveFakeCertificates { get; set; } = false;
/// <summary> /// <summary>
/// The service to save fake certificates. /// The fake certificate cache storage.
/// The default storage saves certificates in folder "crts" (will be created in proxy dll directory). /// The default cache storage implementation saves certificates in folder "crts" (will be created in proxy dll directory).
/// Implement ICertificateCache interface and assign concrete class here to customize.
/// </summary> /// </summary>
public ICertificateCache CertificateStorage public ICertificateCache CertificateStorage
{ {
...@@ -436,41 +439,40 @@ namespace Titanium.Web.Proxy.Network ...@@ -436,41 +439,40 @@ namespace Titanium.Web.Proxy.Network
internal async Task<X509Certificate2> CreateCertificateAsync(string certificateName) internal async Task<X509Certificate2> CreateCertificateAsync(string certificateName)
{ {
// check in cache first // check in cache first
var item = cachedCertificates.GetOrAdd(certificateName, _ => if (cachedCertificates.TryGetValue(certificateName, out var cached))
{ {
var cached = new CachedCertificate(); cached.LastAccess = DateTime.Now;
cached.CreationTask = Task.Run(() => return cached.Certificate;
{ }
var certificate = CreateCertificate(certificateName, false);
// see http://www.albahari.com/threading/part4.aspx for the explanation
// why Thread.MemoryBarrier is used here and below
cached.Certificate = certificate;
Thread.MemoryBarrier();
cached.CreationTask = null;
Thread.MemoryBarrier();
return certificate;
});
return cached;
});
item.LastAccess = DateTime.Now;
if (item.Certificate != null) // handle burst requests with same certificate name
// by checking for existing task for same certificate name
if (pendingCertificateCreationTasks.TryGetValue(certificateName, out var task))
{ {
return item.Certificate; return await task;
} }
// handle burst requests with same certificate name // run certificate creation task & add it to pending tasks
// by checking for existing task task = Task.Run(() =>
Thread.MemoryBarrier(); {
var task = item.CreationTask; var result = CreateCertificate(certificateName, false);
if (result != null)
{
cachedCertificates.TryAdd(certificateName, new CachedCertificate
{
Certificate = result
});
}
Thread.MemoryBarrier(); return result;
});
pendingCertificateCreationTasks.TryAdd(certificateName, task);
// return result // cleanup pending tasks & return result
return item.Certificate ?? await task; var certificate = await task;
pendingCertificateCreationTasks.TryRemove(certificateName, out task);
return certificate;
} }
/// <summary> /// <summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment