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

Commit 9bae951

Browse files
authored
Merge pull request #692 from justcoding121/beta
stable
2 parents 931d59c + f9a74d7 commit 9bae951

File tree

13 files changed

+661
-129
lines changed

13 files changed

+661
-129
lines changed

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

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4+
using System.Linq;
45
using System.Net;
56
using System.Net.Security;
7+
using System.Text;
68
using System.Threading;
79
using System.Threading.Tasks;
810
using Titanium.Web.Proxy.EventArguments;
911
using Titanium.Web.Proxy.Exceptions;
1012
using Titanium.Web.Proxy.Helpers;
1113
using Titanium.Web.Proxy.Http;
1214
using Titanium.Web.Proxy.Models;
15+
using Titanium.Web.Proxy.StreamExtended.Network;
1316

1417
namespace Titanium.Web.Proxy.Examples.Basic
1518
{
@@ -22,6 +25,9 @@ public class ProxyTestController
2225
public ProxyTestController()
2326
{
2427
proxyServer = new ProxyServer();
28+
29+
//proxyServer.EnableHttp2 = true;
30+
2531
// generate root certificate without storing it in file system
2632
//proxyServer.CertificateManager.CreateRootCertificate(false);
2733

@@ -32,11 +38,11 @@ public ProxyTestController()
3238
{
3339
if (exception is ProxyHttpException phex)
3440
{
35-
await writeToConsole(exception.Message + ": " + phex.InnerException?.Message, true);
41+
await writeToConsole(exception.Message + ": " + phex.InnerException?.Message, ConsoleColor.Red);
3642
}
3743
else
3844
{
39-
await writeToConsole(exception.Message, true);
45+
await writeToConsole(exception.Message, ConsoleColor.Red);
4046
}
4147
};
4248
proxyServer.ForwardToUpstreamGateway = true;
@@ -146,6 +152,38 @@ private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSess
146152
}
147153
}
148154

155+
private void WebSocket_DataSent(object sender, DataEventArgs e)
156+
{
157+
var args = (SessionEventArgs)sender;
158+
WebSocketDataSentReceived(args, e, true);
159+
}
160+
161+
private void WebSocket_DataReceived(object sender, DataEventArgs e)
162+
{
163+
var args = (SessionEventArgs)sender;
164+
WebSocketDataSentReceived(args, e, false);
165+
}
166+
167+
private void WebSocketDataSentReceived(SessionEventArgs args, DataEventArgs e, bool sent)
168+
{
169+
var color = sent ? ConsoleColor.Green : ConsoleColor.Blue;
170+
171+
foreach (var frame in args.WebSocketDecoder.Decode(e.Buffer, e.Offset, e.Count))
172+
{
173+
if (frame.OpCode == WebsocketOpCode.Binary)
174+
{
175+
var data = frame.Data.ToArray();
176+
string str = string.Join(",", data.ToArray().Select(x => x.ToString("X2")));
177+
writeToConsole(str, color).Wait();
178+
}
179+
180+
if (frame.OpCode == WebsocketOpCode.Text)
181+
{
182+
writeToConsole(frame.GetText(), color).Wait();
183+
}
184+
}
185+
}
186+
149187
private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
150188
{
151189
return Task.FromResult(false);
@@ -205,6 +243,12 @@ private async Task multipartRequestPartSent(object sender, MultipartRequestPartS
205243

206244
private async Task onResponse(object sender, SessionEventArgs e)
207245
{
246+
if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Websocket)
247+
{
248+
e.DataSent += WebSocket_DataSent;
249+
e.DataReceived += WebSocket_DataReceived;
250+
}
251+
208252
await writeToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
209253

210254
string ext = System.IO.Path.GetExtension(e.HttpClient.Request.RequestUri.AbsolutePath);
@@ -277,14 +321,14 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs
277321
return Task.FromResult(0);
278322
}
279323

280-
private async Task writeToConsole(string message, bool useRedColor = false)
324+
private async Task writeToConsole(string message, ConsoleColor? consoleColor = null)
281325
{
282326
await @lock.WaitAsync();
283327

284-
if (useRedColor)
328+
if (consoleColor.HasValue)
285329
{
286330
ConsoleColor existing = Console.ForegroundColor;
287-
Console.ForegroundColor = ConsoleColor.Red;
331+
Console.ForegroundColor = consoleColor.Value;
288332
Console.WriteLine(message);
289333
Console.ForegroundColor = existing;
290334
}

src/Titanium.Web.Proxy.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
22
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Titanium_002EWeb_002EProxy_002EExamples_002EWpf_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
3+
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=docfx_002Ejson/@EntryIndexedValue">docfx.json</s:String>
34
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
45
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
56
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>

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

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

29+
private WebSocketDecoder webSocketDecoder;
30+
2931
/// <summary>
3032
/// Is this session a HTTP/2 promise?
3133
/// </summary>
@@ -58,6 +60,8 @@ public bool ReRequest
5860
}
5961
}
6062

63+
public WebSocketDecoder WebSocketDecoder => webSocketDecoder ??= new WebSocketDecoder(BufferPool);
64+
6165
/// <summary>
6266
/// Occurs when multipart request part sent.
6367
/// </summary>

src/Titanium.Web.Proxy/ExplicitClientHandler.cs

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
4747
try
4848
{
4949
TunnelConnectSessionEventArgs? connectArgs = null;
50-
50+
51+
var method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);
52+
if (clientStream.IsClosed)
53+
{
54+
return;
55+
}
56+
5157
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
52-
if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, cancellationToken) == 1)
58+
if (method == KnownMethod.Connect)
5359
{
5460
// read the first line HTTP command
5561
var requestLine = await clientStream.ReadRequestLine(cancellationToken);
@@ -75,6 +81,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
7581

7682
// filter out excluded host names
7783
bool decryptSsl = endPoint.DecryptSsl && connectArgs.DecryptSsl;
84+
bool sendRawData = !decryptSsl;
7885

7986
if (connectArgs.DenyConnect)
8087
{
@@ -113,6 +120,10 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
113120
await clientStream.WriteResponseAsync(response, cancellationToken);
114121

115122
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
123+
if (clientStream.IsClosed)
124+
{
125+
return;
126+
}
116127

117128
bool isClientHello = clientHelloInfo != null;
118129
if (clientHelloInfo != null)
@@ -130,26 +141,29 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
130141

131142
bool http2Supported = false;
132143

133-
var alpn = clientHelloInfo.GetAlpn();
134-
if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
144+
if (EnableHttp2)
135145
{
136-
// test server HTTP/2 support
137-
try
138-
{
139-
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
140-
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
141-
true, SslExtensions.Http2ProtocolAsList,
142-
true, cancellationToken);
143-
144-
http2Supported = connection.NegotiatedApplicationProtocol ==
145-
SslApplicationProtocol.Http2;
146-
147-
// release connection back to pool instead of closing when connection pool is enabled.
148-
await tcpConnectionFactory.Release(connection, true);
149-
}
150-
catch (Exception)
146+
var alpn = clientHelloInfo.GetAlpn();
147+
if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
151148
{
152-
// ignore
149+
// test server HTTP/2 support
150+
try
151+
{
152+
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
153+
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
154+
true, SslExtensions.Http2ProtocolAsList,
155+
true, cancellationToken);
156+
157+
http2Supported = connection.NegotiatedApplicationProtocol ==
158+
SslApplicationProtocol.Http2;
159+
160+
// release connection back to pool instead of closing when connection pool is enabled.
161+
await tcpConnectionFactory.Release(connection, true);
162+
}
163+
catch (Exception)
164+
{
165+
// ignore
166+
}
153167
}
154168
}
155169

@@ -224,36 +238,46 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
224238
$"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
225239
}
226240

227-
if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, cancellationToken) == -1)
241+
method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);
242+
if (clientStream.IsClosed)
228243
{
229-
decryptSsl = false;
244+
return;
230245
}
231246

232-
if (!decryptSsl)
247+
if (method == KnownMethod.Invalid)
233248
{
249+
sendRawData = true;
234250
await tcpConnectionFactory.Release(prefetchConnectionTask, true);
235251
prefetchConnectionTask = null;
236252
}
237253
}
254+
else if (clientHelloInfo == null)
255+
{
256+
method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);
257+
if (clientStream.IsClosed)
258+
{
259+
return;
260+
}
261+
}
238262

239263
if (cancellationTokenSource.IsCancellationRequested)
240264
{
241265
throw new Exception("Session was terminated by user.");
242266
}
243267

244-
// Hostname is excluded or it is not an HTTPS connect
245-
if (!decryptSsl || !isClientHello)
268+
if (method == KnownMethod.Invalid)
246269
{
247-
if (!isClientHello)
248-
{
249-
connectRequest.TunnelType = TunnelType.Websocket;
250-
}
270+
sendRawData = true;
271+
}
251272

273+
// Hostname is excluded or it is not an HTTPS connect
274+
if (sendRawData)
275+
{
252276
// create new connection to server.
253277
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
254278
// this connection should not be HTTPS.
255279
var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
256-
true, SslExtensions.Http2ProtocolAsList,
280+
true, null,
257281
true, cancellationToken);
258282

259283
try
@@ -302,7 +326,7 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
302326
}
303327
}
304328

305-
if (connectArgs != null && await HttpHelper.IsPriMethod(clientStream, BufferPool, cancellationToken) == 1)
329+
if (connectArgs != null && method == KnownMethod.Pri)
306330
{
307331
// todo
308332
string? httpCmd = await clientStream.ReadLineAsync(cancellationToken);

0 commit comments

Comments
 (0)