From 56270351dd73154eb38dccd434b18d540a505ef9 Mon Sep 17 00:00:00 2001 From: titanium007 Date: Thu, 16 Jun 2016 21:04:56 -0400 Subject: [PATCH 1/5] update comments --- Titanium.Web.Proxy/Http/HttpWebClient.cs | 4 +++ Titanium.Web.Proxy/RequestHandler.cs | 40 +++++++++--------------- Titanium.Web.Proxy/ResponseHandler.cs | 11 +++++-- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/Titanium.Web.Proxy/Http/HttpWebClient.cs b/Titanium.Web.Proxy/Http/HttpWebClient.cs index 75aeaf750..b7c23053a 100644 --- a/Titanium.Web.Proxy/Http/HttpWebClient.cs +++ b/Titanium.Web.Proxy/Http/HttpWebClient.cs @@ -133,18 +133,22 @@ internal async Task ReceiveResponse() if (this.Response.ResponseStatusCode.Equals("100") && this.Response.ResponseStatusDescription.ToLower().Equals("continue")) { + //Read the next line after 100-continue this.Response.Is100Continue = true; this.Response.ResponseStatusCode = null; await ServerConnection.StreamReader.ReadLineAsync(); + //now receive response await ReceiveResponse(); return; } else if (this.Response.ResponseStatusCode.Equals("417") && this.Response.ResponseStatusDescription.ToLower().Equals("expectation failed")) { + //read next line after expectation failed response this.Response.ExpectationFailed = true; this.Response.ResponseStatusCode = null; await ServerConnection.StreamReader.ReadLineAsync(); + //now receive response await ReceiveResponse(); return; } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 7309fb0fb..22ca17c79 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -64,8 +64,8 @@ private static async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient version = new Version(1, 0); } } - - var excluded = endPoint.ExcludedHttpsHostNameRegex != null ? + //filter out excluded host names + var excluded = endPoint.ExcludedHttpsHostNameRegex != null ? endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false; //Client wants to create a secure tcp tunnel (its a HTTPS request) @@ -82,8 +82,8 @@ private static async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient { //create the Tcp Connection to server and then release it to connection cache //Just doing what CONNECT request is asking as to do - var tunnelClient = await TcpConnectionManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version); - await TcpConnectionManager.ReleaseClient(tunnelClient); + var tunnelClient = await TcpConnectionManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version); + await TcpConnectionManager.ReleaseClient(tunnelClient); sslStream = new SslStream(clientStream, true); var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host, false); @@ -148,12 +148,8 @@ private static async void HandleClient(TransparentProxyEndPoint endPoint, TcpCli if (endPoint.EnableSsl) { var sslStream = new SslStream(clientStream, true); - //if(endPoint.UseServerNameIndication) - //{ - // //implement in future once SNI supported by SSL stream - // certificate = CertManager.CreateCertificate(hostName); - //} - //else + + //implement in future once SNI supported by SSL stream, for now use the same certificate certificate = await CertManager.CreateCertificate(endPoint.GenericCertificateName, false); try @@ -182,6 +178,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, clientStreamReader = new CustomBinaryReader(clientStream); } + //now read the request line var httpCmd = await clientStreamReader.ReadLineAsync(); //Now create the request @@ -411,6 +408,7 @@ private static void PrepareRequestHeaders(List requestHeaders, HttpW { switch (requestHeaders[i].Name.ToLower()) { + //these are the only encoding this proxy can read case "accept-encoding": requestHeaders[i].Value = "gzip,deflate,zlib"; break; @@ -457,28 +455,18 @@ private static async Task SendClientRequestBody(SessionEventArgs args) // End the operation var postStream = args.WebSession.ServerConnection.Stream; + //send the request body bytes to server if (args.WebSession.Request.ContentLength > 0) { - try - { - await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength); - } - catch - { - throw; - } + await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength); + } //Need to revist, find any potential bugs + //send the request body bytes to server in chunks else if (args.WebSession.Request.IsChunked) { - try - { - await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(postStream); - } - catch - { - throw; - } + await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(postStream); + } } diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 304d07a1d..0e633da26 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -83,8 +83,13 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession. { await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response.ResponseHeaders); - if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || - (args.WebSession.Response.HttpVersion.Major == 1 && args.WebSession.Response.HttpVersion.Minor == 0)) + //Write body only if response is chunked or content length >0 + //Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection + if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || !args.WebSession.Response.ResponseKeepAlive) + await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); + //write response if connection:keep-alive header exist and when version is http/1.0 + //Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream) + else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0) await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); } @@ -172,7 +177,7 @@ private static void FixResponseProxyHeaders(List headers) headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection"); } - + /// /// Handle dispose of a client/server session /// From 725e1f759d0daae29b67ac93c49975a7c89e3d2f Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Thu, 16 Jun 2016 23:13:36 -0400 Subject: [PATCH 2/5] make proxy server non-static object --- .../ProxyTestController.cs | 35 ++-- Titanium.Web.Proxy/CertificateHandler.cs | 4 +- .../Decompression/DefaultDecompression.cs | 2 +- .../Decompression/DeflateDecompression.cs | 4 +- .../Decompression/GZipDecompression.cs | 4 +- .../Decompression/IDecompression.cs | 2 +- .../Decompression/ZlibDecompression.cs | 4 +- .../EventArguments/SessionEventArgs.cs | 48 +++-- .../Extensions/StreamExtensions.cs | 28 +-- .../Helpers/CustomBinaryReader.cs | 10 +- Titanium.Web.Proxy/Helpers/Firefox.cs | 6 +- Titanium.Web.Proxy/Helpers/SystemProxy.cs | 26 +-- Titanium.Web.Proxy/Helpers/Tcp.cs | 9 +- Titanium.Web.Proxy/Http/HttpWebClient.cs | 8 +- .../Network/CertificateManager.cs | 15 +- ...TcpConnection.cs => TcpConnectionCache.cs} | 4 +- ...anager.cs => TcpConnectionCacheManager.cs} | 62 ++++--- Titanium.Web.Proxy/ProxyServer.cs | 171 ++++++++++-------- Titanium.Web.Proxy/RequestHandler.cs | 51 +++--- Titanium.Web.Proxy/ResponseHandler.cs | 16 +- Titanium.Web.Proxy/Shared/ProxyConstants.cs | 6 +- Titanium.Web.Proxy/Titanium.Web.Proxy.csproj | 4 +- 22 files changed, 291 insertions(+), 228 deletions(-) rename Titanium.Web.Proxy/Network/{TcpConnection.cs => TcpConnectionCache.cs} (93%) rename Titanium.Web.Proxy/Network/{TcpConnectionManager.cs => TcpConnectionCacheManager.cs} (72%) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index dc2ba74af..7149086b6 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Net; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Models; @@ -10,12 +8,19 @@ namespace Titanium.Web.Proxy.Examples.Basic { public class ProxyTestController { + private ProxyServer proxyServer; + + public ProxyTestController() + { + proxyServer = new ProxyServer(); + } + public void StartProxy() { - ProxyServer.BeforeRequest += OnRequest; - ProxyServer.BeforeResponse += OnResponse; - ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation; - ProxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; + proxyServer.BeforeRequest += OnRequest; + proxyServer.BeforeResponse += OnResponse; + proxyServer.ServerCertificateValidationCallback += OnCertificateValidation; + proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; //Exclude Https addresses you don't want to proxy //Usefull for clients that use certificate pinning @@ -27,8 +32,8 @@ public void StartProxy() //An explicit endpoint is where the client knows about the existance of a proxy //So client sends request in a proxy friendly manner - ProxyServer.AddEndPoint(explicitEndPoint); - ProxyServer.Start(); + proxyServer.AddEndPoint(explicitEndPoint); + proxyServer.Start(); //Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy) @@ -41,26 +46,26 @@ public void StartProxy() { GenericCertificateName = "google.com" }; - ProxyServer.AddEndPoint(transparentEndPoint); + proxyServer.AddEndPoint(transparentEndPoint); //ProxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //ProxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; - foreach (var endPoint in ProxyServer.ProxyEndPoints) + foreach (var endPoint in proxyServer.ProxyEndPoints) Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); //Only explicit proxies can be set as system proxy! - ProxyServer.SetAsSystemHttpProxy(explicitEndPoint); - ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint); + proxyServer.SetAsSystemHttpProxy(explicitEndPoint); + proxyServer.SetAsSystemHttpsProxy(explicitEndPoint); } public void Stop() { - ProxyServer.BeforeRequest -= OnRequest; - ProxyServer.BeforeResponse -= OnResponse; + proxyServer.BeforeRequest -= OnRequest; + proxyServer.BeforeResponse -= OnResponse; - ProxyServer.Stop(); + proxyServer.Stop(); } //intecept & cancel, redirect or update requests diff --git a/Titanium.Web.Proxy/CertificateHandler.cs b/Titanium.Web.Proxy/CertificateHandler.cs index 448e9f56f..288822fff 100644 --- a/Titanium.Web.Proxy/CertificateHandler.cs +++ b/Titanium.Web.Proxy/CertificateHandler.cs @@ -17,7 +17,7 @@ public partial class ProxyServer /// /// /// - internal static bool ValidateServerCertificate( + internal bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, @@ -62,7 +62,7 @@ internal static bool ValidateServerCertificate( /// /// /// - internal static X509Certificate SelectClientCertificate( + internal X509Certificate SelectClientCertificate( object sender, string targetHost, X509CertificateCollection localCertificates, diff --git a/Titanium.Web.Proxy/Decompression/DefaultDecompression.cs b/Titanium.Web.Proxy/Decompression/DefaultDecompression.cs index cbbc2d6ef..b3578d554 100644 --- a/Titanium.Web.Proxy/Decompression/DefaultDecompression.cs +++ b/Titanium.Web.Proxy/Decompression/DefaultDecompression.cs @@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Decompression /// internal class DefaultDecompression : IDecompression { - public Task Decompress(byte[] compressedArray) + public Task Decompress(byte[] compressedArray, int bufferSize) { return Task.FromResult(compressedArray); } diff --git a/Titanium.Web.Proxy/Decompression/DeflateDecompression.cs b/Titanium.Web.Proxy/Decompression/DeflateDecompression.cs index 12d18d0f6..32d7f6603 100644 --- a/Titanium.Web.Proxy/Decompression/DeflateDecompression.cs +++ b/Titanium.Web.Proxy/Decompression/DeflateDecompression.cs @@ -10,13 +10,13 @@ namespace Titanium.Web.Proxy.Decompression /// internal class DeflateDecompression : IDecompression { - public async Task Decompress(byte[] compressedArray) + public async Task Decompress(byte[] compressedArray, int bufferSize) { var stream = new MemoryStream(compressedArray); using (var decompressor = new DeflateStream(stream, CompressionMode.Decompress)) { - var buffer = new byte[ProxyConstants.BUFFER_SIZE]; + var buffer = new byte[bufferSize]; using (var output = new MemoryStream()) { diff --git a/Titanium.Web.Proxy/Decompression/GZipDecompression.cs b/Titanium.Web.Proxy/Decompression/GZipDecompression.cs index 27972308f..c1e9dc274 100644 --- a/Titanium.Web.Proxy/Decompression/GZipDecompression.cs +++ b/Titanium.Web.Proxy/Decompression/GZipDecompression.cs @@ -10,11 +10,11 @@ namespace Titanium.Web.Proxy.Decompression /// internal class GZipDecompression : IDecompression { - public async Task Decompress(byte[] compressedArray) + public async Task Decompress(byte[] compressedArray, int bufferSize) { using (var decompressor = new GZipStream(new MemoryStream(compressedArray), CompressionMode.Decompress)) { - var buffer = new byte[ProxyConstants.BUFFER_SIZE]; + var buffer = new byte[bufferSize]; using (var output = new MemoryStream()) { int read; diff --git a/Titanium.Web.Proxy/Decompression/IDecompression.cs b/Titanium.Web.Proxy/Decompression/IDecompression.cs index 670a0816c..b67e1f78e 100644 --- a/Titanium.Web.Proxy/Decompression/IDecompression.cs +++ b/Titanium.Web.Proxy/Decompression/IDecompression.cs @@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Decompression /// internal interface IDecompression { - Task Decompress(byte[] compressedArray); + Task Decompress(byte[] compressedArray, int bufferSize); } } diff --git a/Titanium.Web.Proxy/Decompression/ZlibDecompression.cs b/Titanium.Web.Proxy/Decompression/ZlibDecompression.cs index cac856d1c..d0e7026c9 100644 --- a/Titanium.Web.Proxy/Decompression/ZlibDecompression.cs +++ b/Titanium.Web.Proxy/Decompression/ZlibDecompression.cs @@ -10,12 +10,12 @@ namespace Titanium.Web.Proxy.Decompression /// internal class ZlibDecompression : IDecompression { - public async Task Decompress(byte[] compressedArray) + public async Task Decompress(byte[] compressedArray, int bufferSize) { var memoryStream = new MemoryStream(compressedArray); using (var decompressor = new ZlibStream(memoryStream, CompressionMode.Decompress)) { - var buffer = new byte[ProxyConstants.BUFFER_SIZE]; + var buffer = new byte[bufferSize]; using (var output = new MemoryStream()) { diff --git a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index 935eafcf3..977404367 100644 --- a/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -20,14 +20,16 @@ namespace Titanium.Web.Proxy.EventArguments /// public class SessionEventArgs : EventArgs, IDisposable { + /// - /// Constructor to initialize the proxy + /// Size of Buffers used by this object /// - internal SessionEventArgs() - { - ProxyClient = new ProxyClient(); - WebSession = new HttpWebClient(); - } + private readonly int bufferSize; + + /// + /// Holds a reference to proxy response handler method + /// + private readonly Func httpResponseHandler; /// /// Holds a reference to client @@ -50,13 +52,18 @@ internal SessionEventArgs() /// - /// implement any cleanup here + /// Constructor to initialize the proxy /// - public void Dispose() + internal SessionEventArgs(int bufferSize, Func httpResponseHandler) { + this.bufferSize = bufferSize; + this.httpResponseHandler = httpResponseHandler; + ProxyClient = new ProxyClient(); + WebSession = new HttpWebClient(); } + /// /// Read request body content as bytes[] for current session /// @@ -80,7 +87,7 @@ private async Task ReadRequestBody() //For chunked request we need to read data as they arrive, until we reach a chunk end symbol if (WebSession.Request.IsChunked) { - await this.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(requestBodyStream); + await this.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream); } else { @@ -88,11 +95,11 @@ private async Task ReadRequestBody() if (WebSession.Request.ContentLength > 0) { //If not chunked then its easy just read the amount of bytes mentioned in content length header of response - await this.ProxyClient.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength); + await this.ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream, WebSession.Request.ContentLength); } else if(WebSession.Request.HttpVersion.Major == 1 && WebSession.Request.HttpVersion.Minor == 0) - await WebSession.ServerConnection.StreamReader.CopyBytesToStream(requestBodyStream, long.MaxValue); + await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, requestBodyStream, long.MaxValue); } WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()); } @@ -117,18 +124,18 @@ private async Task ReadResponseBody() //If chuncked the read chunk by chunk until we hit chunk end symbol if (WebSession.Response.IsChunked) { - await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(responseBodyStream); + await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(bufferSize, responseBodyStream); } else { if (WebSession.Response.ContentLength > 0) { //If not chunked then its easy just read the amount of bytes mentioned in content length header of response - await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength); + await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, WebSession.Response.ContentLength); } else if(WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) - await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue); + await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue); } WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray()); @@ -285,7 +292,7 @@ private async Task GetDecompressedResponseBody(string encodingType, byte var decompressionFactory = new DecompressionFactory(); var decompressor = decompressionFactory.Create(encodingType); - return await decompressor.Decompress(responseBodyStream); + return await decompressor.Decompress(responseBodyStream, bufferSize); } @@ -338,7 +345,7 @@ public async Task Redirect(string url) WebSession.Request.CancelRequest = true; } - + /// a generic responder method public async Task Respond(Response response) { @@ -349,8 +356,15 @@ public async Task Respond(Response response) WebSession.Response = response; - await ProxyServer.HandleHttpSessionResponse(this); + await httpResponseHandler(this); } + /// + /// implement any cleanup here + /// + public void Dispose() + { + + } } } \ No newline at end of file diff --git a/Titanium.Web.Proxy/Extensions/StreamExtensions.cs b/Titanium.Web.Proxy/Extensions/StreamExtensions.cs index 8e9149b1f..f4c80a316 100644 --- a/Titanium.Web.Proxy/Extensions/StreamExtensions.cs +++ b/Titanium.Web.Proxy/Extensions/StreamExtensions.cs @@ -35,22 +35,22 @@ internal static async Task CopyToAsync(this Stream input, string initialData, St /// /// /// - internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, Stream stream, long totalBytesToRead) + internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, int bufferSize, Stream stream, long totalBytesToRead) { var totalbytesRead = 0; long bytesToRead; - if (totalBytesToRead < ProxyConstants.BUFFER_SIZE) + if (totalBytesToRead < bufferSize) { bytesToRead = totalBytesToRead; } else - bytesToRead = ProxyConstants.BUFFER_SIZE; + bytesToRead = bufferSize; while (totalbytesRead < totalBytesToRead) { - var buffer = await streamReader.ReadBytesAsync(bytesToRead); + var buffer = await streamReader.ReadBytesAsync(bufferSize, bytesToRead); if (buffer.Length == 0) break; @@ -72,7 +72,7 @@ internal static async Task CopyBytesToStream(this CustomBinaryReader streamReade /// /// /// - internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, Stream stream) + internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, int bufferSize, Stream stream) { while (true) { @@ -81,7 +81,7 @@ internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clie if (chunkSize != 0) { - var buffer = await clientStreamReader.ReadBytesAsync(chunkSize); + var buffer = await clientStreamReader.ReadBytesAsync(bufferSize, chunkSize); await stream.WriteAsync(buffer, 0, buffer.Length); //chunk trail await clientStreamReader.ReadLineAsync(); @@ -118,7 +118,7 @@ internal static async Task WriteResponseBody(this Stream clientStream, byte[] da /// /// /// - internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, Stream outStream, bool isChunked, long ContentLength) + internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long ContentLength) { if (!isChunked) { @@ -126,12 +126,12 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea if (ContentLength == -1) ContentLength = long.MaxValue; - int bytesToRead = ProxyConstants.BUFFER_SIZE; + int bytesToRead = bufferSize; - if (ContentLength < ProxyConstants.BUFFER_SIZE) + if (ContentLength < bufferSize) bytesToRead = (int)ContentLength; - var buffer = new byte[ProxyConstants.BUFFER_SIZE]; + var buffer = new byte[bufferSize]; var bytesRead = 0; var totalBytesRead = 0; @@ -146,11 +146,11 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea bytesRead = 0; var remainingBytes = (ContentLength - totalBytesRead); - bytesToRead = remainingBytes > (long)ProxyConstants.BUFFER_SIZE ? ProxyConstants.BUFFER_SIZE : (int)remainingBytes; + bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes; } } else - await WriteResponseBodyChunked(inStreamReader, outStream); + await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream); } /// @@ -159,7 +159,7 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea /// /// /// - internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, Stream outStream) + internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream) { while (true) { @@ -168,7 +168,7 @@ internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inSt if (chunkSize != 0) { - var buffer = await inStreamReader.ReadBytesAsync(chunkSize); + var buffer = await inStreamReader.ReadBytesAsync(bufferSize, chunkSize); var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2")); diff --git a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs index 1d003516a..8f3a9ccc4 100644 --- a/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs +++ b/Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs @@ -91,14 +91,14 @@ internal async Task> ReadAllLinesAsync() /// /// /// - internal async Task ReadBytesAsync(long totalBytesToRead) + internal async Task ReadBytesAsync(int bufferSize, long totalBytesToRead) { - int bytesToRead = ProxyConstants.BUFFER_SIZE; + int bytesToRead = bufferSize; - if (totalBytesToRead < ProxyConstants.BUFFER_SIZE) + if (totalBytesToRead < bufferSize) bytesToRead = (int)totalBytesToRead; - var buffer = new byte[ProxyConstants.BUFFER_SIZE]; + var buffer = new byte[bufferSize]; var bytesRead = 0; var totalBytesRead = 0; @@ -115,7 +115,7 @@ internal async Task ReadBytesAsync(long totalBytesToRead) bytesRead = 0; var remainingBytes = (totalBytesToRead - totalBytesRead); - bytesToRead = remainingBytes > (long)ProxyConstants.BUFFER_SIZE ? ProxyConstants.BUFFER_SIZE : (int)remainingBytes; + bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes; } return outStream.ToArray(); diff --git a/Titanium.Web.Proxy/Helpers/Firefox.cs b/Titanium.Web.Proxy/Helpers/Firefox.cs index 02bbec16d..16077ffaf 100644 --- a/Titanium.Web.Proxy/Helpers/Firefox.cs +++ b/Titanium.Web.Proxy/Helpers/Firefox.cs @@ -6,9 +6,9 @@ namespace Titanium.Web.Proxy.Helpers /// /// A helper class to set proxy settings for firefox /// - public class FireFoxHelper + public class FireFoxProxySettingsManager { - public static void AddFirefox() + public void AddFirefox() { try { @@ -38,7 +38,7 @@ public static void AddFirefox() } } - public static void RemoveFirefox() + public void RemoveFirefox() { try { diff --git a/Titanium.Web.Proxy/Helpers/SystemProxy.cs b/Titanium.Web.Proxy/Helpers/SystemProxy.cs index 36597e01d..baa325131 100644 --- a/Titanium.Web.Proxy/Helpers/SystemProxy.cs +++ b/Titanium.Web.Proxy/Helpers/SystemProxy.cs @@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Helpers { - internal static class NativeMethods + internal class NativeMethods { [DllImport("wininet.dll")] internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, @@ -32,13 +32,15 @@ public override string ToString() return "https=" + HostName + ":" + Port; } } - - internal static class SystemProxyHelper + /// + /// Manage system proxy settings + /// + internal class SystemProxyManager { internal const int InternetOptionSettingsChanged = 39; internal const int InternetOptionRefresh = 37; - internal static void SetHttpProxy(string hostname, int port) + internal void SetHttpProxy(string hostname, int port) { var reg = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); @@ -66,7 +68,7 @@ internal static void SetHttpProxy(string hostname, int port) /// /// Remove the http proxy setting from current machine /// - internal static void RemoveHttpProxy() + internal void RemoveHttpProxy() { var reg = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); @@ -100,7 +102,7 @@ internal static void RemoveHttpProxy() /// /// /// - internal static void SetHttpsProxy(string hostname, int port) + internal void SetHttpsProxy(string hostname, int port) { var reg = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); @@ -130,7 +132,7 @@ internal static void SetHttpsProxy(string hostname, int port) /// /// Removes the https proxy setting to nothing /// - internal static void RemoveHttpsProxy() + internal void RemoveHttpsProxy() { var reg = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); @@ -163,7 +165,7 @@ internal static void RemoveHttpsProxy() /// /// Removes all types of proxy settings (both http & https) /// - internal static void DisableAllProxy() + internal void DisableAllProxy() { var reg = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); @@ -182,7 +184,7 @@ internal static void DisableAllProxy() /// /// /// - private static List GetSystemProxyValues(string prevServerValue) + private List GetSystemProxyValues(string prevServerValue) { var result = new List(); @@ -215,7 +217,7 @@ private static List GetSystemProxyValues(string prevServer /// /// /// - private static HttpSystemProxyValue parseProxyValue(string value) + private HttpSystemProxyValue parseProxyValue(string value) { var tmp = Regex.Replace(value, @"\s+", " ").Trim().ToLower(); if (tmp.StartsWith("http=")) @@ -245,7 +247,7 @@ private static HttpSystemProxyValue parseProxyValue(string value) /// Prepares the proxy server registry (create empty values if they don't exist) /// /// - private static void prepareRegistry(RegistryKey reg) + private void prepareRegistry(RegistryKey reg) { if (reg.GetValue("ProxyEnable") == null) { @@ -262,7 +264,7 @@ private static void prepareRegistry(RegistryKey reg) /// /// Refresh the settings so that the system know about a change in proxy setting /// - private static void Refresh() + private void Refresh() { NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0); diff --git a/Titanium.Web.Proxy/Helpers/Tcp.cs b/Titanium.Web.Proxy/Helpers/Tcp.cs index 4353d5943..799db7ba0 100644 --- a/Titanium.Web.Proxy/Helpers/Tcp.cs +++ b/Titanium.Web.Proxy/Helpers/Tcp.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net.Security; using System.Net.Sockets; +using System.Security.Authentication; using System.Text; using System.Threading.Tasks; using Titanium.Web.Proxy.Extensions; @@ -12,7 +13,7 @@ namespace Titanium.Web.Proxy.Helpers { - + internal class TcpHelper { /// @@ -26,8 +27,8 @@ internal class TcpHelper /// /// /// - internal async static Task SendRaw(Stream clientStream, string httpCmd, List requestHeaders, string hostName, - int tunnelPort, bool isHttps) + internal static async Task SendRaw(Stream clientStream, string httpCmd, List requestHeaders, string hostName, + int tunnelPort, bool isHttps, SslProtocols supportedProtocols) { //prepare the prefix content StringBuilder sb = null; @@ -63,7 +64,7 @@ internal async static Task SendRaw(Stream clientStream, string httpCmd, List /// Connection to server /// - internal TcpConnection ServerConnection { get; set; } + internal TcpConnectionCache ServerConnection { get; set; } public Request Request { get; set; } public Response Response { get; set; } @@ -37,7 +37,7 @@ public bool IsHttps /// Set the tcp connection to server used by this webclient /// /// - internal void SetConnection(TcpConnection Connection) + internal void SetConnection(TcpConnectionCache Connection) { Connection.LastAccess = DateTime.Now; ServerConnection = Connection; @@ -53,7 +53,7 @@ internal HttpWebClient() /// Prepare & send the http(s) request /// /// - internal async Task SendRequest() + internal async Task SendRequest(bool enable100ContinueBehaviour) { Stream stream = ServerConnection.Stream; @@ -79,7 +79,7 @@ internal async Task SendRequest() await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.FlushAsync(); - if (ProxyServer.Enable100ContinueBehaviour) + if (enable100ContinueBehaviour) if (this.Request.ExpectContinue) { var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 63af72392..8ac1e6220 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -18,8 +18,15 @@ internal class CertificateManager : IDisposable private const string CertCreateFormat = "-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}"; + /// + /// Cache dictionary + /// private readonly IDictionary certificateCache; - private static SemaphoreSlim semaphoreLock = new SemaphoreSlim(1); + + /// + /// A lock to manage concurrency + /// + private SemaphoreSlim semaphoreLock = new SemaphoreSlim(1); internal string Issuer { get; private set; } internal string RootCertificateName { get; private set; } @@ -241,7 +248,7 @@ protected virtual string GetCertificateCreateArgs(X509Store store, string certif return certCreatArgs; } - private static bool clearCertificates { get; set; } + private bool clearCertificates { get; set; } /// /// Stops the certificate cache clear process @@ -254,7 +261,7 @@ internal void StopClearIdleCertificates() /// /// A method to clear outdated certificates /// - internal async void ClearIdleCertificates() + internal async void ClearIdleCertificates(int certificateCacheTimeOutMinutes) { clearCertificates = true; while (clearCertificates) @@ -263,7 +270,7 @@ internal async void ClearIdleCertificates() try { - var cutOff = DateTime.Now.AddMinutes(-1 * ProxyServer.CertificateCacheTimeOutMinutes); + var cutOff = DateTime.Now.AddMinutes(-1 * certificateCacheTimeOutMinutes); var outdated = certificateCache .Where(x => x.Value.LastAccess < cutOff) diff --git a/Titanium.Web.Proxy/Network/TcpConnection.cs b/Titanium.Web.Proxy/Network/TcpConnectionCache.cs similarity index 93% rename from Titanium.Web.Proxy/Network/TcpConnection.cs rename to Titanium.Web.Proxy/Network/TcpConnectionCache.cs index 9330a2f17..5f636b912 100644 --- a/Titanium.Web.Proxy/Network/TcpConnection.cs +++ b/Titanium.Web.Proxy/Network/TcpConnectionCache.cs @@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Network /// /// An object that holds TcpConnection to a particular server & port /// - public class TcpConnection + public class TcpConnectionCache { internal string HostName { get; set; } internal int port { get; set; } @@ -37,7 +37,7 @@ public class TcpConnection /// internal DateTime LastAccess { get; set; } - internal TcpConnection() + internal TcpConnectionCache() { LastAccess = DateTime.Now; } diff --git a/Titanium.Web.Proxy/Network/TcpConnectionManager.cs b/Titanium.Web.Proxy/Network/TcpConnectionCacheManager.cs similarity index 72% rename from Titanium.Web.Proxy/Network/TcpConnectionManager.cs rename to Titanium.Web.Proxy/Network/TcpConnectionCacheManager.cs index e8524f293..a14c9b137 100644 --- a/Titanium.Web.Proxy/Network/TcpConnectionManager.cs +++ b/Titanium.Web.Proxy/Network/TcpConnectionCacheManager.cs @@ -10,23 +10,25 @@ using System.Threading; using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Shared; +using Titanium.Web.Proxy.Models; +using System.Security.Authentication; namespace Titanium.Web.Proxy.Network { /// /// A class that manages Tcp Connection to server used by this proxy server /// - internal class TcpConnectionManager + internal class TcpConnectionCacheManager { /// /// Connection cache /// - static Dictionary> connectionCache = new Dictionary>(); + Dictionary> connectionCache = new Dictionary>(); /// /// A lock to manage concurrency /// - static SemaphoreSlim connectionAccessLock = new SemaphoreSlim(1); + SemaphoreSlim connectionAccessLock = new SemaphoreSlim(1); /// /// Get a TcpConnection to the specified host, port optionally HTTPS and a particular HTTP version @@ -36,10 +38,12 @@ internal class TcpConnectionManager /// /// /// - internal static async Task GetClient(string hostname, int port, bool isHttps, Version version) + internal async Task GetClient(string hostname, int port, bool isHttps, Version version, + ExternalProxy upStreamHttpProxy, ExternalProxy upStreamHttpsProxy, int bufferSize, SslProtocols supportedSslProtocols, + RemoteCertificateValidationCallback remoteCertificateValidationCallBack, LocalCertificateSelectionCallback localCertificateSelectionCallback) { - List cachedConnections = null; - TcpConnection cached = null; + List cachedConnections = null; + TcpConnectionCache cached = null; //Get a unique string to identify this connection var key = GetConnectionKey(hostname, port, isHttps, version); @@ -77,12 +81,14 @@ internal static async Task GetClient(string hostname, int port, b if (cached == null) - cached = await CreateClient(hostname, port, isHttps, version); + cached = await CreateClient(hostname, port, isHttps, version, upStreamHttpProxy, upStreamHttpsProxy, bufferSize, supportedSslProtocols, + remoteCertificateValidationCallBack, localCertificateSelectionCallback); if (cachedConnections == null || cachedConnections.Count() <= 2) { - var task = CreateClient(hostname, port, isHttps, version) - .ContinueWith(async (x) => { if (x.Status == TaskStatus.RanToCompletion) await ReleaseClient(x.Result); }); + var task = CreateClient(hostname, port, isHttps, version, upStreamHttpProxy, upStreamHttpsProxy, bufferSize, supportedSslProtocols, + remoteCertificateValidationCallBack, localCertificateSelectionCallback) + .ContinueWith(async (x) => { if (x.Status == TaskStatus.RanToCompletion) await ReleaseClient(x.Result); }); } return cached; @@ -96,7 +102,7 @@ internal static async Task GetClient(string hostname, int port, b /// /// /// - internal static string GetConnectionKey(string hostname, int port, bool isHttps, Version version) + internal string GetConnectionKey(string hostname, int port, bool isHttps, Version version) { return string.Format("{0}:{1}:{2}:{3}:{4}", hostname.ToLower(), port, isHttps, version.Major, version.Minor); } @@ -109,7 +115,9 @@ internal static string GetConnectionKey(string hostname, int port, bool isHttps, /// /// /// - private static async Task CreateClient(string hostname, int port, bool isHttps, Version version) + private async Task CreateClient(string hostname, int port, bool isHttps, Version version, + ExternalProxy upStreamHttpProxy, ExternalProxy upStreamHttpsProxy, int bufferSize, SslProtocols supportedSslProtocols, + RemoteCertificateValidationCallback remoteCertificateValidationCallBack, LocalCertificateSelectionCallback localCertificateSelectionCallback) { TcpClient client; Stream stream; @@ -119,12 +127,12 @@ private static async Task CreateClient(string hostname, int port, SslStream sslStream = null; //If this proxy uses another external proxy then create a tunnel request for HTTPS connections - if (ProxyServer.UpStreamHttpsProxy != null) + if (upStreamHttpsProxy != null) { - client = new TcpClient(ProxyServer.UpStreamHttpsProxy.HostName, ProxyServer.UpStreamHttpsProxy.Port); + client = new TcpClient(upStreamHttpsProxy.HostName, upStreamHttpsProxy.Port); stream = (Stream)client.GetStream(); - using (var writer = new StreamWriter(stream, Encoding.ASCII, ProxyConstants.BUFFER_SIZE, true)) + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true)) { await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", hostname, port, version)); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", hostname, port)); @@ -152,9 +160,9 @@ private static async Task CreateClient(string hostname, int port, try { - sslStream = new SslStream(stream, true, new RemoteCertificateValidationCallback(ProxyServer.ValidateServerCertificate), - new LocalCertificateSelectionCallback(ProxyServer.SelectClientCertificate)); - await sslStream.AuthenticateAsClientAsync(hostname, null, ProxyConstants.SupportedSslProtocols, false); + sslStream = new SslStream(stream, true, remoteCertificateValidationCallBack, + localCertificateSelectionCallback); + await sslStream.AuthenticateAsClientAsync(hostname, null, supportedSslProtocols, false); stream = (Stream)sslStream; } catch @@ -166,9 +174,9 @@ private static async Task CreateClient(string hostname, int port, } else { - if (ProxyServer.UpStreamHttpProxy != null) + if (upStreamHttpProxy != null) { - client = new TcpClient(ProxyServer.UpStreamHttpProxy.HostName, ProxyServer.UpStreamHttpProxy.Port); + client = new TcpClient(upStreamHttpProxy.HostName, upStreamHttpProxy.Port); stream = (Stream)client.GetStream(); } else @@ -178,7 +186,7 @@ private static async Task CreateClient(string hostname, int port, } } - return new TcpConnection() + return new TcpConnectionCache() { HostName = hostname, port = port, @@ -195,7 +203,7 @@ private static async Task CreateClient(string hostname, int port, /// /// /// - internal static async Task ReleaseClient(TcpConnection connection) + internal async Task ReleaseClient(TcpConnectionCache connection) { connection.LastAccess = DateTime.Now; @@ -203,25 +211,25 @@ internal static async Task ReleaseClient(TcpConnection connection) await connectionAccessLock.WaitAsync(); try { - List cachedConnections; + List cachedConnections; connectionCache.TryGetValue(key, out cachedConnections); if (cachedConnections != null) cachedConnections.Add(connection); else - connectionCache.Add(key, new List() { connection }); + connectionCache.Add(key, new List() { connection }); } finally { connectionAccessLock.Release(); } } - private static bool clearConenctions { get; set; } + private bool clearConenctions { get; set; } /// /// Stop clearing idle connections /// - internal static void StopClearIdleConnections() + internal void StopClearIdleConnections() { clearConenctions = false; } @@ -229,7 +237,7 @@ internal static void StopClearIdleConnections() /// /// A method to clear idle connections /// - internal async static void ClearIdleConnections() + internal async void ClearIdleConnections(int connectionCacheTimeOutMinutes) { clearConenctions = true; while (clearConenctions) @@ -237,7 +245,7 @@ internal async static void ClearIdleConnections() await connectionAccessLock.WaitAsync(); try { - var cutOff = DateTime.Now.AddMinutes(-1 * ProxyServer.ConnectionCacheTimeOutMinutes); + var cutOff = DateTime.Now.AddMinutes(-1 * connectionCacheTimeOutMinutes); connectionCache .SelectMany(x => x.Value) diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 5a3f3ec28..cc8941302 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -8,125 +8,150 @@ using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Network; using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Net.Security; +using System.Security.Authentication; namespace Titanium.Web.Proxy { /// /// Proxy Server Main class /// - public partial class ProxyServer + public partial class ProxyServer: IDisposable { + + /// + /// Manages certificates used by this proxy + /// + private CertificateManager certificateCacheManager { get; set; } - static ProxyServer() - { - ProxyEndPoints = new List(); - - //default values - ConnectionCacheTimeOutMinutes = 3; - CertificateCacheTimeOutMinutes = 60; - } + /// + /// A object that manages tcp connection cache to server + /// + private TcpConnectionCacheManager tcpConnectionCacheManager { get; set; } /// - /// Manages certificates used by this proxy + /// Manage system proxy settings /// - private static CertificateManager CertManager { get; set; } - + private SystemProxyManager systemProxySettingsManager { get; set; } + + private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; } + /// /// Does the root certificate used by this proxy is trusted by the machine? /// - private static bool certTrusted { get; set; } + private bool certTrusted { get; set; } /// /// Is the proxy currently running /// - private static bool proxyRunning { get; set; } + private bool proxyRunning { get; set; } + + /// + /// Buffer size used throughout this proxy + /// + public int BUFFER_SIZE { get; set; } = 8192; /// /// Name of the root certificate issuer /// - public static string RootCertificateIssuerName { get; set; } + public string RootCertificateIssuerName { get; set; } /// /// Name of the root certificate /// - public static string RootCertificateName { get; set; } + public string RootCertificateName { 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 /// - public static bool Enable100ContinueBehaviour { get; set; } - + public bool Enable100ContinueBehaviour { get; set; } + /// /// Minutes TCP connection cache to servers to be kept alive when in idle state /// - public static int ConnectionCacheTimeOutMinutes { get; set; } - + public int ConnectionCacheTimeOutMinutes { get; set; } + /// /// Minutes certificates should be kept in cache when not used /// - public static int CertificateCacheTimeOutMinutes { get; set; } + public int CertificateCacheTimeOutMinutes { get; set; } /// /// Intercept request to server /// - public static event Func BeforeRequest; + public event Func BeforeRequest; /// /// Intercept response from server /// - public static event Func BeforeResponse; + public event Func BeforeResponse; /// /// External proxy for Http /// - public static ExternalProxy UpStreamHttpProxy { get; set; } + public ExternalProxy UpStreamHttpProxy { get; set; } /// /// External proxy for Http /// - public static ExternalProxy UpStreamHttpsProxy { get; set; } + public ExternalProxy UpStreamHttpsProxy { get; set; } /// /// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication /// - public static event Func ServerCertificateValidationCallback; - + public event Func ServerCertificateValidationCallback; + /// /// Callback tooverride client certificate during SSL mutual authentication /// - public static event Func ClientCertificateSelectionCallback; + public event Func ClientCertificateSelectionCallback; /// /// A list of IpAddress & port this proxy is listening to /// - public static List ProxyEndPoints { get; set; } + public List ProxyEndPoints { get; set; } /// - /// Initialize the proxy + /// List of supported Ssl versions /// - public static void Initialize() + public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; + + /// + /// Constructor + /// + public ProxyServer() { - TcpConnectionManager.ClearIdleConnections(); - CertManager.ClearIdleCertificates(); + //default values + ConnectionCacheTimeOutMinutes = 3; + CertificateCacheTimeOutMinutes = 60; + + ProxyEndPoints = new List(); + tcpConnectionCacheManager = new TcpConnectionCacheManager(); + systemProxySettingsManager = new SystemProxyManager(); + firefoxProxySettingsManager = new FireFoxProxySettingsManager(); + + RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority"; + RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium"; + + certificateCacheManager = new CertificateManager(RootCertificateIssuerName, + RootCertificateName); } + /// - /// Quit the proxy + /// Initialize the proxy /// - public static void Quit() + public void Initialize() { - TcpConnectionManager.StopClearIdleConnections(); - CertManager.StopClearIdleCertificates(); + tcpConnectionCacheManager.ClearIdleConnections(ConnectionCacheTimeOutMinutes); + certificateCacheManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); } /// /// Add a proxy end point /// /// - public static void AddEndPoint(ProxyEndPoint endPoint) + public void AddEndPoint(ProxyEndPoint endPoint) { ProxyEndPoints.Add(endPoint); @@ -139,7 +164,7 @@ public static void AddEndPoint(ProxyEndPoint endPoint) /// Will throw error if the end point does'nt exist /// /// - public static void RemoveEndPoint(ProxyEndPoint endPoint) + public void RemoveEndPoint(ProxyEndPoint endPoint) { if (ProxyEndPoints.Contains(endPoint) == false) @@ -155,19 +180,19 @@ public static void RemoveEndPoint(ProxyEndPoint endPoint) /// Set the given explicit end point as the default proxy server for current machine /// /// - public static void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) + public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) { ValidateEndPointAsSystemProxy(endPoint); //clear any settings previously added ProxyEndPoints.OfType().ToList().ForEach(x => x.IsSystemHttpProxy = false); - SystemProxyHelper.SetHttpProxy( + systemProxySettingsManager.SetHttpProxy( Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port); endPoint.IsSystemHttpProxy = true; #if !DEBUG - FireFoxHelper.AddFirefox(); + firefoxProxySettingsManager.AddFirefox(); #endif Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); @@ -176,16 +201,16 @@ public static void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) /// /// Remove any HTTP proxy setting of current machien /// - public static void DisableSystemHttpProxy() + public void DisableSystemHttpProxy() { - SystemProxyHelper.RemoveHttpProxy(); + systemProxySettingsManager.RemoveHttpProxy(); } /// /// Set the given explicit end point as the default proxy server for current machine /// /// - public static void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) + public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) { ValidateEndPointAsSystemProxy(endPoint); @@ -201,7 +226,7 @@ public static void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) //If certificate was trusted by the machine if (certTrusted) { - SystemProxyHelper.SetHttpsProxy( + systemProxySettingsManager.SetHttpsProxy( Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port); } @@ -209,7 +234,7 @@ public static void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) endPoint.IsSystemHttpsProxy = true; #if !DEBUG - FireFoxHelper.AddFirefox(); + firefoxProxySettingsManager.AddFirefox(); #endif Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); } @@ -217,34 +242,28 @@ public static void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) /// /// Remove any HTTPS proxy setting for current machine /// - public static void DisableSystemHttpsProxy() + public void DisableSystemHttpsProxy() { - SystemProxyHelper.RemoveHttpsProxy(); + systemProxySettingsManager.RemoveHttpsProxy(); } /// /// Clear all proxy settings for current machine /// - public static void DisableAllSystemProxies() + public void DisableAllSystemProxies() { - SystemProxyHelper.DisableAllProxy(); + systemProxySettingsManager.DisableAllProxy(); } /// /// Start this proxy server /// - public static void Start() + public void Start() { if (proxyRunning) throw new Exception("Proxy is already running."); - RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority"; - RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium"; - - CertManager = new CertificateManager(RootCertificateIssuerName, - RootCertificateName); - - certTrusted = CertManager.CreateTrustedRootCertificate().Result; + certTrusted = certificateCacheManager.CreateTrustedRootCertificate().Result; foreach (var endPoint in ProxyEndPoints) { @@ -259,7 +278,7 @@ public static void Start() /// /// Stop this proxy server /// - public static void Stop() + public void Stop() { if (!proxyRunning) throw new Exception("Proxy is not running."); @@ -268,9 +287,9 @@ public static void Stop() if (setAsSystemProxy) { - SystemProxyHelper.DisableAllProxy(); + systemProxySettingsManager.DisableAllProxy(); #if !DEBUG - FireFoxHelper.RemoveFirefox(); + firefoxProxySettingsManager.RemoveFirefox(); #endif } @@ -279,9 +298,8 @@ public static void Stop() endPoint.listener.Stop(); } - CertManager.Dispose(); - - Quit(); + tcpConnectionCacheManager.StopClearIdleConnections(); + certificateCacheManager.StopClearIdleCertificates(); proxyRunning = false; } @@ -290,7 +308,7 @@ public static void Stop() /// Listen on the given end point on local machine /// /// - private static void Listen(ProxyEndPoint endPoint) + private void Listen(ProxyEndPoint endPoint) { endPoint.listener = new TcpListener(endPoint.IpAddress, endPoint.Port); endPoint.listener.Start(); @@ -304,7 +322,7 @@ private static void Listen(ProxyEndPoint endPoint) /// Quit listening on the given end point /// /// - private static void QuitListen(ProxyEndPoint endPoint) + private void QuitListen(ProxyEndPoint endPoint) { endPoint.listener.Stop(); } @@ -313,7 +331,7 @@ private static void QuitListen(ProxyEndPoint endPoint) /// Verifiy if its safe to set this end point as System proxy /// /// - private static void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) + private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) { if (ProxyEndPoints.Contains(endPoint) == false) throw new Exception("Cannot set endPoints not added to proxy as system proxy"); @@ -326,7 +344,7 @@ private static void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint /// When a connection is received from client act /// /// - private static void OnAcceptConnection(IAsyncResult asyn) + private void OnAcceptConnection(IAsyncResult asyn) { var endPoint = (ProxyEndPoint)asyn.AsyncState; @@ -353,8 +371,15 @@ private static void OnAcceptConnection(IAsyncResult asyn) { //Other errors are discarded to keep proxy running } - + + } + + public void Dispose() + { + if (proxyRunning) + Stop(); + + certificateCacheManager.Dispose(); } - } } \ No newline at end of file diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index 22ca17c79..e38ee329a 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -13,8 +13,8 @@ using System.Security.Cryptography.X509Certificates; using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Http; -using Titanium.Web.Proxy.Extensions; using System.Threading.Tasks; +using Titanium.Web.Proxy.Extensions; namespace Titanium.Web.Proxy { @@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy partial class ProxyServer { //This is called when client is aware of proxy - private static async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client) + private async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client) { Stream clientStream = client.GetStream(); var clientStreamReader = new CustomBinaryReader(clientStream); @@ -82,14 +82,17 @@ private static async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient { //create the Tcp Connection to server and then release it to connection cache //Just doing what CONNECT request is asking as to do - var tunnelClient = await TcpConnectionManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version); - await TcpConnectionManager.ReleaseClient(tunnelClient); + var tunnelClient = await tcpConnectionCacheManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version, + UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), + new LocalCertificateSelectionCallback(SelectClientCertificate)); + + await tcpConnectionCacheManager.ReleaseClient(tunnelClient); sslStream = new SslStream(clientStream, true); - var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host, false); + var certificate = await certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, - ProxyConstants.SupportedSslProtocols, false); + SupportedSslProtocols, false); //HTTPS server created - we can now decrypt the client's traffic clientStream = sslStream; @@ -120,7 +123,7 @@ await sslStream.AuthenticateAsServerAsync(certificate, false, //Just relay the request/response without decrypting it await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port, - false); + false, SupportedSslProtocols); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); return; @@ -138,7 +141,7 @@ await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader //This is called when requests are routed through router to this endpoint //For ssl requests - private static async void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) + private async void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) { Stream clientStream = tcpClient.GetStream(); CustomBinaryReader clientStreamReader = null; @@ -150,7 +153,7 @@ private static async void HandleClient(TransparentProxyEndPoint endPoint, TcpCli var sslStream = new SslStream(clientStream, true); //implement in future once SNI supported by SSL stream, for now use the same certificate - certificate = await CertManager.CreateCertificate(endPoint.GenericCertificateName, false); + certificate = await certificateCacheManager.CreateCertificate(endPoint.GenericCertificateName, false); try { @@ -195,10 +198,10 @@ await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamRea /// /// /// - private static async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, + private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool isHttps) { - TcpConnection connection = null; + TcpConnectionCache connection = null; //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) @@ -210,7 +213,7 @@ private static async Task HandleHttpSessionRequest(TcpClient client, string http break; } - var args = new SessionEventArgs(); + var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse); args.ProxyClient.TcpClient = client; try @@ -281,13 +284,15 @@ private static async Task HandleHttpSessionRequest(TcpClient client, string http if (args.WebSession.Request.UpgradeToWebSocket) { await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders, - httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps); + httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps, SupportedSslProtocols); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); return; } //construct the web request that we are going to issue on behalf of the client. - connection = await TcpConnectionManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version); + connection = await tcpConnectionCacheManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version, + UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), + new LocalCertificateSelectionCallback(SelectClientCertificate)); args.WebSession.Request.RequestLocked = true; @@ -303,7 +308,7 @@ await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHe if (args.WebSession.Request.ExpectContinue) { args.WebSession.SetConnection(connection); - await args.WebSession.SendRequest(); + await args.WebSession.SendRequest(Enable100ContinueBehaviour); } //If 100 continue was the response inform that to the client @@ -325,7 +330,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", if (!args.WebSession.Request.ExpectContinue) { args.WebSession.SetConnection(connection); - await args.WebSession.SendRequest(); + await args.WebSession.SendRequest(Enable100ContinueBehaviour); } //If request was modified by user @@ -367,7 +372,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", } //send the tcp connection to server back to connection cache for reuse - await TcpConnectionManager.ReleaseClient(connection); + await tcpConnectionCacheManager.ReleaseClient(connection); // read the next request httpCmd = await clientStreamReader.ReadLineAsync(); @@ -389,7 +394,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", /// /// /// - private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion) + private async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion) { await clientStreamWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2}", httpVersion.Major, httpVersion.Minor, "200 Connection established")); await clientStreamWriter.WriteLineAsync(string.Format("Timestamp: {0}", DateTime.Now)); @@ -402,7 +407,7 @@ private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, /// /// /// - private static void PrepareRequestHeaders(List requestHeaders, HttpWebClient webRequest) + private void PrepareRequestHeaders(List requestHeaders, HttpWebClient webRequest) { for (var i = 0; i < requestHeaders.Count; i++) { @@ -426,7 +431,7 @@ private static void PrepareRequestHeaders(List requestHeaders, HttpW /// Fix proxy specific headers /// /// - private static void FixRequestProxyHeaders(List headers) + private void FixRequestProxyHeaders(List headers) { //If proxy-connection close was returned inform to close the connection var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection"); @@ -450,7 +455,7 @@ private static void FixRequestProxyHeaders(List headers) /// /// /// - private static async Task SendClientRequestBody(SessionEventArgs args) + private async Task SendClientRequestBody(SessionEventArgs args) { // End the operation var postStream = args.WebSession.ServerConnection.Stream; @@ -458,14 +463,14 @@ private static async Task SendClientRequestBody(SessionEventArgs args) //send the request body bytes to server if (args.WebSession.Request.ContentLength > 0) { - await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength); + await args.ProxyClient.ClientStreamReader.CopyBytesToStream(BUFFER_SIZE, postStream, args.WebSession.Request.ContentLength); } //Need to revist, find any potential bugs //send the request body bytes to server in chunks else if (args.WebSession.Request.IsChunked) { - await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(postStream); + await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(BUFFER_SIZE, postStream); } } diff --git a/Titanium.Web.Proxy/ResponseHandler.cs b/Titanium.Web.Proxy/ResponseHandler.cs index 0e633da26..37a90fefc 100644 --- a/Titanium.Web.Proxy/ResponseHandler.cs +++ b/Titanium.Web.Proxy/ResponseHandler.cs @@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy partial class ProxyServer { //Called asynchronously when a request was successfully and we received the response - public static async Task HandleHttpSessionResponse(SessionEventArgs args) + public async Task HandleHttpSessionResponse(SessionEventArgs args) { //read response & headers from server await args.WebSession.ReceiveResponse(); @@ -86,11 +86,11 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession. //Write body only if response is chunked or content length >0 //Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || !args.WebSession.Response.ResponseKeepAlive) - await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); + await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); //write response if connection:keep-alive header exist and when version is http/1.0 //Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream) else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0) - await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); + await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); } await args.ProxyClient.ClientStream.FlushAsync(); @@ -112,7 +112,7 @@ await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession. /// /// /// - private static async Task GetCompressedResponseBody(string encodingType, byte[] responseBodyStream) + private async Task GetCompressedResponseBody(string encodingType, byte[] responseBodyStream) { var compressionFactory = new CompressionFactory(); var compressor = compressionFactory.Create(encodingType); @@ -127,7 +127,7 @@ private static async Task GetCompressedResponseBody(string encodingType, /// /// /// - private static async Task WriteResponseStatus(Version version, string code, string description, + private async Task WriteResponseStatus(Version version, string code, string description, StreamWriter responseWriter) { await responseWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, code, description)); @@ -139,7 +139,7 @@ private static async Task WriteResponseStatus(Version version, string code, stri /// /// /// - private static async Task WriteResponseHeaders(StreamWriter responseWriter, List headers) + private async Task WriteResponseHeaders(StreamWriter responseWriter, List headers) { if (headers != null) { @@ -159,7 +159,7 @@ private static async Task WriteResponseHeaders(StreamWriter responseWriter, List /// Fix the proxy specific headers before sending response headers to client /// /// - private static void FixResponseProxyHeaders(List headers) + private void FixResponseProxyHeaders(List headers) { //If proxy-connection close was returned inform to close the connection var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection"); @@ -186,7 +186,7 @@ private static void FixResponseProxyHeaders(List headers) /// /// /// - private static void Dispose(TcpClient client, IDisposable clientStream, IDisposable clientStreamReader, + private void Dispose(TcpClient client, IDisposable clientStream, IDisposable clientStreamReader, IDisposable clientStreamWriter, IDisposable args) { if (args != null) diff --git a/Titanium.Web.Proxy/Shared/ProxyConstants.cs b/Titanium.Web.Proxy/Shared/ProxyConstants.cs index e5583718c..22502d1ff 100644 --- a/Titanium.Web.Proxy/Shared/ProxyConstants.cs +++ b/Titanium.Web.Proxy/Shared/ProxyConstants.cs @@ -1,5 +1,4 @@ using System; -using System.Security.Authentication; using System.Text; namespace Titanium.Web.Proxy.Shared @@ -9,7 +8,7 @@ namespace Titanium.Web.Proxy.Shared /// internal class ProxyConstants { - internal static readonly char[] SpaceSplit = { ' ' }; + internal static readonly char[] SpaceSplit = { ' ' }; internal static readonly char[] ColonSplit = { ':' }; internal static readonly char[] SemiColonSplit = { ';' }; @@ -18,8 +17,5 @@ internal class ProxyConstants internal static readonly byte[] ChunkEnd = Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine); - public static SslProtocols SupportedSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; - - public static readonly int BUFFER_SIZE = 8192; } } diff --git a/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj b/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj index 10d28b011..2844758bc 100644 --- a/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj +++ b/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj @@ -77,8 +77,8 @@ - - + + From 1cad651947fba730ec32c051e9d90951de7e3c60 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Thu, 16 Jun 2016 23:13:44 -0400 Subject: [PATCH 3/5] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ce1ad3703..44604f243 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ After installing nuget package mark following files to be copied to app director Setup HTTP proxy: ```csharp + var ProxyServer = new ProxyServer(); + ProxyServer.BeforeRequest += OnRequest; ProxyServer.BeforeResponse += OnResponse; ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation; From a426879b5858d47221a51eb5d89f2d1cebf82ea2 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Thu, 16 Jun 2016 23:46:03 -0400 Subject: [PATCH 4/5] unsubscribe --- .../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 7149086b6..55e2e9034 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -64,6 +64,8 @@ public void Stop() { proxyServer.BeforeRequest -= OnRequest; proxyServer.BeforeResponse -= OnResponse; + proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation; + proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection; proxyServer.Stop(); } From 36a721b4dc2471bcde7ec05a61c77ad2842eb6d6 Mon Sep 17 00:00:00 2001 From: justcoding121 Date: Thu, 16 Jun 2016 23:55:41 -0400 Subject: [PATCH 5/5] #81 Fix quit --- Titanium.Web.Proxy/ProxyServer.cs | 40 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index cc8941302..698d094c3 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -137,22 +137,15 @@ public ProxyServer() RootCertificateName); } - - /// - /// Initialize the proxy - /// - public void Initialize() - { - tcpConnectionCacheManager.ClearIdleConnections(ConnectionCacheTimeOutMinutes); - certificateCacheManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); - } - /// /// Add a proxy end point /// /// public void AddEndPoint(ProxyEndPoint endPoint) { + if (ProxyEndPoints.Any(x=>x.IpAddress == endPoint.IpAddress && x.Port == endPoint.Port)) + throw new Exception("Cannot add another endpoint to same port & ip address"); + ProxyEndPoints.Add(endPoint); if (proxyRunning) @@ -166,7 +159,6 @@ public void AddEndPoint(ProxyEndPoint endPoint) /// public void RemoveEndPoint(ProxyEndPoint endPoint) { - if (ProxyEndPoints.Contains(endPoint) == false) throw new Exception("Cannot remove endPoints not added to proxy"); @@ -197,14 +189,7 @@ public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); } - - /// - /// Remove any HTTP proxy setting of current machien - /// - public void DisableSystemHttpProxy() - { - systemProxySettingsManager.RemoveHttpProxy(); - } + /// /// Set the given explicit end point as the default proxy server for current machine @@ -239,6 +224,14 @@ public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); } + /// + /// Remove any HTTP proxy setting of current machien + /// + public void DisableSystemHttpProxy() + { + systemProxySettingsManager.RemoveHttpProxy(); + } + /// /// Remove any HTTPS proxy setting for current machine /// @@ -270,7 +263,8 @@ public void Start() Listen(endPoint); } - Initialize(); + tcpConnectionCacheManager.ClearIdleConnections(ConnectionCacheTimeOutMinutes); + certificateCacheManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); proxyRunning = true; } @@ -295,9 +289,11 @@ public void Stop() foreach (var endPoint in ProxyEndPoints) { - endPoint.listener.Stop(); + QuitListen(endPoint); } + ProxyEndPoints.Clear(); + tcpConnectionCacheManager.StopClearIdleConnections(); certificateCacheManager.StopClearIdleCertificates(); @@ -325,6 +321,8 @@ private void Listen(ProxyEndPoint endPoint) private void QuitListen(ProxyEndPoint endPoint) { endPoint.listener.Stop(); + endPoint.listener.Server.Close(); + endPoint.listener.Server.Dispose(); } ///