Unverified Commit 02ea4aec authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #445 from justcoding121/master

Use polly for retries
parents 17a1aabe fc3a9308
...@@ -3,6 +3,7 @@ using System.Collections.Concurrent; ...@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
...@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
public class ProxyTestController public class ProxyTestController
{ {
private readonly object lockObj = new object(); private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
private readonly ProxyServer proxyServer; private readonly ProxyServer proxyServer;
...@@ -23,34 +24,41 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -23,34 +24,41 @@ namespace Titanium.Web.Proxy.Examples.Basic
public ProxyTestController() public ProxyTestController()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
//proxyServer.EnableConnectionPool = false; proxyServer.EnableConnectionPool = true;
// generate root certificate without storing it in file system // generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false); //proxyServer.CertificateManager.CreateRootCertificate(false);
//proxyServer.CertificateManager.TrustRootCertificate(); //proxyServer.CertificateManager.TrustRootCertificate();
//proxyServer.CertificateManager.TrustRootCertificateAsAdmin(); //proxyServer.CertificateManager.TrustRootCertificateAsAdmin();
proxyServer.ExceptionFunc = exception => proxyServer.ExceptionFunc = async exception =>
{ {
lock (lockObj) await @lock.WaitAsync();
try
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
if (exception is ProxyHttpException phex)
{
Console.WriteLine(exception.Message + ": " + phex.InnerException?.Message);
}
else
{
Console.WriteLine(exception.Message);
}
Console.ForegroundColor = color;
}
finally
{ {
var color = Console.ForegroundColor; @lock.Release();
Console.ForegroundColor = ConsoleColor.Red;
if (exception is ProxyHttpException phex)
{
Console.WriteLine(exception.Message + ": " + phex.InnerException?.Message);
}
else
{
Console.WriteLine(exception.Message);
}
Console.ForegroundColor = color;
} }
}; };
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
proxyServer.CertificateManager.SaveFakeCertificates = true; proxyServer.CertificateManager.SaveFakeCertificates = true;
// optionally set the Certificate Engine // optionally set the Certificate Engine
// Under Mono or Non-Windows runtimes only BouncyCastle will be supported // Under Mono or Non-Windows runtimes only BouncyCastle will be supported
//proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle; //proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
...@@ -122,7 +130,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -122,7 +130,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection; proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
proxyServer.Stop(); proxyServer.Stop();
// remove the generated certificates // remove the generated certificates
//proxyServer.CertificateManager.RemoveTrustedRootCertificates(); //proxyServer.CertificateManager.RemoveTrustedRootCertificates();
} }
...@@ -130,7 +138,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -130,7 +138,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{ {
string hostname = e.WebSession.Request.RequestUri.Host; string hostname = e.WebSession.Request.RequestUri.Host;
WriteToConsole("Tunnel to: " + hostname); await WriteToConsole("Tunnel to: " + hostname);
if (hostname.Contains("dropbox.com")) if (hostname.Contains("dropbox.com"))
{ {
...@@ -148,8 +156,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -148,8 +156,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
// intecept & cancel redirect or update requests // intecept & cancel redirect or update requests
private async Task OnRequest(object sender, SessionEventArgs e) private async Task OnRequest(object sender, SessionEventArgs e)
{ {
WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount); await WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
WriteToConsole(e.WebSession.Request.Url); await WriteToConsole(e.WebSession.Request.Url);
// store it in the UserData property // store it in the UserData property
// It can be a simple integer, Guid, or any type // It can be a simple integer, Guid, or any type
...@@ -187,19 +195,19 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -187,19 +195,19 @@ namespace Titanium.Web.Proxy.Examples.Basic
} }
// Modify response // Modify response
private void MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e) private async Task MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
{ {
var session = (SessionEventArgs)sender; var session = (SessionEventArgs)sender;
WriteToConsole("Multipart form data headers:"); await WriteToConsole("Multipart form data headers:");
foreach (var header in e.Headers) foreach (var header in e.Headers)
{ {
WriteToConsole(header.ToString()); await WriteToConsole(header.ToString());
} }
} }
private async Task OnResponse(object sender, SessionEventArgs e) private async Task OnResponse(object sender, SessionEventArgs e)
{ {
WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount); await WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
string ext = System.IO.Path.GetExtension(e.WebSession.Request.RequestUri.AbsolutePath); string ext = System.IO.Path.GetExtension(e.WebSession.Request.RequestUri.AbsolutePath);
...@@ -271,12 +279,18 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -271,12 +279,18 @@ namespace Titanium.Web.Proxy.Examples.Basic
return Task.FromResult(0); return Task.FromResult(0);
} }
private void WriteToConsole(string message) private async Task WriteToConsole(string message)
{ {
lock (lockObj) await @lock.WaitAsync();
try
{ {
Console.WriteLine(message); Console.WriteLine(message);
} }
finally
{
@lock.Release();
}
} }
///// <summary> ///// <summary>
......
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
...@@ -140,17 +141,17 @@ namespace Titanium.Web.Proxy ...@@ -140,17 +141,17 @@ 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
var connection = await getServerConnection(connectArgs, true, var connection = await getServerConnection(connectArgs, true,
SslExtensions.Http2ProtocolAsList, cancellationToken); SslExtensions.Http2ProtocolAsList, true, cancellationToken);
http2Supported = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2; http2Supported = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;
//release connection back to pool intead of closing when connection pool is enabled. //release connection back to pool intead of closing when connection pool is enabled.
await tcpConnectionFactory.Release(connection); await tcpConnectionFactory.Release(connection, true);
} }
SslStream sslStream = null; SslStream sslStream = null;
prefetchConnectionTask = getServerConnection(connectArgs, true, prefetchConnectionTask = getServerConnection(connectArgs, true,
null, cancellationToken); null, false, cancellationToken);
try try
{ {
sslStream = new SslStream(clientStream); sslStream = new SslStream(clientStream);
...@@ -209,8 +210,8 @@ namespace Titanium.Web.Proxy ...@@ -209,8 +210,8 @@ namespace Titanium.Web.Proxy
// create new connection to server. // create new connection to server.
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
// this connection should not be HTTPS. // this connection should not be HTTPS.
var connection = await getServerConnection(connectArgs, true, var connection = await getServerConnection(connectArgs,
null, cancellationToken); true, SslExtensions.Http2ProtocolAsList, true, cancellationToken);
try try
{ {
...@@ -224,10 +225,9 @@ namespace Titanium.Web.Proxy ...@@ -224,10 +225,9 @@ namespace Titanium.Web.Proxy
try try
{ {
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken); await clientStream.ReadAsync(data, 0, available, cancellationToken);
await connection.StreamWriter.WriteAsync(data, 0, available, true, // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
cancellationToken); await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
} }
finally finally
{ {
...@@ -235,8 +235,7 @@ namespace Titanium.Web.Proxy ...@@ -235,8 +235,7 @@ namespace Titanium.Web.Proxy
} }
} }
var serverHelloInfo = var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);
await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);
((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo; ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
} }
...@@ -244,11 +243,14 @@ namespace Titanium.Web.Proxy ...@@ -244,11 +243,14 @@ namespace Titanium.Web.Proxy
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc); connectArgs.CancellationTokenSource, ExceptionFunc);
} }
finally finally
{ {
await tcpConnectionFactory.Release(connection, true); await tcpConnectionFactory.Release(connection, true);
} }
calledRequestHandler = true;
return; return;
} }
} }
...@@ -278,22 +280,22 @@ namespace Titanium.Web.Proxy ...@@ -278,22 +280,22 @@ namespace Titanium.Web.Proxy
throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received"); throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
} }
// create new connection var connection = await getServerConnection(connectArgs, true,
var connection = await getServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList, SslExtensions.Http2ProtocolAsList, true,
cancellationToken); cancellationToken);
try try
{ {
await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken); await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken); await connection.StreamWriter.WriteLineAsync(cancellationToken);
await connection.StreamWriter.WriteLineAsync("SM", cancellationToken); await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken); await connection.StreamWriter.WriteLineAsync(cancellationToken);
#if NETCOREAPP2_1 #if NETCOREAPP2_1
await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize, await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc); connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif #endif
} }
finally finally
{ {
...@@ -301,6 +303,7 @@ namespace Titanium.Web.Proxy ...@@ -301,6 +303,7 @@ namespace Titanium.Web.Proxy
} }
} }
} }
calledRequestHandler = true; calledRequestHandler = true;
// Now create the request // Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
...@@ -328,19 +331,19 @@ namespace Titanium.Web.Proxy ...@@ -328,19 +331,19 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
if (!calledRequestHandler
&& prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection);
}
clientStream.Dispose(); clientStream.Dispose();
if (!cancellationTokenSource.IsCancellationRequested) if (!cancellationTokenSource.IsCancellationRequested)
{ {
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
} }
if (!calledRequestHandler
&& prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection);
}
} }
} }
} }
......
...@@ -81,18 +81,19 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -81,18 +81,19 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="proxyServer">The current ProxyServer instance.</param> /// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param> /// <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="externalProxy">The external proxy to make request via.</param>
/// <param name="noCache">Not from cache/create new connection.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
internal async Task<TcpServerConnection> GetClient(string remoteHostName, int remotePort, internal async Task<TcpServerConnection> GetClient(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect, Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy, ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken) bool noCache, CancellationToken cancellationToken)
{ {
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort, var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
isHttps, applicationProtocols, isHttps, applicationProtocols,
proxyServer, upStreamEndPoint, externalProxy); proxyServer, upStreamEndPoint, externalProxy);
if (proxyServer.EnableConnectionPool) if (proxyServer.EnableConnectionPool && !noCache)
{ {
if (cache.TryGetValue(cacheKey, out var existingConnections)) if (cache.TryGetValue(cacheKey, out var existingConnections))
{ {
......
...@@ -7,6 +7,7 @@ using System.Security.Authentication; ...@@ -7,6 +7,7 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Polly;
using StreamExtended; using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -123,6 +124,9 @@ namespace Titanium.Web.Proxy ...@@ -123,6 +124,9 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private SystemProxyManager systemProxySettingsManager { get; } private SystemProxyManager systemProxySettingsManager { get; }
//Number of exception retries when connection pool is enabled.
private int retries => EnableConnectionPool ? MaxCachedConnections + 1 : 0;
/// <summary> /// <summary>
/// Is the proxy currently running? /// Is the proxy currently running?
/// </summary> /// </summary>
...@@ -197,7 +201,7 @@ namespace Titanium.Web.Proxy ...@@ -197,7 +201,7 @@ namespace Titanium.Web.Proxy
/// Realm used during Proxy Basic Authentication. /// Realm used during Proxy Basic Authentication.
/// </summary> /// </summary>
public string ProxyRealm { get; set; } = "TitaniumProxy"; public string ProxyRealm { get; set; } = "TitaniumProxy";
/// <summary> /// <summary>
/// List of supported Ssl versions. /// List of supported Ssl versions.
/// </summary> /// </summary>
...@@ -252,7 +256,8 @@ namespace Titanium.Web.Proxy ...@@ -252,7 +256,8 @@ namespace Titanium.Web.Proxy
public ExceptionHandler ExceptionFunc public ExceptionHandler ExceptionFunc
{ {
get => exceptionFunc ?? defaultExceptionFunc; get => exceptionFunc ?? defaultExceptionFunc;
set { set
{
exceptionFunc = value; exceptionFunc = value;
CertificateManager.ExceptionFunc = value; CertificateManager.ExceptionFunc = value;
} }
...@@ -274,7 +279,7 @@ namespace Titanium.Web.Proxy ...@@ -274,7 +279,7 @@ namespace Titanium.Web.Proxy
{ {
Stop(); Stop();
} }
CertificateManager?.Dispose(); CertificateManager?.Dispose();
BufferPool?.Dispose(); BufferPool?.Dispose();
} }
...@@ -800,5 +805,26 @@ namespace Titanium.Web.Proxy ...@@ -800,5 +805,26 @@ namespace Titanium.Web.Proxy
await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc); await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
} }
} }
/// <summary>
/// Connection retry policy when using connection pool.
/// </summary>
private Policy retryPolicy<T>() where T : Exception
{
return Policy.Handle<T>()
.RetryAsync(retries,
onRetryAsync: async (ex, i, context) =>
{
if (context.ContainsKey("connection"))
{
//close connection on error
var connection = (TcpServerConnection)context["connection"];
await tcpConnectionFactory.Release(connection, true);
context.Remove("connection");
}
});
}
} }
} }
...@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy ...@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy
var cancellationToken = cancellationTokenSource.Token; var cancellationToken = cancellationTokenSource.Token;
var prefetchTask = prefetchConnectionTask; var prefetchTask = prefetchConnectionTask;
TcpServerConnection serverConnection = null; TcpServerConnection connection = null;
bool closeServerConnection = false; bool closeServerConnection = false;
try try
...@@ -186,70 +186,57 @@ namespace Titanium.Web.Proxy ...@@ -186,70 +186,57 @@ namespace Titanium.Web.Proxy
} }
//If prefetch task is available. //If prefetch task is available.
if (serverConnection == null && prefetchTask != null) if (connection == null && prefetchTask != null)
{ {
serverConnection = await prefetchTask; connection = await prefetchTask;
prefetchTask = null; prefetchTask = null;
} }
// create a new connection if cache key changes. // create a new connection if cache key changes.
// only gets hit when connection pool is disabled. // only gets hit when connection pool is disabled.
// or when prefetch task has a unexpectedly different connection. // or when prefetch task has a unexpectedly different connection.
if (serverConnection != null if (connection != null
&& (await getConnectionCacheKey(args, && (await getConnectionCacheKey(args,
clientConnection.NegotiatedApplicationProtocol) clientConnection.NegotiatedApplicationProtocol)
!= serverConnection.CacheKey)) != connection.CacheKey))
{ {
await tcpConnectionFactory.Release(serverConnection); await tcpConnectionFactory.Release(connection);
serverConnection = null; connection = null;
} }
//create var contextData = new Dictionary<string, object>();
if (serverConnection == null) if (connection != null)
{ {
serverConnection = await getServerConnection(args, false, contextData.Add("connection", connection);
clientConnection.NegotiatedApplicationProtocol, cancellationToken);
} }
//for connection pool retry fails until cache is exhausted //for connection pool retry fails until cache is exhausted
int attempt = 0; await retryPolicy<ServerConnectionException>().ExecuteAsync(async (context) =>
while (attempt < MaxCachedConnections + 1)
{ {
try
{ connection = context.ContainsKey("connection")?
// if upgrading to websocket then relay the request without reading the contents (TcpServerConnection)context["connection"]
if (request.UpgradeToWebSocket) : await getServerConnection(args, false,
{ clientConnection.NegotiatedApplicationProtocol,
await handleWebSocketUpgrade(httpCmd, args, request, false, cancellationToken);
response, clientStream, clientStreamWriter,
serverConnection, cancellationTokenSource, cancellationToken); context["connection"] = connection;
closeServerConnection = true;
return; // if upgrading to websocket then relay the request without reading the contents
} if (request.UpgradeToWebSocket)
// construct the web request that we are going to issue on behalf of the client.
await handleHttpSessionRequestInternal(serverConnection, args);
}
//connection pool retry
catch (ServerConnectionException)
{ {
attempt++; await handleWebSocketUpgrade(httpCmd, args, request,
if (!EnableConnectionPool || attempt == MaxCachedConnections + 1) response, clientStream, clientStreamWriter,
{ connection, cancellationTokenSource, cancellationToken);
throw; closeServerConnection = true;
} return;
//get new connection from pool
await tcpConnectionFactory.Release(serverConnection, true);
serverConnection = null;
serverConnection = await getServerConnection(args, false,
clientConnection.NegotiatedApplicationProtocol, cancellationToken);
continue;
} }
break; // construct the web request that we are going to issue on behalf of the client.
} await handleHttpSessionRequestInternal(connection, args);
}, contextData);
//user requested //user requested
if (args.WebSession.CloseServerConnection) if (args.WebSession.CloseServerConnection)
...@@ -275,8 +262,8 @@ namespace Titanium.Web.Proxy ...@@ -275,8 +262,8 @@ namespace Titanium.Web.Proxy
//between sessions without using it. //between sessions without using it.
if (EnableConnectionPool) if (EnableConnectionPool)
{ {
await tcpConnectionFactory.Release(serverConnection); await tcpConnectionFactory.Release(connection);
serverConnection = null; connection = null;
} }
} }
...@@ -300,10 +287,10 @@ namespace Titanium.Web.Proxy ...@@ -300,10 +287,10 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
await tcpConnectionFactory.Release(serverConnection, await tcpConnectionFactory.Release(connection,
closeServerConnection); closeServerConnection);
if (prefetchTask!=null) if (prefetchTask != null)
{ {
await tcpConnectionFactory.Release(await prefetchTask, await tcpConnectionFactory.Release(await prefetchTask,
closeServerConnection); closeServerConnection);
...@@ -515,7 +502,7 @@ namespace Titanium.Web.Proxy ...@@ -515,7 +502,7 @@ namespace Titanium.Web.Proxy
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
private Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect, private Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
SslApplicationProtocol applicationProtocol, CancellationToken cancellationToken) SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken)
{ {
List<SslApplicationProtocol> applicationProtocols = null; List<SslApplicationProtocol> applicationProtocols = null;
if (applicationProtocol != default) if (applicationProtocol != default)
...@@ -523,7 +510,7 @@ namespace Titanium.Web.Proxy ...@@ -523,7 +510,7 @@ namespace Titanium.Web.Proxy
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
} }
return getServerConnection(args, isConnect, applicationProtocols, cancellationToken); return getServerConnection(args, isConnect, applicationProtocols, noCache, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -535,7 +522,7 @@ namespace Titanium.Web.Proxy ...@@ -535,7 +522,7 @@ namespace Titanium.Web.Proxy
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
private async Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect, private async Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
List<SslApplicationProtocol> applicationProtocols, CancellationToken cancellationToken) List<SslApplicationProtocol> applicationProtocols, bool noCache, CancellationToken cancellationToken)
{ {
ExternalProxy customUpStreamProxy = null; ExternalProxy customUpStreamProxy = null;
...@@ -554,7 +541,7 @@ namespace Titanium.Web.Proxy ...@@ -554,7 +541,7 @@ namespace Titanium.Web.Proxy
isHttps, applicationProtocols, isConnect, isHttps, applicationProtocols, isConnect,
this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint, this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy), customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy),
cancellationToken); noCache, cancellationToken);
} }
/// <summary> /// <summary>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.2" />
<PackageReference Include="StreamExtended" Version="1.0.175-beta" /> <PackageReference Include="StreamExtended" Version="1.0.175-beta" />
</ItemGroup> </ItemGroup>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.175-beta" /> <dependency id="StreamExtended" version="1.0.175-beta" />
<dependency id="Portable.BouncyCastle" version="1.8.2" /> <dependency id="Portable.BouncyCastle" version="1.8.2" />
<dependency id="Polly" version="6.0.1"/>
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>
......
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
...@@ -63,8 +64,8 @@ namespace Titanium.Web.Proxy ...@@ -63,8 +64,8 @@ namespace Titanium.Web.Proxy
if (endPoint.DecryptSsl && args.DecryptSsl) if (endPoint.DecryptSsl && args.DecryptSsl)
{ {
prefetchConnectionTask = tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port, prefetchConnectionTask = tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
null, true, null, null, true, null, false, this,
false, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken); UpStreamEndPoint, UpStreamHttpsProxy, false, cancellationToken);
SslStream sslStream = null; SslStream sslStream = null;
...@@ -93,25 +94,24 @@ namespace Titanium.Web.Proxy ...@@ -93,25 +94,24 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
// create new connection
var connection = await tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port, var connection = await tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
null, false, null, null, false, null,
true, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken); true, this, UpStreamEndPoint, UpStreamHttpsProxy, true, cancellationToken);
try try
{ {
var serverStream = connection.Stream; CustomBufferedStream serverStream = null;
int available = clientStream.Available; int available = clientStream.Available;
if (available > 0) if (available > 0)
{ {
// send the buffered data // send the buffered data
var data = BufferPool.GetBuffer(BufferSize); var data = BufferPool.GetBuffer(BufferSize);
try try
{ {
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken); await clientStream.ReadAsync(data, 0, available, cancellationToken);
serverStream = connection.Stream;
await serverStream.WriteAsync(data, 0, available, cancellationToken); await serverStream.WriteAsync(data, 0, available, cancellationToken);
await serverStream.FlushAsync(cancellationToken); await serverStream.FlushAsync(cancellationToken);
} }
......
...@@ -3,4 +3,5 @@ ...@@ -3,4 +3,5 @@
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" />
<package id="StreamExtended" version="1.0.175-beta" targetFramework="net45" /> <package id="StreamExtended" version="1.0.175-beta" targetFramework="net45" />
<package id="Polly" version="6.0.1" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
...@@ -450,8 +450,8 @@ Defaults to false.</p> ...@@ -450,8 +450,8 @@ Defaults to false.</p>
<a id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool*"></a> <a id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool">EnableConnectionPool</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool">EnableConnectionPool</h4>
<div class="markdown level1 summary"><p>Should we enable server connection pool? <div class="markdown level1 summary"><p>Should we enable experimental server connection pool?
Defaults to true.</p> Defaults to disable.</p>
</div> </div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5> <h5 class="decalaration">Declaration</h5>
......
This diff is collapsed.
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