From 7b8481668f95746fc80d5f724952451bf6bd32a8 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 01:20:01 +0200 Subject: [PATCH 01/17] reuse buffers, missing dispose added --- .../EventArguments/SessionEventArgs.cs | 1 + .../Helpers/CustomBinaryReader.cs | 9 ++++++- Titanium.Web.Proxy/Http/HttpWebClient.cs | 10 ++++++- Titanium.Web.Proxy/RequestHandler.cs | 26 +++++++++---------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index f70bf8550..6af79368a 100644 --- a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -522,6 +522,7 @@ public async Task Respond(Response response) /// public void Dispose() { + WebSession.Dispose(); } } } diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index 6d10c3d91..32bce44f2 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Text; @@ -18,10 +19,15 @@ internal class CustomBinaryReader : IDisposable private readonly byte[] staticBuffer; private readonly Encoding encoding; + private static readonly ConcurrentQueue buffers = new ConcurrentQueue(); + internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize) { this.stream = stream; - staticBuffer = new byte[bufferSize]; + if (!buffers.TryDequeue(out staticBuffer) || staticBuffer.Length != bufferSize) + { + staticBuffer = new byte[bufferSize]; + } this.bufferSize = bufferSize; @@ -148,6 +154,7 @@ internal async Task ReadBytesAsync(long totalBytesToRead) public void Dispose() { + buffers.Enqueue(staticBuffer); } private void ResizeBuffer(ref byte[] buffer, long size) diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index 46aa379c7..3ce713375 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http /// /// Used to communicate with the server over HTTP(S) /// - public class HttpWebClient + public class HttpWebClient : IDisposable { /// /// Connection to server @@ -208,5 +208,13 @@ internal async Task ReceiveResponse() //Read the response headers in to unique and non-unique header collections await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.NonUniqueResponseHeaders, Response.ResponseHeaders); } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + ServerConnection.Dispose(); + } } } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 624c71546..fbd18d823 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -44,7 +44,6 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli if (string.IsNullOrEmpty(httpCmd)) { - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; } @@ -99,7 +98,6 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false) { - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; } @@ -111,7 +109,8 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli { sslStream = new SslStream(clientStream, true); - var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(httpRemoteUri.Host, false); + var certificate = endPoint.GenericCertificate ?? + CertificateManager.CreateCertificate(httpRemoteUri.Host, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, @@ -119,14 +118,13 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, //HTTPS server created - we can now decrypt the client's traffic clientStream = new CustomBufferedStream(sslStream, BufferSize); + clientStreamReader.Dispose(); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; + clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; } catch { sslStream?.Dispose(); - - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; } @@ -141,24 +139,25 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, //write back successfull CONNECT response await WriteConnectResponse(clientStreamWriter, version); - await TcpHelper.SendRaw(this, + await TcpHelper.SendRaw(this, httpRemoteUri.Host, httpRemoteUri.Port, null, version, null, - false, + false, clientStream, tcpConnectionFactory); - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; } //Now create the request await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, - httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders); + httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, + connectRequestHeaders); } catch (Exception) { - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, null); + } + finally + { + Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } } @@ -544,7 +543,6 @@ await TcpHelper.SendRaw(this, break; } } - } /// From f089accb1a80db70c6a64654b51e52e38af225e8 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 01:29:36 +0200 Subject: [PATCH 02/17] ServerConnection?.Dispose(); Call removed. --- Titanium.Web.Proxy/Http/HttpWebClient.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index 3ce713375..03bb2ae80 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -214,7 +214,6 @@ internal async Task ReceiveResponse() /// public void Dispose() { - ServerConnection.Dispose(); } } } From e6d8eb24e93914774134261550ca8c2fd359bfd0 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 10:19:39 +0200 Subject: [PATCH 03/17] connection count locking + usings sorted --- .../ProxyTestController.cs | 10 +++++----- .../Properties/AssemblyInfo.cs | 1 - .../SslTests.cs | 9 +++++---- .../CertificateManagerTests.cs | 7 +++---- Titanium.Web.Proxy.sln.DotSettings | 5 ++++- .../EventArguments/SessionEventArgs.cs | 10 +++++----- Titanium.Web.Proxy/Helpers/SystemProxy.cs | 8 ++++---- Titanium.Web.Proxy/Helpers/Tcp.cs | 7 ++----- Titanium.Web.Proxy/Http/Request.cs | 2 +- Titanium.Web.Proxy/Http/Response.cs | 6 +++--- .../Network/CertificateManager.cs | 13 +++++++------ .../Network/Tcp/TcpConnectionFactory.cs | 11 ++++++----- Titanium.Web.Proxy/ProxyServer.cs | 17 ++++++++++------- Titanium.Web.Proxy/RequestHandler.cs | 10 +++++----- Titanium.Web.Proxy/ResponseHandler.cs | 13 +++++++------ 15 files changed, 67 insertions(+), 62 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index fe9f16c1c..8f8f7626f 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Net; +using System.Net.Security; using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Models; @@ -47,18 +47,18 @@ public void StartProxy() //Exclude Https addresses you don't want to proxy //Useful for clients that use certificate pinning //for example google.com and dropbox.com - ExcludedHttpsHostNameRegex = new List() { "dropbox.com" } + ExcludedHttpsHostNameRegex = new List { "dropbox.com" } //Include Https addresses you want to proxy (others will be excluded) //for example github.com - // IncludedHttpsHostNameRegex = new List() { "github.com" } + //IncludedHttpsHostNameRegex = new List { "github.com" } //You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown //Use self-issued generic certificate on all https requests //Optimizes performance by not creating a certificate for each https-enabled domain //Useful when certificate trust is not required by proxy clients - // GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password") + //GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password") }; //An explicit endpoint is where the client knows about the existence of a proxy @@ -189,7 +189,7 @@ public async Task OnResponse(object sender, SessionEventArgs e) public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) { //set IsValid to true/false based on Certificate Errors - if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None) + if (e.SslPolicyErrors == SslPolicyErrors.None) { e.IsValid = true; } diff --git a/Tests/Titanium.Web.Proxy.IntegrationTests/Properties/AssemblyInfo.cs b/Tests/Titanium.Web.Proxy.IntegrationTests/Properties/AssemblyInfo.cs index e2f3d0432..2b5ed878b 100644 --- a/Tests/Titanium.Web.Proxy.IntegrationTests/Properties/AssemblyInfo.cs +++ b/Tests/Titanium.Web.Proxy.IntegrationTests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs b/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs index c9f07f40c..83b87ae5b 100644 --- a/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs +++ b/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs @@ -1,11 +1,12 @@ using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Diagnostics; using System.Net; +using System.Net.Http; +using System.Net.Security; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Models; -using System.Net.Http; -using System.Diagnostics; namespace Titanium.Web.Proxy.IntegrationTests { @@ -103,7 +104,7 @@ public async Task OnResponse(object sender, SessionEventArgs e) public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) { //set IsValid to true/false based on Certificate Errors - if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None) + if (e.SslPolicyErrors == SslPolicyErrors.None) { e.IsValid = true; } diff --git a/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs b/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs index 651142271..5d37a623a 100644 --- a/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs +++ b/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs @@ -1,8 +1,8 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Titanium.Web.Proxy.Network; -using System.Threading.Tasks; -using System.Collections.Generic; namespace Titanium.Web.Proxy.UnitTests { @@ -10,8 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests public class CertificateManagerTests { private static readonly string[] hostNames - = new string[] { "facebook.com", "youtube.com", "google.com", - "bing.com", "yahoo.com"}; + = { "facebook.com", "youtube.com", "google.com", "bing.com", "yahoo.com"}; private readonly Random random = new Random(); diff --git a/Titanium.Web.Proxy.sln.DotSettings b/Titanium.Web.Proxy.sln.DotSettings index 1fc658854..6f43e6a16 100644 --- a/Titanium.Web.Proxy.sln.DotSettings +++ b/Titanium.Web.Proxy.sln.DotSettings @@ -5,4 +5,7 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> \ No newline at end of file + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + True + True + True \ No newline at end of file diff --git a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index 6af79368a..a4fbfee94 100644 --- a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; using System.Text; -using Titanium.Web.Proxy.Exceptions; +using System.Threading.Tasks; using Titanium.Web.Proxy.Decompression; +using Titanium.Web.Proxy.Exceptions; +using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http.Responses; -using Titanium.Web.Proxy.Extensions; -using System.Threading.Tasks; -using Titanium.Web.Proxy.Network; -using System.Net; using Titanium.Web.Proxy.Models; +using Titanium.Web.Proxy.Network; namespace Titanium.Web.Proxy.EventArguments { diff --git a/Titanium.Web.Proxy/Helpers/SystemProxy.cs b/Titanium.Web.Proxy/Helpers/SystemProxy.cs index ec348319f..1edeb91b9 100644 --- a/Titanium.Web.Proxy/Helpers/SystemProxy.cs +++ b/Titanium.Web.Proxy/Helpers/SystemProxy.cs @@ -1,9 +1,9 @@ using System; -using System.Runtime.InteropServices; -using Microsoft.Win32; -using System.Text.RegularExpressions; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using Microsoft.Win32; // Helper classes for setting system proxy settings namespace Titanium.Web.Proxy.Helpers @@ -84,7 +84,7 @@ private void SetProxy(string hostname, int port, ProxyProtocolType protocolType) var exisitingContent = reg.GetValue("ProxyServer") as string; var existingSystemProxyValues = GetSystemProxyValues(exisitingContent); existingSystemProxyValues.RemoveAll(x => protocolType == ProxyProtocolType.Https ? x.IsHttps : !x.IsHttps); - existingSystemProxyValues.Add(new HttpSystemProxyValue() + existingSystemProxyValues.Add(new HttpSystemProxyValue { HostName = hostname, IsHttps = protocolType == ProxyProtocolType.Https, diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index e64da17b6..226a26e00 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -3,10 +3,9 @@ using System.IO; using System.Linq; using System.Net.NetworkInformation; -using System.Net.Security; using System.Runtime.InteropServices; -using System.Security.Authentication; using System.Text; +using System.Threading; using System.Threading.Tasks; using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Models; @@ -15,8 +14,6 @@ namespace Titanium.Web.Proxy.Helpers { - using System.Net; - internal enum IpVersion { Ipv4 = 1, @@ -232,7 +229,7 @@ internal static async Task SendRaw(ProxyServer server, finally { tcpConnection.Dispose(); - server.ServerConnectionCount--; + Interlocked.Decrement(ref server.serverConnectionCount); } } } diff --git a/Titanium.Web.Proxy/Http/Request.cs b/Titanium.Web.Proxy/Http/Request.cs index 949cd5570..1af6295b8 100644 --- a/Titanium.Web.Proxy/Http/Request.cs +++ b/Titanium.Web.Proxy/Http/Request.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Text; -using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Extensions; +using Titanium.Web.Proxy.Models; namespace Titanium.Web.Proxy.Http { diff --git a/Titanium.Web.Proxy/Http/Response.cs b/Titanium.Web.Proxy/Http/Response.cs index 08163a4b9..0393f4875 100644 --- a/Titanium.Web.Proxy/Http/Response.cs +++ b/Titanium.Web.Proxy/Http/Response.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Text; -using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Extensions; -using System; +using Titanium.Web.Proxy.Models; namespace Titanium.Web.Proxy.Http { diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index d3e7547de..1fb7953bc 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -1,12 +1,13 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using System.Linq; -using System.Collections.Concurrent; -using System.IO; -using Titanium.Web.Proxy.Network.Certificate; using Titanium.Web.Proxy.Helpers; +using Titanium.Web.Proxy.Network.Certificate; namespace Titanium.Web.Proxy.Network { @@ -132,12 +133,12 @@ private void ClearRootCertificate() private string GetRootCertificatePath() { - var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location; + var assemblyLocation = Assembly.GetExecutingAssembly().Location; // dynamically loaded assemblies returns string.Empty location if (assemblyLocation == string.Empty) { - assemblyLocation = System.Reflection.Assembly.GetEntryAssembly().Location; + assemblyLocation = Assembly.GetEntryAssembly().Location; } var path = Path.GetDirectoryName(assemblyLocation); diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 2bef02038..640d52c74 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -1,13 +1,14 @@ using System; +using System.IO; +using System.Linq; +using System.Net.Security; using System.Net.Sockets; using System.Text; +using System.Threading; using System.Threading.Tasks; -using System.IO; -using System.Net.Security; +using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Models; -using System.Linq; -using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Shared; namespace Titanium.Web.Proxy.Network.Tcp @@ -127,7 +128,7 @@ internal async Task CreateClient(ProxyServer server, client.LingerState = new LingerOption(true, 0); - server.ServerConnectionCount++; + Interlocked.Increment(ref server.serverConnectionCount); return new TcpConnection { diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index de3a0d928..4b66010dd 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -1,16 +1,17 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Sockets; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Network; -using System.Linq; -using System.Security.Authentication; using Titanium.Web.Proxy.Network.Tcp; -using System.Security.Cryptography.X509Certificates; namespace Titanium.Web.Proxy { @@ -35,6 +36,8 @@ public partial class ProxyServer : IDisposable private Action exceptionFunc; private bool trustRootCertificate; + private int clientConnectionCount; + internal int serverConnectionCount; /// /// A object that creates tcp connection to server @@ -229,13 +232,13 @@ public Action ExceptionFunc /// /// Total number of active client connections /// - public int ClientConnectionCount { get; private set; } + public int ClientConnectionCount => clientConnectionCount; /// /// Total number of active server connections /// - public int ServerConnectionCount { get; internal set; } + public int ServerConnectionCount => serverConnectionCount; /// /// Constructor @@ -596,7 +599,7 @@ private void OnAcceptConnection(IAsyncResult asyn) { Task.Run(async () => { - ClientConnectionCount++; + Interlocked.Increment(ref clientConnectionCount); //This line is important! //contributors please don't remove it without discussion @@ -617,7 +620,7 @@ private void OnAcceptConnection(IAsyncResult asyn) } finally { - ClientConnectionCount--; + Interlocked.Decrement(ref clientConnectionCount); tcpClient?.Close(); } }); diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 59ed0c36f..b11e82d86 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -6,15 +6,15 @@ using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; -using Titanium.Web.Proxy.Exceptions; +using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; +using Titanium.Web.Proxy.Exceptions; +using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Helpers; -using Titanium.Web.Proxy.Models; -using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Http; -using System.Threading.Tasks; -using Titanium.Web.Proxy.Extensions; +using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Network.Tcp; +using Titanium.Web.Proxy.Shared; namespace Titanium.Web.Proxy { diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 0fadbbee0..71e3c6706 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -1,14 +1,15 @@ using System; using System.Collections.Generic; using System.IO; -using Titanium.Web.Proxy.EventArguments; -using Titanium.Web.Proxy.Models; -using Titanium.Web.Proxy.Compression; +using System.Threading; using System.Threading.Tasks; +using Titanium.Web.Proxy.Compression; +using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Extensions; -using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Helpers; +using Titanium.Web.Proxy.Http; +using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Network.Tcp; namespace Titanium.Web.Proxy @@ -56,7 +57,7 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) if(args.WebSession.ServerConnection != null) { args.WebSession.ServerConnection.Dispose(); - ServerConnectionCount--; + Interlocked.Decrement(ref serverConnectionCount); } var connection = await GetServerConnection(args); @@ -240,7 +241,7 @@ private void Dispose(Stream clientStream, StreamWriter clientStreamWriter, TcpConnection serverConnection) { - ServerConnectionCount--; + Interlocked.Decrement(ref serverConnectionCount); clientStream?.Close(); clientStream?.Dispose(); From a7d679db811a0216d98a2fd685e2c4f1ec3c5e70 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 10:57:24 +0200 Subject: [PATCH 04/17] event parallel invoke moved to extension class --- Titanium.Web.Proxy/CertificateHandler.cs | 27 +++----------- .../Extensions/FuncExtensions.cs | 37 +++++++++++++++++++ Titanium.Web.Proxy/RequestHandler.cs | 10 +---- Titanium.Web.Proxy/ResponseHandler.cs | 10 +---- Titanium.Web.Proxy/Titanium.Web.Proxy.csproj | 1 + 5 files changed, 45 insertions(+), 40 deletions(-) create mode 100644 Titanium.Web.Proxy/Extensions/FuncExtensions.cs diff --git a/Titanium.Web.Proxy/CertificateHandler.cs b/Titanium.Web.Proxy/CertificateHandler.cs index 134798937..29238dab7 100644 --- a/Titanium.Web.Proxy/CertificateHandler.cs +++ b/Titanium.Web.Proxy/CertificateHandler.cs @@ -3,6 +3,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; +using Titanium.Web.Proxy.Extensions; namespace Titanium.Web.Proxy { @@ -32,17 +33,8 @@ internal bool ValidateServerCertificate( SslPolicyErrors = sslPolicyErrors }; - - Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList(); - Task[] handlerTasks = new Task[invocationList.Length]; - - for (int i = 0; i < invocationList.Length; i++) - { - handlerTasks[i] = ((Func) invocationList[i])(null, args); - } - - Task.WhenAll(handlerTasks).Wait(); - + //why is the sender null? + ServerCertificateValidationCallback.InvokeParallel(null, args); return args.IsValid; } @@ -108,17 +100,8 @@ internal X509Certificate SelectClientCertificate( ClientCertificate = clientCertificate }; - - Delegate[] invocationList = ClientCertificateSelectionCallback.GetInvocationList(); - Task[] handlerTasks = new Task[invocationList.Length]; - - for (int i = 0; i < invocationList.Length; i++) - { - handlerTasks[i] = ((Func) invocationList[i])(null, args); - } - - Task.WhenAll(handlerTasks).Wait(); - + //why is the sender null? + ClientCertificateSelectionCallback.InvokeParallel(null, args); return args.ClientCertificate; } diff --git a/Titanium.Web.Proxy/Extensions/FuncExtensions.cs b/Titanium.Web.Proxy/Extensions/FuncExtensions.cs new file mode 100644 index 000000000..fab60567e --- /dev/null +++ b/Titanium.Web.Proxy/Extensions/FuncExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Titanium.Web.Proxy.Extensions +{ + internal static class FuncExtensions + { + public static void InvokeParallel(this Func callback, object sender, T args) + { + Delegate[] invocationList = callback.GetInvocationList(); + Task[] handlerTasks = new Task[invocationList.Length]; + + for (int i = 0; i < invocationList.Length; i++) + { + handlerTasks[i] = ((Func)invocationList[i])(sender, args); + } + + Task.WhenAll(handlerTasks).Wait(); + } + + public static async Task InvokeParallelAsync(this Func callback, object sender, T args) + { + Delegate[] invocationList = callback.GetInvocationList(); + Task[] handlerTasks = new Task[invocationList.Length]; + + for (int i = 0; i < invocationList.Length; i++) + { + handlerTasks[i] = ((Func)invocationList[i])(sender, args); + } + + await Task.WhenAll(handlerTasks); + } + } +} diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index b11e82d86..5a6cec4ed 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -486,15 +486,7 @@ await CheckAuthorization(clientStreamWriter, //If user requested interception do it if (BeforeRequest != null) { - var invocationList = BeforeRequest.GetInvocationList(); - var handlerTasks = new Task[invocationList.Length]; - - for (var i = 0; i < invocationList.Length; i++) - { - handlerTasks[i] = ((Func)invocationList[i])(this, args); - } - - await Task.WhenAll(handlerTasks); + await BeforeRequest.InvokeParallelAsync(this, args); } //if upgrading to websocket then relay the requet without reading the contents diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 71e3c6706..8fe732013 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -41,15 +41,7 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) //If user requested call back then do it if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { - Delegate[] invocationList = BeforeResponse.GetInvocationList(); - Task[] handlerTasks = new Task[invocationList.Length]; - - for (int i = 0; i < invocationList.Length; i++) - { - handlerTasks[i] = ((Func)invocationList[i])(this, args); - } - - await Task.WhenAll(handlerTasks); + await BeforeResponse.InvokeParallelAsync(this, args); } if (args.ReRequest) diff --git a/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj b/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj index 6b186297c..0e211df08 100644 --- a/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj +++ b/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj @@ -73,6 +73,7 @@ + From c44631d83a9aa1df8290f29b7efe5e0cea70ac26 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 11:36:42 +0200 Subject: [PATCH 05/17] cleanup --- .../ProxyTestController.cs | 2 +- .../SslTests.cs | 1 - Titanium.Web.Proxy.sln.DotSettings | 9 ++++ Titanium.Web.Proxy/CertificateHandler.cs | 1 - .../EventArguments/SessionEventArgs.cs | 4 +- .../Extensions/FuncExtensions.cs | 3 -- .../Extensions/StreamExtensions.cs | 6 +-- .../Helpers/CustomBinaryReader.cs | 4 +- .../Helpers/CustomBufferedStream.cs | 2 +- Titanium.Web.Proxy/Helpers/Tcp.cs | 2 +- Titanium.Web.Proxy/Http/HeaderParser.cs | 6 +-- Titanium.Web.Proxy/Http/HttpWebClient.cs | 4 +- Titanium.Web.Proxy/Http/Request.cs | 2 +- .../Http/Responses/GenericResponse.cs | 2 +- .../Network/Certificate/BCCertificateMaker.cs | 2 +- .../Certificate/WinCertificateMaker.cs | 9 ++-- .../Network/CertificateManager.cs | 2 +- .../Network/Tcp/TcpConnectionFactory.cs | 13 +++--- Titanium.Web.Proxy/ProxyServer.cs | 17 ++++---- Titanium.Web.Proxy/RequestHandler.cs | 41 +++++++++---------- Titanium.Web.Proxy/ResponseHandler.cs | 6 +-- 21 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 8f8f7626f..d16dbfaef 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -150,7 +150,7 @@ public async Task OnRequest(object sender, SessionEventArgs e) //Modify response public async Task OnResponse(object sender, SessionEventArgs e) { - Console.WriteLine("Active Server Connections:" + (sender as ProxyServer).ServerConnectionCount); + Console.WriteLine("Active Server Connections:" + ((ProxyServer) sender).ServerConnectionCount); if (requestBodyHistory.ContainsKey(e.Id)) { diff --git a/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs b/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs index 83b87ae5b..1f851580e 100644 --- a/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs +++ b/Tests/Titanium.Web.Proxy.IntegrationTests/SslTests.cs @@ -14,7 +14,6 @@ namespace Titanium.Web.Proxy.IntegrationTests public class SslTests { [TestMethod] - public void TestSsl() { //expand this to stress test to find diff --git a/Titanium.Web.Proxy.sln.DotSettings b/Titanium.Web.Proxy.sln.DotSettings index 6f43e6a16..2c8ffe8d7 100644 --- a/Titanium.Web.Proxy.sln.DotSettings +++ b/Titanium.Web.Proxy.sln.DotSettings @@ -1,6 +1,15 @@  True + False + 240 BC + CN + DN + EKU + KU + MTA + OID + OIDS <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> diff --git a/Titanium.Web.Proxy/CertificateHandler.cs b/Titanium.Web.Proxy/CertificateHandler.cs index 29238dab7..4dca8be94 100644 --- a/Titanium.Web.Proxy/CertificateHandler.cs +++ b/Titanium.Web.Proxy/CertificateHandler.cs @@ -1,7 +1,6 @@ using System; using System.Net.Security; using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Extensions; diff --git a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index a4fbfee94..9f2824575 100644 --- a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -56,7 +56,7 @@ public class SessionEventArgs : EventArgs, IDisposable /// /// Client End Point. /// - public IPEndPoint ClientEndPoint => (IPEndPoint) ProxyClient.TcpClient.Client.RemoteEndPoint; + public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint; /// /// A web session corresponding to a single request/response sequence @@ -158,7 +158,7 @@ private async Task ReadResponseBody() await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, WebSession.Response.ContentLength); } - else if ((WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) || WebSession.Response.ContentLength == -1) + else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0 || WebSession.Response.ContentLength == -1) { await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue); } diff --git a/Titanium.Web.Proxy/Extensions/FuncExtensions.cs b/Titanium.Web.Proxy/Extensions/FuncExtensions.cs index fab60567e..cfc1a2c78 100644 --- a/Titanium.Web.Proxy/Extensions/FuncExtensions.cs +++ b/Titanium.Web.Proxy/Extensions/FuncExtensions.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Titanium.Web.Proxy.Extensions diff --git a/Titanium.Web.Proxy/Extensions/StreamExtensions.cs b/Titanium.Web.Proxy/Extensions/StreamExtensions.cs index 4cf6be2bc..289e02290 100644 --- a/Titanium.Web.Proxy/Extensions/StreamExtensions.cs +++ b/Titanium.Web.Proxy/Extensions/StreamExtensions.cs @@ -136,7 +136,7 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea if (contentLength < bufferSize) { - bytesToRead = (int) contentLength; + bytesToRead = (int)contentLength; } var buffer = new byte[bufferSize]; @@ -153,8 +153,8 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea break; bytesRead = 0; - var remainingBytes = (contentLength - totalBytesRead); - bytesToRead = remainingBytes > (long) bufferSize ? bufferSize : (int) remainingBytes; + var remainingBytes = contentLength - totalBytesRead; + bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes; } } else diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index 32bce44f2..6f542c6ca 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -118,7 +118,7 @@ internal async Task ReadBytesAsync(long totalBytesToRead) var buffer = staticBuffer; if (totalBytesToRead < bufferSize) { - bytesToRead = (int) totalBytesToRead; + bytesToRead = (int)totalBytesToRead; buffer = new byte[bytesToRead]; } @@ -133,7 +133,7 @@ internal async Task ReadBytesAsync(long totalBytesToRead) break; var remainingBytes = totalBytesToRead - totalBytesRead; - bytesToRead = Math.Min(bufferSize, (int) remainingBytes); + bytesToRead = Math.Min(bufferSize, (int)remainingBytes); if (totalBytesRead + bytesToRead > buffer.Length) { diff --git a/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs b/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs index 78fa6db21..98281be68 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs @@ -198,7 +198,7 @@ public override int EndRead(IAsyncResult asyncResult) { if (asyncResult is ReadAsyncResult) { - return ((ReadAsyncResult) asyncResult).ReadBytes; + return ((ReadAsyncResult)asyncResult).ReadBytes; } return baseStream.EndRead(asyncResult); diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index 226a26e00..5cac82141 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -229,7 +229,7 @@ internal static async Task SendRaw(ProxyServer server, finally { tcpConnection.Dispose(); - Interlocked.Decrement(ref server.serverConnectionCount); + Interlocked.Decrement(ref server.ServerConnectionCountField); } } } diff --git a/Titanium.Web.Proxy/Http/HeaderParser.cs b/Titanium.Web.Proxy/Http/HeaderParser.cs index 27d8e051a..758d373eb 100644 --- a/Titanium.Web.Proxy/Http/HeaderParser.cs +++ b/Titanium.Web.Proxy/Http/HeaderParser.cs @@ -8,8 +8,8 @@ namespace Titanium.Web.Proxy.Http { internal static class HeaderParser { - internal static async Task ReadHeaders(CustomBinaryReader reader, - Dictionary> nonUniqueResponseHeaders, + internal static async Task ReadHeaders(CustomBinaryReader reader, + Dictionary> nonUniqueResponseHeaders, Dictionary headers) { string tmpLine; @@ -29,7 +29,7 @@ internal static async Task ReadHeaders(CustomBinaryReader reader, { var existing = headers[newHeader.Name]; - var nonUniqueHeaders = new List { existing, newHeader }; + var nonUniqueHeaders = new List {existing, newHeader}; nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders); headers.Remove(newHeader.Name); diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index 03bb2ae80..393d4b844 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -79,7 +79,7 @@ internal async Task SendRequest(bool enable100ContinueBehaviour) var requestLines = new StringBuilder(); //prepare the request & headers - if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps)) + if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false || ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps) { requestLines.AppendLine($"{Request.Method} {Request.RequestUri.AbsoluteUri} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}"); } @@ -93,7 +93,7 @@ internal async Task SendRequest(bool enable100ContinueBehaviour) { requestLines.AppendLine("Proxy-Connection: keep-alive"); requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes( - $"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}"))); + $"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}"))); } //write request headers foreach (var headerItem in Request.RequestHeaders) diff --git a/Titanium.Web.Proxy/Http/Request.cs b/Titanium.Web.Proxy/Http/Request.cs index 1af6295b8..0afdea85c 100644 --- a/Titanium.Web.Proxy/Http/Request.cs +++ b/Titanium.Web.Proxy/Http/Request.cs @@ -241,7 +241,7 @@ public bool ExpectContinue /// request body as string /// internal string RequestBodyString { get; set; } - + internal bool RequestBodyRead { get; set; } internal bool RequestLocked { get; set; } diff --git a/Titanium.Web.Proxy/Http/Responses/GenericResponse.cs b/Titanium.Web.Proxy/Http/Responses/GenericResponse.cs index bf3a0753b..d2ad39f05 100644 --- a/Titanium.Web.Proxy/Http/Responses/GenericResponse.cs +++ b/Titanium.Web.Proxy/Http/Responses/GenericResponse.cs @@ -13,7 +13,7 @@ public class GenericResponse : Response /// public GenericResponse(HttpStatusCode status) { - ResponseStatusCode = ((int) status).ToString(); + ResponseStatusCode = ((int)status).ToString(); ResponseStatusDescription = status.ToString(); } diff --git a/Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs b/Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs index e7a8b7eb0..fba922e72 100644 --- a/Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs +++ b/Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs @@ -110,7 +110,7 @@ private static X509Certificate2 GenerateCertificate(string hostName, // Corresponding private key var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); - var seq = (Asn1Sequence) Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded()); + var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded()); if (seq.Count != 9) { diff --git a/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs b/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs index fed9f2ecb..1986c6588 100644 --- a/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs +++ b/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs @@ -114,7 +114,7 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeValue[0] = 2; typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); - typeValue = new object[] {(isRoot ? 2 : 1)}; + typeValue = new object[] {isRoot ? 2 : 1}; typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); if (!isRoot) @@ -243,7 +243,7 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful typeValue[0] = 0; - var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); + var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); typeValue = new[] {2, createCertRequest, 0, string.Empty}; typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); @@ -251,7 +251,7 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful try { - var empty = (string) typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); + var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); } catch (Exception) @@ -293,8 +293,7 @@ private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, var graceTime = DateTime.Now.AddDays(GraceDays); var now = DateTime.Now; - rCert = !isRoot ? MakeCertificate(false, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), signingCert) : - MakeCertificate(true, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), null); + rCert = MakeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), isRoot ? null : signingCert); return rCert; } } diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 1fb7953bc..a33ac22f5 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -52,7 +52,7 @@ internal CertificateEngine Engine if (certEngine == null) { certEngine = engine == CertificateEngine.BouncyCastle - ? (ICertificateMaker) new BCCertificateMaker() + ? (ICertificateMaker)new BCCertificateMaker() : new WinCertificateMaker(); } } diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 640d52c74..297718a0f 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -18,7 +18,6 @@ namespace Titanium.Web.Proxy.Network.Tcp /// internal class TcpConnectionFactory { - /// /// Creates a TCP connection to server /// @@ -31,19 +30,19 @@ internal class TcpConnectionFactory /// /// /// - internal async Task CreateClient(ProxyServer server, + internal async Task CreateClient(ProxyServer server, string remoteHostName, int remotePort, Version httpVersion, - bool isHttps, + bool isHttps, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy, Stream clientStream) { TcpClient client; CustomBufferedStream stream; - bool isLocalhost = (externalHttpsProxy == null && externalHttpProxy == null) ? false : NetworkHelper.IsLocalIpAddress(remoteHostName); + bool isLocalhost = (externalHttpsProxy != null || externalHttpProxy != null) && NetworkHelper.IsLocalIpAddress(remoteHostName); - bool useHttpsProxy = externalHttpsProxy != null && externalHttpsProxy.HostName != remoteHostName && (externalHttpsProxy.BypassForLocalhost && !isLocalhost); - bool useHttpProxy = externalHttpProxy != null && externalHttpProxy.HostName != remoteHostName && (externalHttpProxy.BypassForLocalhost && !isLocalhost); + bool useHttpsProxy = externalHttpsProxy != null && externalHttpsProxy.HostName != remoteHostName && externalHttpsProxy.BypassForLocalhost && !isLocalhost; + bool useHttpProxy = externalHttpProxy != null && externalHttpProxy.HostName != remoteHostName && externalHttpProxy.BypassForLocalhost && !isLocalhost; if (isHttps) { @@ -128,7 +127,7 @@ internal async Task CreateClient(ProxyServer server, client.LingerState = new LingerOption(true, 0); - Interlocked.Increment(ref server.serverConnectionCount); + Interlocked.Increment(ref server.ServerConnectionCountField); return new TcpConnection { diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 4b66010dd..2b25e1b2d 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -36,8 +36,8 @@ public partial class ProxyServer : IDisposable private Action exceptionFunc; private bool trustRootCertificate; - private int clientConnectionCount; - internal int serverConnectionCount; + private int clientConnectionCountField; + internal int ServerConnectionCountField; /// /// A object that creates tcp connection to server @@ -53,8 +53,7 @@ public partial class ProxyServer : IDisposable /// /// Set firefox to use default system proxy /// - private FireFoxProxySettingsManager firefoxProxySettingsManager - = new FireFoxProxySettingsManager(); + private FireFoxProxySettingsManager firefoxProxySettingsManager = new FireFoxProxySettingsManager(); #endif /// @@ -227,18 +226,18 @@ public Action ExceptionFunc /// List of supported Ssl versions /// public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls - | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; + | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; /// /// Total number of active client connections /// - public int ClientConnectionCount => clientConnectionCount; + public int ClientConnectionCount => clientConnectionCountField; /// /// Total number of active server connections /// - public int ServerConnectionCount => serverConnectionCount; + public int ServerConnectionCount => ServerConnectionCountField; /// /// Constructor @@ -599,7 +598,7 @@ private void OnAcceptConnection(IAsyncResult asyn) { Task.Run(async () => { - Interlocked.Increment(ref clientConnectionCount); + Interlocked.Increment(ref clientConnectionCountField); //This line is important! //contributors please don't remove it without discussion @@ -620,7 +619,7 @@ private void OnAcceptConnection(IAsyncResult asyn) } finally { - Interlocked.Decrement(ref clientConnectionCount); + Interlocked.Decrement(ref clientConnectionCountField); tcpClient?.Close(); } }); diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 5a6cec4ed..f30543d1f 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -33,7 +33,7 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - var clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; + var clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; Uri httpRemoteUri; @@ -188,7 +188,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; + clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; //HTTPS server created - we can now decrypt the client's traffic } catch (Exception) @@ -205,7 +205,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, else { clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; + clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; } //now read the request line @@ -392,8 +392,6 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", /// /// /// - /// - /// /// private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, @@ -408,18 +406,18 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St if (string.IsNullOrEmpty(httpCmd)) { Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); + clientStreamReader, + clientStreamWriter, + connection); break; } var args = new SessionEventArgs(BufferSize, HandleHttpSessionResponse) - { - ProxyClient = { TcpClient = client }, - WebSession = { ConnectHeaders = connectHeaders } - }; + { + ProxyClient = {TcpClient = client}, + WebSession = {ConnectHeaders = connectHeaders} + }; args.WebSession.ProcessId = new Lazy(() => { @@ -457,7 +455,8 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St //Read the request headers in to unique and non-unique header collections await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.NonUniqueRequestHeaders, args.WebSession.Request.RequestHeaders); - var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1] + var httpRemoteUri = new Uri(httpsHostName == null + ? httpCmdSplit[1] : string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1])); args.WebSession.Request.RequestUri = httpRemoteUri; @@ -468,14 +467,14 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; - if (httpsHostName == null && - await CheckAuthorization(clientStreamWriter, + if (httpsHostName == null && + await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false) { Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); + clientStreamReader, + clientStreamWriter, + connection); break; } @@ -548,9 +547,9 @@ await TcpHelper.SendRaw(this, ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request", e, args)); Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); + clientStreamReader, + clientStreamWriter, + connection); break; } } diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 8fe732013..30dc49a47 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -46,10 +46,10 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) if (args.ReRequest) { - if(args.WebSession.ServerConnection != null) + if (args.WebSession.ServerConnection != null) { args.WebSession.ServerConnection.Dispose(); - Interlocked.Decrement(ref serverConnectionCount); + Interlocked.Decrement(ref ServerConnectionCountField); } var connection = await GetServerConnection(args); @@ -233,7 +233,7 @@ private void Dispose(Stream clientStream, StreamWriter clientStreamWriter, TcpConnection serverConnection) { - Interlocked.Decrement(ref serverConnectionCount); + Interlocked.Decrement(ref ServerConnectionCountField); clientStream?.Close(); clientStream?.Dispose(); From 0f6434ecd90e98921f8dc9266558b7312ebf370f Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 15:58:18 +0200 Subject: [PATCH 06/17] Pass the ProxyServer object to CertificateSelection callbacks --- Titanium.Web.Proxy/CertificateHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Titanium.Web.Proxy/CertificateHandler.cs b/Titanium.Web.Proxy/CertificateHandler.cs index 4dca8be94..b13ccfd82 100644 --- a/Titanium.Web.Proxy/CertificateHandler.cs +++ b/Titanium.Web.Proxy/CertificateHandler.cs @@ -33,7 +33,7 @@ internal bool ValidateServerCertificate( }; //why is the sender null? - ServerCertificateValidationCallback.InvokeParallel(null, args); + ServerCertificateValidationCallback.InvokeParallel(this, args); return args.IsValid; } @@ -100,7 +100,7 @@ internal X509Certificate SelectClientCertificate( }; //why is the sender null? - ClientCertificateSelectionCallback.InvokeParallel(null, args); + ClientCertificateSelectionCallback.InvokeParallel(this, args); return args.ClientCertificate; } From e7c1c984138646e65070120ea545cefc844ffed4 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 11:08:00 -0400 Subject: [PATCH 07/17] #231 Fix server count; don't dispose twice --- Titanium.Web.Proxy/RequestHandler.cs | 75 ++++++++++++++++++--------- Titanium.Web.Proxy/ResponseHandler.cs | 15 +++--- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index f30543d1f..a238b23ff 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -27,13 +27,15 @@ partial class ProxyServer //So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) { - CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); + var disposed = false; + + var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - var clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; + var clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; Uri httpRemoteUri; @@ -120,7 +122,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, clientStreamReader.Dispose(); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; + clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; } catch { @@ -147,17 +149,23 @@ await TcpHelper.SendRaw(this, return; } + //Now create the request - await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, + disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders); } - catch (Exception) + catch (Exception e) { + ExceptionFunc(new Exception("Error whilst authorizing request", e)); } finally { - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + if (!disposed) + { + Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + } + } } @@ -165,7 +173,9 @@ await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamRea //So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) { - CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); + var disposed = false; + + var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; @@ -188,7 +198,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; + clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; //HTTPS server created - we can now decrypt the client's traffic } catch (Exception) @@ -199,21 +209,27 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, clientStreamReader, clientStreamWriter, null); + disposed = true; return; } } else { clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine}; + clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; } //now read the request line var httpCmd = await clientStreamReader.ReadLineAsync(); //Now create the request - await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, - endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); + disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, + endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); + + if (!disposed) + { + Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + } } /// @@ -271,7 +287,7 @@ private async Task HandleHttpSessionRequestInternal(TcpConnection connecti args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); - return false; + return true; } //if expect continue is enabled then send the headers first @@ -335,12 +351,12 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", //If not expectation failed response was returned by server then parse response if (!args.WebSession.Request.ExpectationFailed) { - var result = await HandleHttpSessionResponse(args); + var disposed = await HandleHttpSessionResponse(args); //already disposed inside above method - if (result == false) + if (disposed) { - return false; + return disposed; } } @@ -352,7 +368,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); - return false; + return true; } } catch (Exception e) @@ -364,7 +380,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); - return false; + return true; } if (closeConnection) @@ -375,10 +391,10 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); - return false; + return true; } - return true; + return false; } /// @@ -393,10 +409,12 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", /// /// /// - private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, + private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List connectHeaders) { + var disposed = false; + TcpConnection connection = null; //Loop through each subsequest request on this particular client connection @@ -410,13 +428,14 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St clientStreamWriter, connection); + disposed = true; break; } var args = new SessionEventArgs(BufferSize, HandleHttpSessionResponse) { - ProxyClient = {TcpClient = client}, - WebSession = {ConnectHeaders = connectHeaders} + ProxyClient = { TcpClient = client }, + WebSession = { ConnectHeaders = connectHeaders } }; args.WebSession.ProcessId = new Lazy(() => @@ -476,6 +495,7 @@ await CheckAuthorization(clientStreamWriter, clientStreamWriter, connection); + disposed = true; break; } @@ -501,6 +521,7 @@ await TcpHelper.SendRaw(this, clientStreamWriter, connection); + disposed = true; break; } @@ -510,9 +531,9 @@ await TcpHelper.SendRaw(this, } //construct the web request that we are going to issue on behalf of the client. - var result = await HandleHttpSessionRequestInternal(connection, args, false); + disposed = await HandleHttpSessionRequestInternal(connection, args, false); - if (result == false) + if (disposed) { //already disposed inside above method break; @@ -525,6 +546,7 @@ await TcpHelper.SendRaw(this, clientStreamWriter, connection); + disposed = true; break; } @@ -536,6 +558,7 @@ await TcpHelper.SendRaw(this, clientStreamWriter, connection); + disposed = true; break; } @@ -550,9 +573,13 @@ await TcpHelper.SendRaw(this, clientStreamReader, clientStreamWriter, connection); + + disposed = true; break; } } + + return disposed; } /// diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 30dc49a47..b8ad38373 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -23,7 +23,7 @@ partial class ProxyServer /// Called asynchronously when a request was successfully and we received the response /// /// - /// true if no errors + /// true if client/server connection was terminated (and disposed) private async Task HandleHttpSessionResponse(SessionEventArgs args) { try @@ -130,12 +130,12 @@ await args.WebSession.ServerConnection.StreamReader Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); - return false; + return true; } args.Dispose(); - return true; + return false; } /// @@ -233,15 +233,18 @@ private void Dispose(Stream clientStream, StreamWriter clientStreamWriter, TcpConnection serverConnection) { - Interlocked.Decrement(ref ServerConnectionCountField); - clientStream?.Close(); clientStream?.Dispose(); clientStreamReader?.Dispose(); clientStreamWriter?.Dispose(); - serverConnection?.Dispose(); + if (serverConnection != null) + { + serverConnection.Dispose(); + Interlocked.Decrement(ref ServerConnectionCountField); + } + } } } From fde583871f6a62635d88f91a724ff5d1e194c028 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 11:20:49 -0400 Subject: [PATCH 08/17] revert experimental changes --- .../Network/Tcp/TcpConnection.cs | 14 ++++++++++++- .../Network/Tcp/TcpConnectionFactory.cs | 2 -- Titanium.Web.Proxy/ProxyServer.cs | 21 ++++++++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs index 70ced1fc0..a295fbbbe 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs @@ -58,7 +58,19 @@ public void Dispose() StreamReader?.Dispose(); - TcpClient?.Close(); + try + { + if (TcpClient != null) + { + //This line is important! + //contributors please don't remove it without discussion + //It helps to avoid eventual deterioration of performance due to TCP port exhaustion + //due to default TCP CLOSE_WAIT timeout for 4 minutes + TcpClient.LingerState = new LingerOption(true, 0); + TcpClient.Close(); + } + } + catch { } } } } diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 297718a0f..51f2ba52b 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -125,8 +125,6 @@ internal async Task CreateClient(ProxyServer server, client.ReceiveTimeout = server.ConnectionTimeOutSeconds * 1000; client.SendTimeout = server.ConnectionTimeOutSeconds * 1000; - client.LingerState = new LingerOption(true, 0); - Interlocked.Increment(ref server.ServerConnectionCountField); return new TcpConnection diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 2b25e1b2d..e27b64ccc 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -600,12 +600,6 @@ private void OnAcceptConnection(IAsyncResult asyn) { Interlocked.Increment(ref clientConnectionCountField); - //This line is important! - //contributors please don't remove it without discussion - //It helps to avoid eventual deterioration of performance due to TCP port exhaustion - //due to default TCP CLOSE_WAIT timeout for 4 minutes - tcpClient.LingerState = new LingerOption(true, 0); - try { if (endPoint.GetType() == typeof(TransparentProxyEndPoint)) @@ -620,7 +614,20 @@ private void OnAcceptConnection(IAsyncResult asyn) finally { Interlocked.Decrement(ref clientConnectionCountField); - tcpClient?.Close(); + + try + { + if (tcpClient != null) + { + //This line is important! + //contributors please don't remove it without discussion + //It helps to avoid eventual deterioration of performance due to TCP port exhaustion + //due to default TCP CLOSE_WAIT timeout for 4 minutes + tcpClient.LingerState = new LingerOption(true, 0); + tcpClient.Close(); + } + } + catch { } } }); } From 270cb1333305ebb08e5c29a5c6beb472c3d32e9a Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 18:05:22 +0200 Subject: [PATCH 09/17] Space within single line array initializer --- .../Program.cs | 2 +- .../ProxyTestController.cs | 4 +-- .../CertificateManagerTests.cs | 5 +-- Titanium.Web.Proxy.sln.DotSettings | 1 + Titanium.Web.Proxy/Http/HeaderParser.cs | 2 +- .../Certificate/WinCertificateMaker.cs | 34 +++++++++---------- .../Network/CertificateManager.cs | 2 +- .../Network/Tcp/TcpConnection.cs | 4 ++- .../Network/Tcp/TcpConnectionFactory.cs | 4 +-- .../ProxyAuthorizationHandler.cs | 4 +-- Titanium.Web.Proxy/ProxyServer.cs | 4 ++- Titanium.Web.Proxy/RequestHandler.cs | 3 +- Titanium.Web.Proxy/ResponseHandler.cs | 1 - Titanium.Web.Proxy/Shared/ProxyConstants.cs | 6 ++-- 14 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs index 13cc0fe86..a1b814534 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs @@ -51,4 +51,4 @@ internal static class NativeMethods // Pinvoke internal delegate bool ConsoleEventDelegate(int eventType); } -} \ No newline at end of file +} diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index d16dbfaef..c88d2c483 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -107,7 +107,7 @@ public void Stop() //intecept & cancel redirect or update requests public async Task OnRequest(object sender, SessionEventArgs e) { - Console.WriteLine("Active Client Connections:" + ((ProxyServer) sender).ClientConnectionCount); + Console.WriteLine("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount); Console.WriteLine(e.WebSession.Request.Url); //read request headers @@ -150,7 +150,7 @@ public async Task OnRequest(object sender, SessionEventArgs e) //Modify response public async Task OnResponse(object sender, SessionEventArgs e) { - Console.WriteLine("Active Server Connections:" + ((ProxyServer) sender).ServerConnectionCount); + Console.WriteLine("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount); if (requestBodyHistory.ContainsKey(e.Id)) { diff --git a/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs b/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs index 5d37a623a..e4388e2e5 100644 --- a/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs +++ b/Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs @@ -10,7 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests public class CertificateManagerTests { private static readonly string[] hostNames - = { "facebook.com", "youtube.com", "google.com", "bing.com", "yahoo.com"}; + = { "facebook.com", "youtube.com", "google.com", "bing.com", "yahoo.com" }; private readonly Random random = new Random(); @@ -35,16 +35,13 @@ public async Task Simple_Create_Certificate_Stress_Test() var certificate = mgr.CreateCertificate(host, false); Assert.IsNotNull(certificate); - })); - } } await Task.WhenAll(tasks.ToArray()); mgr.StopClearIdleCertificates(); - } } } diff --git a/Titanium.Web.Proxy.sln.DotSettings b/Titanium.Web.Proxy.sln.DotSettings index 2c8ffe8d7..5d870b7df 100644 --- a/Titanium.Web.Proxy.sln.DotSettings +++ b/Titanium.Web.Proxy.sln.DotSettings @@ -1,6 +1,7 @@  True False + True 240 BC CN diff --git a/Titanium.Web.Proxy/Http/HeaderParser.cs b/Titanium.Web.Proxy/Http/HeaderParser.cs index 758d373eb..bf69aecbb 100644 --- a/Titanium.Web.Proxy/Http/HeaderParser.cs +++ b/Titanium.Web.Proxy/Http/HeaderParser.cs @@ -29,7 +29,7 @@ internal static async Task ReadHeaders(CustomBinaryReader reader, { var existing = headers[newHeader.Name]; - var nonUniqueHeaders = new List {existing, newHeader}; + var nonUniqueHeaders = new List { existing, newHeader }; nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders); headers.Remove(newHeader.Name); diff --git a/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs b/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs index 1986c6588..42a5db01e 100644 --- a/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs +++ b/Titanium.Web.Proxy/Network/Certificate/WinCertificateMaker.cs @@ -89,7 +89,7 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful } var x500CertDN = Activator.CreateInstance(typeX500DN); - var typeValue = new object[] {fullSubject, 0}; + var typeValue = new object[] { fullSubject, 0 }; typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500CertDN, typeValue); var x500RootCertDN = Activator.CreateInstance(typeX500DN); @@ -110,16 +110,16 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful if (sharedPrivateKey == null) { sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey); - typeValue = new object[] {sProviderName}; + typeValue = new object[] { sProviderName }; typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeValue[0] = 2; typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); - typeValue = new object[] {isRoot ? 2 : 1}; + typeValue = new object[] { isRoot ? 2 : 1 }; typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); if (!isRoot) { - typeValue = new object[] {176}; + typeValue = new object[] { 176 }; typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); } @@ -149,9 +149,9 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful var requestCert = Activator.CreateInstance(typeRequestCert); - typeValue = new[] {1, sharedPrivateKey, string.Empty}; + typeValue = new[] { 1, sharedPrivateKey, string.Empty }; typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert, typeValue); - typeValue = new[] {x500CertDN}; + typeValue = new[] { x500CertDN }; typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeValue[0] = x500RootCertDN; typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, requestCert, typeValue); @@ -186,14 +186,14 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful var extNames = Activator.CreateInstance(typeExtNames); var altDnsNames = Activator.CreateInstance(typeCAlternativeName); - typeValue = new object[] {3, subject}; + typeValue = new object[] { 3, subject }; typeCAlternativeName.InvokeMember("InitializeFromString", BindingFlags.InvokeMethod, null, altDnsNames, typeValue); - typeValue = new[] {altDnsNames}; + typeValue = new[] { altDnsNames }; typeAltNamesCollection.InvokeMember("Add", BindingFlags.InvokeMethod, null, altNameCollection, typeValue); - typeValue = new[] {altNameCollection}; + typeValue = new[] { altNameCollection }; typeExtNames.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, extNames, typeValue); typeValue[0] = extNames; @@ -204,27 +204,27 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful { var signerCertificate = Activator.CreateInstance(typeSignerCertificate); - typeValue = new object[] {0, 0, 12, signingCertificate.Thumbprint}; + typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint }; typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate, typeValue); - typeValue = new[] {signerCertificate}; + typeValue = new[] { signerCertificate }; typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert, typeValue); } else { var basicConstraints = Activator.CreateInstance(typeBasicConstraints); - typeValue = new object[] {"true", "0"}; + typeValue = new object[] { "true", "0" }; typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, basicConstraints, typeValue); - typeValue = new[] {basicConstraints}; + typeValue = new[] { basicConstraints }; typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); } oid = Activator.CreateInstance(typeOID); - typeValue = new object[] {1, 0, 0, hashAlg}; + typeValue = new object[] { 1, 0, 0, hashAlg }; typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, oid, typeValue); - typeValue = new[] {oid}; + typeValue = new[] { oid }; typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, requestCert, null); @@ -244,10 +244,10 @@ private X509Certificate2 MakeCertificate(bool isRoot, string subject, string ful typeValue[0] = 0; var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); - typeValue = new[] {2, createCertRequest, 0, string.Empty}; + typeValue = new[] { 2, createCertRequest, 0, string.Empty }; typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); - typeValue = new object[] {null, 0, 1}; + typeValue = new object[] { null, 0, 1 }; try { diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index a33ac22f5..828e4f8fa 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -265,7 +265,7 @@ internal virtual X509Certificate2 CreateCertificate(string certificateName, bool } if (certificate != null && !certificateCache.ContainsKey(certificateName)) { - certificateCache.Add(certificateName, new CachedCertificate {Certificate = certificate}); + certificateCache.Add(certificateName, new CachedCertificate { Certificate = certificate }); } } else diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs index a295fbbbe..ab621061f 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs @@ -70,7 +70,9 @@ public void Dispose() TcpClient.Close(); } } - catch { } + catch + { + } } } } diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 51f2ba52b..0471f0b73 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -55,7 +55,7 @@ internal async Task CreateClient(ProxyServer server, await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port); stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); - using (var writer = new StreamWriter(stream, Encoding.ASCII, server.BufferSize, true) {NewLine = ProxyConstants.NewLine}) + using (var writer = new StreamWriter(stream, Encoding.ASCII, server.BufferSize, true) { NewLine = ProxyConstants.NewLine }) { await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}"); await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}"); @@ -75,7 +75,7 @@ internal async Task CreateClient(ProxyServer server, { var result = await reader.ReadLineAsync(); - if (!new[] {"200 OK", "connection established"}.Any(s => result.ContainsIgnoreCase(s))) + if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s))) { throw new Exception("Upstream proxy failed to create a secure tunnel"); } diff --git a/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs b/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs index c8279925c..ad03921fe 100644 --- a/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs +++ b/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs @@ -70,8 +70,8 @@ private async Task SendAuthentication407Response(StreamWriter clientStreamWriter { ResponseHeaders = new Dictionary { - {"Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")}, - {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} + { "Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"") }, + { "Proxy-Connection", new HttpHeader("Proxy-Connection", "close") } } }; await WriteResponseHeaders(clientStreamWriter, response); diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index e27b64ccc..8eccc0da7 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -627,7 +627,9 @@ private void OnAcceptConnection(IAsyncResult asyn) tcpClient.Close(); } } - catch { } + catch + { + } } }); } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index a238b23ff..fe76c51dc 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -165,7 +165,6 @@ await TcpHelper.SendRaw(this, { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } - } } @@ -224,7 +223,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, //Now create the request disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, - endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); + endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); if (!disposed) { diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index b8ad38373..3f4b32619 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -244,7 +244,6 @@ private void Dispose(Stream clientStream, serverConnection.Dispose(); Interlocked.Decrement(ref ServerConnectionCountField); } - } } } diff --git a/Titanium.Web.Proxy/Shared/ProxyConstants.cs b/Titanium.Web.Proxy/Shared/ProxyConstants.cs index 6e2a1f29b..d2535b651 100644 --- a/Titanium.Web.Proxy/Shared/ProxyConstants.cs +++ b/Titanium.Web.Proxy/Shared/ProxyConstants.cs @@ -7,9 +7,9 @@ namespace Titanium.Web.Proxy.Shared /// internal class ProxyConstants { - internal static readonly char[] SpaceSplit = {' '}; - internal static readonly char[] ColonSplit = {':'}; - internal static readonly char[] SemiColonSplit = {';'}; + internal static readonly char[] SpaceSplit = { ' ' }; + internal static readonly char[] ColonSplit = { ':' }; + internal static readonly char[] SemiColonSplit = { ';' }; internal static readonly byte[] NewLineBytes = Encoding.ASCII.GetBytes(NewLine); From 1641a29727a009fa2d33f224c445681a88b95b73 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 12:15:30 -0400 Subject: [PATCH 10/17] #232 experimental fix --- Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index 6f542c6ca..aca960bb1 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -19,7 +19,10 @@ internal class CustomBinaryReader : IDisposable private readonly byte[] staticBuffer; private readonly Encoding encoding; - private static readonly ConcurrentQueue buffers = new ConcurrentQueue(); + private static readonly ConcurrentQueue buffers + = new ConcurrentQueue(); + + private volatile bool disposed = false; internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize) { @@ -154,9 +157,18 @@ internal async Task ReadBytesAsync(long totalBytesToRead) public void Dispose() { - buffers.Enqueue(staticBuffer); + if (!disposed) + { + disposed = true; + buffers.Enqueue(staticBuffer); + } } + /// + /// Increase size of buffer and copy existing content to new buffer + /// + /// + /// private void ResizeBuffer(ref byte[] buffer, long size) { var newBuffer = new byte[size]; From 20ce85b0c086b288f5068bf7cda66e3633d6ce70 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 12:38:03 -0400 Subject: [PATCH 11/17] set body bytes to null after use --- Titanium.Web.Proxy/Http/HttpWebClient.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index 393d4b844..32f6bf826 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -214,6 +214,10 @@ internal async Task ReceiveResponse() /// public void Dispose() { + //not really needed since GC will collect it + //but just to be on safe side + Request.RequestBody = null; + Response.ResponseBody = null; } } } From 30f5389677699eae076f63dc25b8a9df039ca39e Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 21:13:02 +0200 Subject: [PATCH 12/17] move Dispose all to finally blocks --- .../Helpers/CustomBinaryReader.cs | 2 +- Titanium.Web.Proxy/ProxyServer.cs | 3 +- Titanium.Web.Proxy/RequestHandler.cs | 147 ++++++------------ Titanium.Web.Proxy/ResponseHandler.cs | 4 +- 4 files changed, 50 insertions(+), 106 deletions(-) diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index aca960bb1..7217a2719 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -22,7 +22,7 @@ internal class CustomBinaryReader : IDisposable private static readonly ConcurrentQueue buffers = new ConcurrentQueue(); - private volatile bool disposed = false; + private volatile bool disposed; internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize) { diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 8eccc0da7..a912b0d53 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -383,8 +383,7 @@ public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) #if !DEBUG firefoxProxySettingsManager.AddFirefox(); #endif - Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", - endPoint.IpAddress, endPoint.Port); + Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", endPoint.IpAddress, endPoint.Port); } /// diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index fe76c51dc..82364e507 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -117,6 +117,7 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); + //HTTPS server created - we can now decrypt the client's traffic clientStream = new CustomBufferedStream(sslStream, BufferSize); @@ -138,6 +139,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, { //Siphon out CONNECT request headers await clientStreamReader.ReadAndIgnoreAllLinesAsync(); + //write back successfull CONNECT response await WriteConnectResponse(clientStreamWriter, version); @@ -172,8 +174,7 @@ await TcpHelper.SendRaw(this, //So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) { - var disposed = false; - + bool disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; @@ -182,52 +183,39 @@ private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcp CustomBinaryReader clientStreamReader = null; StreamWriter clientStreamWriter = null; - if (endPoint.EnableSsl) + try { - var sslStream = new SslStream(clientStream, true); + if (endPoint.EnableSsl) + { + var sslStream = new SslStream(clientStream, true); + clientStream = new CustomBufferedStream(sslStream, BufferSize); - //implement in future once SNI supported by SSL stream, for now use the same certificate - var certificate = CertificateManager.CreateCertificate(endPoint.GenericCertificateName, false); + //implement in future once SNI supported by SSL stream, for now use the same certificate + var certificate = CertificateManager.CreateCertificate(endPoint.GenericCertificateName, false); - try - { //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false); - clientStream = new CustomBufferedStream(sslStream, BufferSize); - clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); - clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; //HTTPS server created - we can now decrypt the client's traffic } - catch (Exception) - { - sslStream.Dispose(); - - Dispose(sslStream, - clientStreamReader, - clientStreamWriter, null); - disposed = true; - return; - } - } - else - { clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; - } - - //now read the request line - var httpCmd = await clientStreamReader.ReadLineAsync(); - //Now create the request - disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, - endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); + //now read the request line + var httpCmd = await clientStreamReader.ReadLineAsync(); - if (!disposed) + //Now create the request + disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, + endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); + } + finally { - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + if (!disposed) + { + Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + } } } @@ -271,9 +259,10 @@ private async Task GetServerConnection( } - private async Task HandleHttpSessionRequestInternal(TcpConnection connection, - SessionEventArgs args, bool closeConnection) + private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, bool closeConnection) { + bool dispose = true; + try { args.WebSession.Request.RequestLocked = true; @@ -281,11 +270,6 @@ private async Task HandleHttpSessionRequestInternal(TcpConnection connecti //If request was cancelled by user then dispose the client if (args.WebSession.Request.CancelRequest) { - Dispose(args.ProxyClient.ClientStream, - args.ProxyClient.ClientStreamReader, - args.ProxyClient.ClientStreamWriter, - args.WebSession.ServerConnection); - return true; } @@ -355,45 +339,38 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", //already disposed inside above method if (disposed) { - return disposed; + dispose = false; + return true; } } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { - Dispose(args.ProxyClient.ClientStream, - args.ProxyClient.ClientStreamReader, - args.ProxyClient.ClientStreamWriter, - args.WebSession.ServerConnection); - return true; } + + if (!closeConnection) + { + dispose = false; + return false; + } } catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request (internal)", e, args)); - - Dispose(args.ProxyClient.ClientStream, - args.ProxyClient.ClientStreamReader, - args.ProxyClient.ClientStreamWriter, - args.WebSession.ServerConnection); - return true; } - - if (closeConnection) + finally { - //dispose - Dispose(args.ProxyClient.ClientStream, - args.ProxyClient.ClientStreamReader, - args.ProxyClient.ClientStreamWriter, - args.WebSession.ServerConnection); - - return true; + if (dispose) + { + //dispose + Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); + } } - return false; + return true; } /// @@ -412,7 +389,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpC CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List connectHeaders) { - var disposed = false; + bool disposed = false; TcpConnection connection = null; @@ -422,12 +399,6 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpC { if (string.IsNullOrEmpty(httpCmd)) { - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); - - disposed = true; break; } @@ -489,12 +460,6 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpC await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false) { - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); - - disposed = true; break; } @@ -515,12 +480,6 @@ await TcpHelper.SendRaw(this, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, clientStream, tcpConnectionFactory); - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); - - disposed = true; break; } @@ -540,24 +499,12 @@ await TcpHelper.SendRaw(this, if (args.WebSession.Request.CancelRequest) { - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); - - disposed = true; break; } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); - - disposed = true; break; } @@ -567,18 +514,16 @@ await TcpHelper.SendRaw(this, catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request", e, args)); - - Dispose(clientStream, - clientStreamReader, - clientStreamWriter, - connection); - - disposed = true; break; } } - return disposed; + if (!disposed) + { + Dispose(clientStream, clientStreamReader, clientStreamWriter, connection); + } + + return true; } /// diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 3f4b32619..d9ade74a0 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -53,8 +53,8 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) } var connection = await GetServerConnection(args); - var result = await HandleHttpSessionRequestInternal(null, args, true); - return result; + var disposed = await HandleHttpSessionRequestInternal(null, args, true); + return disposed; } args.WebSession.Response.ResponseLocked = true; From c994c82527e8d2a87d873daa75e7fe02eff15c8b Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 15:20:20 -0400 Subject: [PATCH 13/17] fix issue with console quickedit mode where app hangs until key press --- Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs index a1b814534..21e8f666f 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace Titanium.Web.Proxy.Examples.Basic @@ -9,11 +10,14 @@ public class Program public static void Main(string[] args) { + //fix console hang due to QuickEdit mode + var handle = Process.GetCurrentProcess().MainWindowHandle; + NativeMethods.SetConsoleMode(handle, NativeMethods.ENABLE_EXTENDED_FLAGS); + //On Console exit make sure we also exit the proxy NativeMethods.Handler = ConsoleEventCallback; NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true); - //Start proxy controller controller.StartProxy(); @@ -42,6 +46,8 @@ private static bool ConsoleEventCallback(int eventType) internal static class NativeMethods { + internal const uint ENABLE_EXTENDED_FLAGS = 0x0080; + // Keeps it from getting garbage collected internal static ConsoleEventDelegate Handler; @@ -50,5 +56,8 @@ internal static class NativeMethods // Pinvoke internal delegate bool ConsoleEventDelegate(int eventType); + + [DllImport("kernel32.dll")] + internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); } } From ab6488712b81c833bd86ee401826377c535a53ae Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 15:29:42 -0400 Subject: [PATCH 14/17] improve readability --- Titanium.Web.Proxy/RequestHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index fe76c51dc..fa5db3947 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -355,7 +355,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", //already disposed inside above method if (disposed) { - return disposed; + return true; } } From acde2dffbc7596197187761e0c724d8f9b48db4d Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 21:42:12 +0200 Subject: [PATCH 15/17] dispose flag changed to disposed flag in HandleHttpSessionRequestInternal --- Titanium.Web.Proxy/RequestHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 82364e507..5a0cfda33 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -261,7 +261,8 @@ private async Task GetServerConnection( private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, bool closeConnection) { - bool dispose = true; + bool disposed = false; + bool keepAlive = false; try { @@ -334,12 +335,11 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", //If not expectation failed response was returned by server then parse response if (!args.WebSession.Request.ExpectationFailed) { - var disposed = await HandleHttpSessionResponse(args); + disposed = await HandleHttpSessionResponse(args); //already disposed inside above method if (disposed) { - dispose = false; return true; } } @@ -352,7 +352,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", if (!closeConnection) { - dispose = false; + keepAlive = true; return false; } } @@ -363,7 +363,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", } finally { - if (dispose) + if (!disposed && !keepAlive) { //dispose Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); From 1c35a2d9d8771feb7f35546555f136d9265bde56 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 17:19:11 -0400 Subject: [PATCH 16/17] #184 dispose args --- Titanium.Web.Proxy/Http/HttpWebClient.cs | 4 ++++ Titanium.Web.Proxy/RequestHandler.cs | 7 +++++++ Titanium.Web.Proxy/ResponseHandler.cs | 2 -- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index 32f6bf826..bd4278320 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -218,6 +218,10 @@ public void Dispose() //but just to be on safe side Request.RequestBody = null; Response.ResponseBody = null; + + Request.RequestBodyString = null; + Response.ResponseBodyString = null; + } } } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 5a0cfda33..99918465d 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -460,6 +460,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpC await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false) { + args.Dispose(); break; } @@ -480,6 +481,7 @@ await TcpHelper.SendRaw(this, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, clientStream, tcpConnectionFactory); + args.Dispose(); break; } @@ -494,20 +496,25 @@ await TcpHelper.SendRaw(this, if (disposed) { //already disposed inside above method + args.Dispose(); break; } if (args.WebSession.Request.CancelRequest) { + args.Dispose(); break; } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { + args.Dispose(); break; } + args.Dispose(); + // read the next request httpCmd = await clientStreamReader.ReadLineAsync(); } diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index d9ade74a0..63275e69f 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -133,8 +133,6 @@ await args.WebSession.ServerConnection.StreamReader return true; } - args.Dispose(); - return false; } From 522d0b5e42c320b488edf339f6b64be34029bae6 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Tue, 16 May 2017 17:38:00 -0400 Subject: [PATCH 17/17] disable quick edit causing app hang --- .../Helpers/ConsoleHelper.cs | 52 +++++++++++++++++++ .../Program.cs | 9 ++-- .../Titanium.Web.Proxy.Examples.Basic.csproj | 1 + 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 Examples/Titanium.Web.Proxy.Examples.Basic/Helpers/ConsoleHelper.cs diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/Helpers/ConsoleHelper.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/Helpers/ConsoleHelper.cs new file mode 100644 index 000000000..86b06a857 --- /dev/null +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/Helpers/ConsoleHelper.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.InteropServices; + +namespace Titanium.Web.Proxy.Examples.Basic.Helpers +{ + /// + /// Adapated from + /// http://stackoverflow.com/questions/13656846/how-to-programmatic-disable-c-sharp-console-applications-quick-edit-mode + /// + internal static class ConsoleHelper + { + const uint ENABLE_QUICK_EDIT = 0x0040; + + // STD_INPUT_HANDLE (DWORD): -10 is the standard input device. + const int STD_INPUT_HANDLE = -10; + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll")] + static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll")] + static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); + + internal static bool DisableQuickEditMode() + { + + IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE); + + // get current console mode + uint consoleMode; + if (!GetConsoleMode(consoleHandle, out consoleMode)) + { + // ERROR: Unable to get console mode. + return false; + } + + // Clear the quick edit bit in the mode flags + consoleMode &= ~ENABLE_QUICK_EDIT; + + // set the new mode + if (!SetConsoleMode(consoleHandle, consoleMode)) + { + // ERROR: Unable to set console mode + return false; + } + + return true; + } + } +} diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs index 21e8f666f..bbfb5a25f 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/Program.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using Titanium.Web.Proxy.Examples.Basic.Helpers; namespace Titanium.Web.Proxy.Examples.Basic { @@ -11,8 +12,7 @@ public class Program public static void Main(string[] args) { //fix console hang due to QuickEdit mode - var handle = Process.GetCurrentProcess().MainWindowHandle; - NativeMethods.SetConsoleMode(handle, NativeMethods.ENABLE_EXTENDED_FLAGS); + ConsoleHelper.DisableQuickEditMode(); //On Console exit make sure we also exit the proxy NativeMethods.Handler = ConsoleEventCallback; @@ -46,8 +46,6 @@ private static bool ConsoleEventCallback(int eventType) internal static class NativeMethods { - internal const uint ENABLE_EXTENDED_FLAGS = 0x0080; - // Keeps it from getting garbage collected internal static ConsoleEventDelegate Handler; @@ -57,7 +55,6 @@ internal static class NativeMethods // Pinvoke internal delegate bool ConsoleEventDelegate(int eventType); - [DllImport("kernel32.dll")] - internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); } + } diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj b/Examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj index 84c0b9f89..3ebd19cb6 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj @@ -55,6 +55,7 @@ +