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