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

Basic HTTP/2 support. Disabled by default. Warning added to the enabl… #583

Merged
merged 4 commits into from
Apr 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e)
SessionListItem item = null;
await Dispatcher.InvokeAsync(() => { item = addSession(e); });

if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
{
// GetRequestBody for HTTP/2 currently not supported
return;
}

if (e.HttpClient.Request.HasBody)
{
e.HttpClient.Request.KeepBody = true;
Expand All @@ -168,6 +174,12 @@ await Dispatcher.InvokeAsync(() =>
}
});

if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
{
// GetRequestBody for HTTP/2 currently not supported
return;
}

if (item != null)
{
if (e.HttpClient.Response.HasBody)
Expand Down Expand Up @@ -217,6 +229,12 @@ private SessionListItem createSessionListItem(SessionEventArgsBase e)
var session = (SessionEventArgsBase)sender;
if (sessionDictionary.TryGetValue(session.HttpClient, out var li))
{
var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
if (tunnelType != TunnelType.Unknown)
{
li.Protocol = TunnelTypeToString(tunnelType);
}

li.ReceivedDataCount += args.Count;
}
};
Expand All @@ -226,6 +244,12 @@ private SessionListItem createSessionListItem(SessionEventArgsBase e)
var session = (SessionEventArgsBase)sender;
if (sessionDictionary.TryGetValue(session.HttpClient, out var li))
{
var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
if (tunnelType != TunnelType.Unknown)
{
li.Protocol = TunnelTypeToString(tunnelType);
}

li.SentDataCount += args.Count;
}
};
Expand All @@ -235,6 +259,21 @@ private SessionListItem createSessionListItem(SessionEventArgsBase e)
return item;
}

private string TunnelTypeToString(TunnelType tunnelType)
{
switch (tunnelType)
{
case TunnelType.Https:
return "https";
case TunnelType.Websocket:
return "websocket";
case TunnelType.Http2:
return "http2";
}

return null;
}

private void ListViewSessions_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
Expand Down
48 changes: 30 additions & 18 deletions examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class SessionListItem : INotifyPropertyChanged
private long? bodySize;
private Exception exception;
private string host;
private string process;
private int processId;
private string protocol;
private long receivedDataCount;
private long sentDataCount;
Expand Down Expand Up @@ -54,10 +54,32 @@ public long? BodySize
set => SetField(ref bodySize, value);
}

public int ProcessId
{
get => processId;
set
{
if (SetField(ref processId, value))
{
OnPropertyChanged(nameof(Process));
}
}
}

public string Process
{
get => process;
set => SetField(ref process, value);
get
{
try
{
var process = System.Diagnostics.Process.GetProcessById(processId);
return process.ProcessName + ":" + processId;
}
catch (Exception)
{
return string.Empty;
}
}
}

public long ReceivedDataCount
Expand All @@ -80,13 +102,16 @@ public Exception Exception

public event PropertyChangedEventHandler PropertyChanged;

protected void SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (!Equals(field, value))
{
field = value;
OnPropertyChanged(propertyName);
return true;
}

return false;
}

[NotifyPropertyChangedInvocator]
Expand Down Expand Up @@ -132,20 +157,7 @@ public void Update()
BodySize = responseSize;
}

Process = GetProcessDescription(HttpClient.ProcessId.Value);
}

private string GetProcessDescription(int processId)
{
try
{
var process = System.Diagnostics.Process.GetProcessById(processId);
return process.ProcessName + ":" + processId;
}
catch (Exception)
{
return string.Empty;
}
ProcessId = HttpClient.ProcessId.Value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Properties\AssemblyInfo.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public abstract class SessionEventArgsBase : EventArgs, IDisposable
/// <summary>
/// Relative milliseconds for various events.
/// </summary>
public Dictionary<string, DateTime> TimeLine { get; set; } = new Dictionary<string, DateTime>();
public Dictionary<string, DateTime> TimeLine { get; } = new Dictionary<string, DateTime>();

/// <summary>
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
Expand Down
21 changes: 18 additions & 3 deletions src/Titanium.Web.Proxy/ExplicitClientHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
bool isClientHello = clientHelloInfo != null;
if (isClientHello)
{
connectRequest.TunnelType = TunnelType.Https;
connectRequest.ClientHelloInfo = clientHelloInfo;
}

Expand Down Expand Up @@ -208,9 +209,9 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
}
catch (Exception e)
{
var certname = certificate?.GetNameInfo(X509NameType.SimpleName, false);
var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
throw new ProxyConnectException(
$"Couldn't authenticate host '{connectHostname}' with certificate '{certname}'.", e, connectArgs);
$"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
}

if (await HttpHelper.IsConnectMethod(clientStream) == -1)
Expand All @@ -233,6 +234,11 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
// Hostname is excluded or it is not an HTTPS connect
if (!decryptSsl || !isClientHello)
{
if (!isClientHello)
{
connectRequest.TunnelType = TunnelType.Websocket;
}

// 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.
Expand Down Expand Up @@ -286,6 +292,8 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
string httpCmd = await clientStream.ReadLineAsync(cancellationToken);
if (httpCmd == "PRI * HTTP/2.0")
{
connectArgs.HttpClient.ConnectRequest.TunnelType = TunnelType.Http2;

// HTTP/2 Connection Preface
string line = await clientStream.ReadLineAsync(cancellationToken);
if (line != string.Empty)
Expand Down Expand Up @@ -318,9 +326,16 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
() => new SessionEventArgs(this, endPoint, cancellationTokenSource)
{
ProxyClient = { Connection = clientConnection },
HttpClient = { ConnectRequest = connectArgs?.HttpClient.ConnectRequest },
UserData = connectArgs?.UserData
},
async args => { await invokeBeforeRequest(args); },
async args => { await invokeBeforeResponse(args); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif

}
finally
{
Expand Down
4 changes: 2 additions & 2 deletions src/Titanium.Web.Proxy/Helpers/HttpHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader)
private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, string expectedStart)
{
bool isExpected = true;
int legthToCheck = 10;
for (int i = 0; i < legthToCheck; i++)
int lengthToCheck = 10;
for (int i = 0; i < lengthToCheck; i++)
{
int b = await clientStreamReader.PeekByteAsync(i);
if (b == -1)
Expand Down
32 changes: 0 additions & 32 deletions src/Titanium.Web.Proxy/Helpers/Ref.cs

This file was deleted.

2 changes: 2 additions & 0 deletions src/Titanium.Web.Proxy/Http/ConnectRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public ConnectRequest()
Method = "CONNECT";
}

public TunnelType TunnelType { get; internal set; }

public ClientHelloInfo ClientHelloInfo { get; set; }
}
}
18 changes: 9 additions & 9 deletions src/Titanium.Web.Proxy/Http/KnownHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@
public static class KnownHeaders
{
// Both
public const string Connection = "connection";
public const string Connection = "Connection";
public const string ConnectionClose = "close";
public const string ConnectionKeepAlive = "keep-alive";

public const string ContentLength = "content-length";
public const string ContentLength = "Content-Length";

public const string ContentType = "content-type";
public const string ContentType = "Content-Type";
public const string ContentTypeCharset = "charset";
public const string ContentTypeBoundary = "boundary";

public const string Upgrade = "upgrade";
public const string Upgrade = "Upgrade";
public const string UpgradeWebsocket = "websocket";

// Request headers
public const string AcceptEncoding = "accept-encoding";
public const string AcceptEncoding = "Accept-Encoding";

public const string Authorization = "Authorization";

public const string Expect = "expect";
public const string Expect = "Expect";
public const string Expect100Continue = "100-continue";

public const string Host = "host";
public const string Host = "Host";

public const string ProxyAuthorization = "Proxy-Authorization";
public const string ProxyAuthorizationBasic = "basic";
Expand All @@ -36,7 +36,7 @@ public static class KnownHeaders
public const string ProxyConnectionClose = "close";

// Response headers
public const string ContentEncoding = "content-encoding";
public const string ContentEncoding = "Content-Encoding";
public const string ContentEncodingDeflate = "deflate";
public const string ContentEncodingGzip = "gzip";
public const string ContentEncodingBrotli = "br";
Expand All @@ -45,7 +45,7 @@ public static class KnownHeaders

public const string ProxyAuthenticate = "Proxy-Authenticate";

public const string TransferEncoding = "transfer-encoding";
public const string TransferEncoding = "Transfer-Encoding";
public const string TransferEncodingChunked = "chunked";
}
}
10 changes: 10 additions & 0 deletions src/Titanium.Web.Proxy/Http/TunnelType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Titanium.Web.Proxy.Http
{
public enum TunnelType
{
Unknown,
Https,
Websocket,
Http2,
}
}
Loading