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