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

Commit 2560b80

Browse files
Merge pull request #449 from justcoding121/master
Beta
2 parents 8ce2dc2 + 421e259 commit 2560b80

File tree

52 files changed

+784
-467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+784
-467
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,9 @@ private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSess
149149
}
150150
}
151151

152-
private async Task OnBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
152+
private Task OnBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
153153
{
154+
return Task.FromResult(false);
154155
}
155156

156157
// intecept & cancel redirect or update requests

Titanium.Web.Proxy/ExplicitClientHandler.cs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response,
140140
{
141141
// test server HTTP/2 support
142142
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
143-
var connection = await getServerConnection(connectArgs, true,
144-
SslExtensions.Http2ProtocolAsList, true, cancellationToken);
143+
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
144+
isConnect: true, applicationProtocols: SslExtensions.Http2ProtocolAsList,
145+
noCache: true, cancellationToken: cancellationToken);
145146

146147
http2Supported = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;
147148

@@ -153,8 +154,9 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response,
153154

154155
//don't pass cancellation token here
155156
//it could cause floating server connections when client exits
156-
prefetchConnectionTask = getServerConnection(connectArgs, true,
157-
null, false, CancellationToken.None);
157+
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
158+
isConnect: true, applicationProtocols: null, noCache: false,
159+
cancellationToken: CancellationToken.None);
158160

159161
try
160162
{
@@ -201,6 +203,12 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response,
201203
{
202204
decryptSsl = false;
203205
}
206+
207+
if(!decryptSsl)
208+
{
209+
await tcpConnectionFactory.Release(prefetchConnectionTask, true);
210+
prefetchConnectionTask = null;
211+
}
204212
}
205213

206214
if (cancellationTokenSource.IsCancellationRequested)
@@ -214,8 +222,9 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response,
214222
// create new connection to server.
215223
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
216224
// this connection should not be HTTPS.
217-
var connection = await getServerConnection(connectArgs,
218-
true, SslExtensions.Http2ProtocolAsList, true, cancellationToken);
225+
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
226+
isConnect: true, applicationProtocols: SslExtensions.Http2ProtocolAsList,
227+
noCache: true, cancellationToken: cancellationToken);
219228

220229
try
221230
{
@@ -247,8 +256,6 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
247256
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
248257
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
249258
connectArgs.CancellationTokenSource, ExceptionFunc);
250-
251-
252259
}
253260
finally
254261
{
@@ -284,9 +291,9 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
284291
throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
285292
}
286293

287-
var connection = await getServerConnection(connectArgs, true,
288-
SslExtensions.Http2ProtocolAsList, true,
289-
cancellationToken);
294+
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
295+
isConnect: true, applicationProtocols: SslExtensions.Http2ProtocolAsList,
296+
noCache: true, cancellationToken: cancellationToken);
290297
try
291298
{
292299
await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);
@@ -335,19 +342,9 @@ await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientS
335342
}
336343
finally
337344
{
338-
if (!calledRequestHandler
339-
&& prefetchConnectionTask != null)
345+
if (!calledRequestHandler)
340346
{
341-
TcpServerConnection prefetchedConnection = null;
342-
try
343-
{
344-
prefetchedConnection = await prefetchConnectionTask;
345-
346-
}
347-
finally
348-
{
349-
await tcpConnectionFactory.Release(prefetchedConnection, closeServerConnection);
350-
}
347+
await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);
351348
}
352349

353350
clientStream.Dispose();
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace Titanium.Web.Proxy.Models
2+
{
3+
public enum ProxyAuthenticationResult
4+
{
5+
/// <summary>
6+
/// Indicates the authentication request was successful
7+
/// </summary>
8+
Success,
9+
/// <summary>
10+
/// Indicates the authentication request failed
11+
/// </summary>
12+
Failure,
13+
/// <summary>
14+
/// Indicates that this stage of the authentication request succeeded
15+
/// And a second pass of the handshake needs to occur
16+
/// </summary>
17+
ContinuationNeeded
18+
}
19+
20+
/// <summary>
21+
/// A context container for authentication flows
22+
/// </summary>
23+
public class ProxyAuthenticationContext
24+
{
25+
/// <summary>
26+
/// The result of the current authentication request
27+
/// </summary>
28+
public ProxyAuthenticationResult Result { get; set; }
29+
30+
/// <summary>
31+
/// An optional continuation token to return to the caller if set
32+
/// </summary>
33+
public string Continuation { get; set; }
34+
35+
public static ProxyAuthenticationContext Failed()
36+
{
37+
return new ProxyAuthenticationContext
38+
{
39+
Result = ProxyAuthenticationResult.Failure,
40+
Continuation = null
41+
};
42+
}
43+
44+
public static ProxyAuthenticationContext Succeeded()
45+
{
46+
return new ProxyAuthenticationContext
47+
{
48+
Result = ProxyAuthenticationResult.Success,
49+
Continuation = null
50+
};
51+
}
52+
}
53+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using Polly;
2+
using System;
3+
using System.Threading.Tasks;
4+
using Titanium.Web.Proxy.Network.Tcp;
5+
6+
namespace Titanium.Web.Proxy.Network
7+
{
8+
internal class RetryPolicy<T> where T : Exception
9+
{
10+
private readonly int retries;
11+
private readonly TcpConnectionFactory tcpConnectionFactory;
12+
13+
private TcpServerConnection currentConnection;
14+
private Policy policy;
15+
16+
internal RetryPolicy(int retries, TcpConnectionFactory tcpConnectionFactory)
17+
{
18+
this.retries = retries;
19+
this.tcpConnectionFactory = tcpConnectionFactory;
20+
21+
policy = getRetryPolicy();
22+
}
23+
24+
/// <summary>
25+
/// Execute and retry the given action until retry number of times.
26+
/// </summary>
27+
/// <param name="action">The action to retry with return value specifying whether caller should continue execution.</param>
28+
/// <param name="generator">The Tcp connection generator to be invoked to get new connection for retry.</param>
29+
/// <param name="initialConnection">Initial Tcp connection to use.</param>
30+
/// <returns>Returns the latest connection used and the latest exception if any.</returns>
31+
internal async Task<RetryResult> ExecuteAsync(Func<TcpServerConnection, Task<bool>> action,
32+
Func<Task<TcpServerConnection>> generator, TcpServerConnection initialConnection)
33+
{
34+
currentConnection = initialConnection;
35+
Exception exception = null;
36+
bool @continue = true;
37+
38+
try
39+
{
40+
//retry on error with polly policy
41+
//do not use polly context to store connection; it does not save states b/w attempts
42+
await policy.ExecuteAsync(async () =>
43+
{
44+
//setup connection
45+
currentConnection = currentConnection as TcpServerConnection ??
46+
await generator();
47+
//try
48+
@continue = await action(currentConnection);
49+
50+
});
51+
}
52+
catch (Exception e) { exception = e; }
53+
54+
return new RetryResult(currentConnection, exception, @continue);
55+
}
56+
57+
//get the policy
58+
private Policy getRetryPolicy()
59+
{
60+
return Policy.Handle<T>()
61+
.RetryAsync(retries,
62+
onRetryAsync: onRetry);
63+
}
64+
65+
//before retry clear connection
66+
private async Task onRetry(Exception ex, int attempt)
67+
{
68+
if (currentConnection != null)
69+
{
70+
//close connection on error
71+
await tcpConnectionFactory.Release(currentConnection, true);
72+
currentConnection = null;
73+
}
74+
}
75+
}
76+
77+
internal class RetryResult
78+
{
79+
internal bool IsSuccess => Exception == null;
80+
internal TcpServerConnection LatestConnection { get; }
81+
internal Exception Exception { get; }
82+
internal bool Continue { get; }
83+
84+
internal RetryResult(TcpServerConnection lastConnection, Exception exception, bool @continue)
85+
{
86+
LatestConnection = lastConnection;
87+
Exception = exception;
88+
Continue = @continue;
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)