Unverified Commit 17a1aabe authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #444 from justcoding121/master

connection pool improvements
parents 45b13d34 c9c47fab
......@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy
{
string connectHostname = null;
TunnelConnectSessionEventArgs connectArgs = null;
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if (await HttpHelper.IsConnectMethod(clientStream) == 1)
......@@ -212,38 +212,43 @@ namespace Titanium.Web.Proxy
var connection = await getServerConnection(connectArgs, true,
null, cancellationToken);
if (isClientHello)
try
{
int available = clientStream.Available;
if (available > 0)
if (isClientHello)
{
// send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
await connection.StreamWriter.WriteAsync(data, 0, available, true,
cancellationToken);
}
finally
int available = clientStream.Available;
if (available > 0)
{
BufferPool.ReturnBuffer(data);
// send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
await connection.StreamWriter.WriteAsync(data, 0, available, true,
cancellationToken);
}
finally
{
BufferPool.ReturnBuffer(data);
}
}
var serverHelloInfo =
await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);
((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
}
var serverHelloInfo =
await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);
((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc);
}
finally
{
await tcpConnectionFactory.Release(connection, true);
}
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc);
await tcpConnectionFactory.Release(connection, true);
return;
}
}
......@@ -276,7 +281,8 @@ namespace Titanium.Web.Proxy
// create new connection
var connection = await getServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList,
cancellationToken);
try
{
await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken);
await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);
......@@ -288,7 +294,11 @@ namespace Titanium.Web.Proxy
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif
await tcpConnectionFactory.Release(connection, true);
}
finally
{
await tcpConnectionFactory.Release(connection, true);
}
}
}
calledRequestHandler = true;
......@@ -318,7 +328,7 @@ namespace Titanium.Web.Proxy
}
finally
{
if (!calledRequestHandler
if (!calledRequestHandler
&& prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
......
......@@ -94,34 +94,25 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (proxyServer.EnableConnectionPool)
{
try
if (cache.TryGetValue(cacheKey, out var existingConnections))
{
await @lock.WaitAsync();
if (cache.TryGetValue(cacheKey, out var existingConnections))
while (existingConnections.Count > 0)
{
while (existingConnections.Count > 0)
if (existingConnections.TryDequeue(out var recentConnection))
{
if (existingConnections.TryDequeue(out var recentConnection))
{
//+3 seconds for potential delay after getting connection
var cutOff = DateTime.Now.AddSeconds(-1 * proxyServer.ConnectionTimeOutSeconds + 3);
if (recentConnection.LastAccess > cutOff
&& isGoodConnection(recentConnection.TcpClient))
{
return recentConnection;
}
//+3 seconds for potential delay after getting connection
var cutOff = DateTime.Now.AddSeconds(-1 * proxyServer.ConnectionTimeOutSeconds + 3);
disposalBag.Add(recentConnection);
if (recentConnection.LastAccess > cutOff
&& isGoodConnection(recentConnection.TcpClient))
{
return recentConnection;
}
disposalBag.Add(recentConnection);
}
}
}
finally
{
@lock.Release();
}
}
var connection = await createClient(remoteHostName, remotePort, httpVersion, isHttps,
......
......@@ -130,6 +130,7 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// Defaults to false.
/// </summary>
public bool ForwardToUpstreamGateway { get; set; }
......@@ -138,6 +139,7 @@ namespace Titanium.Web.Proxy
/// Note: NTLM/Kerberos will always send local credentials of current user
/// running the proxy process. This is because a man
/// in middle attack with Windows domain authentication is not currently supported.
/// Defaults to false.
/// </summary>
public bool EnableWinAuth { get; set; }
......@@ -155,18 +157,21 @@ namespace Titanium.Web.Proxy
public bool Enable100ContinueBehaviour { get; set; }
/// <summary>
/// Should we enable server connection pool?
/// Defaults to true.
/// Should we enable experimental server connection pool?
/// Defaults to disable.
/// </summary>
public bool EnableConnectionPool { get; set; } = true;
public bool EnableConnectionPool { get; set; }
/// <summary>
/// Buffer size used throughout this proxy.
/// Buffer size in bytes used throughout this proxy.
/// Default value is 8192 bytes.
/// </summary>
public int BufferSize { get; set; } = 8192;
/// <summary>
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete.
/// This will also determine the pool eviction time when connection pool is enabled.
/// Default value is 60 seconds.
/// </summary>
public int ConnectionTimeOutSeconds { get; set; }
......@@ -174,9 +179,9 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Maximum number of concurrent connections per remote host in cache.
/// Only valid when connection pooling is enabled.
/// Default value is 3.
/// Default value is 2.
/// </summary>
public int MaxCachedConnections { get; set; } = 3;
public int MaxCachedConnections { get; set; } = 2;
/// <summary>
/// Total number of active client connections.
......@@ -204,6 +209,8 @@ namespace Titanium.Web.Proxy
/// <summary>
/// The buffer pool used throughout this proxy instance.
/// Set custom implementations by implementing this interface.
/// By default this uses DefaultBufferPool implementation available in StreamExtended library package.
/// </summary>
public IBufferPool BufferPool { get; set; }
......
......@@ -98,34 +98,38 @@ namespace Titanium.Web.Proxy
null, false, null,
true, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken);
var serverStream = connection.Stream;
int available = clientStream.Available;
if (available > 0)
try
{
// send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
var serverStream = connection.Stream;
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
await serverStream.WriteAsync(data, 0, available, cancellationToken);
await serverStream.FlushAsync(cancellationToken);
}
finally
int available = clientStream.Available;
if (available > 0)
{
BufferPool.ReturnBuffer(data);
// send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
await serverStream.WriteAsync(data, 0, available, cancellationToken);
await serverStream.FlushAsync(cancellationToken);
}
finally
{
BufferPool.ReturnBuffer(data);
}
}
}
await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
}
finally
{
await tcpConnectionFactory.Release(connection, true);
}
await tcpConnectionFactory.Release(connection, true);
return;
}
}
calledRequestHandler = true;
......
......@@ -260,7 +260,9 @@ Should return true for successful authentication.</p>
<a id="Titanium_Web_Proxy_ProxyServer_BufferPool_" data-uid="Titanium.Web.Proxy.ProxyServer.BufferPool*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_BufferPool" data-uid="Titanium.Web.Proxy.ProxyServer.BufferPool">BufferPool</h4>
<div class="markdown level1 summary"><p>The buffer pool used throughout this proxy instance.</p>
<div class="markdown level1 summary"><p>The buffer pool used throughout this proxy instance.
Set custom implementations by implementing this interface.
By default this uses DefaultBufferPool implementation available in StreamExtended library package.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......@@ -286,7 +288,8 @@ Should return true for successful authentication.</p>
<a id="Titanium_Web_Proxy_ProxyServer_BufferSize_" data-uid="Titanium.Web.Proxy.ProxyServer.BufferSize*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_BufferSize" data-uid="Titanium.Web.Proxy.ProxyServer.BufferSize">BufferSize</h4>
<div class="markdown level1 summary"><p>Buffer size used throughout this proxy.</p>
<div class="markdown level1 summary"><p>Buffer size in bytes used throughout this proxy.
Default value is 8192 bytes.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......@@ -391,7 +394,9 @@ Note: If enabled can reduce performance. Defaults to false.</p>
<a id="Titanium_Web_Proxy_ProxyServer_ConnectionTimeOutSeconds_" data-uid="Titanium.Web.Proxy.ProxyServer.ConnectionTimeOutSeconds*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_ConnectionTimeOutSeconds" data-uid="Titanium.Web.Proxy.ProxyServer.ConnectionTimeOutSeconds">ConnectionTimeOutSeconds</h4>
<div class="markdown level1 summary"><p>Seconds client/server connection are to be kept alive when waiting for read/write to complete.</p>
<div class="markdown level1 summary"><p>Seconds client/server connection are to be kept alive when waiting for read/write to complete.
This will also determine the pool eviction time when connection pool is enabled.
Default value is 60 seconds.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......@@ -475,7 +480,8 @@ Defaults to true.</p>
<div class="markdown level1 summary"><p>Enable disable Windows Authentication (NTLM/Kerberos).
Note: NTLM/Kerberos will always send local credentials of current user
running the proxy process. This is because a man
in middle attack with Windows domain authentication is not currently supported.</p>
in middle attack with Windows domain authentication is not currently supported.
Defaults to false.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......@@ -527,7 +533,8 @@ in middle attack with Windows domain authentication is not currently supported.<
<a id="Titanium_Web_Proxy_ProxyServer_ForwardToUpstreamGateway_" data-uid="Titanium.Web.Proxy.ProxyServer.ForwardToUpstreamGateway*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_ForwardToUpstreamGateway" data-uid="Titanium.Web.Proxy.ProxyServer.ForwardToUpstreamGateway">ForwardToUpstreamGateway</h4>
<div class="markdown level1 summary"><p>Gets or sets a value indicating whether requests will be chained to upstream gateway.</p>
<div class="markdown level1 summary"><p>Gets or sets a value indicating whether requests will be chained to upstream gateway.
Defaults to false.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......@@ -582,7 +589,7 @@ User should return the ExternalProxy object with valid credentials.</p>
<h4 id="Titanium_Web_Proxy_ProxyServer_MaxCachedConnections" data-uid="Titanium.Web.Proxy.ProxyServer.MaxCachedConnections">MaxCachedConnections</h4>
<div class="markdown level1 summary"><p>Maximum number of concurrent connections per remote host in cache.
Only valid when connection pooling is enabled.
Default value is 3.</p>
Default value is 2.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......
This diff is collapsed.
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