Commit 5c618dee authored by Honfika's avatar Honfika

Add client user data, which is unique for each client connection (but not for...

Add client user data, which is unique for each client connection (but not for each request) Similar to PR #690
parent 46c59104
using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Examples.Basic
{
public static class ProxyEventArgsBaseExtensions
{
public static SampleClientState GetState(this ProxyEventArgsBase args)
{
if (args.ClientUserData == null)
{
args.ClientUserData = new SampleClientState();
}
return (SampleClientState)args.ClientUserData;
}
}
}
...@@ -4,7 +4,7 @@ using System.Collections.Generic; ...@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Text; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -64,6 +64,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -64,6 +64,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
proxyServer.BeforeRequest += onRequest; proxyServer.BeforeRequest += onRequest;
proxyServer.BeforeResponse += onResponse; proxyServer.BeforeResponse += onResponse;
proxyServer.AfterResponse += onAfterResponse;
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation; proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
...@@ -128,6 +129,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -128,6 +129,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg) private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg)
{ {
arg.GetState().PipelineInfo.AppendLine(nameof(onGetCustomUpStreamProxyFunc));
// this is just to show the functionality, provided values are junk // this is just to show the functionality, provided values are junk
return new ExternalProxy return new ExternalProxy
{ {
...@@ -138,6 +141,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -138,6 +141,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task<IExternalProxy> onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg) private async Task<IExternalProxy> onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg)
{ {
arg.GetState().PipelineInfo.AppendLine(nameof(onCustomUpStreamProxyFailureFunc));
// this is just to show the functionality, provided values are junk // this is just to show the functionality, provided values are junk
return new ExternalProxy return new ExternalProxy
{ {
...@@ -149,6 +154,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -149,6 +154,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{ {
string hostname = e.HttpClient.Request.RequestUri.Host; string hostname = e.HttpClient.Request.RequestUri.Host;
e.GetState().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectRequest) + ":" + hostname);
await writeToConsole("Tunnel to: " + hostname); await writeToConsole("Tunnel to: " + hostname);
if (hostname.Contains("dropbox.com")) if (hostname.Contains("dropbox.com"))
...@@ -194,12 +200,16 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -194,12 +200,16 @@ namespace Titanium.Web.Proxy.Examples.Basic
private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e) private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
{ {
e.GetState().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectResponse) + ":" + e.HttpClient.Request.RequestUri);
return Task.FromResult(false); return Task.FromResult(false);
} }
// intercept & cancel redirect or update requests // intercept & cancel redirect or update requests
private async Task onRequest(object sender, SessionEventArgs e) private async Task onRequest(object sender, SessionEventArgs e)
{ {
e.GetState().PipelineInfo.AppendLine(nameof(onRequest) + ":" + e.HttpClient.Request.RequestUri);
await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount); await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
await writeToConsole(e.HttpClient.Request.Url); await writeToConsole(e.HttpClient.Request.Url);
...@@ -241,6 +251,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -241,6 +251,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
// Modify response // Modify response
private async Task multipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e) private async Task multipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
{ {
e.GetState().PipelineInfo.AppendLine(nameof(multipartRequestPartSent));
var session = (SessionEventArgs)sender; var session = (SessionEventArgs)sender;
await writeToConsole("Multipart form data headers:"); await writeToConsole("Multipart form data headers:");
foreach (var header in e.Headers) foreach (var header in e.Headers)
...@@ -251,6 +263,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -251,6 +263,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task onResponse(object sender, SessionEventArgs e) private async Task onResponse(object sender, SessionEventArgs e)
{ {
e.GetState().PipelineInfo.AppendLine(nameof(onResponse));
if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Websocket) if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Websocket)
{ {
e.DataSent += WebSocket_DataSent; e.DataSent += WebSocket_DataSent;
...@@ -301,6 +315,11 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -301,6 +315,11 @@ namespace Titanium.Web.Proxy.Examples.Basic
//} //}
} }
private async Task onAfterResponse(object sender, SessionEventArgs e)
{
await writeToConsole($"Pipelineinfo: {e.GetState().PipelineInfo}", ConsoleColor.Yellow);
}
/// <summary> /// <summary>
/// Allows overriding default certificate validation logic /// Allows overriding default certificate validation logic
/// </summary> /// </summary>
...@@ -308,6 +327,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -308,6 +327,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
/// <param name="e"></param> /// <param name="e"></param>
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{ {
e.GetState().PipelineInfo.AppendLine(nameof(OnCertificateValidation));
// set IsValid to true/false based on Certificate Errors // set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == SslPolicyErrors.None) if (e.SslPolicyErrors == SslPolicyErrors.None)
{ {
...@@ -324,6 +345,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -324,6 +345,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
/// <param name="e"></param> /// <param name="e"></param>
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e) public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
{ {
e.GetState().PipelineInfo.AppendLine(nameof(OnCertificateSelection));
// set e.clientCertificate to override // set e.clientCertificate to override
return Task.FromResult(0); return Task.FromResult(0);
...@@ -360,3 +383,4 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -360,3 +383,4 @@ namespace Titanium.Web.Proxy.Examples.Basic
//} //}
} }
} }
using System;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Examples.Basic
{
public class SampleClientState
{
public StringBuilder PipelineInfo { get; } = new StringBuilder();
}
}
using System; using System;
using System.Threading; using System.Threading;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
/// <summary> /// <summary>
/// This is used in transparent endpoint before authenticating client. /// This is used in transparent endpoint before authenticating client.
/// </summary> /// </summary>
public class BeforeSslAuthenticateEventArgs : EventArgs public class BeforeSslAuthenticateEventArgs : ProxyEventArgsBase
{ {
internal readonly CancellationTokenSource TaskCancellationSource; internal readonly CancellationTokenSource TaskCancellationSource;
internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource, string sniHostName) internal BeforeSslAuthenticateEventArgs(TcpClientConnection clientConnection, CancellationTokenSource taskCancellationSource, string sniHostName) : base(clientConnection)
{ {
TaskCancellationSource = taskCancellationSource; TaskCancellationSource = taskCancellationSource;
SniHostName = sniHostName; SniHostName = sniHostName;
......
...@@ -6,10 +6,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -6,10 +6,10 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// An argument passed on to user for client certificate selection during mutual SSL authentication. /// An argument passed on to user for client certificate selection during mutual SSL authentication.
/// </summary> /// </summary>
public class CertificateSelectionEventArgs : EventArgs public class CertificateSelectionEventArgs : ProxyEventArgsBase
{ {
public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost, public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost,
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) : base(session.ClientConnection)
{ {
Session = session; Session = session;
TargetHost = targetHost; TargetHost = targetHost;
......
...@@ -8,9 +8,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -8,9 +8,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// An argument passed on to the user for validating the server certificate /// An argument passed on to the user for validating the server certificate
/// during SSL authentication. /// during SSL authentication.
/// </summary> /// </summary>
public class CertificateValidationEventArgs : EventArgs public class CertificateValidationEventArgs : ProxyEventArgsBase
{ {
public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) : base(session.ClientConnection)
{ {
Session = session; Session = session;
Certificate = certificate; Certificate = certificate;
......
using System; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
/// <summary> /// <summary>
/// Class that wraps the multipart sent request arguments. /// Class that wraps the multipart sent request arguments.
/// </summary> /// </summary>
public class MultipartRequestPartSentEventArgs : EventArgs public class MultipartRequestPartSentEventArgs : ProxyEventArgsBase
{ {
internal MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers) internal MultipartRequestPartSentEventArgs(SessionEventArgs session, string boundary, HeaderCollection headers) : base(session.ClientConnection)
{ {
Session = session;
Boundary = boundary; Boundary = boundary;
Headers = headers; Headers = headers;
} }
/// <value>
/// The session arguments.
/// </value>
public SessionEventArgs Session { get; }
/// <summary> /// <summary>
/// Boundary. /// Boundary.
/// </summary> /// </summary>
......
using System;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// The base event arguments
/// </summary>
/// <seealso cref="System.EventArgs" />
public abstract class ProxyEventArgsBase : EventArgs
{
private readonly TcpClientConnection clientConnection;
public object ClientUserData
{
get => clientConnection.ClientUserData;
set => clientConnection.ClientUserData = value;
}
internal ProxyEventArgsBase(TcpClientConnection clientConnection)
{
this.clientConnection = clientConnection;
}
}
}
...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
private bool reRequest; private bool reRequest;
private WebSocketDecoder? webSocketDecoder; private WebSocketDecoder webSocketDecoder;
/// <summary> /// <summary>
/// Is this session a HTTP/2 promise? /// Is this session a HTTP/2 promise?
...@@ -127,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -127,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
try try
{ {
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary.ToString(), headers)); MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(this, boundary.ToString(), headers));
} }
catch (Exception ex) catch (Exception ex)
{ {
......
...@@ -18,7 +18,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -18,7 +18,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// A proxy session ends when client terminates connection to proxy /// A proxy session ends when client terminates connection to proxy
/// or when server terminates connection from proxy. /// or when server terminates connection from proxy.
/// </summary> /// </summary>
public abstract class SessionEventArgsBase : EventArgs, IDisposable public abstract class SessionEventArgsBase : ProxyEventArgsBase, IDisposable
{ {
private static bool isWindowsAuthenticationSupported => RunTime.IsWindows; private static bool isWindowsAuthenticationSupported => RunTime.IsWindows;
...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class. /// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary> /// </summary>
private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint, private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource) HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource) : base(clientStream.Connection)
{ {
BufferPool = server.BufferPool; BufferPool = server.BufferPool;
ExceptionFunc = server.ExceptionFunc; ExceptionFunc = server.ExceptionFunc;
......
...@@ -16,6 +16,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -16,6 +16,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal class TcpClientConnection : IDisposable internal class TcpClientConnection : IDisposable
{ {
public object ClientUserData { get; set; }
internal TcpClientConnection(ProxyServer proxyServer, TcpClient tcpClient) internal TcpClientConnection(ProxyServer proxyServer, TcpClient tcpClient)
{ {
this.tcpClient = tcpClient; this.tcpClient = tcpClient;
......
...@@ -445,7 +445,7 @@ retry: ...@@ -445,7 +445,7 @@ retry:
sessionArgs.TimeLine["Connection Established"] = DateTime.Now; sessionArgs.TimeLine["Connection Established"] = DateTime.Now;
} }
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false); await proxyServer.InvokeServerConnectionCreateEvent(tcpClient);
stream = new HttpServerStream(tcpClient.GetStream(), proxyServer.BufferPool); stream = new HttpServerStream(tcpClient.GetStream(), proxyServer.BufferPool);
......
...@@ -795,7 +795,7 @@ namespace Titanium.Web.Proxy ...@@ -795,7 +795,7 @@ namespace Titanium.Web.Proxy
tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds); tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds);
await InvokeConnectionCreateEvent(tcpClient, true); await InvokeClientConnectionCreateEvent(tcpClient);
using (var clientConnection = new TcpClientConnection(this, tcpClient)) using (var clientConnection = new TcpClientConnection(this, tcpClient))
{ {
...@@ -866,21 +866,28 @@ namespace Titanium.Web.Proxy ...@@ -866,21 +866,28 @@ namespace Titanium.Web.Proxy
} }
/// <summary> /// <summary>
/// Invoke client/server tcp connection events if subscribed by API user. /// Invoke client tcp connection events if subscribed by API user.
/// </summary> /// </summary>
/// <param name="client">The TcpClient object.</param> /// <param name="client">The TcpClient object.</param>
/// <param name="isClientConnection">Is this a client connection created event? If not then we would assume that its a server connection create event.</param>
/// <returns></returns> /// <returns></returns>
internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection) internal async Task InvokeClientConnectionCreateEvent(TcpClient client)
{ {
// client connection created // client connection created
if (isClientConnection && OnClientConnectionCreate != null) if (OnClientConnectionCreate != null)
{ {
await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc); await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
} }
}
/// <summary>
/// Invoke server tcp connection events if subscribed by API user.
/// </summary>
/// <param name="client">The TcpClient object.</param>
/// <returns></returns>
internal async Task InvokeServerConnectionCreateEvent(TcpClient client)
{
// server connection created // server connection created
if (!isClientConnection && OnServerConnectionCreate != null) if (OnServerConnectionCreate != null)
{ {
await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc); await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
} }
......
...@@ -41,13 +41,11 @@ namespace Titanium.Web.Proxy ...@@ -41,13 +41,11 @@ namespace Titanium.Web.Proxy
{ {
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken); var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
string? httpsHostName = null;
if (clientHelloInfo != null) if (clientHelloInfo != null)
{ {
httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName; var httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;
var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource, httpsHostName); var args = new BeforeSslAuthenticateEventArgs(clientConnection, cancellationTokenSource, httpsHostName);
await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc); await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment