Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit 5c618de

Browse files
committed
Add client user data, which is unique for each client connection (but not for each request) Similar to PR #690
1 parent 46c5910 commit 5c618de

14 files changed

+116
-26
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Titanium.Web.Proxy.EventArguments;
2+
3+
namespace Titanium.Web.Proxy.Examples.Basic
4+
{
5+
public static class ProxyEventArgsBaseExtensions
6+
{
7+
public static SampleClientState GetState(this ProxyEventArgsBase args)
8+
{
9+
if (args.ClientUserData == null)
10+
{
11+
args.ClientUserData = new SampleClientState();
12+
}
13+
14+
return (SampleClientState)args.ClientUserData;
15+
}
16+
}
17+
}

examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.Linq;
55
using System.Net;
66
using System.Net.Security;
7-
using System.Text;
7+
using System.Security.Cryptography.X509Certificates;
88
using System.Threading;
99
using System.Threading.Tasks;
1010
using Titanium.Web.Proxy.EventArguments;
@@ -64,6 +64,7 @@ public void StartProxy()
6464
{
6565
proxyServer.BeforeRequest += onRequest;
6666
proxyServer.BeforeResponse += onResponse;
67+
proxyServer.AfterResponse += onAfterResponse;
6768

6869
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
6970
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
@@ -128,6 +129,8 @@ public void Stop()
128129

129130
private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg)
130131
{
132+
arg.GetState().PipelineInfo.AppendLine(nameof(onGetCustomUpStreamProxyFunc));
133+
131134
// this is just to show the functionality, provided values are junk
132135
return new ExternalProxy
133136
{
@@ -138,6 +141,8 @@ private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgs
138141

139142
private async Task<IExternalProxy> onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg)
140143
{
144+
arg.GetState().PipelineInfo.AppendLine(nameof(onCustomUpStreamProxyFailureFunc));
145+
141146
// this is just to show the functionality, provided values are junk
142147
return new ExternalProxy
143148
{
@@ -149,6 +154,7 @@ private async Task<IExternalProxy> onCustomUpStreamProxyFailureFunc(SessionEvent
149154
private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
150155
{
151156
string hostname = e.HttpClient.Request.RequestUri.Host;
157+
e.GetState().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectRequest) + ":" + hostname);
152158
await writeToConsole("Tunnel to: " + hostname);
153159

154160
if (hostname.Contains("dropbox.com"))
@@ -194,12 +200,16 @@ private void WebSocketDataSentReceived(SessionEventArgs args, DataEventArgs e, b
194200

195201
private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
196202
{
203+
e.GetState().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectResponse) + ":" + e.HttpClient.Request.RequestUri);
204+
197205
return Task.FromResult(false);
198206
}
199207

200208
// intercept & cancel redirect or update requests
201209
private async Task onRequest(object sender, SessionEventArgs e)
202210
{
211+
e.GetState().PipelineInfo.AppendLine(nameof(onRequest) + ":" + e.HttpClient.Request.RequestUri);
212+
203213
await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
204214
await writeToConsole(e.HttpClient.Request.Url);
205215

@@ -241,6 +251,8 @@ private async Task onRequest(object sender, SessionEventArgs e)
241251
// Modify response
242252
private async Task multipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
243253
{
254+
e.GetState().PipelineInfo.AppendLine(nameof(multipartRequestPartSent));
255+
244256
var session = (SessionEventArgs)sender;
245257
await writeToConsole("Multipart form data headers:");
246258
foreach (var header in e.Headers)
@@ -251,6 +263,8 @@ private async Task multipartRequestPartSent(object sender, MultipartRequestPartS
251263

252264
private async Task onResponse(object sender, SessionEventArgs e)
253265
{
266+
e.GetState().PipelineInfo.AppendLine(nameof(onResponse));
267+
254268
if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Websocket)
255269
{
256270
e.DataSent += WebSocket_DataSent;
@@ -301,13 +315,20 @@ private async Task onResponse(object sender, SessionEventArgs e)
301315
//}
302316
}
303317

318+
private async Task onAfterResponse(object sender, SessionEventArgs e)
319+
{
320+
await writeToConsole($"Pipelineinfo: {e.GetState().PipelineInfo}", ConsoleColor.Yellow);
321+
}
322+
304323
/// <summary>
305324
/// Allows overriding default certificate validation logic
306325
/// </summary>
307326
/// <param name="sender"></param>
308327
/// <param name="e"></param>
309328
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
310329
{
330+
e.GetState().PipelineInfo.AppendLine(nameof(OnCertificateValidation));
331+
311332
// set IsValid to true/false based on Certificate Errors
312333
if (e.SslPolicyErrors == SslPolicyErrors.None)
313334
{
@@ -324,6 +345,8 @@ public Task OnCertificateValidation(object sender, CertificateValidationEventArg
324345
/// <param name="e"></param>
325346
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
326347
{
348+
e.GetState().PipelineInfo.AppendLine(nameof(OnCertificateSelection));
349+
327350
// set e.clientCertificate to override
328351

329352
return Task.FromResult(0);
@@ -360,3 +383,4 @@ private async Task writeToConsole(string message, ConsoleColor? consoleColor = n
360383
//}
361384
}
362385
}
386+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
using System.Text;
3+
using System.Threading.Tasks;
4+
5+
namespace Titanium.Web.Proxy.Examples.Basic
6+
{
7+
public class SampleClientState
8+
{
9+
public StringBuilder PipelineInfo { get; } = new StringBuilder();
10+
}
11+
}

src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
using System;
22
using System.Threading;
3+
using Titanium.Web.Proxy.Network.Tcp;
34

45
namespace Titanium.Web.Proxy.EventArguments
56
{
67
/// <summary>
78
/// This is used in transparent endpoint before authenticating client.
89
/// </summary>
9-
public class BeforeSslAuthenticateEventArgs : EventArgs
10+
public class BeforeSslAuthenticateEventArgs : ProxyEventArgsBase
1011
{
1112
internal readonly CancellationTokenSource TaskCancellationSource;
1213

13-
internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource, string sniHostName)
14+
internal BeforeSslAuthenticateEventArgs(TcpClientConnection clientConnection, CancellationTokenSource taskCancellationSource, string sniHostName) : base(clientConnection)
1415
{
1516
TaskCancellationSource = taskCancellationSource;
1617
SniHostName = sniHostName;

src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ namespace Titanium.Web.Proxy.EventArguments
66
/// <summary>
77
/// An argument passed on to user for client certificate selection during mutual SSL authentication.
88
/// </summary>
9-
public class CertificateSelectionEventArgs : EventArgs
9+
public class CertificateSelectionEventArgs : ProxyEventArgsBase
1010
{
1111
public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost,
12-
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
12+
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) : base(session.ClientConnection)
1313
{
1414
Session = session;
1515
TargetHost = targetHost;

src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ namespace Titanium.Web.Proxy.EventArguments
88
/// An argument passed on to the user for validating the server certificate
99
/// during SSL authentication.
1010
/// </summary>
11-
public class CertificateValidationEventArgs : EventArgs
11+
public class CertificateValidationEventArgs : ProxyEventArgsBase
1212
{
13-
public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
13+
public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) : base(session.ClientConnection)
1414
{
1515
Session = session;
1616
Certificate = certificate;

src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
using System;
2-
using Titanium.Web.Proxy.Http;
1+
using Titanium.Web.Proxy.Http;
32

43
namespace Titanium.Web.Proxy.EventArguments
54
{
65
/// <summary>
76
/// Class that wraps the multipart sent request arguments.
87
/// </summary>
9-
public class MultipartRequestPartSentEventArgs : EventArgs
8+
public class MultipartRequestPartSentEventArgs : ProxyEventArgsBase
109
{
11-
internal MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
10+
internal MultipartRequestPartSentEventArgs(SessionEventArgs session, string boundary, HeaderCollection headers) : base(session.ClientConnection)
1211
{
12+
Session = session;
1313
Boundary = boundary;
1414
Headers = headers;
1515
}
1616

17+
/// <value>
18+
/// The session arguments.
19+
/// </value>
20+
public SessionEventArgs Session { get; }
21+
1722
/// <summary>
1823
/// Boundary.
1924
/// </summary>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using Titanium.Web.Proxy.Network.Tcp;
3+
4+
namespace Titanium.Web.Proxy.EventArguments
5+
{
6+
/// <summary>
7+
/// The base event arguments
8+
/// </summary>
9+
/// <seealso cref="System.EventArgs" />
10+
public abstract class ProxyEventArgsBase : EventArgs
11+
{
12+
private readonly TcpClientConnection clientConnection;
13+
14+
public object ClientUserData
15+
{
16+
get => clientConnection.ClientUserData;
17+
set => clientConnection.ClientUserData = value;
18+
}
19+
20+
internal ProxyEventArgsBase(TcpClientConnection clientConnection)
21+
{
22+
this.clientConnection = clientConnection;
23+
}
24+
}
25+
}

src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class SessionEventArgs : SessionEventArgsBase
2626
/// </summary>
2727
private bool reRequest;
2828

29-
private WebSocketDecoder? webSocketDecoder;
29+
private WebSocketDecoder webSocketDecoder;
3030

3131
/// <summary>
3232
/// Is this session a HTTP/2 promise?
@@ -127,7 +127,7 @@ internal void OnMultipartRequestPartSent(ReadOnlySpan<char> boundary, HeaderColl
127127
{
128128
try
129129
{
130-
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary.ToString(), headers));
130+
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(this, boundary.ToString(), headers));
131131
}
132132
catch (Exception ex)
133133
{

src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Titanium.Web.Proxy.EventArguments
1818
/// A proxy session ends when client terminates connection to proxy
1919
/// or when server terminates connection from proxy.
2020
/// </summary>
21-
public abstract class SessionEventArgsBase : EventArgs, IDisposable
21+
public abstract class SessionEventArgsBase : ProxyEventArgsBase, IDisposable
2222
{
2323
private static bool isWindowsAuthenticationSupported => RunTime.IsWindows;
2424

@@ -50,7 +50,7 @@ public abstract class SessionEventArgsBase : EventArgs, IDisposable
5050
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
5151
/// </summary>
5252
private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
53-
HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
53+
HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource) : base(clientStream.Connection)
5454
{
5555
BufferPool = server.BufferPool;
5656
ExceptionFunc = server.ExceptionFunc;

src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
1616
/// </summary>
1717
internal class TcpClientConnection : IDisposable
1818
{
19+
public object ClientUserData { get; set; }
20+
1921
internal TcpClientConnection(ProxyServer proxyServer, TcpClient tcpClient)
2022
{
2123
this.tcpClient = tcpClient;

src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ private async Task<TcpServerConnection> createServerConnection(string remoteHost
445445
sessionArgs.TimeLine["Connection Established"] = DateTime.Now;
446446
}
447447

448-
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
448+
await proxyServer.InvokeServerConnectionCreateEvent(tcpClient);
449449

450450
stream = new HttpServerStream(tcpClient.GetStream(), proxyServer.BufferPool);
451451

src/Titanium.Web.Proxy/ProxyServer.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ private async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
795795

796796
tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds);
797797

798-
await InvokeConnectionCreateEvent(tcpClient, true);
798+
await InvokeClientConnectionCreateEvent(tcpClient);
799799

800800
using (var clientConnection = new TcpClientConnection(this, tcpClient))
801801
{
@@ -866,21 +866,28 @@ internal void UpdateServerConnectionCount(bool increment)
866866
}
867867

868868
/// <summary>
869-
/// Invoke client/server tcp connection events if subscribed by API user.
869+
/// Invoke client tcp connection events if subscribed by API user.
870870
/// </summary>
871871
/// <param name="client">The TcpClient object.</param>
872-
/// <param name="isClientConnection">Is this a client connection created event? If not then we would assume that its a server connection create event.</param>
873872
/// <returns></returns>
874-
internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection)
873+
internal async Task InvokeClientConnectionCreateEvent(TcpClient client)
875874
{
876875
// client connection created
877-
if (isClientConnection && OnClientConnectionCreate != null)
876+
if (OnClientConnectionCreate != null)
878877
{
879878
await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
880879
}
880+
}
881881

882+
/// <summary>
883+
/// Invoke server tcp connection events if subscribed by API user.
884+
/// </summary>
885+
/// <param name="client">The TcpClient object.</param>
886+
/// <returns></returns>
887+
internal async Task InvokeServerConnectionCreateEvent(TcpClient client)
888+
{
882889
// server connection created
883-
if (!isClientConnection && OnServerConnectionCreate != null)
890+
if (OnServerConnectionCreate != null)
884891
{
885892
await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
886893
}

src/Titanium.Web.Proxy/TransparentClientHandler.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,11 @@ private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConn
4141
{
4242
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
4343

44-
string? httpsHostName = null;
45-
4644
if (clientHelloInfo != null)
4745
{
48-
httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;
46+
var httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;
4947

50-
var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource, httpsHostName);
48+
var args = new BeforeSslAuthenticateEventArgs(clientConnection, cancellationTokenSource, httpsHostName);
5149

5250
await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);
5351

0 commit comments

Comments
 (0)