onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg)
- {
- // this is just to show the functionality, provided values are junk
- return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.10", Port = 9191, Password = "fake2", UserName = "fake2", UseDefaultCredentials = false };
- }
-
- private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
- {
- string hostname = e.HttpClient.Request.RequestUri.Host;
- await writeToConsole("Tunnel to: " + hostname);
-
- if (hostname.Contains("dropbox.com"))
- {
- // Exclude Https addresses you don't want to proxy
- // Useful for clients that use certificate pinning
- // for example dropbox.com
- e.DecryptSsl = false;
- }
- }
-
- private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
- {
- return Task.FromResult(false);
- }
-
- // intercept & cancel redirect or update requests
- private async Task onRequest(object sender, SessionEventArgs e)
- {
- await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
- await writeToConsole(e.HttpClient.Request.Url);
-
- // store it in the UserData property
- // It can be a simple integer, Guid, or any type
- //e.UserData = new CustomUserData()
- //{
- // RequestHeaders = e.HttpClient.Request.Headers,
- // RequestBody = e.HttpClient.Request.HasBody ? e.HttpClient.Request.Body:null,
- // RequestBodyString = e.HttpClient.Request.HasBody? e.HttpClient.Request.BodyString:null
- //};
-
- ////This sample shows how to get the multipart form data headers
- //if (e.HttpClient.Request.Host == "mail.yahoo.com" && e.HttpClient.Request.IsMultipartFormData)
- //{
- // e.MultipartRequestPartSent += MultipartRequestPartSent;
- //}
-
- // To cancel a request with a custom HTML content
- // Filter URL
- //if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("yahoo.com"))
- //{
- // e.Ok("" +
- // "" +
- // "Website Blocked" +
- // "
" +
- // "Blocked by titanium web proxy.
" +
- // "" +
- // "");
- //}
-
- ////Redirect example
- //if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
- //{
- // e.Redirect("https://www.paypal.com");
- //}
- }
-
- // Modify response
- private async Task multipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
- {
- var session = (SessionEventArgs)sender;
- await writeToConsole("Multipart form data headers:");
- foreach (var header in e.Headers)
- {
- await writeToConsole(header.ToString());
- }
- }
-
- private async Task onResponse(object sender, SessionEventArgs e)
- {
- await writeToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
-
- string ext = System.IO.Path.GetExtension(e.HttpClient.Request.RequestUri.AbsolutePath);
-
- // access user data set in request to do something with it
- //var userData = e.HttpClient.UserData as CustomUserData;
-
- //if (ext == ".gif" || ext == ".png" || ext == ".jpg")
- //{
- // byte[] btBody = Encoding.UTF8.GetBytes("" +
- // "" +
- // "Image is blocked" +
- // "
" +
- // "Blocked by Titanium
" +
- // "" +
- // "");
-
- // var response = new OkResponse(btBody);
- // response.HttpVersion = e.HttpClient.Request.HttpVersion;
-
- // e.Respond(response);
- // e.TerminateServerConnection();
- //}
-
- //// print out process id of current session
- ////WriteToConsole($"PID: {e.HttpClient.ProcessId.Value}");
-
- ////if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
- //if (e.HttpClient.Request.Method == "GET" || e.HttpClient.Request.Method == "POST")
- //{
- // if (e.HttpClient.Response.StatusCode == (int)HttpStatusCode.OK)
- // {
- // if (e.HttpClient.Response.ContentType != null && e.HttpClient.Response.ContentType.Trim().ToLower().Contains("text/html"))
- // {
- // var bodyBytes = await e.GetResponseBody();
- // await e.SetResponseBody(bodyBytes);
-
- // string body = await e.GetResponseBodyAsString();
- // await e.SetResponseBodyString(body);
- // }
- // }
- //}
- }
-
- ///
- /// Allows overriding default certificate validation logic
- ///
- ///
- ///
- public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
- {
- // set IsValid to true/false based on Certificate Errors
- if (e.SslPolicyErrors == SslPolicyErrors.None)
- {
- e.IsValid = true;
- }
-
- return Task.FromResult(0);
- }
-
- ///
- /// Allows overriding default client certificate selection logic during mutual authentication
- ///
- ///
- ///
- public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
- {
- // set e.clientCertificate to override
-
- return Task.FromResult(0);
- }
-
- private async Task writeToConsole(string message, bool useRedColor = false)
- {
- await @lock.WaitAsync();
-
- if (useRedColor)
- {
- ConsoleColor existing = Console.ForegroundColor;
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine(message);
- Console.ForegroundColor = existing;
- }
- else
- {
- Console.WriteLine(message);
- }
-
- @lock.Release();
- }
-
- /////
- ///// User data object as defined by user.
- ///// User data can be set to each SessionEventArgs.HttpClient.UserData property
- /////
- //public class CustomUserData
- //{
- // public HeaderCollection RequestHeaders { get; set; }
- // public byte[] RequestBody { get; set; }
- // public string RequestBodyString { get; set; }
- //}
- }
+{
+ static class ExtendedProxyStateHelher
+ {
+ internal static ExtendedProxyState Extended(this RequestStateBase state)
+ {
+ return (ExtendedProxyState)state;
+ }
+ }
+ public class ExtendedProxyState : RequestState
+ {
+
+ internal StringBuilder PipelineInfo=new StringBuilder();
+
+ public override async Task OnErrorAsync(Exception e)
+ {
+ await Console.Out.WriteLineAsync($"OnErrorAsync: {PipelineInfo} : {e}");
+ }
+ public override void OnError(Exception e)
+ {
+ Console.Out.WriteLine($"OnErrorAsync: {PipelineInfo}: {e}");
+ }
+
+ }
+
+ public class ProxyServer : ExtendedProxyState.ProxyServer
+ {
+
+ }
+
+ public class ProxyTestController
+ {
+ private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
+ private readonly ProxyServer proxyServer;
+ private ExplicitProxyEndPoint explicitEndPoint;
+
+ public ProxyTestController()
+ {
+ proxyServer = new ProxyServer();
+ // generate root certificate without storing it in file system
+ //proxyServer.CertificateManager.CreateRootCertificate(false);
+
+ //proxyServer.CertificateManager.TrustRootCertificate();
+ //proxyServer.CertificateManager.TrustRootCertificateAsAdmin();
+
+ proxyServer.ExceptionFunc = async exception =>
+ {
+ if (exception is ProxyHttpException phex)
+ {
+ await writeToConsole(exception.Message + ": " + phex.InnerException?.Message, true);
+ }
+ else
+ {
+ await writeToConsole(exception.Message, true);
+ }
+ };
+ proxyServer.ForwardToUpstreamGateway = true;
+ proxyServer.CertificateManager.SaveFakeCertificates = true;
+
+ // this is just to show the functionality, provided implementations use junk value
+ //proxyServer.GetCustomUpStreamProxyFunc = onGetCustomUpStreamProxyFunc;
+ //proxyServer.CustomUpStreamProxyFailureFunc = onCustomUpStreamProxyFailureFunc;
+
+ // optionally set the Certificate Engine
+ // Under Mono or Non-Windows runtimes only BouncyCastle will be supported
+ //proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
+
+ // optionally set the Root Certificate
+ //proxyServer.CertificateManager.RootCertificate = new X509Certificate2("myCert.pfx", string.Empty, X509KeyStorageFlags.Exportable);
+ }
+
+ public void StartProxy()
+ {
+ proxyServer.BeforeRequest += onRequest;
+ proxyServer.BeforeResponse += onResponse;
+ proxyServer.AfterResponse += ProxyServer_AfterResponse;
+
+ proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
+ proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
+
+ //proxyServer.EnableWinAuth = true;
+
+ explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000);
+
+ // Fired when a CONNECT request is received
+ explicitEndPoint.BeforeTunnelConnectRequest += onBeforeTunnelConnectRequest;
+ explicitEndPoint.BeforeTunnelConnectResponse += onBeforeTunnelConnectResponse;
+
+ // An explicit endpoint is where the client knows about the existence of a proxy
+ // So client sends request in a proxy friendly manner
+ proxyServer.AddEndPoint(explicitEndPoint);
+ proxyServer.Start();
+
+ // Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy)
+ // A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS
+ // to send data to this endPoint
+ //var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 443, true)
+ //{
+ // // Generic Certificate hostname to use
+ // // When SNI is disabled by client
+ // GenericCertificateName = "google.com"
+ //};
+
+ //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)
+ {
+ 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);
+ if (RunTime.IsWindows)
+ {
+ proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp);
+ }
+ }
+
+ private async Task ProxyServer_AfterResponse(object sender, SessionEventArgs e)
+ {
+ await Console.Out.WriteLineAsync($"Piplineinfo: {e.State.Extended().PipelineInfo}");
+ }
+
+ public void Stop()
+ {
+ explicitEndPoint.BeforeTunnelConnectRequest -= onBeforeTunnelConnectRequest;
+ explicitEndPoint.BeforeTunnelConnectResponse -= onBeforeTunnelConnectResponse;
+
+ proxyServer.BeforeRequest -= onRequest;
+ proxyServer.BeforeResponse -= onResponse;
+ proxyServer.AfterResponse -= ProxyServer_AfterResponse;
+
+ proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
+ proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
+
+ proxyServer.Stop();
+
+ // remove the generated certificates
+ //proxyServer.CertificateManager.RemoveTrustedRootCertificates();
+ }
+
+ private async Task onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg)
+ {
+
+ arg.State.Extended().PipelineInfo.AppendLine(nameof(onGetCustomUpStreamProxyFunc));
+
+ // this is just to show the functionality, provided values are junk
+ return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.9", Port = 9090, Password = "fake", UserName = "fake", UseDefaultCredentials = false };
+ }
+
+ private async Task onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg)
+ {
+ arg.State.Extended().PipelineInfo.AppendLine(nameof(onCustomUpStreamProxyFailureFunc));
+
+ // this is just to show the functionality, provided values are junk
+ return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.10", Port = 9191, Password = "fake2", UserName = "fake2", UseDefaultCredentials = false };
+ }
+
+ private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
+ {
+
+ string hostname = e.HttpClient.Request.RequestUri.Host;
+ e.State.Extended().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectRequest) + ":" +hostname);
+ //await writeToConsole("Tunnel to: " + hostname);
+
+ if (hostname.Contains("dropbox.com"))
+ {
+ // Exclude Https addresses you don't want to proxy
+ // Useful for clients that use certificate pinning
+ // for example dropbox.com
+ e.DecryptSsl = false;
+ }
+ }
+
+ private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
+ {
+ e.State.Extended().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectResponse) + ":" + e.HttpClient.Request.RequestUri);
+
+ return Task.FromResult(false);
+ }
+
+ // intercept & cancel redirect or update requests
+ private async Task onRequest(object sender, SessionEventArgs e)
+ {
+ e.State.Extended().PipelineInfo.AppendLine(nameof(onRequest) + ":" + e.HttpClient.Request.RequestUri);
+
+ await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
+ await writeToConsole(e.HttpClient.Request.Url);
+
+ // store it in the UserData property
+ // It can be a simple integer, Guid, or any type
+ //e.UserData = new CustomUserData()
+ //{
+ // RequestHeaders = e.HttpClient.Request.Headers,
+ // RequestBody = e.HttpClient.Request.HasBody ? e.HttpClient.Request.Body:null,
+ // RequestBodyString = e.HttpClient.Request.HasBody? e.HttpClient.Request.BodyString:null
+ //};
+
+ ////This sample shows how to get the multipart form data headers
+ //if (e.HttpClient.Request.Host == "mail.yahoo.com" && e.HttpClient.Request.IsMultipartFormData)
+ //{
+ // e.MultipartRequestPartSent += MultipartRequestPartSent;
+ //}
+
+ // To cancel a request with a custom HTML content
+ // Filter URL
+ //if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("yahoo.com"))
+ //{
+ // e.Ok("" +
+ // "" +
+ // "Website Blocked" +
+ // "
" +
+ // "Blocked by titanium web proxy.
" +
+ // "" +
+ // "");
+ //}
+
+ ////Redirect example
+ //if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
+ //{
+ // e.Redirect("https://www.paypal.com");
+ //}
+ }
+
+ // Modify response
+ private async Task multipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
+ {
+ e.State.Extended().PipelineInfo.AppendLine(nameof(multipartRequestPartSent));
+
+ var session = (SessionEventArgs)sender;
+ await writeToConsole("Multipart form data headers:");
+ foreach (var header in e.Headers)
+ {
+ await writeToConsole(header.ToString());
+ }
+ }
+
+ private async Task onResponse(object sender, SessionEventArgs e)
+ {
+ e.State.Extended().PipelineInfo.AppendLine(nameof(onResponse));
+
+ await writeToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
+
+ string ext = System.IO.Path.GetExtension(e.HttpClient.Request.RequestUri.AbsolutePath);
+
+ // access user data set in request to do something with it
+ //var userData = e.HttpClient.UserData as CustomUserData;
+
+ //if (ext == ".gif" || ext == ".png" || ext == ".jpg")
+ //{
+ // byte[] btBody = Encoding.UTF8.GetBytes("" +
+ // "" +
+ // "Image is blocked" +
+ // "
" +
+ // "Blocked by Titanium
" +
+ // "" +
+ // "");
+
+ // var response = new OkResponse(btBody);
+ // response.HttpVersion = e.HttpClient.Request.HttpVersion;
+
+ // e.Respond(response);
+ // e.TerminateServerConnection();
+ //}
+
+ //// print out process id of current session
+ ////WriteToConsole($"PID: {e.HttpClient.ProcessId.Value}");
+
+ ////if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
+ //if (e.HttpClient.Request.Method == "GET" || e.HttpClient.Request.Method == "POST")
+ //{
+ // if (e.HttpClient.Response.StatusCode == (int)HttpStatusCode.OK)
+ // {
+ // if (e.HttpClient.Response.ContentType != null && e.HttpClient.Response.ContentType.Trim().ToLower().Contains("text/html"))
+ // {
+ // var bodyBytes = await e.GetResponseBody();
+ // await e.SetResponseBody(bodyBytes);
+
+ // string body = await e.GetResponseBodyAsString();
+ // await e.SetResponseBodyString(body);
+ // }
+ // }
+ //}
+ }
+
+ ///
+ /// Allows overriding default certificate validation logic
+ ///
+ ///
+ ///
+ public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
+ {
+ e.State.Extended().PipelineInfo.AppendLine(nameof(OnCertificateValidation));
+ // set IsValid to true/false based on Certificate Errors
+ if (e.SslPolicyErrors == SslPolicyErrors.None)
+ {
+ e.IsValid = true;
+ }
+
+ return Task.FromResult(0);
+ }
+
+ ///
+ /// Allows overriding default client certificate selection logic during mutual authentication
+ ///
+ ///
+ ///
+ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
+ {
+ e.State.Extended().PipelineInfo.AppendLine(nameof(OnCertificateSelection));
+ // set e.clientCertificate to override
+
+ return Task.FromResult(0);
+ }
+
+ private async Task writeToConsole(string message, bool useRedColor = false)
+ {
+ //await @lock.WaitAsync();
+
+ if (useRedColor)
+ {
+ ConsoleColor existing = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(message);
+ Console.ForegroundColor = existing;
+ }
+ else
+ {
+ Console.WriteLine(message);
+ }
+
+ @lock.Release();
+ }
+
+ /////
+ ///// User data object as defined by user.
+ ///// User data can be set to each SessionEventArgs.HttpClient.UserData property
+ /////
+ //public class CustomUserData
+ //{
+ // public HeaderCollection RequestHeaders { get; set; }
+ // public byte[] RequestBody { get; set; }
+ // public string RequestBodyString { get; set; }
+ //}
+ }
}
diff --git a/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf_lmb5j4or_wpftmp.csproj b/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf_lmb5j4or_wpftmp.csproj
new file mode 100644
index 000000000..952043108
--- /dev/null
+++ b/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf_lmb5j4or_wpftmp.csproj
@@ -0,0 +1,174 @@
+
+
+ WinExe
+ net461;netcoreapp3.0
+ true
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Titanium.Web.Proxy/CertificateHandler.cs b/src/Titanium.Web.Proxy/CertificateHandler.cs
index b7efcfbe5..c4222d528 100644
--- a/src/Titanium.Web.Proxy/CertificateHandler.cs
+++ b/src/Titanium.Web.Proxy/CertificateHandler.cs
@@ -6,7 +6,22 @@
namespace Titanium.Web.Proxy
{
- public partial class ProxyServer
+ public partial class RequestStateBase
+ {
+ internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
+ SslPolicyErrors sslPolicyErrors)
+ {
+ return Server.ValidateServerCertificate(this, sender, certificate, chain, sslPolicyErrors);
+ }
+ internal X509Certificate? SelectClientCertificate(object sender, string targetHost,
+ X509CertificateCollection localCertificates,
+ X509Certificate remoteCertificate, string[] acceptableIssuers)
+ {
+ return Server.SelectClientCertificate(this, sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers);
+ }
+
+ }
+ public partial class ProxyServerBase
{
///
/// Call back to override server certificate validation
@@ -16,16 +31,16 @@ public partial class ProxyServer
/// The certificate chain.
/// Ssl policy errors
/// Return true if valid certificate.
- internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
+ internal bool ValidateServerCertificate(RequestStateBase state, object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
{
- var args = new CertificateValidationEventArgs(certificate, chain, sslPolicyErrors);
+ var args = new CertificateValidationEventArgs(state, certificate, chain, sslPolicyErrors);
// why is the sender null?
- ServerCertificateValidationCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
+ ServerCertificateValidationCallback.InvokeAsync(this, args, state.OnError).Wait();
return args.IsValid;
}
@@ -48,7 +63,7 @@ internal bool ValidateServerCertificate(object sender, X509Certificate certifica
/// The remote certificate of server.
/// The acceptable issues for client certificate as listed by server.
///
- internal X509Certificate? SelectClientCertificate(object sender, string targetHost,
+ internal X509Certificate? SelectClientCertificate(RequestStateBase state, object sender, string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers)
{
@@ -75,7 +90,7 @@ internal bool ValidateServerCertificate(object sender, X509Certificate certifica
// If user call back is registered
if (ClientCertificateSelectionCallback != null)
{
- var args = new CertificateSelectionEventArgs
+ var args = new CertificateSelectionEventArgs(state)
{
TargetHost = targetHost,
LocalCertificates = localCertificates,
diff --git a/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs
index 191a9bf1e..145783316 100644
--- a/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs
@@ -6,11 +6,12 @@ namespace Titanium.Web.Proxy.EventArguments
///
/// This is used in transparent endpoint before authenticating client.
///
- public class BeforeSslAuthenticateEventArgs : EventArgs
+ public class BeforeSslAuthenticateEventArgs : ProxyEventArgsBase
{
internal readonly CancellationTokenSource TaskCancellationSource;
- internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource, string sniHostName)
+ internal BeforeSslAuthenticateEventArgs(RequestStateBase state ,CancellationTokenSource taskCancellationSource, string sniHostName)
+ :base(state)
{
TaskCancellationSource = taskCancellationSource;
SniHostName = sniHostName;
diff --git a/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
index 257d80ca0..0ee5232d9 100644
--- a/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs
@@ -6,8 +6,11 @@ namespace Titanium.Web.Proxy.EventArguments
///
/// An argument passed on to user for client certificate selection during mutual SSL authentication.
///
- public class CertificateSelectionEventArgs : EventArgs
+ public class CertificateSelectionEventArgs : ProxyEventArgsBase
{
+ public CertificateSelectionEventArgs(RequestStateBase state)
+ :base(state)
+ { }
///
/// The proxy server instance.
///
diff --git a/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs
index 22e9bf381..49f8aef2e 100644
--- a/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs
@@ -8,9 +8,10 @@ namespace Titanium.Web.Proxy.EventArguments
/// An argument passed on to the user for validating the server certificate
/// during SSL authentication.
///
- public class CertificateValidationEventArgs : EventArgs
+ public class CertificateValidationEventArgs : ProxyEventArgsBase
{
- public CertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ public CertificateValidationEventArgs(RequestStateBase state, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ :base(state)
{
Certificate = certificate;
Chain = chain;
diff --git a/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs
index 988b19fa7..b9ed84d1f 100644
--- a/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments
@@ -6,9 +6,10 @@ namespace Titanium.Web.Proxy.EventArguments
///
/// Class that wraps the multipart sent request arguments.
///
- public class MultipartRequestPartSentEventArgs : EventArgs
+ public class MultipartRequestPartSentEventArgs : ProxyEventArgsBase
{
- internal MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
+ internal MultipartRequestPartSentEventArgs(RequestStateBase state, string boundary, HeaderCollection headers)
+ :base(state)
{
Boundary = boundary;
Headers = headers;
diff --git a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
index f9201877b..e674fdcc1 100644
--- a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
@@ -34,8 +34,8 @@ public class SessionEventArgs : SessionEventArgsBase
///
/// Constructor to initialize the proxy
///
- internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
- : base(server, endPoint, clientConnection, clientStream, connectRequest, new Request(), cancellationTokenSource)
+ internal SessionEventArgs(RequestStateBase state, ProxyEndPoint endPoint, TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
+ : base(state, endPoint, clientConnection, clientStream, connectRequest, new Request(), cancellationTokenSource)
{
}
@@ -123,7 +123,7 @@ internal void OnMultipartRequestPartSent(ReadOnlySpan boundary, HeaderColl
{
try
{
- MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary.ToString(), headers));
+ MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(this.State, boundary.ToString(), headers));
}
catch (Exception ex)
{
diff --git a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
index b6bdb5aef..e74cf68c2 100644
--- a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
@@ -12,13 +12,22 @@
namespace Titanium.Web.Proxy.EventArguments
{
+ public abstract class ProxyEventArgsBase : EventArgs
+ {
+ public readonly RequestStateBase State;
+ public ProxyEventArgsBase(RequestStateBase state)
+ {
+ this.State = state;
+ }
+
+ }
///
/// Holds info related to a single proxy session (single request/response sequence).
/// A proxy session is bounded to a single connection from client.
/// A proxy session ends when client terminates connection to proxy
/// or when server terminates connection from proxy.
///
- public abstract class SessionEventArgsBase : EventArgs, IDisposable
+ public abstract class SessionEventArgsBase : ProxyEventArgsBase, IDisposable
{
private static bool isWindowsAuthenticationSupported => RunTime.IsWindows;
@@ -49,9 +58,11 @@ public abstract class SessionEventArgsBase : EventArgs, IDisposable
///
/// Initializes a new instance of the class.
///
- private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
+ private protected SessionEventArgsBase(RequestStateBase state, ProxyEndPoint endPoint,
TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
+ :base(state)
{
+ var server = state.Server;
BufferPool = server.BufferPool;
ExceptionFunc = server.ExceptionFunc;
TimeLine["Session Created"] = DateTime.Now;
diff --git a/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
index 494b51754..10393b656 100644
--- a/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
@@ -16,9 +16,9 @@ public class TunnelConnectSessionEventArgs : SessionEventArgsBase
{
private bool? isHttpsConnect;
- internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
+ internal TunnelConnectSessionEventArgs(RequestStateBase state, ProxyEndPoint endPoint, ConnectRequest connectRequest,
TcpClientConnection clientConnection, HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource)
- : base(server, endPoint, clientConnection, clientStream, connectRequest, connectRequest, cancellationTokenSource)
+ : base(state, endPoint, clientConnection, clientStream, connectRequest, connectRequest, cancellationTokenSource)
{
}
diff --git a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
index f78076a80..09a1b080a 100644
--- a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
+++ b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
@@ -22,8 +22,8 @@
namespace Titanium.Web.Proxy
{
- public partial class ProxyServer
- {
+ public partial class ProxyServerBase
+ {
///
/// 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
@@ -31,8 +31,9 @@ public partial class ProxyServer
/// The explicit endpoint.
/// The client connection.
/// The task.
- private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
+ internal async Task handleClient(ExplicitProxyEndPoint endPoint, RequestStateBase state)
{
+ TcpClientConnection clientConnection = state;
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
@@ -66,7 +67,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);
- connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
+ connectArgs = new TunnelConnectSessionEventArgs(state, endPoint, connectRequest,
clientConnection, clientStream, cancellationTokenSource);
clientStream.DataRead += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);
@@ -137,7 +138,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
try
{
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
- var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
+ var connection = await tcpConnectionFactory.GetServerConnection(state, connectArgs,
true, SslExtensions.Http2ProtocolAsList,
true, cancellationToken);
@@ -167,7 +168,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
{
// don't pass cancellation token here
// it could cause floating server connections when client exits
- prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
+ prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(state, connectArgs,
true, null, false,
CancellationToken.None);
}
@@ -252,7 +253,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
// create new connection to server.
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
// this connection should not be HTTPS.
- var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
+ var connection = await tcpConnectionFactory.GetServerConnection(state, connectArgs,
true, SslExtensions.Http2ProtocolAsList,
true, cancellationToken);
@@ -329,7 +330,7 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
}
- var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
+ var connection = await tcpConnectionFactory.GetServerConnection(state, connectArgs,
true, SslExtensions.Http2ProtocolAsList,
true, cancellationToken);
try
@@ -338,7 +339,7 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
var connectionPreface = new ReadOnlyMemory(Http2Helper.ConnectionPreface);
await connection.Stream.WriteAsync(connectionPreface, cancellationToken);
await Http2Helper.SendHttp2(clientStream, connection.Stream,
- () => new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
+ () => new SessionEventArgs(state, endPoint, clientConnection, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
{
UserData = connectArgs?.UserData
},
@@ -357,7 +358,7 @@ await Http2Helper.SendHttp2(clientStream, connection.Stream,
calledRequestHandler = true;
// Now create the request
- await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
+ await handleHttpSessionRequest(endPoint, state, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
}
catch (ProxyException e)
{
diff --git a/src/Titanium.Web.Proxy/Helpers/ProxyInfo.cs b/src/Titanium.Web.Proxy/Helpers/ProxyInfo.cs
index 600e3a9d9..5a9006d3d 100644
--- a/src/Titanium.Web.Proxy/Helpers/ProxyInfo.cs
+++ b/src/Titanium.Web.Proxy/Helpers/ProxyInfo.cs
@@ -140,11 +140,11 @@ private static string convertRegexReservedChars(string rawString)
}
ProxyProtocolType? protocolType = null;
- if (protocolTypeStr.Equals(Proxy.ProxyServer.UriSchemeHttp, StringComparison.InvariantCultureIgnoreCase))
+ if (protocolTypeStr.Equals(Proxy.ProxyServerBase.UriSchemeHttp, StringComparison.InvariantCultureIgnoreCase))
{
protocolType = ProxyProtocolType.Http;
}
- else if (protocolTypeStr.Equals(Proxy.ProxyServer.UriSchemeHttps,
+ else if (protocolTypeStr.Equals(Proxy.ProxyServerBase.UriSchemeHttps,
StringComparison.InvariantCultureIgnoreCase))
{
protocolType = ProxyProtocolType.Https;
diff --git a/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs b/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs
index 81ea5224b..a38dc3946 100644
--- a/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs
+++ b/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs
@@ -21,10 +21,10 @@ public override string ToString()
switch (ProtocolType)
{
case ProxyProtocolType.Http:
- protocol = ProxyServer.UriSchemeHttp;
+ protocol = ProxyServerBase.UriSchemeHttp;
break;
case ProxyProtocolType.Https:
- protocol = ProxyServer.UriSchemeHttps;
+ protocol = ProxyServerBase.UriSchemeHttps;
break;
default:
throw new Exception("Unsupported protocol type");
diff --git a/src/Titanium.Web.Proxy/Http/Request.cs b/src/Titanium.Web.Proxy/Http/Request.cs
index f7d42c099..f4191e0d4 100644
--- a/src/Titanium.Web.Proxy/Http/Request.cs
+++ b/src/Titanium.Web.Proxy/Http/Request.cs
@@ -35,7 +35,7 @@ internal ByteString RequestUriString8
var scheme = getUriScheme(value);
if (scheme.Length > 0)
{
- IsHttps = scheme.Equals(ProxyServer.UriSchemeHttps8);
+ IsHttps = scheme.Equals(ProxyServerBase.UriSchemeHttps8);
}
}
}
diff --git a/src/Titanium.Web.Proxy/Http2/Http2Helper.cs b/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
index f86898cbb..b030b000e 100644
--- a/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
+++ b/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
@@ -263,7 +263,7 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output,
request.HttpVersion = HttpVersion.Version20;
request.Method = method.GetString();
- request.IsHttps = headerListener.Scheme == ProxyServer.UriSchemeHttps;
+ request.IsHttps = headerListener.Scheme == ProxyServerBase.UriSchemeHttps;
request.Authority = headerListener.Authority;
request.RequestUriString8 = path;
@@ -592,14 +592,14 @@ public string Scheme
{
get
{
- if (scheme.Equals(ProxyServer.UriSchemeHttp8))
+ if (scheme.Equals(ProxyServerBase.UriSchemeHttp8))
{
- return ProxyServer.UriSchemeHttp;
+ return ProxyServerBase.UriSchemeHttp;
}
- if (scheme.Equals(ProxyServer.UriSchemeHttps8))
+ if (scheme.Equals(ProxyServerBase.UriSchemeHttps8))
{
- return ProxyServer.UriSchemeHttps;
+ return ProxyServerBase.UriSchemeHttps;
}
return string.Empty;
diff --git a/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs b/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
index a364a9b2d..4c6a01ecf 100644
--- a/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
+++ b/src/Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
@@ -42,7 +42,7 @@ public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = tr
///
public event AsyncEventHandler? BeforeTunnelConnectResponse;
- internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer,
+ internal async Task InvokeBeforeTunnelConnectRequest(ProxyServerBase proxyServer,
TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc)
{
if (BeforeTunnelConnectRequest != null)
@@ -51,7 +51,7 @@ internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer,
}
}
- internal async Task InvokeBeforeTunnelConnectResponse(ProxyServer proxyServer,
+ internal async Task InvokeBeforeTunnelConnectResponse(ProxyServerBase proxyServer,
TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc, bool isClientHello = false)
{
if (BeforeTunnelConnectResponse != null)
diff --git a/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs b/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
index 8b0599e8e..e2572debf 100644
--- a/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
+++ b/src/Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
@@ -36,7 +36,7 @@ public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl =
///
public event AsyncEventHandler? BeforeSslAuthenticate;
- internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
+ internal async Task InvokeBeforeSslAuthenticate(ProxyServerBase proxyServer,
BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
{
if (BeforeSslAuthenticate != null)
diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs b/src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs
index 22d09da03..ef05a1ccd 100644
--- a/src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs
+++ b/src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs
@@ -15,15 +15,20 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// An object that holds TcpConnection to a particular server and port
///
internal class TcpClientConnection : IDisposable
- {
- internal TcpClientConnection(ProxyServer proxyServer, TcpClient tcpClient)
+ {
+ public static implicit operator TcpClientConnection(RequestStateBase state)
+ {
+ return state.ClientConnection;
+ }
+
+ internal TcpClientConnection(ProxyServerBase proxyServer, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
this.proxyServer = proxyServer;
this.proxyServer.UpdateClientConnectionCount(true);
}
- private ProxyServer proxyServer { get; }
+ private ProxyServerBase proxyServer { get; }
public Guid Id { get; } = Guid.NewGuid();
diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
index 6d3c4ae79..da6427cbd 100644
--- a/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+++ b/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
@@ -39,13 +39,13 @@ private readonly ConcurrentDictionary await clearOutdatedConnections());
}
- internal ProxyServer Server { get; }
+ internal ProxyServerBase Server { get; }
internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
bool isHttps, List? applicationProtocols,
@@ -106,7 +106,7 @@ internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
/// The session event arguments.
/// The application protocol.
///
- internal async Task GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase session,
+ internal async Task GetConnectionCacheKey(ProxyServerBase server, SessionEventArgsBase session,
SslApplicationProtocol applicationProtocol)
{
List? applicationProtocols = null;
@@ -145,7 +145,7 @@ internal async Task GetConnectionCacheKey(ProxyServer server, SessionEve
/// if set to true [no cache].
/// The cancellation token for this async task.
///
- internal Task GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
+ internal Task GetServerConnection(RequestStateBase state, SessionEventArgsBase session, bool isConnect,
SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken)
{
List? applicationProtocols = null;
@@ -154,7 +154,7 @@ internal Task GetServerConnection(ProxyServer server, Sessi
applicationProtocols = new List { applicationProtocol };
}
- return GetServerConnection(server, session, isConnect, applicationProtocols, noCache, cancellationToken);
+ return GetServerConnection(state, session, isConnect, applicationProtocols, noCache, cancellationToken);
}
///
@@ -167,9 +167,10 @@ internal Task GetServerConnection(ProxyServer server, Sessi
/// if set to true [no cache].
/// The cancellation token for this async task.
///
- internal async Task GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
+ internal async Task GetServerConnection(RequestStateBase state, SessionEventArgsBase session, bool isConnect,
List? applicationProtocols, bool noCache, CancellationToken cancellationToken)
{
+ var server = state.Server;
IExternalProxy? customUpStreamProxy = null;
bool isHttps = session.IsHttps;
@@ -210,7 +211,7 @@ internal async Task GetServerConnection(ProxyServer server,
port,
session.HttpClient.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
- server, session, session.HttpClient.UpStreamEndPoint ?? server.UpStreamEndPoint,
+ state, session, session.HttpClient.UpStreamEndPoint ?? server.UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy),
noCache, cancellationToken);
}
@@ -233,9 +234,11 @@ internal async Task GetServerConnection(ProxyServer server,
///
internal async Task GetServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List? applicationProtocols, bool isConnect,
- ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
+ RequestStateBase state, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
bool noCache, CancellationToken cancellationToken)
{
+ var proxyServer = state.Server;
+
var sslProtocol = session?.ClientConnection.SslProtocol ?? SslProtocols.None;
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
isHttps, applicationProtocols, upStreamEndPoint, externalProxy);
@@ -260,10 +263,10 @@ internal async Task GetServerConnection(string remoteHostNa
}
}
}
- }
-
+ }
+
var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol,
- applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
+ applicationProtocols, isConnect, state, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
return connection;
}
@@ -287,9 +290,10 @@ internal async Task GetServerConnection(string remoteHostNa
///
private async Task createServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, SslProtocols sslProtocol, List? applicationProtocols, bool isConnect,
- ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
+ RequestStateBase state, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
CancellationToken cancellationToken)
{
+ var proxyServer = state.Server;
// deny connection to proxy end points to avoid infinite connection loop.
if (Server.ProxyEndPoints.Any(x => x.Port == remotePort)
&& NetworkHelper.IsLocalIpAddress(remoteHostName))
@@ -432,7 +436,7 @@ private async Task createServerConnection(string remoteHost
{
session.CustomUpStreamProxyUsed = newUpstreamProxy;
session.TimeLine["Retrying Upstream Proxy Connection"] = DateTime.Now;
- return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
+ return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, state, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
}
}
@@ -481,8 +485,8 @@ private async Task createServerConnection(string remoteHost
if (isHttps)
{
- var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
- proxyServer.SelectClientCertificate);
+ var sslStream = new SslStream(stream, false, state.ValidateServerCertificate,
+ state.SelectClientCertificate);
stream = new HttpServerStream(sslStream, proxyServer.BufferPool);
var options = new SslClientAuthenticationOptions
diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs b/src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs
index c924ba40e..7c6319176 100644
--- a/src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs
+++ b/src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs
@@ -17,7 +17,7 @@ internal class TcpServerConnection : IDisposable
{
public Guid Id { get; } = Guid.NewGuid();
- internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient, HttpServerStream stream,
+ internal TcpServerConnection(ProxyServerBase proxyServer, TcpClient tcpClient, HttpServerStream stream,
string hostName, int port, bool isHttps, SslApplicationProtocol negotiatedApplicationProtocol,
Version version, IExternalProxy? upStreamProxy, IPEndPoint? upStreamEndPoint, string cacheKey)
{
@@ -37,7 +37,7 @@ internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient, HttpS
CacheKey = cacheKey;
}
- private ProxyServer proxyServer { get; }
+ private ProxyServerBase proxyServer { get; }
internal bool IsClosed => Stream.IsClosed;
diff --git a/src/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs b/src/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
index a70eda3fd..be8b7c26f 100644
--- a/src/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
+++ b/src/Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
@@ -10,7 +10,7 @@
namespace Titanium.Web.Proxy
{
- public partial class ProxyServer
+ public partial class ProxyServerBase
{
///
/// Callback to authorize clients of this proxy instance.
diff --git a/src/Titanium.Web.Proxy/ProxyServer.cs b/src/Titanium.Web.Proxy/ProxyServer.cs
index 64ea30829..0b8374aa5 100644
--- a/src/Titanium.Web.Proxy/ProxyServer.cs
+++ b/src/Titanium.Web.Proxy/ProxyServer.cs
@@ -18,50 +18,149 @@
using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy
-{
+{
+ public class RequestState : RequestState
+ {
+
+ }
+ public class ProxyServer : RequestState.ProxyServer
+ {
+
+ }
+ public abstract partial class RequestStateBase
+ {
+ public abstract ProxyServerBase Server { get; }
+ internal abstract TcpClientConnection ClientConnection { get; }
+
+ public virtual async Task OnErrorAsync(Exception e)
+ {
+ Server.ExceptionFunc?.Invoke(e);
+ }
+ public virtual void OnError(Exception e)
+ {
+ Server.ExceptionFunc?.Invoke(e);
+ }
+
+ }
+ public class RequestState : RequestStateBase, IDisposable
+ where TRequestState : RequestState, new()
+ {
+ ProxyServer server;
+ TcpClientConnection clientConnection;
+ public override ProxyServerBase Server => server;
+ internal override TcpClientConnection ClientConnection => clientConnection;
+
+ #region IDisposable Support
+ private bool disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ ClientConnection?.Dispose();
+ // dispose managed state (managed objects).
+ }
+
+ // free unmanaged resources (unmanaged objects) and override a finalizer below.
+ // set large fields to null.
+
+ disposedValue = true;
+ }
+ }
+
+ // override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
+ // ~RequestState()
+ // {
+ // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ // Dispose(false);
+ // }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+ #endregion
+ public class ProxyServer : ProxyServerBase
+ {
+ protected override async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
+ {
+ tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
+ tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000;
+
+ tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds);
+
+ await InvokeConnectionCreateEvent(tcpClient, true);
+ using (var requestState = new TRequestState())
+ {
+ var clientConnection = new TcpClientConnection(this, tcpClient);
+ requestState.server = this;
+ requestState.clientConnection = clientConnection;
+
+ if (endPoint is TransparentProxyEndPoint tep)
+ {
+ await handleClient(tep, requestState);
+ }
+ else
+ {
+ await handleClient((ExplicitProxyEndPoint)endPoint, requestState);
+ }
+ }
+ }
+ }
+ }
///
///
/// This class is the backbone of proxy. One can create as many instances as needed.
/// However care should be taken to avoid using the same listening ports across multiple instances.
///
- public partial class ProxyServer : IDisposable
- {
+ public abstract partial class ProxyServerBase : IDisposable
+ {
+ public static implicit operator ProxyServerBase(RequestStateBase state)
+ {
+ return state.Server;
+ }
///
/// HTTP & HTTPS scheme shorthands.
///
- internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp;
- internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps;
-
- internal static ByteString UriSchemeHttp8 = (ByteString)UriSchemeHttp;
- internal static ByteString UriSchemeHttps8 = (ByteString)UriSchemeHttps;
-
-
+ internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp;
+ internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps;
+
+ internal static ByteString UriSchemeHttp8 = (ByteString)UriSchemeHttp;
+ internal static ByteString UriSchemeHttps8 = (ByteString)UriSchemeHttps;
+
+
///
/// A default exception log func.
///
- private readonly ExceptionHandler defaultExceptionFunc = e => { };
-
+ private readonly ExceptionHandler defaultExceptionFunc = e => { };
+
///
/// Backing field for exposed public property.
///
- private int clientConnectionCount;
-
+ private int clientConnectionCount;
+
///
/// Backing field for exposed public property.
///
- private ExceptionHandler? exceptionFunc;
-
+ private ExceptionHandler? exceptionFunc;
+
///
/// Backing field for exposed public property.
///
- private int serverConnectionCount;
-
+ private int serverConnectionCount;
+
///
/// Upstream proxy manager.
///
- private WinHttpWebProxyFinder? systemProxyResolver;
-
-
+ private WinHttpWebProxyFinder? systemProxyResolver;
+
+
///
///
/// Initializes a new instance of ProxyServer class with provided parameters.
@@ -75,12 +174,12 @@ public partial class ProxyServer : IDisposable
/// Should we attempt to trust certificates with elevated permissions by
/// prompting for UAC if required?
///
- public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
- bool trustRootCertificateAsAdmin = false) : this(null, null, userTrustRootCertificate,
- machineTrustRootCertificate, trustRootCertificateAsAdmin)
- {
- }
-
+ public ProxyServerBase(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
+ bool trustRootCertificateAsAdmin = false) : this(null, null, userTrustRootCertificate,
+ machineTrustRootCertificate, trustRootCertificateAsAdmin)
+ {
+ }
+
///
/// Initializes a new instance of ProxyServer class with provided parameters.
///
@@ -95,48 +194,48 @@ public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCe
/// Should we attempt to trust certificates with elevated permissions by
/// prompting for UAC if required?
///
- public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerName,
- bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
- bool trustRootCertificateAsAdmin = false)
- {
- BufferPool = new DefaultBufferPool();
- ProxyEndPoints = new List();
- tcpConnectionFactory = new TcpConnectionFactory(this);
- if (RunTime.IsWindows && !RunTime.IsUwpOnWindows)
- {
- systemProxySettingsManager = new SystemProxyManager();
- }
-
- CertificateManager = new CertificateManager(rootCertificateName, rootCertificateIssuerName,
- userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin, ExceptionFunc);
- }
-
+ public ProxyServerBase(string? rootCertificateName, string? rootCertificateIssuerName,
+ bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
+ bool trustRootCertificateAsAdmin = false)
+ {
+ BufferPool = new DefaultBufferPool();
+ ProxyEndPoints = new List();
+ tcpConnectionFactory = new TcpConnectionFactory(this);
+ if (RunTime.IsWindows && !RunTime.IsUwpOnWindows)
+ {
+ systemProxySettingsManager = new SystemProxyManager();
+ }
+
+ CertificateManager = new CertificateManager(rootCertificateName, rootCertificateIssuerName,
+ userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin, ExceptionFunc);
+ }
+
///
/// An factory that creates tcp connection to server.
///
- private TcpConnectionFactory tcpConnectionFactory { get; }
-
+ internal TcpConnectionFactory tcpConnectionFactory { get; }
+
///
/// Manage system proxy settings.
///
- private SystemProxyManager? systemProxySettingsManager { get; }
-
+ private SystemProxyManager? systemProxySettingsManager { get; }
+
///
/// Number of exception retries when connection pool is enabled.
///
- private int retries => EnableConnectionPool ? MaxCachedConnections : 0;
-
+ private int retries => EnableConnectionPool ? MaxCachedConnections : 0;
+
///
/// Is the proxy currently running?
///
- public bool ProxyRunning { get; private set; }
-
+ public bool ProxyRunning { get; private set; }
+
///
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// Defaults to false.
///
- public bool ForwardToUpstreamGateway { get; set; }
-
+ public bool ForwardToUpstreamGateway { get; set; }
+
///
/// Enable disable Windows Authentication (NTLM/Kerberos).
/// Note: NTLM/Kerberos will always send local credentials of current user
@@ -144,35 +243,35 @@ public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerNam
/// in middle attack with Windows domain authentication is not currently supported.
/// Defaults to false.
///
- public bool EnableWinAuth { get; set; }
-
+ public bool EnableWinAuth { get; set; }
+
///
/// Enable disable HTTP/2 support.
/// Warning: HTTP/2 support is very limited
/// - only enabled when both client and server supports it (no protocol changing in proxy)
/// - cannot modify the request/response (e.g header modifications in BeforeRequest/Response events are ignored)
///
- public bool EnableHttp2 { get; set; } = false;
-
+ public bool EnableHttp2 { get; set; } = false;
+
///
/// Should we check for certificate revocation during SSL authentication to servers
/// Note: If enabled can reduce performance. Defaults to false.
///
- public X509RevocationMode CheckCertificateRevocation { get; set; }
-
+ public X509RevocationMode CheckCertificateRevocation { get; set; }
+
///
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 continue implementations on server/client may cause problems if enabled.
/// Defaults to false.
///
- public bool Enable100ContinueBehaviour { get; set; }
-
+ public bool Enable100ContinueBehaviour { get; set; }
+
///
/// Should we enable experimental server connection pool?
/// Defaults to true.
///
- public bool EnableConnectionPool { get; set; } = true;
-
+ public bool EnableConnectionPool { get; set; } = true;
+
///
/// Should we enable tcp server connection prefetching?
/// When enabled, as soon as we receive a client connection we concurrently initiate
@@ -181,731 +280,712 @@ public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerNam
/// If a server connection is available in cache then this prefetch task will immediately return with the available connection from cache.
/// Defaults to true.
///
- public bool EnableTcpServerConnectionPrefetch { get; set; } = true;
-
+ public bool EnableTcpServerConnectionPrefetch { get; set; } = true;
+
///
/// Gets or sets a Boolean value that specifies whether server and client stream Sockets are using the Nagle algorithm.
/// Defaults to true, no nagle algorithm is used.
///
- public bool NoDelay { get; set; } = true;
-
+ public bool NoDelay { get; set; } = true;
+
///
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete.
/// This will also determine the pool eviction time when connection pool is enabled.
/// Default value is 60 seconds.
///
- public int ConnectionTimeOutSeconds { get; set; } = 60;
-
+ public int ConnectionTimeOutSeconds { get; set; } = 60;
+
///
/// Seconds server connection are to wait for connection to be established.
/// Default value is 20 seconds.
///
- public int ConnectTimeOutSeconds { get; set; } = 20;
-
+ public int ConnectTimeOutSeconds { get; set; } = 20;
+
///
/// Maximum number of concurrent connections per remote host in cache.
/// Only valid when connection pooling is enabled.
/// Default value is 2.
///
- public int MaxCachedConnections { get; set; } = 2;
-
+ public int MaxCachedConnections { get; set; } = 2;
+
///
/// Number of seconds to linger when Tcp connection is in TIME_WAIT state.
/// Default value is 30.
///
- public int TcpTimeWaitSeconds { get; set; } = 30;
-
+ public int TcpTimeWaitSeconds { get; set; } = 30;
+
///
/// Should we reuse client/server tcp sockets.
/// Default is true (disabled for linux/macOS due to bug in .Net core).
///
- public bool ReuseSocket { get; set; } = true;
-
+ public bool ReuseSocket { get; set; } = true;
+
///
/// Total number of active client connections.
///
- public int ClientConnectionCount => clientConnectionCount;
-
+ public int ClientConnectionCount => clientConnectionCount;
+
///
/// Total number of active server connections.
///
- public int ServerConnectionCount => serverConnectionCount;
-
+ public int ServerConnectionCount => serverConnectionCount;
+
///
/// Realm used during Proxy Basic Authentication.
///
- public string ProxyAuthenticationRealm { get; set; } = "TitaniumProxy";
-
+ public string ProxyAuthenticationRealm { get; set; } = "TitaniumProxy";
+
///
/// List of supported Ssl versions.
///
#pragma warning disable 618
- public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
+ public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
#pragma warning restore 618
-
+
///
/// The buffer pool used throughout this proxy instance.
/// Set custom implementations by implementing this interface.
/// By default this uses DefaultBufferPool implementation available in StreamExtended library package.
/// Buffer size should be at least 10 bytes.
///
- public IBufferPool BufferPool { get; set; }
-
+ public IBufferPool BufferPool { get; set; }
+
///
/// Manages certificates used by this proxy.
///
- public CertificateManager CertificateManager { get; }
-
+ public CertificateManager CertificateManager { get; }
+
///
/// External proxy used for Http requests.
///
- public IExternalProxy? UpStreamHttpProxy { get; set; }
-
+ public IExternalProxy? UpStreamHttpProxy { get; set; }
+
///
/// External proxy used for Https requests.
///
- public IExternalProxy? UpStreamHttpsProxy { get; set; }
-
+ public IExternalProxy? UpStreamHttpsProxy { get; set; }
+
///
/// Local adapter/NIC endpoint where proxy makes request via.
/// Defaults via any IP addresses of this machine.
///
- public IPEndPoint? UpStreamEndPoint { get; set; }
-
+ public IPEndPoint? UpStreamEndPoint { get; set; }
+
///
/// A list of IpAddress and port this proxy is listening to.
///
- public List ProxyEndPoints { get; set; }
-
+ public List ProxyEndPoints { get; set; }
+
///
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests.
/// User should return the ExternalProxy object with valid credentials.
///
- public Func>? GetCustomUpStreamProxyFunc { get; set; }
-
+ public Func>? GetCustomUpStreamProxyFunc { get; set; }
+
///
/// A callback to provide a chance for an upstream proxy failure to be handled by a new upstream proxy.
/// User should return the ExternalProxy object with valid credentials or null.
///
- public Func>? CustomUpStreamProxyFailureFunc { get; set; }
-
+ public Func>? CustomUpStreamProxyFailureFunc { get; set; }
+
///
/// Callback for error events in this proxy instance.
///
- public ExceptionHandler ExceptionFunc
- {
- get => exceptionFunc ?? defaultExceptionFunc;
- set
- {
- exceptionFunc = value;
- CertificateManager.ExceptionFunc = value;
- }
- }
-
+ public ExceptionHandler ExceptionFunc
+ {
+ get => exceptionFunc ?? defaultExceptionFunc;
+ set
+ {
+ exceptionFunc = value;
+ CertificateManager.ExceptionFunc = value;
+ }
+ }
+ ///
+ /// Handle exception.
+ ///
+ /// The client stream.
+ /// The exception.
+ private void onException(HttpClientStream clientStream, Exception exception)
+ {
+ ExceptionFunc(exception);
+ }
+
+
+
///
/// A callback to authenticate proxy clients via basic authentication.
/// Parameters are username and password as provided by client.
/// Should return true for successful authentication.
///
- public Func>? ProxyBasicAuthenticateFunc { get; set; }
-
+ public Func>? ProxyBasicAuthenticateFunc { get; set; }
+
///
/// A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc.
/// Parameters are current working session, schemeType, and token as provided by a calling client.
/// Should return success for successful authentication, continuation if the package requests, or failure.
///
- public Func>? ProxySchemeAuthenticateFunc { get; set; }
-
+ public Func>? ProxySchemeAuthenticateFunc { get; set; }
+
///
/// A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required.
/// Works in relation with ProxySchemeAuthenticateFunc.
///
- public IEnumerable ProxyAuthenticationSchemes { get; set; } = new string[0];
-
+ public IEnumerable ProxyAuthenticationSchemes { get; set; } = new string[0];
+
///
/// Event occurs when client connection count changed.
///
- public event EventHandler? ClientConnectionCountChanged;
-
+ public event EventHandler? ClientConnectionCountChanged;
+
///
/// Event occurs when server connection count changed.
///
- public event EventHandler? ServerConnectionCountChanged;
-
+ public event EventHandler? ServerConnectionCountChanged;
+
///
/// Event to override the default verification logic of remote SSL certificate received during authentication.
///
- public event AsyncEventHandler? ServerCertificateValidationCallback;
-
+ public event AsyncEventHandler? ServerCertificateValidationCallback;
+
///
/// Event to override client certificate selection during mutual SSL authentication.
///
- public event AsyncEventHandler? ClientCertificateSelectionCallback;
-
+ public event AsyncEventHandler? ClientCertificateSelectionCallback;
+
///
/// Intercept request event to server.
///
- public event AsyncEventHandler? BeforeRequest;
-
+ public event AsyncEventHandler? BeforeRequest;
+
///
/// Intercept response event from server.
///
- public event AsyncEventHandler? BeforeResponse;
-
+ public event AsyncEventHandler? BeforeResponse;
+
///
/// Intercept after response event from server.
///
- public event AsyncEventHandler? AfterResponse;
-
+ public event AsyncEventHandler? AfterResponse;
+
///
/// Customize TcpClient used for client connection upon create.
///
- public event AsyncEventHandler? OnClientConnectionCreate;
-
+ public event AsyncEventHandler? OnClientConnectionCreate;
+
///
/// Customize TcpClient used for server connection upon create.
///
- public event AsyncEventHandler? OnServerConnectionCreate;
-
+ public event AsyncEventHandler? OnServerConnectionCreate;
+
///
/// Customize the minimum ThreadPool size (increase it on a server)
///
- public int ThreadPoolWorkerThread { get; set; } = Environment.ProcessorCount;
-
+ public int ThreadPoolWorkerThread { get; set; } = Environment.ProcessorCount;
+
///
/// Add a proxy end point.
///
/// The proxy endpoint.
- public void AddEndPoint(ProxyEndPoint endPoint)
- {
- if (ProxyEndPoints.Any(x =>
- x.IpAddress.Equals(endPoint.IpAddress) && endPoint.Port != 0 && x.Port == endPoint.Port))
- {
- throw new Exception("Cannot add another endpoint to same port & ip address");
- }
-
- ProxyEndPoints.Add(endPoint);
-
- if (ProxyRunning)
- {
- listen(endPoint);
- }
- }
-
+ public void AddEndPoint(ProxyEndPoint endPoint)
+ {
+ if (ProxyEndPoints.Any(x =>
+ x.IpAddress.Equals(endPoint.IpAddress) && endPoint.Port != 0 && x.Port == endPoint.Port))
+ {
+ throw new Exception("Cannot add another endpoint to same port & ip address");
+ }
+
+ ProxyEndPoints.Add(endPoint);
+
+ if (ProxyRunning)
+ {
+ listen(endPoint);
+ }
+ }
+
///
/// Remove a proxy end point.
/// Will throw error if the end point doesn't exist.
///
/// The existing endpoint to remove.
- public void RemoveEndPoint(ProxyEndPoint endPoint)
- {
- if (ProxyEndPoints.Contains(endPoint) == false)
- {
- throw new Exception("Cannot remove endPoints not added to proxy");
- }
-
- ProxyEndPoints.Remove(endPoint);
-
- if (ProxyRunning)
- {
- quitListen(endPoint);
- }
- }
-
+ public void RemoveEndPoint(ProxyEndPoint endPoint)
+ {
+ if (ProxyEndPoints.Contains(endPoint) == false)
+ {
+ throw new Exception("Cannot remove endPoints not added to proxy");
+ }
+
+ ProxyEndPoints.Remove(endPoint);
+
+ if (ProxyRunning)
+ {
+ quitListen(endPoint);
+ }
+ }
+
///
/// Set the given explicit end point as the default proxy server for current machine.
///
/// The explicit endpoint.
- public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint)
- {
- SetAsSystemProxy(endPoint, ProxyProtocolType.Http);
- }
-
+ public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint)
+ {
+ SetAsSystemProxy(endPoint, ProxyProtocolType.Http);
+ }
+
///
/// Set the given explicit end point as the default proxy server for current machine.
///
/// The explicit endpoint.
- public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint)
- {
- SetAsSystemProxy(endPoint, ProxyProtocolType.Https);
- }
-
+ public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint)
+ {
+ SetAsSystemProxy(endPoint, ProxyProtocolType.Https);
+ }
+
///
/// Set the given explicit end point as the default proxy server for current machine.
///
/// The explicit endpoint.
/// The proxy protocol type.
- public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType)
- {
- if (systemProxySettingsManager == null)
- {
+ public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType)
+ {
+ if (systemProxySettingsManager == null)
+ {
throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows.
- Please manually confugure you operating system to use this proxy's port and address.");
- }
-
- validateEndPointAsSystemProxy(endPoint);
-
- bool isHttp = (protocolType & ProxyProtocolType.Http) > 0;
- bool isHttps = (protocolType & ProxyProtocolType.Https) > 0;
-
- if (isHttps)
- {
- CertificateManager.EnsureRootCertificate();
-
- // If certificate was trusted by the machine
- if (!CertificateManager.CertValidated)
- {
- protocolType = protocolType & ~ProxyProtocolType.Https;
- isHttps = false;
- }
- }
-
- // clear any settings previously added
- if (isHttp)
- {
- ProxyEndPoints.OfType().ToList().ForEach(x => x.IsSystemHttpProxy = false);
- }
-
- if (isHttps)
- {
- ProxyEndPoints.OfType().ToList().ForEach(x => x.IsSystemHttpsProxy = false);
- }
-
- systemProxySettingsManager.SetProxy(
- Equals(endPoint.IpAddress, IPAddress.Any) |
- Equals(endPoint.IpAddress, IPAddress.Loopback)
- ? "localhost"
- : endPoint.IpAddress.ToString(),
- endPoint.Port,
- protocolType);
-
- if (isHttp)
- {
- endPoint.IsSystemHttpProxy = true;
- }
-
- if (isHttps)
- {
- endPoint.IsSystemHttpsProxy = true;
- }
-
- string? proxyType = null;
- switch (protocolType)
- {
- case ProxyProtocolType.Http:
- proxyType = "HTTP";
- break;
- case ProxyProtocolType.Https:
- proxyType = "HTTPS";
- break;
- case ProxyProtocolType.AllHttp:
- proxyType = "HTTP and HTTPS";
- break;
- }
-
- if (protocolType != ProxyProtocolType.None)
- {
- Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress,
- endPoint.Port, proxyType);
- }
- }
-
+ Please manually confugure you operating system to use this proxy's port and address.");
+ }
+
+ validateEndPointAsSystemProxy(endPoint);
+
+ bool isHttp = (protocolType & ProxyProtocolType.Http) > 0;
+ bool isHttps = (protocolType & ProxyProtocolType.Https) > 0;
+
+ if (isHttps)
+ {
+ CertificateManager.EnsureRootCertificate();
+
+ // If certificate was trusted by the machine
+ if (!CertificateManager.CertValidated)
+ {
+ protocolType = protocolType & ~ProxyProtocolType.Https;
+ isHttps = false;
+ }
+ }
+
+ // clear any settings previously added
+ if (isHttp)
+ {
+ ProxyEndPoints.OfType().ToList().ForEach(x => x.IsSystemHttpProxy = false);
+ }
+
+ if (isHttps)
+ {
+ ProxyEndPoints.OfType().ToList().ForEach(x => x.IsSystemHttpsProxy = false);
+ }
+
+ systemProxySettingsManager.SetProxy(
+ Equals(endPoint.IpAddress, IPAddress.Any) |
+ Equals(endPoint.IpAddress, IPAddress.Loopback)
+ ? "localhost"
+ : endPoint.IpAddress.ToString(),
+ endPoint.Port,
+ protocolType);
+
+ if (isHttp)
+ {
+ endPoint.IsSystemHttpProxy = true;
+ }
+
+ if (isHttps)
+ {
+ endPoint.IsSystemHttpsProxy = true;
+ }
+
+ string? proxyType = null;
+ switch (protocolType)
+ {
+ case ProxyProtocolType.Http:
+ proxyType = "HTTP";
+ break;
+ case ProxyProtocolType.Https:
+ proxyType = "HTTPS";
+ break;
+ case ProxyProtocolType.AllHttp:
+ proxyType = "HTTP and HTTPS";
+ break;
+ }
+
+ if (protocolType != ProxyProtocolType.None)
+ {
+ Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress,
+ endPoint.Port, proxyType);
+ }
+ }
+
///
/// Clear HTTP proxy settings of current machine.
///
- public void DisableSystemHttpProxy()
- {
- DisableSystemProxy(ProxyProtocolType.Http);
- }
-
+ public void DisableSystemHttpProxy()
+ {
+ DisableSystemProxy(ProxyProtocolType.Http);
+ }
+
///
/// Clear HTTPS proxy settings of current machine.
///
- public void DisableSystemHttpsProxy()
- {
- DisableSystemProxy(ProxyProtocolType.Https);
- }
-
+ public void DisableSystemHttpsProxy()
+ {
+ DisableSystemProxy(ProxyProtocolType.Https);
+ }
+
///
/// Restores the original proxy settings.
///
- public void RestoreOriginalProxySettings()
- {
- if (systemProxySettingsManager == null)
- {
+ public void RestoreOriginalProxySettings()
+ {
+ if (systemProxySettingsManager == null)
+ {
throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows.
- Please manually configure your operating system to use this proxy's port and address.");
- }
-
- systemProxySettingsManager.RestoreOriginalSettings();
- }
-
+ Please manually configure your operating system to use this proxy's port and address.");
+ }
+
+ systemProxySettingsManager.RestoreOriginalSettings();
+ }
+
///
/// Clear the specified proxy setting for current machine.
///
- public void DisableSystemProxy(ProxyProtocolType protocolType)
- {
- if (systemProxySettingsManager == null)
- {
+ public void DisableSystemProxy(ProxyProtocolType protocolType)
+ {
+ if (systemProxySettingsManager == null)
+ {
throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows.
- Please manually configure your operating system to use this proxy's port and address.");
- }
-
- systemProxySettingsManager.RemoveProxy(protocolType);
- }
-
+ Please manually configure your operating system to use this proxy's port and address.");
+ }
+
+ systemProxySettingsManager.RemoveProxy(protocolType);
+ }
+
///
/// Clear all proxy settings for current machine.
///
- public void DisableAllSystemProxies()
- {
- if (systemProxySettingsManager == null)
- {
+ public void DisableAllSystemProxies()
+ {
+ if (systemProxySettingsManager == null)
+ {
throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows.
- Please manually confugure you operating system to use this proxy's port and address.");
- }
-
- systemProxySettingsManager.DisableAllProxy();
- }
-
+ Please manually confugure you operating system to use this proxy's port and address.");
+ }
+
+ systemProxySettingsManager.DisableAllProxy();
+ }
+
///
/// Start this proxy server instance.
///
- public void Start()
- {
- if (ProxyRunning)
- {
- throw new Exception("Proxy is already running.");
- }
-
- setThreadPoolMinThread(ThreadPoolWorkerThread);
-
- if (ProxyEndPoints.OfType().Any(x => x.GenericCertificate == null))
- {
- CertificateManager.EnsureRootCertificate();
- }
-
- // clear any system proxy settings which is pointing to our own endpoint (causing a cycle)
- // due to ungracious proxy shutdown before or something else
- if (systemProxySettingsManager != null && RunTime.IsWindows && !RunTime.IsUwpOnWindows)
- {
- var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry();
- if (proxyInfo?.Proxies != null)
- {
- var protocolToRemove = ProxyProtocolType.None;
- foreach (var proxy in proxyInfo.Proxies.Values)
- {
- if (NetworkHelper.IsLocalIpAddress(proxy.HostName)
- && ProxyEndPoints.Any(x => x.Port == proxy.Port))
- {
- protocolToRemove |= proxy.ProtocolType;
- }
- }
-
- if (protocolToRemove != ProxyProtocolType.None)
- {
- systemProxySettingsManager.RemoveProxy(protocolToRemove, false);
- }
- }
- }
-
- if (ForwardToUpstreamGateway && GetCustomUpStreamProxyFunc == null && systemProxySettingsManager != null)
- {
- // Use WinHttp to handle PAC/WAPD scripts.
- systemProxyResolver = new WinHttpWebProxyFinder();
- systemProxyResolver.LoadFromIE();
-
- GetCustomUpStreamProxyFunc = getSystemUpStreamProxy;
- }
-
- ProxyRunning = true;
-
- CertificateManager.ClearIdleCertificates();
-
- foreach (var endPoint in ProxyEndPoints)
- {
- listen(endPoint);
- }
- }
-
+ public void Start()
+ {
+ if (ProxyRunning)
+ {
+ throw new Exception("Proxy is already running.");
+ }
+
+ setThreadPoolMinThread(ThreadPoolWorkerThread);
+
+ if (ProxyEndPoints.OfType().Any(x => x.GenericCertificate == null))
+ {
+ CertificateManager.EnsureRootCertificate();
+ }
+
+ // clear any system proxy settings which is pointing to our own endpoint (causing a cycle)
+ // due to ungracious proxy shutdown before or something else
+ if (systemProxySettingsManager != null && RunTime.IsWindows && !RunTime.IsUwpOnWindows)
+ {
+ var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry();
+ if (proxyInfo?.Proxies != null)
+ {
+ var protocolToRemove = ProxyProtocolType.None;
+ foreach (var proxy in proxyInfo.Proxies.Values)
+ {
+ if (NetworkHelper.IsLocalIpAddress(proxy.HostName)
+ && ProxyEndPoints.Any(x => x.Port == proxy.Port))
+ {
+ protocolToRemove |= proxy.ProtocolType;
+ }
+ }
+
+ if (protocolToRemove != ProxyProtocolType.None)
+ {
+ systemProxySettingsManager.RemoveProxy(protocolToRemove, false);
+ }
+ }
+ }
+
+ if (ForwardToUpstreamGateway && GetCustomUpStreamProxyFunc == null && systemProxySettingsManager != null)
+ {
+ // Use WinHttp to handle PAC/WAPD scripts.
+ systemProxyResolver = new WinHttpWebProxyFinder();
+ systemProxyResolver.LoadFromIE();
+
+ GetCustomUpStreamProxyFunc = getSystemUpStreamProxy;
+ }
+
+ ProxyRunning = true;
+
+ CertificateManager.ClearIdleCertificates();
+
+ foreach (var endPoint in ProxyEndPoints)
+ {
+ listen(endPoint);
+ }
+ }
+
///
/// Stop this proxy server instance.
///
- public void Stop()
- {
- if (!ProxyRunning)
- {
- throw new Exception("Proxy is not running.");
- }
-
- if (systemProxySettingsManager != null)
- {
- bool setAsSystemProxy = ProxyEndPoints.OfType()
- .Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
-
- if (setAsSystemProxy)
- {
- systemProxySettingsManager.RestoreOriginalSettings();
- }
- }
-
- foreach (var endPoint in ProxyEndPoints)
- {
- quitListen(endPoint);
- }
-
- ProxyEndPoints.Clear();
-
- CertificateManager?.StopClearIdleCertificates();
- tcpConnectionFactory.Dispose();
-
- ProxyRunning = false;
- }
-
+ public void Stop()
+ {
+ if (!ProxyRunning)
+ {
+ throw new Exception("Proxy is not running.");
+ }
+
+ if (systemProxySettingsManager != null)
+ {
+ bool setAsSystemProxy = ProxyEndPoints.OfType()
+ .Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
+
+ if (setAsSystemProxy)
+ {
+ systemProxySettingsManager.RestoreOriginalSettings();
+ }
+ }
+
+ foreach (var endPoint in ProxyEndPoints)
+ {
+ quitListen(endPoint);
+ }
+
+ ProxyEndPoints.Clear();
+
+ CertificateManager?.StopClearIdleCertificates();
+ tcpConnectionFactory.Dispose();
+
+ ProxyRunning = false;
+ }
+
///
/// Listen on given end point of local machine.
///
/// The end point to listen.
- private void listen(ProxyEndPoint endPoint)
- {
- endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
-
- if (ReuseSocket && RunTime.IsSocketReuseAvailable)
- {
- endPoint.Listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
- }
-
- try
- {
- endPoint.Listener.Start();
-
- endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port;
-
- // accept clients asynchronously
- endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
- }
- catch (SocketException ex)
- {
- var pex = new Exception(
- $"Endpoint {endPoint} failed to start. Check inner exception and exception data for details.", ex);
- pex.Data.Add("ipAddress", endPoint.IpAddress);
- pex.Data.Add("port", endPoint.Port);
- throw pex;
- }
- }
-
+ private void listen(ProxyEndPoint endPoint)
+ {
+ endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
+
+ if (ReuseSocket && RunTime.IsSocketReuseAvailable)
+ {
+ endPoint.Listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ }
+
+ try
+ {
+ endPoint.Listener.Start();
+
+ endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port;
+
+ // accept clients asynchronously
+ endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
+ }
+ catch (SocketException ex)
+ {
+ var pex = new Exception(
+ $"Endpoint {endPoint} failed to start. Check inner exception and exception data for details.", ex);
+ pex.Data.Add("ipAddress", endPoint.IpAddress);
+ pex.Data.Add("port", endPoint.Port);
+ throw pex;
+ }
+ }
+
///
/// Verify if its safe to set this end point as system proxy.
///
/// The end point to validate.
- private void validateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
- {
- if (endPoint == null)
- {
- throw new ArgumentNullException(nameof(endPoint));
- }
-
- if (ProxyEndPoints.Contains(endPoint) == false)
- {
- throw new Exception("Cannot set endPoints not added to proxy as system proxy");
- }
-
- if (!ProxyRunning)
- {
- throw new Exception("Cannot set system proxy settings before proxy has been started.");
- }
- }
-
+ private void validateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
+ {
+ if (endPoint == null)
+ {
+ throw new ArgumentNullException(nameof(endPoint));
+ }
+
+ if (ProxyEndPoints.Contains(endPoint) == false)
+ {
+ throw new Exception("Cannot set endPoints not added to proxy as system proxy");
+ }
+
+ if (!ProxyRunning)
+ {
+ throw new Exception("Cannot set system proxy settings before proxy has been started.");
+ }
+ }
+
///
/// Gets the system up stream proxy.
///
/// The session.
/// The external proxy as task result.
- private Task getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
- {
- var proxy = systemProxyResolver!.GetProxy(sessionEventArgs.HttpClient.Request.RequestUri);
- return Task.FromResult(proxy);
- }
-
+ private Task getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
+ {
+ var proxy = systemProxyResolver!.GetProxy(sessionEventArgs.HttpClient.Request.RequestUri);
+ return Task.FromResult(proxy);
+ }
+
///
/// Act when a connection is received from client.
///
- private void onAcceptConnection(IAsyncResult asyn)
- {
- var endPoint = (ProxyEndPoint)asyn.AsyncState;
-
- TcpClient? tcpClient = null;
-
- try
- {
- // based on end point type call appropriate request handlers
- tcpClient = endPoint.Listener.EndAcceptTcpClient(asyn);
- tcpClient.NoDelay = NoDelay;
- }
- catch (ObjectDisposedException)
- {
- // The listener was Stop()'d, disposing the underlying socket and
- // triggering the completion of the callback. We're already exiting,
- // so just return.
- return;
- }
- catch
- {
- // Other errors are discarded to keep proxy running
- }
-
- if (tcpClient != null)
- {
- Task.Run(async () =>
- {
- await handleClient(tcpClient, endPoint);
- });
- }
-
- // Get the listener that handles the client request.
- endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
- }
-
-
+ private void onAcceptConnection(IAsyncResult asyn)
+ {
+ var endPoint = (ProxyEndPoint)asyn.AsyncState;
+
+ TcpClient? tcpClient = null;
+
+ try
+ {
+ // based on end point type call appropriate request handlers
+ tcpClient = endPoint.Listener.EndAcceptTcpClient(asyn);
+ tcpClient.NoDelay = NoDelay;
+ }
+ catch (ObjectDisposedException)
+ {
+ // The listener was Stop()'d, disposing the underlying socket and
+ // triggering the completion of the callback. We're already exiting,
+ // so just return.
+ return;
+ }
+ catch
+ {
+ // Other errors are discarded to keep proxy running
+ }
+
+ if (tcpClient != null)
+ {
+ Task.Run(async () =>
+ {
+ await handleClient(tcpClient, endPoint);
+ });
+ }
+
+ // Get the listener that handles the client request.
+ endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
+ }
+
+
///
/// Change the ThreadPool.WorkerThread minThread
///
/// minimum Threads allocated in the ThreadPool
- private void setThreadPoolMinThread(int workerThreads)
- {
- ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
- ThreadPool.GetMaxThreads(out int maxWorkerThreads, out _);
-
- minWorkerThreads = Math.Min(maxWorkerThreads, Math.Max(workerThreads, Environment.ProcessorCount));
-
- ThreadPool.SetMinThreads(minWorkerThreads, minCompletionPortThreads);
- }
-
-
+ private void setThreadPoolMinThread(int workerThreads)
+ {
+ ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
+ ThreadPool.GetMaxThreads(out int maxWorkerThreads, out _);
+
+ minWorkerThreads = Math.Min(maxWorkerThreads, Math.Max(workerThreads, Environment.ProcessorCount));
+
+ ThreadPool.SetMinThreads(minWorkerThreads, minCompletionPortThreads);
+ }
+
+
///
/// Handle the client.
///
/// The client.
/// The proxy endpoint.
/// The task.
- private async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
- {
- tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
- tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000;
-
- tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds);
-
- await InvokeConnectionCreateEvent(tcpClient, true);
-
- using (var clientConnection = new TcpClientConnection(this, tcpClient))
- {
- if (endPoint is TransparentProxyEndPoint tep)
- {
- await handleClient(tep, clientConnection);
- }
- else
- {
- await handleClient((ExplicitProxyEndPoint)endPoint, clientConnection);
- }
- }
- }
-
- ///
- /// Handle exception.
- ///
- /// The client stream.
- /// The exception.
- private void onException(HttpClientStream clientStream, Exception exception)
- {
- ExceptionFunc(exception);
- }
-
+ protected abstract Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint);
+
///
/// Quit listening on the given end point.
///
- private void quitListen(ProxyEndPoint endPoint)
- {
- endPoint.Listener.Stop();
- endPoint.Listener.Server.Dispose();
- }
-
+ private void quitListen(ProxyEndPoint endPoint)
+ {
+ endPoint.Listener.Stop();
+ endPoint.Listener.Server.Dispose();
+ }
+
///
/// Update client connection count.
///
/// Should we increment/decrement?
- internal void UpdateClientConnectionCount(bool increment)
- {
- if (increment)
- {
- Interlocked.Increment(ref clientConnectionCount);
- }
- else
- {
- Interlocked.Decrement(ref clientConnectionCount);
- }
-
- ClientConnectionCountChanged?.Invoke(this, EventArgs.Empty);
- }
-
+ internal void UpdateClientConnectionCount(bool increment)
+ {
+ if (increment)
+ {
+ Interlocked.Increment(ref clientConnectionCount);
+ }
+ else
+ {
+ Interlocked.Decrement(ref clientConnectionCount);
+ }
+
+ ClientConnectionCountChanged?.Invoke(this, EventArgs.Empty);
+ }
+
///
/// Update server connection count.
///
/// Should we increment/decrement?
- internal void UpdateServerConnectionCount(bool increment)
- {
- if (increment)
- {
- Interlocked.Increment(ref serverConnectionCount);
- }
- else
- {
- Interlocked.Decrement(ref serverConnectionCount);
- }
-
- ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty);
- }
-
+ internal void UpdateServerConnectionCount(bool increment)
+ {
+ if (increment)
+ {
+ Interlocked.Increment(ref serverConnectionCount);
+ }
+ else
+ {
+ Interlocked.Decrement(ref serverConnectionCount);
+ }
+
+ ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty);
+ }
+
///
/// Invoke client/server tcp connection events if subscribed by API user.
///
/// The TcpClient object.
/// Is this a client connection created event? If not then we would assume that its a server connection create event.
///
- internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection)
- {
- // client connection created
- if (isClientConnection && OnClientConnectionCreate != null)
- {
- await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
- }
-
- // server connection created
- if (!isClientConnection && OnServerConnectionCreate != null)
- {
- await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
- }
- }
-
+ internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection)
+ {
+ // client connection created
+ if (isClientConnection && OnClientConnectionCreate != null)
+ {
+ await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
+ }
+
+ // server connection created
+ if (!isClientConnection && OnServerConnectionCreate != null)
+ {
+ await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
+ }
+ }
+
///
/// Connection retry policy when using connection pool.
///
- private RetryPolicy retryPolicy() where T : Exception
- {
- return new RetryPolicy(retries, tcpConnectionFactory);
- }
-
+ private RetryPolicy retryPolicy() where T : Exception
+ {
+ return new RetryPolicy(retries, tcpConnectionFactory);
+ }
+
///
/// Dispose the Proxy instance.
///
- public void Dispose()
- {
- if (ProxyRunning)
- {
- Stop();
- }
-
- CertificateManager?.Dispose();
- BufferPool?.Dispose();
- }
+ public void Dispose()
+ {
+ if (ProxyRunning)
+ {
+ Stop();
+ }
+
+ CertificateManager?.Dispose();
+ BufferPool?.Dispose();
+ }
}
}
diff --git a/src/Titanium.Web.Proxy/RequestHandler.cs b/src/Titanium.Web.Proxy/RequestHandler.cs
index be93160fb..7e6412604 100644
--- a/src/Titanium.Web.Proxy/RequestHandler.cs
+++ b/src/Titanium.Web.Proxy/RequestHandler.cs
@@ -21,7 +21,7 @@ namespace Titanium.Web.Proxy
///
/// Handle the request
///
- public partial class ProxyServer
+ public partial class ProxyServerBase
{
///
/// This is the core request handler method for a particular connection from client.
@@ -34,10 +34,11 @@ public partial class ProxyServer
/// The cancellation token source for this async task.
/// The Connect request if this is a HTTPS request from explicit endpoint.
/// Prefetched server connection for current client using Connect/SNI headers.
- private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
+ private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, RequestStateBase state,
HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
Task? prefetchConnectionTask = null)
{
+ var clientConnection = state.ClientConnection;
var connectRequest = connectArgs?.HttpClient.ConnectRequest;
var prefetchTask = prefetchConnectionTask;
@@ -64,7 +65,7 @@ private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientCon
return;
}
- var args = new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectRequest, cancellationTokenSource)
+ var args = new SessionEventArgs(state, endPoint, clientConnection, clientStream, connectRequest, cancellationTokenSource)
{
UserData = connectArgs?.UserData
};
@@ -266,7 +267,7 @@ private async Task handleHttpSessionRequest(SessionEventArgs args,
// a connection generator task with captured parameters via closure.
Func> generator = () =>
- tcpConnectionFactory.GetServerConnection(this,
+ tcpConnectionFactory.GetServerConnection(args.State,
args,
false,
sslApplicationProtocol,
diff --git a/src/Titanium.Web.Proxy/ResponseHandler.cs b/src/Titanium.Web.Proxy/ResponseHandler.cs
index 782ec0b0b..8ec4ab455 100644
--- a/src/Titanium.Web.Proxy/ResponseHandler.cs
+++ b/src/Titanium.Web.Proxy/ResponseHandler.cs
@@ -11,14 +11,14 @@ namespace Titanium.Web.Proxy
///
/// Handle the response from server.
///
- public partial class ProxyServer
+ public partial class ProxyServerBase
{
///
/// Called asynchronously when a request was successful and we received the response.
///
/// The session event arguments.
/// The task.
- private async Task handleHttpSessionResponse(SessionEventArgs args)
+ protected async Task handleHttpSessionResponse(SessionEventArgs args)
{
var cancellationToken = args.CancellationTokenSource.Token;
@@ -132,7 +132,7 @@ await serverStream.CopyBodyAsync(response, false, clientStream, TransformationMo
///
///
///
- private async Task onBeforeResponse(SessionEventArgs args)
+ protected async Task onBeforeResponse(SessionEventArgs args)
{
if (BeforeResponse != null)
{
@@ -145,7 +145,7 @@ private async Task onBeforeResponse(SessionEventArgs args)
///
///
///
- private async Task onAfterResponse(SessionEventArgs args)
+ protected async Task onAfterResponse(SessionEventArgs args)
{
if (AfterResponse != null)
{
diff --git a/src/Titanium.Web.Proxy/TransparentClientHandler.cs b/src/Titanium.Web.Proxy/TransparentClientHandler.cs
index 02da48df6..c82523a62 100644
--- a/src/Titanium.Web.Proxy/TransparentClientHandler.cs
+++ b/src/Titanium.Web.Proxy/TransparentClientHandler.cs
@@ -19,8 +19,8 @@
namespace Titanium.Web.Proxy
{
- public partial class ProxyServer
- {
+ public partial class ProxyServerBase
+ {
///
/// 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
@@ -28,8 +28,9 @@ public partial class ProxyServer
/// The transparent endpoint.
/// The client connection.
///
- private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
+ internal virtual async Task handleClient(TransparentProxyEndPoint endPoint, RequestStateBase state)
{
+ var clientConnection = state.ClientConnection;
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
@@ -47,7 +48,7 @@ private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConn
{
httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;
- var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource, httpsHostName);
+ var args = new BeforeSslAuthenticateEventArgs(state,cancellationTokenSource, httpsHostName);
await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);
@@ -80,18 +81,18 @@ private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConn
catch (Exception e)
{
var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
- var session = new SessionEventArgs(this, endPoint, clientConnection, clientStream, null,
+ var session = new SessionEventArgs(state, endPoint, clientConnection, clientStream, null,
cancellationTokenSource);
throw new ProxyConnectException(
$"Couldn't authenticate host '{httpsHostName}' with certificate '{certName}'.", e, session);
- }
-
+ }
+
}
else
{
var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
HttpHeader.VersionUnknown, false, null,
- true, this, null, UpStreamEndPoint,
+ true, state, null, UpStreamEndPoint,
UpStreamHttpsProxy, true, cancellationToken);
try
@@ -131,7 +132,7 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
// HTTPS server created - we can now decrypt the client's traffic
// Now create the request
- await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource);
+ await handleHttpSessionRequest(endPoint, state, clientStream, cancellationTokenSource);
}
catch (ProxyException e)
{
diff --git a/src/Titanium.Web.Proxy/WebSocketHandler.cs b/src/Titanium.Web.Proxy/WebSocketHandler.cs
index e0c7df4bf..87a02316e 100644
--- a/src/Titanium.Web.Proxy/WebSocketHandler.cs
+++ b/src/Titanium.Web.Proxy/WebSocketHandler.cs
@@ -10,7 +10,7 @@
namespace Titanium.Web.Proxy
{
- public partial class ProxyServer
+ public partial class ProxyServerBase
{
///
diff --git a/src/Titanium.Web.Proxy/WinAuthHandler.cs b/src/Titanium.Web.Proxy/WinAuthHandler.cs
index c72d333e1..3671cf7fe 100644
--- a/src/Titanium.Web.Proxy/WinAuthHandler.cs
+++ b/src/Titanium.Web.Proxy/WinAuthHandler.cs
@@ -11,7 +11,7 @@
namespace Titanium.Web.Proxy
{
- public partial class ProxyServer
+ public partial class ProxyServerBase
{
///
/// possible header names.
From 8dbb49966838998a2d4fd05410e3c27f81fa5524 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D1=8C=D0=BA=D0=BE=20=D0=90?=
=?UTF-8?q?=20=D0=92?=
Date: Sun, 1 Dec 2019 09:40:12 +0300
Subject: [PATCH 8/9] fix merge error
---
src/Titanium.Web.Proxy/ExplicitClientHandler.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
index f22946262..afcee9491 100644
--- a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
+++ b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs
@@ -151,7 +151,7 @@ internal async Task handleClient(ExplicitProxyEndPoint endPoint, RequestStateBas
try
{
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
- var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
+ var connection = await tcpConnectionFactory.GetServerConnection(state, connectArgs,
true, SslExtensions.Http2ProtocolAsList,
true, cancellationToken);
From b13b1ca84c41cb5a6a852441f707ccd464ca16dc Mon Sep 17 00:00:00 2001
From: volanavlad
Date: Sun, 1 Dec 2019 10:17:47 +0300
Subject: [PATCH 9/9] WIP dotnetcore.yml
work in progress
---
.github/workflows/dotnetcore.yml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 .github/workflows/dotnetcore.yml
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
new file mode 100644
index 000000000..0f9d2a677
--- /dev/null
+++ b/.github/workflows/dotnetcore.yml
@@ -0,0 +1,17 @@
+name: .NET Core
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 2.2.108
+ - name: Build with dotnet
+ run: dotnet build --configuration Release