Commit 33d50f32 authored by justcoding121's avatar justcoding121

Fix -ive server connection count

parent e662451f
...@@ -37,6 +37,7 @@ namespace Titanium.Web.Proxy ...@@ -37,6 +37,7 @@ namespace Titanium.Web.Proxy
Task<TcpServerConnection> prefetchConnectionTask = null; Task<TcpServerConnection> prefetchConnectionTask = null;
bool closeServerConnection = false; bool closeServerConnection = false;
bool calledRequestHandler = false;
try try
{ {
...@@ -289,7 +290,7 @@ namespace Titanium.Web.Proxy ...@@ -289,7 +290,7 @@ namespace Titanium.Web.Proxy
await tcpConnectionFactory.Release(connection, true); await tcpConnectionFactory.Release(connection, true);
} }
} }
calledRequestHandler = true;
// Now create the request // Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest, prefetchConnectionTask); cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest, prefetchConnectionTask);
...@@ -316,7 +317,8 @@ namespace Titanium.Web.Proxy ...@@ -316,7 +317,8 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
if (prefetchConnectionTask != null) if (!calledRequestHandler
&& prefetchConnectionTask != null)
{ {
var connection = await prefetchConnectionTask; var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection); await tcpConnectionFactory.Release(connection, closeServerConnection);
......
...@@ -42,6 +42,29 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -42,6 +42,29 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal ProxyServer server { get; set; } internal ProxyServer server { get; set; }
internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy)
{
var cacheKeyBuilder = new StringBuilder($"{remoteHostName}-{remotePort}" +
$"-{(httpVersion == null ? string.Empty : httpVersion.ToString())}" +
$"-{isHttps}-{isConnect}-");
if (applicationProtocols != null)
{
foreach (var protocol in applicationProtocols)
{
cacheKeyBuilder.Append($"{protocol}-");
}
}
cacheKeyBuilder.Append(upStreamEndPoint != null
? $"{upStreamEndPoint.Address}-{upStreamEndPoint.Port}-"
: string.Empty);
cacheKeyBuilder.Append(externalProxy != null ? $"{externalProxy.GetCacheKey()}-" : string.Empty);
return cacheKeyBuilder.ToString();
}
/// <summary> /// <summary>
/// Gets a TCP connection to server from connection pool. /// Gets a TCP connection to server from connection pool.
/// </summary> /// </summary>
...@@ -61,23 +84,9 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -61,23 +84,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy, ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var cacheKeyBuilder = new StringBuilder($"{remoteHostName}-{remotePort}" + var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
$"-{(httpVersion == null ? string.Empty : httpVersion.ToString())}" + httpVersion, isHttps, applicationProtocols, isConnect,
$"-{isHttps}-{isConnect}-"); proxyServer, upStreamEndPoint, externalProxy);
if (applicationProtocols != null)
{
foreach (var protocol in applicationProtocols)
{
cacheKeyBuilder.Append($"{protocol}-");
}
}
cacheKeyBuilder.Append(upStreamEndPoint != null
? $"{upStreamEndPoint.Address}-{upStreamEndPoint.Port}-"
: string.Empty);
cacheKeyBuilder.Append(externalProxy != null ? $"{externalProxy.GetCacheKey()}-" : string.Empty);
string cacheKey = cacheKeyBuilder.ToString();
if (proxyServer.EnableConnectionPool) if (proxyServer.EnableConnectionPool)
{ {
......
...@@ -189,11 +189,11 @@ namespace Titanium.Web.Proxy ...@@ -189,11 +189,11 @@ namespace Titanium.Web.Proxy
newConnection = true; newConnection = true;
} }
// create a new connection if hostname/upstream end point changes // create a new connection if cache key changes
if (serverConnection != null if (serverConnection != null
&& (!serverConnection.HostName.EqualsIgnoreCase(request.RequestUri.Host) && (await getConnectionCacheKey(args, false,
|| args.WebSession.UpStreamEndPoint?.Equals(serverConnection.UpStreamEndPoint) == clientConnection.NegotiatedApplicationProtocol)
false)) != serverConnection.CacheKey))
{ {
await tcpConnectionFactory.Release(serverConnection); await tcpConnectionFactory.Release(serverConnection);
serverConnection = null; serverConnection = null;
...@@ -263,16 +263,14 @@ namespace Titanium.Web.Proxy ...@@ -263,16 +263,14 @@ namespace Titanium.Web.Proxy
throw new Exception("Session was terminated by user."); throw new Exception("Session was terminated by user.");
} }
//TODO find why enabling this cause server connection count to go -ive
//TODO find why enabling this causes invalid httpCmd exception occationally
//With connection pool get connection for each HTTP session instead of per client connection. //With connection pool get connection for each HTTP session instead of per client connection.
//That will be more efficient especially when client is holding connection but not using it //That will be more efficient especially when client is holding connection but not using it
//if (EnableConnectionPool) if (EnableConnectionPool)
//{ {
// await tcpConnectionFactory.Release(serverConnection); await tcpConnectionFactory.Release(serverConnection);
// serverConnection = null; serverConnection = null;
// prefetchTask = null; prefetchTask = null;
//} }
} }
catch (Exception e) when (!(e is ProxyHttpException)) catch (Exception e) when (!(e is ProxyHttpException))
...@@ -294,17 +292,10 @@ namespace Titanium.Web.Proxy ...@@ -294,17 +292,10 @@ namespace Titanium.Web.Proxy
} }
} }
finally finally
{
//don't release prefetched connection here.
//it will be handled by the parent method which created it.
if (prefetchConnectionTask == null
|| serverConnection != await prefetchConnectionTask)
{ {
await tcpConnectionFactory.Release(serverConnection, await tcpConnectionFactory.Release(serverConnection,
closeServerConnection); closeServerConnection);
} }
}
} }
/// <summary> /// <summary>
...@@ -386,54 +377,59 @@ namespace Titanium.Web.Proxy ...@@ -386,54 +377,59 @@ namespace Titanium.Web.Proxy
} }
/// <summary> /// <summary>
/// Create a server connection. /// Handle upgrade to websocket
/// </summary> /// </summary>
/// <param name="args">The session event arguments.</param> private async Task handleWebSocketUpgrade(string httpCmd,
/// <param name="isConnect">Is this a CONNECT request.</param> SessionEventArgs args, Request request, Response response,
/// <param name="applicationProtocol"></param> CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
/// <param name="cancellationToken">The cancellation token for this async task.</param> TcpServerConnection serverConnection,
/// <returns></returns> CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
private Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
SslApplicationProtocol applicationProtocol, CancellationToken cancellationToken)
{ {
List<SslApplicationProtocol> applicationProtocols = null; // prepare the prefix content
if (applicationProtocol != default) await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken);
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers,
cancellationToken: cancellationToken);
string httpStatus;
try
{ {
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; httpStatus = await serverConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
{
throw new ServerConnectionException("Server connection was closed.");
} }
return getServerConnection(args, isConnect, applicationProtocols, cancellationToken);
} }
catch (Exception e) when (!(e is ServerConnectionException))
/// <summary>
/// Create a server connection.
/// </summary>
/// <param name="args">The session event arguments.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocols"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private async Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
List<SslApplicationProtocol> applicationProtocols, CancellationToken cancellationToken)
{ {
ExternalProxy customUpStreamProxy = null; throw new ServerConnectionException("Server connection was closed.", e);
}
bool isHttps = args.IsHttps; Response.ParseResponseLine(httpStatus, out var responseVersion,
if (GetCustomUpStreamProxyFunc != null) out int responseStatusCode,
out string responseStatusDescription);
response.HttpVersion = responseVersion;
response.StatusCode = responseStatusCode;
response.StatusDescription = responseStatusDescription;
await HeaderParser.ReadHeaders(serverConnection.Stream, response.Headers,
cancellationToken);
if (!args.IsTransparent)
{ {
customUpStreamProxy = await GetCustomUpStreamProxyFunc(args); await clientStreamWriter.WriteResponseAsync(response,
cancellationToken: cancellationToken);
} }
args.CustomUpStreamProxyUsed = customUpStreamProxy; // If user requested call back then do it
if (!args.WebSession.Response.Locked)
{
await invokeBeforeResponse(args);
}
return await tcpConnectionFactory.GetClient( await TcpHelper.SendRaw(clientStream, serverConnection.Stream, BufferPool, BufferSize,
args.WebSession.Request.RequestUri.Host, (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); },
args.WebSession.Request.RequestUri.Port, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); },
args.WebSession.Request.HttpVersion, cancellationTokenSource, ExceptionFunc);
isHttps, applicationProtocols, isConnect,
this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy),
cancellationToken);
} }
/// <summary> /// <summary>
...@@ -462,62 +458,93 @@ namespace Titanium.Web.Proxy ...@@ -462,62 +458,93 @@ namespace Titanium.Web.Proxy
requestHeaders.FixProxyHeaders(); requestHeaders.FixProxyHeaders();
} }
/// <summary> /// <summary>
/// Handle upgrade to websocket /// Gets the connection cache key.
/// </summary> /// </summary>
private async Task handleWebSocketUpgrade(string httpCmd, /// <param name="args">The session event arguments.</param>
SessionEventArgs args, Request request, Response response, /// <param name="isConnect">Is this a CONNECT request.</param>
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, /// <param name="applicationProtocol"></param>
TcpServerConnection serverConnection, /// <returns></returns>
CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken) private async Task<string> getConnectionCacheKey(SessionEventArgsBase args, bool isConnect,
{ SslApplicationProtocol applicationProtocol)
// prepare the prefix content
await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken);
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers,
cancellationToken: cancellationToken);
string httpStatus;
try
{ {
httpStatus = await serverConnection.Stream.ReadLineAsync(cancellationToken); List<SslApplicationProtocol> applicationProtocols = null;
if (httpStatus == null) if (applicationProtocol != default)
{ {
throw new ServerConnectionException("Server connection was closed."); applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}
} }
catch (Exception e) when (!(e is ServerConnectionException))
ExternalProxy customUpStreamProxy = null;
bool isHttps = args.IsHttps;
if (GetCustomUpStreamProxyFunc != null)
{ {
throw new ServerConnectionException("Server connection was closed.", e); customUpStreamProxy = await GetCustomUpStreamProxyFunc(args);
} }
Response.ParseResponseLine(httpStatus, out var responseVersion, args.CustomUpStreamProxyUsed = customUpStreamProxy;
out int responseStatusCode,
out string responseStatusDescription);
response.HttpVersion = responseVersion;
response.StatusCode = responseStatusCode;
response.StatusDescription = responseStatusDescription;
await HeaderParser.ReadHeaders(serverConnection.Stream, response.Headers, return tcpConnectionFactory.GetConnectionCacheKey(
cancellationToken); args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy));
}
if (!args.IsTransparent)
/// <summary>
/// Create a server connection.
/// </summary>
/// <param name="args">The session event arguments.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocol"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
SslApplicationProtocol applicationProtocol, CancellationToken cancellationToken)
{ {
await clientStreamWriter.WriteResponseAsync(response, List<SslApplicationProtocol> applicationProtocols = null;
cancellationToken: cancellationToken); if (applicationProtocol != default)
{
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
} }
// If user requested call back then do it return getServerConnection(args, isConnect, applicationProtocols, cancellationToken);
if (!args.WebSession.Response.Locked)
{
await invokeBeforeResponse(args);
} }
await TcpHelper.SendRaw(clientStream, serverConnection.Stream, BufferPool, BufferSize, /// <summary>
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, /// Create a server connection.
(buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }, /// </summary>
cancellationTokenSource, ExceptionFunc); /// <param name="args">The session event arguments.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocols"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private async Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
List<SslApplicationProtocol> applicationProtocols, CancellationToken cancellationToken)
{
ExternalProxy customUpStreamProxy = null;
bool isHttps = args.IsHttps;
if (GetCustomUpStreamProxyFunc != null)
{
customUpStreamProxy = await GetCustomUpStreamProxyFunc(args);
} }
args.CustomUpStreamProxyUsed = customUpStreamProxy;
return await tcpConnectionFactory.GetClient(
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy),
cancellationToken);
}
/// <summary> /// <summary>
/// Invoke before request handler if it is set. /// Invoke before request handler if it is set.
......
...@@ -35,6 +35,7 @@ namespace Titanium.Web.Proxy ...@@ -35,6 +35,7 @@ namespace Titanium.Web.Proxy
Task<TcpServerConnection> prefetchConnectionTask = null; Task<TcpServerConnection> prefetchConnectionTask = null;
bool closeServerConnection = false; bool closeServerConnection = false;
bool calledRequestHandler = false;
try try
{ {
...@@ -127,7 +128,7 @@ namespace Titanium.Web.Proxy ...@@ -127,7 +128,7 @@ namespace Titanium.Web.Proxy
} }
} }
calledRequestHandler = true;
// HTTPS server created - we can now decrypt the client's traffic // HTTPS server created - we can now decrypt the client's traffic
// Now create the request // Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
...@@ -155,7 +156,8 @@ namespace Titanium.Web.Proxy ...@@ -155,7 +156,8 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
if (prefetchConnectionTask != null) if (!calledRequestHandler
&& prefetchConnectionTask != null)
{ {
var connection = await prefetchConnectionTask; var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection); await tcpConnectionFactory.Release(connection, closeServerConnection);
......
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