Commit 989c8831 authored by justcoding121's avatar justcoding121

prefetch server connection async during client ssl authentication

parent 190da80f
......@@ -14,7 +14,6 @@ using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy
......@@ -34,14 +33,17 @@ namespace Titanium.Web.Proxy
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
Task<TcpServerConnection> prefetchConnectionTask = null;
bool closeServerConnection = false;
try
{
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)
{
......@@ -135,9 +137,6 @@ namespace Titanium.Web.Proxy
{
// test server HTTP/2 support
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
// When connection pool becomes stable we can connect to server preemptively in a separate task here without awaiting
// using HTTPS CONNECT hostname. Then by releasing server connection
// back to connection pool we can authenticate against client/server concurrently and save time.
var connection = await getServerConnection(connectArgs, true,
SslExtensions.Http2ProtocolAsList, cancellationToken);
......@@ -148,7 +147,8 @@ namespace Titanium.Web.Proxy
}
SslStream sslStream = null;
prefetchConnectionTask = getServerConnection(connectArgs, true,
null, cancellationToken);
try
{
sslStream = new SslStream(clientStream);
......@@ -292,27 +292,38 @@ namespace Titanium.Web.Proxy
// Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest);
cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest, prefetchConnectionTask);
}
catch (ProxyException e)
{
closeServerConnection = true;
onException(clientStream, e);
}
catch (IOException e)
{
closeServerConnection = true;
onException(clientStream, new Exception("Connection was aborted", e));
}
catch (SocketException e)
{
closeServerConnection = true;
onException(clientStream, new Exception("Could not connect", e));
}
catch (Exception e)
{
closeServerConnection = true;
onException(clientStream, new Exception("Error occured in whilst handling the client", e));
}
finally
{
clientStream.Dispose();
if (prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection || !EnableConnectionPool);
}
if (!cancellationTokenSource.IsCancellationRequested)
{
cancellationTokenSource.Cancel();
......
......@@ -114,6 +114,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="close">Should we just close the connection instead of reusing?</param>
internal async Task Release(TcpServerConnection connection, bool close = false)
{
if (connection == null)
{
return;
}
if (close || connection.IsWinAuthenticated)
{
disposalBag.Add(connection);
......
......@@ -49,13 +49,16 @@ namespace Titanium.Web.Proxy
/// explicit endpoint.
/// </param>
/// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param>
/// <param name="prefetchConnectionTask">Prefected server connection for current client using Connect/SNI headers.</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest)
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest,
Task<TcpServerConnection> prefetchConnectionTask = null)
{
var cancellationToken = cancellationTokenSource.Token;
TcpServerConnection serverConnection = null;
bool serverConnectionClose = false;
bool closeServerConnection = false;
try
{
......@@ -178,6 +181,13 @@ namespace Titanium.Web.Proxy
}
bool newConnection = false;
if (serverConnection == null && prefetchConnectionTask != null)
{
serverConnection = await prefetchConnectionTask;
newConnection = true;
}
// create a new connection if hostname/upstream end point changes
if (serverConnection != null
&& (!serverConnection.HostName.EqualsIgnoreCase(request.RequestUri.Host)
......@@ -245,7 +255,7 @@ namespace Titanium.Web.Proxy
// if connection is closing exit
if (!response.KeepAlive)
{
serverConnectionClose = true;
closeServerConnection = true;
return;
}
......@@ -262,7 +272,7 @@ namespace Titanium.Web.Proxy
catch (Exception e)
{
args.Exception = e;
serverConnectionClose = true;
closeServerConnection = true;
throw;
}
finally
......@@ -274,7 +284,15 @@ namespace Titanium.Web.Proxy
}
finally
{
await tcpConnectionFactory.Release(serverConnection, serverConnectionClose || !EnableConnectionPool);
//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,
closeServerConnection || !EnableConnectionPool);
}
}
}
......
......@@ -31,9 +31,11 @@ namespace Titanium.Web.Proxy
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
Task<TcpServerConnection> prefetchConnectionTask = null;
bool closeServerConnection = false;
try
{
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
......@@ -59,11 +61,11 @@ namespace Titanium.Web.Proxy
if (endPoint.DecryptSsl && args.DecryptSsl)
{
SslStream sslStream = null;
prefetchConnectionTask = tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
null, true, null,
false, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken);
// When connection pool becomes stable we can connect to server preemptively in a separate task here without awaiting
// for all HTTPS requests using SNI hostname. Then by releasing server connection
// back to connection pool we can authenticate against client/server concurrently and save time.
SslStream sslStream = null;
//do client authentication using fake certificate
try
......@@ -129,27 +131,38 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic
// Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, isHttps ? httpsHostName : null, null);
cancellationTokenSource, isHttps ? httpsHostName : null, null, prefetchConnectionTask);
}
catch (ProxyException e)
{
closeServerConnection = true;
onException(clientStream, e);
}
catch (IOException e)
{
closeServerConnection = true;
onException(clientStream, new Exception("Connection was aborted", e));
}
catch (SocketException e)
{
closeServerConnection = true;
onException(clientStream, new Exception("Could not connect", e));
}
catch (Exception e)
{
closeServerConnection = true;
onException(clientStream, new Exception("Error occured in whilst handling the client", e));
}
finally
{
clientStream.Dispose();
if (prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection || !EnableConnectionPool);
}
if (!cancellationTokenSource.IsCancellationRequested)
{
cancellationTokenSource.Cancel();
......
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