Commit c75ad1a5 authored by Honfika's avatar Honfika

prefetch fix

parent 37d1d6a2
......@@ -45,7 +45,7 @@
<value>True</value>
</setting>
<setting name="EnableTcpServerConnectionPrefetch" serializeAs="String">
<value>False</value>
<value>True</value>
</setting>
<setting name="EnableWinAuth" serializeAs="String">
<value>False</value>
......
......@@ -95,7 +95,7 @@ namespace WindowsServiceExample.Properties
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool EnableTcpServerConnectionPrefetch
{
get
......
......@@ -21,7 +21,7 @@
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="EnableTcpServerConnectionPrefetch" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">False</Value>
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="EnableWinAuth" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">False</Value>
......
......@@ -35,7 +35,7 @@ namespace Titanium.Web.Proxy
var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken);
Task<TcpServerConnection>? prefetchConnectionTask = null;
Task<TcpServerConnection?>? prefetchConnectionTask = null;
bool closeServerConnection = false;
try
......@@ -145,13 +145,16 @@ namespace Titanium.Web.Proxy
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
true, SslExtensions.Http2ProtocolAsList,
true, cancellationToken);
true, true, cancellationToken);
http2Supported = connection.NegotiatedApplicationProtocol ==
SslApplicationProtocol.Http2;
if (connection != null)
{
http2Supported = connection.NegotiatedApplicationProtocol ==
SslApplicationProtocol.Http2;
// release connection back to pool instead of closing when connection pool is enabled.
await tcpConnectionFactory.Release(connection, true);
// release connection back to pool instead of closing when connection pool is enabled.
await tcpConnectionFactory.Release(connection, true);
}
}
catch (Exception)
{
......@@ -162,22 +165,11 @@ namespace Titanium.Web.Proxy
if (EnableTcpServerConnectionPrefetch)
{
IPAddress[]? ipAddresses = null;
try
{
// make sure the host can be resolved before creating the prefetch task
ipAddresses = await Dns.GetHostAddressesAsync(connectArgs.HttpClient.Request.RequestUri.Host);
}
catch (SocketException) { }
if (ipAddresses != null && ipAddresses.Length > 0)
{
// don't pass cancellation token here
// it could cause floating server connections when client exits
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
true, null, false,
CancellationToken.None);
}
// don't pass cancellation token here
// it could cause floating server connections when client exits
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
true, null, false, true,
CancellationToken.None);
}
string connectHostname = requestLine.RequestUri.GetString();
......@@ -272,9 +264,9 @@ namespace Titanium.Web.Proxy
// create new connection to server.
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
// this connection should not be HTTPS.
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
true, null,
true, cancellationToken);
true, false, cancellationToken))!;
try
{
......@@ -349,9 +341,9 @@ namespace Titanium.Web.Proxy
throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
}
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
true, SslExtensions.Http2ProtocolAsList,
true, cancellationToken);
true, false, cancellationToken))!;
try
{
#if NETSTANDARD2_1
......
......@@ -154,7 +154,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}
return GetServerConnection(proxyServer, session, isConnect, applicationProtocols, noCache, cancellationToken);
return GetServerConnection(proxyServer, session, isConnect, applicationProtocols, noCache, false, cancellationToken)!;
}
/// <summary>
......@@ -165,10 +165,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocols"></param>
/// <param name="noCache">if set to <c>true</c> [no cache].</param>
/// <param name="prefetch">if set to <c>true</c> [prefetch].</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, SessionEventArgsBase session, bool isConnect,
List<SslApplicationProtocol>? applicationProtocols, bool noCache, CancellationToken cancellationToken)
internal async Task<TcpServerConnection?> GetServerConnection(ProxyServer proxyServer, SessionEventArgsBase session, bool isConnect,
List<SslApplicationProtocol>? applicationProtocols, bool noCache, bool prefetch, CancellationToken cancellationToken)
{
var customUpStreamProxy = session.CustomUpStreamProxy;
......@@ -208,7 +209,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
var upStreamEndPoint = session.HttpClient.UpStreamEndPoint ?? proxyServer.UpStreamEndPoint;
var upStreamProxy = customUpStreamProxy ?? (isHttps ? proxyServer.UpStreamHttpsProxy : proxyServer.UpStreamHttpProxy);
return await GetServerConnection(proxyServer, host, port, session.HttpClient.Request.HttpVersion, isHttps,
applicationProtocols, isConnect, session, upStreamEndPoint, upStreamProxy, noCache, cancellationToken);
applicationProtocols, isConnect, session, upStreamEndPoint, upStreamProxy, noCache, prefetch, cancellationToken);
}
/// <summary>
......@@ -225,12 +226,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="noCache">Not from cache/create new connection.</param>
/// <param name="prefetch">if set to <c>true</c> [prefetch].</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, string remoteHostName, int remotePort,
internal async Task<TcpServerConnection?> GetServerConnection(ProxyServer proxyServer, string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
SessionEventArgsBase sessionArgs, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
bool noCache, CancellationToken cancellationToken)
bool noCache, bool prefetch, CancellationToken cancellationToken)
{
var sslProtocol = sessionArgs.ClientConnection.SslProtocol;
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
......@@ -259,7 +261,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
}
var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol,
applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, prefetch, cancellationToken);
return connection;
}
......@@ -279,12 +281,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cacheKey">The connection cache key</param>
/// <param name="prefetch">if set to <c>true</c> [prefetch].</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
private async Task<TcpServerConnection?> createServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, SslProtocols sslProtocol, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase sessionArgs, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
CancellationToken cancellationToken)
bool prefetch, CancellationToken cancellationToken)
{
// deny connection to proxy end points to avoid infinite connection loop.
if (Server.ProxyEndPoints.Any(x => x.Port == remotePort)
......@@ -350,6 +353,11 @@ retry:
var ipAddresses = await Dns.GetHostAddressesAsync(hostname);
if (ipAddresses == null || ipAddresses.Length == 0)
{
if (prefetch)
{
return null;
}
throw new Exception($"Could not resolve the hostname {hostname}");
}
......@@ -486,10 +494,15 @@ retry:
{
sessionArgs.CustomUpStreamProxyUsed = newUpstreamProxy;
sessionArgs.TimeLine["Retrying Upstream Proxy Connection"] = DateTime.UtcNow;
return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, prefetch, cancellationToken);
}
}
if (prefetch)
{
return null;
}
throw new Exception($"Could not establish connection to {hostname}", lastException);
}
......@@ -502,7 +515,7 @@ retry:
stream = new HttpServerStream(new NetworkStream(tcpServerSocket, true), proxyServer.BufferPool, cancellationToken);
if ((externalProxy != null && externalProxy.ProxyType == ExternalProxyType.Http) && (isConnect || isHttps))
if (externalProxy != null && externalProxy.ProxyType == ExternalProxyType.Http && (isConnect || isHttps))
{
var authority = $"{remoteHostName}:{remotePort}".GetByteString();
var connectRequest = new ConnectRequest(authority)
......@@ -658,7 +671,7 @@ retry:
}
}
internal async Task Release(Task<TcpServerConnection>? connectionCreateTask, bool closeServerConnection)
internal async Task Release(Task<TcpServerConnection?>? connectionCreateTask, bool closeServerConnection)
{
if (connectionCreateTask == null)
{
......
......@@ -182,9 +182,9 @@ namespace Titanium.Web.Proxy
/// corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request
/// we will have the server connection immediately ready or in the process of getting ready.
/// If a server connection is available in cache then this prefetch task will immediately return with the available connection from cache.
/// Defaults to false.
/// Defaults to true.
/// </summary>
public bool EnableTcpServerConnectionPrefetch { get; set; } = false;
public bool EnableTcpServerConnectionPrefetch { get; set; } = true;
/// <summary>
/// Gets or sets a Boolean value that specifies whether server and client stream Sockets are using the Nagle algorithm.
......
......@@ -35,7 +35,7 @@ namespace Titanium.Web.Proxy
/// <param name="isHttps">Is HTTPS</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, HttpClientStream clientStream,
CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
Task<TcpServerConnection>? prefetchConnectionTask = null, bool isHttps = false)
Task<TcpServerConnection?>? prefetchConnectionTask = null, bool isHttps = false)
{
var connectRequest = connectArgs?.HttpClient.ConnectRequest;
......
......@@ -91,10 +91,10 @@ namespace Titanium.Web.Proxy
else
{
var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
var connection = await tcpConnectionFactory.GetServerConnection(this, httpsHostName, port,
var connection = (await tcpConnectionFactory.GetServerConnection(this, httpsHostName, port,
HttpHeader.VersionUnknown, false, null,
true, sessionArgs, UpStreamEndPoint,
UpStreamHttpsProxy, true, cancellationToken);
UpStreamHttpsProxy, true, false, cancellationToken))!;
try
{
......
......@@ -7,13 +7,16 @@ namespace Titanium.Web.Proxy.IntegrationTests.Setup
{
public class TestProxyServer : IDisposable
{
public ProxyServer ProxyServer { get; private set; }
public ProxyServer ProxyServer { get; }
public int ListeningPort => ProxyServer.ProxyEndPoints[0].Port;
public CertificateManager CertificateManager => ProxyServer.CertificateManager;
public TestProxyServer(bool isReverseProxy, ProxyServer upStreamProxy = null)
{
ProxyServer = new ProxyServer();
var explicitEndPoint = isReverseProxy ?
(ProxyEndPoint)new TransparentProxyEndPoint(IPAddress.Any, 0, true) :
new ExplicitProxyEndPoint(IPAddress.Any, 0, true);
......
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