Commit 0ff6870d authored by justcoding121's avatar justcoding121

Prepare for TCP connection pool

parent 24b0ffbd
......@@ -136,10 +136,11 @@ namespace Titanium.Web.Proxy
{
// test server HTTP/2 support
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
using (var connection = await GetServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList, cancellationToken))
{
var connection = await GetServerConnection(connectArgs, true,
SslExtensions.Http2ProtocolAsList, cancellationToken);
http2Supproted = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;
}
tcpConnectionFactory.Release(connection);
}
SslStream sslStream = null;
......@@ -176,7 +177,6 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
}
catch (Exception e)
......@@ -201,8 +201,9 @@ namespace Titanium.Web.Proxy
if (!decryptSsl || !isClientHello)
{
// create new connection
using (var connection = await GetServerConnection(connectArgs, true, clientConnection.NegotiatedApplicationProtocol, cancellationToken))
{
var connection = await GetServerConnection(connectArgs, true,
clientConnection.NegotiatedApplicationProtocol, cancellationToken);
if (isClientHello)
{
int available = clientStream.Available;
......@@ -233,8 +234,8 @@ namespace Titanium.Web.Proxy
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc);
}
tcpConnectionFactory.Release(connection);
return;
}
}
......@@ -265,8 +266,9 @@ namespace Titanium.Web.Proxy
}
// create new connection
using (var connection = await GetServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList, cancellationToken))
{
var connection = await GetServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList,
cancellationToken);
await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken);
await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);
......@@ -278,7 +280,7 @@ namespace Titanium.Web.Proxy
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif
}
tcpConnectionFactory.Release(connection);
}
}
......
......@@ -77,5 +77,6 @@ namespace Titanium.Web.Proxy.Models
{
return $"{HostName}:{Port}";
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
......@@ -13,30 +15,75 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network.Tcp
{
/// <summary>
/// A class that manages Tcp Connection to server used by this proxy server
/// A class that manages Tcp Connection to server used by this proxy server.
/// </summary>
internal class TcpConnectionFactory
{
//private readonly ConcurrentDictionary<string, List<TcpServerConnection>> cache
// = new ConcurrentDictionary<string, List<TcpServerConnection>>();
/// <summary>
/// Gets a TCP connection to server from connection pool.
/// </summary>
/// <param name="remoteHostName">The remote hostname.</param>
/// <param name="remotePort">The remote port.</param>
/// <param name="httpVersion">The http version to use.</param>
/// <param name="isHttps">Is this a HTTPS request.</param>
/// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="upStreamEndPoint">The upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal async Task<TcpServerConnection> GetClient(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken)
{
//TODO fix cacheKey with all possible properties that uniquely identify a connection
//var cacheKey = $"{remoteHostName}{remotePort}{httpVersion}" +
// $"{isHttps}{isConnect}";
//if (cache.TryGetValue(cacheKey, out var existingConnections))
//{
// var recentConnection = existingConnections.Last();
// existingConnections.RemoveAt(existingConnections.Count - 1);
// //TODO check if connection is still active before returning
// return recentConnection;
//}
var connection = await CreateClient(remoteHostName, remotePort, httpVersion, isHttps,
applicationProtocols, isConnect, proxyServer, upStreamEndPoint, externalProxy, cancellationToken);
//connection.CacheKey = cacheKey;
return connection;
}
/// <summary>
/// Creates a TCP connection to server
/// </summary>
/// <param name="remoteHostName"></param>
/// <param name="remotePort"></param>
/// <param name="httpVersion"></param>
/// <param name="decryptSsl"></param>
/// <param name="applicationProtocols"></param>
/// <param name="isConnect"></param>
/// <param name="proxyServer"></param>
/// <param name="upStreamEndPoint"></param>
/// <param name="externalProxy"></param>
/// <param name="cancellationToken"></param>
/// <param name="remoteHostName">The remote hostname.</param>
/// <param name="remotePort">The remote port.</param>
/// <param name="httpVersion">The http version to use.</param>
/// <param name="isHttps">Is this a HTTPS request.</param>
/// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="upStreamEndPoint">The upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal async Task<TcpServerConnection> CreateClient(string remoteHostName, int remotePort,
Version httpVersion, bool decryptSsl, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
private async Task<TcpServerConnection> CreateClient(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken)
{
bool useUpstreamProxy = false;
// check if external proxy is set for HTTP/HTTPS
......@@ -59,12 +106,14 @@ namespace Titanium.Web.Proxy.Network.Tcp
try
{
tcpClient = new TcpClient(upStreamEndPoint);
tcpClient = new TcpClient(upStreamEndPoint)
{
ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000,
SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000,
SendBufferSize = proxyServer.BufferSize,
ReceiveBufferSize = proxyServer.BufferSize
};
tcpClient.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
tcpClient.SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
tcpClient.SendBufferSize = proxyServer.BufferSize;
tcpClient.ReceiveBufferSize = proxyServer.BufferSize;
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
......@@ -80,7 +129,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferSize);
if (useUpstreamProxy && (isConnect || decryptSsl))
if (useUpstreamProxy && (isConnect || isHttps))
{
var writer = new HttpRequestWriter(stream, proxyServer.BufferSize);
var connectRequest = new ConnectRequest
......@@ -113,18 +162,20 @@ namespace Titanium.Web.Proxy.Network.Tcp
await stream.ReadAndIgnoreAllLinesAsync(cancellationToken);
}
if (decryptSsl)
if (isHttps)
{
var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
proxyServer.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, proxyServer.BufferSize);
var options = new SslClientAuthenticationOptions();
options.ApplicationProtocols = applicationProtocols;
options.TargetHost = remoteHostName;
options.ClientCertificates = null;
options.EnabledSslProtocols = proxyServer.SupportedSslProtocols;
options.CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation;
var options = new SslClientAuthenticationOptions
{
ApplicationProtocols = applicationProtocols,
TargetHost = remoteHostName,
ClientCertificates = null,
EnabledSslProtocols = proxyServer.SupportedSslProtocols,
CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation
};
await sslStream.AuthenticateAsClientAsync(options, cancellationToken);
#if NETCOREAPP2_1
negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
......@@ -141,11 +192,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
return new TcpServerConnection(proxyServer, tcpClient)
{
//CacheKey = cacheKey,
UpStreamProxy = externalProxy,
UpStreamEndPoint = upStreamEndPoint,
HostName = remoteHostName,
Port = remotePort,
IsHttps = decryptSsl,
IsHttps = isHttps,
NegotiatedApplicationProtocol = negotiatedApplicationProtocol,
UseUpstreamProxy = useUpstreamProxy,
StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferSize),
......@@ -153,5 +205,29 @@ namespace Titanium.Web.Proxy.Network.Tcp
Version = httpVersion
};
}
/// <summary>
/// Release connection back to cache.
/// </summary>
/// <param name="connection">The Tcp server connection to return.</param>
internal void Release(TcpServerConnection connection)
{
//while (true)
//{
// if (cache.TryGetValue(connection.Key, out var existingConnections))
// {
// existingConnections.Add(connection);
// break;
// }
// if (cache.TryAdd(connection.Key, new List<TcpServerConnection> { connection }))
// {
// break;
// };
//}
connection?.Dispose();
}
}
}
......@@ -63,6 +63,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal DateTime LastAccess { get; set; }
/// <summary>
/// The cache key used to uniquely identify this connection properties
/// </summary>
internal string CacheKey { get; set; }
/// <summary>
/// Dispose.
/// </summary>
......
......@@ -268,7 +268,7 @@ namespace Titanium.Web.Proxy
}
finally
{
serverConnection?.Dispose();
tcpConnectionFactory.Release(serverConnection);
}
}
......@@ -398,7 +398,7 @@ namespace Titanium.Web.Proxy
args.CustomUpStreamProxyUsed = customUpStreamProxy;
return await tcpConnectionFactory.CreateClient(
return await tcpConnectionFactory.GetClient(
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
......@@ -87,14 +88,12 @@ namespace Titanium.Web.Proxy
else
{
// create new connection
var connection = new TcpClient(UpStreamEndPoint);
await connection.ConnectAsync(httpsHostName, endPoint.Port);
connection.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
connection.SendTimeout = ConnectionTimeOutSeconds * 1000;
var connection = await tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
null, false, new List<SslApplicationProtocol> { clientConnection.NegotiatedApplicationProtocol },
true, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken);
using (connection)
{
var serverStream = connection.GetStream();
var serverStream = connection.Stream;
int available = clientStream.Available;
if (available > 0)
......@@ -115,11 +114,12 @@ namespace Titanium.Web.Proxy
}
}
////var serverHelloInfo = await SslTools.PeekServerHello(serverStream);
await TcpHelper.SendRaw(clientStream, serverStream, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
}
tcpConnectionFactory.Release(connection);
return;
}
}
......
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