From 72c9135f8fec9149917cf44cceb910ebbb621837 Mon Sep 17 00:00:00 2001 From: Honfika Date: Mon, 15 May 2017 10:43:57 +0200 Subject: [PATCH 01/40] Allow to generate root certificate with Titanium without storing it in file system --- .../ProxyTestController.cs | 6 +++ .../Network/CertificateManager.cs | 17 ++++---- Titanium.Web.Proxy/ProxyServer.cs | 42 +++++++++---------- Titanium.Web.Proxy/RequestHandler.cs | 4 +- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 6e1b67067..1c0e2329c 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -17,6 +17,12 @@ public class ProxyTestController public ProxyTestController() { proxyServer = new ProxyServer(); + + //generate root certificate without storing it in file system + //proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle; + //proxyServer.CertificateManager.CreateTrustedRootCertificate(false); + //proxyServer.CertificateManager.TrustRootCertificate(); + proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message); proxyServer.TrustRootCertificate = true; diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 1d11444f2..43698ddec 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -29,9 +29,9 @@ public enum CertificateEngine /// /// A class to manage SSL certificates used by this proxy server /// - internal class CertificateManager : IDisposable + public class CertificateManager : IDisposable { - public CertificateEngine Engine + internal CertificateEngine Engine { get { return engine; } set @@ -164,10 +164,13 @@ internal X509Certificate2 LoadRootCertificate() /// /// Attempts to create a RootCertificate /// - /// true if succeeded, else false - internal bool CreateTrustedRootCertificate() + /// if set to true try to load/save the certificate from rootCert.pfx. + /// + /// true if succeeded, else false + /// + public bool CreateTrustedRootCertificate(bool persistToFile = true) { - if (RootCertificate == null) + if (persistToFile && RootCertificate == null) { RootCertificate = LoadRootCertificate(); } @@ -186,7 +189,7 @@ internal bool CreateTrustedRootCertificate() exceptionFunc(e); } - if (RootCertificate != null) + if (persistToFile && RootCertificate != null) { try { @@ -205,7 +208,7 @@ internal bool CreateTrustedRootCertificate() /// /// Trusts the root certificate. /// - internal void TrustRootCertificate() + public void TrustRootCertificate() { //current user TrustRootCertificate(StoreLocation.CurrentUser); diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 75ee12fef..720aee6b7 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -24,11 +24,6 @@ public partial class ProxyServer : IDisposable /// private bool proxyRunning { get; set; } - /// - /// Manages certificates used by this proxy - /// - private CertificateManager certificateManager { get; set; } - /// /// An default exception log func /// @@ -64,13 +59,18 @@ private FireFoxProxySettingsManager firefoxProxySettingsManager /// public int BufferSize { get; set; } = 8192; + /// + /// Manages certificates used by this proxy + /// + public CertificateManager CertificateManager { get; } + /// /// The root certificate /// public X509Certificate2 RootCertificate { - get { return certificateManager.RootCertificate; } - set { certificateManager.RootCertificate = value; } + get { return CertificateManager.RootCertificate; } + set { CertificateManager.RootCertificate = value; } } /// @@ -79,8 +79,8 @@ public X509Certificate2 RootCertificate /// public string RootCertificateIssuerName { - get { return certificateManager.Issuer; } - set { certificateManager.RootCertificateName = value; } + get { return CertificateManager.Issuer; } + set { CertificateManager.RootCertificateName = value; } } /// @@ -92,8 +92,8 @@ public string RootCertificateIssuerName /// public string RootCertificateName { - get { return certificateManager.RootCertificateName; } - set { certificateManager.Issuer = value; } + get { return CertificateManager.RootCertificateName; } + set { CertificateManager.Issuer = value; } } /// @@ -121,8 +121,8 @@ public bool TrustRootCertificate /// public CertificateEngine CertificateEngine { - get { return certificateManager.Engine; } - set { certificateManager.Engine = value; } + get { return CertificateManager.Engine; } + set { CertificateManager.Engine = value; } } /// @@ -251,7 +251,7 @@ public ProxyServer(string rootCertificateName, string rootCertificateIssuerName) new FireFoxProxySettingsManager(); #endif - certificateManager = new CertificateManager(ExceptionFunc); + CertificateManager = new CertificateManager(ExceptionFunc); if (rootCertificateName != null) { RootCertificateName = rootCertificateName; @@ -356,7 +356,7 @@ public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) EnsureRootCertificate(); //If certificate was trusted by the machine - if (certificateManager.CertValidated) + if (CertificateManager.CertValidated) { systemProxySettingsManager.SetHttpsProxy( Equals(endPoint.IpAddress, IPAddress.Any) | @@ -435,7 +435,7 @@ public void Start() Listen(endPoint); } - certificateManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); + CertificateManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); proxyRunning = true; } @@ -468,7 +468,7 @@ public void Stop() ProxyEndPoints.Clear(); - certificateManager?.StopClearIdleCertificates(); + CertificateManager?.StopClearIdleCertificates(); proxyRunning = false; } @@ -483,7 +483,7 @@ public void Dispose() Stop(); } - certificateManager?.Dispose(); + CertificateManager?.Dispose(); } /// @@ -543,13 +543,13 @@ private Task GetSystemUpStreamProxy(SessionEventArgs sessionEvent private void EnsureRootCertificate() { - if (!certificateManager.CertValidated) + if (!CertificateManager.CertValidated) { - certificateManager.CreateTrustedRootCertificate(); + CertificateManager.CreateTrustedRootCertificate(); if (TrustRootCertificate) { - certificateManager.TrustRootCertificate(); + CertificateManager.TrustRootCertificate(); } } } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index b4fc6fa0c..37e41d8e6 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -110,7 +110,7 @@ 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, @@ -177,7 +177,7 @@ private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcp var sslStream = new SslStream(clientStream, true); //implement in future once SNI supported by SSL stream, for now use the same certificate - var certificate = certificateManager.CreateCertificate(endPoint.GenericCertificateName, false); + var certificate = CertificateManager.CreateCertificate(endPoint.GenericCertificateName, false); try { From 0cb7db766acf75a94abf5186164395498c4980bc Mon Sep 17 00:00:00 2001 From: Honfika Date: Mon, 15 May 2017 10:57:03 +0200 Subject: [PATCH 02/40] Allow to remove the root certificates from windows certmgr --- .../ProxyTestController.cs | 3 ++ .../Network/CertificateManager.cs | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 1c0e2329c..7a27bc17d 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -98,6 +98,9 @@ public void Stop() proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection; proxyServer.Stop(); + + //remove the generated certificates + //proxyServer.CertificateManager.RemoveTrustedRootCertificates(); } //intecept & cancel redirect or update requests diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 43698ddec..d3e7547de 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -217,6 +217,18 @@ public void TrustRootCertificate() TrustRootCertificate(StoreLocation.LocalMachine); } + /// + /// Removes the trusted certificates. + /// + public void RemoveTrustedRootCertificates() + { + //current user + RemoveTrustedRootCertificates(StoreLocation.CurrentUser); + + //current system + RemoveTrustedRootCertificates(StoreLocation.LocalMachine); + } + /// /// Create an SSL certificate /// @@ -339,6 +351,46 @@ internal void TrustRootCertificate(StoreLocation storeLocation) } } + /// + /// Remove the Root Certificate trust + /// + /// + /// + internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) + { + if (RootCertificate == null) + { + exceptionFunc( + new Exception("Could not set root certificate" + + " as system proxy since it is null or empty.")); + + return; + } + + X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); + var x509PersonalStore = new X509Store(StoreName.My, storeLocation); + + try + { + x509RootStore.Open(OpenFlags.ReadWrite); + x509PersonalStore.Open(OpenFlags.ReadWrite); + + x509RootStore.Remove(RootCertificate); + x509PersonalStore.Remove(RootCertificate); + } + catch (Exception e) + { + exceptionFunc( + new Exception("Failed to make system trust root certificate " + + $" for {storeLocation} store location. You may need admin rights.", e)); + } + finally + { + x509RootStore.Close(); + x509PersonalStore.Close(); + } + } + public void Dispose() { } From f6277d302c58e8582346fc4091fe388e57044fd7 Mon Sep 17 00:00:00 2001 From: Honfika Date: Mon, 15 May 2017 11:56:02 +0200 Subject: [PATCH 03/40] Certificate Issuer/Name setter fix --- Titanium.Web.Proxy/ProxyServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 720aee6b7..e0ce23bc3 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -80,7 +80,7 @@ public X509Certificate2 RootCertificate public string RootCertificateIssuerName { get { return CertificateManager.Issuer; } - set { CertificateManager.RootCertificateName = value; } + set { CertificateManager.Issuer = value; } } /// @@ -93,7 +93,7 @@ public string RootCertificateIssuerName public string RootCertificateName { get { return CertificateManager.RootCertificateName; } - set { CertificateManager.Issuer = value; } + set { CertificateManager.RootCertificateName = value; } } /// From 985890a56f83d14c979f26bad04004ebb1bb3777 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Mon, 15 May 2017 13:14:56 -0400 Subject: [PATCH 04/40] #184 add connection count; TODO: Find out why large number of client connections are active --- .../ProxyTestController.cs | 1 + .../Network/Tcp/TcpConnection.cs | 3 +- .../Network/Tcp/TcpConnectionFactory.cs | 4 - Titanium.Web.Proxy/ProxyServer.cs | 25 ++-- Titanium.Web.Proxy/RequestHandler.cs | 128 +++++++++++++----- Titanium.Web.Proxy/ResponseHandler.cs | 44 +++--- 6 files changed, 137 insertions(+), 68 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 7a27bc17d..2f995bb0c 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -106,6 +106,7 @@ public void Stop() //intecept & cancel redirect or update requests public async Task OnRequest(object sender, SessionEventArgs e) { + Console.WriteLine("Active Connections:" + (sender as ProxyServer).ClientConnectionCount); Console.WriteLine(e.WebSession.Request.Url); //read request headers diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs index 5b3bdfce5..46c4d1207 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs @@ -55,10 +55,11 @@ public void Dispose() { Stream?.Close(); Stream?.Dispose(); + StreamReader?.Dispose(); TcpClient.LingerState = new LingerOption(true, 0); - TcpClient.Close(); + TcpClient?.Close(); } } } diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index c5cf213ee..4f4795c2a 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -133,10 +133,6 @@ internal async Task CreateClient(int bufferSize, int connectionTi client.ReceiveTimeout = connectionTimeOutSeconds * 1000; client.SendTimeout = connectionTimeOutSeconds * 1000; - stream.ReadTimeout = connectionTimeOutSeconds * 1000; - stream.WriteTimeout = connectionTimeOutSeconds * 1000; - - return new TcpConnection { UpStreamHttpProxy = externalHttpProxy, diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index e0ce23bc3..eca634869 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -226,6 +226,8 @@ public Action ExceptionFunc public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; + public int ClientConnectionCount { get; private set; } + /// /// Constructor /// @@ -495,7 +497,7 @@ private void Listen(ProxyEndPoint endPoint) endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port); endPoint.Listener.Start(); - endPoint.Port = ((IPEndPoint) endPoint.Listener.LocalEndpoint).Port; + endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port; // accept clients asynchronously endPoint.Listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint); } @@ -560,7 +562,7 @@ private void EnsureRootCertificate() /// private void OnAcceptConnection(IAsyncResult asyn) { - var endPoint = (ProxyEndPoint) asyn.AsyncState; + var endPoint = (ProxyEndPoint)asyn.AsyncState; TcpClient tcpClient = null; @@ -586,6 +588,8 @@ private void OnAcceptConnection(IAsyncResult asyn) { Task.Run(async () => { + ClientConnectionCount++; + try { if (endPoint.GetType() == typeof(TransparentProxyEndPoint)) @@ -599,15 +603,14 @@ private void OnAcceptConnection(IAsyncResult asyn) } finally { - 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(); - } + //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(); + + ClientConnectionCount--; } }); } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 37e41d8e6..8367197cd 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -33,9 +33,10 @@ 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; + try { //read the first line HTTP command @@ -119,7 +120,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 }; } catch { @@ -156,7 +157,9 @@ await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamRea } catch (Exception) { - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, null); } } @@ -187,21 +190,24 @@ 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) { sslStream.Dispose(); - Dispose(sslStream, clientStreamReader, clientStreamWriter, null); + Dispose(sslStream, + clientStreamReader, + clientStreamWriter, null); + 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 @@ -212,7 +218,8 @@ await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamRea endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); } - private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, ExternalProxy customUpStreamHttpProxy, ExternalProxy customUpStreamHttpsProxy, bool closeConnection) + private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, + ExternalProxy customUpStreamHttpProxy, ExternalProxy customUpStreamHttpsProxy, bool closeConnection) { try { @@ -241,7 +248,10 @@ private async Task HandleHttpSessionRequestInternal(TcpConnection connection, Se args.IsHttps, SupportedSslProtocols, ValidateServerCertificate, SelectClientCertificate, - customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, args.ProxyClient.ClientStream, UpStreamEndPoint); + customUpStreamHttpProxy ?? UpStreamHttpProxy, + customUpStreamHttpsProxy ?? UpStreamHttpsProxy, + args.ProxyClient.ClientStream, + UpStreamEndPoint); } args.WebSession.Request.RequestLocked = true; @@ -249,8 +259,12 @@ private async Task HandleHttpSessionRequestInternal(TcpConnection connection, Se //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); - return; + Dispose(args.ProxyClient.ClientStream, + args.ProxyClient.ClientStreamReader, + args.ProxyClient.ClientStreamWriter, + args.WebSession.ServerConnection); + + return false; } //if expect continue is enabled then send the headers first @@ -314,28 +328,50 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", //If not expectation failed response was returned by server then parse response if (!args.WebSession.Request.ExpectationFailed) { - await HandleHttpSessionResponse(args); + var result = await HandleHttpSessionResponse(args); + + //already disposed inside above method + if (result == false) + { + return false; + } } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { - Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args); - return; + Dispose(args.ProxyClient.ClientStream, + args.ProxyClient.ClientStreamReader, + args.ProxyClient.ClientStreamWriter, + args.WebSession.ServerConnection); + + 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); - return; + + Dispose(args.ProxyClient.ClientStream, + args.ProxyClient.ClientStreamReader, + args.ProxyClient.ClientStreamWriter, + args.WebSession.ServerConnection); + + return false; } if (closeConnection) { //dispose - connection?.Dispose(); + Dispose(args.ProxyClient.ClientStream, + args.ProxyClient.ClientStreamReader, + args.ProxyClient.ClientStreamWriter, + args.WebSession.ServerConnection); + + return false; } + + return true; } /// @@ -356,28 +392,30 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null) { - TcpConnection connection = null; - //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) { if (string.IsNullOrEmpty(httpCmd)) { - Dispose(clientStream, clientStreamReader, clientStreamWriter, null); + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, + null); + break; } var args = new SessionEventArgs(BufferSize, HandleHttpSessionResponse) { - ProxyClient = {TcpClient = client}, - WebSession = {ConnectHeaders = connectHeaders} + ProxyClient = { TcpClient = client }, + WebSession = { ConnectHeaders = connectHeaders } }; args.WebSession.ProcessId = new Lazy(() => { - var remoteEndPoint = (IPEndPoint) args.ProxyClient.TcpClient.Client.RemoteEndPoint; + var remoteEndPoint = (IPEndPoint)args.ProxyClient.TcpClient.Client.RemoteEndPoint; //If client is localhost get the process id if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address)) @@ -388,6 +426,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St //can't access process Id of remote request from remote machine return -1; }); + try { //break up the line into three components (method, remote URL & Http Version) @@ -426,8 +465,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St { var existing = args.WebSession.Request.RequestHeaders[newHeader.Name]; - var nonUniqueHeaders = new List {existing, newHeader}; - + var nonUniqueHeaders = new List { existing, newHeader }; args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders); args.WebSession.Request.RequestHeaders.Remove(newHeader.Name); @@ -450,9 +488,14 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; - if (httpsHostName == null && (await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false)) + if (httpsHostName == null && + (await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false)) { - Dispose(clientStream, clientStreamReader, clientStreamWriter, args); + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, + args.WebSession.ServerConnection); + break; } @@ -467,7 +510,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St for (var i = 0; i < invocationList.Length; i++) { - handlerTasks[i] = ((Func) invocationList[i])(null, args); + handlerTasks[i] = ((Func)invocationList[i])(this, args); } await Task.WhenAll(handlerTasks); @@ -482,22 +525,41 @@ await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host SelectClientCertificate, clientStream, tcpConnectionFactory, UpStreamEndPoint); - Dispose(clientStream, clientStreamReader, clientStreamWriter, args); + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, + args.WebSession.ServerConnection); + break; } //construct the web request that we are going to issue on behalf of the client. - await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false); + var result = await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false); + if (result == false) + { + //already disposed inside above method + break; + } if (args.WebSession.Request.CancelRequest) { + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, + args.WebSession.ServerConnection); + break; } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, + args.WebSession.ServerConnection); + break; } @@ -507,13 +569,15 @@ await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request", e, args)); - Dispose(clientStream, clientStreamReader, clientStreamWriter, args); + + Dispose(clientStream, + clientStreamReader, + clientStreamWriter, + args.WebSession.ServerConnection); break; } } - //dispose - connection?.Dispose(); } /// diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index e3fce3059..e046e9d15 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -9,6 +9,8 @@ using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Helpers; +using System.Net.Sockets; +using Titanium.Web.Proxy.Network.Tcp; namespace Titanium.Web.Proxy { @@ -21,14 +23,14 @@ partial class ProxyServer /// Called asynchronously when a request was successfully and we received the response /// /// - /// - private async Task HandleHttpSessionResponse(SessionEventArgs args) + /// true if no errors + private async Task HandleHttpSessionResponse(SessionEventArgs args) { - //read response & headers from server - await args.WebSession.ReceiveResponse(); - try { + //read response & headers from server + await args.WebSession.ReceiveResponse(); + if (!args.WebSession.Response.ResponseBodyRead) { args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream; @@ -44,7 +46,7 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) for (int i = 0; i < invocationList.Length; i++) { - handlerTasks[i] = ((Func) invocationList[i])(this, args); + handlerTasks[i] = ((Func)invocationList[i])(this, args); } await Task.WhenAll(handlerTasks); @@ -53,7 +55,7 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) if (args.ReRequest) { await HandleHttpSessionRequestInternal(null, args, null, null, true); - return; + return true; } args.WebSession.Response.ResponseLocked = true; @@ -125,14 +127,16 @@ await args.WebSession.ServerConnection.StreamReader } catch (Exception e) { - ExceptionFunc(new ProxyHttpException("Error occured wilst handling session response", e, args)); + ExceptionFunc(new ProxyHttpException("Error occured whilst handling session response", e, args)); Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, - args.ProxyClient.ClientStreamWriter, args); - } - finally - { - args.Dispose(); + args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); + + return false; } + + args.Dispose(); + + return true; } /// @@ -219,24 +223,24 @@ private void FixProxyHeaders(Dictionary headers) } /// - /// Handle dispose of a client/server session + /// Handle dispose of a client/server session /// /// /// /// - /// - private void Dispose(Stream clientStream, CustomBinaryReader clientStreamReader, - StreamWriter clientStreamWriter, IDisposable args) + /// + private void Dispose(Stream clientStream, + CustomBinaryReader clientStreamReader, + StreamWriter clientStreamWriter, + TcpConnection serverConnection) { clientStream?.Close(); clientStream?.Dispose(); clientStreamReader?.Dispose(); - - clientStreamWriter?.Close(); clientStreamWriter?.Dispose(); - args?.Dispose(); + serverConnection?.Dispose(); } } } From 922398fe602aa4eb727243ab5fed3473163e80de Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Mon, 15 May 2017 13:23:58 -0400 Subject: [PATCH 05/40] #184 move linger state experimentally to avoid object disposed exception on Close --- Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs | 1 - .../Network/Tcp/TcpConnectionFactory.cs | 2 ++ Titanium.Web.Proxy/ProxyServer.cs | 15 +++++++-------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs index 46c4d1207..70ced1fc0 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs @@ -58,7 +58,6 @@ public void Dispose() StreamReader?.Dispose(); - TcpClient.LingerState = new LingerOption(true, 0); TcpClient?.Close(); } } diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 4f4795c2a..dccf8fa31 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -133,6 +133,8 @@ internal async Task CreateClient(int bufferSize, int connectionTi client.ReceiveTimeout = connectionTimeOutSeconds * 1000; client.SendTimeout = connectionTimeOutSeconds * 1000; + client.LingerState = new LingerOption(true, 0); + return new TcpConnection { UpStreamHttpProxy = externalHttpProxy, diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index eca634869..0fdaec68d 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -583,13 +583,18 @@ private void OnAcceptConnection(IAsyncResult asyn) //Other errors are discarded to keep proxy running } - if (tcpClient != null) { Task.Run(async () => { ClientConnectionCount++; + //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)) @@ -603,14 +608,8 @@ private void OnAcceptConnection(IAsyncResult asyn) } finally { - //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(); - ClientConnectionCount--; + tcpClient?.Close(); } }); } From 45b0d873c154ed866fc1b302ae70a6a472657951 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Mon, 15 May 2017 17:52:11 -0400 Subject: [PATCH 06/40] Add Server Connection Count; Reduce Params --- Titanium.Web.Proxy/CertificateHandler.cs | 4 +- Titanium.Web.Proxy/Helpers/Tcp.cs | 24 +++----- .../Network/Tcp/TcpConnectionFactory.cs | 56 +++++++++---------- Titanium.Web.Proxy/ProxyServer.cs | 9 +++ Titanium.Web.Proxy/RequestHandler.cs | 28 ++++------ Titanium.Web.Proxy/ResponseHandler.cs | 2 + 6 files changed, 59 insertions(+), 64 deletions(-) diff --git a/Titanium.Web.Proxy/CertificateHandler.cs b/Titanium.Web.Proxy/CertificateHandler.cs index 275ffde1c..134798937 100644 --- a/Titanium.Web.Proxy/CertificateHandler.cs +++ b/Titanium.Web.Proxy/CertificateHandler.cs @@ -16,7 +16,7 @@ public partial class ProxyServer /// /// /// - private bool ValidateServerCertificate( + internal bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, @@ -65,7 +65,7 @@ private bool ValidateServerCertificate( /// /// /// - private X509Certificate SelectClientCertificate( + internal X509Certificate SelectClientCertificate( object sender, string targetHost, X509CertificateCollection localCertificates, diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index 8f38b802e..aefc96dd8 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -127,26 +127,21 @@ internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion) /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix /// Usefull for websocket requests /// - /// - /// + /// /// + /// /// /// /// /// - /// - /// - /// - /// /// /// - /// /// - internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds, - string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary requestHeaders, - bool isHttps, SslProtocols supportedProtocols, - RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback, - Stream clientStream, TcpConnectionFactory tcpConnectionFactory, IPEndPoint upStreamEndPoint) + internal static async Task SendRaw(ProxyServer server, + string remoteHostName, int remotePort, + string httpCmd, Version httpVersion, Dictionary requestHeaders, + bool isHttps, + Stream clientStream, TcpConnectionFactory tcpConnectionFactory) { //prepare the prefix content StringBuilder sb = null; @@ -172,11 +167,10 @@ internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds, sb.Append(ProxyConstants.NewLine); } - var tcpConnection = await tcpConnectionFactory.CreateClient(bufferSize, connectionTimeOutSeconds, + var tcpConnection = await tcpConnectionFactory.CreateClient(server, remoteHostName, remotePort, httpVersion, isHttps, - supportedProtocols, remoteCertificateValidationCallback, localCertificateSelectionCallback, - null, null, clientStream, upStreamEndPoint); + null, null, clientStream); try { diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index dccf8fa31..2bef02038 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -6,43 +6,35 @@ using System.Net.Security; using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Models; -using System.Security.Authentication; using System.Linq; using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Shared; namespace Titanium.Web.Proxy.Network.Tcp { - using System.Net; - /// /// A class that manages Tcp Connection to server used by this proxy server /// internal class TcpConnectionFactory { + /// /// Creates a TCP connection to server /// - /// - /// + /// /// + /// /// /// - /// - /// - /// - /// /// /// /// - /// /// - internal async Task CreateClient(int bufferSize, int connectionTimeOutSeconds, + internal async Task CreateClient(ProxyServer server, string remoteHostName, int remotePort, Version httpVersion, - bool isHttps, SslProtocols supportedSslProtocols, - RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback, + bool isHttps, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy, - Stream clientStream, IPEndPoint upStreamEndPoint) + Stream clientStream) { TcpClient client; CustomBufferedStream stream; @@ -59,11 +51,11 @@ internal async Task CreateClient(int bufferSize, int connectionTi //If this proxy uses another external proxy then create a tunnel request for HTTPS connections if (useHttpsProxy) { - client = new TcpClient(upStreamEndPoint); + client = new TcpClient(server.UpStreamEndPoint); await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port); - stream = new CustomBufferedStream(client.GetStream(), bufferSize); + stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); - using (var writer = new StreamWriter(stream, Encoding.ASCII, 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}"); @@ -79,7 +71,7 @@ internal async Task CreateClient(int bufferSize, int connectionTi writer.Close(); } - using (var reader = new CustomBinaryReader(stream, bufferSize)) + using (var reader = new CustomBinaryReader(stream, server.BufferSize)) { var result = await reader.ReadLineAsync(); @@ -93,19 +85,19 @@ internal async Task CreateClient(int bufferSize, int connectionTi } else { - client = new TcpClient(upStreamEndPoint); + client = new TcpClient(server.UpStreamEndPoint); await client.ConnectAsync(remoteHostName, remotePort); - stream = new CustomBufferedStream(client.GetStream(), bufferSize); + stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); } try { - sslStream = new SslStream(stream, true, remoteCertificateValidationCallback, - localCertificateSelectionCallback); + sslStream = new SslStream(stream, true, server.ValidateServerCertificate, + server.SelectClientCertificate); - await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false); + await sslStream.AuthenticateAsClientAsync(remoteHostName, null, server.SupportedSslProtocols, false); - stream = new CustomBufferedStream(sslStream, bufferSize); + stream = new CustomBufferedStream(sslStream, server.BufferSize); } catch { @@ -118,23 +110,25 @@ internal async Task CreateClient(int bufferSize, int connectionTi { if (useHttpProxy) { - client = new TcpClient(upStreamEndPoint); + client = new TcpClient(server.UpStreamEndPoint); await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port); - stream = new CustomBufferedStream(client.GetStream(), bufferSize); + stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); } else { - client = new TcpClient(upStreamEndPoint); + client = new TcpClient(server.UpStreamEndPoint); await client.ConnectAsync(remoteHostName, remotePort); - stream = new CustomBufferedStream(client.GetStream(), bufferSize); + stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); } } - client.ReceiveTimeout = connectionTimeOutSeconds * 1000; - client.SendTimeout = connectionTimeOutSeconds * 1000; + client.ReceiveTimeout = server.ConnectionTimeOutSeconds * 1000; + client.SendTimeout = server.ConnectionTimeOutSeconds * 1000; client.LingerState = new LingerOption(true, 0); + server.ServerConnectionCount++; + return new TcpConnection { UpStreamHttpProxy = externalHttpProxy, @@ -143,7 +137,7 @@ internal async Task CreateClient(int bufferSize, int connectionTi Port = remotePort, IsHttps = isHttps, TcpClient = client, - StreamReader = new CustomBinaryReader(stream, bufferSize), + StreamReader = new CustomBinaryReader(stream, server.BufferSize), Stream = stream, Version = httpVersion }; diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 0fdaec68d..a4174e10e 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -226,8 +226,17 @@ public Action ExceptionFunc public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; + /// + /// Total number of active client connections + /// public int ClientConnectionCount { get; private set; } + + /// + /// Total number of active server connections + /// + public int ServerConnectionCount { get; internal set; } + /// /// Constructor /// diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 8367197cd..846c38b53 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -141,12 +141,11 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, //write back successfull CONNECT response await WriteConnectResponse(clientStreamWriter, version); - await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, + await TcpHelper.SendRaw(this, + httpRemoteUri.Host, httpRemoteUri.Port, null, version, null, - false, SupportedSslProtocols, - ValidateServerCertificate, - SelectClientCertificate, - clientStream, tcpConnectionFactory, UpStreamEndPoint); + false, + clientStream, tcpConnectionFactory); Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; @@ -243,15 +242,13 @@ private async Task HandleHttpSessionRequestInternal(TcpConnection connecti args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy; - connection = await tcpConnectionFactory.CreateClient(BufferSize, ConnectionTimeOutSeconds, - args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, - args.IsHttps, SupportedSslProtocols, - ValidateServerCertificate, - SelectClientCertificate, + connection = await tcpConnectionFactory.CreateClient(this, + args.WebSession.Request.RequestUri.Host, + args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, + args.IsHttps, customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, - args.ProxyClient.ClientStream, - UpStreamEndPoint); + args.ProxyClient.ClientStream); } args.WebSession.Request.RequestLocked = true; @@ -519,11 +516,10 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { - await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, + await TcpHelper.SendRaw(this, + httpRemoteUri.Host, httpRemoteUri.Port, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, - SupportedSslProtocols, ValidateServerCertificate, - SelectClientCertificate, - clientStream, tcpConnectionFactory, UpStreamEndPoint); + clientStream, tcpConnectionFactory); Dispose(clientStream, clientStreamReader, diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index e046e9d15..70b22ef28 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -234,6 +234,8 @@ private void Dispose(Stream clientStream, StreamWriter clientStreamWriter, TcpConnection serverConnection) { + ServerConnectionCount--; + clientStream?.Close(); clientStream?.Dispose(); From eed3ff9b1d162dc488d148e033eb7b1fc35fc1fa Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Mon, 15 May 2017 17:52:31 -0400 Subject: [PATCH 07/40] print server connections for debugging --- .../ProxyTestController.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 2f995bb0c..b2d821def 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -106,7 +106,7 @@ public void Stop() //intecept & cancel redirect or update requests public async Task OnRequest(object sender, SessionEventArgs e) { - Console.WriteLine("Active Connections:" + (sender as ProxyServer).ClientConnectionCount); + Console.WriteLine("Active Client Connections:" + (sender as ProxyServer).ClientConnectionCount); Console.WriteLine(e.WebSession.Request.Url); //read request headers @@ -149,7 +149,9 @@ await e.Ok("" + //Modify response public async Task OnResponse(object sender, SessionEventArgs e) { - if(requestBodyHistory.ContainsKey(e.Id)) + Console.WriteLine("Active Server Connections:" + (sender as ProxyServer).ServerConnectionCount); + + if (requestBodyHistory.ContainsKey(e.Id)) { //access request body by looking up the shared dictionary using requestId var requestBody = requestBodyHistory[e.Id]; @@ -159,7 +161,7 @@ public async Task OnResponse(object sender, SessionEventArgs e) var responseHeaders = e.WebSession.Response.ResponseHeaders; // print out process id of current session - Console.WriteLine($"PID: {e.WebSession.ProcessId.Value}"); + //Console.WriteLine($"PID: {e.WebSession.ProcessId.Value}"); //if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return; if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST") From ac58fee5465cf4878ee367bfad15975ca1b02cea Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Mon, 15 May 2017 17:54:55 -0400 Subject: [PATCH 08/40] decrement server connection when closed --- Titanium.Web.Proxy/Helpers/Tcp.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index aefc96dd8..2147e7290 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -186,6 +186,7 @@ internal static async Task SendRaw(ProxyServer server, finally { tcpConnection.Dispose(); + server.ServerConnectionCount--; } } } From 90c935ea6116e4a4b5d5867548ec401a1fbefbb8 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 00:13:03 +0200 Subject: [PATCH 09/40] - Create less TcpRow objects - Use static Http Verion objects - Common HTTP header parser (request, response) - Code duplication removed from ProxyAuthorizationHandler - Fix? for issue #227 --- .../ProxyTestController.cs | 7 +- .../EventArguments/SessionEventArgs.cs | 3 +- .../Extensions/TcpExtensions.cs | 21 +++++ .../Helpers/CustomBinaryReader.cs | 4 +- .../Helpers/CustomBufferedStream.cs | 2 + Titanium.Web.Proxy/Helpers/Network.cs | 3 +- Titanium.Web.Proxy/Helpers/Tcp.cs | 46 ++++++++++ Titanium.Web.Proxy/Http/HeaderParser.cs | 45 ++++++++++ Titanium.Web.Proxy/Http/HttpWebClient.cs | 41 ++------- Titanium.Web.Proxy/Http/Request.cs | 4 +- Titanium.Web.Proxy/Models/HttpHeader.cs | 4 + Titanium.Web.Proxy/Network/Tcp/TcpRow.cs | 35 ++++++-- .../ProxyAuthorizationHandler.cs | 89 ++++++------------- Titanium.Web.Proxy/RequestHandler.cs | 53 +++-------- Titanium.Web.Proxy/ResponseHandler.cs | 1 - Titanium.Web.Proxy/Titanium.Web.Proxy.csproj | 1 + 16 files changed, 203 insertions(+), 156 deletions(-) create mode 100644 Titanium.Web.Proxy/Http/HeaderParser.cs diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index b2d821def..0daadd298 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; @@ -106,14 +107,14 @@ public void Stop() //intecept & cancel redirect or update requests public async Task OnRequest(object sender, SessionEventArgs e) { - Console.WriteLine("Active Client Connections:" + (sender as ProxyServer).ClientConnectionCount); + Console.WriteLine("Active Client Connections:" + ((ProxyServer) sender).ClientConnectionCount); Console.WriteLine(e.WebSession.Request.Url); //read request headers var requestHeaders = e.WebSession.Request.RequestHeaders; var method = e.WebSession.Request.Method.ToUpper(); - if ((method == "POST" || method == "PUT" || method == "PATCH")) + if (method == "POST" || method == "PUT" || method == "PATCH") { //Get/Set request body bytes byte[] bodyBytes = await e.GetRequestBody(); @@ -168,7 +169,7 @@ public async Task OnResponse(object sender, SessionEventArgs e) { if (e.WebSession.Response.ResponseStatusCode == "200") { - if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html")) + if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html")) { byte[] bodyBytes = await e.GetResponseBody(); await e.SetResponseBody(bodyBytes); diff --git a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index fda283883..f70bf8550 100644 --- a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -93,7 +93,7 @@ private async Task ReadRequestBody() { //GET request don't have a request body to read var method = WebSession.Request.Method.ToUpper(); - if ((method != "POST" && method != "PUT" && method != "PATCH")) + if (method != "POST" && method != "PUT" && method != "PATCH") { throw new BodyNotFoundException("Request don't have a body. " + "Please verify that this request is a Http POST/PUT/PATCH and request " + @@ -411,6 +411,7 @@ public async Task Ok(byte[] result, Dictionary headers) { response.ResponseHeaders = headers; } + response.HttpVersion = WebSession.Request.HttpVersion; response.ResponseBody = result; diff --git a/Titanium.Web.Proxy/Extensions/TcpExtensions.cs b/Titanium.Web.Proxy/Extensions/TcpExtensions.cs index 7657aadea..8dc4d5a64 100644 --- a/Titanium.Web.Proxy/Extensions/TcpExtensions.cs +++ b/Titanium.Web.Proxy/Extensions/TcpExtensions.cs @@ -1,4 +1,5 @@ using System.Net.Sockets; +using Titanium.Web.Proxy.Helpers; namespace Titanium.Web.Proxy.Extensions { @@ -32,5 +33,25 @@ internal static bool IsConnected(this Socket client) client.Blocking = blockingState; } } + + /// + /// Gets the local port from a native TCP row object. + /// + /// The TCP row. + /// The local port + internal static int GetLocalPort(this NativeMethods.TcpRow tcpRow) + { + return (tcpRow.localPort1 << 8) + tcpRow.localPort2 + (tcpRow.localPort3 << 24) + (tcpRow.localPort4 << 16); + } + + /// + /// Gets the remote port from a native TCP row object. + /// + /// The TCP row. + /// The remote port + internal static int GetRemotePort(this NativeMethods.TcpRow tcpRow) + { + return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16); + } } } diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index 77028ec46..81bb47ab6 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -123,9 +123,11 @@ internal async Task ReadBytesAsync(long totalBytesToRead) int bytesToRead = bufferSize; if (totalBytesToRead < bufferSize) + { bytesToRead = (int) totalBytesToRead; + } - var buffer = staticBuffer; + var buffer = new byte[bytesToRead]; int bytesRead; var totalBytesRead = 0; diff --git a/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs b/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs index 825faa5eb..78fa6db21 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs @@ -168,6 +168,7 @@ public override async Task CopyToAsync(Stream destination, int bufferSize, Cance if (bufferLength > 0) { await destination.WriteAsync(streamBuffer, bufferPos, bufferLength, cancellationToken); + bufferLength = 0; } await baseStream.CopyToAsync(destination, bufferSize, cancellationToken); @@ -307,6 +308,7 @@ public byte ReadByteFromBuffer() /// /// A task that represents the asynchronous write operation. /// + [DebuggerStepThrough] public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return baseStream.WriteAsync(buffer, offset, count, cancellationToken); diff --git a/Titanium.Web.Proxy/Helpers/Network.cs b/Titanium.Web.Proxy/Helpers/Network.cs index 121926813..7ee2d9a55 100644 --- a/Titanium.Web.Proxy/Helpers/Network.cs +++ b/Titanium.Web.Proxy/Helpers/Network.cs @@ -8,8 +8,7 @@ internal class NetworkHelper { private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion) { - var tcpRow = TcpHelper.GetExtendedTcpTable(ipVersion).FirstOrDefault( - row => row.LocalEndPoint.Port == port); + var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port); return tcpRow?.ProcessId ?? 0; } diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index 2147e7290..5261ec338 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -123,6 +123,52 @@ internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion) return new TcpTable(tcpRows); } + /// + /// Gets the TCP row by local port number. + /// + /// . + internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort) + { + IntPtr tcpTable = IntPtr.Zero; + int tcpTableLength = 0; + + var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6; + + if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) != 0) + { + try + { + tcpTable = Marshal.AllocHGlobal(tcpTableLength); + if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) == 0) + { + NativeMethods.TcpTable table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable)); + + IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length)); + + for (int i = 0; i < table.length; ++i) + { + var tcpRow = (NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow)); + if (tcpRow.GetLocalPort() == localPort) + { + return new TcpRow(tcpRow); + } + + rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow))); + } + } + } + finally + { + if (tcpTable != IntPtr.Zero) + { + Marshal.FreeHGlobal(tcpTable); + } + } + } + + return null; + } + /// /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix /// Usefull for websocket requests diff --git a/Titanium.Web.Proxy/Http/HeaderParser.cs b/Titanium.Web.Proxy/Http/HeaderParser.cs new file mode 100644 index 000000000..27d8e051a --- /dev/null +++ b/Titanium.Web.Proxy/Http/HeaderParser.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Titanium.Web.Proxy.Helpers; +using Titanium.Web.Proxy.Models; +using Titanium.Web.Proxy.Shared; + +namespace Titanium.Web.Proxy.Http +{ + internal static class HeaderParser + { + internal static async Task ReadHeaders(CustomBinaryReader reader, + Dictionary> nonUniqueResponseHeaders, + Dictionary headers) + { + string tmpLine; + while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync())) + { + var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); + + var newHeader = new HttpHeader(header[0], header[1]); + + //if header exist in non-unique header collection add it there + if (nonUniqueResponseHeaders.ContainsKey(newHeader.Name)) + { + nonUniqueResponseHeaders[newHeader.Name].Add(newHeader); + } + //if header is alread in unique header collection then move both to non-unique collection + else if (headers.ContainsKey(newHeader.Name)) + { + var existing = headers[newHeader.Name]; + + var nonUniqueHeaders = new List { existing, newHeader }; + + nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders); + headers.Remove(newHeader.Name); + } + //add to unique header collection + else + { + headers.Add(newHeader.Name, newHeader); + } + } + } + } +} diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index b91618223..46aa379c7 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -170,10 +170,10 @@ internal async Task ReceiveResponse() var httpVersion = httpResult[0].Trim().ToLower(); - var version = new Version(1, 1); - if (0 == string.CompareOrdinal(httpVersion, "http/1.0")) + var version = HttpHeader.Version11; + if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase)) { - version = new Version(1, 0); + version = HttpHeader.Version10; } Response.HttpVersion = version; @@ -192,8 +192,9 @@ internal async Task ReceiveResponse() await ReceiveResponse(); return; } - else if (Response.ResponseStatusCode.Equals("417") - && Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase)) + + if (Response.ResponseStatusCode.Equals("417") + && Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase)) { //read next line after expectation failed response Response.ExpectationFailed = true; @@ -204,36 +205,8 @@ internal async Task ReceiveResponse() return; } - //Read the Response headers //Read the response headers in to unique and non-unique header collections - string tmpLine; - while (!string.IsNullOrEmpty(tmpLine = await ServerConnection.StreamReader.ReadLineAsync())) - { - var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); - - var newHeader = new HttpHeader(header[0], header[1]); - - //if header exist in non-unique header collection add it there - if (Response.NonUniqueResponseHeaders.ContainsKey(newHeader.Name)) - { - Response.NonUniqueResponseHeaders[newHeader.Name].Add(newHeader); - } - //if header is alread in unique header collection then move both to non-unique collection - else if (Response.ResponseHeaders.ContainsKey(newHeader.Name)) - { - var existing = Response.ResponseHeaders[newHeader.Name]; - - var nonUniqueHeaders = new List {existing, newHeader}; - - Response.NonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders); - Response.ResponseHeaders.Remove(newHeader.Name); - } - //add to unique header collection - else - { - Response.ResponseHeaders.Add(newHeader.Name, newHeader); - } - } + await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.NonUniqueResponseHeaders, Response.ResponseHeaders); } } } diff --git a/Titanium.Web.Proxy/Http/Request.cs b/Titanium.Web.Proxy/Http/Request.cs index 8adf03842..949cd5570 100644 --- a/Titanium.Web.Proxy/Http/Request.cs +++ b/Titanium.Web.Proxy/Http/Request.cs @@ -241,9 +241,9 @@ 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/Models/HttpHeader.cs b/Titanium.Web.Proxy/Models/HttpHeader.cs index 6f884e720..c5a678e69 100644 --- a/Titanium.Web.Proxy/Models/HttpHeader.cs +++ b/Titanium.Web.Proxy/Models/HttpHeader.cs @@ -9,6 +9,10 @@ namespace Titanium.Web.Proxy.Models /// public class HttpHeader { + internal static Version Version10 = new Version(1, 0); + + internal static Version Version11 = new Version(1, 1); + /// /// Constructor. /// diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpRow.cs b/Titanium.Web.Proxy/Network/Tcp/TcpRow.cs index 9a8a5f6c3..5bc306c05 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpRow.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpRow.cs @@ -1,4 +1,5 @@ using System.Net; +using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Helpers; namespace Titanium.Web.Proxy.Network.Tcp @@ -17,24 +18,42 @@ internal TcpRow(NativeMethods.TcpRow tcpRow) { ProcessId = tcpRow.owningPid; - int localPort = (tcpRow.localPort1 << 8) + (tcpRow.localPort2) + (tcpRow.localPort3 << 24) + (tcpRow.localPort4 << 16); - long localAddress = tcpRow.localAddr; - LocalEndPoint = new IPEndPoint(localAddress, localPort); + LocalPort = tcpRow.GetLocalPort(); + LocalAddress = tcpRow.localAddr; - int remotePort = (tcpRow.remotePort1 << 8) + (tcpRow.remotePort2) + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16); - long remoteAddress = tcpRow.remoteAddr; - RemoteEndPoint = new IPEndPoint(remoteAddress, remotePort); + RemotePort = tcpRow.GetRemotePort(); + RemoteAddress = tcpRow.remoteAddr; } + /// + /// Gets the local end point address. + /// + internal long LocalAddress { get; } + + /// + /// Gets the local end point port. + /// + internal int LocalPort { get; } + /// /// Gets the local end point. /// - internal IPEndPoint LocalEndPoint { get; } + internal IPEndPoint LocalEndPoint => new IPEndPoint(LocalAddress, LocalPort); + + /// + /// Gets the remote end point address. + /// + internal long RemoteAddress { get; } + + /// + /// Gets the remote end point port. + /// + internal int RemotePort { get; } /// /// Gets the remote end point. /// - internal IPEndPoint RemoteEndPoint { get; } + internal IPEndPoint RemoteEndPoint => new IPEndPoint(RemoteAddress, RemotePort); /// /// Gets the process identifier. diff --git a/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs b/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs index 9c9a78e4e..c8279925c 100644 --- a/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs +++ b/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs @@ -19,78 +19,36 @@ private async Task CheckAuthorization(StreamWriter clientStreamWriter, IEn return true; } - var httpHeaders = headers as HttpHeader[] ?? headers.ToArray(); + var httpHeaders = headers as ICollection ?? headers.ToArray(); try { if (httpHeaders.All(t => t.Name != "Proxy-Authorization")) { - await WriteResponseStatus(new Version(1, 1), "407", - "Proxy Authentication Required", clientStreamWriter); - var response = new Response - { - ResponseHeaders = new Dictionary - { - { - "Proxy-Authenticate", - new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"") - }, - {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} - } - }; - await WriteResponseHeaders(clientStreamWriter, response); - - await clientStreamWriter.WriteLineAsync(); + await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Required"); return false; } + var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization"); - if (null == header) throw new NullReferenceException(); + if (header == null) throw new NullReferenceException(); var headerValue = header.Value.Trim(); if (!headerValue.StartsWith("basic", StringComparison.CurrentCultureIgnoreCase)) { //Return not authorized - await WriteResponseStatus(new Version(1, 1), "407", - "Proxy Authentication Invalid", clientStreamWriter); - var response = new Response - { - ResponseHeaders = new Dictionary - { - { - "Proxy-Authenticate", - new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"") - }, - {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} - } - }; - await WriteResponseHeaders(clientStreamWriter, response); - - await clientStreamWriter.WriteLineAsync(); + await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid"); return false; } + headerValue = headerValue.Substring(5).Trim(); var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue)); if (decoded.Contains(":") == false) { //Return not authorized - await WriteResponseStatus(new Version(1, 1), "407", - "Proxy Authentication Invalid", clientStreamWriter); - var response = new Response - { - ResponseHeaders = new Dictionary - { - { - "Proxy-Authenticate", - new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"") - }, - {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} - } - }; - await WriteResponseHeaders(clientStreamWriter, response); - - await clientStreamWriter.WriteLineAsync(); + await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid"); return false; } + var username = decoded.Substring(0, decoded.IndexOf(':')); var password = decoded.Substring(decoded.IndexOf(':') + 1); return await AuthenticateUserFunc(username, password); @@ -98,22 +56,27 @@ await WriteResponseStatus(new Version(1, 1), "407", catch (Exception e) { ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", e, httpHeaders)); - //Return not authorized - await WriteResponseStatus(new Version(1, 1), "407", - "Proxy Authentication Invalid", clientStreamWriter); - var response = new Response - { - ResponseHeaders = new Dictionary - { - {"Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")}, - {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} - } - }; - await WriteResponseHeaders(clientStreamWriter, response); - await clientStreamWriter.WriteLineAsync(); + //Return not authorized + await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid"); return false; } } + + private async Task SendAuthentication407Response(StreamWriter clientStreamWriter, string description) + { + await WriteResponseStatus(HttpHeader.Version11, "407", description, clientStreamWriter); + var response = new Response + { + ResponseHeaders = new Dictionary + { + {"Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")}, + {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} + } + }; + await WriteResponseHeaders(clientStreamWriter, response); + + await clientStreamWriter.WriteLineAsync(); + } } } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 846c38b53..624c71546 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -57,14 +57,14 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli httpRemoteUri = httpVerb == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]); //parse the HTTP version - var version = new Version(1, 1); + var version = HttpHeader.Version11; if (httpCmdSplit.Length == 3) { var httpVersion = httpCmdSplit[2].Trim(); - if (0 == string.CompareOrdinal(httpVersion, "http/1.0")) + if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase)) { - version = new Version(1, 0); + version = HttpHeader.Version10; } } @@ -87,8 +87,8 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli if (httpVerb == "CONNECT" && !excluded && httpRemoteUri.Port != 80) { httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); - string tmpLine; connectRequestHeaders = new List(); + string tmpLine; while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) { var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); @@ -432,50 +432,22 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St var httpMethod = httpCmdSplit[0]; //find the request HTTP version - var httpVersion = new Version(1, 1); + var httpVersion = HttpHeader.Version11; if (httpCmdSplit.Length == 3) { - var httpVersionString = httpCmdSplit[2].ToLower().Trim(); + var httpVersionString = httpCmdSplit[2].Trim(); - if (0 == string.CompareOrdinal(httpVersionString, "http/1.0")) + if (string.Equals(httpVersionString, "HTTP/1.0", StringComparison.OrdinalIgnoreCase)) { - httpVersion = new Version(1, 0); + httpVersion = HttpHeader.Version10; } } - //Read the request headers in to unique and non-unique header collections - string tmpLine; - while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) - { - var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); - - var newHeader = new HttpHeader(header[0], header[1]); - - //if header exist in non-unique header collection add it there - if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey(newHeader.Name)) - { - args.WebSession.Request.NonUniqueRequestHeaders[newHeader.Name].Add(newHeader); - } - //if header is alread in unique header collection then move both to non-unique collection - else if (args.WebSession.Request.RequestHeaders.ContainsKey(newHeader.Name)) - { - var existing = args.WebSession.Request.RequestHeaders[newHeader.Name]; - - var nonUniqueHeaders = new List { existing, newHeader }; - - args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders); - args.WebSession.Request.RequestHeaders.Remove(newHeader.Name); - } - //add to unique header collection - else - { - args.WebSession.Request.RequestHeaders.Add(newHeader.Name, newHeader); - } - } + await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.NonUniqueRequestHeaders, args.WebSession.Request.RequestHeaders); var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1] - : (string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1]))); + : string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1])); args.WebSession.Request.RequestUri = httpRemoteUri; @@ -483,10 +455,9 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St args.WebSession.Request.HttpVersion = httpVersion; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; - args.ProxyClient.ClientStreamWriter = clientStreamWriter; + args.ProxyClient.ClientStreamWriter = clientStreamWriter; - if (httpsHostName == null && - (await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false)) + if (httpsHostName == null && await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false) { Dispose(clientStream, clientStreamReader, diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 70b22ef28..3a8ac88a8 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -9,7 +9,6 @@ using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Helpers; -using System.Net.Sockets; using Titanium.Web.Proxy.Network.Tcp; namespace Titanium.Web.Proxy diff --git a/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj b/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj index c3eb67485..6b186297c 100644 --- a/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj +++ b/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj @@ -77,6 +77,7 @@ + From 5e9e1297ba4dcb09b9866c324c34ddbde8f0a3a8 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 00:23:04 +0200 Subject: [PATCH 10/40] Fix for #227 --- .../Helpers/CustomBinaryReader.cs | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index 81bb47ab6..6d10c3d91 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -15,27 +15,14 @@ internal class CustomBinaryReader : IDisposable { private readonly CustomBufferedStream stream; private readonly int bufferSize; + private readonly byte[] staticBuffer; private readonly Encoding encoding; - [ThreadStatic] - private static byte[] staticBufferField; - - private byte[] staticBuffer - { - get - { - if (staticBufferField == null || staticBufferField.Length != bufferSize) - { - staticBufferField = new byte[bufferSize]; - } - - return staticBufferField; - } - } - internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize) { this.stream = stream; + staticBuffer = new byte[bufferSize]; + this.bufferSize = bufferSize; //default to UTF-8 @@ -122,12 +109,13 @@ internal async Task ReadBytesAsync(long totalBytesToRead) { int bytesToRead = bufferSize; + var buffer = staticBuffer; if (totalBytesToRead < bufferSize) { bytesToRead = (int) totalBytesToRead; + buffer = new byte[bytesToRead]; } - var buffer = new byte[bytesToRead]; int bytesRead; var totalBytesRead = 0; From d5082b42712c69959650627ceaed84639baa7999 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Mon, 15 May 2017 18:50:47 -0400 Subject: [PATCH 11/40] Fix server connection reuse --- Titanium.Web.Proxy/RequestHandler.cs | 80 ++++++++++++++++----------- Titanium.Web.Proxy/ResponseHandler.cs | 9 ++- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 624c71546..3a3013724 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -141,10 +141,10 @@ 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); @@ -217,40 +217,45 @@ await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamRea endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); } - private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, - ExternalProxy customUpStreamHttpProxy, ExternalProxy customUpStreamHttpsProxy, bool closeConnection) + private async Task GetServerConnection( + SessionEventArgs args, + ExternalProxy customUpStreamHttpProxy, + ExternalProxy customUpStreamHttpsProxy, bool closeConnection) { - try + + if (args.WebSession.Request.RequestUri.Scheme == "http") { - if (connection == null) + if (GetCustomUpStreamHttpProxyFunc != null) { - if (args.WebSession.Request.RequestUri.Scheme == "http") - { - if (GetCustomUpStreamHttpProxyFunc != null) - { - customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args); - } - } - else - { - if (GetCustomUpStreamHttpsProxyFunc != null) - { - customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args); - } - } + customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args); + } + } + else + { + if (GetCustomUpStreamHttpsProxyFunc != null) + { + customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args); + } + } - args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; - args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy; + args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; + args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy; + + return await tcpConnectionFactory.CreateClient(this, + args.WebSession.Request.RequestUri.Host, + args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, + args.IsHttps, + customUpStreamHttpProxy ?? UpStreamHttpProxy, + customUpStreamHttpsProxy ?? UpStreamHttpsProxy, + args.ProxyClient.ClientStream); + } - connection = await tcpConnectionFactory.CreateClient(this, - args.WebSession.Request.RequestUri.Host, - args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, - args.IsHttps, - customUpStreamHttpProxy ?? UpStreamHttpProxy, - customUpStreamHttpsProxy ?? UpStreamHttpsProxy, - args.ProxyClient.ClientStream); - } + private async Task HandleHttpSessionRequestInternal(TcpConnection connection, + SessionEventArgs args, bool closeConnection) + { + try + { args.WebSession.Request.RequestLocked = true; //If request was cancelled by user then dispose the client @@ -389,6 +394,8 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null) { + TcpConnection connection = null; + //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) @@ -455,7 +462,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St args.WebSession.Request.HttpVersion = httpVersion; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; - args.ProxyClient.ClientStreamWriter = clientStreamWriter; + args.ProxyClient.ClientStreamWriter = clientStreamWriter; if (httpsHostName == null && await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false) { @@ -487,7 +494,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { - await TcpHelper.SendRaw(this, + await TcpHelper.SendRaw(this, httpRemoteUri.Host, httpRemoteUri.Port, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, clientStream, tcpConnectionFactory); @@ -500,8 +507,13 @@ await TcpHelper.SendRaw(this, break; } + if (connection == null) + { + connection = await GetServerConnection(args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false); + } + //construct the web request that we are going to issue on behalf of the client. - var result = await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false); + var result = await HandleHttpSessionRequestInternal(connection, args, false); if (result == false) { @@ -545,6 +557,8 @@ await TcpHelper.SendRaw(this, } } + connection?.Dispose(); + } /// diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 3a8ac88a8..870fd2523 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -53,7 +53,14 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) if (args.ReRequest) { - await HandleHttpSessionRequestInternal(null, args, null, null, true); + if(args.WebSession.ServerConnection != null) + { + args.WebSession.ServerConnection.Dispose(); + ServerConnectionCount--; + } + + var connection = await GetServerConnection(args, null, null, true); + await HandleHttpSessionRequestInternal(null, args, true); return true; } From 07f86b87711fc3f94a342363db03e517792ef963 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Mon, 15 May 2017 18:51:21 -0400 Subject: [PATCH 12/40] format --- Titanium.Web.Proxy/Helpers/Tcp.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index 5261ec338..e64da17b6 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -93,21 +93,21 @@ internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion) var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6; - if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int) NativeMethods.TcpTableType.OwnerPidAll, 0) != 0) + if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) != 0) { try { tcpTable = Marshal.AllocHGlobal(tcpTableLength); - if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int) NativeMethods.TcpTableType.OwnerPidAll, 0) == 0) + if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) == 0) { - NativeMethods.TcpTable table = (NativeMethods.TcpTable) Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable)); + NativeMethods.TcpTable table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable)); - IntPtr rowPtr = (IntPtr) ((long) tcpTable + Marshal.SizeOf(table.length)); + IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length)); for (int i = 0; i < table.length; ++i) { - tcpRows.Add(new TcpRow((NativeMethods.TcpRow) Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow)))); - rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow))); + tcpRows.Add(new TcpRow((NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow)))); + rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow))); } } } @@ -184,9 +184,9 @@ internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort) /// /// internal static async Task SendRaw(ProxyServer server, - string remoteHostName, int remotePort, + string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary requestHeaders, - bool isHttps, + bool isHttps, Stream clientStream, TcpConnectionFactory tcpConnectionFactory) { //prepare the prefix content @@ -213,7 +213,7 @@ internal static async Task SendRaw(ProxyServer server, sb.Append(ProxyConstants.NewLine); } - var tcpConnection = await tcpConnectionFactory.CreateClient(server, + var tcpConnection = await tcpConnectionFactory.CreateClient(server, remoteHostName, remotePort, httpVersion, isHttps, null, null, clientStream); From 063d78a343c8e7118f5730ce619460777b75cf79 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Mon, 15 May 2017 18:52:53 -0400 Subject: [PATCH 13/40] call dispose correctly --- Titanium.Web.Proxy/RequestHandler.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 3a3013724..ca80edf03 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -405,7 +405,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St Dispose(clientStream, clientStreamReader, clientStreamWriter, - null); + connection); break; } @@ -557,8 +557,6 @@ await TcpHelper.SendRaw(this, } } - connection?.Dispose(); - } /// From f183d65e9653d4de41fae8c48566101b703cb5a0 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Mon, 15 May 2017 19:08:24 -0400 Subject: [PATCH 14/40] update sample proj for convenience (don't block google, evade dropbox) --- .../ProxyTestController.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 0daadd298..fe9f16c1c 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -47,7 +47,7 @@ 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() { "google.com", "dropbox.com" } + ExcludedHttpsHostNameRegex = new List() { "dropbox.com" } //Include Https addresses you want to proxy (others will be excluded) //for example github.com @@ -127,24 +127,24 @@ public async Task OnRequest(object sender, SessionEventArgs e) requestBodyHistory[e.Id] = bodyString; } - //To cancel a request with a custom HTML content - //Filter URL - if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com")) - { - await e.Ok("" + - "

" + - "Website Blocked" + - "

" + - "

Blocked by titanium web proxy.

" + - "" + - ""); - } - - //Redirect example - if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org")) - { - await e.Redirect("https://www.paypal.com"); - } + ////To cancel a request with a custom HTML content + ////Filter URL + //if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com")) + //{ + // await e.Ok("" + + // "

" + + // "Website Blocked" + + // "

" + + // "

Blocked by titanium web proxy.

" + + // "" + + // ""); + //} + + ////Redirect example + //if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org")) + //{ + // await e.Redirect("https://www.paypal.com"); + //} } //Modify response From 7b8481668f95746fc80d5f724952451bf6bd32a8 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 01:20:01 +0200 Subject: [PATCH 15/40] 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 278e2a5d02f2abf96d8f30a02002de32166d6561 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Mon, 15 May 2017 19:20:01 -0400 Subject: [PATCH 16/40] reduce timeout; cleanup --- Titanium.Web.Proxy/ProxyServer.cs | 2 +- Titanium.Web.Proxy/RequestHandler.cs | 35 ++++++++++++++++----------- Titanium.Web.Proxy/ResponseHandler.cs | 6 ++--- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index a4174e10e..de3a0d928 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -252,7 +252,7 @@ public ProxyServer() : this(null, null) public ProxyServer(string rootCertificateName, string rootCertificateIssuerName) { //default values - ConnectionTimeOutSeconds = 120; + ConnectionTimeOutSeconds = 30; CertificateCacheTimeOutMinutes = 60; ProxyEndPoints = new List(); diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index ca80edf03..60f3975e8 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -217,11 +217,16 @@ await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamRea endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null); } + /// + /// Create a Server Connection + /// + /// + /// private async Task GetServerConnection( - SessionEventArgs args, - ExternalProxy customUpStreamHttpProxy, - ExternalProxy customUpStreamHttpsProxy, bool closeConnection) + SessionEventArgs args) { + ExternalProxy customUpStreamHttpProxy = null; + ExternalProxy customUpStreamHttpsProxy = null; if (args.WebSession.Request.RequestUri.Scheme == "http") { @@ -243,7 +248,8 @@ private async Task GetServerConnection( return await tcpConnectionFactory.CreateClient(this, args.WebSession.Request.RequestUri.Host, - args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, + args.WebSession.Request.RequestUri.Port, + args.WebSession.Request.HttpVersion, args.IsHttps, customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, @@ -392,7 +398,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", /// private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, - ProxyEndPoint endPoint, List connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null) + ProxyEndPoint endPoint, List connectHeaders) { TcpConnection connection = null; @@ -410,8 +416,7 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St break; } - var args = - new SessionEventArgs(BufferSize, HandleHttpSessionResponse) + var args = new SessionEventArgs(BufferSize, HandleHttpSessionResponse) { ProxyClient = { TcpClient = client }, WebSession = { ConnectHeaders = connectHeaders } @@ -464,12 +469,14 @@ private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, St args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; - if (httpsHostName == null && await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false) + if (httpsHostName == null && + await CheckAuthorization(clientStreamWriter, + args.WebSession.Request.RequestHeaders.Values) == false) { Dispose(clientStream, clientStreamReader, clientStreamWriter, - args.WebSession.ServerConnection); + connection); break; } @@ -502,14 +509,14 @@ await TcpHelper.SendRaw(this, Dispose(clientStream, clientStreamReader, clientStreamWriter, - args.WebSession.ServerConnection); + connection); break; } if (connection == null) { - connection = await GetServerConnection(args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false); + connection = await GetServerConnection(args); } //construct the web request that we are going to issue on behalf of the client. @@ -526,7 +533,7 @@ await TcpHelper.SendRaw(this, Dispose(clientStream, clientStreamReader, clientStreamWriter, - args.WebSession.ServerConnection); + connection); break; } @@ -537,7 +544,7 @@ await TcpHelper.SendRaw(this, Dispose(clientStream, clientStreamReader, clientStreamWriter, - args.WebSession.ServerConnection); + connection); break; } @@ -552,7 +559,7 @@ await TcpHelper.SendRaw(this, Dispose(clientStream, clientStreamReader, clientStreamWriter, - args.WebSession.ServerConnection); + connection); break; } } diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 870fd2523..0fadbbee0 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -59,9 +59,9 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) ServerConnectionCount--; } - var connection = await GetServerConnection(args, null, null, true); - await HandleHttpSessionRequestInternal(null, args, true); - return true; + var connection = await GetServerConnection(args); + var result = await HandleHttpSessionRequestInternal(null, args, true); + return result; } args.WebSession.Response.ResponseLocked = true; From f089accb1a80db70c6a64654b51e52e38af225e8 Mon Sep 17 00:00:00 2001 From: Honfika Date: Tue, 16 May 2017 01:29:36 +0200 Subject: [PATCH 17/40] 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 18/40] 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 19/40] 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 20/40] 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 21/40] 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 22/40] #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 23/40] 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 24/40] 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 25/40] #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 26/40] 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 27/40] 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 28/40] 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 29/40] 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 30/40] 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 31/40] #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 32/40] 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 @@ + From 652b4445699b0087999601ac68083873dcd9d10c Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Tue, 16 May 2017 18:42:39 -0400 Subject: [PATCH 33/40] move around methods based on logical flow --- Titanium.Web.Proxy/RequestHandler.cs | 338 ++++++++++++++------------- 1 file changed, 179 insertions(+), 159 deletions(-) diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 99918465d..d1a7a28d0 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -23,8 +23,13 @@ namespace Titanium.Web.Proxy /// partial class ProxyServer { - //This is called when client is aware of proxy - //So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy + /// + /// This is called when client is aware of proxy + /// 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) { var disposed = false; @@ -139,7 +144,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, { //Siphon out CONNECT request headers await clientStreamReader.ReadAndIgnoreAllLinesAsync(); - + //write back successfull CONNECT response await WriteConnectResponse(clientStreamWriter, version); @@ -170,8 +175,13 @@ await TcpHelper.SendRaw(this, } } - //This is called when this proxy acts as a reverse proxy (like a real http server) - //So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client + /// + /// This is called when this proxy acts as a reverse proxy (like a real http server) + /// 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) { bool disposed = false; @@ -219,162 +229,10 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, } } - /// - /// Create a Server Connection - /// - /// - /// - private async Task GetServerConnection( - SessionEventArgs args) - { - ExternalProxy customUpStreamHttpProxy = null; - ExternalProxy customUpStreamHttpsProxy = null; - - if (args.WebSession.Request.RequestUri.Scheme == "http") - { - if (GetCustomUpStreamHttpProxyFunc != null) - { - customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args); - } - } - else - { - if (GetCustomUpStreamHttpsProxyFunc != null) - { - customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args); - } - } - - args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; - args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy; - - return await tcpConnectionFactory.CreateClient(this, - args.WebSession.Request.RequestUri.Host, - args.WebSession.Request.RequestUri.Port, - args.WebSession.Request.HttpVersion, - args.IsHttps, - customUpStreamHttpProxy ?? UpStreamHttpProxy, - customUpStreamHttpsProxy ?? UpStreamHttpsProxy, - args.ProxyClient.ClientStream); - } - - - private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, bool closeConnection) - { - bool disposed = false; - bool keepAlive = false; - - try - { - args.WebSession.Request.RequestLocked = true; - - //If request was cancelled by user then dispose the client - if (args.WebSession.Request.CancelRequest) - { - return true; - } - - //if expect continue is enabled then send the headers first - //and see if server would return 100 conitinue - if (args.WebSession.Request.ExpectContinue) - { - args.WebSession.SetConnection(connection); - await args.WebSession.SendRequest(Enable100ContinueBehaviour); - } - - //If 100 continue was the response inform that to the client - if (Enable100ContinueBehaviour) - { - if (args.WebSession.Request.Is100Continue) - { - await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", - "Continue", args.ProxyClient.ClientStreamWriter); - await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); - } - else if (args.WebSession.Request.ExpectationFailed) - { - await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", - "Expectation Failed", args.ProxyClient.ClientStreamWriter); - await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); - } - } - - //If expect continue is not enabled then set the connectio and send request headers - if (!args.WebSession.Request.ExpectContinue) - { - args.WebSession.SetConnection(connection); - await args.WebSession.SendRequest(Enable100ContinueBehaviour); - } - - //If request was modified by user - if (args.WebSession.Request.RequestBodyRead) - { - if (args.WebSession.Request.ContentEncoding != null) - { - args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody); - } - //chunked send is not supported as of now - args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length; - - var newStream = args.WebSession.ServerConnection.Stream; - await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length); - } - else - { - if (!args.WebSession.Request.ExpectationFailed) - { - //If its a post/put/patch request, then read the client html body and send it to server - var method = args.WebSession.Request.Method.ToUpper(); - if (method == "POST" || method == "PUT" || method == "PATCH") - { - await SendClientRequestBody(args); - } - } - } - - //If not expectation failed response was returned by server then parse response - if (!args.WebSession.Request.ExpectationFailed) - { - disposed = await HandleHttpSessionResponse(args); - - //already disposed inside above method - if (disposed) - { - return true; - } - } - - //if connection is closing exit - if (args.WebSession.Response.ResponseKeepAlive == false) - { - return true; - } - - if (!closeConnection) - { - keepAlive = true; - return false; - } - } - catch (Exception e) - { - ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request (internal)", e, args)); - return true; - } - finally - { - if (!disposed && !keepAlive) - { - //dispose - Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); - } - } - - return true; - } - /// /// This is the core request handler method for a particular connection from client + /// Will create new session (request/response) sequence until + /// client/server abruptly terminates connection or by normal HTTP termination /// /// /// @@ -533,6 +391,168 @@ await TcpHelper.SendRaw(this, return true; } + /// + /// Handle a specific session (request/response sequence) + /// + /// + /// + /// + /// + private async Task HandleHttpSessionRequestInternal(TcpConnection connection, + SessionEventArgs args, bool closeConnection) + { + bool disposed = false; + bool keepAlive = false; + + try + { + args.WebSession.Request.RequestLocked = true; + + //If request was cancelled by user then dispose the client + if (args.WebSession.Request.CancelRequest) + { + return true; + } + + //if expect continue is enabled then send the headers first + //and see if server would return 100 conitinue + if (args.WebSession.Request.ExpectContinue) + { + args.WebSession.SetConnection(connection); + await args.WebSession.SendRequest(Enable100ContinueBehaviour); + } + + //If 100 continue was the response inform that to the client + if (Enable100ContinueBehaviour) + { + if (args.WebSession.Request.Is100Continue) + { + await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", + "Continue", args.ProxyClient.ClientStreamWriter); + await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); + } + else if (args.WebSession.Request.ExpectationFailed) + { + await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", + "Expectation Failed", args.ProxyClient.ClientStreamWriter); + await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); + } + } + + //If expect continue is not enabled then set the connectio and send request headers + if (!args.WebSession.Request.ExpectContinue) + { + args.WebSession.SetConnection(connection); + await args.WebSession.SendRequest(Enable100ContinueBehaviour); + } + + //If request was modified by user + if (args.WebSession.Request.RequestBodyRead) + { + if (args.WebSession.Request.ContentEncoding != null) + { + args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody); + } + //chunked send is not supported as of now + args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length; + + var newStream = args.WebSession.ServerConnection.Stream; + await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length); + } + else + { + if (!args.WebSession.Request.ExpectationFailed) + { + //If its a post/put/patch request, then read the client html body and send it to server + var method = args.WebSession.Request.Method.ToUpper(); + if (method == "POST" || method == "PUT" || method == "PATCH") + { + await SendClientRequestBody(args); + } + } + } + + //If not expectation failed response was returned by server then parse response + if (!args.WebSession.Request.ExpectationFailed) + { + disposed = await HandleHttpSessionResponse(args); + + //already disposed inside above method + if (disposed) + { + return true; + } + } + + //if connection is closing exit + if (args.WebSession.Response.ResponseKeepAlive == false) + { + return true; + } + + if (!closeConnection) + { + keepAlive = true; + return false; + } + } + catch (Exception e) + { + ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request (internal)", e, args)); + return true; + } + finally + { + if (!disposed && !keepAlive) + { + //dispose + Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); + } + } + + return true; + } + + /// + /// Create a Server Connection + /// + /// + /// + private async Task GetServerConnection( + SessionEventArgs args) + { + ExternalProxy customUpStreamHttpProxy = null; + ExternalProxy customUpStreamHttpsProxy = null; + + if (args.WebSession.Request.RequestUri.Scheme == "http") + { + if (GetCustomUpStreamHttpProxyFunc != null) + { + customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args); + } + } + else + { + if (GetCustomUpStreamHttpsProxyFunc != null) + { + customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args); + } + } + + args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; + args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy; + + return await tcpConnectionFactory.CreateClient(this, + args.WebSession.Request.RequestUri.Host, + args.WebSession.Request.RequestUri.Port, + args.WebSession.Request.HttpVersion, + args.IsHttps, + customUpStreamHttpProxy ?? UpStreamHttpProxy, + customUpStreamHttpsProxy ?? UpStreamHttpsProxy, + args.ProxyClient.ClientStream); + } + + /// /// Write successfull CONNECT response to client /// From f1e3741c096bedf3e60fbf39a9d1d2e20deca920 Mon Sep 17 00:00:00 2001 From: Honfika Date: Wed, 17 May 2017 16:48:13 +0200 Subject: [PATCH 34/40] Allow to query the root certificate status (IsRootCertificateTrusted and IsRootCertificateMachineTrusted) Allow to machine trust the root certificate (it will show the UAC dialog when required) --- .../Network/CertificateManager.cs | 101 ++++++++++++++++-- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 828e4f8fa..befb2d91b 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -30,7 +31,7 @@ public enum CertificateEngine /// /// A class to manage SSL certificates used by this proxy server /// - public class CertificateManager : IDisposable + public sealed class CertificateManager : IDisposable { internal CertificateEngine Engine { @@ -147,7 +148,7 @@ private string GetRootCertificatePath() return fileName; } - internal X509Certificate2 LoadRootCertificate() + private X509Certificate2 LoadRootCertificate() { var fileName = GetRootCertificatePath(); if (!File.Exists(fileName)) return null; @@ -218,6 +219,51 @@ public void TrustRootCertificate() TrustRootCertificate(StoreLocation.LocalMachine); } + /// + /// Puts the certificate to the local machine's certificate store. + /// Needs elevated permission. Works only on Windows. + /// + /// + public bool TrustRootCertificateAsAdministrator() + { + if (RunTime.IsRunningOnMono()) + { + return false; + } + + var fileName = Path.GetTempFileName(); + File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12)); + + var info = new ProcessStartInfo + { + FileName = "certutil.exe", + Arguments = "-importPFX -Enterprise -p \"\" -f \"" + fileName + "\"", + CreateNoWindow = true, + UseShellExecute = true, + Verb = "runas", + ErrorDialog = false, + }; + + try + { + var process = Process.Start(info); + if (process == null) + { + return false; + } + + process.WaitForExit(); + + File.Delete(fileName); + } + catch + { + return false; + } + + return true; + } + /// /// Removes the trusted certificates. /// @@ -230,13 +276,49 @@ public void RemoveTrustedRootCertificates() RemoveTrustedRootCertificates(StoreLocation.LocalMachine); } + /// + /// Determines whether the root certificate is trusted. + /// + public bool IsRootCertificateTrusted() + { + return FindRootCertificate(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted(); + } + + /// + /// Determines whether the root certificate is machine trusted. + /// + public bool IsRootCertificateMachineTrusted() + { + return FindRootCertificate(StoreLocation.LocalMachine); + } + + private bool FindRootCertificate(StoreLocation storeLocation) + { + string value = $"{RootCertificate.Issuer}"; + return FindCertificates(StoreName.Root, storeLocation, value).Count > 0; + } + + private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue) + { + X509Store x509Store = new X509Store(storeName, storeLocation); + try + { + x509Store.Open(OpenFlags.OpenExistingOnly); + return x509Store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, findValue, false); + } + finally + { + x509Store.Close(); + } + } + /// /// Create an SSL certificate /// /// /// /// - internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) + internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) { if (certificateCache.ContainsKey(certificateName)) { @@ -317,7 +399,7 @@ internal async void ClearIdleCertificates(int certificateCacheTimeOutMinutes) /// /// /// - internal void TrustRootCertificate(StoreLocation storeLocation) + private void TrustRootCertificate(StoreLocation storeLocation) { if (RootCertificate == null) { @@ -328,7 +410,7 @@ internal void TrustRootCertificate(StoreLocation storeLocation) return; } - X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); + var x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation); try @@ -357,7 +439,7 @@ internal void TrustRootCertificate(StoreLocation storeLocation) /// /// /// - internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) + private void RemoveTrustedRootCertificates(StoreLocation storeLocation) { if (RootCertificate == null) { @@ -368,7 +450,7 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) return; } - X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); + var x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation); try @@ -382,7 +464,7 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) catch (Exception e) { exceptionFunc( - new Exception("Failed to make system trust root certificate " + new Exception("Failed to remove root certificate trust " + $" for {storeLocation} store location. You may need admin rights.", e)); } finally @@ -392,6 +474,9 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) } } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// public void Dispose() { } From cad1b6d100d8bc02fc902f26d43a1331305502d5 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Wed, 17 May 2017 11:18:38 -0400 Subject: [PATCH 35/40] Not sure why this was added in PR #187 --- Titanium.Web.Proxy/ProxyServer.cs | 41 ------------------------------- 1 file changed, 41 deletions(-) diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index a912b0d53..fefd59b4d 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -647,46 +647,5 @@ private void QuitListen(ProxyEndPoint endPoint) endPoint.Listener.Server.Close(); endPoint.Listener.Server.Dispose(); } - - /// - /// Invocator for BeforeRequest event. - /// - /// - /// - protected virtual void OnBeforeRequest(object sender, SessionEventArgs e) - { - BeforeRequest?.Invoke(sender, e); - } - - /// - /// Invocator for BeforeResponse event. - /// - /// - /// - /// - protected virtual void OnBeforeResponse(object sender, SessionEventArgs e) - { - BeforeResponse?.Invoke(sender, e); - } - - /// - /// Invocator for ServerCertificateValidationCallback event. - /// - /// - /// - protected virtual void OnServerCertificateValidationCallback(object sender, CertificateValidationEventArgs e) - { - ServerCertificateValidationCallback?.Invoke(sender, e); - } - - /// - /// Invocator for ClientCertifcateSelectionCallback event. - /// - /// - /// - protected virtual void OnClientCertificateSelectionCallback(object sender, CertificateSelectionEventArgs e) - { - ClientCertificateSelectionCallback?.Invoke(sender, e); - } } } From 03e69d2e2c2f52f975b44ad56de7b7d538a184cd Mon Sep 17 00:00:00 2001 From: Honfika Date: Wed, 17 May 2017 17:51:26 +0200 Subject: [PATCH 36/40] Enterprise parameter removed --- Titanium.Web.Proxy/Network/CertificateManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index befb2d91b..ddc9cd2ed 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -237,7 +237,7 @@ public bool TrustRootCertificateAsAdministrator() var info = new ProcessStartInfo { FileName = "certutil.exe", - Arguments = "-importPFX -Enterprise -p \"\" -f \"" + fileName + "\"", + Arguments = "-importPFX -p \"\" -f \"" + fileName + "\"", CreateNoWindow = true, UseShellExecute = true, Verb = "runas", From 10e5e3175c4eae810018dd6ac6e882bdaa353a59 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Wed, 17 May 2017 15:53:37 -0400 Subject: [PATCH 37/40] move dispose; remove references to actice objects after dispose --- .../EventArguments/SessionEventArgs.cs | 6 +++++- Titanium.Web.Proxy/Http/HttpWebClient.cs | 10 +++------- Titanium.Web.Proxy/Http/Request.cs | 17 ++++++++++++++++- Titanium.Web.Proxy/Http/Response.cs | 17 ++++++++++++++++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index 9f2824575..869dcf62d 100644 --- a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -30,7 +30,7 @@ public class SessionEventArgs : EventArgs, IDisposable /// /// Holds a reference to proxy response handler method /// - private readonly Func httpResponseHandler; + private Func httpResponseHandler; /// /// Holds a reference to client @@ -522,6 +522,10 @@ public async Task Respond(Response response) /// public void Dispose() { + httpResponseHandler = null; + CustomUpStreamHttpProxyUsed = null; + CustomUpStreamHttpsProxyUsed = null; + WebSession.Dispose(); } } diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index bd4278320..ac61bbea1 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -214,14 +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; - - Request.RequestBodyString = null; - Response.ResponseBodyString = null; + ConnectHeaders = null; + Request.Dispose(); + Response.Dispose(); } } } diff --git a/Titanium.Web.Proxy/Http/Request.cs b/Titanium.Web.Proxy/Http/Request.cs index 0afdea85c..2784511a9 100644 --- a/Titanium.Web.Proxy/Http/Request.cs +++ b/Titanium.Web.Proxy/Http/Request.cs @@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Http /// /// A HTTP(S) request object /// - public class Request + public class Request : IDisposable { /// /// Request Method @@ -294,5 +294,20 @@ public Request() RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); NonUniqueRequestHeaders = new Dictionary>(StringComparer.OrdinalIgnoreCase); } + + /// + /// Dispose off + /// + public void Dispose() + { + //not really needed since GC will collect it + //but just to be on safe side + + RequestHeaders = null; + NonUniqueRequestHeaders = null; + + RequestBody = null; + RequestBody = null; + } } } diff --git a/Titanium.Web.Proxy/Http/Response.cs b/Titanium.Web.Proxy/Http/Response.cs index 0393f4875..82b5fdd6c 100644 --- a/Titanium.Web.Proxy/Http/Response.cs +++ b/Titanium.Web.Proxy/Http/Response.cs @@ -10,7 +10,7 @@ namespace Titanium.Web.Proxy.Http /// /// Http(s) response object /// - public class Response + public class Response : IDisposable { /// /// Response Status Code. @@ -234,5 +234,20 @@ public Response() ResponseHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); NonUniqueResponseHeaders = new Dictionary>(StringComparer.OrdinalIgnoreCase); } + + /// + /// Dispose off + /// + public void Dispose() + { + //not really needed since GC will collect it + //but just to be on safe side + + ResponseHeaders = null; + NonUniqueResponseHeaders = null; + + ResponseBody = null; + ResponseBodyString = null; + } } } From a630f283ec3c1c14d6bb999712ea85b112e5fc5a Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Thu, 18 May 2017 20:02:33 -0400 Subject: [PATCH 38/40] add TLS cert revocation check flag; cleanup --- Titanium.Web.Proxy/Helpers/Tcp.cs | 2 +- .../Network/Tcp/TcpConnectionFactory.cs | 5 +-- Titanium.Web.Proxy/ProxyServer.cs | 32 +++++++++++++++---- Titanium.Web.Proxy/RequestHandler.cs | 6 ---- Titanium.Web.Proxy/ResponseHandler.cs | 4 +-- 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index 5cac82141..226a26e00 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.ServerConnectionCountField); + Interlocked.Decrement(ref server.serverConnectionCount); } } } diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 0471f0b73..4567dff61 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -95,12 +95,13 @@ internal async Task CreateClient(ProxyServer server, sslStream = new SslStream(stream, true, server.ValidateServerCertificate, server.SelectClientCertificate); - await sslStream.AuthenticateAsClientAsync(remoteHostName, null, server.SupportedSslProtocols, false); + await sslStream.AuthenticateAsClientAsync(remoteHostName, null, server.SupportedSslProtocols, server.CheckCertificateRevocation); stream = new CustomBufferedStream(sslStream, server.BufferSize); } catch { + sslStream?.Close(); sslStream?.Dispose(); throw; @@ -125,7 +126,7 @@ internal async Task CreateClient(ProxyServer server, client.ReceiveTimeout = server.ConnectionTimeOutSeconds * 1000; client.SendTimeout = server.ConnectionTimeOutSeconds * 1000; - Interlocked.Increment(ref server.ServerConnectionCountField); + Interlocked.Increment(ref server.serverConnectionCount); return new TcpConnection { diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index fefd59b4d..a9c3936ad 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -35,9 +35,20 @@ public partial class ProxyServer : IDisposable /// private Action exceptionFunc; + /// + /// Backing field for corresponding public property + /// private bool trustRootCertificate; - private int clientConnectionCountField; - internal int ServerConnectionCountField; + + /// + /// Backing field for corresponding public property + /// + private int clientConnectionCount; + + /// + /// Backing field for corresponding public property + /// + internal int serverConnectionCount; /// /// A object that creates tcp connection to server @@ -127,6 +138,12 @@ public CertificateEngine CertificateEngine set { CertificateManager.Engine = value; } } + /// + /// Should we check for certificare revocation during SSL authentication to servers + /// Note: If enabled can reduce performance (Default disabled) + /// + public bool CheckCertificateRevocation { get; set; } + /// /// Does this proxy uses the HTTP protocol 100 continue behaviour strictly? /// Broken 100 contunue implementations on server/client may cause problems if enabled @@ -231,13 +248,13 @@ public Action ExceptionFunc /// /// Total number of active client connections /// - public int ClientConnectionCount => clientConnectionCountField; + public int ClientConnectionCount => clientConnectionCount; /// /// Total number of active server connections /// - public int ServerConnectionCount => ServerConnectionCountField; + public int ServerConnectionCount => serverConnectionCount; /// /// Constructor @@ -597,7 +614,10 @@ private void OnAcceptConnection(IAsyncResult asyn) { Task.Run(async () => { - Interlocked.Increment(ref clientConnectionCountField); + Interlocked.Increment(ref clientConnectionCount); + + tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000; + tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000; try { @@ -612,7 +632,7 @@ private void OnAcceptConnection(IAsyncResult asyn) } finally { - Interlocked.Decrement(ref clientConnectionCountField); + Interlocked.Decrement(ref clientConnectionCount); try { diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index d1a7a28d0..38ee110ed 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -36,9 +36,6 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli 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 }; @@ -187,9 +184,6 @@ private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcp bool disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); - clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; - clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; - CustomBinaryReader clientStreamReader = null; StreamWriter clientStreamWriter = null; diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 63275e69f..3a25aacb7 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -49,7 +49,7 @@ private async Task HandleHttpSessionResponse(SessionEventArgs args) if (args.WebSession.ServerConnection != null) { args.WebSession.ServerConnection.Dispose(); - Interlocked.Decrement(ref ServerConnectionCountField); + Interlocked.Decrement(ref serverConnectionCount); } var connection = await GetServerConnection(args); @@ -240,7 +240,7 @@ private void Dispose(Stream clientStream, if (serverConnection != null) { serverConnection.Dispose(); - Interlocked.Decrement(ref ServerConnectionCountField); + Interlocked.Decrement(ref serverConnectionCount); } } } From c046a32f05f79941d8b4e9c7f397eefaaa0171d0 Mon Sep 17 00:00:00 2001 From: Jehonathan Thomas Date: Fri, 19 May 2017 12:38:43 -0400 Subject: [PATCH 39/40] already supported --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b7e366ee7..f7ae16734 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ Features * Safely relays Web Socket requests over HTTP * Support mutual SSL authentication * Fully asynchronous proxy -* Supports proxy authentication - +* Supports proxy authentication & automatic proxy detection Usage ===== @@ -204,10 +203,8 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs ``` Future road map (Pull requests are welcome!) ============ +* Implement Kerberos/NTLM authentication over HTTP protocols for windows domain * Support Server Name Indication (SNI) for transparent endpoints * Support HTTP 2.0 -* Support upstream AutoProxy detection * Support SOCKS protocol -* Implement Kerberos/NTLM authentication over HTTP protocols for windows domain - From 2260a89e1783ea518fdf99ecb79f784bd3d90f92 Mon Sep 17 00:00:00 2001 From: "PDR\\jehonathan.thomas" Date: Fri, 19 May 2017 16:22:05 -0400 Subject: [PATCH 40/40] #242 Fix external proxy --- Titanium.Web.Proxy/Models/ExternalProxy.cs | 2 +- .../Network/Tcp/TcpConnectionFactory.cs | 33 +++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Titanium.Web.Proxy/Models/ExternalProxy.cs b/Titanium.Web.Proxy/Models/ExternalProxy.cs index e1516a477..5922c3a47 100644 --- a/Titanium.Web.Proxy/Models/ExternalProxy.cs +++ b/Titanium.Web.Proxy/Models/ExternalProxy.cs @@ -21,7 +21,7 @@ public class ExternalProxy /// /// Bypass this proxy for connections to localhost? /// - public bool BypassForLocalhost { get; set; } + public bool BypassLocalhost { get; set; } /// /// Username. diff --git a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs index 4567dff61..7940afb19 100644 --- a/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs @@ -39,10 +39,37 @@ internal async Task CreateClient(ProxyServer server, TcpClient client; CustomBufferedStream stream; - bool isLocalhost = (externalHttpsProxy != null || externalHttpProxy != null) && NetworkHelper.IsLocalIpAddress(remoteHostName); + + bool useHttpProxy = false; + + //check if external proxy is set for HTTP + if (!isHttps && externalHttpProxy != null + && externalHttpProxy.HostName != remoteHostName) + { + useHttpProxy = true; + + //check if we need to ByPass + if (externalHttpProxy.BypassLocalhost + && NetworkHelper.IsLocalIpAddress(remoteHostName)) + { + useHttpProxy = false; + } + } - bool useHttpsProxy = externalHttpsProxy != null && externalHttpsProxy.HostName != remoteHostName && externalHttpsProxy.BypassForLocalhost && !isLocalhost; - bool useHttpProxy = externalHttpProxy != null && externalHttpProxy.HostName != remoteHostName && externalHttpProxy.BypassForLocalhost && !isLocalhost; + bool useHttpsProxy = false; + //check if external proxy is set for HTTPS + if (isHttps && externalHttpsProxy != null + && externalHttpsProxy.HostName != remoteHostName) + { + useHttpsProxy = true; + + //check if we need to ByPass + if (externalHttpsProxy.BypassLocalhost + && NetworkHelper.IsLocalIpAddress(remoteHostName)) + { + useHttpsProxy = false; + } + } if (isHttps) {