Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit c75ad1a

Browse files
committed
prefetch fix
1 parent 37d1d6a commit c75ad1a

File tree

9 files changed

+55
-47
lines changed

9 files changed

+55
-47
lines changed

examples/Titanium.Web.Proxy.Examples.WindowsService/App.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<value>True</value>
4646
</setting>
4747
<setting name="EnableTcpServerConnectionPrefetch" serializeAs="String">
48-
<value>False</value>
48+
<value>True</value>
4949
</setting>
5050
<setting name="EnableWinAuth" serializeAs="String">
5151
<value>False</value>

examples/Titanium.Web.Proxy.Examples.WindowsService/Properties/Settings.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Titanium.Web.Proxy.Examples.WindowsService/Properties/Settings.settings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<Value Profile="(Default)">True</Value>
2222
</Setting>
2323
<Setting Name="EnableTcpServerConnectionPrefetch" Type="System.Boolean" Scope="Application">
24-
<Value Profile="(Default)">False</Value>
24+
<Value Profile="(Default)">True</Value>
2525
</Setting>
2626
<Setting Name="EnableWinAuth" Type="System.Boolean" Scope="Application">
2727
<Value Profile="(Default)">False</Value>

src/Titanium.Web.Proxy/ExplicitClientHandler.cs

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
3535

3636
var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken);
3737

38-
Task<TcpServerConnection>? prefetchConnectionTask = null;
38+
Task<TcpServerConnection?>? prefetchConnectionTask = null;
3939
bool closeServerConnection = false;
4040

4141
try
@@ -145,13 +145,16 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
145145
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
146146
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
147147
true, SslExtensions.Http2ProtocolAsList,
148-
true, cancellationToken);
148+
true, true, cancellationToken);
149149

150-
http2Supported = connection.NegotiatedApplicationProtocol ==
151-
SslApplicationProtocol.Http2;
150+
if (connection != null)
151+
{
152+
http2Supported = connection.NegotiatedApplicationProtocol ==
153+
SslApplicationProtocol.Http2;
152154

153-
// release connection back to pool instead of closing when connection pool is enabled.
154-
await tcpConnectionFactory.Release(connection, true);
155+
// release connection back to pool instead of closing when connection pool is enabled.
156+
await tcpConnectionFactory.Release(connection, true);
157+
}
155158
}
156159
catch (Exception)
157160
{
@@ -162,22 +165,11 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
162165

163166
if (EnableTcpServerConnectionPrefetch)
164167
{
165-
IPAddress[]? ipAddresses = null;
166-
try
167-
{
168-
// make sure the host can be resolved before creating the prefetch task
169-
ipAddresses = await Dns.GetHostAddressesAsync(connectArgs.HttpClient.Request.RequestUri.Host);
170-
}
171-
catch (SocketException) { }
172-
173-
if (ipAddresses != null && ipAddresses.Length > 0)
174-
{
175-
// don't pass cancellation token here
176-
// it could cause floating server connections when client exits
177-
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
178-
true, null, false,
179-
CancellationToken.None);
180-
}
168+
// don't pass cancellation token here
169+
// it could cause floating server connections when client exits
170+
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
171+
true, null, false, true,
172+
CancellationToken.None);
181173
}
182174

183175
string connectHostname = requestLine.RequestUri.GetString();
@@ -272,9 +264,9 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
272264
// create new connection to server.
273265
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
274266
// this connection should not be HTTPS.
275-
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
267+
var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
276268
true, null,
277-
true, cancellationToken);
269+
true, false, cancellationToken))!;
278270

279271
try
280272
{
@@ -349,9 +341,9 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
349341
throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
350342
}
351343

352-
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
344+
var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
353345
true, SslExtensions.Http2ProtocolAsList,
354-
true, cancellationToken);
346+
true, false, cancellationToken))!;
355347
try
356348
{
357349
#if NETSTANDARD2_1

src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ internal Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer,
154154
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
155155
}
156156

157-
return GetServerConnection(proxyServer, session, isConnect, applicationProtocols, noCache, cancellationToken);
157+
return GetServerConnection(proxyServer, session, isConnect, applicationProtocols, noCache, false, cancellationToken)!;
158158
}
159159

160160
/// <summary>
@@ -165,10 +165,11 @@ internal Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer,
165165
/// <param name="isConnect">Is this a CONNECT request.</param>
166166
/// <param name="applicationProtocols"></param>
167167
/// <param name="noCache">if set to <c>true</c> [no cache].</param>
168+
/// <param name="prefetch">if set to <c>true</c> [prefetch].</param>
168169
/// <param name="cancellationToken">The cancellation token for this async task.</param>
169170
/// <returns></returns>
170-
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, SessionEventArgsBase session, bool isConnect,
171-
List<SslApplicationProtocol>? applicationProtocols, bool noCache, CancellationToken cancellationToken)
171+
internal async Task<TcpServerConnection?> GetServerConnection(ProxyServer proxyServer, SessionEventArgsBase session, bool isConnect,
172+
List<SslApplicationProtocol>? applicationProtocols, bool noCache, bool prefetch, CancellationToken cancellationToken)
172173
{
173174
var customUpStreamProxy = session.CustomUpStreamProxy;
174175

@@ -208,7 +209,7 @@ internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxySe
208209
var upStreamEndPoint = session.HttpClient.UpStreamEndPoint ?? proxyServer.UpStreamEndPoint;
209210
var upStreamProxy = customUpStreamProxy ?? (isHttps ? proxyServer.UpStreamHttpsProxy : proxyServer.UpStreamHttpProxy);
210211
return await GetServerConnection(proxyServer, host, port, session.HttpClient.Request.HttpVersion, isHttps,
211-
applicationProtocols, isConnect, session, upStreamEndPoint, upStreamProxy, noCache, cancellationToken);
212+
applicationProtocols, isConnect, session, upStreamEndPoint, upStreamProxy, noCache, prefetch, cancellationToken);
212213
}
213214

214215
/// <summary>
@@ -225,12 +226,13 @@ internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxySe
225226
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
226227
/// <param name="externalProxy">The external proxy to make request via.</param>
227228
/// <param name="noCache">Not from cache/create new connection.</param>
229+
/// <param name="prefetch">if set to <c>true</c> [prefetch].</param>
228230
/// <param name="cancellationToken">The cancellation token for this async task.</param>
229231
/// <returns></returns>
230-
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, string remoteHostName, int remotePort,
232+
internal async Task<TcpServerConnection?> GetServerConnection(ProxyServer proxyServer, string remoteHostName, int remotePort,
231233
Version httpVersion, bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
232234
SessionEventArgsBase sessionArgs, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
233-
bool noCache, CancellationToken cancellationToken)
235+
bool noCache, bool prefetch, CancellationToken cancellationToken)
234236
{
235237
var sslProtocol = sessionArgs.ClientConnection.SslProtocol;
236238
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
@@ -259,7 +261,7 @@ internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxySe
259261
}
260262

261263
var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol,
262-
applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
264+
applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, prefetch, cancellationToken);
263265

264266
return connection;
265267
}
@@ -279,12 +281,13 @@ internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxySe
279281
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
280282
/// <param name="externalProxy">The external proxy to make request via.</param>
281283
/// <param name="cacheKey">The connection cache key</param>
284+
/// <param name="prefetch">if set to <c>true</c> [prefetch].</param>
282285
/// <param name="cancellationToken">The cancellation token for this async task.</param>
283286
/// <returns></returns>
284-
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
287+
private async Task<TcpServerConnection?> createServerConnection(string remoteHostName, int remotePort,
285288
Version httpVersion, bool isHttps, SslProtocols sslProtocol, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
286289
ProxyServer proxyServer, SessionEventArgsBase sessionArgs, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
287-
CancellationToken cancellationToken)
290+
bool prefetch, CancellationToken cancellationToken)
288291
{
289292
// deny connection to proxy end points to avoid infinite connection loop.
290293
if (Server.ProxyEndPoints.Any(x => x.Port == remotePort)
@@ -350,6 +353,11 @@ private async Task<TcpServerConnection> createServerConnection(string remoteHost
350353
var ipAddresses = await Dns.GetHostAddressesAsync(hostname);
351354
if (ipAddresses == null || ipAddresses.Length == 0)
352355
{
356+
if (prefetch)
357+
{
358+
return null;
359+
}
360+
353361
throw new Exception($"Could not resolve the hostname {hostname}");
354362
}
355363

@@ -486,10 +494,15 @@ private async Task<TcpServerConnection> createServerConnection(string remoteHost
486494
{
487495
sessionArgs.CustomUpStreamProxyUsed = newUpstreamProxy;
488496
sessionArgs.TimeLine["Retrying Upstream Proxy Connection"] = DateTime.UtcNow;
489-
return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
497+
return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, prefetch, cancellationToken);
490498
}
491499
}
492500

501+
if (prefetch)
502+
{
503+
return null;
504+
}
505+
493506
throw new Exception($"Could not establish connection to {hostname}", lastException);
494507
}
495508

@@ -502,7 +515,7 @@ private async Task<TcpServerConnection> createServerConnection(string remoteHost
502515

503516
stream = new HttpServerStream(new NetworkStream(tcpServerSocket, true), proxyServer.BufferPool, cancellationToken);
504517

505-
if ((externalProxy != null && externalProxy.ProxyType == ExternalProxyType.Http) && (isConnect || isHttps))
518+
if (externalProxy != null && externalProxy.ProxyType == ExternalProxyType.Http && (isConnect || isHttps))
506519
{
507520
var authority = $"{remoteHostName}:{remotePort}".GetByteString();
508521
var connectRequest = new ConnectRequest(authority)
@@ -658,7 +671,7 @@ internal async Task Release(TcpServerConnection connection, bool close = false)
658671
}
659672
}
660673

661-
internal async Task Release(Task<TcpServerConnection>? connectionCreateTask, bool closeServerConnection)
674+
internal async Task Release(Task<TcpServerConnection?>? connectionCreateTask, bool closeServerConnection)
662675
{
663676
if (connectionCreateTask == null)
664677
{

src/Titanium.Web.Proxy/ProxyServer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerNam
182182
/// corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request
183183
/// we will have the server connection immediately ready or in the process of getting ready.
184184
/// If a server connection is available in cache then this prefetch task will immediately return with the available connection from cache.
185-
/// Defaults to false.
185+
/// Defaults to true.
186186
/// </summary>
187-
public bool EnableTcpServerConnectionPrefetch { get; set; } = false;
187+
public bool EnableTcpServerConnectionPrefetch { get; set; } = true;
188188

189189
/// <summary>
190190
/// Gets or sets a Boolean value that specifies whether server and client stream Sockets are using the Nagle algorithm.

src/Titanium.Web.Proxy/RequestHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public partial class ProxyServer
3535
/// <param name="isHttps">Is HTTPS</param>
3636
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, HttpClientStream clientStream,
3737
CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
38-
Task<TcpServerConnection>? prefetchConnectionTask = null, bool isHttps = false)
38+
Task<TcpServerConnection?>? prefetchConnectionTask = null, bool isHttps = false)
3939
{
4040
var connectRequest = connectArgs?.HttpClient.ConnectRequest;
4141

src/Titanium.Web.Proxy/TransparentClientHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ private async Task handleClient(TransparentBaseProxyEndPoint endPoint, TcpClient
9191
else
9292
{
9393
var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
94-
var connection = await tcpConnectionFactory.GetServerConnection(this, httpsHostName, port,
94+
var connection = (await tcpConnectionFactory.GetServerConnection(this, httpsHostName, port,
9595
HttpHeader.VersionUnknown, false, null,
9696
true, sessionArgs, UpStreamEndPoint,
97-
UpStreamHttpsProxy, true, cancellationToken);
97+
UpStreamHttpsProxy, true, false, cancellationToken))!;
9898

9999
try
100100
{

tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestProxyServer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ namespace Titanium.Web.Proxy.IntegrationTests.Setup
77
{
88
public class TestProxyServer : IDisposable
99
{
10-
public ProxyServer ProxyServer { get; private set; }
10+
public ProxyServer ProxyServer { get; }
11+
1112
public int ListeningPort => ProxyServer.ProxyEndPoints[0].Port;
13+
1214
public CertificateManager CertificateManager => ProxyServer.CertificateManager;
1315

1416
public TestProxyServer(bool isReverseProxy, ProxyServer upStreamProxy = null)
1517
{
1618
ProxyServer = new ProxyServer();
19+
1720
var explicitEndPoint = isReverseProxy ?
1821
(ProxyEndPoint)new TransparentProxyEndPoint(IPAddress.Any, 0, true) :
1922
new ExplicitProxyEndPoint(IPAddress.Any, 0, true);

0 commit comments

Comments
 (0)