Commit c3e40786 authored by justcoding121's avatar justcoding121

fix performance issues

parent ed3ce01c
using System;
using System.IO;
namespace Titanium.Web.Proxy.Models
{
internal class ConnectRequest
{
internal Stream Stream { get; set; }
internal Uri Uri { get; set; }
}
}
...@@ -14,10 +14,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -14,10 +14,6 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal class CustomSslStream : SslStream internal class CustomSslStream : SslStream
{ {
/// <summary>
/// Holds the current session
/// </summary>
internal object Param { get; set; }
internal CustomSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback clientCertificateSelectionCallback) internal CustomSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback clientCertificateSelectionCallback)
:base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, clientCertificateSelectionCallback) :base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, clientCertificateSelectionCallback)
......
...@@ -40,11 +40,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -40,11 +40,8 @@ namespace Titanium.Web.Proxy.Network
static Dictionary<string, List<TcpConnection>> connectionCache = new Dictionary<string, List<TcpConnection>>(); static Dictionary<string, List<TcpConnection>> connectionCache = new Dictionary<string, List<TcpConnection>>();
static SemaphoreSlim connectionAccessLock = new SemaphoreSlim(1); static SemaphoreSlim connectionAccessLock = new SemaphoreSlim(1);
internal static async Task<TcpConnection> GetClient(string hostname, int port, bool isHttps, Version version) internal static async Task<TcpConnection> GetClient(string hostname, int port, bool isHttps, Version version)
{
return await GetClient(null, hostname, port, isHttps, version);
}
internal static async Task<TcpConnection> GetClient(ConnectRequest connectRequest, string hostname, int port, bool isHttps, Version version)
{ {
List<TcpConnection> cachedConnections = null; List<TcpConnection> cachedConnections = null;
TcpConnection cached = null; TcpConnection cached = null;
...@@ -71,14 +68,24 @@ namespace Titanium.Web.Proxy.Network ...@@ -71,14 +68,24 @@ namespace Titanium.Web.Proxy.Network
finally { connectionAccessLock.Release(); } finally { connectionAccessLock.Release(); }
if (cached != null && !cached.TcpClient.Client.IsConnected()) if (cached != null && !cached.TcpClient.Client.IsConnected())
{
cached.TcpClient.Client.Dispose();
cached.TcpClient.Close();
continue; continue;
}
if (cached == null) if (cached == null)
break; break;
} }
if (cached == null) if (cached == null)
cached = await CreateClient(connectRequest, hostname, port, isHttps, version).ConfigureAwait(false); cached = await CreateClient(hostname, port, isHttps, version).ConfigureAwait(false);
if (cachedConnections == null || cachedConnections.Count() <= 2)
{
await CreateClient(hostname, port, isHttps, version)
.ContinueWith(async (x) => { if (x.Status == TaskStatus.RanToCompletion) await ReleaseClient(x.Result); });
}
return cached; return cached;
} }
...@@ -88,7 +95,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -88,7 +95,7 @@ namespace Titanium.Web.Proxy.Network
return string.Format("{0}:{1}:{2}:{3}:{4}", hostname.ToLower(), port, isHttps, version.Major, version.Minor); return string.Format("{0}:{1}:{2}:{3}:{4}", hostname.ToLower(), port, isHttps, version.Major, version.Minor);
} }
private static async Task<TcpConnection> CreateClient(ConnectRequest connectRequest, string hostname, int port, bool isHttps, Version version) private static async Task<TcpConnection> CreateClient(string hostname, int port, bool isHttps, Version version)
{ {
TcpClient client; TcpClient client;
Stream stream; Stream stream;
...@@ -132,7 +139,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -132,7 +139,6 @@ namespace Titanium.Web.Proxy.Network
{ {
sslStream = new CustomSslStream(stream, true, new RemoteCertificateValidationCallback(ProxyServer.ValidateServerCertificate), sslStream = new CustomSslStream(stream, true, new RemoteCertificateValidationCallback(ProxyServer.ValidateServerCertificate),
new LocalCertificateSelectionCallback(ProxyServer.SelectClientCertificate)); new LocalCertificateSelectionCallback(ProxyServer.SelectClientCertificate));
sslStream.Param = connectRequest;
await sslStream.AuthenticateAsClientAsync(hostname, null, Constants.SupportedProtocols, false).ConfigureAwait(false); await sslStream.AuthenticateAsClientAsync(hostname, null, Constants.SupportedProtocols, false).ConfigureAwait(false);
stream = (Stream)sslStream; stream = (Stream)sslStream;
} }
...@@ -172,6 +178,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -172,6 +178,7 @@ namespace Titanium.Web.Proxy.Network
internal static async Task ReleaseClient(TcpConnection connection) internal static async Task ReleaseClient(TcpConnection connection)
{ {
connection.LastAccess = DateTime.Now; connection.LastAccess = DateTime.Now;
var key = GetConnectionKey(connection.HostName, connection.port, connection.IsHttps, connection.Version); var key = GetConnectionKey(connection.HostName, connection.port, connection.IsHttps, connection.Version);
await connectionAccessLock.WaitAsync(); await connectionAccessLock.WaitAsync();
......
...@@ -72,29 +72,23 @@ namespace Titanium.Web.Proxy ...@@ -72,29 +72,23 @@ namespace Titanium.Web.Proxy
await WriteConnectResponse(clientStreamWriter, version).ConfigureAwait(false); await WriteConnectResponse(clientStreamWriter, version).ConfigureAwait(false);
var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host, false);
SslStream sslStream = null; SslStream sslStream = null;
try try
{ {
var connectRequest = new ConnectRequest() { Stream = clientStream, Uri = httpRemoteUri };
await TcpConnectionManager.GetClient(connectRequest, httpRemoteUri.Host, httpRemoteUri.Port, true, version).ConfigureAwait(false); await TcpConnectionManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version).ConfigureAwait(false);
sslStream = new SslStream(clientStream, true);
var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host, false);
//Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false,
Constants.SupportedProtocols, false).ConfigureAwait(false);
//HTTPS server created - we can now decrypt the client's traffic
clientStream = sslStream;
if (clientStream is SslStream)
{
sslStream = clientStream as SslStream;
}
else
{
sslStream = new SslStream(clientStream, true);
//Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false,
Constants.SupportedProtocols, false).ConfigureAwait(false);
//HTTPS server created - we can now decrypt the client's traffic
clientStream = sslStream;
}
clientStreamReader = new CustomBinaryReader(sslStream); clientStreamReader = new CustomBinaryReader(sslStream);
clientStreamWriter = new StreamWriter(sslStream); clientStreamWriter = new StreamWriter(sslStream);
...@@ -192,8 +186,6 @@ namespace Titanium.Web.Proxy ...@@ -192,8 +186,6 @@ namespace Titanium.Web.Proxy
private static async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private static async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool isHttps) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool isHttps)
{ {
TcpConnection connection = null;
string lastRequest = null;
while (true) while (true)
{ {
...@@ -276,12 +268,8 @@ namespace Titanium.Web.Proxy ...@@ -276,12 +268,8 @@ namespace Titanium.Web.Proxy
} }
//construct the web request that we are going to issue on behalf of the client. //construct the web request that we are going to issue on behalf of the client.
connection = connection == null ? var connection = await TcpConnectionManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false);
await TcpConnectionManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false)
: lastRequest != args.WebSession.Request.RequestUri.Host ? await TcpConnectionManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false)
: connection;
lastRequest = TcpConnectionManager.GetConnectionKey(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version);
args.WebSession.Request.RequestLocked = true; args.WebSession.Request.RequestLocked = true;
...@@ -350,11 +338,12 @@ namespace Titanium.Web.Proxy ...@@ -350,11 +338,12 @@ namespace Titanium.Web.Proxy
//if connection is closing exit //if connection is closing exit
if (args.WebSession.Response.ResponseKeepAlive == false) if (args.WebSession.Response.ResponseKeepAlive == false)
{ {
connection.TcpClient.Close();
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
await TcpConnectionManager.ReleaseClient(connection);
// read the next request // read the next request
httpCmd = await clientStreamReader.ReadLineAsync().ConfigureAwait(false); httpCmd = await clientStreamReader.ReadLineAsync().ConfigureAwait(false);
...@@ -367,8 +356,6 @@ namespace Titanium.Web.Proxy ...@@ -367,8 +356,6 @@ namespace Titanium.Web.Proxy
} }
if (connection != null)
await TcpConnectionManager.ReleaseClient(connection);
} }
private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion) private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion)
...@@ -460,8 +447,6 @@ namespace Titanium.Web.Proxy ...@@ -460,8 +447,6 @@ namespace Titanium.Web.Proxy
X509Chain chain, X509Chain chain,
SslPolicyErrors sslPolicyErrors) SslPolicyErrors sslPolicyErrors)
{ {
var customSslStream = sender as CustomSslStream;
if (ServerCertificateValidationCallback != null) if (ServerCertificateValidationCallback != null)
{ {
var args = new CertificateValidationEventArgs(); var args = new CertificateValidationEventArgs();
...@@ -510,43 +495,25 @@ namespace Titanium.Web.Proxy ...@@ -510,43 +495,25 @@ namespace Titanium.Web.Proxy
X509Certificate clientCertificate = null; X509Certificate clientCertificate = null;
var customSslStream = sender as CustomSslStream; var customSslStream = sender as CustomSslStream;
if (customSslStream.Param is ConnectRequest && remoteCertificate != null) if (acceptableIssuers != null &&
{ acceptableIssuers.Length > 0 &&
var connectRequest = customSslStream.Param as ConnectRequest; localCertificates != null &&
localCertificates.Count > 0)
var sslStream = new SslStream(connectRequest.Stream, true);
var certificate = CertManager.CreateCertificate(connectRequest.Uri.Host, false).Result;
//Successfully managed to authenticate the client using the fake certificate
sslStream.AuthenticateAsServerAsync(certificate, true,
Constants.SupportedProtocols, false).Wait();
connectRequest.Stream = sslStream;
clientCertificate = sslStream.RemoteCertificate;
}
else if (customSslStream.Param is ConnectRequest)
{ {
if (acceptableIssuers != null && // Use the first certificate that is from an acceptable issuer.
acceptableIssuers.Length > 0 && foreach (X509Certificate certificate in localCertificates)
localCertificates != null &&
localCertificates.Count > 0)
{ {
// Use the first certificate that is from an acceptable issuer. string issuer = certificate.Issuer;
foreach (X509Certificate certificate in localCertificates) if (Array.IndexOf(acceptableIssuers, issuer) != -1)
{ clientCertificate = certificate;
string issuer = certificate.Issuer;
if (Array.IndexOf(acceptableIssuers, issuer) != -1)
clientCertificate = certificate;
}
} }
if (localCertificates != null &&
localCertificates.Count > 0)
clientCertificate = localCertificates[0];
} }
if (localCertificates != null &&
localCertificates.Count > 0)
clientCertificate = localCertificates[0];
if (ClientCertificateSelectionCallback != null) if (ClientCertificateSelectionCallback != null)
{ {
var args = new CertificateSelectionEventArgs(); var args = new CertificateSelectionEventArgs();
......
...@@ -62,7 +62,6 @@ ...@@ -62,7 +62,6 @@
<Compile Include="Decompression\ZlibDecompression.cs" /> <Compile Include="Decompression\ZlibDecompression.cs" />
<Compile Include="EventArguments\CertificateSelectionEventArgs.cs" /> <Compile Include="EventArguments\CertificateSelectionEventArgs.cs" />
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="Models\ConnectRequest.cs" />
<Compile Include="Network\ProxyClient.cs" /> <Compile Include="Network\ProxyClient.cs" />
<Compile Include="Exceptions\BodyNotFoundException.cs" /> <Compile Include="Exceptions\BodyNotFoundException.cs" />
<Compile Include="Extensions\HttpWebResponseExtensions.cs" /> <Compile Include="Extensions\HttpWebResponseExtensions.cs" />
......
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