Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit e4dfa44

Browse files
committed
issue #13 Fix
Mark items internal; keep only MRU certificates in cache
1 parent dfc6d9a commit e4dfa44

19 files changed

+164
-104
lines changed

Titanium.Web.Proxy/Decompression/DecompressionFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
namespace Titanium.Web.Proxy.Decompression
22
{
3-
class DecompressionFactory
3+
internal class DecompressionFactory
44
{
5-
public IDecompression Create(string type)
5+
internal IDecompression Create(string type)
66
{
77
switch(type)
88
{

Titanium.Web.Proxy/Decompression/DefaultDecompression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Titanium.Web.Proxy.Decompression
44
{
5-
class DefaultDecompression : IDecompression
5+
internal class DefaultDecompression : IDecompression
66
{
77
public Task<byte[]> Decompress(byte[] compressedArray)
88
{

Titanium.Web.Proxy/Decompression/DeflateDecompression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Titanium.Web.Proxy.Decompression
77
{
8-
class DeflateDecompression : IDecompression
8+
internal class DeflateDecompression : IDecompression
99
{
1010
public async Task<byte[]> Decompress(byte[] compressedArray)
1111
{

Titanium.Web.Proxy/Decompression/GZipDecompression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Titanium.Web.Proxy.Decompression
77
{
8-
class GZipDecompression : IDecompression
8+
internal class GZipDecompression : IDecompression
99
{
1010
public async Task<byte[]> Decompress(byte[] compressedArray)
1111
{

Titanium.Web.Proxy/Decompression/IDecompression.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
namespace Titanium.Web.Proxy.Decompression
55
{
6-
interface IDecompression
6+
internal interface IDecompression
77
{
8-
Task<byte[]> Decompress(byte[] compressedArray);
8+
Task<byte[]> Decompress(byte[] compressedArray);
99
}
1010
}

Titanium.Web.Proxy/Decompression/ZlibDecompression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Titanium.Web.Proxy.Decompression
77
{
8-
class ZlibDecompression : IDecompression
8+
internal class ZlibDecompression : IDecompression
99
{
1010
public async Task<byte[]> Decompress(byte[] compressedArray)
1111
{

Titanium.Web.Proxy/Extensions/HttpWebRequestExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ namespace Titanium.Web.Proxy.Extensions
77
/// <summary>
88
/// Extensions on HttpWebSession object
99
/// </summary>
10-
public static class HttpWebRequestExtensions
10+
internal static class HttpWebRequestExtensions
1111
{
1212
//Get encoding of the HTTP request
13-
public static Encoding GetEncoding(this Request request)
13+
internal static Encoding GetEncoding(this Request request)
1414
{
1515
try
1616
{

Titanium.Web.Proxy/Extensions/HttpWebResponseExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Titanium.Web.Proxy.Extensions
66
{
7-
public static class HttpWebResponseExtensions
7+
internal static class HttpWebResponseExtensions
88
{
9-
public static Encoding GetResponseCharacterEncoding(this Response response)
9+
internal static Encoding GetResponseCharacterEncoding(this Response response)
1010
{
1111
try
1212
{

Titanium.Web.Proxy/Extensions/StreamExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
namespace Titanium.Web.Proxy.Extensions
1010
{
11-
public static class StreamHelper
11+
internal static class StreamHelper
1212
{
13-
public static async Task CopyToAsync(this Stream input, string initialData, Stream output)
13+
internal static async Task CopyToAsync(this Stream input, string initialData, Stream output)
1414
{
1515
if (!string.IsNullOrEmpty(initialData))
1616
{

Titanium.Web.Proxy/Extensions/TcpExtensions.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using System.Net.Sockets;
22

3-
43
namespace Titanium.Web.Proxy.Extensions
54
{
6-
75
internal static class TcpExtensions
86
{
9-
public static bool IsConnected(this Socket client)
7+
internal static bool IsConnected(this Socket client)
108
{
119
// This is how you can determine whether a socket is still connected.
1210
bool blockingState = client.Blocking;

Titanium.Web.Proxy/Helpers/CertificateManager.cs

Lines changed: 103 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,55 +6,68 @@
66
using System.Reflection;
77
using System.Threading.Tasks;
88
using System.Threading;
9+
using System.Linq;
910

1011
namespace Titanium.Web.Proxy.Helpers
1112
{
12-
public class CertificateManager : IDisposable
13+
internal class CachedCertificate
14+
{
15+
internal X509Certificate2 Certificate { get; set; }
16+
17+
internal DateTime LastAccess { get; set; }
18+
19+
internal CachedCertificate()
20+
{
21+
LastAccess = DateTime.Now;
22+
}
23+
24+
}
25+
internal class CertificateManager : IDisposable
1326
{
1427
private const string CertCreateFormat =
1528
"-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}";
1629

17-
private readonly IDictionary<string, X509Certificate2> _certificateCache;
30+
private readonly IDictionary<string, CachedCertificate> certificateCache;
1831
private static SemaphoreSlim semaphoreLock = new SemaphoreSlim(1);
1932

20-
public string Issuer { get; private set; }
21-
public string RootCertificateName { get; private set; }
33+
internal string Issuer { get; private set; }
34+
internal string RootCertificateName { get; private set; }
2235

23-
public X509Store MyStore { get; private set; }
24-
public X509Store RootStore { get; private set; }
36+
internal X509Store MyStore { get; private set; }
37+
internal X509Store RootStore { get; private set; }
2538

26-
public CertificateManager(string issuer, string rootCertificateName)
39+
internal CertificateManager(string issuer, string rootCertificateName)
2740
{
2841
Issuer = issuer;
2942
RootCertificateName = rootCertificateName;
3043

3144
MyStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
3245
RootStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
3346

34-
_certificateCache = new Dictionary<string, X509Certificate2>();
47+
certificateCache = new Dictionary<string, CachedCertificate>();
3548
}
3649

3750
/// <summary>
3851
/// Attempts to move a self-signed certificate to the root store.
3952
/// </summary>
4053
/// <returns>true if succeeded, else false</returns>
41-
public async Task<bool> CreateTrustedRootCertificate()
54+
internal async Task<bool> CreateTrustedRootCertificate()
4255
{
4356
X509Certificate2 rootCertificate =
44-
await CreateCertificate(RootStore, RootCertificateName);
57+
await CreateCertificate(RootStore, RootCertificateName, true);
4558

4659
return rootCertificate != null;
4760
}
4861
/// <summary>
4962
/// Attempts to remove the self-signed certificate from the root store.
5063
/// </summary>
5164
/// <returns>true if succeeded, else false</returns>
52-
public async Task<bool> DestroyTrustedRootCertificate()
65+
internal bool DestroyTrustedRootCertificate()
5366
{
54-
return await DestroyCertificate(RootStore, RootCertificateName);
67+
return DestroyCertificate(RootStore, RootCertificateName, false);
5568
}
5669

57-
public X509Certificate2Collection FindCertificates(string certificateSubject)
70+
internal X509Certificate2Collection FindCertificates(string certificateSubject)
5871
{
5972
return FindCertificates(MyStore, certificateSubject);
6073
}
@@ -67,29 +80,36 @@ protected virtual X509Certificate2Collection FindCertificates(X509Store store, s
6780
discoveredCertificates : null;
6881
}
6982

70-
public async Task<X509Certificate2> CreateCertificate(string certificateName)
83+
internal async Task<X509Certificate2> CreateCertificate(string certificateName, bool isRootCertificate)
7184
{
72-
return await CreateCertificate(MyStore, certificateName);
85+
return await CreateCertificate(MyStore, certificateName, isRootCertificate);
7386
}
74-
protected async virtual Task<X509Certificate2> CreateCertificate(X509Store store, string certificateName)
75-
{
76-
77-
if (_certificateCache.ContainsKey(certificateName))
78-
return _certificateCache[certificateName];
7987

88+
protected async virtual Task<X509Certificate2> CreateCertificate(X509Store store, string certificateName, bool isRootCertificate)
89+
{
8090
await semaphoreLock.WaitAsync();
8191

82-
X509Certificate2 certificate = null;
8392
try
8493
{
94+
if (certificateCache.ContainsKey(certificateName))
95+
{
96+
var cached = certificateCache[certificateName];
97+
cached.LastAccess = DateTime.Now;
98+
return cached.Certificate;
99+
}
100+
X509Certificate2 certificate = null;
85101
store.Open(OpenFlags.ReadWrite);
86102
string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
87103

88-
var certificates =
89-
FindCertificates(store, certificateSubject);
104+
X509Certificate2Collection certificates;
105+
106+
if (isRootCertificate)
107+
{
108+
certificates = FindCertificates(store, certificateSubject);
90109

91-
if (certificates != null)
92-
certificate = certificates[0];
110+
if (certificates != null)
111+
certificate = certificates[0];
112+
}
93113

94114
if (certificate == null)
95115
{
@@ -99,22 +119,26 @@ protected async virtual Task<X509Certificate2> CreateCertificate(X509Store store
99119
await CreateCertificate(args);
100120
certificates = FindCertificates(store, certificateSubject);
101121

102-
return certificates != null ?
103-
certificates[0] : null;
122+
//remove it from store
123+
if (!isRootCertificate)
124+
DestroyCertificate(certificateName);
125+
126+
if (certificates != null)
127+
certificate = certificates[0];
104128
}
105129

106130
store.Close();
107-
if (certificate != null && !_certificateCache.ContainsKey(certificateName))
108-
_certificateCache.Add(certificateName, certificate);
131+
if (certificate != null && !certificateCache.ContainsKey(certificateName))
132+
certificateCache.Add(certificateName, new CachedCertificate() { Certificate = certificate });
109133

110134
return certificate;
111135
}
112136
finally
113137
{
114-
semaphoreLock.Release();
138+
semaphoreLock.Release();
115139
}
116-
117140
}
141+
118142
protected virtual Task<int> CreateCertificate(string[] args)
119143
{
120144

@@ -153,40 +177,32 @@ protected virtual Task<int> CreateCertificate(string[] args)
153177

154178
}
155179

156-
public async Task<bool> DestroyCertificate(string certificateName)
180+
internal bool DestroyCertificate(string certificateName)
157181
{
158-
return await DestroyCertificate(MyStore, certificateName);
182+
return DestroyCertificate(MyStore, certificateName, false);
159183
}
160-
protected virtual async Task<bool> DestroyCertificate(X509Store store, string certificateName)
161-
{
162-
await semaphoreLock.WaitAsync();
163184

185+
protected virtual bool DestroyCertificate(X509Store store, string certificateName, bool removeFromCache)
186+
{
164187
X509Certificate2Collection certificates = null;
165-
try
166-
{
167-
store.Open(OpenFlags.ReadWrite);
168-
string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
169188

189+
store.Open(OpenFlags.ReadWrite);
190+
string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
191+
192+
certificates = FindCertificates(store, certificateSubject);
193+
if (certificates != null)
194+
{
195+
store.RemoveRange(certificates);
170196
certificates = FindCertificates(store, certificateSubject);
171-
if (certificates != null)
172-
{
173-
store.RemoveRange(certificates);
174-
certificates = FindCertificates(store, certificateSubject);
175-
}
176-
177-
store.Close();
178-
if (certificates == null &&
179-
_certificateCache.ContainsKey(certificateName))
180-
{
181-
_certificateCache.Remove(certificateName);
182-
}
183-
return certificates == null;
184197
}
185-
finally
198+
199+
store.Close();
200+
if (removeFromCache &&
201+
certificateCache.ContainsKey(certificateName))
186202
{
187-
semaphoreLock.Release();
203+
certificateCache.Remove(certificateName);
188204
}
189-
205+
return certificates == null;
190206
}
191207

192208
protected virtual string GetCertificateCreateArgs(X509Store store, string certificateName)
@@ -203,6 +219,37 @@ protected virtual string GetCertificateCreateArgs(X509Store store, string certif
203219
return certCreatArgs;
204220
}
205221

222+
private static bool clearCertificates { get; set; }
223+
224+
internal void StopClearIdleCertificates()
225+
{
226+
clearCertificates = false;
227+
}
228+
229+
internal async void ClearIdleCertificates()
230+
{
231+
clearCertificates = true;
232+
while (clearCertificates)
233+
{
234+
await semaphoreLock.WaitAsync();
235+
236+
try
237+
{
238+
var cutOff = DateTime.Now.AddSeconds(-60);
239+
240+
var outdated = certificateCache
241+
.Where(x => x.Value.LastAccess < cutOff)
242+
.ToList();
243+
244+
foreach (var cache in outdated)
245+
certificateCache.Remove(cache.Key);
246+
}
247+
finally { semaphoreLock.Release(); }
248+
249+
await Task.Delay(1000 * 60 * 3).ConfigureAwait(false);
250+
}
251+
}
252+
206253
public void Dispose()
207254
{
208255
if (MyStore != null)

0 commit comments

Comments
 (0)