Skip to content

Commit

Permalink
Add localhost proxy server (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
Philippe Birbaum authored Jul 17, 2021
1 parent 8de252a commit 4ee5cd8
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 3 deletions.
12 changes: 12 additions & 0 deletions src/Tool/src/Boost.Abstractions/Web/ILocalProxyServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Boost.Web.Proxy
{
public interface ILocalProxyServer : IDisposable
{
Task<string> StartAsync(LocalProxyOptions options, CancellationToken cancellationToken);
Task StopAsync();
}
}
9 changes: 9 additions & 0 deletions src/Tool/src/Boost.Abstractions/Web/LocalProxyOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Boost.Web
{
public class LocalProxyOptions
{
public int Port { get; set; }

public string DestinationAddress { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/Tool/src/Boost.Core/Boost.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
Expand Down Expand Up @@ -28,6 +28,7 @@
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="HotChocolate.AspNetCore" Version="$(HotChocolateVersion)" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0-preview.11.21223.5" />


</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/Tool/src/Boost.Core/BoostServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Boost.Security;
using Boost.Settings;
using Boost.Utils;
using Boost.Web.Proxy;
using Boost.Workspace;
using HotChocolate.Execution.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -51,6 +52,7 @@ public static IServiceCollection AddBoost(this IServiceCollection services)
services.AddSingleton<IAuthTokenStoreReader, UserDataAuthTokenStoreReader>();
services.AddSingleton<IVersionChecker, VersionChecker>();
services.AddSingleton<IAppNavigationService, AppNavigationService>();
services.AddSingleton<ILocalProxyServer, LocalProxyServer>();

services.AddUserDataProtection();

Expand Down
84 changes: 84 additions & 0 deletions src/Tool/src/Boost.Core/Commands/LocalProxyCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Threading.Tasks;
using Boost.Infrastructure;
using Boost.Web;
using Boost.Web.Proxy;
using McMaster.Extensions.CommandLineUtils;

namespace Boost.Commands
{
[Command(
Name = "lp",
FullName = "Local proxy",
Description = "Starts a local proxy server")]
public class LocalProxyCommand : CommandBase
{
private readonly ILocalProxyServer _proxyServer;

public LocalProxyCommand(ILocalProxyServer proxyServer)
{
_proxyServer = proxyServer;
}

[Option("--port <PORT>", Description = "Webserver port")]
public int Port { get; set; } = 5001;

[Argument(0, "DestinationAddress", ShowInHelpText = true)]
public string DestinationAddress { get; set; } = default!;

public async Task OnExecute(IConsole console)
{
console.WriteLine("Starting local proxy");
var port = NetworkExtensions.GetAvailablePort(Port);

if (port != Port)
{
console.WriteLine($"Port {Port} is allready in use.", ConsoleColor.Yellow);
var useOther = Prompt.GetYesNo($"Start proxy on port: {port}", true);

if (useOther)
{
Port = port;
}
else
{
return;
}
}
var options = new LocalProxyOptions
{
Port = Port,
DestinationAddress = DestinationAddress
};

string url = await _proxyServer.StartAsync(
options,
CommandAborded);

ProcessHelpers.OpenBrowser(url);

var stopMessage = "Press 'q' or 'esc' to stop";
console.WriteLine(stopMessage);

while (true)
{
ConsoleKeyInfo key = Console.ReadKey();
if (key.KeyChar == 'q' || key.Key == ConsoleKey.Escape)
{
break;
}
else
{
console.ClearLine();
console.WriteLine("Unknown command", ConsoleColor.Red);
Console.WriteLine(stopMessage);
}
}

console.WriteLine("Stopping proxy....");
await _proxyServer.StopAsync();

_proxyServer.Dispose();
}
}
}
81 changes: 81 additions & 0 deletions src/Tool/src/Boost.Core/Web/Proxy/LocalProxyServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Yarp.ReverseProxy.Abstractions;

namespace Boost.Web.Proxy
{
public class LocalProxyServer : ILocalProxyServer, IDisposable
{
private IHost _host = default!;

public async Task<string> StartAsync(
LocalProxyOptions options,
CancellationToken cancellationToken)
{
var url = $"https://localhost:{options.Port}";

_host = Host.CreateDefaultBuilder()
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(url);
webBuilder.UseStartup<Startup>();
})
.ConfigureServices((ctx, services) =>
{
ProxyRoute[]? routes = new[]
{
new ProxyRoute()
{
RouteId = "route1",
ClusterId = "cluster1",
Match = new RouteMatch
{
Path = "{**catch-all}"
}
}
};

Cluster[] clusters = new[]
{
new Cluster()
{
Id = "cluster1",
Destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new Destination() { Address = options.DestinationAddress} }
}
}
};

services.AddReverseProxy()
.LoadFromMemory(routes, clusters);
})

.Build();

await _host.StartAsync(cancellationToken);

return url;
}

public Task StopAsync()
{
return _host.StopAsync();
}

public void Dispose()
{
if (_host is { })
{
_host.Dispose();
}
}
}
}
66 changes: 66 additions & 0 deletions src/Tool/src/Boost.Core/Web/Proxy/ProxyConfigProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Yarp.ReverseProxy.Abstractions;
using Yarp.ReverseProxy.Service;

namespace Boost.Web.Proxy
{
public static class InMemoryConfigProviderExtensions
{
public static IReverseProxyBuilder LoadFromMemory(
this IReverseProxyBuilder builder,
IReadOnlyList<ProxyRoute> routes,
IReadOnlyList<Cluster> clusters)
{
builder.Services.AddSingleton<IProxyConfigProvider>(
new InMemoryConfigProvider(routes, clusters));

return builder;
}
}
public class InMemoryConfigProvider : IProxyConfigProvider
{
private volatile InMemoryConfig _config;

public InMemoryConfigProvider(
IReadOnlyList<ProxyRoute> routes,
IReadOnlyList<Cluster> clusters)
{
_config = new InMemoryConfig(routes, clusters);
}

public IProxyConfig GetConfig() => _config;

public void Update(IReadOnlyList<ProxyRoute> routes, IReadOnlyList<Cluster> clusters)
{
InMemoryConfig oldConfig = _config;
_config = new InMemoryConfig(routes, clusters);
oldConfig.SignalChange();
}

private class InMemoryConfig : IProxyConfig
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();

public InMemoryConfig(IReadOnlyList<ProxyRoute> routes, IReadOnlyList<Cluster> clusters)
{
Routes = routes;
Clusters = clusters;
ChangeToken = new CancellationChangeToken(_cts.Token);
}

public IReadOnlyList<ProxyRoute> Routes { get; }

public IReadOnlyList<Cluster> Clusters { get; }

public IChangeToken ChangeToken { get; }

internal void SignalChange()
{
_cts.Cancel();
}
}
}
}
19 changes: 19 additions & 0 deletions src/Tool/src/Boost.Core/Web/Proxy/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Builder;

namespace Boost.Web.Proxy
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();

app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapReverseProxy();
});
}
}
}
2 changes: 1 addition & 1 deletion src/Tool/src/Boost.Tool/Boost.Tool.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
Expand Down
1 change: 1 addition & 0 deletions src/Tool/src/Boost.Tool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Boost.Tool
typeof(QuickActionsCommand),
typeof(VersionCommand),
typeof(SwitchRepositoryCommand),
typeof(LocalProxyCommand),
typeof(IndexRepositoriesCommand))]
class Program
{
Expand Down
2 changes: 1 addition & 1 deletion src/Tool/src/Boost.Tool/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"Boost.Tool": {
"commandName": "Project",
"commandLineArgs": "ui"
"commandLineArgs": "lp https://c4c-sidecar-blueprint.a.portals.swisslife.ch"
}
}
}

0 comments on commit 4ee5cd8

Please sign in to comment.