Skip to content

Commit

Permalink
Extract sdk from client, add aspnetcore extension
Browse files Browse the repository at this point in the history
  • Loading branch information
cristipufu committed Oct 10, 2024
1 parent 7955830 commit f0e6330
Show file tree
Hide file tree
Showing 20 changed files with 252 additions and 110 deletions.
16 changes: 15 additions & 1 deletion Tunnelite.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.TcpServer", "test\Test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.TcpForwarder", "test\Test.TcpForwarder\Test.TcpForwarder.csproj", "{BCC3AD29-688B-4482-B952-F0095D104020}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.WebApi", "test\Test.WebApi\Test.WebApi.csproj", "{B2656A6C-79E5-41A1-93B2-F87D550FB02E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.WebApi", "test\Test.WebApi\Test.WebApi.csproj", "{B2656A6C-79E5-41A1-93B2-F87D550FB02E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tunnelite.Sdk", "src\Tunnelite.Sdk\Tunnelite.Sdk.csproj", "{BCC85AF5-8BD0-4E4E-AB0E-306FF640537D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tunnelite.AspNetCore", "src\Tunnelite.AspNetCore\Tunnelite.AspNetCore.csproj", "{E66B7CBA-88CB-41EF-B0B2-8A57F2F9CE81}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -49,6 +53,14 @@ Global
{B2656A6C-79E5-41A1-93B2-F87D550FB02E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2656A6C-79E5-41A1-93B2-F87D550FB02E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2656A6C-79E5-41A1-93B2-F87D550FB02E}.Release|Any CPU.Build.0 = Release|Any CPU
{BCC85AF5-8BD0-4E4E-AB0E-306FF640537D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCC85AF5-8BD0-4E4E-AB0E-306FF640537D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCC85AF5-8BD0-4E4E-AB0E-306FF640537D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCC85AF5-8BD0-4E4E-AB0E-306FF640537D}.Release|Any CPU.Build.0 = Release|Any CPU
{E66B7CBA-88CB-41EF-B0B2-8A57F2F9CE81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E66B7CBA-88CB-41EF-B0B2-8A57F2F9CE81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E66B7CBA-88CB-41EF-B0B2-8A57F2F9CE81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E66B7CBA-88CB-41EF-B0B2-8A57F2F9CE81}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -60,6 +72,8 @@ Global
{9E529D00-D5E7-4E2E-80AD-83B793B77DF8} = {230DD554-CD20-4A77-874B-E9BA59A2DC18}
{BCC3AD29-688B-4482-B952-F0095D104020} = {230DD554-CD20-4A77-874B-E9BA59A2DC18}
{B2656A6C-79E5-41A1-93B2-F87D550FB02E} = {230DD554-CD20-4A77-874B-E9BA59A2DC18}
{BCC85AF5-8BD0-4E4E-AB0E-306FF640537D} = {AFD8CF9B-F8B9-47C9-BEAB-FC0161D2AFB8}
{E66B7CBA-88CB-41EF-B0B2-8A57F2F9CE81} = {AFD8CF9B-F8B9-47C9-BEAB-FC0161D2AFB8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E385A6C3-BECA-4888-B12B-31AA9984F86F}
Expand Down
37 changes: 37 additions & 0 deletions src/Tunnelite.AspNetCore/Tunnelite.AspNetCore.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>Tunnelite.AspNetCore</PackageId>
<PackageTags>tunnel;tunneling;websockets;signalr;reverse-proxy;proxy;</PackageTags>
<Authors>Cristi Pufu</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>SDK for tunneling AspNetCore apps</Description>
<PackageProjectUrl>https://github.com/cristipufu/ws-tunnel-signalr</PackageProjectUrl>
<RepositoryUrl>https://github.com/cristipufu/ws-tunnel-signalr</RepositoryUrl>
<Version>1.0.0</Version>
<IsPackable>true</IsPackable>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Tunnelite.Sdk\Tunnelite.Sdk.csproj" />
</ItemGroup>

</Project>
68 changes: 68 additions & 0 deletions src/Tunnelite.AspNetCore/TunneliteExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Tunnelite.Sdk;

#nullable disable
namespace Tunnelite.AspNetCore;

public static class TunneliteExtensions
{
private static readonly Guid ClientId = Guid.NewGuid();

public static IApplicationBuilder UseTunnelite(this IApplicationBuilder app)
{
var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
var server = app.ApplicationServices.GetRequiredService<IServer>();

lifetime.ApplicationStarted.Register(() =>
{
var addressFeature = server.Features.Get<IServerAddressesFeature>();
var localUrl = addressFeature?.Addresses.FirstOrDefault();
if (string.IsNullOrEmpty(localUrl))
{
throw new InvalidOperationException("Unable to determine the local URL of the application.");
}
Task.Run(async () =>
{
var httpTunnel = new HttpTunnelRequest
{
ClientId = ClientId,
LocalUrl = localUrl,
PublicUrl = "https://tunnelite.com",
};
var client = new HttpTunnelClient(httpTunnel, null);
client.Log += x => Console.WriteLine(x);
client.LogRequest += (method, path) => Console.WriteLine($"{DateTimeOffset.Now:HH:mm:ss} [{method}]: {path}");
client.LogFailedRequest += (method, path) => Console.Write($"{DateTimeOffset.Now:HH:mm:ss} [{method}]: {path}");
client.LogError += x => Console.WriteLine(x);
client.LogException += x => Console.WriteLine(x.Message);
await client.ConnectAsync();
LogTunnelInfo(client.TunnelUrl);
}, CancellationToken.None);
});

return app;
}

private static void LogTunnelInfo(string tunnelUrl)
{
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════════════╗");
Console.WriteLine($" Tunnelite URL: {tunnelUrl,-67}");
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════════╝");
Console.ResetColor();
Console.WriteLine();
}
}
14 changes: 0 additions & 14 deletions src/Tunnelite.Client/ITunnelClient.cs

This file was deleted.

30 changes: 22 additions & 8 deletions src/Tunnelite.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
using Microsoft.Extensions.Logging;
using Spectre.Console;
using System.CommandLine;
using Tunnelite.Client.HttpTunnel;
using Tunnelite.Client.TcpTunnel;
using Tunnelite.Sdk;

namespace Tunnelite.Client;

Expand Down Expand Up @@ -100,6 +99,9 @@ private static async Task InitializeTcpTunnel(string localUrl, string publicUrl,
ctx.Status("Connecting to TCP tunnel...");

Client = new TcpTunnelClient(tcpTunnel, logLevel);

AddLogging(Client);

await Client.ConnectAsync();

ctx.Status("TCP tunnel established.");
Expand All @@ -117,6 +119,9 @@ private static async Task InitializeHttpTunnel(string localUrl, string publicUrl
ctx.Status("Connecting to HTTP tunnel...");

Client = new HttpTunnelClient(httpTunnel, logLevel);

AddLogging(Client);

await Client.ConnectAsync();

ctx.Status("HTTP tunnel established.");
Expand Down Expand Up @@ -176,7 +181,7 @@ private static async Task RunMainLoop(string localUrl)
}
}

public static Table WriteStatusTable(string localUrl, string? tunnelUrl, string color, string currentStatus)
private static Table WriteStatusTable(string localUrl, string? tunnelUrl, string color, string currentStatus)
{
AnsiConsole.Clear();

Expand All @@ -199,32 +204,41 @@ private static void WriteHelp()
AnsiConsole.WriteLine();
}

public static void Log(string log)
private static void Log(string log)
{
string entry = $"[yellow] {DateTimeOffset.Now:HH:mm:ss}[/]: {Markup.Escape(log)}";
AnsiConsole.MarkupLine(entry);
}

public static void LogRequest(string method, string path)
private static void LogRequest(string method, string path)
{
string entry = $"[green] {DateTimeOffset.Now:HH:mm:ss} [[{method}]][/]: {Markup.Escape(path)}";
AnsiConsole.MarkupLine(entry);
}

public static void LogFailedRequest(string method, string path)
private static void LogFailedRequest(string method, string path)
{
string entry = $"[red] {DateTimeOffset.Now:HH:mm:ss} [[{method}]][/]: {Markup.Escape(path)}";
AnsiConsole.MarkupLine(entry);
}

public static void LogError(string message)
private static void LogError(string message)
{
string entry = $"[red] {DateTimeOffset.Now:HH:mm:ss}[/]: {Markup.Escape(message)}";
AnsiConsole.MarkupLine(entry);
}

public static void LogException(Exception ex)
private static void LogException(Exception ex)
{
AnsiConsole.WriteException(ex);
}

private static void AddLogging(ITunnelClient client)
{
client.Log += Log;
client.LogRequest += LogRequest;
client.LogFailedRequest += LogFailedRequest;
client.LogError += LogError;
client.LogException += LogException;
}
}
10 changes: 6 additions & 4 deletions src/Tunnelite.Client/Tunnelite.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
<Description>Tool for tunneling URLs</Description>
<PackageProjectUrl>https://github.com/cristipufu/ws-tunnel-signalr</PackageProjectUrl>
<RepositoryUrl>https://github.com/cristipufu/ws-tunnel-signalr</RepositoryUrl>
<Version>1.1.5</Version>
<Version>1.1.6</Version>
<IsPackable>true</IsPackable>
</PropertyGroup>

<ItemGroup>
Expand All @@ -27,11 +28,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Spectre.Console" Version="0.49.1" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Tunnelite.Sdk\Tunnelite.Sdk.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#nullable disable
namespace Tunnelite.Client.HttpTunnel;
namespace Tunnelite.Sdk;

public class HttpConnection
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
using System.Net.WebSockets;
using System.Runtime.CompilerServices;

namespace Tunnelite.Client.HttpTunnel;
namespace Tunnelite.Sdk;

public class HttpTunnelClient : ITunnelClient
{
public event Func<Task>? Connected;
public event Action<string, string>? LogRequest;
public event Action<string, string>? LogFailedRequest;
public event Action<Exception>? LogException;
public event Action<string>? Log;
public event Action<string>? LogError;

public HubConnection Connection { get; }
public string? TunnelUrl
{
Expand Down Expand Up @@ -51,7 +57,7 @@ public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel? logLevel)

Connection.On<HttpConnection>("NewHttpConnection", (httpConnection) =>
{
Program.LogRequest(httpConnection.Method, httpConnection.Path);
LogRequest?.Invoke(httpConnection.Method, httpConnection.Path);
_ = TunnelHttpConnectionAsync(httpConnection);
Expand All @@ -60,7 +66,7 @@ public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel? logLevel)

Connection.On<WsConnection>("NewWsConnection", (wsConnection) =>
{
Program.LogRequest("WS", wsConnection.Path);
LogRequest?.Invoke("WS", wsConnection.Path);
_ = TunnelWsConnectionAsync(wsConnection);
Expand Down Expand Up @@ -157,8 +163,8 @@ private async Task TunnelHttpConnectionAsync(HttpConnection httpConnection)
}
catch (Exception ex)
{
Program.LogFailedRequest(httpConnection.Method, httpConnection.Path);
Program.LogException(ex);
LogFailedRequest?.Invoke(httpConnection.Method, httpConnection.Path);
LogException?.Invoke(ex);
using var errorRequest = new HttpRequestMessage(HttpMethod.Delete, requestUrl);
using var response = await ServerHttpClient.SendAsync(errorRequest);
}
Expand All @@ -181,14 +187,14 @@ private async Task TunnelWsConnectionAsync(WsConnection wsConnection)
}
catch (Exception ex)
{
Program.LogFailedRequest("WS", wsConnection.Path);
Program.LogException(ex);
LogFailedRequest?.Invoke("WS", wsConnection.Path);
LogException?.Invoke(ex);
}
finally
{
await cts.CancelAsync();

Program.Log($"[WS] Connection {wsConnection.RequestId} closed.");
Log?.Invoke($"[WS] Connection {wsConnection.RequestId} closed.");
}
}

Expand Down Expand Up @@ -217,7 +223,7 @@ private async Task StreamIncomingWsAsync(WebSocket webSocket, WsConnection wsCon
}
finally
{
Program.Log($"[WS] Writing data to connection {wsConnection.RequestId} finished.");
Log?.Invoke($"[WS] Writing data to connection {wsConnection.RequestId} finished.");
}
}

Expand All @@ -226,7 +232,7 @@ private async Task StreamOutgoingWsAsync(WebSocket localWebSocket, WsConnection
await Connection.InvokeAsync("StreamOutgoingWsAsync", StreamLocalWsAsync(localWebSocket, wsConnection, cancellationToken), wsConnection, cancellationToken: cancellationToken);
}

private static async IAsyncEnumerable<(ReadOnlyMemory<byte>, WebSocketMessageType)> StreamLocalWsAsync(WebSocket webSocket, WsConnection wsConnection, [EnumeratorCancellation] CancellationToken cancellationToken)
private async IAsyncEnumerable<(ReadOnlyMemory<byte>, WebSocketMessageType)> StreamLocalWsAsync(WebSocket webSocket, WsConnection wsConnection, [EnumeratorCancellation] CancellationToken cancellationToken)
{
const int chunkSize = 32 * 1024;

Expand All @@ -248,7 +254,7 @@ private async Task StreamOutgoingWsAsync(WebSocket localWebSocket, WsConnection
}
finally
{
Program.Log($"[WS] Reading data from connection {wsConnection.RequestId} finished.");
Log?.Invoke($"[WS] Reading data from connection {wsConnection.RequestId} finished.");

ArrayPool<byte>.Shared.Return(buffer);
}
Expand All @@ -270,13 +276,13 @@ private async Task StreamOutgoingWsAsync(WebSocket localWebSocket, WsConnection

if (!response.IsSuccessStatusCode)
{
Program.LogError($"{tunnelResponse!.Message}:{tunnelResponse.Error}");
LogError?.Invoke($"{tunnelResponse!.Message}:{tunnelResponse.Error}");
}
}
catch (Exception ex)
{
Program.LogError($"[HTTP] An error occurred while registering the tunnel:");
Program.LogException(ex);
LogError?.Invoke($"[HTTP] An error occurred while registering the tunnel:");
LogException?.Invoke(ex);

await Task.Delay(5000);
}
Expand All @@ -303,7 +309,7 @@ private async Task<bool> ConnectWithRetryAsync(HubConnection connection, Cancell
}
catch
{
Program.LogError($"[HTTP] Cannot connect to the public server on {Tunnel.PublicUrl}.");
LogError?.Invoke($"[HTTP] Cannot connect to the public server on {Tunnel.PublicUrl}.");

await Task.Delay(5000, token);
}
Expand Down
Loading

0 comments on commit f0e6330

Please sign in to comment.